Back to Community
A "framework" to manage bracket orders (with SL, TP) and additional features (TrailingStop, BreakEven, Expiration) and advanced statistics

Hello,

Quantopian Open will start soon but sometimes I wonder if such contest won't destroy the community spirit of Quantopian (I'm not saying that because I'm pretty sure I won't win this contest ;-) ).

In order to stay in the share spirit of this community (and more generally the open source community) I'm giving a "framework" to manage bracket orders also named here "BO" (with SL, TP) and additional features.

This is still something very experimental... it's not a "strategy" just a way to manage several orders differently.

A bracket order is composed of 3 orders:
- A master order
- A limit order (take profit)
- A stop order (stop loss)

How does it works ?

I created (on top of Zipline blotter) a special blotter named "bo_blotter" (class BOBlotter).

This bo_blotter is created at initialize:

def initialize(context):  
    context.bo_blotter = BOBlotter(context)  

and updated at handle_data

def handle_data(context, data):  
    bo_blotter = context.bo_blotter  
    bo_blotter.update(context, data)

This object have several methods which looks like Metatrader 4 API.

tkt = bo_blotter.order_send(symb, LimitOrder(80), volume, context.price_stop_loss, context.price_take_profit, comment, context.magic, features)  

for example will create a BracketOrder with a limit order with price of 80 (a buy order if volume>0). 2 others orders are around this order (a sell limit order at price_take_profit and a sell stop order at price_stop_loss).

Every bracket order can have a collection of "features" attached (BOFeatures is a collection of BOFeature).

For now there is 3 features for BO: Expiration, TrailingStop and BreakEven.

An Expiration feature can be attach to a (pending) BO.
If BO was never triggered before expiration reached, bracket order will be canceled (so master order, limit order and stop order will be canceled). Expiration can be set either using a datetime or a timedelta.

