I have been experimenting with algorithmic trading for a couple of weeks. Zipline is a Python library for backtesting trading algorithms and I would like to share one of the algorithms I made.
import talib
from zipline.api import record, order_target, history, add_history
import dateutil
import logging
from zipline.utils.factory import load_from_yahoo
from zipline.finance.slippage import FixedSlippage
from zipline.algorithm import TradingAlgorithm
from zipline.finance import commission
logging.basicConfig(level=logging.DEBUG)
# initialize algorithm
def initialize(context):
logging.debug('enter initialize')
context.set_slippage(FixedSlippage())
context.set_commission(commission.PerTrade(cost=5))
context.LOW_RSI = initialize.low_RSI
context.HIGH_RSI = initialize.high_RSI
context.rsi_window = initialize.rsi_window
add_history(context.rsi_window, '1d', 'price')
context.i = 0
context.invested = False
# default parameters for algorithm
initialize.rsi_window = 15
initialize.low_RSI = 30
initialize.high_RSI = 70
# Will be called on every trade event for the securities you specify.
def handle_data(context, data):
logging.debug('enter handle_data')
context.i += 1
if context.i < context.rsi_window:
return
# get the last RSI value
prices = history(context.rsi_window, '1d', 'price')
sec_rsi = talib.RSI(
prices[context.security].values,
timeperiod=context.rsi_window - 1)
# buy and sell flags
buy = False
sell = False
if sec_rsi[-1] < context.LOW_RSI and not context.invested:
# RSI under 30 indicates oversold, time to buy
order_target(context.security, 1000)
logging.debug('Buying {}'.format(context.security))
context.invested = True
buy = True
elif sec_rsi[-1] > context.HIGH_RSI and context.invested:
# RSI over 70 indicates overbought, sell everything
order_target(context.security, 0)
logging.debug('Selling {}'.format(context.security))
context.invested = False
sell = True
# record data for each time increment
record(secRSI=sec_rsi[-1],
price=data[context.security].price,
buy=buy,
sell=sell)
logging.info(context.portfolio.cash)
def run_algorithm(
security='AAPL',
start_date='20100101',
end_date='20150101',
initial_cash=100000,
rsi_window=15,
low_RSI=30,
high_RSI=70):
logging.debug('run_algorithm begin')
# dates
start = dateutil.parser.parse(start_date)
end = dateutil.parser.parse(end_date)
# get data from yahoo
data = load_from_yahoo(stocks=[security], indexes={}, start=start, end=end)
logging.debug('done loading from yahoo. {} {} {}'.format(
security, start_date, end_date))
# create and run algorithm
algo = TradingAlgorithm(
initialize=initialize,
handle_data=handle_data,
capital_base=initial_cash)
algo.security = security
initialize.low_RSI = low_RSI
initialize.high_RSI = high_RSI
initialize.rsi_window = rsi_window
logging.debug('starting to run algo...')
results = algo.run(data).dropna()
logging.debug('done running algo')
return results
if __name__ == '__main__':
import matplotlib.pyplot as plt
# run algorithm and get results
results = run_algorithm(
security='AAPL',
start_date='20100101',
end_date='20150101',
initial_cash=100000,
rsi_window=15,
low_RSI=30,
high_RSI=70)
# get s&p500 and nasdaq indexes
index_data = load_from_yahoo(
stocks=['^gspc', '^ixic'],
indexes={},
start=results.index[0],
end=results.index[-1])
# portfolio value, stock holdings and S&P 500 index
fig = plt.figure(figsize=(12, 6))
ax11 = fig.add_subplot(311)
ax12, ax13 = ax11.twinx(), ax11.twinx()
ax13.spines['right'].set_position(('axes', 1.07))
ax11.set_ylabel('portfolio value', color='blue')
ax12.set_ylabel('holdings', color='green')
ax13.set_ylabel('S&P 500', color='red')
# portfolio value
ax11.plot(results.index, results.portfolio_value, color='blue')
# holdings (number of stocks owned)
holdings = [0 if t == [] else t[0]['amount'] for t in results.positions]
ax12.plot(results.index, holdings, color='green')
ax12.set_ylim([min(holdings) - 30, max(holdings) + 30])
# index
ax13.plot(index_data.index, index_data['^gspc'], color='red')
# algo visualization
ax21 = fig.add_subplot(312)
ax21.set_ylabel('stock price', color='blue')
ax22 = ax21.twinx()
ax22.set_ylabel('rsi', color='red')
# stock
ax21.plot(results.index, results.price, color='blue')
# add sell and buy flags on top of stock price
ax21.plot(
results.ix[results.buy].index,
results.price[results.buy],
'^',
markersize=10,
color='green')
ax21.plot(
results.ix[results.sell].index,
results.price[results.sell],
'v',
markersize=10,
color='red')
# rsi value
ax22.plot(results.index, results.secRSI, color='red')
# add lines to show under- and over value indicator
ax22.plot([results.index[0], results.index[-1]], [30, 30], 'k-')
ax22.plot([results.index[0], results.index[-1]], [70, 70], 'k-')
# portfolio value, stock value and index in percentage
ax31 = fig.add_subplot(313)
ax32, ax33 = ax31.twinx(), ax31.twinx() # share x for other plots
ax31.set_ylabel('algo %', color='blue')
ax32.set_ylabel('snp index %', color='green')
ax33.set_ylabel('stock %', color='red')
ax33.spines['right'].set_position(('axes', 1.07))
# portfolio value
ax31.plot(
results.index,
results.portfolio_value / results.portfolio_value[0] * 100 - 100,
color='blue')
# index
ax32.plot(
index_data.index,
index_data['^gspc'] / index_data['^gspc'][0] * 100 - 100,
color='green')
# stock value
ax33.plot(
results.index,
results.price /
results.price[0] * 100 - 100,
color='red')
plt.show()
If you get it working you should see a plot similar to this one:
If you are observant, it is easy to see that the performance of this algorithm is not good enough to be used on a real portfolio, and it is more of a test.
Links: