admin

Guideline for Strategy Parameter Optimizer

Programming


Overview

It is common that a trading algorithm involves a number of user defined trading parameters. To optimize the algorithm's trading performance, developers usually need to tweet the parameters, re-run backtest, and compare the results manually.

ALGOGENE is excited to announce a new system feature to support such parameter optimization process!


Preparation

Before going into the Strategy Optimization function, all we need to do is to develop a backtest script as usual. Let's borrow the code from this article [A simple RSI strategy].

  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
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
from AlgoAPI import AlgoAPIUtil, AlgoAPI_Backtest
from datetime import datetime, timedelta
from talib import RSI
import numpy as np


class AlgoEvent:
    def __init__(self):
        self.timer = datetime(1970,1,1)
        self.rsi_period = 14
        self.rsi_overbought = 70
        self.rsi_oversold = 30
        self.position = 0
        self.last_tradeID = ""
        self.instrument = "USDJPY"
        
    def start(self, mEvt):
        self.evt = AlgoAPI_Backtest.AlgoEvtHandler(self, mEvt)
        self.evt.start()
        
    def on_marketdatafeed(self, md, ab):
        # execute stratey every 24 hours
        if md.timestamp >= self.timer+timedelta(hours=24):
            # get last 14 closing price
            res = self.evt.getHistoricalBar({"instrument":self.instrument}, self.rsi_period+1, "D")
            arr = [res[t]['c'] for t in res]
            
            # calculate the current RSI value
            RSI_cur = RSI(np.array(arr), self.rsi_period)[-1]
            
            # print out RSI value to console
            self.evt.consoleLog("RSI_cur = ", RSI_cur)
            
            # open an order if we have no outstanding position
            if self.position==0:
                
                # open a sell order if it is overbought
                if RSI_cur>self.rsi_overbought:
                    self.open_order(-1)
                    
                # open a buy order if it is oversold
                elif RSI_cur<self.rsi_oversold:
                    self.open_order(1)
            
            # check condition to close an order
            else:
                
                # close a position if we have previously open a buy order and RSI now reverse above 50 
                if self.position>0 and RSI_cur>50:
                    self.close_order()
                
                # close a position if we have previously open a sell order and RSI now reverse below 50 
                elif self.position<0 and RSI_cur<50:
                    self.close_order()
            
            # update timer
            self.timer = md.timestamp


    def open_order(self, buysell):
        order = AlgoAPIUtil.OrderObject()
        order.instrument = self.instrument
        order.openclose = 'open'
        order.buysell = buysell    #1=buy, -1=sell
        order.ordertype = 0  #0=market, 1=limit
        order.volume = 0.01
        self.evt.sendOrder(order)
        
    def close_order(self):
        order = AlgoAPIUtil.OrderObject()
        order.openclose = 'close'
        order.tradeID = self.last_tradeID
        self.evt.sendOrder(order)
        

    def on_bulkdatafeed(self, isSync, bd, ab):
        pass

    def on_newsdatafeed(self, nd):
        pass

    def on_weatherdatafeed(self, wd):
        pass
    
    def on_econsdatafeed(self, ed):
        pass
        
        
    def on_orderfeed(self, of):
        # when system confirm an order, update last_tradeID and position
        if of.status=="success":
            self.position += of.fill_volume*of.buysell
            if self.position==0:
                self.last_tradeID = ""
            else:
                self.last_tradeID = of.tradeID
            
    def on_dailyPLfeed(self, pl):
        pass

    def on_openPositionfeed(self, op, oo, uo):
        pass

Optimization

  • First of all, we need to backtest our strategy script to create a "finished" backtest record. It is to ensure that our base strategy is bug-free.
  • Go to [My History] > [Finished Backtest], under your base strategy, click "Optimize"

  • history

  • It will load our base strategy in left panel, we then need to set the following, and click "Optimize"
    • Optimization objective
    • Optimization method
    • The parameters set to optimize
      • from a starting value to the end value inclusively, with an incremental step size
      • 'Value' specifies the initial estimate for the optimization problem

    setup

  • It will take some time for the engine to generate the backtest results. Depending on the complexity of our base strategy and the grid size of our parameter sets, the overall optimization process may ranging from a few minutes to a couple of days.

  • Upon the optimization process finished, we can obtain the
    • performance summary
    • summary

    • visualization charts
    • cummulative returns
    • return


Load optimization results

When we reload [My History] page, the optimization results can be found under the same group ID. We can load to see the backtest result, or compare with other optimization results.

history


Demo Video



Now, we got a new tool for speed up the algo development process! Happy Trading!


 
Nic C
Cool feature!