GitHub で見る
AMA トレーダー 2 戦略
概要
AMA Trader 2 戦略は、ウラジミール カルプトフによるオリジナルの MetaTrader エキスパートの平均化ワークフローを再現しています。これは、Kaufman Adaptive Moving Average (AMA) トレンド フィルターと相対強度指数 (RSI) 確認ブロックを組み合わせたものです。価格がAMAを上回って終了し、RSIが売られ過ぎの領域に落ちた場合、戦略は長期エクスポージャーを追加します。対称ルールは、価格が AMA を下回って終了し、RSI が買われすぎの読み取り値を表示する場合の空売り取引に適用されます。平均取引は固定ロットサイズで送信され、最大ポジション数、最小エントリー間隔、保護トレーリングストップなどのリスクパラメーターによって制限できます。
市場の仮定
- 商品: 狭いスプレッドで取引されるFX/CFDシンボル用に設計されていますが、平均化が許容されるあらゆる流動性商品に適用できます。
- データ: 完成した時間ベースのローソク足で動作します。時間枠は、
CandleType パラメータを通じて設定できます (デフォルト: 1 分)。
- セッション: オプションの日中ウィンドウ。
UseTimeWindow フラグを使用すると、取引を UTC の開始時間と終了時間に制限できます。
インジケーター
- Kaufman Adaptive Moving Average (AMA) – 構成可能な高速/低速平滑化定数と平均長を使用して、一般的な傾向を検出します。
- 相対強度指数 (RSI) – 運動量の極値を検証します。信号を確認する必要がある連続した RSI 読み取りの数は、
StepLength によって制御されます (0 は 1 のように動作し、MQL のバージョンと一致します)。
取引ロジック
- 完成したキャンドルのみを処理し、戦略がオンラインで取引可能であることを確認します。
- オプションの時間フィルターを適用します。有効にすると、日中枠外の処理をスキップします。
- 最近の RSI 値のキューを更新し、既存のエクスポージャのトレーリングストップ調整を計算します。
- 長い設定: 終値が AMA を上回り、検査された RSI 値の少なくとも 1 つが
RsiLevelDown を下回っています。アクティブなロングポジションが損失を出している場合、平均化注文は標準エントリーの前にキューに入れられ、エキスパートアドバイザーの「損失回復」動作を模倣します。短い信号は対称規則 (RsiLevelUp) に従います。
- エントリは
MaxPositions、MinStep、および OnlyOnePosition を尊重します。 CloseOpposite が有効な場合、戦略は最初に反対側を相殺し、フラット化トレードが確認された後にのみ新しいエントリーを考慮します。
- 新しいポジションごとに、固定のストップロス/テイクプロフィット距離を付けることができ、オプションで、アクティブ化、距離、およびステップしきい値を備えた利益ベースのトレーリングストップを有効にすることができます。
リスク管理
- 固定ロットサイズ: すべてのエントリーは
LotSize を使用し、パラメーターまたはホスティング ポートフォリオを介してポジションのサイジングを可能にします。
- 最大平均深度:
MaxPositions は、方向ごとに露出を増やすことができる回数を制限します。
- 間隔制御:
MinStep は、連続するエントリ間の最小価格距離を強制し、同じレベルでのクラスタリングを減らします。
- 保護的出口: オプションのストップロス、テイクプロフィット、トレーリングロジックは、MetaTrader エキスパートの保護ツールキットを複製します。
- 反対エクスポージャー:
CloseOpposite は、ロングを開く前にショートを閉じる戦略を強制します (逆も同様です)。 OnlyOnePosition は、戦略が双方を同時に維持しないことを保証します。
パラメーター
| パラメータ |
説明 |
CandleType |
計算に使用されるローソク足のデータ型/タイムフレーム。 |
LotSize |
各成行注文の出来高。 |
RsiLength |
RSI の平均期間。 |
StepLength |
検査された最近の RSI 読み取り値の数 (0 → 1)。 |
RsiLevelUp |
RSI のショートエントリーの買われすぎのしきい値。 |
RsiLevelDown |
RSI のロングエントリーの売られ過ぎのしきい値。 |
AmaLength |
AMA スムージングの長さ。 |
AmaFastPeriod |
AMA の高速平滑化定数。 |
AmaSlowPeriod |
AMA の低速平滑化定数。 |
StopLoss |
価格単位での固定停止距離 (0 無効)。 |
TakeProfit |
価格単位での固定ターゲット距離 (0 無効)。 |
TrailingActivation |
トレーリングストップを作動させるために必要な利益 (0 無効)。 |
TrailingDistance |
トレーリングストップによって維持される距離。 |
TrailingStep |
トレーリングストップが強化される前の最小限の改善。 |
MaxPositions |
方向ごとの最大平均エントリ数 (0 無効)。 |
MinStep |
連続するエントリ間の最小距離 (0 は無効)。 |
CloseOpposite |
取引を開始する前に、反対のエクスポージャをクローズします。 |
OnlyOnePosition |
ポジションが存在する場合は常に新しいエントリーをブロックします。 |
UseTimeWindow |
日中の開始/終了時間のフィルタリングを有効にします。 |
StartTime |
ウィンドウが有効な場合のセッション開始時刻 (UTC)。 |
EndTime |
ウィンドウが有効になっているときのセッション終了時刻 (UTC)。 |
実装メモ
- 高レベルの API のみ: キャンドルは
SubscribeCandles 経由でサブスクライブされ、AMA と RSI は .Bind にバインドされ、すべての計算は禁止されたインジケーター ゲッターを使用せずにバインドされたコールバックで行われます。
- ポジション会計は、MQL のエキスパートを反映しています。個別のアキュムレーターがロングとショートの出来高/平均価格を追跡し、平均化の決定のために未実現損益を評価します。
- トレーリング ストップは、注文キューを直接操作するのではなく、ストラテジー レベルのストップロス距離を再構成し、StockSharp 実行モデルとの互換性を保ちます。
- シグナルはサイドごとにバーごとに 1 回の実行に制限されており、同じローソク足での重複エントリーを防ぐ MetaTrader チェックが再現されます。
- マジックナンバー、偏差、フリーズレベルチェック、テスター引き出しエミュレーションなどの、MetaTrader 固有のパラメータは省略されています。 StockSharp 環境は、注文の遅延と手数料を内部で管理します。
- ストップ/リミット価格は、ビッド/アスクティックではなくローソク足の終値から計算されます。これは、StockSharp のキャンドルベースのワークフローと一致します。
- 元の EA は、口座証拠金設定を使用して動的なロットサイズを計算します。ポートは固定の
LotSize を維持し、リスクに基づいたサイジングはホスティング環境に任せます。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Adaptive moving average strategy with RSI confirmation.
/// Simplified from the "AMA Trader 2" MetaTrader expert using EMA + RSI.
/// </summary>
public class AmaTrader2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<decimal> _rsiLevelUp;
private readonly StrategyParam<decimal> _rsiLevelDown;
private RelativeStrengthIndex _rsi;
private ExponentialMovingAverage _ema;
private decimal? _prevPrice;
private decimal? _prevEma;
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// EMA period.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// RSI overbought level.
/// </summary>
public decimal RsiLevelUp
{
get => _rsiLevelUp.Value;
set => _rsiLevelUp.Value = value;
}
/// <summary>
/// RSI oversold level.
/// </summary>
public decimal RsiLevelDown
{
get => _rsiLevelDown.Value;
set => _rsiLevelDown.Value = value;
}
public AmaTrader2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Indicators");
_emaLength = Param(nameof(EmaLength), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "Adaptive MA period", "Indicators");
_rsiLevelUp = Param(nameof(RsiLevelUp), 60m)
.SetDisplay("RSI Up", "RSI bullish confirmation threshold", "Signals");
_rsiLevelDown = Param(nameof(RsiLevelDown), 40m)
.SetDisplay("RSI Down", "RSI bearish confirmation threshold", "Signals");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevPrice = null;
_prevEma = null;
_rsi = new RelativeStrengthIndex { Length = RsiLength };
_ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, _ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_rsi.IsFormed || !_ema.IsFormed)
{
_prevPrice = candle.ClosePrice;
_prevEma = emaValue;
return;
}
if (_prevPrice is null || _prevEma is null)
{
_prevPrice = candle.ClosePrice;
_prevEma = emaValue;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
// Price crosses above EMA + RSI confirms bullish
var priceAboveEma = candle.ClosePrice > emaValue;
var prevBelowEma = _prevPrice < _prevEma;
if (priceAboveEma && prevBelowEma && rsiValue > RsiLevelUp)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
// Price crosses below EMA + RSI confirms bearish
else if (!priceAboveEma && !prevBelowEma && rsiValue < RsiLevelDown)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevPrice = candle.ClosePrice;
_prevEma = emaValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_rsi = null;
_ema = null;
_prevPrice = null;
_prevEma = null;
base.OnReseted();
}
}
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, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ama_trader2_strategy(Strategy):
def __init__(self):
super(ama_trader2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._rsi_length = self.Param("RsiLength", 14)
self._ema_length = self.Param("EmaLength", 50)
self._rsi_level_up = self.Param("RsiLevelUp", 60.0)
self._rsi_level_down = self.Param("RsiLevelDown", 40.0)
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiLength(self):
return self._rsi_length.Value
@RsiLength.setter
def RsiLength(self, value):
self._rsi_length.Value = value
@property
def EmaLength(self):
return self._ema_length.Value
@EmaLength.setter
def EmaLength(self, value):
self._ema_length.Value = value
@property
def RsiLevelUp(self):
return self._rsi_level_up.Value
@RsiLevelUp.setter
def RsiLevelUp(self, value):
self._rsi_level_up.Value = value
@property
def RsiLevelDown(self):
return self._rsi_level_down.Value
@RsiLevelDown.setter
def RsiLevelDown(self, value):
self._rsi_level_down.Value = value
def OnReseted(self):
super(ama_trader2_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(ama_trader2_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiLength
ema = ExponentialMovingAverage()
ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, ema, self._process_candle).Start()
def _process_candle(self, candle, rsi_value, ema_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
rsi_val = float(rsi_value)
ema_val = float(ema_value)
if self._has_prev:
price_above_ema = close > ema_val
prev_below_ema = self._prev_close < self._prev_ema
if price_above_ema and prev_below_ema and rsi_val > float(self.RsiLevelUp) and self.Position <= 0:
self.BuyMarket()
elif not price_above_ema and not prev_below_ema and rsi_val < float(self.RsiLevelDown) and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
def CreateClone(self):
return ama_trader2_strategy()