Lucky Code 策略
Lucky Code 源自 MetaTrader 的「Lucky_code」专家顾问,是一套超短线突破型剥头皮策略。它实时监控买卖价差的极值,当最优卖价相对上一笔报价跃升、或最优买价下挫超过设定的点数距离时立即出手。策略的持仓管理非常激进:行情一旦朝有利方向跳动就平仓锁定利润,若出现超过阈值的不利波动则立即止损。
数据与执行
- 市场数据:需要 Level 1 报价流以获取最新的最优买价和卖价。
- 委托类型:所有进出场均使用市价单,完整复刻 MQL 版本的逐笔执行方式。
- 持仓模式:兼容净额和锁仓账户。多笔成交会合并为单一净持仓并按整体处理。
参数
- Shift points – 连续两笔报价之间触发交易所需的最小点数差。数值越大,交易频率越低,噪声过滤越强。
- Limit points – 容许的最大不利点数。一旦亏损幅度超过该数值,仓位将被强制平掉。该参数会根据标的的最小价位单位转换为实际价格距离。
交易逻辑
- 初始化
- 使用品种的最小变动价位将点数参数换算成真实价格距离。
- 订阅 Level 1 数据,并清空内部记录的上一笔买、卖价。
- 入场规则
- 当最优卖价相较前一笔报价上跳至少指定点数时,策略建立空头仓位(与原版 EA 在上冲后做空的逻辑一致)。
- 当最优买价较前一笔报价下挫达到同样的点数时,策略建立多头仓位,博取反弹。
- 仓位大小
- 以策略的
Volume属性作为默认下单数量。 - 如果能够获取到投资组合市值,会将数量调整为
round(Equity / 10,000, 1)手,以模拟 MetaTrader 中AccountFreeMargin/10000的头寸分配。
- 以策略的
- 出场规则
- 多头仓位在买价高于平均建仓价时立即平仓;若卖价跌破建仓价达到限额,也会强制止损。
- 空头仓位在卖价低于建仓价时立即平仓;若买价上穿建仓价达到限额,则触发止损。
实践提示
- 策略对每一次报价变动都做出响应,实盘时可适当增大 Shift 参数或对高噪声行情做节流处理。
- 使用市价单意味着需要较好的流动性,以避免行情剧烈波动时的滑点风险。
- 建议结合额外的组合风险管理(如日内止损、最大回撤限制、持仓数量上限)共同使用。
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()