Quantopian's community platform is shutting down. Please read this post for more information and download your code.
Back to Community
Take Profit

Example for taking profit at a given threshold over cost basis (for both long or short).
You could drop this into an algorithm at the end of initialize with a single paste quickly to see how it does with the settings as-is.

    # Take profit several times a day  
    context.profit_threshold = .05  
    context.profit_logging   = 1  
    for i in range(20, 390, 60):    # (low, high, every i minutes)  
        #break    # uncomment to deactivate  
        schedule_function(take_profit, date_rules.every_day(), time_rules.market_open(minutes=i))

def take_profit(context, data):    # Close some positions to take profit  
    pos = context.portfolio.positions  
    for s in pos:  
        if not data.can_trade(s): continue  
        prc = data.current(s, 'price')  
        amt = pos[s].amount  
        if (amt / abs(amt)) * ((prc / pos[s].cost_basis) - 1) > context.profit_threshold:  
            order_target(s, 0)  
            if not context.profit_logging: continue  
            log.info('take profit {} {}  cb {}  now {}'.format(  
                amt, s.symbol, '%.2f' % pos[s].cost_basis, prc))  

First, this image is the backtest without the code active, 30.9%

Then with the function active, the log shows long and short positions being closed when beyond the threshold at 5% profit.
cb is cost basis, price per share on open with commission per share added

2016-01-05 06:31 longs:33 INFO order SPY  
2016-01-06 06:31 shrts:39 INFO order SH  
2016-03-30 06:50 take_profit:28 INFO close -137 SH  cb 21.42  now 20.3  
2016-04-06 06:31 shrts:39 INFO order SH  
2016-06-07 06:50 take_profit:28 INFO close 14 SPY  cb 201.72  now 211.81  
2016-06-14 06:31 longs:33 INFO order SPY  
2016-07-11 06:50 take_profit:28 INFO close -78.0 SH  cb 41.04  now 38.885  
2016-07-13 06:31 shrts:39 INFO order SH  
2016-08-11 08:50 take_profit:28 INFO close 16 SPY  cb 208.24  now 218.72  
2016-08-16 06:31 longs:33 INFO order SPY  
2016-12-08 06:50 take_profit:28 INFO close -92 SH  cb 38.53  now 36.583  

This is the backtest with code active, 36.1% (an increase of 5.2 percentage points in this instance).

Edit: Sometimes when editing a message, an attached backtest disappears. Regrettably, that happened with this one, however everything needed is in the code above.

10 responses

Blue, this is great. Thanks for posting this.

Thanks for the appreciation.

Could maybe also be used in reverse as a stoploss so this is both take profit and stop loss combined.
Stoploss untested.

    # From https://www.quantopian.com/posts/take-profit, several times a day ...  
    # Take Profit  
    context.take_profit_active = 1  
    context.profit_threshold   = .05  
    context.profit_logging     = 1  
    # Stop Loss  
    context.stoploss_active    = 1  
    context.loss_threshold     = -.05  
    context.stoploss_logging   = 1

    for i in range(20, 390, 60):    # (low, high, every i minutes) Profit or StopLoss  
        #break    # uncomment to deactivate  
        schedule_function(profit_or_loss, date_rules.every_day(), time_rules.market_open(minutes=i))

def profit_or_loss(context, data):  # Close positions at thresholds for profit or loss.  
    pos = context.portfolio.positions  
    for s in pos:  
        if not data.can_trade(s): continue  
        if get_open_orders(s):    continue  
        prc = data.current(s, 'price')  
        amt = pos[s].amount  
        if context.take_profit_active:  
            if (amt / abs(amt)) * ((prc / pos[s].cost_basis) - 1) > context.profit_threshold:  
                order_target(s, 0)  
                if not context.profit_logging: continue  
                log.info('take profit {} {}  cb {}  now {}'.format(  
                    amt, s.symbol, '%.2f' % pos[s].cost_basis, prc))  
            continue  
        if context.stoploss_active:  
            if (amt / abs(amt)) * ((prc / pos[s].cost_basis) - 1) < context.loss_threshold:  
                order_target(s, 0)  
                if not context.stoploss_logging: continue  
                log.info('stoploss {} {}  cb {}  now {}'.format(  
                    amt, s.symbol, '%.2f' % pos[s].cost_basis, prc))  

Think about adding slope to this from related code-to-capture-profit-on-price-jump
Could maybe multiply slope and the ratio (compared to threshold) so that, for example, if down only -.02 yet slope sharply down, close, while if -.048 nudging up against the threshold and slope not down much, hang in there, or if slope up, keep no matter what.

You might find this article interesting. It's a research on the impact of stop-loss and take-profit logics on algorithm performance.

Looks good Blue.

Luca that was good article, I've been running backtests on Marketinout.com and noticed way better results w/o having set a stop loss or setting a 40%+ SL. However, the only way to get away with letting your losses run is by minimizing your losses/ having an extremely high accuracy 88%+ which I managed to achieve on backtests with a reasonable TP level. The question is will the strategy remain as accurate during live trading?

Thank you Blue for sharing this idea!

This is fantastic - thanks everyone

Nice code. But how do you deal with trailing stop?

May I know why the stop loss can't be triggered?

for some reason, this logic did not work for me on shorts / have - have anyone tried it... with better success? looks like the pct calc on short position does not work as well... strange.