トレンドアングル移動平均クロスオーバー戦略

作者: リン・ハーンチャオチャン開催日:2024年1月25日 (月) 14:35:13
タグ:

img

概要

この戦略は,長期および短期取引の価格変動率指標と組み合わせた移動平均値の傾斜角を計算することによって傾向方向を決定する.本質的には,移動平均値の傾斜角を使用して価格傾向を決定し,価格変動率指標を使用して範囲限定市場をフィルタリングするトレンドフォロー戦略である.

戦略の論理

この戦略は主に以下の判断指標に基づいています.

  1. 移動平均傾斜角: 価格トレンド方向を決定するために,ジュリック移動平均と指数関数移動平均の傾斜角を計算する. 0 以上の角度は上昇傾向を示し,0 未満は下落傾向を示します.

  2. 価格変動率: 波動性によってシグナルをフィルタリングするために,最後の12バーにおける閉店価格の変化率を計算します.

移動平均傾斜が上昇し (0以上) 価格変動率が基準を満たす場合,ロングに行く.傾斜が低下し (0未満) 価格変動率が基準を満たす場合,ショートに行く.

具体的には,戦略はまずJurik MAとEMAの傾斜角を計算する.その後,価格変動率指標は,範囲限定期間をフィルタリングするために計算される.移動平均傾斜信号のトレンドと価格変動率の両方が基準を満たしたとき,取引信号が生成される.

利点分析

この戦略の利点は

  1. 傾向を決定するためにMA傾斜を使用することは,良い勝利率で非常に信頼性があります.

  2. 価格変動率指標は,無効な取引を避けるため,変動範囲を効果的にフィルターします.

  3. Jurik MAは突破に迅速に対応し,EMAは安定したトレンド判断を提供し,両者は互いを補完しています.

  4. トレンドマーケットで長回り短回りすると より大きな利益を得ることができます

リスク分析

この戦略のリスクは

  1. 極端な価格ウィップソーでは,MAは間違った信号を生成する可能性があります. これはパラメータ最適化によって軽減できます.

  2. シグナルが頻繁に変化して,不必要な取引が起こる.追加のフィルターを追加することもできます.

  3. ストップ・ロスは突然の価格ギャップで破られる.より広いストップ・ロスは使用できる.

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

戦略は次の側面で最適化できます.

  1. 最適なパラメータ組み合わせを見つけ,安定性を高めるために MA パラメータを最適化する.

  2. 不正取引をさらに減らすため,変動,ボリュームフィルターなどを追加します.

  3. ストップ・ロスの位置付けを賢くするために他の指標を組み込む.

  4. 安定した収益性を得るための適応型位置サイズアルゴリズムを開発する.

結論

概してこれは非常に実用的なトレンドフォロー戦略である.それはMA傾斜角を使用してトレンドを信頼的に決定し,価格変動率指標を使用してノイズ信号を効果的にフィルタリングする.ロングとショートポジションの両方を取ることは良い利益を得ることができる.継続的な最適化により,この戦略は非常に安定し信頼性の高い定量戦略になることができます.


