Back to Community
Long-only counter-trend with Zipline

Hi all,

First post. This is a mangled version of Dan's martingale example. I added a run loss limit and the ability to easily change the bet size function of the number of down steps. One can still discover what a disaster a pure martingale is by setting the down step limit to a large number.

This algorithm has two parameters: the price step percentage at which to adjust it's position, and the down step loss limit. It is always long and increases position on down step losses according to the bet size. When the down step limit is exceeded, the position is reset to a single long position. When an up step is exceeded, all positions except the single long are closed. If only the single long, then the last trade price is moved up a step without trading, so the single long follows prices up.

This will run in Zipline if the comment blocks around the code at the top and bottom are removed. Or else just delete them if you are not using Zipline.

best regards,

Bob Schmidt

Clone Algorithm
16
Loading...
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
from __future__ import division
from datetime import datetime
import pytz
import matplotlib.pyplot as plt
import math

from zipline.algorithm import TradingAlgorithm
from zipline.finance import trading, commission, slippage
from zipline.utils.factory import load_from_yahoo
from zipline.api import order, record, symbol, get_open_orders, order_target
"""

# code above is outside Quantopian in Zipline
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# code below is inside Quantopian


"""
Long-only Counter-Trend system that adjusts every X percent price change.
It is always long and increases position on down price steps.
Reset to a single long position if down steps exceed down_limit.
The single long position follows price steps up.

The classic martingale doubles the bet on a loss with no limit on loss runs.
Please, never trade this algorithm.  It's a bad idea.
read more: http://en.wikipedia.org/wiki/Martingale_(betting_system)

