Recently, when talking about strategies with my friends, I learned that many strategies written in MyLanguage suffer from flexibility. In many cases, it is necessary to use the standard K-line period that is not provided by the system. For example, the maximum requirement is to use the K-line for 4 hours. This problem has been solved in an article. If you are interested, please take a look: Link. However, in MyLanguage strategy, due to the high encapsulation feature of MyLanguage, it is not flexible to process data by itself. At this time, it is necessary to transplant the strategy idea to other languages.
It is very simple for trend strategy transplant. We can use a sample code to fill in the data calculation part of the code that drives the strategy, and fill in the trading signal trigger conditions.
Reusable sample code:
Take the strategy for OKX futures as an example.
// Global variables var IDLE = 0 var LONG = 1 var SHORT = 2 var OPENLONG = 3 var OPENSHORT = 4 var COVERLONG = 5 var COVERSHORT = 6 var BREAK = 9 var SHOCK = 10 var _State = IDLE var Amount = 0 // Record the number of positions var TradeInterval = 500 // Polling intervals var PriceTick = 1 // Price per jump var Symbol = "this_week" function OnTick(){ // Ticker processing part of the driving strategy // To be filled... // Trading signal trigger processing section // To be filled... // Execution of trading logic var pos = null var price = null var currBar = records[records.length - 1] if(_State == OPENLONG){ pos = GetPosition(PD_LONG) // Determine whether the state is satisfied, and if so, modify the state. if(pos[1] >= Amount){ _State = LONG Amount = pos[1] // Update the actual volume. return } price = currBar.Close - (currBar.Close % PriceTick) + PriceTick * 2 Trade(OPENLONG, price, Amount - pos[1], pos, PriceTick) // (Type, Price, Amount, CurrPos, PriceTick) } if(_State == OPENSHORT){ pos = GetPosition(PD_SHORT) if(pos[1] >= Amount){ _State = SHORT Amount = pos[1] // Update the actual volume. return } price = currBar.Close - (currBar.Close % PriceTick) - PriceTick * 2 Trade(OPENSHORT, price, Amount - pos[1], pos, PriceTick) } if(_State == COVERLONG){ pos = GetPosition(PD_LONG) if(pos[1] == 0){ _State = IDLE return } price = currBar.Close - (currBar.Close % PriceTick) - PriceTick * 2 Trade(COVERLONG, price, pos[1], pos, PriceTick) } if(_State == COVERSHORT){ pos = GetPosition(PD_SHORT) if(pos[1] == 0){ _State = IDLE return } price = currBar.Close - (currBar.Close % PriceTick) + PriceTick * 2 Trade(COVERSHORT, price, pos[1], pos, PriceTick) } } // Trading logic section function GetPosition(posType) { var positions = _C(exchange.GetPosition) var count = 0 for(var j = 0; j < positions.length; j++){ if(positions[j].ContractType == Symbol){ count++ } } if(count > 1){ throw "positions error:" + JSON.stringify(positions) } for (var i = 0; i < positions.length; i++) { if (positions[i].ContractType == Symbol && positions[i].Type === posType) { return [positions[i].Price, positions[i].Amount]; } } Sleep(TradeInterval); return [0, 0]; } function CancelPendingOrders() { while (true) { var orders = _C(exchange.GetOrders) for (var i = 0; i < orders.length; i++) { exchange.CancelOrder(orders[i].Id); Sleep(TradeInterval); } if (orders.length === 0) { break; } } } function Trade(Type, Price, Amount, CurrPos, OnePriceTick){ // Processing transactions if(Type == OPENLONG || Type == OPENSHORT){ // Processing of opening positions exchange.SetDirection(Type == OPENLONG ? "buy" : "sell") var pfnOpen = Type == OPENLONG ? exchange.Buy : exchange.Sell var idOpen = pfnOpen(Price, Amount, CurrPos, OnePriceTick, Type) Sleep(TradeInterval) if(idOpen) { exchange.CancelOrder(idOpen) } else { CancelPendingOrders() } } else if(Type == COVERLONG || Type == COVERSHORT){ // Processing of closing positions exchange.SetDirection(Type == COVERLONG ? "closebuy" : "closesell") var pfnCover = Type == COVERLONG ? exchange.Sell : exchange.Buy var idCover = pfnCover(Price, Amount, CurrPos, OnePriceTick, Type) Sleep(TradeInterval) if(idCover){ exchange.CancelOrder(idCover) } else { CancelPendingOrders() } } else { throw "Type error:" + Type } } function main() { // Set up the contract exchange.SetContractType(Symbol) while(1){ OnTick() Sleep(1000) } }
Example: Transplantation of double EMA strategy
MyLanguage backtest:
MyLanguage strategy code:
MA5^^MA(C,5); MA15^^MA(C,15); CROSSUP(MA5,MA15),BPK; CROSSDOWN(MA5,MA15),SPK;
Transplant to JavaScript Strategy
First, fill in the ticker acquisition and indicator calculation parts for the reusable sample code:
// The ticker processing part of the driving strategy var records = _C(exchange.GetRecords) if (records.length < 15) { return } var ma5 = TA.MA(records, 5) var ma15 = TA.MA(records, 15) var ma5_pre = ma5[ma5.length - 3] var ma15_pre = ma15[ma15.length - 3] var ma5_curr = ma5[ma5.length - 2] var ma15_curr = ma15[ma15.length - 2]
As you can see, the double EMA strategy is very simple. First, obtain the K-line data records
, and then use the EMA function TA.MA
of TA function library
to calculate the 5-day EMA and the 15-day EMA (as we can see in the backtest interface, the K-line period is set to the daily K-line, so TA.MA (records, 5)
is to calculate the 5-day EMA, TA.MA (records, 15)
is to calculate the 15-day EMA).
Then get the penultimate point ma5_curr
(indicator value), the last third point ma5_pre
(indicator value) of the indicator data ma5
, and the same for the ma15
indicator data. Then we can use these indicator data to judge the Golden Cross and Bearish Crossover, as shown in the figure:
Whenever such a state is formed, it is a definite Golden Cross or Bearish Crossover.
Then the part of judging the signal can be written as follows:
if(_State == IDLE && ma5_pre < ma15_pre && ma5_curr > ma15_curr){ _State = OPENLONG Amount = 1 } if(_State == IDLE && ma5_pre > ma15_pre && ma5_curr < ma15_curr){ _State = OPENSHORT Amount = 1 } if(_State == LONG && ma5_pre > ma15_pre && ma5_curr < ma15_curr){ _State = COVERLONG Amount = 1 } if(_State == SHORT && ma5_pre < ma15_pre && ma5_curr > ma15_curr){ _State = COVERSHORT Amount = 1 }
In this way, the transplant is OK. We can backtest:
Backtesting of JavaScript strategy
Backtesting configuration:
Backtesting result:
https://www.fmz.com/upload/asset/16baa65d35e034e06a58.png
Backtesting of MyLanguage
It can be seen that the results of the backtest are almost the same. In this way, if you want to continue to add interactive functions, data processing (such as K-line synthesis), and customized chart display to the strategy, you can achieve this.
If you are interested, please try.