/*backtest
start: 2023-12-01 00:00:00
end: 2023-12-31 23:59:59
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

//@version=4
// Based on ma angles code by Duyck which also uses Everget Jurik MA calulation and angle calculation by KyJ
strategy("Trend Angle BF", overlay=false)

/////////////// Time Frame ///////////////
testStartYear = input(2017, "Backtest Start Year") 
testStartMonth = input(1, "Backtest Start Month")
testStartDay = input(1, "Backtest Start Day")
testPeriodStart = timestamp(testStartYear,testStartMonth,testStartDay, 0, 0)

testStopYear = input(2019, "Backtest Stop Year")
testStopMonth = input(12, "Backtest Stop Month")
testStopDay = input(31, "Backtest Stop Day")
testPeriodStop = timestamp(testStopYear,testStopMonth,testStopDay, 0, 0)

testPeriod() => true
    
src=input(ohlc4,title="source")

// definition of "Jurik Moving Average", by Everget
jma(_src,_length,_phase,_power) =>
    phaseRatio = _phase < -100 ? 0.5 : _phase > 100 ? 2.5 : _phase / 100 + 1.5
    beta = 0.45 * (_length - 1) / (0.45 * (_length - 1) + 2)
    alpha = pow(beta, _power)
    jma = 0.0
    e0 = 0.0
    e0 := (1 - alpha) * _src + alpha * nz(e0[1])
    e1 = 0.0
    e1 := (_src - e0) * (1 - beta) + beta * nz(e1[1])
    e2 = 0.0
    e2 := (e0 + phaseRatio * e1 - nz(jma[1])) * pow(1 - alpha, 2) + pow(alpha, 2) * nz(e2[1])
    jma := e2 + nz(jma[1])

//// //// Determine Angle by KyJ //// //// 
angle(_src) =>
    rad2degree=180/3.14159265359  //pi 
    ang=rad2degree*atan((_src[0] - _src[1])/atr(14)) 

jma_line=jma(src,10,50,1)
ma=ema(src,input(56))
jma_slope=angle(jma_line)
ma_slope=angle(ma)

///////////// Rate Of Change ///////////// 
source = close
roclength = input(12, minval=1)
pcntChange = input(2, minval=1)
roc = 100 * (source - source[roclength]) / source[roclength]
emaroc = ema(roc, roclength / 2)
isMoving() => emaroc > (pcntChange / 2) or emaroc < (0 - (pcntChange / 2))

/////////////// Strategy ///////////////
long = ma_slope>=0 and isMoving()
short = ma_slope<=0 and isMoving()

last_long = 0.0
last_short = 0.0
last_long := long ? time : nz(last_long[1])
last_short := short ? time : nz(last_short[1])

long_signal = crossover(last_long, last_short)
short_signal = crossover(last_short, last_long)

last_open_long_signal = 0.0
last_open_short_signal = 0.0
last_open_long_signal := long_signal ? open : nz(last_open_long_signal[1])
last_open_short_signal := short_signal ? open : nz(last_open_short_signal[1])

last_long_signal = 0.0
last_short_signal = 0.0
last_long_signal := long_signal ? time : nz(last_long_signal[1])
last_short_signal := short_signal ? time : nz(last_short_signal[1])

in_long_signal = last_long_signal > last_short_signal
in_short_signal = last_short_signal > last_long_signal

last_high = 0.0
last_low = 0.0
last_high := not in_long_signal ? na : in_long_signal and (na(last_high[1]) or high > nz(last_high[1])) ? high : nz(last_high[1])
last_low := not in_short_signal ? na : in_short_signal and (na(last_low[1]) or low < nz(last_low[1])) ? low : nz(last_low[1])
sl_inp = input(2.0, title='Stop Loss %') / 100
tp_inp = input(900.0, title='Take Profit %') / 100 
 
take_level_l = strategy.position_avg_price * (1 + tp_inp)
take_level_s = strategy.position_avg_price * (1 - tp_inp) 

since_longEntry = barssince(last_open_long_signal != last_open_long_signal[1]) 
since_shortEntry = barssince(last_open_short_signal != last_open_short_signal[1]) 

slLong = in_long_signal ? strategy.position_avg_price * (1 - sl_inp) : na
slShort = strategy.position_avg_price * (1 + sl_inp)
long_sl = in_long_signal ? slLong : na
short_sl = in_short_signal ? slShort : na

/////////////// Execution /////////////// 
if testPeriod()
    strategy.entry("Long",  strategy.long, when=long)
    strategy.entry("Short", strategy.short, when=short)
    strategy.exit("Long Ex", "Long", stop=long_sl, limit=take_level_l, when=since_longEntry > 0)
    strategy.exit("Short Ex", "Short", stop=short_sl, limit=take_level_s, when=since_shortEntry > 0)
    
///////////// Plotting /////////////
hline(0, title='Zero line', color=color.purple, linewidth=1)
plot(ma_slope,title="ma slope", linewidth=2,color=ma_slope>=0?color.lime:color.red)
bgcolor(isMoving() ? long ? color.green : short ? color.red : na : color.white, transp=80)
bgcolor(long_signal ? color.lime : short_signal ? color.red : na, transp=30) 


もっと