본문 바로가기

Data Mining & R

Algorithmic trading with Python

http://sebastiannilsson.com/en/blogg/algorithmic-trading-with-python/


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.

  1. import talib
  2. from zipline.api import record, order_target, history, add_history
  3. import dateutil
  4. import logging
  5. from zipline.utils.factory import load_from_yahoo
  6. from zipline.finance.slippage import FixedSlippage
  7. from zipline.algorithm import TradingAlgorithm
  8. from zipline.finance import commission
  9. logging.basicConfig(level=logging.DEBUG)
  10. # initialize algorithm
  11. def initialize(context):
  12. logging.debug('enter initialize')
  13. context.set_slippage(FixedSlippage())
  14. context.set_commission(commission.PerTrade(cost=5))
  15. context.LOW_RSI = initialize.low_RSI
  16. context.HIGH_RSI = initialize.high_RSI
  17. context.rsi_window = initialize.rsi_window
  18. add_history(context.rsi_window, '1d', 'price')
  19. context.i = 0
  20. context.invested = False
  21. # default parameters for algorithm
  22. initialize.rsi_window = 15
  23. initialize.low_RSI = 30
  24. initialize.high_RSI = 70
  25. # Will be called on every trade event for the securities you specify.
  26. def handle_data(context, data):
  27. logging.debug('enter handle_data')
  28. context.i += 1
  29. if context.i < context.rsi_window:
  30. return
  31. # get the last RSI value
  32. prices = history(context.rsi_window, '1d', 'price')
  33. sec_rsi = talib.RSI(
  34. prices[context.security].values,
  35. timeperiod=context.rsi_window - 1)
  36. # buy and sell flags
  37. buy = False
  38. sell = False
  39. if sec_rsi[-1] < context.LOW_RSI and not context.invested:
  40. # RSI under 30 indicates oversold, time to buy
  41. order_target(context.security, 1000)
  42. logging.debug('Buying {}'.format(context.security))
  43. context.invested = True
  44. buy = True
  45. elif sec_rsi[-1] > context.HIGH_RSI and context.invested:
  46. # RSI over 70 indicates overbought, sell everything
  47. order_target(context.security, 0)
  48. logging.debug('Selling {}'.format(context.security))
  49. context.invested = False
  50. sell = True
  51. # record data for each time increment
  52. record(secRSI=sec_rsi[-1],
  53. price=data[context.security].price,
  54. buy=buy,
  55. sell=sell)
  56. logging.info(context.portfolio.cash)
  57. def run_algorithm(
  58. security='AAPL',
  59. start_date='20100101',
  60. end_date='20150101',
  61. initial_cash=100000,
  62. rsi_window=15,
  63. low_RSI=30,
  64. high_RSI=70):
  65. logging.debug('run_algorithm begin')
  66. # dates
  67. start = dateutil.parser.parse(start_date)
  68. end = dateutil.parser.parse(end_date)
  69. # get data from yahoo
  70. data = load_from_yahoo(stocks=[security], indexes={}, start=start, end=end)
  71. logging.debug('done loading from yahoo. {} {} {}'.format(
  72. security, start_date, end_date))
  73. # create and run algorithm
  74. algo = TradingAlgorithm(
  75. initialize=initialize,
  76. handle_data=handle_data,
  77. capital_base=initial_cash)
  78. algo.security = security
  79. initialize.low_RSI = low_RSI
  80. initialize.high_RSI = high_RSI
  81. initialize.rsi_window = rsi_window
  82. logging.debug('starting to run algo...')
  83. results = algo.run(data).dropna()
  84. logging.debug('done running algo')
  85. return results
  86. if __name__ == '__main__':
  87. import matplotlib.pyplot as plt
  88. # run algorithm and get results
  89. results = run_algorithm(
  90. security='AAPL',
  91. start_date='20100101',
  92. end_date='20150101',
  93. initial_cash=100000,
  94. rsi_window=15,
  95. low_RSI=30,
  96. high_RSI=70)
  97. # get s&p500 and nasdaq indexes
  98. index_data = load_from_yahoo(
  99. stocks=['^gspc', '^ixic'],
  100. indexes={},
  101. start=results.index[0],
  102. end=results.index[-1])
  103. # portfolio value, stock holdings and S&P 500 index
  104. fig = plt.figure(figsize=(12, 6))
  105. ax11 = fig.add_subplot(311)
  106. ax12, ax13 = ax11.twinx(), ax11.twinx()
  107. ax13.spines['right'].set_position(('axes', 1.07))
  108. ax11.set_ylabel('portfolio value', color='blue')
  109. ax12.set_ylabel('holdings', color='green')
  110. ax13.set_ylabel('S&P 500', color='red')
  111. # portfolio value
  112. ax11.plot(results.index, results.portfolio_value, color='blue')
  113. # holdings (number of stocks owned)
  114. holdings = [0 if t == [] else t[0]['amount'] for t in results.positions]
  115. ax12.plot(results.index, holdings, color='green')
  116. ax12.set_ylim([min(holdings) - 30, max(holdings) + 30])
  117. # index
  118. ax13.plot(index_data.index, index_data['^gspc'], color='red')
  119. # algo visualization
  120. ax21 = fig.add_subplot(312)
  121. ax21.set_ylabel('stock price', color='blue')
  122. ax22 = ax21.twinx()
  123. ax22.set_ylabel('rsi', color='red')
  124. # stock
  125. ax21.plot(results.index, results.price, color='blue')
  126. # add sell and buy flags on top of stock price
  127. ax21.plot(
  128. results.ix[results.buy].index,
  129. results.price[results.buy],
  130. '^',
  131. markersize=10,
  132. color='green')
  133. ax21.plot(
  134. results.ix[results.sell].index,
  135. results.price[results.sell],
  136. 'v',
  137. markersize=10,
  138. color='red')
  139. # rsi value
  140. ax22.plot(results.index, results.secRSI, color='red')
  141. # add lines to show under- and over value indicator
  142. ax22.plot([results.index[0], results.index[-1]], [30, 30], 'k-')
  143. ax22.plot([results.index[0], results.index[-1]], [70, 70], 'k-')
  144. # portfolio value, stock value and index in percentage
  145. ax31 = fig.add_subplot(313)
  146. ax32, ax33 = ax31.twinx(), ax31.twinx() # share x for other plots
  147. ax31.set_ylabel('algo %', color='blue')
  148. ax32.set_ylabel('snp index %', color='green')
  149. ax33.set_ylabel('stock %', color='red')
  150. ax33.spines['right'].set_position(('axes', 1.07))
  151. # portfolio value
  152. ax31.plot(
  153. results.index,
  154. results.portfolio_value / results.portfolio_value[0] * 100 - 100,
  155. color='blue')
  156. # index
  157. ax32.plot(
  158. index_data.index,
  159. index_data['^gspc'] / index_data['^gspc'][0] * 100 - 100,
  160. color='green')
  161. # stock value
  162. ax33.plot(
  163. results.index,
  164. results.price /
  165. results.price[0] * 100 - 100,
  166. color='red')
  167. plt.show()

If you get it working you should see a plot similar to this one:
Skärmavbild 2015-04-04 kl. 22.20.20

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: