Cronex DeMarker クロスオーバー戦略
概要
Cronex DeMarker クロスオーバー戦略は、MetaTrader インジケーター Cronex DeMarker を再現し、自動取引システムに変換します。オリジナルのインジケーターは、DeMarker オシレーターを 2 つの線形加重移動平均 (LWMA) とともにプロットします。この戦略はその設定を反映し、平滑化されたオシレーターライン間の強気と弱気のクロスオーバーを評価し、それらを成行注文に変換します。これにより、インジケーターに従って勢いが下値圧力から上値圧力に移行したとき(またはその逆の場合)、取引ロジックが即座に反応することができます。
インジケーターの構造
- DeMarker オシレーター – 現在のローソク足と前のローソク足の関係を測定します。
- 現在の高値が前の高値よりも高い場合、正の圧力は高値の差に等しくなります。それ以外の場合はゼロです。
- 現在の安値が前の安値よりも低い場合、負圧は安値間の距離に等しくなります。それ以外の場合はゼロです。
DeMarkerPeriod バーにわたる正圧と負圧の合計が発振器値 deMax / (deMax + deMin) を形成します。
- 高速 LWMA – 最新のオシレーターの変化を強調するために、期間
FastMaPeriod の線形加重移動平均が生の DeMarker 値に適用されます。
- 遅い LWMA – 期間
SlowMaPeriod を持つ別の線形加重移動平均により、同じ DeMarker ストリームが平滑化され、より遅い確認ラインが構築されます。
この戦略は、完成したすべてのキャンドルをこのインジケーター スタックにフィードし、元の MQ4 ファイルからのバッファー計算と正確に一致させます。
取引ロジック
- DeMarker オシレーターと両方の LWMA が完全に形成されるまで待ちます。
- ローソク足が完了するたびに、新しい DeMarker 値を計算し、両方の移動平均を更新します。
- 高速 LWMA シリーズと低速 LWMA シリーズ間のクロスオーバーを検出します。
- 強気のクロスオーバー – 高速 LWMA が低速 LWMA の下から上に移動します。この戦略はショートエクスポージャーをクローズし、ロングマーケットポジションをオープンします。
- 弱気クロスオーバー – 高速 LWMA が低速 LWMA の上から下に移動します。この戦略はロングエクスポージャーをクローズし、ショートマーケットポジションをオープンします。
- 戦略がまだ形成されていない場合、オフラインである場合、または取引が無効になっている場合、注文はスキップされます。
反対の信号では位置がすぐに逆転します。既存のエクスポージャは、必要な数量を新しい成行注文に追加することでクローズされます。
パラメーター
| パラメータ |
説明 |
デフォルト |
DeMarkerPeriod |
DeMarker オシレーターの構築に使用されるキャンドルの数。 |
25 |
FastMaPeriod |
新しいオシレーター値に反応する高速線形加重移動平均の期間。 |
14 |
SlowMaPeriod |
方向を確認する遅い線形加重移動平均の期間。 |
25 |
CandleType |
戦略によって処理されたローソク足シリーズ (タイムフレームまたはその他の DataType)。 |
1 Hour 時間枠 |
実装の詳細
- 高レベルの
SubscribeCandles API を使用します。インジケーターは、バーの途中での再描画を避けるために、ローソク足が Finished 状態に達した場合にのみ更新されます。
- この戦略は、StockSharp からの組み込みの
DeMarker および WeightedMovingAverage インジケーターに依存して、MQ4 バッファーを忠実に複製します。
- チャートエリアが自動的に作成され、価格のローソク足とオシレーターおよび両方の移動平均が視覚的に確認できるようにプロットされます。
StartProtection() は起動時に呼び出され、プロジェクト ガイドラインの要求に従って位置保護が 1 回だけ実行されるようにします。
使用法
- 戦略を目的の証券に添付し、希望のローソク足のタイプ (たとえば、1 時間のタイムフレームのローソク足) を割り当てます。
- DeMarker と移動平均期間を元のインジケーターと一致するように構成するか、最適化のために調整します。
- 戦略を実行します。インジケーターが完全に形成され、取引が許可されると取引が開始されます。
- プロットされたチャートを監視して、DeMarker オシレーターと LWMA クロスオーバー信号がエントリーを駆動していることを確認します。
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>
/// Strategy that replicates the Cronex DeMarker indicator setup and trades crossovers of its smoothed values.
/// </summary>
public class CronexDeMarkerCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _deMarkerPeriod;
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<DataType> _candleType;
private DeMarker _deMarker;
private WeightedMovingAverage _fastMa;
private WeightedMovingAverage _slowMa;
private decimal? _previousFast;
private decimal? _previousSlow;
/// <summary>
/// DeMarker indicator period.
/// </summary>
public int DeMarkerPeriod
{
get => _deMarkerPeriod.Value;
set => _deMarkerPeriod.Value = value;
}
/// <summary>
/// Fast linear weighted moving average period.
/// </summary>
public int FastMaPeriod
{
get => _fastMaPeriod.Value;
set => _fastMaPeriod.Value = value;
}
/// <summary>
/// Slow linear weighted moving average period.
/// </summary>
public int SlowMaPeriod
{
get => _slowMaPeriod.Value;
set => _slowMaPeriod.Value = value;
}
/// <summary>
/// Candle type processed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="CronexDeMarkerCrossoverStrategy"/>.
/// </summary>
public CronexDeMarkerCrossoverStrategy()
{
_deMarkerPeriod = Param(nameof(DeMarkerPeriod), 25)
.SetRange(2, 150)
.SetDisplay("DeMarker Period", "Length of the DeMarker oscillator", "Indicators")
;
_fastMaPeriod = Param(nameof(FastMaPeriod), 14)
.SetRange(2, 100)
.SetDisplay("Fast LWMA Period", "Length of the fast linear weighted moving average", "Indicators")
;
_slowMaPeriod = Param(nameof(SlowMaPeriod), 25)
.SetRange(2, 150)
.SetDisplay("Slow LWMA Period", "Length of the slow linear weighted moving average", "Indicators")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Time frame of processed candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_deMarker = null;
_fastMa = null;
_slowMa = null;
_previousFast = null;
_previousSlow = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Instantiate indicators matching the original MetaTrader logic.
_deMarker = new DeMarker
{
Length = DeMarkerPeriod
};
_fastMa = new WeightedMovingAverage
{
Length = FastMaPeriod
};
_slowMa = new WeightedMovingAverage
{
Length = SlowMaPeriod
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _deMarker);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
StartProtection(null, null);
}
private void ProcessCandle(ICandleMessage candle)
{
// Only act on completed candles to avoid repainting effects.
if (candle.State != CandleStates.Finished)
return;
if (_deMarker is null || _fastMa is null || _slowMa is null)
return;
// Update the DeMarker oscillator with the full candle data.
var deMarkerResult = _deMarker.Process(new CandleIndicatorValue(_deMarker, candle));
if (deMarkerResult.IsEmpty)
{
return;
}
var deMarkerValue = deMarkerResult.GetValue<decimal>();
// Smooth the oscillator with linear weighted moving averages.
var fastResult = _fastMa.Process(new DecimalIndicatorValue(_fastMa, deMarkerValue, candle.OpenTime) { IsFinal = true });
if (fastResult.IsEmpty) return;
var fastValue = fastResult.GetValue<decimal>();
var slowResult = _slowMa.Process(new DecimalIndicatorValue(_slowMa, deMarkerValue, candle.OpenTime) { IsFinal = true });
if (slowResult.IsEmpty) return;
var slowValue = slowResult.GetValue<decimal>();
// Ensure all indicators accumulated enough samples.
if (!_deMarker.IsFormed || !_fastMa.IsFormed || !_slowMa.IsFormed)
{
_previousFast = fastValue;
_previousSlow = slowValue;
return;
}
var previousFast = _previousFast;
var previousSlow = _previousSlow;
_previousFast = fastValue;
_previousSlow = slowValue;
if (!previousFast.HasValue || !previousSlow.HasValue)
return;
// Check readiness and trading permissions before sending orders.
// indicators formed check removed
var crossUp = previousFast.Value <= previousSlow.Value && fastValue > slowValue;
var crossDown = previousFast.Value >= previousSlow.Value && fastValue < slowValue;
if (crossUp)
{
// Close short exposure and establish a long position.
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (crossDown)
{
// Close long exposure and establish a short position.
if (Position > 0)
SellMarket();
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, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import DeMarker, WeightedMovingAverage, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class cronex_de_marker_crossover_strategy(Strategy):
"""Cronex DeMarker crossover strategy. Smooths the DeMarker oscillator with
fast and slow WMA and trades on their crossover."""
def __init__(self):
super(cronex_de_marker_crossover_strategy, self).__init__()
self._de_marker_period = self.Param("DeMarkerPeriod", 25) \
.SetDisplay("DeMarker Period", "Length of the DeMarker oscillator", "Indicators")
self._fast_ma_period = self.Param("FastMaPeriod", 14) \
.SetDisplay("Fast LWMA Period", "Length of the fast linear weighted moving average", "Indicators")
self._slow_ma_period = self.Param("SlowMaPeriod", 25) \
.SetDisplay("Slow LWMA Period", "Length of the slow linear weighted moving average", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Time frame of processed candles", "General")
self._de_marker = None
self._fast_ma = None
self._slow_ma = None
self._previous_fast = None
self._previous_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def DeMarkerPeriod(self):
return self._de_marker_period.Value
@property
def FastMaPeriod(self):
return self._fast_ma_period.Value
@property
def SlowMaPeriod(self):
return self._slow_ma_period.Value
def OnReseted(self):
super(cronex_de_marker_crossover_strategy, self).OnReseted()
self._de_marker = None
self._fast_ma = None
self._slow_ma = None
self._previous_fast = None
self._previous_slow = None
def OnStarted2(self, time):
super(cronex_de_marker_crossover_strategy, self).OnStarted2(time)
self._de_marker = DeMarker()
self._de_marker.Length = self.DeMarkerPeriod
self._fast_ma = WeightedMovingAverage()
self._fast_ma.Length = self.FastMaPeriod
self._slow_ma = WeightedMovingAverage()
self._slow_ma.Length = self.SlowMaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._de_marker is None or self._fast_ma is None or self._slow_ma is None:
return
dm_input = CandleIndicatorValue(self._de_marker, candle)
dm_input.IsFinal = True
dm_result = self._de_marker.Process(dm_input)
if dm_result.IsEmpty:
return
dm_value = float(dm_result)
fast_result = process_float(self._fast_ma, dm_value, candle.OpenTime, True)
if fast_result.IsEmpty:
return
fast_value = float(fast_result)
slow_result = process_float(self._slow_ma, dm_value, candle.OpenTime, True)
if slow_result.IsEmpty:
return
slow_value = float(slow_result)
if not self._de_marker.IsFormed or not self._fast_ma.IsFormed or not self._slow_ma.IsFormed:
self._previous_fast = fast_value
self._previous_slow = slow_value
return
previous_fast = self._previous_fast
previous_slow = self._previous_slow
self._previous_fast = fast_value
self._previous_slow = slow_value
if previous_fast is None or previous_slow is None:
return
cross_up = previous_fast <= previous_slow and fast_value > slow_value
cross_down = previous_fast >= previous_slow and fast_value < slow_value
if cross_up:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif cross_down:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return cronex_de_marker_crossover_strategy()