Estrategia de robot KA-Gold
La Estrategia KA-Gold Bot es una adaptación directa del asesor experto MetaTrader "KA-Gold Bot". Intercambia rupturas de un canal personalizado estilo Keltner y alinea señales con filtros de tendencia a mediano plazo. El puerto se basa en StockSharp suscripciones de velas de alto nivel, enlaces de indicadores y parámetros de estrategia para que el comportamiento siga siendo configurable desde la interfaz de usuario y listo para la optimización.
Lógica de trading
- Calcule tres medias móviles exponenciales (EMA):
- EMA(10) para una confirmación rápida del impulso.
- EMA(200) para detectar la tendencia de período de tiempo más alto.
- EMA(punto) como centro del canal; se utiliza la misma longitud para promediar el rango de la vela (alto-bajo).
- Promedie el rango diario con una media móvil simple para formar envolventes dinámicas:
- Banda superior = EMA(período) + SMA(alto-bajo, punto).
- Banda inferior = EMA(período) − SMA(alto-bajo, punto).
- Una configuración larga requiere todo lo siguiente en la última vela cerrada:
- Precio de cierre por encima de la banda superior.
- Precio de cierre superior a EMA(200).
- EMA(10) cruzó desde debajo de la banda superior anterior hasta encima de la última banda superior.
- Una configuración corta refleja las reglas:
- Precio de cierre por debajo de la banda inferior.
- Precio de cierre por debajo de EMA(200).
- EMA(10) cruzó desde arriba de la banda inferior anterior hasta debajo de la última banda inferior.
- Sólo puede haber una posición abierta a la vez; Las señales opuestas se ignoran hasta que la estrategia sea plana.
Dimensionamiento de posiciones
Se admiten dos modelos de volumen:
- Modo de lote fijo: utilice el parámetro
BaseVolume directamente.
- Modo de porcentaje de riesgo: cuando
UseRiskPercent = true, el proxy de capital gratuito (Portfolio.CurrentValue o Portfolio.BeginValue) se multiplica por RiskPercent. El resultado se escala en 100.000 (MetaTrader convención de lote) y se redondea a múltiplos de BaseVolume, respetando Security.MinVolume, Security.MaxVolume y Security.VolumeStep.
Gestión del riesgo
- Las compensaciones de stop-loss y take-profit se definen en pips. Los pips se convierten a distancias de precios absolutas mediante el paso de seguridad. Los símbolos de divisas de tres y cinco decimales reutilizan la regla MetaTrader
pip = step × 10.
- Las órdenes de protección iniciales se registran inmediatamente después del primer cumplimiento y se mantienen sincronizadas con el tamaño de la posición actual.
- Los trailingstops se activan una vez que las ganancias no realizadas alcanzan
TrailingTriggerPips:
- Las posiciones largas mantienen el stop
TrailingStopPips alejado del cierre.
- Las posiciones cortas utilizan la distancia simétrica por encima del mercado.
- El tope se mueve solo si la distancia mejora al menos
TrailingStepPips para evitar una activación excesiva.
- Cuando se cierra la posición, las órdenes de protección pendientes se cancelan automáticamente.
Filtros de sesión y difusión
- Ventana de negociación opcional controlada por
UseTimeFilter, StartHour, StartMinute, EndHour y EndMinute (ventana inclusiva-exclusiva). Se admiten períodos nocturnos (finalizan antes de que el inicio finalice después de la medianoche).
- Un filtro de diferencial opcional rechaza nuevas entradas si el diferencial actual (diferencia entre la mejor oferta y la mejor oferta en incrementos de precios) excede
MaxSpreadPoints.
Notas de implementación
- Las velas se procesan a través de
SubscribeCandles().Bind(...); los valores EMA(10) y EMA(200) llegan a través del enlace, mientras que el canal EMA y el promedio del rango se actualizan dentro del controlador sin usar GetValue.
- El estado del indicador se almacena únicamente a través de campos escalares que reflejan la lógica de cambio MetaTrader
iClose y CopyBuffer, preservando el requisito de comparar las dos últimas barras cerradas.
- La lógica de seguimiento y protección utiliza asistentes de pedidos de alto nivel (
BuyStop, SellStop, BuyLimit, SellLimit) para reflejar las llamadas PositionModify de MetaTrader.
- El tamaño basado en la cartera depende de la información de capital disponible en StockSharp; si falta, la estrategia vuelve al volumen fijo.
Parámetros
| Parámetro |
Descripción |
Predeterminado |
KeltnerPeriod |
Periodo para el canal EMA y suavizado de rango. |
50 |
FastEmaPeriod |
Longitud del filtro EMA rápida. |
10 |
SlowEmaPeriod |
Longitud del filtro de tendencia EMA lenta. |
200 |
BaseVolume |
Volumen mínimo de pedido (tamaño de lote). |
0,01 |
UseRiskPercent |
Habilite el tamaño de posición basado en el equilibrio. |
cierto |
RiskPercent |
Porcentaje de capital utilizado por operación cuando el tamaño del riesgo está activo. |
1 |
StopLossPips |
Distancia de stop-loss en pips. |
500 |
TakeProfitPips |
Distancia de toma de ganancias en pips (0 inhabilitaciones). |
500 |
TrailingTriggerPips |
Umbral de beneficio para armar el trailing stop. |
300 |
TrailingStopPips |
Distancia mantenida por el trailing stop una vez armado. |
300 |
TrailingStepPips |
Mejora mínima antes de que se mueva la parada. |
100 |
UseTimeFilter |
Alternar el filtro de la sesión de negociación. |
cierto |
StartHour, StartMinute |
Hora de inicio de la sesión. |
02:30 |
EndHour, EndMinute |
Hora de finalización de la sesión (exclusiva). |
21:00 |
MaxSpreadPoints |
Spread máximo permitido en pasos de precio (0 = deshabilitado). |
65 |
CandleType |
Marco de tiempo utilizado para las velas de señal. |
velas de 5 minutos |
- La implementación del trailing-stop recrea la secuencia
PositionModify usando órdenes de stop StockSharp; La funcionalidad es equivalente pero se basa en pedidos confirmados por el intercambio.
- MetaTrader calculó el ancho del canal a partir del rango medio alto-bajo; el puerto reproduce el mismo promedio con un promedio móvil simple para mantener los desgloses idénticos.
- La dimensionamiento del riesgo accede al capital de la cartera en lugar del margen libre. Esta aproximación coincide con la intención (porcentaje de capital), pero puede diferir si no se dispone de datos de margen específicos del apalancamiento.
- Los cheques extendidos utilizan
Security.BestAskPrice y Security.BestBidPrice. Cuando la profundidad no está disponible, se omite el filtro, reflejando la opción de "extensión flotante" en el experto original.
Consejos de uso
- Adjunte la estrategia a instrumentos donde la definición de pip sigue las convenciones de Forex (3 o 5 decimales) para mantener los parámetros de riesgo alineados con los del experto original.
- Optimice los períodos EMA y la duración del canal para instrumentos que no sean de oro porque la estrategia de origen se ajustó para XAUUSD.
- Supervise la ventana de la cartera para asegurarse de que los valores de las acciones se completen cuando
UseRiskPercent esté habilitado.
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()