Alternatively, we can reset after a limited run of losses and set the
bet_size to a different function of down_step to reduce the risk(reward).
"""


def initialize(context):
    context.symbol = symbol('SPY')  # 'SPY' to match benchmark in Zipline
    context.pct_step = 3  # adjust position at pct_step price changes
    context.down_limit = 4  # reset the system if exceeded by down_step
    initstr = 'trade at ' + str(context.pct_step) + '% price moves, with '
    initstr += str(context.down_limit) + ' down step limit'
    print(initstr)
    commissionPerShare = 0.006  # set all three to zero for ideal result
    minCommissionPerTrade = 1.25  # set all three to zero for ideal result
    bidAskSpread = 0.03  # set all three to zero for ideal result
    context.min_size = 100  # so commissions make sense
    context.down_step = 0  # counts down steps
    context.trade_price = 0  # price of the last trade
    context.acnt = 0  # ideal cash account (no commissions or slippage)
    context.show_steps = True  # print trades for each price step
    context.starter_message = True
    context.instant_fill = True  # trade on this bar's close instantly
    context.crashed = False  # if true, the money has gone to money heaven

    try:  # check for Quantopian
        set_commission(commission.PerShare(cost=commissionPerShare,
                       min_trade_cost=minCommissionPerTrade))
        set_slippage(slippage.FixedSlippage(spread=bidAskSpread))
        set_benchmark(context.symbol)
        context.runningQuantopian = True
    except:  # running Zipline (or my error above)
        context.runningQuantopian = False
        context.set_commission(commission.PerShare(cost=commissionPerShare,
                               min_trade_cost=minCommissionPerTrade))
        context.set_slippage(slippage.FixedSlippage(spread=bidAskSpread))


def handle_data(context, data):
    if context.crashed:
        return
    if context.starter_message:
        print('Martingale strategies lose in the long run. Have fun!')
        context.starter_message = False
    if context.runningQuantopian:
        datestr = ""  # Quantopian has built-in date string printing
    else:
        datestr = algo.get_datetime().strftime('%Y-%m-%d')
    curr_price = data[context.symbol].price
    if context.trade_price > 0:
        trade_price = context.trade_price
    else:
        trade_price = curr_price
    position = context.portfolio.positions[context.symbol].amount

    openord = get_open_orders()  # Zipline always true?
    for ord in openord:
        if len(openord[ord]) > 0:
            print('open order:', ord, openord[ord])
            return

    # bet_size differences are smaller for small values of down_limit
    #bet_size = 2**context.down_step  # double your bet each loss (max risk)
    #bet_size = context.down_step + 1  # increase bet by one each loss
    bet_size = round(context.down_step**0.7)  # increase bet by < 1 each loss
    #bet_size = 1  # increase long with constant bet size each loss

    tradeSize = max(1, bet_size) * context.min_size
    step_mult = 1 + context.pct_step / 100.

    if position == 0:  # always have at least a single long position
        order(context.symbol, tradeSize)
        trade_price = curr_price
        context.acnt -= tradeSize * curr_price
        context.down_step = 0
        position = tradeSize
        if context.show_steps:
            print(datestr + ' initial buy ' + str(tradeSize) + ' @ ' + str(curr_price))

    # up step from last trade so sell everything but one long position
    elif curr_price > context.trade_price * step_mult:
        if position > context.min_size:  # there are positions to sell
            order_target(context.symbol, context.min_size)
            context.down_step = 0
            trade_price = curr_price
            context.acnt += (position - context.min_size) * curr_price
            if context.show_steps:
                sellstr = datestr + ' sell ' + str(position - context.min_size)
                sellstr += ' @ ' + str(curr_price) + ' ideal: '
                sellstr += str(context.acnt + curr_price * context.min_size)
                print(sellstr)
            position = context.min_size
        else:  # adjust the trade_price up a step without trading
            context.down_step = 0
            trade_price = curr_price
            if context.show_steps:
                upstr = datestr + ' step up ' + str(position) + ' @ ' + str(curr_price)
                upstr += ' ideal: ' + str(context.acnt + curr_price * position)
                print(upstr)

    # down step so increase long position if down_step less than down_limit
    elif curr_price < context.trade_price / step_mult:
        if context.portfolio.cash < 0:
            print('Damn. I owe my broker money! I cannot order more.')
            print("Don't believe any results.")
            context.crashed = True
            return
        if tradeSize > round(context.portfolio.cash/curr_price):
            print('Crap! I don''t have enough money to make my Martingale bet!')
            print("Don't believe any results.")
            context.crashed = True
            return
        context.down_step += 1
        if context.down_step <= context.down_limit:  # increase long position
            order(context.symbol, tradeSize)
            trade_price = curr_price
            context.acnt -= tradeSize * curr_price
            position += tradeSize
            if context.show_steps:
                buystr = datestr + ' buy ' + str(tradeSize) + ' @ ' + str(curr_price)
                buystr += ' ideal: ' + str(context.acnt + curr_price * position)
                print(buystr)
        else:  # reset to single long position when down_limit is exceeded
            order_target(context.symbol, context.min_size)
            context.down_step = 0
            trade_price = curr_price
            context.acnt += (position - context.min_size) * curr_price
            if context.show_steps:
                resetstr = datestr + ' * RESET SELL * '
                resetstr += str(position - context.min_size) + ' @ ' + str(curr_price)
                resetstr += ' ideal: ' + str(context.acnt + curr_price * context.min_size)
                print(resetstr)
            position = context.min_size

    record(position=position)
    record(ideal=context.acnt + curr_price * position, trade_price=trade_price)
    context.trade_price = trade_price


#  code above goes inside Quantopian
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#  code below is outside Quantopian in Zipline

"""
def show_results(algo, data, results):
    br = trading.environment.benchmark_returns
    bm_returns = br[(br.index >= start) & (br.index <= end)]
    results['benchmark_returns'] = (1 + bm_returns).cumprod().values
    results['algorithm_returns'] = (1 + results.returns).cumprod()
    #sharpe = [risk['sharpe'] for risk in algo.risk_report['one_month']]
    #print("Monthly Sharpe ratios: {0}".format(sharpe))

    print("ideal netpnl: " + str(round(results.ideal[-1], 2)))
    actual = results.portfolio_value - algo.portfolio.starting_cash
    print("actual netpnl: " + str(actual[-1]))

    fig = plt.figure(1, figsize=(8, 10))
    plt.subplots_adjust(left=0.1, right=0.98, bottom=0.0, top=0.96)

    ax1 = fig.add_subplot(311, ylabel='Cumulative Returns')
    results[['algorithm_returns', 'benchmark_returns']].plot(ax=ax1, sharex=True)
    plt.setp(ax1.get_xticklabels(), visible=False)
    plt.legend(loc=0)

    ax2 = fig.add_subplot(312, ylabel='Price')
    data[algo.symbol].plot(ax=ax2, color='blue')
    results.trade_price.plot(ax=ax2, color='red')
    plt.setp(ax2.get_xticklabels(), visible=False)
    plt.legend(loc=0)

    ax3 = fig.add_subplot(313, ylabel='Position Size')
    results.position.plot(ax=ax3, color='blue')
    plt.legend(loc=0)

    plt.gcf().set_size_inches(18, 8)
    plt.show()


