Diese Strategie portiert den MetaTrader Expert Advisor Triangle v1 auf die High-Level-API von StockSharp. Der ursprüngliche EA kombinierte Filter mit gewichteten gleitenden Durchschnitten auf einem höheren Zeitrahmen, eine Momentum-Divergenzprüfung und eine sehr langfristige MACD-Bestätigung, bevor er breakoutartige Orders platzierte. Die StockSharp-Version behält die Multi-Timeframe-Logik bei und ersetzt tickbasiertes Money Management durch kerzenbasierte Schutzorders.
Funktionsweise
Multi-Timeframe-Filter. Der Arbeitszeitrahmen (CandleType, Standard 15 Minuten) wird zur Ausführung von Trades verwendet. Trend- und Momentum-Filter werden auf einem höheren Zeitrahmen (TrendCandleType, Standard 1 Stunde) berechnet, um die MQL-Aufrufe zu spiegeln, die T referenzierten.
LWMA-Trend-Gate. Schnelle und langsame gewichtete gleitende Durchschnitte (LWMA-Äquivalent) müssen ausgerichtet sein. Long-Setups verlangen, dass die schnelle LWMA über der langsamen LWMA bleibt; Shorts verlangen die entgegengesetzte Beziehung.
Momentum-Abweichung. Eine 14-Perioden-Momentum-Reihe auf dem höheren Zeitrahmen muss in einer der letzten drei abgeschlossenen Kerzen mindestens um MomentumThreshold vom neutralen Niveau (100) abweichen, wodurch die Prüfungen MomLevelB/MomLevelS reproduziert werden.
MACD-Bestätigung. Ein sehr hoher Zeitrahmen (MacdCandleType, Standard 30-Tage-Kerzen ≈ monatlich) muss die MACD-Hauptlinie auf der richtigen Seite der Signallinie zeigen, bevor Trades erlaubt werden; dies kopiert die Bedingung MacdMAIN0 gegenüber MacdSIGNAL0.
Schutzausstiege. Stop-Loss- und Take-Profit-Distanzen werden in Preisschritten konfiguriert. Wenn eines der Niveaus auf einer abgeschlossenen Bar erreicht wird, schließt die Strategie die Position mit einer Marktorder.
Parameter
Parameter
Beschreibung
FastMaPeriod, SlowMaPeriod
Längen der gewichteten gleitenden Durchschnitte des höheren Zeitrahmens.
MomentumPeriod
Periode für den Momentum-Filter auf dem höheren Zeitrahmen.
MomentumThreshold
Minimale absolute Abweichung von 100, die in einer der letzten drei Momentum-Messungen erforderlich ist. Auf 0 setzen, um den Filter zu deaktivieren.
MacdFastLength, MacdSlowLength, MacdSignalLength
MACD-Parameter, die auf MacdCandleType angewendet werden.
StopLossSteps, TakeProfitSteps
Schutz-Stop- und Ziel-Distanzen, gemessen in Instrumenten-Preisschritten (Ticks). 0 verwenden, um zu deaktivieren.
CandleType
Handelszeitrahmen für die Orderausführung.
TrendCandleType
Höherer Zeitrahmen, der LWMAs und Momentum speist.
MacdCandleType
Zeitrahmen für den MACD-Bestätigungsfilter.
Verwendung
Wählen Sie ein Instrument und konfigurieren Sie CandleType, TrendCandleType und MacdCandleType passend zu den Zeitrahmen, die Sie analysieren möchten.
Passen Sie MA-, Momentum- und MACD-Längen an, wenn Sie das System an einen anderen Markt oder ein anderes Volatilitätsregime anpassen möchten.
Setzen Sie StopLossSteps und TakeProfitSteps entsprechend der Tickgröße des Instruments. Die Strategie wandelt die Schrittzahlen automatisch in tatsächliche Preisdistanzen um.
Starten Sie die Strategie. Sie abonniert alle erforderlichen Kerzenströme, aktualisiert Indikatoren mit der High-Level-Bind-API und verwaltet die Position, wenn Stops oder Ziele getroffen werden.
Unterschiede zum ursprünglichen EA
Geldbasierte Ausstiege (Use_TP_In_Money, Use_TP_In_percent) und der Kontostands-Schutzblock werden nicht nachgebildet, weil StockSharp in Instrumenteneinheiten arbeitet. Ein entsprechendes Verhalten kann durch Abstimmung von StopLossSteps/TakeProfitSteps erreicht werden.
Trailing-Stop-, Break-even- und Equity-Stop-Logik aus dem EA beruhten auf Tickverarbeitung und MetaTrader-spezifischen Orderänderungsaufrufen. Die Portierung behält aus Gründen der Klarheit den einfacheren Fixed-Stop-Ansatz bei; Benutzer können UpdatePositionState bei Bedarf um Trailing-Regeln erweitern.
Manuelle Trendlinien (TREND/TRENDLOW) und Fraktal-Arrays wurden im EA als diskretionäre Filter verwendet. Sie werden bewusst weggelassen, damit die StockSharp-Strategie vollständig systematisch bleibt.
Die Strategie hält immer höchstens eine Nettoposition, was der typischen Nutzung entspricht, obwohl der EA einen Parameter Max_Trades bereitstellte.
Stimmen Sie Schwellenwerte und Zeitrahmenparameter auf das gehandelte Instrument ab. Für volatile Märkte sind meist breitere Werte erforderlich, damit kleine Momentum-Schwankungen nicht alles herausfiltern.
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 TriangleStrategy : 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 TriangleStrategy()
{
_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 triangle_strategy(Strategy):
def __init__(self):
super(triangle_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(triangle_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(triangle_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 triangle_strategy()