AdaptiveTrader Pro 戦略
概要
AdaptiveTrader Pro は、MetaTrader 5 エキスパート アドバイザー AdaptiveTrader_Pro_Final_EA.mq5 から変換されたマルチタイムフレームのトレンドフォロー戦略です。 RSI、ATR、移動平均を組み合わせて、資金管理コントロールを適用しながら、支配的なトレンドの方向に取引します。
この戦略は、構成可能な主な時間枠 (デフォルトは 5 分) で機能し、より高い時間枠の移動平均 (デフォルトは 1 時間) を使用してトレンドの方向を確認します。エントリーは、両方の移動平均と一致する売られすぎ/買われすぎの RSI シグナルに依存します。
取引ルール
- ロングエントリー: RSI が 30 を下回り、ローソク足の終値がメインタイムフレーム SMA と上位タイムフレーム SMA を上回っている場合。
- ショートエントリー: RSI が 70 を超え、ローソク足の終値が両方の SMA を下回った場合。
- 単一位置: 一度に 1 方向の位置のみが保持されます。反対のポジションは反転する前にクローズされます。
リスクと貿易の管理
- ポジション サイズ: ポジション サイズは、ポートフォリオの資産、リスクの割合、および ATR ベースのストップ ディスタンスから計算されます。
- ストップ処理: ATR ベースのトレーリング ストップは価格に従い、設定可能な ATR 倍数で取引が有利に進んだ後、損益分岐点まで引き締められます。
- 部分利益: ポジションの設定可能な部分が最初のターゲット (ATR 倍) でクローズされます。残量はトレーリングストップで管理されます。
パラメーター
| 名前 | 説明 | デフォルト |
|---|---|---|
MaxRiskPercent |
取引ごとにアカウントに適用されるリスクの割合。 | 0.2 |
RsiPeriod |
メインタイムフレームの長さは RSI です。 | 14 |
AtrPeriod |
メインタイムフレームの長さは ATR です。 | 14 |
AtrMultiplier |
初期停止距離の乗数は ATR です。 | 1.5 |
TrailingStopMultiplier |
ATR 乗数はストップのトレーリング中に使用されました。 | 1.0 |
TrailingTakeProfitMultiplier |
部分的なテイクプロフィットターゲットの乗数は ATR です。 | 2.0 |
TrendPeriod |
メインタイムフレームの長さは SMA です。 | 20 |
HigherTrendPeriod |
より高いタイムフレームでは SMA の長さです。 | 50 |
BreakEvenMultiplier |
ストップを損益分岐点に移動させるトリガーとなる ATR 乗数。 | 1.5 |
PartialCloseFraction |
最初のターゲットで閉じられた初期ポジションの割合。 | 0.5 |
MaxSpreadPoints |
取引を開始する前の価格ステップにおける最大許容スプレッド。 | 20 |
CandleType |
分析に使用される主なローソク足のタイプ (時間枠)。 | 5 minute candles |
HigherCandleType |
確認のために使用されるより高いタイムフレームのローソク足タイプ。 | 1 hour candles |
注意事項
- この戦略では、ローソク足サブスクリプションとインジケーター バインディングを備えた StockSharp の高レベルの API を使用します。
- スプレッドは最良の買値/売値を通じて監視されます。スプレッドが設定された制限内に収まるまで、取引は一時停止されます。
- Python の実装は命令ごとに意図的に省略されています。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Adaptive multi-timeframe strategy converted from the "AdaptiveTrader Pro" expert advisor.
/// Combines RSI, ATR and dual moving averages to align entries with the prevailing trend.
/// Applies risk-based position sizing, partial profit taking, break-even logic and ATR driven trailing stops.
/// </summary>
public class AdaptiveTraderProStrategy : Strategy
{
private readonly StrategyParam<decimal> _maxRiskPercent;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<decimal> _trailingStopMultiplier;
private readonly StrategyParam<decimal> _trailingTakeProfitMultiplier;
private readonly StrategyParam<int> _trendPeriod;
private readonly StrategyParam<int> _higherTrendPeriod;
private readonly StrategyParam<decimal> _breakEvenMultiplier;
private readonly StrategyParam<decimal> _partialCloseFraction;
private readonly StrategyParam<decimal> _maxSpreadPoints;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<DataType> _higherCandleType;
private decimal? _bestBidPrice;
private decimal? _bestAskPrice;
private decimal _lastHigherTrendValue;
private decimal _entryPrice;
private decimal _entryVolume;
private decimal _entryAtr;
private bool _breakEvenApplied;
private bool _partialTakeProfitDone;
private decimal _trailingStopLevel;
/// <summary>
/// Maximum risk percentage allocated per trade.
/// </summary>
public decimal MaxRiskPercent
{
get => _maxRiskPercent.Value;
set => _maxRiskPercent.Value = value;
}
/// <summary>
/// RSI period used on the main timeframe.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// ATR period used on the main timeframe.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Multiplier applied to ATR for stop-loss sizing.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Multiplier applied to ATR for trailing stop adjustments.
/// </summary>
public decimal TrailingStopMultiplier
{
get => _trailingStopMultiplier.Value;
set => _trailingStopMultiplier.Value = value;
}
/// <summary>
/// Multiplier applied to ATR for the partial take-profit objective.
/// </summary>
public decimal TrailingTakeProfitMultiplier
{
get => _trailingTakeProfitMultiplier.Value;
set => _trailingTakeProfitMultiplier.Value = value;
}
/// <summary>
/// Moving average period used on the main timeframe.
/// </summary>
public int TrendPeriod
{
get => _trendPeriod.Value;
set => _trendPeriod.Value = value;
}
/// <summary>
/// Moving average period used on the higher timeframe.
/// </summary>
public int HigherTrendPeriod
{
get => _higherTrendPeriod.Value;
set => _higherTrendPeriod.Value = value;
}
/// <summary>
/// ATR multiplier that defines when to move the stop to break even.
/// </summary>
public decimal BreakEvenMultiplier
{
get => _breakEvenMultiplier.Value;
set => _breakEvenMultiplier.Value = value;
}
/// <summary>
/// Fraction of the initial position closed at the first target.
/// </summary>
public decimal PartialCloseFraction
{
get => _partialCloseFraction.Value;
set => _partialCloseFraction.Value = value;
}
/// <summary>
/// Maximum allowed spread expressed in price steps.
/// </summary>
public decimal MaxSpreadPoints
{
get => _maxSpreadPoints.Value;
set => _maxSpreadPoints.Value = value;
}
/// <summary>
/// Candle type used on the main timeframe.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Candle type used for higher timeframe confirmation.
/// </summary>
public DataType HigherCandleType
{
get => _higherCandleType.Value;
set => _higherCandleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="AdaptiveTraderProStrategy"/> class.
/// </summary>
public AdaptiveTraderProStrategy()
{
_maxRiskPercent = Param(nameof(MaxRiskPercent), 0.2m)
.SetGreaterThanZero()
.SetDisplay("Max Risk %", "Risk percentage applied on each trade", "Risk Management");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Length of the RSI indicator", "Indicators")
.SetOptimize(8, 20, 1);
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Length of the ATR indicator", "Indicators")
.SetOptimize(7, 21, 1);
_atrMultiplier = Param(nameof(AtrMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Multiplier applied to ATR for stops", "Risk Management")
.SetOptimize(1.0m, 3.0m, 0.5m);
_trailingStopMultiplier = Param(nameof(TrailingStopMultiplier), 3.0m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop Multiplier", "ATR multiplier for trailing stop", "Risk Management")
.SetOptimize(0.5m, 2.5m, 0.5m);
_trailingTakeProfitMultiplier = Param(nameof(TrailingTakeProfitMultiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Trailing TP Multiplier", "ATR multiplier for partial profit", "Risk Management")
.SetOptimize(1.0m, 3.0m, 0.5m);
_trendPeriod = Param(nameof(TrendPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Main Trend Period", "SMA length on the main timeframe", "Indicators");
_higherTrendPeriod = Param(nameof(HigherTrendPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Higher Trend Period", "SMA length on the higher timeframe", "Indicators");
_breakEvenMultiplier = Param(nameof(BreakEvenMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Break Even Multiplier", "ATR multiplier that activates break even", "Risk Management");
_partialCloseFraction = Param(nameof(PartialCloseFraction), 0m)
.SetDisplay("Partial Close Fraction", "Fraction of the volume closed at the first target", "Risk Management");
_maxSpreadPoints = Param(nameof(MaxSpreadPoints), 20m)
.SetDisplay("Max Spread (points)", "Maximum allowed spread in price steps", "Filters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Main Candle Type", "Primary timeframe used for signals", "General");
_higherCandleType = Param(nameof(HigherCandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Higher Candle Type", "Confirmation timeframe used for trend", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
if (HigherCandleType != CandleType)
yield return (Security, HigherCandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_entryVolume = 0m;
_entryAtr = 0m;
_breakEvenApplied = false;
_partialTakeProfitDone = false;
_trailingStopLevel = 0m;
_bestBidPrice = null;
_bestAskPrice = null;
_lastHigherTrendValue = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetTradeState();
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var trendMa = new SimpleMovingAverage { Length = TrendPeriod };
var higherTrendMa = new SimpleMovingAverage { Length = HigherTrendPeriod };
var mainSubscription = SubscribeCandles(CandleType);
mainSubscription.Bind(rsi, atr, trendMa, ProcessMainCandle).Start();
var higherSubscription = SubscribeCandles(HigherCandleType);
higherSubscription.Bind(higherTrendMa, ProcessHigherCandle).Start();
}
private void ProcessHigherCandle(ICandleMessage candle, decimal higherTrend)
{
if (candle.State != CandleStates.Finished)
return;
_lastHigherTrendValue = higherTrend;
}
private void ProcessMainCandle(ICandleMessage candle, decimal rsiValue, decimal atrValue, decimal trendValue)
{
if (candle.State != CandleStates.Finished)
return;
//if (!IsFormedAndOnlineAndAllowTrading())
// return;
//if (!_hasHigherTrend)
// return;
//if (!IsSpreadAllowed())
// return;
UpdateTrailingManagement(candle, atrValue);
if (Position != 0m)
return;
if (atrValue <= 0m)
return;
var closePrice = candle.ClosePrice;
if (rsiValue < 45m && closePrice > trendValue)
{
TryEnterLong(closePrice, atrValue);
}
else if (rsiValue > 55m && closePrice < trendValue)
{
TryEnterShort(closePrice, atrValue);
}
}
private void TryEnterLong(decimal entryPrice, decimal atrValue)
{
if (Position < 0m)
{
BuyMarket();
return;
}
BuyMarket();
InitializeTradeState(1, entryPrice, atrValue, Volume > 0 ? Volume : 1m);
}
private void TryEnterShort(decimal entryPrice, decimal atrValue)
{
if (Position > 0m)
{
SellMarket();
return;
}
SellMarket();
InitializeTradeState(-1, entryPrice, atrValue, Volume > 0 ? Volume : 1m);
}
private void UpdateTrailingManagement(ICandleMessage candle, decimal atrValue)
{
if (Position > 0m)
{
var atrForTargets = _entryAtr > 0m ? _entryAtr : atrValue;
var trailingDistance = atrValue * TrailingStopMultiplier;
var candidateStop = candle.ClosePrice - trailingDistance;
if (_trailingStopLevel <= 0m || candidateStop > _trailingStopLevel)
_trailingStopLevel = candidateStop;
if (!_breakEvenApplied && atrForTargets > 0m)
{
var breakEvenTrigger = _entryPrice + atrForTargets * BreakEvenMultiplier;
if (candle.HighPrice >= breakEvenTrigger)
{
_trailingStopLevel = Math.Max(_trailingStopLevel, _entryPrice);
_breakEvenApplied = true;
}
}
if (!_partialTakeProfitDone && PartialCloseFraction > 0m && PartialCloseFraction < 1m && atrForTargets > 0m)
{
var partialTarget = _entryPrice + atrForTargets * TrailingTakeProfitMultiplier;
if (candle.HighPrice >= partialTarget)
{
var desiredVolume = NormalizeVolume(_entryVolume * PartialCloseFraction);
var availableVolume = Math.Max(Position, 0m);
var volumeToClose = Math.Min(availableVolume, desiredVolume);
if (volumeToClose > 0m)
{
SellMarket(volumeToClose);
_partialTakeProfitDone = true;
}
}
}
if (_trailingStopLevel > 0m && candle.LowPrice <= _trailingStopLevel)
{
SellMarket(Math.Max(Position, 0m));
ResetTradeState();
}
}
else if (Position < 0m)
{
var atrForTargets = _entryAtr > 0m ? _entryAtr : atrValue;
var trailingDistance = atrValue * TrailingStopMultiplier;
var candidateStop = candle.ClosePrice + trailingDistance;
if (_trailingStopLevel <= 0m || candidateStop < _trailingStopLevel)
_trailingStopLevel = candidateStop;
if (!_breakEvenApplied && atrForTargets > 0m)
{
var breakEvenTrigger = _entryPrice - atrForTargets * BreakEvenMultiplier;
if (candle.LowPrice <= breakEvenTrigger)
{
_trailingStopLevel = Math.Min(_trailingStopLevel, _entryPrice);
_breakEvenApplied = true;
}
}
if (!_partialTakeProfitDone && PartialCloseFraction > 0m && PartialCloseFraction < 1m && atrForTargets > 0m)
{
var partialTarget = _entryPrice - atrForTargets * TrailingTakeProfitMultiplier;
if (candle.LowPrice <= partialTarget)
{
var desiredVolume = NormalizeVolume(_entryVolume * PartialCloseFraction);
var availableVolume = Math.Max(Math.Abs(Position), 0m);
var volumeToClose = Math.Min(availableVolume, desiredVolume);
if (volumeToClose > 0m)
{
BuyMarket(volumeToClose);
_partialTakeProfitDone = true;
}
}
}
if (_trailingStopLevel > 0m && candle.HighPrice >= _trailingStopLevel)
{
BuyMarket(Math.Abs(Position));
ResetTradeState();
}
}
else
{
ResetTradeState();
}
}
private bool IsSpreadAllowed()
{
if (MaxSpreadPoints <= 0m)
return true;
if (_bestBidPrice is not decimal bid || _bestAskPrice is not decimal ask)
return false;
var step = Security?.PriceStep ?? 1m;
if (step <= 0m)
step = 1m;
var spreadPoints = (ask - bid) / step;
return spreadPoints <= MaxSpreadPoints;
}
// Quote handling removed - not needed for backtest
private void InitializeTradeState(int direction, decimal entryPrice, decimal atrValue, decimal volume)
{
_entryPrice = entryPrice;
_entryVolume = volume;
_entryAtr = atrValue;
_breakEvenApplied = false;
_partialTakeProfitDone = false;
_trailingStopLevel = direction == 1
? entryPrice - atrValue * TrailingStopMultiplier
: entryPrice + atrValue * TrailingStopMultiplier;
}
private void ResetTradeState()
{
_entryPrice = 0m;
_entryVolume = 0m;
_entryAtr = 0m;
_breakEvenApplied = false;
_partialTakeProfitDone = false;
_trailingStopLevel = 0m;
}
private decimal CalculateOrderVolume(decimal atrValue)
{
return Volume > 0 ? Volume : 1m;
}
private decimal NormalizeVolume(decimal volume)
{
if (volume <= 0m)
return Volume > 0 ? Volume : 1m;
return volume;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, AverageTrueRange, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class adaptive_trader_pro_strategy(Strategy):
def __init__(self):
super(adaptive_trader_pro_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._atr_period = self.Param("AtrPeriod", 14)
self._trailing_stop_multiplier = self.Param("TrailingStopMultiplier", 3.0)
self._break_even_multiplier = self.Param("BreakEvenMultiplier", 1.5)
self._trend_period = self.Param("TrendPeriod", 20)
self._entry_price = 0.0
self._entry_atr = 0.0
self._break_even_applied = False
self._trailing_stop_level = 0.0
self._direction = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def TrailingStopMultiplier(self):
return self._trailing_stop_multiplier.Value
@TrailingStopMultiplier.setter
def TrailingStopMultiplier(self, value):
self._trailing_stop_multiplier.Value = value
@property
def BreakEvenMultiplier(self):
return self._break_even_multiplier.Value
@BreakEvenMultiplier.setter
def BreakEvenMultiplier(self, value):
self._break_even_multiplier.Value = value
@property
def TrendPeriod(self):
return self._trend_period.Value
@TrendPeriod.setter
def TrendPeriod(self, value):
self._trend_period.Value = value
def OnReseted(self):
super(adaptive_trader_pro_strategy, self).OnReseted()
self._entry_price = 0.0
self._entry_atr = 0.0
self._break_even_applied = False
self._trailing_stop_level = 0.0
self._direction = 0
def OnStarted2(self, time):
super(adaptive_trader_pro_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._entry_atr = 0.0
self._break_even_applied = False
self._trailing_stop_level = 0.0
self._direction = 0
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
trend_ma = SimpleMovingAverage()
trend_ma.Length = self.TrendPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, atr, trend_ma, self._process_candle).Start()
def _reset_trade_state(self):
self._entry_price = 0.0
self._entry_atr = 0.0
self._break_even_applied = False
self._trailing_stop_level = 0.0
self._direction = 0
def _process_candle(self, candle, rsi_value, atr_value, trend_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
atr_val = float(atr_value)
trend_val = float(trend_value)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
trail_mult = float(self.TrailingStopMultiplier)
be_mult = float(self.BreakEvenMultiplier)
# Trailing stop management
if self._direction > 0 and self.Position > 0:
atr_for_targets = self._entry_atr if self._entry_atr > 0 else atr_val
trailing_distance = atr_val * trail_mult
candidate_stop = close - trailing_distance
if self._trailing_stop_level <= 0 or candidate_stop > self._trailing_stop_level:
self._trailing_stop_level = candidate_stop
if not self._break_even_applied and atr_for_targets > 0:
be_trigger = self._entry_price + atr_for_targets * be_mult
if high >= be_trigger:
self._trailing_stop_level = max(self._trailing_stop_level, self._entry_price)
self._break_even_applied = True
if self._trailing_stop_level > 0 and low <= self._trailing_stop_level:
self.SellMarket()
self._reset_trade_state()
return
elif self._direction < 0 and self.Position < 0:
atr_for_targets = self._entry_atr if self._entry_atr > 0 else atr_val
trailing_distance = atr_val * trail_mult
candidate_stop = close + trailing_distance
if self._trailing_stop_level <= 0 or candidate_stop < self._trailing_stop_level:
self._trailing_stop_level = candidate_stop
if not self._break_even_applied and atr_for_targets > 0:
be_trigger = self._entry_price - atr_for_targets * be_mult
if low <= be_trigger:
self._trailing_stop_level = min(self._trailing_stop_level, self._entry_price)
self._break_even_applied = True
if self._trailing_stop_level > 0 and high >= self._trailing_stop_level:
self.BuyMarket()
self._reset_trade_state()
return
else:
if self.Position == 0:
self._reset_trade_state()
if self.Position != 0:
return
if atr_val <= 0:
return
# Entry logic
if rsi_val < 45.0 and close > trend_val:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._entry_atr = atr_val
self._direction = 1
self._trailing_stop_level = close - atr_val * trail_mult
self._break_even_applied = False
elif rsi_val > 55.0 and close < trend_val:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._entry_atr = atr_val
self._direction = -1
self._trailing_stop_level = close + atr_val * trail_mult
self._break_even_applied = False
def CreateClone(self):
return adaptive_trader_pro_strategy()