This article explains the design of a simple trend strategy, only from the level of strategy design, to help beginners learn how to design a simple strategy and understand the execution process of the strategy program. As for the quality of strategy performance, it is largely related to strategy parameters (which is the case for almost most trend strategies).
Strategy Design
When use the two EMA indicators and both moving averages have a breakpoint, the breakpoints are used as a signal to open long and short positions (or reverse), to design a fixed target profit spread close positions. Remarks are written directly in the strategy code for easy reading. The overall strategy code is very short and suitable for beginners.
Strategy Code
/*backtest start: 2021-09-01 00:00:00 end: 2021-12-02 00:00:00 period: 1h basePeriod: 5m exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}] */ // above /**/ inside these are the default backtest settings; the backtest page can be reset by the related controls on the backtest page var LONG = 1 // mark of holding long positions; enum constant var SHORT = -1 // mark of holding short positions; enum constant var IDLE = 0 // mark of not holding no position; enum constant // obtain the positions with a specified direction; positions indicates position data; direction indicates the direction of the positions to be obtained function getPosition(positions, direction) { var ret = {Price : 0, Amount : 0, Type : ""} // define a structure when holding no position // traverse positions, among which find the positions conforming to the direction if (pos.Type == direction) { ret = pos } }) // return the positions found return ret } // cancel all pending orders of the current trading pair and contract function cancellAll() { // infinite loop, detect without stop, until break is triggered while (true) { // obtain the pending orders data of the current trading pair and contract, namely orders var orders = _C(exchange.GetOrders) if (orders.length == 0) { // when orders is a null array, namely orders.length == 0, execute "break" statement to break the while loop break } else { // traverse all the current pending orders, and cancel them one by one for (var i = 0 ; i < orders.length ; i++) { // the function to cancel a specified order, canceling the order with an ID of:orders[i].Id exchange.CancelOrder(orders[i].Id, orders[i]) Sleep(500) } } Sleep(500) } } // function of closing position, which executes close position, according to the passed tradeFunc and direction function cover(tradeFunc, direction) { var mapDirection = {"closebuy": PD_LONG, "closesell": PD_SHORT} var positions = _C(exchange.GetPosition) // obtain the position data of the current trading pair and contract var pos = getPosition(positions, mapDirection[direction]) // find the position information of the specified close position direction // when position volume is over 0 (only when there is a position, you can operate close position) if (pos.Amount > 0) { // cancel all possibly existing pending orders cancellAll() // set trading direction exchange.SetDirection(direction) // execute close position tradefunc if (tradeFunc(-1, pos.Amount)) { // if ordering successfully, return true return true } else { // if the ordering fails, return false return false } } // no position returns true return true } // strategy main function function main() { // used to switch to OKEX V5 simulated bot if (okexSimulate) { exchange.IO("simulate", true) // switch to OKEX V5 simulated bot to test Log("switch to OKEX V5 simulated bot") } // set contract code; set ct to swap, namely set the current operated contract to a perpetual contract exchange.SetContractType(ct) // the initial status is no position var state = IDLE // the initial position price is 0 var holdPrice = 0 // innitialize the timestamp for comparison, to compare whether the current K-line BAR changes var preTime = 0 // strategy main loop while (true) { // obtain the K-line data of the current trading pair and contract var r = _C(exchange.GetRecords) // obtain the K-line length, namely 1 var l = r.length // judge the K-line length of 1, which has to be longer than the indicator period (if the length is less than the indicator period, the indicator function cannot calculate the effective indicatorr data); if not, the loop will be restarted if (l < Math.max(ema1Period, ema2Period)) { // wait 1000 miliseconds, namely 1 second, to avoid rotating too fast Sleep(1000) // ignore the code after "if" at the moment, and restart the while loop continue } // calculate EMA indicator data var ema1 = TA.EMA(r, ema1Period) var ema2 = TA.EMA(r, ema2Period) // plot $.PlotRecords(r, 'K-line') // draw K-line chart // when the timestamp of the last BAR changes, namely when the new K-line BAR is generated if(preTime !== r[l - 1].Time){ // before the new K-line BAR is generated, it is the last update of the last BAR $.PlotLine('ema1', ema1[l - 2], r[l - 2].Time) $.PlotLine('ema2', ema2[l - 2], r[l - 2].Time) // draw the EMA lines of the new BAR, namely the EMA indicator data on the current last BAR $.PlotLine('ema1', ema1[l - 1], r[l - 1].Time) $.PlotLine('ema2', ema2[l - 1], r[l - 1].Time) // update the timestamp for comparison preTime = r[l - 1].Time } else { // at the moment when there is no new BAR generated, just update the EMA indicator data of the last BAR in the chart $.PlotLine('ema1', ema1[l - 1], r[l - 1].Time) $.PlotLine('ema2', ema2[l - 1], r[l - 1].Time) } // condition of open long position, breakpoint var up = (ema1[l - 2] > ema1[l - 3] && ema1[l - 4] > ema1[l - 3]) && (ema2[l - 2] > ema2[l - 3] && ema2[l - 4] > ema2[l - 3]) // condition of open short position, breakpoint var down = (ema1[l - 2] < ema1[l - 3] && ema1[l - 4] < ema1[l - 3]) && (ema2[l - 2] < ema2[l - 3] && ema2[l - 4] < ema2[l - 3]) // when the condition of open long position is triggered and currently short positions are held, or when the condition of open long position is triggered but there is no position if (up && (state == SHORT || state == IDLE)) { // if holding short, close first if (state == SHORT && cover(exchange.Buy, "closesell")) { // after close positions, mark the status of no position state = IDLE // reset the position price to 0 holdPrice = 0 // mark on the chart $.PlotFlag(r[l - 1].Time, 'coverShort', 'CS') } // after close positions, reverse to open long exchange.SetDirection("buy") if (exchange.Buy(-1, amount)) { // mark the current status state = LONG // record the current price holdPrice = r[l - 1].Close $.PlotFlag(r[l - 1].Time, 'openLong', 'L') } } else if (down && (state == LONG || state == IDLE)) { // similar to the judge of up condition if (state == LONG && cover(exchange.Sell, "closebuy")) { state = IDLE holdPrice = 0 $.PlotFlag(r[l - 1].Time, 'coverLong', 'CL') } exchange.SetDirection("sell") if (exchange.Sell(-1, amount)) { state = SHORT holdPrice = r[l - 1].Close $.PlotFlag(r[l - 1].Time, 'openShort', 'S') } } // stop profit if (state == LONG && r[l - 1].Close - holdPrice > profitTarget && cover(exchange.Sell, "closebuy")) { state = IDLE holdPrice = 0 $.PlotFlag(r[l - 1].Time, 'coverLong', 'CL') } else if (state == SHORT && holdPrice - r[l - 1].Close > profitTarget && cover(exchange.Buy, "closesell")) { state = IDLE holdPrice = 0 $.PlotFlag(r[l - 1].Time, 'coverShort', 'CS') } // display the time on the status bar LogStatus(_D()) Sleep(500) } }
/upload/asset/269828ec3ccbeba3c0bfc.png
/upload/asset/2694caa2a8b6af5ac7faa.png
Strategy source code: https://www.fmz.com/strategy/333269
The strategy is only a teaching of program design, so please do not use it in a bot.