首页
/
策略示例
在 GitHub 上查看
Up3x1 Premium 2vM 策略
概述
该策略是 MetaTrader 4 智能交易程序 up3x1_Premium_2vM 的等价移植版本。它只交易单一品种,并且任何时刻最多持有一个仓位。入场条件结合了平滑移动平均线、强势 K 线区间以及日线级别的午夜突破过滤器。风控方面使用以“价格点”为单位的固定止盈与止损距离,可选的移动止损会在行情向持仓方向运行后持续收紧,完全复刻原始 EA 的行为。
工作原理
主时间框可配置,默认对应原始 EA 的图表周期。策略在主时间框上绑定两个典型价的平滑移动平均线(SMMA),周期分别为 12 和 26。
额外订阅一份日线数据流,重建 MQL 代码中使用的 PERIOD_D1 数据,并驱动 10 周期的日线简单移动平均线。
在空仓时评估最近两根已完成 K 线及缓存的 SMMA 数值:
做多条件 :快线向上穿越慢线且两根 K 线的开盘价逐步抬高;或者最近一根 K 线满足多头区间/实体阈值;或者上一根日线实体上涨且振幅大于阈值。原 EA 还比较了日线均线与买价,该判断在 MQL 中始终为真,因此为了兼容性仍予保留。
做空条件 :与多头逻辑对称,使用空头区间和均线向下交叉判断。
只要任一多头条件满足就市价买入,否则若满足任一空头条件则市价卖出。下单前会根据交易所的成交量步长对手数进行归一化处理。
持仓期间持续监控上一根 K 线的快/慢 SMMA 值。当两者的绝对差小于 ConvergenceTolerance 时立即平仓,对应原 EA 中“均线相等即平仓”的规则。
移动止损模块跟踪平均持仓价。一旦价格突破设定的距离就上调/下调止损,使其始终与行情保持固定间距;当行情触及该价位时立即离场,等效于 MQL 中反复 OrderModify 的行为。
参数
名称
默认值
说明
CandleType
TimeFrame(1h)
主时间框。
FastMaPeriod
12
快速 SMMA 的周期(典型价)。
SlowMaPeriod
26
慢速 SMMA 的周期(典型价)。
RangeThreshold
0.0060
区间过滤器要求的最小高低点差。
BodyThreshold
0.0050
区间过滤器要求的最小实体长度。
DailyRangeThreshold
0.0060
最新日线的最小开收盘距离,用于午夜突破过滤。
TakeProfitPoints
150
以价格点表示的止盈距离,设为 0 以关闭。
StopLossPoints
100
以价格点表示的止损距离,设为 0 以关闭。
TrailingStopPoints
10
移动止损与价格之间的距离,设为 0 禁用移动止损。
TradeVolume
0.05
市价单的基础手数,下单前会做步长归一化。
ConvergenceTolerance
0.00001
触发平仓时允许的快慢均线最大差值。
备注
保留了原 EA 中“日线均线与买价比较永远为真”的特性,以确保回测结果一致。
止盈/止损通过 StartProtection 注册,可自动适配券商的价格步长。
移动止损只有在 TrailingStopPoints 为正且 Security.PriceStep 有效时才会生效;缺少任一信息都会禁用该功能。
手数归一化遵循交易所限制(VolumeStep、VolumeMin、VolumeMax)。若需要百分比仓位,可在此基础上扩展自定义逻辑并传入负值。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Up3x1Premium2VmStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
private int _cooldownRemaining;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Up3x1Premium2VmStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = default;
_prevSlow = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevFast = fast;
_prevSlow = slow;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class up3x1_premium_2vm_strategy(Strategy):
def __init__(self):
super(up3x1_premium_2vm_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 80) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 100) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(up3x1_premium_2vm_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(up3x1_premium_2vm_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._cooldown_remaining = 0
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.process_candle).Start()
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast)
slow_val = float(slow)
if not self._has_prev:
self._prev_fast = fast_val
self._prev_slow = slow_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return up3x1_premium_2vm_strategy()