In this article, we will explain how to design a simple trending strategy, only at the strategy design level, to help beginners learn how to design a simple strategy and understand the strategy execution process. The performance of the strategy is largely related to the strategy parameters (as is the case with almost all trending strategies).
Strategy design
We use two EMA indicators when both EMA averages have turning points. The turning point is used as a signal to open positions (or sell the opening position) for opening long, opening short positions, and a fixed target profit differential position closing is designed. The comments are written directly in the strategy code for convenient reading. The strategy code is generally very short and suitable for beginners to learn.
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"}] */ // The above /**/ is the default setting for backtesting, you can reset it on the backtesting page by using the relevant controls var LONG = 1 // Markers for holding long positions, enum constant var SHORT = -1 // Markers for holding short positions, enum constant var IDLE = 0 // Markers without holding positions, enum constant // Obtain positions in the specified direction. Positions is the position data, and direction is the position direction to be obtained function getPosition(positions, direction) { var ret = {Price : 0, Amount : 0, Type : ""} // Define a structure when no position is held // Iterate through the positions and find the positions that match the direction _.each(positions, function(pos) { if (pos.Type == direction) { ret = pos } }) // Return to found positions return ret } // Cancel all makers of current trading pairs and contracts function cancellAll() { // Dead loop, keep detecting until break is triggered while (true) { // Obtain the makers' data of the current trading pair and contract, i.e. orders var orders = _C(exchange.GetOrders) if (orders.length == 0) { // When orders is an empty array, i.e. orders.length == 0, break is executed to exit the while loop break } else { // Iterate through all current makers and cancel them one by one for (var i = 0 ; i < orders.length ; i++) { // The function to cancel the specific order, cancel the order with ID: orders[i].Id exchange.CancelOrder(orders[i].Id, orders[i]) Sleep(500) } } Sleep(500) } } // The closing function, used for closing positions according to the trading function-tradeFunc and direction passed in 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 in the specified closing position direction // When the position is greater than 0 (the position can be closed only when there is a position) if (pos.Amount > 0) { // Cancel all possible makers cancellAll() // Set the trading direction exchange.SetDirection(direction) // Execute closing position trade functions if (tradeFunc(-1, pos.Amount)) { // Return to true if the order is placed return true } else { // Return to false if the order is failed to place return false } } // Return to true if there is no position return true } // Strategy main functions function main() { // For switching to OKEX V5 Demo if (okexSimulate) { exchange.IO("simulate", true) // Switch to OKEX V5 Demo for a test Log("Switch to OKEX V5 Demo") } // Set the contract code, if ct is swap, set the current contract to be a perpetual contract exchange.SetContractType(ct) // Initialization status is open position var state = IDLE // The initialized position price is 0 var holdPrice = 0 // Timestamp for initialization comparison, used to compare whether the current K-Line BAR has changed var preTime = 0 // Strategy main loop while (true) { // Obtain the K-line data for current trading pairs and contracts var r = _C(exchange.GetRecords) // Obtain the length of the K-line data, i.e. l var l = r.length // Judge the K-line length that l must be greater than the indicator period (if not, the indicator function cannot calculate valid indicator data), or it will be recycled if (l < Math.max(ema1Period, ema2Period)) { // Wait for 1,000 milliseconds, i.e. 1 second, to avoid rotating too fast Sleep(1000) // Ignore the code after the current if, and execute while loop again continue } // Calculate ema indicator data var ema1 = TA.EMA(r, ema1Period) var ema2 = TA.EMA(r, ema2Period) // Drawing chart $.PlotRecords(r, 'K-Line') // Drawing the K-line chart // When the last BAR timestamp changes, i.e. when a new K-line BAR is created if(preTime !== r[l - 1].Time){ // The last update of the last BAR before the new BAR appears $.PlotLine('ema1', ema1[l - 2], r[l - 2].Time) $.PlotLine('ema2', ema2[l - 2], r[l - 2].Time) // Draw the indicator line of the new BAR, i.e. the 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 used for comparison preTime = r[l - 1].Time } else { // When no new BARs are generated, only the indicator data of the last BAR on the chart is updated $.PlotLine('ema1', ema1[l - 1], r[l - 1].Time) $.PlotLine('ema2', ema2[l - 1], r[l - 1].Time) } // Conditions for opening long positions, turning points 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]) // Conditions for opening short positions, turning points 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]) // The condition of opening a long position is triggered and the current short position is held, or the condition of opening a long position is triggered and no position is held if (up && (state == SHORT || state == IDLE)) { // If you have a short position, close it first if (state == SHORT && cover(exchange.Buy, "closesell")) { // Mark open positions after closing them state = IDLE // The price of reset position is 0 holdPrice = 0 // Mark on the chart $.PlotFlag(r[l - 1].Time, 'coverShort', 'CS') } // Open a long position after closing the position 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)) { // The same as the judgment 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 profits 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 time on the status bar LogStatus(_D()) Sleep(500) } }
Strategy source code: https://www.fmz.com/strategy/333269
The strategy is for program design tutorial only, please do not use it in the real bot.