Quantopian zipline trading algorithm parameter optimization with Spearmint Bayesian Optimizer - Part 2

The zipline version of the algorithm is shown in figure 2 and can be downloaded here :

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

44

44

45

46

47

48

from zipline.api import (add_history,

     history,

     order_target_percent,

     record,

     symbol)

import talib

def initialize(context):

   context.secs = [ symbol('SPY') ]

   context.history_depth  = 30

   context.iwarmup       = 0

   add_history(30, '1d', 'price')

   context.BBANDS_timeperiod = 16

   context.BBANDS_nbdevup    = 1.819

   context.BBANDS_nbdevdn    = 1.470

   UseParams = False

   try:

      Nparams=len(context.algo_params)

      if Nparams ==  3 :

         UseParams = True

      else:

         print 'len context.algo_params is',Nparams,' expecting 3'

   except Exception as e:

      print 'context.algo_params not passed'

   if UseParams:

      print 'Setting Algo parameters via passed algo_params'

      context.BBANDS_timeperiod = context.algo_params['timeperiod']

      context.BBANDS_nbdevup    = context.algo_params['nbdevup']

      context.BBANDS_nbdevdn    = context.algo_params['nbdevdn']

def handle_data(context, data):

   context.iwarmup = context.iwarmup + 1

   if context.iwarmup <= ( context.history_depth + 1 ) :

      return

   dfHistD = history(30, '1d', 'price')

   S=context.secs[0]

   CurP=data[S].price

   BolU,BolM,BolL = talib.BBANDS(

      dfHistD[S].values,

      timeperiod=context.BBANDS_timeperiod,

      nbdevup=context.BBANDS_nbdevup,

      nbdevdn=context.BBANDS_nbdevdn,

      matype=0)

   record(CurP=CurP,BolU=BolU[-1],BolM=BolM[-1],BolL=BolL[-1])

   if CurP < BolL[-1] :

      order_target_percent(S,+0.97)

   else:

      if CurP > BolU[-1] :

         order_target_percent(S,-0.97)

   return

 

Figure 2 : Zipline version of the BBANDS Trading Algorithm ready for spearmint

 

Lines 15 to 28 were added to allow the spearmint optimizer to pass experiment parameters to BBANDS.  Stock zipline does not have “algo_parms” attached to context (eg, Trading Algorithm object) so the try statement will catch the error and use the hardcoded tuning constants from lines 12 to 14.

 

To pass parameters to the BBANDS algo, we must first patch zipline in a minor way (patch can be downloaded here and here ):

 

quant@QUANT2-> pwd

/usr/local/lib/python2.7/dist-packages

 

quant@QUANT2-> diff -Naur zipline/algorithm.py_orig zipline/algorithm.py    

--- zipline/algorithm.py_orig   2015-01-06 07:09:22.000000000 -0500

+++ zipline/algorithm.py        2015-01-06 07:54:32.000000000 -0500

@@ -167,6 +167,9 @@

         # set the capital base

         self.capital_base = kwargs.pop('capital_base', DEFAULT_CAPITAL_BASE)

 

+        # set the algo_params

+        self.algo_params = kwargs.pop('algo_params')

+

         self.sim_params = kwargs.pop('sim_params', None)

         if self.sim_params is None:

             self.sim_params = create_simulation_parameters(

 

quant@QUANT2-> diff -Naur zipline/utils/cli.py_orig zipline/utils/cli.py

--- zipline/utils/cli.py_orig   2015-01-06 07:26:56.000000000 -0500

+++ zipline/utils/cli.py        2015-01-06 07:53:51.000000000 -0500

@@ -101,6 +101,7 @@

     parser.add_argument('--start', '-s')

     parser.add_argument('--end', '-e')

     parser.add_argument('--capital_base')

+    parser.add_argument('--algo_params')

     parser.add_argument('--source', choices=('yahoo',))

     parser.add_argument('--symbols')

     parser.add_argument('--output', '-o')

@@ -191,6 +192,7 @@

     algo = zipline.TradingAlgorithm(script=algo_text,

                                     namespace=kwargs.get('namespace', {}),

                                     capital_base=float(kwargs['capital_base']),

+                                    algo_params=kwargs['algo_params'],

                                     algo_filename=kwargs.get('algofile'))

 

     perf = algo.run(source)

Figure 3 : Patching zipline to pass parameters to a trading algorithm

 

 

We must also create a spearmint compatible version of zipline’s /usr/bin/run_algo.py.  This can be downloaded here and is shown in Figure 4.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

#!/usr/bin/python

import sys

from zipline.utils import parse_args, run_pipeline

def main(job_id,D):

    parsed={}

    parsed['symbols']='SPY'

    parsed['start']='2014-01-01'

    parsed['end']='2014-12-31'

    parsed['algofile']= '/home/quant/pta/py/algos/BBANDS-zipday.py'

    parsed['capital_base']='100000'

 

    parsed['data_frequency']='daily'

    parsed['conf_file']= None

    parsed['source']='yahoo'

    parsed['output']= None

    parsed['risk']= None

 

    # Below what we expect spearmint to pass us

    # parsed['algo_params']=[47,88.7,7.7]

    # D={}

    # D['timeperiod']=10

    # D['nbdevup']=1.00

    # D['nbdevdn']=1.00

    parsed['algo_params']=D

 

    perf = run_pipeline(print_algo=False, **parsed)

 

    StartV=perf['portfolio_value'][ 0]

 

    EndV=perf['portfolio_value'][-1]

 

    # spearmint wants to minimize so return negative profit

    BBANDS=(StartV-EndV)

 

    return BBANDS

 

if __name__ == "__main__":

    # Below will be overridden by spearmint when it runs

    main(47, {'timeperiod':10,'nbdevup':1.00,'nbdevdn':1.00})

 

Figure 4 : BBANDS.py – The python script that spearmint will call to perform experiments on

 

The logic flow of passing parameters (spearmint experiments) through to zipline can now be described:

1

Figure 4 : Spearmint will read in BBANDS.py, and create experiments by replacing line 39 with the experimental data.  I use a python dictionary construction for the parameters to pass.

 

2

Figure 4 : The dictionary is picked up as variable D in line 4, and added to the dictionary parsed in line 24.

 

3

Figure 3 : The patch attaches dictionary of algo parameters to the TradingAlgorithm object.

 

4

Figure 2 : The zipline version of the algorithm can now read the passed parameters (lines 15 to 28) and use them in the handle_data section of the algorithm .

 

5

Figure 4 : At the completion of the zipline run, the standard zipline perf dataframe is retuned.  The opening and closing portfolio value is used to calculate the return.  Spearmint is designed to minimize the objective function, so we return the a negative profit  lines 24 to 33.

 

Click Part 3 where we configure spearmint to optimize our algorithm. Or, click to go back to Part 1 .