if __name__ == '__main__':
    start = datetime(2008, 1, 1, 0, 0, 0, 0, pytz.utc)
    end = datetime(2014, 6, 1, 0, 0, 0, 0, pytz.utc)
    algo = TradingAlgorithm(initialize=initialize, handle_data=handle_data)
    data = load_from_yahoo(stocks=[algo.symbol], indexes={},
                           start=start, end=end).dropna()
    results = algo.run(data)
    show_results(algo, data, results)
"""
There was a runtime error.
5 responses

in my test the strategy on Zipline0.61 , the show_results didn't be invoke after algo.run, what was wrong for me ?

Hi Novice TAI,

I think that Quantopian/Zipline compatibility is a high priority for the developers right now based on recent releases, so we are probably pushing into areas that are in heavy development. But I am only guessing. It sounds like the algo.run worked for you. Did you get a long printout of all the trades? But no plots? Did you get an error message?

In previous runing through run_algo.py, the show_result method cannot be invoke,
If direct runing through paython commnad it running well ,please see the running result image,
but the reuslt seems have difference compare running in Quantopian, anyway thanks Bob share the strategy!

Thank you for finding the problem. I was running the file in Spyder. I'm not familiar with running Zipline that way. The results are a little different. Yahoo and Quantopian have somewhat different historical prices. There is a recent post about that.

I also realized that there was no need for the algorithm to crash with no information when the cash was used up or the bet size was too big. Instead it resets to a single long position and keeps on going. That way we can see better the effect on returns when the algorithm takes its loss.

Clone Algorithm
9
Loading...
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
from __future__ import division
from datetime import datetime
import pytz
import math
import pylab as pl

from zipline.algorithm import TradingAlgorithm
from zipline.finance import trading, commission, slippage
from zipline.utils.factory import load_from_yahoo
from zipline.api import order, record, symbol, get_open_orders, order_target
"""


# code above is outside Quantopian
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# code below goes inside Quantopian


"""
Long-only Counter-Trend system that adjusts every X percent price change.
It is always long and increases position on down price steps.
Reset to a single long position if down steps exceed down_limit.
The single long position follows price steps up.

The classic martingale doubles the bet on a loss with no limit on loss runs.
Please, never trade this algorithm.  It's a bad idea.
read more: http://en.wikipedia.org/wiki/Martingale_(betting_system)

Alternatively, we can reset after a limited run of losses and set the
bet_size to a different function of down_step to reduce the risk(reward).
"""


