Ma Adx 戦略
MAとADXインジケーターに基づく戦略。価格がMAを強いトレンドで交差したときにポジションを取ります。
テストでは年平均リターン約184%を示しています。暗号資産市場で最もパフォーマンスが高いです。
移動平均がトレンドを指示し、ADXがそれが取引するのに十分なほど強いかどうかを検証します。ADXが閾値を超えたときの価格のMA交差に従ってエントリーが行われます。
このクラシックなトレンドアプローチはシステマティックなトレーダーに向いています。損失はパーセントストップで管理し、利益はATRベースの目標で確定します。
詳細
- エントリー条件:
- ロング:
Close > MA && ADX > 25 - ショート:
Close < MA && ADX > 25
- ロング:
- ロング/ショート: 両方
- エグジット条件: 逆方向のMAクロスまたはストップ
- ストップ:
StopLossPercentパーセント、テイクプロフィットTakeProfitAtrMultiplierATR - デフォルト値:
MaPeriod= 20AdxPeriod= 14CandleType= TimeSpan.FromMinutes(5).TimeFrame()StopLossPercent= 2mTakeProfitAtrMultiplier= 2mAdxThreshold= 25AtrPeriod= 14 エントリーには、ADXがAdxThresholdを上回る中でその足の終値がSMAを横切ることが必要です。逆方向のクロスでポジションを決済し、ADXが引き続き強ければ反対側を建てます。目標はエントリー時の終値からTakeProfitAtrMultiplier倍のATR(AtrPeriod)の位置で、足の終値で判定します。
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: Moving Average, ADX
- ストップ: はい
- 複雑さ: 中級
- 時間軸: 中期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MA ADX strategy.
/// A close crossing above the MaPeriod SMA while ADX is above AdxThreshold goes long and a cross below goes short; the reverse cross
/// closes the position and, with ADX still strong, opens the other side. The target lies TakeProfitAtrMultiplier ATR from the entry close
/// and is checked on candle closes, and a percent stop limits the loss.
/// </summary>
public class MaAdxStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<decimal> _takeProfitAtrMultiplier;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevClose;
private decimal _prevMa;
private decimal _targetPrice;
/// <summary>
/// Period of the SMA.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Period of ADX.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// ADX level of a strong trend.
/// </summary>
public decimal AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
/// <summary>
/// Target distance from the entry in ATRs.
/// </summary>
public decimal TakeProfitAtrMultiplier
{
get => _takeProfitAtrMultiplier.Value;
set => _takeProfitAtrMultiplier.Value = value;
}
/// <summary>
/// Period of the target ATR.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MaAdxStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period of the SMA", "Indicators");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Period of ADX", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 25m)
.SetDisplay("ADX Threshold", "ADX level of a strong trend", "Indicators");
_takeProfitAtrMultiplier = Param(nameof(TakeProfitAtrMultiplier), 2m)
.SetNotNegative()
.SetDisplay("Take Profit ATR", "Target distance from the entry in ATRs", "Risk");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period of the target ATR", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = null;
_prevMa = default;
_targetPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = null;
_prevMa = default;
_targetPrice = default;
var sma = new SimpleMovingAverage { Length = MaPeriod };
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sma, adx, atr, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, adx);
}
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue smaValue, IIndicatorValue adxValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!smaValue.IsFormed || !adxValue.IsFormed || !atrValue.IsFormed)
return;
if (adxValue is not AverageDirectionalIndexValue { MovingAverage: decimal strength })
return;
var ma = smaValue.GetValue<decimal>();
var close = candle.ClosePrice;
var prevClose = _prevClose;
var prevMa = _prevMa;
_prevClose = close;
_prevMa = ma;
if (prevClose is not decimal lastClose)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var atr = atrValue.GetValue<decimal>();
var crossUp = lastClose <= prevMa && close > ma;
var crossDown = lastClose >= prevMa && close < ma;
var strong = strength > AdxThreshold;
if (crossUp && strong && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_targetPrice = close + TakeProfitAtrMultiplier * atr;
}
else if (crossDown && strong && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_targetPrice = close - TakeProfitAtrMultiplier * atr;
}
else if (Position > 0 && (crossDown || (TakeProfitAtrMultiplier > 0 && close >= _targetPrice)))
{
SellMarket(Position);
}
else if (Position < 0 && (crossUp || (TakeProfitAtrMultiplier > 0 && close <= _targetPrice)))
{
BuyMarket(-Position);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageDirectionalIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class ma_adx_strategy(Strategy):
"""
MA ADX strategy.
A close crossing above the MaPeriod SMA while ADX is above AdxThreshold goes long and a cross below goes short; the reverse cross
closes the position and, with ADX still strong, opens the other side. The target lies TakeProfitAtrMultiplier ATR from the entry close
and is checked on candle closes, and a percent stop limits the loss.
"""
def __init__(self):
super(ma_adx_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 20).SetGreaterThanZero().SetDisplay("MA Period", "Period of the SMA", "Indicators")
self._adx_period = self.Param("AdxPeriod", 14).SetGreaterThanZero().SetDisplay("ADX Period", "Period of ADX", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 25.0).SetDisplay("ADX Threshold", "ADX level of a strong trend", "Indicators")
self._take_profit_atr_multiplier = self.Param("TakeProfitAtrMultiplier", 2.0).SetNotNegative().SetDisplay("Take Profit ATR", "Target distance from the entry in ATRs", "Risk")
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero().SetDisplay("ATR Period", "Period of the target ATR", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_close = None
self._prev_ma = Decimal(0)
self._target_price = Decimal(0)
def OnReseted(self):
super(ma_adx_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(ma_adx_strategy, self).OnStarted2(time)
self._reset_state()
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
adx = AverageDirectionalIndex()
adx.Length = self._adx_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sma, adx, atr, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, adx)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, sma_value, adx_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not sma_value.IsFormed or not adx_value.IsFormed or not atr_value.IsFormed:
return
if adx_value.MovingAverage is None:
return
ma = sma_value.GetValue[Decimal](None)
close = candle.ClosePrice
prev_close = self._prev_close
prev_ma = self._prev_ma
self._prev_close = close
self._prev_ma = ma
if prev_close is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
atr = atr_value.GetValue[Decimal](None)
cross_up = prev_close <= prev_ma and close > ma
cross_down = prev_close >= prev_ma and close < ma
strong = adx_value.MovingAverage > Decimal(self._adx_threshold.Value)
target_atr = Decimal(self._take_profit_atr_multiplier.Value)
if cross_up and strong and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._target_price = close + target_atr * atr
elif cross_down and strong and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._target_price = close - target_atr * atr
elif self.Position > 0 and (cross_down or (target_atr > 0 and close >= self._target_price)):
self.SellMarket(self.Position)
elif self.Position < 0 and (cross_up or (target_atr > 0 and close <= self._target_price)):
self.BuyMarket(-self.Position)
def CreateClone(self):
return ma_adx_strategy()