Die KA-Gold Bot-Strategie ist eine direkte Portierung des MetaTrader Expertenberaters „KA-Gold Bot“. Es handelt mit Ausbrüchen eines benutzerdefinierten Kanals im Keltner-Stil und gleicht Signale mit mittelfristigen Trendfiltern ab. Der Port basiert auf StockSharp High-Level-Kerzenabonnements, Indikatorbindungen und Strategieparametern, sodass das Verhalten über die Benutzeroberfläche konfigurierbar und für die Optimierung bereit bleibt.
Handelslogik
Berechnen Sie drei exponentielle gleitende Durchschnitte (EMA):
EMA(10) für eine schnelle Momentum-Bestätigung.
EMA(200), um den Trend zu höheren Zeitrahmen zu erkennen.
EMA(Punkt) als Mittelpunkt des Kanals; Die gleiche Länge wird verwendet, um den Kerzenbereich (Hoch-Tief) zu mitteln.
Mitteln Sie den Tagesbereich mit einem einfachen gleitenden Durchschnitt, um dynamische Hüllkurven zu bilden:
Oberes Band = EMA(Punkt) + SMA(Hoch-Tief, Punkt).
Unteres Band = EMA(Periode) − SMA(Hoch-Tief, Periode).
Ein langes Setup erfordert bei der letzten geschlossenen Kerze Folgendes:
Schlusskurs über dem oberen Band.
Schlusskurs über EMA(200).
EMA(10) kreuzte von unterhalb des vorherigen oberen Bandes nach oberhalb des neuesten oberen Bandes.
Ein kurzer Aufbau spiegelt die Regeln wider:
Schlusskurs unterhalb des unteren Bandes.
Schlusskurs unter EMA(200).
EMA(10) hat von oberhalb des vorherigen unteren Bandes bis unterhalb des neuesten unteren Bandes gekreuzt.
Es kann jeweils nur eine Stelle offen sein; Gegensignale werden ignoriert, bis die Strategie flach ist.
Positionsgrößen
Es werden zwei Volumenmodelle unterstützt:
Fester Losmodus – verwenden Sie den Parameter BaseVolume direkt.
Risikoprozentsatzmodus – bei UseRiskPercent = true wird der Free-Equity-Proxy (Portfolio.CurrentValue oder Portfolio.BeginValue) mit RiskPercent multipliziert. Das Ergebnis wird mit 100.000 skaliert (Lotkonvention MetaTrader) und auf Vielfache von BaseVolume gerundet, wobei Security.MinVolume, Security.MaxVolume und Security.VolumeStep berücksichtigt werden.
Risikomanagement
Stop-Loss- und Take-Profit-Offsets werden in Pips definiert. Pips werden mithilfe des Sicherheitsschritts in absolute Preisabstände umgerechnet. Drei- und fünfdezimale Forex-Symbole verwenden die MetaTrader-Regel pip = step × 10.
Erste Schutzaufträge werden unmittelbar nach der ersten Ausführung registriert und mit der aktuellen Positionsgröße synchronisiert.
Trailing Stops werden aktiviert, sobald der nicht realisierte Gewinn TrailingTriggerPips erreicht:
Long-Positionen bleiben zurück, indem der Stopp TrailingStopPips vom Schluss entfernt bleibt.
Short-Positionen nutzen den symmetrischen Abstand über dem Markt.
Der Stopp wird nur verschoben, wenn sich der Abstand um mindestens TrailingStepPips verbessert, um eine Übertriggerung zu vermeiden.
Wenn die Position geschlossen wird, werden ausstehende Schutzaufträge automatisch storniert.
Sitzungs- und Spread-Filter
Optionales Handelsfenster, gesteuert durch UseTimeFilter, StartHour, StartMinute, EndHour und EndMinute (Inklusiv-Exklusiv-Fenster). Nachtfenster werden unterstützt (Ende früher als Start nach Mitternacht).
Ein optionaler Spread-Filter lehnt neue Eingaben ab, wenn der aktuelle Spread (Differenz zwischen bestem Brief- und Geldkurs in Preisschritten) MaxSpreadPoints überschreitet.
Implementierungshinweise
Kerzen werden über SubscribeCandles().Bind(...) verarbeitet; Die Werte EMA(10) und EMA(200) kommen über die Bindung an, während der Kanal EMA und der Bereichsdurchschnitt innerhalb des Handlers ohne Verwendung von GetValue aktualisiert werden.
Der Indikatorstatus wird nur durch Skalarfelder gespeichert, die die Verschiebungslogik MetaTrader iClose und CopyBuffer widerspiegeln, wobei die Anforderung zum Vergleich der letzten beiden geschlossenen Balken erhalten bleibt.
Die Schutz- und Nachfolgelogik verwendet High-Level-Reihenfolgehelfer (BuyStop, SellStop, BuyLimit, SellLimit), um die PositionModify-Aufrufe von MetaTrader zu spiegeln.
Die Portfoliogröße hängt von den verfügbaren Aktieninformationen in StockSharp ab; Fehlt es, greift die Strategie auf das Festvolumen zurück.
Parameter
Parameter
Beschreibung
Standard
KeltnerPeriod
Zeitraum für den Kanal EMA und Bereichsglättung.
50
FastEmaPeriod
Länge des schnellen EMA-Filters.
10
SlowEmaPeriod
Länge des langsamen EMA-Trendfilters.
200
BaseVolume
Mindestbestellmenge (Losgröße).
0,01
UseRiskPercent
Aktivieren Sie die ausgleichsbasierte Positionsgrößenbestimmung.
wahr
RiskPercent
Prozentsatz des pro Trade verwendeten Eigenkapitals, wenn die Risikogrößenbestimmung aktiv ist.
1
StopLossPips
Stop-Loss-Distanz in Pips.
500
TakeProfitPips
Take-Profit-Distanz in Pips (0 deaktiviert).
500
TrailingTriggerPips
Gewinnschwelle, um den Trailing Stop zu aktivieren.
300
TrailingStopPips
Der Abstand wird durch den Trailing Stop aufrechterhalten, sobald er aktiviert ist.
300
TrailingStepPips
Minimale Verbesserung, bevor der Stopp verschoben wird.
100
UseTimeFilter
Schalten Sie den Handelssitzungsfilter um.
wahr
StartHour, StartMinute
Startzeit der Sitzung.
02:30
EndHour, EndMinute
Endzeit der Sitzung (exklusiv).
21:00
MaxSpreadPoints
Maximal zulässiger Spread in Preisschritten (0 = deaktiviert).
65
CandleType
Für Signalkerzen verwendeter Zeitrahmen.
5-Minuten-Kerzen
Unterschiede im Vergleich zur MetaTrader-Version
Die Trailing-Stop-Implementierung erstellt die PositionModify-Sequenz mithilfe von StockSharp-Stop-Orders neu; Die Funktionalität ist gleichwertig, basiert jedoch auf von der Börse bestätigten Bestellungen.
MetaTrader berechnete Kanalbreite aus dem durchschnittlichen Hoch-Tief-Bereich; Der Port reproduziert die gleiche Mittelung mit einem einfachen gleitenden Durchschnitt, um Ausbrüche identisch zu halten.
Bei der Risikodimensionierung wird auf Portfolioeigenkapital statt auf freie Marge zurückgegriffen. Diese Annäherung entspricht der Absicht (Prozentsatz des Kapitals), kann jedoch abweichen, wenn keine Leverage-spezifischen Margin-Daten verfügbar sind.
Spread-Prüfungen verwenden Security.BestAskPrice und Security.BestBidPrice. Wenn keine Tiefe verfügbar ist, wird der Filter übersprungen und spiegelt die Option „Floating Spread“ im Original-Experten wider.
Nutzungstipps
Hängen Sie die Strategie an Instrumente an, bei denen die Pip-Definition den Forex-Konventionen (3 oder 5 Dezimalstellen) folgt, um die Risikoparameter mit dem ursprünglichen Experten in Einklang zu bringen.
Optimieren Sie die EMA-Zeiträume und die Kanallänge für Nicht-Gold-Instrumente, da die Quellstrategie auf XAUUSD abgestimmt wurde.
Überwachen Sie das Portfoliofenster, um sicherzustellen, dass Aktienwerte ausgefüllt sind, wenn UseRiskPercent aktiviert ist.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Port of the MetaTrader expert "KA-Gold Bot".
/// Trades breakouts of a Keltner-style channel confirmed by trend filters from fast and slow EMA.
/// Buys when close breaks above upper Keltner band and fast EMA is above slow EMA.
/// Sells when close breaks below lower Keltner band and fast EMA is below slow EMA.
/// Exits when price crosses the opposite Keltner band or when EMA trend reverses.
/// </summary>
public class KAGoldBotStrategy : Strategy
{
private readonly StrategyParam<int> _keltnerPeriod;
private readonly StrategyParam<int> _fastEmaPeriod;
private readonly StrategyParam<int> _slowEmaPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _bandMultiplier;
private ExponentialMovingAverage _fastEma;
private ExponentialMovingAverage _slowEma;
private ExponentialMovingAverage _keltnerEma;
private SimpleMovingAverage _rangeAverage;
private bool _prevAboveUpper;
private bool _prevBelowLower;
private decimal _entryPrice;
/// <summary>
/// Keltner channel length used for the midline EMA and range average.
/// </summary>
public int KeltnerPeriod
{
get => _keltnerPeriod.Value;
set => _keltnerPeriod.Value = value;
}
/// <summary>
/// Fast EMA period for crossover signal.
/// </summary>
public int FastEmaPeriod
{
get => _fastEmaPeriod.Value;
set => _fastEmaPeriod.Value = value;
}
/// <summary>
/// Slow EMA period for trend filter.
/// </summary>
public int SlowEmaPeriod
{
get => _slowEmaPeriod.Value;
set => _slowEmaPeriod.Value = value;
}
/// <summary>
/// Multiplier for Keltner channel band width.
/// </summary>
public decimal BandMultiplier
{
get => _bandMultiplier.Value;
set => _bandMultiplier.Value = value;
}
/// <summary>
/// Candle type used for signal generation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public KAGoldBotStrategy()
{
_keltnerPeriod = Param(nameof(KeltnerPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Keltner Period", "Length of the EMA and range average", "Indicators");
_fastEmaPeriod = Param(nameof(FastEmaPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Period of the fast EMA filter", "Indicators");
_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Period of the slow EMA trend filter", "Indicators");
_bandMultiplier = Param(nameof(BandMultiplier), 3m)
.SetGreaterThanZero()
.SetDisplay("Band Multiplier", "Multiplier for Keltner band width", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastEma = new ExponentialMovingAverage { Length = FastEmaPeriod };
_slowEma = new ExponentialMovingAverage { Length = SlowEmaPeriod };
_keltnerEma = new ExponentialMovingAverage { Length = KeltnerPeriod };
_rangeAverage = new SimpleMovingAverage { Length = KeltnerPeriod };
_prevAboveUpper = false;
_prevBelowLower = false;
_entryPrice = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastEma, _slowEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastEma);
DrawIndicator(area, _slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
// Process Keltner EMA and range average manually (IsFinal=true for finished candles)
var midResult = _keltnerEma.Process(new DecimalIndicatorValue(_keltnerEma, close, candle.OpenTime) { IsFinal = true });
var rangeResult = _rangeAverage.Process(new DecimalIndicatorValue(_rangeAverage, candle.HighPrice - candle.LowPrice, candle.OpenTime) { IsFinal = true });
if (!_keltnerEma.IsFormed || !_rangeAverage.IsFormed)
return;
var mid = midResult.GetValue<decimal>();
var avgRange = rangeResult.GetValue<decimal>();
var upper = mid + avgRange * BandMultiplier;
var lower = mid - avgRange * BandMultiplier;
var aboveUpper = close > upper;
var belowLower = close < lower;
// Exit logic: close crosses opposite band
if (Position > 0 && close < lower)
{
SellMarket();
}
else if (Position < 0 && close > upper)
{
BuyMarket();
}
// Entry logic: Keltner breakout + EMA trend confirmation
if (Position == 0)
{
// Buy: close breaks above upper band, fast EMA above slow EMA
if (!_prevAboveUpper && aboveUpper && fastValue > slowValue)
{
BuyMarket();
_entryPrice = close;
}
// Sell: close breaks below lower band, fast EMA below slow EMA
else if (!_prevBelowLower && belowLower && fastValue < slowValue)
{
SellMarket();
_entryPrice = close;
}
}
_prevAboveUpper = aboveUpper;
_prevBelowLower = belowLower;
}
/// <inheritdoc />
protected override void OnReseted()
{
_fastEma = null;
_slowEma = null;
_keltnerEma = null;
_rangeAverage = null;
_prevAboveUpper = false;
_prevBelowLower = false;
_entryPrice = 0;
base.OnReseted();
}
}
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, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ka_gold_bot_strategy(Strategy):
def __init__(self):
super(ka_gold_bot_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._keltner_period = self.Param("KeltnerPeriod", 50)
self._fast_ema_period = self.Param("FastEmaPeriod", 10)
self._slow_ema_period = self.Param("SlowEmaPeriod", 30)
self._band_multiplier = self.Param("BandMultiplier", 3.0)
self._keltner_ema_value = None
self._range_buffer = []
self._candle_count = 0
self._prev_above_upper = False
self._prev_below_lower = False
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def KeltnerPeriod(self):
return self._keltner_period.Value
@KeltnerPeriod.setter
def KeltnerPeriod(self, value):
self._keltner_period.Value = value
@property
def FastEmaPeriod(self):
return self._fast_ema_period.Value
@FastEmaPeriod.setter
def FastEmaPeriod(self, value):
self._fast_ema_period.Value = value
@property
def SlowEmaPeriod(self):
return self._slow_ema_period.Value
@SlowEmaPeriod.setter
def SlowEmaPeriod(self, value):
self._slow_ema_period.Value = value
@property
def BandMultiplier(self):
return self._band_multiplier.Value
@BandMultiplier.setter
def BandMultiplier(self, value):
self._band_multiplier.Value = value
def OnReseted(self):
self._keltner_ema_value = None
self._range_buffer = []
self._candle_count = 0
self._prev_above_upper = False
self._prev_below_lower = False
self._entry_price = 0.0
super(ka_gold_bot_strategy, self).OnReseted()
def OnStarted2(self, time):
super(ka_gold_bot_strategy, self).OnStarted2(time)
self._keltner_ema_value = None
self._range_buffer = []
self._candle_count = 0
self._prev_above_upper = False
self._prev_below_lower = False
self._entry_price = 0.0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastEmaPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowEmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self._process_candle).Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
candle_range = high - low
period = self.KeltnerPeriod
mult = float(self.BandMultiplier)
# EMA for Keltner midline (manual computation)
if self._keltner_ema_value is None:
self._keltner_ema_value = close
else:
k = 2.0 / (period + 1.0)
self._keltner_ema_value = close * k + self._keltner_ema_value * (1.0 - k)
# SMA for range average (manual computation)
self._range_buffer.append(candle_range)
if len(self._range_buffer) > period:
self._range_buffer.pop(0)
self._candle_count += 1
if self._candle_count < period:
return
mid = self._keltner_ema_value
avg_range = sum(self._range_buffer) / len(self._range_buffer)
upper = mid + avg_range * mult
lower = mid - avg_range * mult
above_upper = close > upper
below_lower = close < lower
fast_val = float(fast_value)
slow_val = float(slow_value)
# Exit logic: close crosses opposite band
if self.Position > 0 and close < lower:
self.SellMarket()
elif self.Position < 0 and close > upper:
self.BuyMarket()
# Entry logic: Keltner breakout + EMA trend confirmation
if self.Position == 0:
if not self._prev_above_upper and above_upper and fast_val > slow_val:
self.BuyMarket()
self._entry_price = close
elif not self._prev_below_lower and below_lower and fast_val < slow_val:
self.SellMarket()
self._entry_price = close
self._prev_above_upper = above_upper
self._prev_below_lower = below_lower
def CreateClone(self):
return ka_gold_bot_strategy()