Esta é uma estratégia simples de tendência baseada na média móvel. Ele julga a direção e duração da tendência atual comparando a relação de tamanho entre as médias móveis de diferentes ciclos. Ele fica longo quando a média móvel de ciclo curto cruza acima do ciclo longo, e fica curto quando o oposto acontece. Ao mesmo tempo, pontos de stop loss e take profit são definidos para controlar riscos.
A estratégia usa 4 médias móveis com ciclos diferentes: linhas de 5 dias, 10 dias, 15 dias e 25 dias.
Quando MA1>MA2>MA3>MA4, indica uma tendência ascendente e vai longo.
As condições de posição aberta para longo e curto precisam satisfazer o filtro de stop loss ATR ao mesmo tempo, ou seja, o valor ATR deve ser maior que a SMA de 40 dias do ATR. Isso evita gerar sinais errados quando a flutuação do preço é muito pequena.
A estratégia apresenta as seguintes vantagens:
A estratégia apresenta igualmente os seguintes riscos:
Para reduzir esses riscos, os parâmetros podem ser otimizados adequadamente ou podem ser adicionadas condições de filtro adicionais para melhorar a estabilidade da estratégia.
As direcções de otimização da estratégia incluem:
Em geral, esta é uma estratégia de tendência relativamente simples. Ele julga a direção da tendência através de médias móveis e define stop loss e take profit razoáveis para controlar os níveis de risco. Ainda há muito espaço para otimização, como ajustar parâmetros, adicionar filtros etc. para melhorar ainda mais a estabilidade e rentabilidade da estratégia.
/*backtest start: 2023-01-17 00:00:00 end: 2024-01-23 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}] */ // This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © fpemehd // @version=5 // # ========================================================================= # // # | STRATEGY | // # ========================================================================= # strategy(title = 'MA Simple Strategy with SL & TP & ATR Filters', shorttitle = 'MA Strategy', overlay = true, pyramiding = 0, default_qty_type = strategy.percent_of_equity, default_qty_value = 100, commission_type = strategy.commission.percent, commission_value = 0.1, initial_capital = 100000, max_lines_count = 150, max_labels_count = 300) // # ========================================================================= # // # Inputs // # ========================================================================= # // 1. Time i_start = input (defval = timestamp("20 Jan 1990 00:00 +0900"), title = "Start Date", tooltip = "Choose Backtest Start Date", inline = "Start Date", group = "Time" ) i_end = input (defval = timestamp("20 Dec 2030 00:00 +0900"), title = "End Date", tooltip = "Choose Backtest End Date", inline = "End Date", group = "Time" ) c_timeCond = true // 2. Inputs for direction: Long? Short? Both? i_longEnabled = input.bool(defval = true , title = "Long?", tooltip = "Enable Long Position Trade?", inline = "Long / Short", group = "Long / Short" ) i_shortEnabled = input.bool(defval = true , title = "Short?", tooltip = "Enable Short Position Trade?", inline = "Long / Short", group = "Long / Short" ) // 3. Use Filters? What Filters? i_ATRFilterOn = input.bool(defval = true , title = "ATR Filter On?", tooltip = "ATR Filter On?", inline = "ATR Filter", group = "Filters") i_ATRSMALen = input.int(defval = 40 , title = "SMA Length for ATR SMA", minval = 1 , maxval = 100000 , step = 1 , tooltip = "ATR should be bigger than this", inline = "ATR Filter", group = "Filters") // 3. Shared inputs for Long and Short //// 3-1. Inputs for Stop Loss Type: normal? or trailing? //// If trailing, always trailing or trailing after take profit order executed? i_useSLTP = input.bool(defval = true, title = "Enable SL & TP?", tooltip = "", inline = "Enable SL & TP & SL Type", group = "Shared Inputs") i_tslEnabled = input.bool(defval = false , title = "Enable Trailing SL?", tooltip = "Enable Stop Loss & Take Profit? \n\Enable Trailing SL?", inline = "Enable SL & TP & SL Type", group = "Shared Inputs") // i_tslAfterTP = input.bool(defval = true , title = "Enable Trailing SL after TP?", tooltip = "Enable Trailing SL after TP?", inline = "Trailing SL Execution", group = "Shared Inputs") i_slType = input.string(defval = "ATR", title = "Stop Loss Type", options = ["Percent", "ATR"], tooltip = "Stop Loss based on %? ATR?", inline = "Stop Loss Type", group = "Shared Inputs") i_slATRLen = input.int(defval = 14, title = "ATR Length", minval = 1 , maxval = 200 , step = 1, inline = "Stop Loss ATR", group = "Shared Inputs") i_tpType = input.string(defval = "R:R", title = "Take Profit Type", options = ["Percent", "ATR", "R:R"], tooltip = "Take Profit based on %? ATR? R-R ratio?", inline = "Take Profit Type", group = "Shared Inputs") //// 3-2. Inputs for Quantity i_tpQuantityPerc = input.float(defval = 50, title = 'Take Profit Quantity %', minval = 0.0, maxval = 100, step = 1.0, tooltip = '% of position when tp target is met.', group = 'Shared Inputs') // 4. Inputs for Long Stop Loss & Long Take Profit i_slPercentLong = input.float(defval = 3, title = "SL Percent", tooltip = "", inline = "Percent > Long Stop Loss / Take Profit Percent", group = "Long Stop Loss / Take Profit") i_tpPercentLong = input.float(defval = 3, title = "TP Percent", tooltip = "Long Stop Loss && Take Profit Percent?", inline = "Percent > Long Stop Loss / Take Profit Percent", group = "Long Stop Loss / Take Profit") i_slATRMultLong = input.float(defval = 3, title = "SL ATR Multiplier", minval = 1 , maxval = 200 , step = 0.1, tooltip = "", inline = "Long Stop Loss / Take Profit ATR", group = "Long Stop Loss / Take Profit") i_tpATRMultLong = input.float(defval = 3, title = "TP ATR Multiplier", minval = 1 , maxval = 200 , step = 0.1, tooltip = "ATR > Long Stop Loss && Take Profit ATR Multiplier? \n\Stop Loss = i_slATRMultLong * ATR (i_slATRLen) \n\Take Profit = i_tpATRMultLong * ATR (i_tpATRLen)", inline = "Long Stop Loss / Take Profit ATR", group = "Long Stop Loss / Take Profit") i_tpRRratioLong = input.float(defval = 1.8, title = "R:R Ratio", minval = 0.1 , maxval = 200 , step = 0.1, tooltip = "R:R Ratio > Risk Reward Ratio? It will automatically set Take Profit % based on Stop Loss", inline = "R:R Ratio", group = "Long Stop Loss / Take Profit") // 5. Inputs for Short Stop Loss & Short Take Profit i_slPercentShort = input.float(defval = 3, title = "SL Percent", tooltip = "", inline = "Percent > Short Stop Loss / Take Profit Percent", group = "Short Stop Loss / Take Profit") i_tpPercentShort = input.float(defval = 3, title = "TP Percent", tooltip = "Short Stop Loss && Take Profit Percent?", inline = "Percent > Short Stop Loss / Take Profit Percent", group = "Short Stop Loss / Take Profit") i_slATRMultShort = input.float(defval = 3, title = "SL ATR Multiplier", minval = 1 , maxval = 200 , step = 0.1, tooltip = "", inline = "ATR > Short Stop Loss / Take Profit ATR", group = "Short Stop Loss / Take Profit") i_tpATRMultShort = input.float(defval = 3, title = "TP ATR Multiplier", minval = 1 , maxval = 200 , step = 0.1, tooltip = "ATR > Short Stop Loss && Take Profit ATR Multiplier? \n\Stop Loss = i_slATRMultShort * ATR (i_slATRLen) \n\Take Profit = i_tpATRMultShort * ATR (i_tpATRLen)", inline = "ATR > Short Stop Loss / Take Profit ATR", group = "Short Stop Loss / Take Profit") i_tpRRratioShort = input.float(defval = 1.8, title = "R:R Ratio", minval = 0.1 , maxval = 200 , step = 0.1, tooltip = "R:R Ratio > Risk Reward Ratio? It will automatically set Take Profit % based on Stop Loss", inline = "R:R Ratio", group = "Short Stop Loss / Take Profit") // 6. Inputs for logic i_MAType = input.string(defval = "RMA", title = "MA Type", options = ["SMA", "EMA", "WMA", "HMA", "RMA", "VWMA", "SWMA", "ALMA", "VWAP"], tooltip = "Choose MA Type", inline = "MA Type", group = 'Strategy') i_MA1Len = input.int(defval = 5, title = 'MA 1 Length', minval = 1, inline = 'MA Length', group = 'Strategy') i_MA2Len = input.int(defval = 10, title = 'MA 2 Length', minval = 1, inline = 'MA Length', group = 'Strategy') i_MA3Len = input.int(defval = 15, title = 'MA 3 Length', minval = 1, inline = 'MA Length', group = 'Strategy') i_MA4Len = input.int(defval = 25, title = 'MA 4 Length', minval = 1, inline = 'MA Length', group = 'Strategy') i_ALMAOffset = input.float(defval = 0.7 , title = "ALMA Offset Value", tooltip = "The Value of ALMA offset", inline = "ALMA Input", group = 'Strategy') i_ALMASigma = input.float(defval = 7 , title = "ALMA Sigma Value", tooltip = "The Value of ALMA sigma", inline = "ALMA Input", group = 'Strategy') // # ========================================================================= # // # Entry, Close Logic // # ========================================================================= # bool i_ATRFilter = ta.atr(length = i_slATRLen) >= ta.sma(source = ta.atr(length = i_slATRLen), length = i_ATRSMALen) ? true : false // calculate Technical Indicators for the Logic getMAValue (source, length, almaOffset, almaSigma) => switch i_MAType 'SMA' => ta.sma(source = source, length = length) 'EMA' => ta.ema(source = source, length = length) 'WMA' => ta.wma(source = source, length = length) 'HMA' => ta.hma(source = source, length = length) 'RMA' => ta.rma(source = source, length = length) 'SWMA' => ta.swma(source = source) 'ALMA' => ta.alma(series = source, length = length, offset = almaOffset, sigma = almaSigma) 'VWMA' => ta.vwma(source = source, length = length) 'VWAP' => ta.vwap(source = source) => na float c_MA1 = getMAValue(close, i_MA1Len, i_ALMAOffset, i_ALMASigma) float c_MA2 = getMAValue(close, i_MA2Len, i_ALMAOffset, i_ALMASigma) float c_MA3 = getMAValue(close, i_MA3Len, i_ALMAOffset, i_ALMASigma) float c_MA4 = getMAValue(close, i_MA4Len, i_ALMAOffset, i_ALMASigma) // Logic: 정배열 될 떄 들어가 var ma1Color = color.new(color.red, 0) plot(series = c_MA1, title = 'SMA 1', color = ma1Color, linewidth = 1, style = plot.style_line) var ma2Color = color.new(color.orange, 0) plot(series = c_MA2, title = 'SMA 2', color = ma2Color, linewidth = 1, style = plot.style_line) var ma3Color = color.new(color.yellow, 0) plot(series = c_MA3, title = 'SMA 3', color = ma3Color, linewidth = 1, style = plot.style_line) var ma4Color = color.new(color.green, 0) plot(series = c_MA4, title = 'SMA 4', color = ma4Color, linewidth = 1, style = plot.style_line) bool openLongCond = (c_MA1 >= c_MA2 and c_MA2 >= c_MA3 and c_MA3 >= c_MA4) bool openShortCond = (c_MA1 <= c_MA2 and c_MA2 <= c_MA3 and c_MA3 <= c_MA4) bool openLong = i_longEnabled and openLongCond and (not i_ATRFilterOn or i_ATRFilter) bool openShort = i_shortEnabled and openShortCond and (not i_ATRFilterOn or i_ATRFilter) openLongCondColor = openLongCond ? color.new(color = color.blue, transp = 80) : na bgcolor(color = openLongCondColor) ATRFilterColor = i_ATRFilter ? color.new(color = color.orange, transp = 80) : na bgcolor(color = ATRFilterColor) bool enterLong = openLong and not (strategy.opentrades.size(strategy.opentrades-1) > 0) bool enterShort = openShort and not (strategy.opentrades.size(strategy.opentrades-1) < 0) bool closeLong = i_longEnabled and (c_MA1[1] >= c_MA2[1] and c_MA2[1] >= c_MA3[1] and c_MA3[1] >= c_MA4[1]) and not (c_MA1 >= c_MA2 and c_MA2 >= c_MA3 and c_MA3 >= c_MA4) bool closeShort = i_shortEnabled and (c_MA1[1] <= c_MA2[1] and c_MA2[1] <= c_MA3[1] and c_MA3[1] <= c_MA4[1]) and not (c_MA1 <= c_MA2 and c_MA2 <= c_MA3 and c_MA3 <= c_MA4) // # ========================================================================= # // # Position, Status Conrtol // # ========================================================================= # // longisActive: New Long || Already Long && not closeLong, short is the same bool longIsActive = enterLong or strategy.opentrades.size(strategy.opentrades - 1) > 0 and not closeLong bool shortIsActive = enterShort or strategy.opentrades.size(strategy.opentrades - 1) < 0 and not closeShort // before longTPExecution: no trailing SL && after longTPExecution: trailing SL starts // longTPExecution qunatity should be less than 100% bool longTPExecuted = false bool shortTPExecuted = false // # ========================================================================= # // # Long Stop Loss Logic // # ========================================================================= # float openAtr = ta.valuewhen(enterLong or enterShort, ta.atr(i_slATRLen), 0) f_getLongSL (source) => switch i_slType 'Percent' => source * (1 - (i_slPercentLong/100)) 'ATR' => source - i_slATRMultLong * openAtr => na var float c_longSLPrice = na c_longSLPrice := if (longIsActive) if (enterLong) f_getLongSL(close) else c_stopPrice = f_getLongSL(i_tslEnabled ? high : strategy.opentrades.entry_price(trade_num = strategy.opentrades - 1)) math.max(c_stopPrice, nz(c_longSLPrice[1])) else na // # ========================================================================= # // # Short Stop Loss Logic // # ========================================================================= # f_getShortSL (source) => switch i_slType 'Percent' => source * (1 + (i_slPercentShort)/100) 'ATR' => source + i_slATRMultShort * openAtr => na var float c_shortSLPrice = na c_shortSLPrice := if (shortIsActive) if (enterShort) f_getShortSL (close) else c_stopPrice = f_getShortSL(i_tslEnabled ? low : strategy.opentrades.entry_price(strategy.opentrades - 1)) math.min(c_stopPrice, nz(c_shortSLPrice[1], 999999.9)) else na // # ========================================================================= # // # Long Take Profit Logic // # ========================================================================= # f_getLongTP () => switch i_tpType 'Percent' => close * (1 + (i_tpPercentLong/100)) 'ATR' => close + i_tpATRMultLong * openAtr 'R:R' => close + i_tpRRratioLong * (close - f_getLongSL(close)) => na var float c_longTPPrice = na c_longTPPrice := if (longIsActive and not longTPExecuted) if (enterLong) f_getLongTP() else nz(c_longTPPrice[1], f_getLongTP()) else na longTPExecuted := strategy.opentrades.size(strategy.opentrades - 1) > 0 and (longTPExecuted[1] or strategy.opentrades.size(strategy.opentrades - 1) < strategy.opentrades.size(strategy.opentrades - 1)[1] or strategy.opentrades.size(strategy.opentrades - 1)[1] == 0 and high >= c_longTPPrice) // # ========================================================================= # // # Short Take Profit Logic // # ========================================================================= # f_getShortTP () => switch i_tpType 'Percent' => close * (1 - (i_tpPercentShort/100)) 'ATR' => close - i_tpATRMultShort * openAtr 'R:R' => close - i_tpRRratioShort * (close - f_getLongSL(close)) => na var float c_shortTPPrice = na c_shortTPPrice := if (shortIsActive and not shortTPExecuted) if (enterShort) f_getShortTP() else nz(c_shortTPPrice[1], f_getShortTP()) else na shortTPExecuted := strategy.opentrades.size(strategy.opentrades - 1) < 0 and (shortTPExecuted[1] or strategy.opentrades.size(strategy.opentrades - 1) > strategy.opentrades.size(strategy.opentrades - 1)[1] or strategy.opentrades.size(strategy.opentrades - 1)[1] == 0 and low <= c_shortTPPrice) // # ========================================================================= # // # Make Orders // # ========================================================================= # if (c_timeCond) if (enterLong) strategy.entry(id = "Long Entry", direction = strategy.long , comment = 'Long(' + syminfo.ticker + '): Started', alert_message = 'Long(' + syminfo.ticker + '): Started') if (enterShort) strategy.entry(id = "Short Entry", direction = strategy.short , comment = 'Short(' + syminfo.ticker + '): Started', alert_message = 'Short(' + syminfo.ticker + '): Started') if (closeLong) strategy.close(id = 'Long Entry', comment = 'Close Long', alert_message = 'Long: Closed at market price') if (closeShort) strategy.close(id = 'Short Entry', comment = 'Close Short', alert_message = 'Short: Closed at market price') if (longIsActive and i_useSLTP) strategy.exit(id = 'Long Take Profit / Stop Loss', from_entry = 'Long Entry', qty_percent = i_tpQuantityPerc, limit = c_longTPPrice, stop = c_longSLPrice, alert_message = 'Long(' + syminfo.ticker + '): Take Profit or Stop Loss executed') strategy.exit(id = 'Long Stop Loss', from_entry = 'Long Entry', stop = c_longSLPrice, alert_message = 'Long(' + syminfo.ticker + '): Stop Loss executed') if (shortIsActive and i_useSLTP) strategy.exit(id = 'Short Take Profit / Stop Loss', from_entry = 'Short Entry', qty_percent = i_tpQuantityPerc, limit = c_shortTPPrice, stop = c_shortSLPrice, alert_message = 'Short(' + syminfo.ticker + '): Take Profit or Stop Loss executed') strategy.exit(id = 'Short Stop Loss', from_entry = 'Short Entry', stop = c_shortSLPrice, alert_message = 'Short(' + syminfo.ticker + '): Stop Loss executed') // # ========================================================================= # // # Plot // # ========================================================================= # var posColor = color.new(color.white, 0) plot(series = strategy.opentrades.entry_price(strategy.opentrades - 1), title = 'Position', color = posColor, linewidth = 1, style = plot.style_linebr) var stopLossColor = color.new(color.maroon, 0) plot(series = c_longSLPrice, title = 'Long Stop Loss', color = stopLossColor, linewidth = 1, style = plot.style_linebr, offset = 1) plot(series = c_shortSLPrice, title = 'Short Stop Loss', color = stopLossColor, linewidth = 1, style = plot.style_linebr, offset = 1) longTPExecutedColor = longTPExecuted ? color.new(color = color.green, transp = 80) : na //bgcolor(color = longTPExecutedColor) shortTPExecutedColor = shortTPExecuted ? color.new(color = color.red, transp = 80) : na //bgcolor(color = shortTPExecutedColor) // isPositionOpenedColor = strategy.opentrades.size(strategy.opentrades-1) != 0 ? color.new(color = color.yellow, transp = 90) : na // bgcolor(color = isPositionOpenedColor) var takeProfitColor = color.new(color.teal, 0) plot(series = c_longTPPrice, title = 'Long Take Profit', color = takeProfitColor, linewidth = 1, style = plot.style_linebr, offset = 1) plot(series = c_shortTPPrice, title = 'Short Take Profit', color = takeProfitColor, linewidth = 1, style = plot.style_linebr, offset = 1)