Diese Strategie portiert den MetaTrader Experten-Advisor Exp_CronexAO auf die StockSharp High-Level-API. Der ursprüngliche Roboter handelt Crossover zwischen den zwei Linien des Cronex Awesome Oscillator (AO). Die StockSharp-Version abonniert eine konfigurierbare Kerzenserie, berechnet den AO, glättet ihn zweimal mit gleitenden Durchschnitten, um die Cronex-Linien zu reproduzieren, und öffnet oder schließt Positionen, wenn die schnelle Linie die langsame Linie kreuzt.
Handelslogik
Den Awesome Oscillator aus den ausgewählten Kerzen aufbauen.
Den Oszillator zweimal mit einfachen gleitenden Durchschnitten glätten. Die erste Glättung erstellt die "schnelle" Cronex-Linie, die zweite Glättung erzeugt die "Signal"-Linie.
SignalBar abgeschlossene Kerzen zurückblicken und die Cronex-Linien auf diesem Balken und dem vorherigen vergleichen.
Ein Kauf-Signal erscheint, wenn die schnelle Linie über der langsamen Linie liegt und auf dem Rückblick-Balken einen Aufwärts-Crossover vollzogen hat. Die Strategie schließt optional eine Short-Position und öffnet, wenn erlaubt, eine Long-Market-Order.
Ein Verkaufs-Signal spiegelt die vorherige Regel wider: Die schnelle Linie muss unter der langsamen Linie liegen und muss auf dem Rückblick-Balken nach unten gekreuzt haben. Die Strategie schließt optional eine Long-Position und öffnet, wenn erlaubt, eine Short-Market-Order.
Stop-Loss- und Take-Profit-Levels, ausgedrückt in Instrument-Punkten, werden an die resultierende Position angehängt, wenn ein neuer Trade eröffnet wird.
Es wird nur eine Nettoposition gehalten. Wenn die Richtung wechselt, kombiniert die Strategie das zum Schließen der entgegengesetzten Position benötigte Volumen mit dem neuen Trade-Volumen, um den Netting-Modus von MetaTrader zu emulieren.
Parameter
Parameter
Beschreibung
CandleType
Datentyp der für Cronex-AO-Berechnungen verwendeten Kerzen. Standard ist ein 8-Stunden-Zeitrahmen.
FastPeriod
Länge der ersten Glättung, die auf den Awesome Oscillator angewendet wird.
SlowPeriod
Länge der zweiten Glättung, die auf die schnelle Linie angewendet wird.
SignalBar
Anzahl abgeschlossener Balken zurück, die das Kreuzsignal enthalten müssen. Die Strategie prüft auch den folgenden Balken zur Richtungsbestätigung.
BuyOpenEnabled / SellOpenEnabled
Öffnen von Long- oder Short-Positionen aktivieren oder deaktivieren.
BuyCloseEnabled / SellCloseEnabled
Steuern, ob entgegengesetzte Positionen bei einem inversen Signal geschlossen werden können.
TakeProfit
Gewinnziel in Punkten, nach jedem neuen Einstieg angewendet wenn größer als null.
StopLoss
Schutz-Stop in Punkten, auch nach jedem neuen Einstieg angewendet wenn größer als null.
Risikomanagement
Die Stop-Loss- und Take-Profit-Abstände ahmen die punktbasierten Eingaben der MetaTrader-Version nach. Sie werden bei jedem neuen Trade neu berechnet, damit Schutzorders immer mit der aktuellen Nettopositionsgröße übereinstimmen.
Unterschiede zur MetaTrader-Version
Die StockSharp-Implementierung verwendet einfache gleitende Durchschnitte für beide Cronex-Glättungsstufen. Die ursprüngliche XMA-Implementierung erlaubt mehrere Glättungsmethoden, aber die Standardkonfiguration entspricht dem hier reproduzierten einfachen Durchschnitt.
Slippage- und Geldmanagement-Routinen aus der TradeAlgorithms-Bibliothek werden nicht repliziert. Positionsgrößen werden über die Standard-Volume-Eigenschaft gesteuert.
Die Trade-Ausführung basiert auf dem Netting-Verhalten von StockSharp. Wenn die Richtung umgekehrt wird, wird eine einzelne Market-Order mit ausreichend Volumen ausgegeben, um die Position in einem Schritt zu glätten und umzukehren, was der MT5-Netting-Konto-Logik entspricht.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class ExpCronexAOStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public ExpCronexAOStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_cronex_ao_strategy(Strategy):
def __init__(self):
super(exp_cronex_ao_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(exp_cronex_ao_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(exp_cronex_ao_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return exp_cronex_ao_strategy()