Lucky Code es un revendedor de ruptura a corto plazo convertido del asesor experto MetaTrader "Lucky_code" original. La estrategia observa los extremos de los diferenciales y reacciona cuando la mejor oferta salta por encima o la mejor oferta cae por debajo de la cotización anterior en una distancia configurable. Todas las operaciones se cierran agresivamente: las ganancias se obtienen inmediatamente una vez que el precio oscila favorablemente, mientras que las pérdidas se reducen cuando una excursión adversa supera un límite de protección.
Datos y ejecución
Datos de mercado: requiere un flujo constante de cotizaciones de Nivel 1 para leer los mejores valores de oferta y demanda más recientes.
Tipos de órdenes: utiliza órdenes de mercado para cada entrada y salida para reflejar la ejecución basada en ticks de la versión MQL.
Modo de posición: admite cuentas de compensación y de cobertura. Múltiples rellenos se acumulan en una única posición neta que se gestiona como un bloque.
Parámetros
Puntos de cambio: número mínimo de puntos (pips) entre cotizaciones consecutivas que desbloquea una nueva entrada. Los valores más altos reducen la frecuencia comercial y la sensibilidad al ruido.
Puntos límite: distancia adversa máxima permitida antes de que se cierren a la fuerza las posiciones. El valor se convierte en unidades de precio con el tamaño del tick del instrumento.
Lógica comercial
Inicialización
Convierte parámetros basados en puntos en compensaciones de precios reales utilizando el tamaño del tick de seguridad.
Se suscribe a los datos de Nivel 1 y restablece los buffers internos para la última oferta y demanda vista.
Reglas de entrada
Cuando la mejor demanda avanza al menos el desplazamiento configurado por encima de la demanda anterior, la estrategia abre una posición corta (que coincide con el comportamiento original de EA que vende después de picos alcistas).
Cuando la mejor oferta cae al menos en el mismo desplazamiento que la oferta anterior, la estrategia abre una posición larga para capturar el rebote.
Tamaño del volumen
Comienza desde la propiedad de la estrategia Volume.
Si el valor de la cartera está disponible, el tamaño aumenta a round(Equity / 10,000, 1) lotes, emulando el tamaño basado en margen MetaTrader.
Reglas de salida
La exposición larga se cierra inmediatamente una vez que la oferta excede el precio de entrada promedio o la demanda baja por el límite de pérdida configurado.
La exposición corta se cierra una vez que la demanda cae por debajo del precio de entrada o la oferta lo supera en el límite de pérdidas.
Notas de implementación
La estrategia reacciona ante cada actualización de cotización, así que considere limitar las transmisiones ruidosas o aumentar el parámetro de cambio en entornos de producción.
Debido a que las órdenes de mercado se utilizan tanto para abrir como para cerrar operaciones, asegúrese de tener suficiente liquidez para evitar picos de deslizamiento durante los saltos rápidos de las cotizaciones.
Se recomiendan controles de riesgo adicionales a nivel de cartera (parada diaria, reducción máxima, etc.) al ejecutar la estrategia en vivo.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Momentum strategy that opens trades when candle price jumps reach a configurable distance and manages exits with profit and drawdown filters.
/// </summary>
public class LuckyCodeStrategy : Strategy
{
private readonly StrategyParam<int> _shiftPoints;
private readonly StrategyParam<int> _limitPoints;
private decimal? _previousClose;
private decimal _shiftThreshold;
private decimal _limitThreshold;
private decimal _entryPrice;
private bool _thresholdsReady;
private int _holdBars;
/// <summary>
/// Minimum price movement in points required before opening a new trade.
/// </summary>
public int ShiftPoints
{
get => _shiftPoints.Value;
set => _shiftPoints.Value = value;
}
/// <summary>
/// Maximum adverse excursion in points tolerated before forcing an exit.
/// </summary>
public int LimitPoints
{
get => _limitPoints.Value;
set => _limitPoints.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public LuckyCodeStrategy()
{
_shiftPoints = Param(nameof(ShiftPoints), 3)
.SetGreaterThanZero()
.SetDisplay("Shift points", "Minimum price jump required to trigger entries", "Trading")
.SetOptimize(1, 20, 1);
_limitPoints = Param(nameof(LimitPoints), 18)
.SetGreaterThanZero()
.SetDisplay("Limit points", "Maximum number of points allowed against the position", "Risk management")
.SetOptimize(5, 100, 5);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousClose = null;
_shiftThreshold = 0m;
_limitThreshold = 0m;
_entryPrice = 0m;
_thresholdsReady = false;
_holdBars = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Subscribe to candles and process each finished candle.
var tf = TimeSpan.FromMinutes(5).TimeFrame();
SubscribeCandles(tf)
.Bind(ProcessCandle)
.Start();
}
private void EnsureThresholds(decimal price)
{
if (_thresholdsReady)
return;
if (price <= 0m)
return;
// Use percentage of price. ShiftPoints=3 means 3% shift, LimitPoints=18 means 18% limit.
_shiftThreshold = price * ShiftPoints * 0.01m;
_limitThreshold = price * LimitPoints * 0.01m;
_thresholdsReady = true;
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
EnsureThresholds(close);
if (!_thresholdsReady)
return;
// Count hold bars for position management.
if (Position != 0)
_holdBars++;
if (_previousClose is decimal prevClose)
{
var delta = close - prevClose;
// Only enter if flat.
if (Position == 0)
{
// Price dropped sharply -> buy on expected rebound.
if (-delta >= _shiftThreshold)
{
BuyMarket();
_entryPrice = close;
_holdBars = 0;
LogInfo($"Buy triggered by fast price drop. Price={close:0.#####}");
}
// Price rose sharply -> sell on expected reversal.
else if (delta >= _shiftThreshold)
{
SellMarket();
_entryPrice = close;
_holdBars = 0;
LogInfo($"Sell triggered by fast price rise. Price={close:0.#####}");
}
}
}
_previousClose = close;
TryClosePosition(close);
}
private void TryClosePosition(decimal currentPrice)
{
if (Position == 0)
return;
var avgPrice = _entryPrice;
if (avgPrice <= 0m)
return;
// Minimum hold of 3 bars before checking exit.
if (_holdBars < 3)
return;
// Use half of shift threshold as profit target.
var profitTarget = _shiftThreshold * 0.5m;
if (Position > 0)
{
// Close long on profit target or drawdown limit.
if (currentPrice - avgPrice >= profitTarget)
{
SellMarket();
_holdBars = 0;
LogInfo($"Closed long on profit. Price={currentPrice:0.#####}");
}
else if (_limitThreshold > 0m && avgPrice - currentPrice >= _limitThreshold)
{
SellMarket();
_holdBars = 0;
LogInfo($"Closed long on drawdown limit. Price={currentPrice:0.#####}");
}
}
else if (Position < 0)
{
// Close short on profit target or drawdown limit.
if (avgPrice - currentPrice >= profitTarget)
{
BuyMarket();
_holdBars = 0;
LogInfo($"Closed short on profit. Price={currentPrice:0.#####}");
}
else if (_limitThreshold > 0m && currentPrice - avgPrice >= _limitThreshold)
{
BuyMarket();
_holdBars = 0;
LogInfo($"Closed short on drawdown limit. Price={currentPrice:0.#####}");
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class lucky_code_strategy(Strategy):
"""Momentum strategy that opens trades when candle price jumps reach a configurable distance
and manages exits with profit and drawdown filters."""
def __init__(self):
super(lucky_code_strategy, self).__init__()
self._shift_points = self.Param("ShiftPoints", 3) \
.SetGreaterThanZero() \
.SetDisplay("Shift points", "Minimum price jump required to trigger entries", "Trading") \
.SetOptimize(1, 20, 1)
self._limit_points = self.Param("LimitPoints", 18) \
.SetGreaterThanZero() \
.SetDisplay("Limit points", "Maximum number of points allowed against the position", "Risk management") \
.SetOptimize(5, 100, 5)
self._previous_close = None
self._shift_threshold = 0.0
self._limit_threshold = 0.0
self._entry_price = 0.0
self._thresholds_ready = False
self._hold_bars = 0
@property
def ShiftPoints(self):
return self._shift_points.Value
@ShiftPoints.setter
def ShiftPoints(self, value):
self._shift_points.Value = value
@property
def LimitPoints(self):
return self._limit_points.Value
@LimitPoints.setter
def LimitPoints(self, value):
self._limit_points.Value = value
def OnReseted(self):
super(lucky_code_strategy, self).OnReseted()
self._previous_close = None
self._shift_threshold = 0.0
self._limit_threshold = 0.0
self._entry_price = 0.0
self._thresholds_ready = False
self._hold_bars = 0
def OnStarted2(self, time):
super(lucky_code_strategy, self).OnStarted2(time)
tf = DataType.TimeFrame(TimeSpan.FromMinutes(5))
subscription = self.SubscribeCandles(tf)
subscription.Bind(self._process_candle).Start()
def _ensure_thresholds(self, price):
if self._thresholds_ready:
return
if price <= 0.0:
return
# Use percentage of price. ShiftPoints=3 means 3% shift, LimitPoints=18 means 18% limit.
self._shift_threshold = price * self.ShiftPoints * 0.01
self._limit_threshold = price * self.LimitPoints * 0.01
self._thresholds_ready = True
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
self._ensure_thresholds(close)
if not self._thresholds_ready:
return
# Count hold bars for position management.
if self.Position != 0:
self._hold_bars += 1
if self._previous_close is not None:
prev_close = self._previous_close
delta = close - prev_close
# Only enter if flat.
if self.Position == 0:
# Price dropped sharply -> buy on expected rebound.
if (-delta) >= self._shift_threshold:
self.BuyMarket()
self._entry_price = close
self._hold_bars = 0
self.LogInfo("Buy triggered by fast price drop. Price=" + str(close))
# Price rose sharply -> sell on expected reversal.
elif delta >= self._shift_threshold:
self.SellMarket()
self._entry_price = close
self._hold_bars = 0
self.LogInfo("Sell triggered by fast price rise. Price=" + str(close))
self._previous_close = close
self._try_close_position(close)
def _try_close_position(self, current_price):
if self.Position == 0:
return
avg_price = self._entry_price
if avg_price <= 0.0:
return
# Minimum hold of 3 bars before checking exit.
if self._hold_bars < 3:
return
# Use half of shift threshold as profit target.
profit_target = self._shift_threshold * 0.5
if self.Position > 0:
# Close long on profit target or drawdown limit.
if current_price - avg_price >= profit_target:
self.SellMarket()
self._hold_bars = 0
self.LogInfo("Closed long on profit. Price=" + str(current_price))
elif self._limit_threshold > 0.0 and avg_price - current_price >= self._limit_threshold:
self.SellMarket()
self._hold_bars = 0
self.LogInfo("Closed long on drawdown limit. Price=" + str(current_price))
elif self.Position < 0:
# Close short on profit target or drawdown limit.
if avg_price - current_price >= profit_target:
self.BuyMarket()
self._hold_bars = 0
self.LogInfo("Closed short on profit. Price=" + str(current_price))
elif self._limit_threshold > 0.0 and current_price - avg_price >= self._limit_threshold:
self.BuyMarket()
self._hold_bars = 0
self.LogInfo("Closed short on drawdown limit. Price=" + str(current_price))
def CreateClone(self):
return lucky_code_strategy()