Die AeroSpine-Strategie ist eine Konvertierung des MetaTrader-Experten AEROSPINE.mq4. Sie handelt ein einzelnes Symbol und versucht, Ausbrüche vom täglichen Eröffnungspreis zu erfassen. Der ursprüngliche Roboter war für Tagescharts konzipiert, während er Ticks überwachte; der Port behält die Idee des täglichen Eröffnungsausbruchs bei, stützt sich jedoch auf fertige Kerzen von StockSharp.
Handelslogik
Zu Beginn jedes Handelstages speichert die Strategie den täglichen Eröffnungspreis aus der ersten Kerze des Tages.
Neue Positionen werden nur nach der konfigurierten Einstiegsstunde bewertet. Fertige Kerzen müssen einen minimalen Volumenfilter erfüllen und der aktuelle Spread muss unter dem konfigurierten Limit liegen.
Wenn keine Position offen ist und kein Recovery-Trade ausstehend ist:
Ein Long-Trade wird eröffnet, sobald das Kerzenhoch die tägliche Eröffnung um EntryOffsetPips übersteigt.
Ein Short-Trade wird eröffnet, sobald das Kerzentief die tägliche Eröffnung um EntryOffsetPips unterschreitet.
Nach einem Verlusttrade bereitet die Strategie einen Recovery-Einstieg in die entgegengesetzte Richtung vor. Recovery-Trades verwenden RecoveryOffsetPips und erhöhen das Volumen durch Addition des Basisvolumens zur Größe des Verlusttrades, was das Martingale-ähnliche Sizing des MQL-Experten repliziert.
Offene Positionen werden mit drei Mechanismen verwaltet:
Ein fester Take-Profit bei TakeProfitPips vom Einstiegspreis.
Ein optionaler Break-Even-Auslöser, der den Trade schließt, wenn der Preis zur Break-Even-Distanz zurückkehrt, nachdem er sich zugunsten der Position bewegt hat.
Ein Schutzausstieg, wenn der Preis zur täglichen Eröffnung zurückkehrt und diese um ExitOffsetPips gegen die Position überschreitet.
Parameter
Name
Beschreibung
Candle Type
Zeitrahmen der Arbeitskerzen für die Signalbewertung.
Volume
Basis-Auftragsgröße für Ersteinstiege und zum Aufbau des Recovery-Volumens.
Entry Hour
Mindeststunde (Börsenzeit), ab der neue Einstiege möglich sind.
Entry Offset
Abstand in Pips von der täglichen Eröffnung, der überschritten werden muss, um den ersten Trade des Tages zu eröffnen.
Exit Offset
Abstand in Pips jenseits der täglichen Eröffnung zum Schließen von Positionen, die zur Eröffnung zurückkehren.
Recovery Offset
Abstand in Pips von der täglichen Eröffnung für einen Recovery-Trade nach einem Verlust.
Take Profit
Fester Take-Profit-Abstand in Pips vom Einstiegspreis.
Break Even
Abstand in Pips zum Aktivieren des Break-Even-Ausstiegs.
Use Break Even
Aktiviert oder deaktiviert den Break-Even-Verwaltungsblock.
Volume Filter
Minimales Kerzenvolumen für neue Einstiege, entsprechend der originalen Volume[0] > 10000-Prüfung.
Max Spread
Lehnt neue Einstiege ab, wenn der aktuelle Spread breiter als der erlaubte Wert ist (aus Pips umgerechnet).
Enable Recovery
Aktiviert die entgegengesetzte Recovery-Logik nach einem Verlusttrade.
Hinweise zur Konvertierung
Das originale EA platzierte Aufträge direkt bei Ticks während es einen Tageschart erzwang. Der Port emuliert dies mit Intraday-Kerzen: Die tägliche Eröffnung wird bei der ersten Kerze jedes Tages aktualisiert und die Ausbruchsprüfungen verwenden Kerzenhochs/-tiefs.
Alle MetaTrader-Schnittstellenelemente (Labels, Kapitalberechnungen über mehrere Symbole usw.) wurden entfernt. Nur die relevante Handelslogik für das aktuelle Symbol wurde beibehalten.
Break-Even und Stop-Modifikationen (OrderModify) werden über explizite ClosePosition()-Aufrufe simuliert, wenn die berechneten Schwellenwerte berührt werden.
Spread- und Volumenfilter entsprechen direkt den originalen MODE_SPREAD- und Volume[0]-Prüfungen.
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 AeroSpineStrategy : 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 AeroSpineStrategy()
{
_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 aero_spine_strategy(Strategy):
def __init__(self):
super(aero_spine_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(aero_spine_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(aero_spine_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 aero_spine_strategy()