Lucky Code é um scalper de curto prazo convertido do consultor especialista MetaTrader "Lucky_code" original. A estratégia observa os extremos do spread e reage quando o melhor pedido salta acima ou o melhor lance cai abaixo da cotação anterior por uma distância configurável. Todas as negociações são fechadas de forma agressiva: os lucros são obtidos imediatamente quando o preço sobe favoravelmente, enquanto as perdas são reduzidas quando uma excursão adversa viola um limite de proteção.
Dados e execução
Dados de mercado: requer um fluxo constante de cotações de Nível 1 para ler os melhores valores de compra e venda mais recentes.
Tipos de ordens: usa ordens de mercado para cada entrada e saída para espelhar a execução baseada em ticks da versão MQL.
Modo de posição: suporta contas de compensação e de hedge. Vários preenchimentos se acumulam em uma única posição líquida que é gerenciada como um bloco.
Parâmetros
Pontos de deslocamento – número mínimo de pontos (pips) entre cotações consecutivas que desbloqueia uma nova entrada. Valores mais altos reduzem a frequência comercial e a sensibilidade ao ruído.
Pontos limites – distância adversa máxima permitida antes do fechamento forçado das posições. O valor é convertido em unidades de preço com o tamanho do tick do instrumento.
Lógica de negociação
Inicialização
Converte parâmetros baseados em pontos em compensações de preços reais usando o tamanho do tick de segurança.
Assina os dados do Nível 1 e redefine os buffers internos para o último lance e venda visto.
Regras de entrada
Quando a melhor venda avança pelo menos o deslocamento configurado acima da venda anterior, a estratégia abre uma posição curta (correspondendo ao comportamento original EA que vende após picos ascendentes).
Quando a melhor oferta cai pelo menos na mesma mudança da oferta anterior, a estratégia abre uma posição longa para capturar a recuperação.
Dimensionamento de volume
Começa na propriedade da estratégia Volume.
Se o valor do portfólio estiver disponível, o tamanho é aumentado para round(Equity / 10,000, 1) lotes, emulando o dimensionamento baseado em margem de MetaTrader.
Regras de saída
A exposição longa é fechada imediatamente quando o lance excede o preço médio de entrada ou o pedido desce pelo limite de perda configurado.
A exposição curta é encerrada quando o preço de venda cai abaixo do preço de entrada ou o lance sobe acima dele até o limite de perda.
Notas de implementação
A estratégia reage a cada atualização de cotação, portanto, considere limitar feeds barulhentos ou aumentar o parâmetro de mudança em ambientes de produção.
Como as ordens de mercado são usadas tanto para abertura quanto para fechamento de negociações, garanta liquidez suficiente para evitar picos de derrapagem durante saltos rápidos nas cotações.
Controles adicionais de risco em nível de portfólio (stop diário, rebaixamento máximo, etc.) são recomendados ao executar a estratégia ao 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()