We can also attach a TrailingStop to a given bracket order (either using order_send and passing a TrailingStop object as feature or after order_send, when a ticket for BO is given, using bo_blotter.add_bo_feature(tkt, feature)

We can also attach a BreakEven to a given bracket order.

I also add some code from https://github.com/Numergy/signalslot/ a signal slot library.
So you can attach a slot to an "event" (signal).
So for example when expiration is reached you can do something. When take profit is reached you can also do something else. Same for stop loss. This event oriented approach can be very convenient.
An other approach for this could be the use of a pub/sub (publish / subscribe) library.
http://en.wikipedia.org/wiki/Signals_and_slots
http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern

BO_Blotter also have statistics for each bracket order, for example Maximum Adverse Excursion (MAE) is compute, same for MFE (Maximum Favorable Excursion).
BracketOrders are stored inside an OrderedDict (attribute of bo_blotter) : with 3 keys: 'trades' (for opened bracket orders), 'history' (for closed bracket orders), 'canceled' (for canceled bracket orders)

All this work is still very experimental and probably still need many improvements. I'm aware of this.

There is also many items in the ToDo list:
for example there is no support for partial close of bracket orders.
MAE, MFE statistics should probably also be defined outside of blotter (maybe a BOBlotterFeature should be created).
Bracket order should also be send according a given risk, a given leverage, ....
Bracket order blotter should also be "secured" in order to ensure that there is no difference between this blotter and the original blotter (but maybe a cleaner approach could be to have this kind of order (bracket orders) directly managed by Zipline blotter.
see https://github.com/quantopian/zipline/issues/366
and https://github.com/quantopian/zipline/issues/189
what happens if master order is only partially filled ?

I hope this can help.

Here:
open BO buy limit AAPL @ 80 with SL=66.9 and TP=96.17
BO is closed because TP is reached

Clone Algorithm
59
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 54cc9a24a8fdc04085929cb2
There was a runtime error.
6 responses

An example with TrailingStop
(and some minor fix to attach feature to bracket order)

Here: open BO buy limit AAPL @ 80 with SL=66.9 and TP=108.72 (I increased this value willingly to avoid this order to triggered).
So neither original SL and TP triggered but a TrailingStop was set with a price difference of 8.0
Highest price was 100.32 so BO is closed at 100.32-8.0 approx 92 because of trailing stop

Clone Algorithm
59
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 54cca127e8885b4850dbdd1f
There was a runtime error.

An example of BreakEven usage:

2012/3/1 open a BO buy AAPL at market price
trade is going first in positive zone up to trigger breakeven (6% higher than open price) and turns to negative zone but is closed before going very negative (because of BreakEven).

Clone Algorithm
59
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Backtest ID: 54cca7bcb13ee10d3dbfe1ca
There was a runtime error.

Wow - this is a great framework! Thanks for sharing

Disclaimer

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by Quantopian. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. No information contained herein should be regarded as a suggestion to engage in or refrain from any investment-related course of action as none of Quantopian nor any of its affiliates is undertaking to provide investment advice, act as an adviser to any plan or entity subject to the Employee Retirement Income Security Act of 1974, as amended, individual retirement account or individual retirement annuity, or give advice in a fiduciary capacity with respect to the materials presented herein. If you are an individual retirement or other investor, contact your financial advisor or other fiduciary unrelated to Quantopian about whether any given investment idea, strategy, product or service described herein may be appropriate for your circumstances. All investments involve risk, including loss of principal. Quantopian makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances.

Thanks.

Feel free to take some of my ideas because many Quantopian users are looking for bracket orders, SL, TP, trailing stop...

Maybe a part of this code could be include into zipline and braket orders directly send from original blotter.

There is still some lacks:
no support of partial order close
no support of partial order open

@Femto: Great, thanks, I was trying to create something like it but my python skills are in contango or backwardation compared to yours. There is one one thing I'm struggling with in my IB account is that it is a cash account. (Ozzies are screwed like that). Now if I want to do a rebalance. I need to calculate the ratio's, sell the sids I need to sell, wait three days and after its settled I can do my buy actions... with the risk off course that I need to adjust the number of shares as the price could have moved. I have developed 2 functions: store_order which will or do the "sell" or store the "buy" and then check_order which will check wether there is enough spendable money in the account and then execute the trade.

How would you solve this in your framework? Just point me in the right direction and I'll try it myself.....


def store_order(context, sid, no_shares, tprice):  
    if  has_orders(sid):  
        orders = get_open_orders(sid)  
        for oo in orders:  
            if oo.filled == 0 and order.amount > 0: #buy order  
                cancel_order(order)  
            elif oo.filled > 0 and order.amount > 0: #buy order  
                no_shares = no_shares-oo.filled  
                log.info('EXCEPTION: partially filled order for ' + sid.symbol +'. Attempt CANCEL')  
                cancel_order(order)  
            elif oo.filled <> 0 and order.amount < 0: #sell order  
                if no_shares < oo.filled:  
                    #need to add to sell order  
                    no_shares = no_shares + abs(oo.filled)  
                    return order(sid,no_shares)  
            elif oo.filled == 0 and order.amount < 0: #sell order  
                    return order(sid,no_shares)  
         #No orders left  
    elif  
    context.orders[sid.symbol] = {  
        'date'         : pd.Timestamp(get_datetime()).tz_convert('US/Eastern'),  
        'price'        : tprice,  
        'shares'       : no_shares,  
        'total_trans'  : no_shares*tprice  
        'other'        : 'order in waiting'  
    }





def check_orders():

Hi,
This is a great framework, congrats. I've been following some of the posts regarding Bracket Orders, and all seem to agree that:

1) in backtesting you need to wait one bar after placing a market order before putting a limit or an stop order and check if the order was filled, this is very unfortunate for strategies where changes can happen in a matter of seconds.

2) it would be great to have such Bracket Orders implemented in the Q platform

3) I can't find anywhere what would be the behavior of placing such limit and stop orders in real trading within the interface with IB. Do you still have to wait for the next bar before placing the limit/stop orders?