def initialize(context):
    context.symbol = symbol('SPY')  # 'SPY' to match benchmark in Zipline
    context.pct_step = 2  # adjust position at pct_step price changes
    context.down_limit = 3  # reset the system if exceeded by down_step
    initstr = 'trade at ' + str(context.pct_step) + '% price moves, with '
    initstr += str(context.down_limit) + ' down step limit'
    print(initstr)
    commissionPerShare = 0.006  # set all three to zero for ideal result
    minCommissionPerTrade = 1.25  # set all three to zero for ideal result
    bidAskSpread = 0.03  # set all three to zero for ideal result
    context.min_size = 100  # so commissions make sense
    context.down_step = 0  # counts down steps
    context.trade_price = 0  # price of the last trade
    context.acnt = 0  # ideal cash account (no commissions or slippage)
    context.show_steps = True  # print trades for each price step
    context.instant_fill = True  # trade on this bar's close instantly

    try:  # check for Quantopian
        set_commission(commission.PerShare(cost=commissionPerShare,
                       min_trade_cost=minCommissionPerTrade))
        set_slippage(slippage.FixedSlippage(spread=bidAskSpread))
        set_benchmark(context.symbol)
        context.runningQuantopian = True
    except:  # running Zipline (or my error above)
        context.runningQuantopian = False
        context.set_commission(commission.PerShare(cost=commissionPerShare,
                               min_trade_cost=minCommissionPerTrade))
        context.set_slippage(slippage.FixedSlippage(spread=bidAskSpread))


def handle_data(context, data):
    if context.runningQuantopian:
        datestr = ""  # Quantopian has built-in date string printing
    else:
        datestr = algo.get_datetime().strftime('%Y-%m-%d')
    curr_price = data[context.symbol].price
    if context.trade_price > 0:
        trade_price = context.trade_price
    else:
        trade_price = curr_price
    position = context.portfolio.positions[context.symbol].amount

    openord = get_open_orders()  # Zipline always true?
    for ord in openord:
        if len(openord[ord]) > 0:
            print('open order:', ord, openord[ord])
            return

    # bet_size differences are smaller for small values of down_limit
    #bet_size = 2**context.down_step  # double your bet each loss (max risk)
    bet_size = context.down_step + 1  # increase bet by one each loss
    #bet_size = round(context.down_step**0.7)  # increase bet by < 1 each loss
    #bet_size = 1  # increase long with constant bet size each loss

    tradeSize = max(1, bet_size) * context.min_size
    step_mult = 1 + context.pct_step / 100.

    if position == 0:  # always have at least a single long position
        order(context.symbol, tradeSize)
        trade_price = curr_price
        context.acnt -= tradeSize * curr_price
        context.down_step = 0
        position = tradeSize
        if context.show_steps:
            print(datestr + ' initial buy ' + str(tradeSize) + ' @ ' + str(curr_price))

    # up step from last trade so sell everything but one long position
    elif curr_price > context.trade_price * step_mult:
        if position > context.min_size:  # there are positions to sell
            order_target(context.symbol, context.min_size)
            context.down_step = 0
            trade_price = curr_price
            context.acnt += (position - context.min_size) * curr_price
            if context.show_steps:
                sellstr = datestr + ' sell ' + str(position - context.min_size)
                sellstr += ' @ ' + str(curr_price) + ' ideal: '
                sellstr += str(context.acnt + curr_price * context.min_size)
                print(sellstr)
            position = context.min_size
        else:  # adjust the trade_price up a step without trading
            context.down_step = 0
            trade_price = curr_price
            if context.show_steps:
                upstr = datestr + ' step up ' + str(position) + ' @ ' + str(curr_price)
                upstr += ' ideal: ' + str(context.acnt + curr_price * position)
                print(upstr)

    # down step from last trade so try to increase long position
    elif curr_price < context.trade_price / step_mult:
        if context.down_step <= context.down_limit:  # down_limit not exceeded
            down_ok = True
            if context.portfolio.cash < 0:  # total position is too big so reset
                crashstr = " RESET (CASH) SELL "
                down_ok = False
            elif tradeSize > int(context.portfolio.cash / curr_price):
                crashstr = " RESET (BET SIZE) SELL "  # bet_size is too big so reset
                down_ok = False
            if down_ok:
                context.down_step += 1
                order(context.symbol, tradeSize)
                trade_price = curr_price
                context.acnt -= tradeSize * curr_price
                position += tradeSize
                if context.show_steps:
                    buystr = datestr + ' buy ' + str(tradeSize) + ' @ ' + str(curr_price)
                    buystr += ' ideal: ' + str(context.acnt + curr_price * position)
                    print(buystr)
        else:  # down_limit is exceeded by down_step so reset
            crashstr = " RESET (DOWN STEPS) SELL "
            down_ok = False

        if not down_ok:  # reset to single long position if we can't step down
            order_target(context.symbol, context.min_size)
            context.down_step = 0
            trade_price = curr_price
            context.acnt += (position - context.min_size) * curr_price
            if context.show_steps:
                resetstr = datestr + crashstr + str(position - context.min_size)
                resetstr += ' @ ' + str(curr_price) + ' ideal: '
                resetstr += str(context.acnt + curr_price * context.min_size)
                print(resetstr)
            position = context.min_size

    record(position=position)
    record(ideal=context.acnt + curr_price * position, trade_price=trade_price)
    context.trade_price = trade_price


#  code above goes inside Quantopian
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#  code below is outside Quantopian


"""
def show_results(algo, data, results):
    br = trading.environment.benchmark_returns
    bm_returns = br[(br.index >= start) & (br.index <= end)]
    results['benchmark_returns'] = (1 + bm_returns).cumprod().values
    results['algorithm_returns'] = (1 + results.returns).cumprod()
    #sharpe = [risk['sharpe'] for risk in algo.risk_report['one_month']]
    #print("Monthly Sharpe ratios: {0}".format(sharpe))

    print("ideal netpnl: " + str(round(results.ideal[-1], 2)))
    actual = results.portfolio_value - algo.portfolio.starting_cash
    print("actual netpnl: " + str(actual[-1]))

    fig = pl.figure(1, figsize=(8, 10))
    pl.subplots_adjust(left=0.1, right=0.98, bottom=0.0, top=0.96)

    ax1 = fig.add_subplot(311, ylabel='Cumulative Returns')
    results[['algorithm_returns', 'benchmark_returns']].plot(ax=ax1, sharex=True)
    pl.setp(ax1.get_xticklabels(), visible=False)
    pl.legend(loc=0)

    ax2 = fig.add_subplot(312, ylabel='Price')
    data[algo.symbol].plot(ax=ax2, color='blue')
    results.trade_price.plot(ax=ax2, color='red')
    pl.setp(ax2.get_xticklabels(), visible=False)
    pl.legend(loc=0)

    ax3 = fig.add_subplot(313, ylabel='Position Size')
    results.position.plot(ax=ax3, color='blue')
    pl.legend(loc=0)

    pl.gcf().set_size_inches(18, 8)
    pl.show()


if __name__ == '__main__':
    start = datetime(2008, 1, 1, 0, 0, 0, 0, pytz.utc)
    end = datetime(2014, 6, 1, 0, 0, 0, 0, pytz.utc)
    algo = TradingAlgorithm(initialize=initialize, handle_data=handle_data)
    data = load_from_yahoo(stocks=[algo.symbol], indexes={},
                           start=start, end=end).dropna()
    results = algo.run(data)
    show_results(algo, data, results)
"""
There was a runtime error.

I introduced a bug where it was allowing one position too many. Sigh . . .

Clone Algorithm
9
Loading...
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
from __future__ import division
from datetime import datetime
import pytz
import math
import pylab as pl

from zipline.algorithm import TradingAlgorithm
from zipline.finance import trading, commission, slippage
from zipline.utils.factory import load_from_yahoo
from zipline.api import order, record, symbol, get_open_orders, order_target
"""


# code above is outside Quantopian
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# code below goes inside Quantopian


"""
Long-only Counter-Trend system that adjusts every X percent price change.
It is always long and increases position on down price steps.
Reset to a single long position if down steps exceed down_limit, or if
the position size has exceeded cash, or if the bet is too big.
The single long position follows price steps up.

The classic martingale doubles the bet on a loss with no limit on loss runs.
Please, never trade this algorithm.  It's a bad idea.
read more: http://en.wikipedia.org/wiki/Martingale_(betting_system)

Alternatively, we can reset after a limited run of losses and set the
bet_size to a different function of down_step to reduce the risk(reward).
"""


def initialize(context):
    context.symbol = symbol('SPY')  # 'SPY' to match benchmark in Zipline
    context.pct_step = 2  # adjust position at pct_step price changes
    context.down_limit = 4  # reset the system if exceeded by down_step
    initstr = 'trade at ' + str(context.pct_step) + '% price moves, with '
    initstr += str(context.down_limit) + ' down step limit'
    print(initstr)
    commissionPerShare = 0.006  # set all three to zero for ideal result
    minCommissionPerTrade = 1.25  # set all three to zero for ideal result
    bidAskSpread = 0.03  # set all three to zero for ideal result
    context.min_size = 100  # so commissions make sense
    context.down_step = 0  # counts down steps
    context.trade_price = 0  # price of the last trade
    context.acnt = 0  # ideal cash account (no commissions or slippage)
    context.show_steps = True  # print trades for each price step
    context.instant_fill = True  # trade on this bar's close instantly

    try:  # check for Quantopian
        set_commission(commission.PerShare(cost=commissionPerShare,
                       min_trade_cost=minCommissionPerTrade))
        set_slippage(slippage.FixedSlippage(spread=bidAskSpread))
        set_benchmark(context.symbol)
        context.runningQuantopian = True
    except:  # running Zipline (or my error above)
        context.runningQuantopian = False
        context.set_commission(commission.PerShare(cost=commissionPerShare,
                               min_trade_cost=minCommissionPerTrade))
        context.set_slippage(slippage.FixedSlippage(spread=bidAskSpread))


def handle_data(context, data):
    if context.runningQuantopian:
        datestr = ""  # Quantopian has built-in date string printing
    else:
        datestr = algo.get_datetime().strftime('%Y-%m-%d')
    curr_price = data[context.symbol].price
    if context.trade_price > 0:
        trade_price = context.trade_price
    else:
        trade_price = curr_price
    position = context.portfolio.positions[context.symbol].amount

    openord = get_open_orders()  # Zipline always true?
    for ord in openord:
        if len(openord[ord]) > 0:
            print('open order:', ord, openord[ord])
            return

    # bet_size differences are smaller for small values of down_limit
    #bet_size = 2**context.down_step  # double your bet each loss (max risk)
    bet_size = context.down_step + 1  # increase bet by one each loss
    #bet_size = round(context.down_step**0.7)  # increase bet by < 1 each loss
    #bet_size = 1  # increase long with constant bet size each loss

    tradeSize = max(1, bet_size) * context.min_size
    step_mult = 1 + context.pct_step / 100.

    if position == 0:  # always have at least a single long position
        order(context.symbol, tradeSize)
        trade_price = curr_price
        context.acnt -= tradeSize * curr_price
        context.down_step = 0
        position = tradeSize
        if context.show_steps:
            print(datestr + ' initial buy ' + str(tradeSize) + ' @ ' + str(curr_price))

    # up step from last trade so sell everything but one long position
    elif curr_price > context.trade_price * step_mult:
        if position > context.min_size:  # there are positions to sell
            order_target(context.symbol, context.min_size)
            context.down_step = 0
            trade_price = curr_price
            context.acnt += (position - context.min_size) * curr_price
            if context.show_steps:
                sellstr = datestr + ' sell ' + str(position - context.min_size)
                sellstr += ' @ ' + str(curr_price) + ' ideal: '
                sellstr += str(context.acnt + curr_price * context.min_size)
                print(sellstr)
            position = context.min_size
        else:  # adjust the trade_price up a step without trading
            context.down_step = 0
            trade_price = curr_price
            if context.show_steps:
                upstr = datestr + ' step up ' + str(position) + ' @ ' + str(curr_price)
                upstr += ' ideal: ' + str(context.acnt + curr_price * position)
                print(upstr)

    # down step from last trade so try to increase long position
    elif curr_price < context.trade_price / step_mult:
        context.down_step += 1
        if context.down_step <= context.down_limit:  # down_limit not exceeded
            down_ok = True
            if context.portfolio.cash < 0:  # total position is too big so reset
                crashstr = " RESET (CASH) SELL "
                down_ok = False
            elif tradeSize > int(context.portfolio.cash / curr_price):
                crashstr = " RESET (BET SIZE) SELL "  # bet_size is too big so reset
                down_ok = False
            if down_ok:
                order(context.symbol, tradeSize)
                trade_price = curr_price
                context.acnt -= tradeSize * curr_price
                position += tradeSize
                if context.show_steps:
                    buystr = datestr + ' buy ' + str(tradeSize) + ' @ ' + str(curr_price)
                    buystr += ' ideal: ' + str(context.acnt + curr_price * position)
                    print(buystr)
        else:  # down_limit is exceeded by down_step so reset
            crashstr = " RESET (DOWN STEPS) SELL "
            down_ok = False

        if not down_ok:  # reset to single long position if we can't step down
            order_target(context.symbol, context.min_size)
            context.down_step = 0
            trade_price = curr_price
            context.acnt += (position - context.min_size) * curr_price
            if context.show_steps:
                resetstr = datestr + crashstr + str(position - context.min_size)
                resetstr += ' @ ' + str(curr_price) + ' ideal: '
                resetstr += str(context.acnt + curr_price * context.min_size)
                print(resetstr)
            position = context.min_size

    record(position=position)
    record(ideal=context.acnt + curr_price * position, trade_price=trade_price)
    context.trade_price = trade_price


#  code above goes inside Quantopian
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#  code below is outside Quantopian


"""
def show_results(algo, data, results):
    br = trading.environment.benchmark_returns
    bm_returns = br[(br.index >= start) & (br.index <= end)]
    results['benchmark_returns'] = (1 + bm_returns).cumprod().values
    results['algorithm_returns'] = (1 + results.returns).cumprod()
    #sharpe = [risk['sharpe'] for risk in algo.risk_report['one_month']]
    #print("Monthly Sharpe ratios: {0}".format(sharpe))

    print("ideal netpnl: " + str(round(results.ideal[-1], 2)))
    actual = results.portfolio_value - algo.portfolio.starting_cash
    print("actual netpnl: " + str(actual[-1]))

    fig = pl.figure(1, figsize=(8, 10))
    pl.subplots_adjust(left=0.1, right=0.98, bottom=0.0, top=0.96)

    ax1 = fig.add_subplot(311, ylabel='Cumulative Returns')
    results[['algorithm_returns', 'benchmark_returns']].plot(ax=ax1, sharex=True)
    pl.setp(ax1.get_xticklabels(), visible=False)
    pl.legend(loc=0)

    ax2 = fig.add_subplot(312, ylabel='Price')
    data[algo.symbol].plot(ax=ax2, color='blue')
    results.trade_price.plot(ax=ax2, color='red')
    pl.setp(ax2.get_xticklabels(), visible=False)
    pl.legend(loc=0)

    ax3 = fig.add_subplot(313, ylabel='Position Size')
    results.position.plot(ax=ax3, color='blue')
    pl.legend(loc=0)

    pl.gcf().set_size_inches(18, 8)
    pl.show()


if __name__ == '__main__':
    start = datetime(2008, 1, 1, 0, 0, 0, 0, pytz.utc)
    end = datetime(2014, 6, 1, 0, 0, 0, 0, pytz.utc)
    algo = TradingAlgorithm(initialize=initialize, handle_data=handle_data)
    data = load_from_yahoo(stocks=[algo.symbol], indexes={},
                           start=start, end=end).dropna()
    results = algo.run(data)
    show_results(algo, data, results)
"""
There was a runtime error.