GitHub で見る
Dematus戦略
概要
Dematus戦略は、MetaTrader 5の元のエキスパートアドバイザー「Dematus」のロジックを複製します。DeMarkerオシレーターを使用してモメンタムの反転を検出し、適応的なポジションサイジングによるピラミッディングをサポートします。戦略は単一インストルメント向けに設計されており、CandleTypeパラメーターで定義されたローソク足シリーズで取引します。
各確定済みローソク足で2つのDeMarker値が評価されます:最新の値と2バー前の値。売られすぎ閾値(0.3)から上へのクロスオーバーはロングの機会を示し、買われすぎ閾値(0.7)から下へのクロスオーバーはショートの機会を示します。初回エントリー後、価格が最後の実行済みエントリー価格から設定可能な距離を移動し、DeMarkerシグナルが再び発生すると、戦略はポジションに追加できます。
トレードルール
- 主要エントリー:
- 2バー前のDeMarker値が0.3を下回り、現在の値が0.3を上回ったときにロングポジションを開きます(オープンポジションがない場合)。
- 2バー前のDeMarker値が0.7を上回り、現在の値が0.7を下回ったときにショートポジションを開きます(オープンポジションがない場合)。
- スケーリングロジック:
- ポジションがアクティブな間、戦略は最後のフィルの正確な価格を記憶します。価格がポジションに対して少なくとも
DistancePips(価格単位に変換)移動し、対応するDeMarkerクロスオーバーが再発すると、戦略は同じ方向に追加の注文を提出します。
- 各追加注文のサイズは、前回の実行されたボリュームに
VolumeMultiplierを乗じて、インストルメントのボリュームステップに丸め、取引所の制限によって制約されます。これは元のエキスパートアドバイザーのロット係数動作を反映しています。
- ストップ管理:
- 各新しいポジションに
StopLossPipsを使用した初期ストップロスが付随します。ストップレベルは各スケーリングトレード後に再計算されるため、連結された純ポジションは常に有効な保護レベルを持ちます。
TrailingStopPipsが有効な場合、オープン利益がTrailingStopPips + TrailingStepPipsを超えるとストップレベルが絞られ、MQL実装のトレーリングストップロジックをエミュレートします。
- 純資産保護:
- フラット時、戦略は
Balance - VirtualStopEquityに等しい仮想純資産フロアを定義します。
- 浮動純資産が少なくとも
TrailingStartEquity上昇すると、トレーリング純資産ストップが起動され、ピーク純資産からTrailingEquityを差し引いた値に追随します。
- ポジションがオープンな状態で口座純資産が仮想フロアを下回ると、すべてのポジションが即座に清算されます。
パラメーター
| パラメーター |
説明 |
InitialVolume |
最初のトレードの基本注文サイズ。ポジションが完全にクローズされると再度使用されます。 |
DemarkerLength |
DeMarkerインジケーターの期間。 |
StopLossPips |
各エントリーに適用される保護ストップ距離(pips単位)。ゼロに設定すると静的ストップロスが無効になります。 |
TrailingStopPips |
トレーリングストップ距離(pips単位)。ゼロに設定するとトレーリングが無効になります。 |
TrailingStepPips |
トレーリングストップが移動する前に必要な追加の有利な移動(pips単位)。トレーリングがアクティブな場合は正でなければなりません。 |
DistancePips |
ポジションにスケールインする前の最後のフィルからの最小価格距離(pips単位)。 |
TrailingEquity |
純資産ピークと保護純資産フロアの間の距離。 |
VirtualStopEquity |
戦略がフラットな場合の仮想純資産フロア計算に使用される残高以下の初期バッファ。 |
TrailingStartEquity |
純資産トレーリングをアクティブにする残高を超えた利益閾値。 |
VolumeMultiplier |
ピラミッディング時に最後の実行注文サイズに適用される乗数。 |
ResetEntryPrice |
有効にすると、各エグジット後に保存されたエントリー価格をクリアし、新しいトレードが発生するまでスケーリングを防ぎます。 |
CandleType |
インジケーター計算とシグナル生成に使用するローソク足データ型(タイムフレーム)。 |
実装上の注意
- 戦略はStockSharpの高レベルAPIで実装されています。ローソク足サブスクリプションは
SubscribeCandlesで処理され、DeMarkerインジケーターはBindでバインドされて、インジケーター値がすぐに使用できる小数点として届きます。
- インジケーター状態は単純なスカラー変数で追跡されます:最新の値、前の値、2バー前の値で、MQLソースコードのバッファアクセスパターン(
iDeMarkerGet(0)とiDeMarkerGet(2))を正確に反映します。
- 注文ボリュームはインストルメントのボリュームステップに従って丸められ、拒否を防ぐために最小値と最大値に対して検証されます。
- 純資産管理は
Portfolio.CurrentValueを使用して元のコードにある残高/純資産チェックを反映します。純資産ベースのストップが発動すると、戦略はマーケットオーダーを通じてすべてのオープンポジションをクローズします。
- pipsサイズは
Security.PriceStepから導出されます。3桁または5桁小数のインストルメントは、MQLバージョンでポイントをpipsに変換するために使用される10倍の調整を自動的に受け取ります。
使用上の注意
- 接続されたポートフォリオが現在の純資産情報を提供するようにして、純資産トレーリングロジックが正しく動作するようにしてください。
- 戦略は確定済みローソク足(
CandleStates.Finished)のみで動作します。部分的に形成されたバーを無視し、元のエキスパートアドバイザーの「新しいバー」ゲーティングロジックに一致します。
- デフォルトの閾値(0.3/0.7)はコードに埋め込まれていますが、必要に応じて定数を変更することで調整できます。
- 戦略はライブ取引とバックテストをサポートします。バックテストでは、純資産トレーリングロジックが実行できるよう、ポートフォリオシミュレーターが純資産値を提供することを確認してください。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Dematus strategy using DEMA crossover for trend detection.
/// </summary>
public class DematusStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public DematusStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast DEMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow DEMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
var fast = new DoubleExponentialMovingAverage { Length = FastPeriod };
var slow = new DoubleExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
if (_prevFast == null || _prevSlow == null)
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fastVal > slowVal;
_prevFast = fastVal;
_prevSlow = slowVal;
if (!prevAbove && currAbove)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (prevAbove && !currAbove)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
}
}
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 DoubleExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class dematus_strategy(Strategy):
def __init__(self):
super(dematus_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast DEMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow Period", "Slow DEMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(dematus_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(dematus_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = DoubleExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = DoubleExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif prev_above and not curr_above:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return dematus_strategy()