Die ACB1-Strategie ist der StockSharp-Port des MetaTrader-Expertenberaters, der als MQL/8586/ACB1.MQ4 vertrieben wird. Das ursprüngliche System handelt mit dem EURUSD-Paar und wartet auf starke tägliche Ausbrüche, bevor es in den Markt eintritt. Diese Konvertierung reproduziert den gleichen Entscheidungsprozess mit StockSharp High-Level-Primitiven:
Tageskerzen (SignalCandleType) definieren die Ausbruchsrichtung und stellen die Stop- und Take-Profit-Anker dar.
H4-Kerzen (TrailCandleType) bestimmen die Nachlaufstrecke, die mit TrailFactor multipliziert wird.
Aufträge werden zum Marktwert ausgeführt, sobald die Ausbruchsbedingungen erfüllt sind und die Strategie nur eine Nettoposition behält, was die OrdersTotal()-Prüfungen im MQL-Code widerspiegelt.
Stop-Loss und Take-Profit werden intern verwaltet: Die Strategie überwacht die besten Geld-/Briefkurse und schließt die Position mit Marktaufträgen, wenn die virtuellen Schutzniveaus durchbrochen werden.
Handelsregeln
Lange Einrichtung
Verwenden Sie die zuvor fertige Tageskerze.
Wenn Close > (High + Low) / 2und der aktuelle Briefkurs über dem vorherigen Höchststand liegt, eröffnen Sie eine Long-Marktposition.
Der Stop-Loss wird auf dem vorherigen Tief platziert (gerundet auf die Preisstufe des Instruments).
Der Take-Profit entspricht dem Einstiegspreis plus (High − Low) × TakeFactor.
Kurze Einrichtung
Wenn Close < (High + Low) / 2und der aktuelle Geldkurs unter dem vorherigen Tief liegt, eröffnen Sie eine Short-Marktposition.
Stop-Loss wird auf das vorherige Hoch gesetzt; Take-Profit subtrahiert (High − Low) × TakeFactor vom Einstiegspreis.
Bei Long-Positionen folgt der Stop Bid − TrailDistance, während der Preis unter dem Take-Profit abzüglich des Broker-Stop-Levels bleibt.
Bei Short-Positionen folgt der Stop Ask + TrailDistance, während der Preis über dem Take-Profit plus dem Stop-Level des Brokers bleibt.
Risikowächter
Die Strategie verfolgt das maximal beobachtete Portfolioeigenkapital. Der Handel wird genau wie beim ursprünglichen Berater immer dann unterbrochen, wenn das aktuelle Eigenkapital unter 50 % dieses Höchstwerts fällt.
Eine Abklingzeit von fünf Sekunden (CooldownSeconds) verhindert, dass neue Bestellungen eingehen oder Aktualisierungen zu häufig gestoppt werden, wodurch die TimeLocal()-Drosselung von MQL reproduziert wird.
Positionsgrößenbestimmung und Risikokontrolle
Das Volumen pro Trade ergibt sich aus Portfolio.CurrentValue × RiskFraction.
Das monetäre Risiko pro Vertrag wird aus der Stop-Distanz und den Sicherheitsmetadaten (PriceStep und StepPrice) berechnet.
Die resultierende Größe wird auf Security.VolumeStep ausgerichtet und auf [Security.MinVolume, Security.MaxVolume] begrenzt, dann durch den Parameter MaxVolume begrenzt (Standard 5 Lose).
Aufträge werden übersprungen, wenn das normalisierte Volumen Null ist oder wenn die Stoppdistanz gegen MinStopDistancePoints verstößt, was die MetaTrader MODE_STOPLEVEL-Prüfung emuliert.
Parameter
Parameter
Standard
Beschreibung
SignalCandleType
Täglich
Kerzentyp, der zur Ausbruchserkennung verwendet wird.
TrailCandleType
4 Stunden
Kerzentyp, der die Trailing-Stop-Distanz liefert.
TakeFactor
0,8
Auf den Tagesbereich angewendeter Multiplikator zur Berechnung des Take-Profits.
TrailFactor
10
Beim Aktualisieren des Stopps wird ein Multiplikator auf den Endbereich angewendet.
RiskFraction
0,05
Anteil des Portfolio-Eigenkapitals, das bei jedem Trade riskiert wird (5 %).
MaxVolume
5
Feste Obergrenze für das endgültige Bestellvolumen.
MinStopDistancePoints
0
Minimale Stop-/Take-Distanz, ausgedrückt in Preispunkten; Legen Sie es auf den Broker MODE_STOPLEVEL fest.
CooldownSeconds
5
Minimale Verzögerung zwischen aufeinanderfolgenden Handelsaktionen.
Hinweise zur Implementierung
Die Strategie erfordert die richtigen Instrumentenmetadaten: Security.PriceStep, Security.StepPrice, Security.VolumeStep, Security.MinVolume und (falls verfügbar) Security.MaxVolume.
Schutzstufen sind virtuell. StockSharp schließt Positionen über Marktaufträge, wenn Bid/Ask den berechneten Stop-Loss oder Take-Profit berührt.
Die Eigenkapitalverfolgung verwendet Portfolio.CurrentValue. Wenn der Connector dieses Feld nicht bereitstellt, wird der Risikoschutz den Handel so lange deaktiviert lassen, bis es verfügbar ist.
Es wird nur eine einzige Nettoposition gehalten. Gegensignale während eines aktiven Handels werden ignoriert, bis die Position vollständig geschlossen ist.
Es ist kein Python-Port enthalten; Dieses Verzeichnis enthält nur die C#-Implementierung und Dokumentation.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Breakout strategy converted from the "ACB1" MetaTrader expert advisor.
/// Enters on breakouts above previous candle high / below previous candle low,
/// with trailing stop based on ATR.
/// </summary>
public class Acb1Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _takeFactor;
private readonly StrategyParam<decimal> _trailFactor;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _prevClose;
private decimal _prevMid;
private decimal _entryPrice;
private decimal _stopPrice;
private bool _hasPrev;
public Acb1Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for breakout detection.", "General");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "Period for ATR indicator used in trailing.", "Indicators");
_takeFactor = Param(nameof(TakeFactor), 2m)
.SetDisplay("Take Factor", "ATR multiplier for take profit distance.", "Execution");
_trailFactor = Param(nameof(TrailFactor), 1.5m)
.SetDisplay("Trail Factor", "ATR multiplier for trailing stop distance.", "Execution");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public decimal TakeFactor
{
get => _takeFactor.Value;
set => _takeFactor.Value = value;
}
public decimal TrailFactor
{
get => _trailFactor.Value;
set => _trailFactor.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0;
_prevLow = 0;
_prevClose = 0;
_prevMid = 0;
_entryPrice = 0;
_stopPrice = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = 0;
_prevLow = 0;
_prevClose = 0;
_prevMid = 0;
_entryPrice = 0;
_stopPrice = 0;
_hasPrev = false;
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (atrValue <= 0)
return;
// Manage open position
if (Position != 0)
{
if (Position > 0)
{
// Trailing stop for long
var newStop = candle.ClosePrice - atrValue * TrailFactor;
if (newStop > _stopPrice)
_stopPrice = newStop;
// Check stop hit
if (candle.LowPrice <= _stopPrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
}
// Check take profit
else if (_entryPrice > 0 && candle.HighPrice >= _entryPrice + atrValue * TakeFactor)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
}
}
else
{
// Trailing stop for short
var newStop = candle.ClosePrice + atrValue * TrailFactor;
if (_stopPrice == 0 || newStop < _stopPrice)
_stopPrice = newStop;
// Check stop hit
if (candle.HighPrice >= _stopPrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
}
// Check take profit
else if (_entryPrice > 0 && candle.LowPrice <= _entryPrice - atrValue * TakeFactor)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
}
}
}
// Entry logic after managing position
if (_hasPrev && Position == 0)
{
if (_prevClose > _prevMid && candle.ClosePrice > _prevHigh)
{
// Bullish breakout
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _prevLow;
}
else if (_prevClose < _prevMid && candle.ClosePrice < _prevLow)
{
// Bearish breakout
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _prevHigh;
}
}
// Store for next candle
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevClose = candle.ClosePrice;
_prevMid = (candle.HighPrice + candle.LowPrice) / 2m;
_hasPrev = true;
}
}
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 CandleStates
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class acb1_strategy(Strategy):
"""
Breakout strategy converted from the "ACB1" MetaTrader expert advisor.
Enters on breakouts above previous candle high / below previous candle low,
with trailing stop based on ATR.
"""
def __init__(self):
super(acb1_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Timeframe for breakout detection.", "General")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "Period for ATR indicator used in trailing.", "Indicators")
self._take_factor = self.Param("TakeFactor", 2.0) \
.SetDisplay("Take Factor", "ATR multiplier for take profit distance.", "Execution")
self._trail_factor = self.Param("TrailFactor", 1.5) \
.SetDisplay("Trail Factor", "ATR multiplier for trailing stop distance.", "Execution")
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def TakeFactor(self):
return self._take_factor.Value
@TakeFactor.setter
def TakeFactor(self, value):
self._take_factor.Value = value
@property
def TrailFactor(self):
return self._trail_factor.Value
@TrailFactor.setter
def TrailFactor(self, value):
self._trail_factor.Value = value
def OnReseted(self):
super(acb1_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(acb1_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
self._has_prev = False
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
if atr_value <= 0:
return
# Manage open position
if self.Position != 0:
if self.Position > 0:
# Trailing stop for long
new_stop = float(candle.ClosePrice) - atr_value * self.TrailFactor
if new_stop > self._stop_price:
self._stop_price = new_stop
# Check stop hit
if float(candle.LowPrice) <= self._stop_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
# Check take profit
elif self._entry_price > 0 and float(candle.HighPrice) >= self._entry_price + atr_value * self.TakeFactor:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
else:
# Trailing stop for short
new_stop = float(candle.ClosePrice) + atr_value * self.TrailFactor
if self._stop_price == 0 or new_stop < self._stop_price:
self._stop_price = new_stop
# Check stop hit
if float(candle.HighPrice) >= self._stop_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
# Check take profit
elif self._entry_price > 0 and float(candle.LowPrice) <= self._entry_price - atr_value * self.TakeFactor:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
# Entry logic after managing position
if self._has_prev and self.Position == 0:
if self._prev_close > self._prev_mid and float(candle.ClosePrice) > self._prev_high:
# Bullish breakout
self.BuyMarket()
self._entry_price = float(candle.ClosePrice)
self._stop_price = self._prev_low
elif self._prev_close < self._prev_mid and float(candle.ClosePrice) < self._prev_low:
# Bearish breakout
self.SellMarket()
self._entry_price = float(candle.ClosePrice)
self._stop_price = self._prev_high
# Store for next candle
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_close = float(candle.ClosePrice)
self._prev_mid = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
self._has_prev = True
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return acb1_strategy()