e-TurboFx モメンタム戦略
概要
e-TurboFx モメンタム戦略 は、オリジナルの MetaTrader 4 エキスパート アドバイザー「e-TurboFx」を直接移植したものです。このシステムは、完成した最新のキャンドルをスキャンし、キャンドル本体が膨張し続ける方向のストレッチを探します。実体サイズが拡大する連続した弱気のローソク足は、長期エントリーで薄れる可能性がある降伏の可能性を示しますが、実体が拡大する連続する強気のローソク足は、空売りされる可能性のある過度の上昇相場を示唆します。 StockSharp の実装は、ローソク足のサブスクリプションを通じてロジックのイベント駆動を維持し、オプションのストップロスおよびテイクプロフィット保護を自動的にアタッチします。
取引ロジック
- 構成可能なキャンドル タイプ (時間枠) をサブスクライブし、完了したキャンドルのみを処理します。
- 2 つの別々のシーケンスを追跡します。1 つは弱気のローソク足で、もう 1 つは強気のローソク足です。
- 各キャンドルについて、本体の絶対サイズ (
|Close - Open|) を測定します。
- ローソク足が反対方向に閉じるとすぐに、反対方向のシーケンスをリセットします。
- 各シーケンス内では、厳密に拡張されたボディが必要です。すべての新しいキャンドルは、前のキャンドルよりも大きなボディを持たなければなりません。短縮すると、シーケンス カウンターが 1 から再スタートします。
- シーケンス内のローソク足の数が
DepthAnalysis に達すると、最後のシーケンスとは逆方向に市場エントリーをトリガーします (弱気の連続の後に買い、強気の連続の後に売り)。
- ポジションがオープンになったら、ストラテジーがフラット ポジションに戻るまでシグナル検出を一時停止します。組み込みの
StartProtection は、価格ステップ (ティック) で表されるオプションのストップロスとテイクプロフィットの距離を管理します。
この動作は、専門アドバイザーが最後の N の閉じたローソク足をチェックし、すべての物体が同じ方向に並んでいることと、各物体が次の古いローソク足の体よりも大きいことを確認した MQL4 アルゴリズムを再現します。
実装の詳細
- プロジェクト ガイドラインへの準拠を維持するために、ハイレベルのキャンドル サブスクリプション API を
SubscribeCandles および Bind とともに使用します。
- カスタム コレクションを回避し、イベント間の内部状態に依存するために、スカラー フィールド (
_bearishSequence、_bullishSequence、_previousBearishBody、_previousBullishBody) のみを保持します。
OnStarted で StartProtection を 1 回だけ呼び出して、オプションのストップロス注文とテイクプロフィット注文を価格ステップで構成します。 0 の値は、元の専門家と同様に各保護命令を無効にします。
- リセットやエントリ トリガーの説明を含む、ソース コード内に広範な英語のコメントが含まれています。
- デバッグを容易にするために、デザイナーまたは UI 内で実行するときにチャート領域にローソク足と独自の取引を描画します。
パラメーター
| パラメータ |
説明 |
デフォルト |
DepthAnalysis |
取引を開始する前に、エキスパンドボディで一方向に必要な連続終了キャンドルの数。 |
3 |
TakeProfitSteps |
取引価格ステップ (ティック) で測定される利食い距離。テイクプロフィットを無効にするには、0 に設定します。 |
120 |
StopLossSteps |
為替価格ステップ (ティック) で測定されるストップロス距離。ストップロスを無効にするには、0 に設定します。 |
70 |
TradeVolume |
各成行注文で送信されるボリューム。このパラメータを変更すると、ベースの Strategy.Volume も更新されます。 |
0.1 |
CandleType |
分析用に登録されたローソク足のデータ タイプ (時間枠)。 |
1 hour |
すべての数値パラメーターは最適化メタデータを公開するため、必要に応じて StockSharp オプティマイザーで戦略を調整できます。
注意事項と推奨事項
- この戦略はローソク体の拡大に反応するため、選択した時間枠はシグナル周波数に大きな影響を与えます。間隔が短いほど多くの取引が可能になりますが、より厳しい保護距離が必要になる場合があります。
- 接続されたセキュリティが有効な
PriceStep を定義していることを確認してください。そうしないと、ステップベースの保護距離を絶対価格に変換できません。
- ライブ デプロイの前に、StockSharp デザイナー内のポートをバックテストして、選択した機器に対してストップとターゲットがどのように変換されるかを検証します。
- この戦略では、一度に 1 つのオープン ポジションを維持します。終了するたびにカウンタがリセットされ、元の MQL4 の動作を反映してパターンを最初から再構築する必要があります。
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>
/// Momentum reversal strategy that tracks consecutive candles with expanding bodies.
/// </summary>
public class ETurboFxMomentumStrategy : Strategy
{
private readonly StrategyParam<int> _depthAnalysis;
private readonly StrategyParam<decimal> _takeProfitSteps;
private readonly StrategyParam<decimal> _stopLossSteps;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<DataType> _candleType;
private int _bearishSequence;
private int _bullishSequence;
private decimal _previousBearishBody;
private decimal _previousBullishBody;
/// <summary>
/// Number of recent candles analysed for momentum confirmation.
/// </summary>
public int DepthAnalysis
{
get => _depthAnalysis.Value;
set => _depthAnalysis.Value = value;
}
/// <summary>
/// Take profit distance measured in price steps (ticks).
/// A value of zero disables the take profit order.
/// </summary>
public decimal TakeProfitSteps
{
get => _takeProfitSteps.Value;
set => _takeProfitSteps.Value = value;
}
/// <summary>
/// Stop loss distance measured in price steps (ticks).
/// A value of zero disables the protective stop.
/// </summary>
public decimal StopLossSteps
{
get => _stopLossSteps.Value;
set => _stopLossSteps.Value = value;
}
/// <summary>
/// Volume used when sending market orders.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set
{
_tradeVolume.Value = value;
Volume = value;
}
}
/// <summary>
/// Candle type analysed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ETurboFxMomentumStrategy" /> class.
/// </summary>
public ETurboFxMomentumStrategy()
{
_depthAnalysis = Param(nameof(DepthAnalysis), 3)
.SetGreaterThanZero()
.SetDisplay("Depth Analysis", "Number of finished candles used for pattern detection", "Trading Rules")
.SetOptimize(2, 6, 1);
_takeProfitSteps = Param(nameof(TakeProfitSteps), 120m)
.SetNotNegative()
.SetDisplay("Take Profit (steps)", "Take profit distance in price steps (ticks)", "Risk Management")
.SetOptimize(60m, 180m, 20m);
_stopLossSteps = Param(nameof(StopLossSteps), 70m)
.SetNotNegative()
.SetDisplay("Stop Loss (steps)", "Stop loss distance in price steps (ticks)", "Risk Management")
.SetOptimize(40m, 120m, 10m);
_tradeVolume = Param(nameof(TradeVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Trade Volume", "Order volume used for entries", "Trading Rules")
.SetOptimize(0.1m, 0.5m, 0.1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe of the candles analysed by the strategy", "Market Data");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
Volume = TradeVolume;
var takeProfitUnit = CreateStepUnit(TakeProfitSteps);
var stopLossUnit = CreateStepUnit(StopLossSteps);
if (takeProfitUnit != null || stopLossUnit != null)
{
// Configure protective orders once the strategy starts.
StartProtection(
takeProfit: takeProfitUnit,
stopLoss: stopLossUnit,
isStopTrailing: false,
useMarketOrders: true);
}
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private Unit CreateStepUnit(decimal steps)
{
if (steps <= 0)
return null;
// Convert the user-friendly tick distance into a StockSharp Unit instance.
return new Unit(steps, UnitTypes.Absolute);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// indicators formed check removed
if (Position != 0)
{
// Do not look for new signals while a position is active.
ResetState();
return;
}
var bodySize = Math.Abs(candle.ClosePrice - candle.OpenPrice);
// The original expert compared absolute bodies of the latest N closed candles.
// Measuring the body here reproduces that behaviour candle by candle.
if (candle.ClosePrice < candle.OpenPrice)
{
HandleBearishCandle(bodySize);
}
else if (candle.ClosePrice > candle.OpenPrice)
{
HandleBullishCandle(bodySize);
}
else
{
// Neutral candle breaks both sequences.
ResetState();
}
}
private void HandleBearishCandle(decimal bodySize)
{
// Bearish candles reset the bullish path and allow the downside streak to continue.
ResetBullishSequence();
if (bodySize <= 0)
{
ResetBearishSequence();
return;
}
if (_bearishSequence == 0 || bodySize > _previousBearishBody)
{
// Body is larger than the previous bearish candle, extend the sequence.
_bearishSequence++;
}
else
{
// Sequence restarts because body did not expand.
_bearishSequence = 1;
}
_previousBearishBody = bodySize;
if (_bearishSequence >= DepthAnalysis)
{
// Expanding bearish bodies suggest exhaustion that can trigger a long entry.
BuyMarket();
ResetBearishSequence();
}
}
private void HandleBullishCandle(decimal bodySize)
{
// Bullish candles reset the bearish path and allow the upside streak to continue.
ResetBearishSequence();
if (bodySize <= 0)
{
ResetBullishSequence();
return;
}
if (_bullishSequence == 0 || bodySize > _previousBullishBody)
{
// Body is larger than the previous bullish candle, extend the sequence.
_bullishSequence++;
}
else
{
// Sequence restarts because body did not expand.
_bullishSequence = 1;
}
_previousBullishBody = bodySize;
if (_bullishSequence >= DepthAnalysis)
{
// Expanding bullish bodies suggest potential reversal to the downside.
SellMarket();
ResetBullishSequence();
}
}
private void ResetBearishSequence()
{
_bearishSequence = 0;
_previousBearishBody = 0m;
}
private void ResetBullishSequence()
{
_bullishSequence = 0;
_previousBullishBody = 0m;
}
private void ResetState()
{
ResetBearishSequence();
ResetBullishSequence();
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class e_turbo_fx_momentum_strategy(Strategy):
"""Momentum reversal strategy that tracks consecutive candles with expanding bodies.
Uses StartProtection for SL/TP."""
def __init__(self):
super(e_turbo_fx_momentum_strategy, self).__init__()
self._depth_analysis = self.Param("DepthAnalysis", 3) \
.SetGreaterThanZero() \
.SetDisplay("Depth Analysis", "Number of finished candles used for pattern detection", "Trading Rules")
self._take_profit_steps = self.Param("TakeProfitSteps", 120.0) \
.SetDisplay("Take Profit (steps)", "Take profit distance in price steps (ticks)", "Risk Management")
self._stop_loss_steps = self.Param("StopLossSteps", 70.0) \
.SetDisplay("Stop Loss (steps)", "Stop loss distance in price steps (ticks)", "Risk Management")
self._trade_volume = self.Param("TradeVolume", 0.1) \
.SetGreaterThanZero() \
.SetDisplay("Trade Volume", "Order volume used for entries", "Trading Rules")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe of the candles analysed by the strategy", "Market Data")
self._bearish_sequence = 0
self._bullish_sequence = 0
self._previous_bearish_body = 0.0
self._previous_bullish_body = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def DepthAnalysis(self):
return self._depth_analysis.Value
@property
def TakeProfitSteps(self):
return self._take_profit_steps.Value
@property
def StopLossSteps(self):
return self._stop_loss_steps.Value
@property
def TradeVolume(self):
return self._trade_volume.Value
def OnReseted(self):
super(e_turbo_fx_momentum_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(e_turbo_fx_momentum_strategy, self).OnStarted2(time)
self._reset_state()
self.Volume = float(self.TradeVolume)
tp_steps = float(self.TakeProfitSteps)
sl_steps = float(self.StopLossSteps)
tp_unit = Unit(tp_steps, UnitTypes.Absolute) if tp_steps > 0 else None
sl_unit = Unit(sl_steps, UnitTypes.Absolute) if sl_steps > 0 else None
if tp_unit is not None or sl_unit is not None:
self.StartProtection(tp_unit, sl_unit)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self.Position != 0:
self._reset_state()
return
body_size = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
if float(candle.ClosePrice) < float(candle.OpenPrice):
self._handle_bearish_candle(body_size)
elif float(candle.ClosePrice) > float(candle.OpenPrice):
self._handle_bullish_candle(body_size)
else:
self._reset_state()
def _handle_bearish_candle(self, body_size):
self._reset_bullish_sequence()
if body_size <= 0:
self._reset_bearish_sequence()
return
if self._bearish_sequence == 0 or body_size > self._previous_bearish_body:
self._bearish_sequence += 1
else:
self._bearish_sequence = 1
self._previous_bearish_body = body_size
if self._bearish_sequence >= self.DepthAnalysis:
self.BuyMarket()
self._reset_bearish_sequence()
def _handle_bullish_candle(self, body_size):
self._reset_bearish_sequence()
if body_size <= 0:
self._reset_bullish_sequence()
return
if self._bullish_sequence == 0 or body_size > self._previous_bullish_body:
self._bullish_sequence += 1
else:
self._bullish_sequence = 1
self._previous_bullish_body = body_size
if self._bullish_sequence >= self.DepthAnalysis:
self.SellMarket()
self._reset_bullish_sequence()
def _reset_bearish_sequence(self):
self._bearish_sequence = 0
self._previous_bearish_body = 0.0
def _reset_bullish_sequence(self):
self._bullish_sequence = 0
self._previous_bullish_body = 0.0
def _reset_state(self):
self._reset_bearish_sequence()
self._reset_bullish_sequence()
def CreateClone(self):
return e_turbo_fx_momentum_strategy()