動向平均に基づく戦略をフォローする傾向

作者: リン・ハーンチャオチャン,日付: 2024-01-24 14:24:36
タグ:

img

概要

移動平均をベースにしたシンプルなトレンドフォロー戦略である.異なるサイクルの移動平均のサイズ関係を比較することによって,現在のトレンドの方向性と期間を判断する.短いサイクルの移動平均が長いサイクルの上を横切ると長くなって,逆が起こるときに短くなってしまう.同時に,ストップ・ロストとテイク・プロフィートポイントはリスクを制御するために設定されている.

戦略の論理

この戦略は,5日,10日,15日および25日間の異なるサイクルの4つの移動平均値を使用しています.これらはMA1,MA2,MA3およびMA4と呼ばれます. MA1は最短でMA4は最長です.

MA1>MA2>MA3>MA4は上昇傾向を示し,ロングになります.MA1

ロングとショートの両方のオープンポジション条件は,ATRストップ損失フィルタを同時に満たす必要がある.つまり,ATR値は,ATRの40日SMAよりも大きくなければならない.これは価格変動が小さすぎるときに間違った信号を生成することを避ける.

利点分析

この戦略には以下の利点があります.

  1. 論理はシンプルで 実行も簡単です
  2. トレンド方向を決定するために複数の移動平均値を使用することは信頼性があります
  3. ストップ・ロストとテイク・プロフィートポイントを設定することで,取引毎の最大損失を効果的に制御できます.
  4. ATRストップ損失フィルターは,価格変動が小さいときに誤った信号を回避します.

リスク分析

この戦略には次のリスクもあります

  1. ショック市場では 間違った信号を出すのは簡単です
  2. 不適切なパラメータ設定 (移動平均のサイクルなど) は,戦略の不良なパフォーマンスを引き起こす可能性があります.
  3. 基本値や重要なニュースの価格への影響は考慮されません.

これらのリスクを軽減するために,パラメータを適切に最適化したり,戦略の安定性を向上させるために追加のフィルター条件を追加したりすることができます.

オプティマイゼーションの方向性

戦略の最適化方向には,以下の内容が含まれます.

  1. 移動平均周期パラメータの異なる組み合わせをテストして最適なパラメータを見つけます.
  2. 信号の信頼性を判断するためにMACDやKDJなどの他の技術指標フィルターを追加します.
  3. 取引量フィルターを追加します. 取引量が拡大するときにのみ取引します.
  4. 品種間の違いに基づいて微調整するパラメータ
  5. 信号を判断する機械学習アルゴリズムを追加します

結論

一般的には,これは比較的単純なトレンドフォロー戦略である.移動平均値を通じてトレンド方向を判断し,リスクレベルを制御するために合理的なストップロストとテイクプロフィートを設定する. 戦略の安定性と収益性をさらに向上させるために,パラメータを調整し,フィルターを追加するなど,最適化のための余地はまだたくさんあります.


/*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)

もっと