在 GitHub 上查看
Up3x1 移位SMA策略(MT4版本移植)
概览
- 将
MQL/8097 中的 MetaTrader 4 智能交易系统 up3x1.mq4 移植到 StockSharp 高层 API。
- 保留三个简单移动平均线(SMA)并向右偏移6根K线的原始计算方法。
- 仅在K线收盘后处理数据,模拟脚本里
Volume[0] > 1 的单次决策约束。
- 支持止盈、止损、亏损后自动减仓及可选的移动止损等风控功能。
交易逻辑
- 指标:三个带有图表偏移的SMA(默认周期分别为24、60、120)。
- 做多条件:
- 前一根K线:
SMAfast₍t-1₎ < SMAmedium₍t-1₎ < SMAslow₍t-1₎;
- 当前K线:
SMAmedium₍t₎ < SMAfast₍t₎ < SMAslow₍t₎,对应原代码的 ma1 < ma2 < ma3 && ma5 < ma4 < ma6。
- 做空条件:
- 前一根K线:
SMAfast₍t-1₎ > SMAmedium₍t-1₎ > SMAslow₍t-1₎;
- 当前K线:
SMAmedium₍t₎ > SMAfast₍t₎ > SMAslow₍t₎。
- 离场规则:
- 止盈、止损按照设定的点数并结合
Security.PriceStep 计算价格距离。
- 移动止损在浮盈超过
TrailingStopPoints 后跟踪最高/最低价。
- 当均线次序反转时强制平仓,对应 MQL 中的
OrderClose 逻辑。
仓位管理
- 当无法获取组合权益时,默认下单量为
BaseVolume(0.1手)。
- 若可读取
Portfolio.CurrentValue,则按 RiskFraction(默认 0.00002,等价于 FreeMargin * 0.02 / 1000)计算动态仓位。
- 连续亏损次数大于1时,按照
volume * losses / 3 减少下单量,与 LotsOptimized 完全一致。
- 下单量根据
Security.VolumeStep 向下取整,若低于 Security.MinVolume 则放弃交易。
参数
| 参数 |
默认值 |
说明 |
FastPeriod |
24 |
最快移位SMA的周期。 |
MediumPeriod |
60 |
中速移位SMA的周期。 |
SlowPeriod |
120 |
最慢移位SMA的周期。 |
TakeProfitPoints |
150 |
距离入场价的止盈点数。 |
StopLossPoints |
100 |
距离入场价的止损点数。 |
TrailingStopPoints |
100 |
移动止损点数(设为0可关闭)。 |
BaseVolume |
0.1 |
默认下单量及减仓后的最小值。 |
RiskFraction |
0.00002 |
组合权益乘数,用于动态仓位。 |
CandleType |
1小时K线 |
指标所使用的K线类型。 |
转换说明
- 使用
SubscribeCandles 和 Bind 实现高层事件驱动,无需自建历史缓存。
- 通过保存上一根K线的指标数值模拟 MQL 的
shift 行为。
- 以市价单执行止盈/止损/移动止损,保持与 StockSharp 抽象模型兼容。
- 代码中的注释均为英文,符合项目要求。
使用建议
- 在 StockSharp Designer 或代码中绑定策略到具体的证券与投资组合。
- 根据实际需要调整
CandleType(默认H1)。
- 根据品种最小价格变动调整各类点数参数(例如外汇常见的0.0001)。
- 不需要移动止损时将
TrailingStopPoints 设为0。
- 关注日志中的 “Enter long/short” 与 “Exit long/short” 提示以监控策略行为。
目录结构
API/3924/
├── CS/Up3x1ShiftedSmaStrategy.cs
├── README.md
├── README_zh.md
└── README_ru.md
免责声明
量化交易存在较大风险。该策略仅用于教学示例,实盘使用前必须充分回测与模拟验证。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Up3x1ShiftedSmaStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Up3x1ShiftedSmaStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 8).SetDisplay("Fast WMA", "Fast WMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 24).SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = default;
_prevSlow = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new SimpleMovingAverage { Length = FastPeriod };
var slow = new SimpleMovingAverage { 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 (!IsFormedAndOnlineAndAllowTrading()) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 2;
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 2;
}
_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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class up_3x1_shifted_sma_strategy(Strategy):
"""Simple SMA crossover (fast/slow) with cooldown between trades."""
def __init__(self):
super(up_3x1_shifted_sma_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 8).SetDisplay("Fast SMA", "Fast SMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 24).SetDisplay("Slow SMA", "Slow SMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(up_3x1_shifted_sma_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._has_prev = False
self._cooldown = 0
def OnStarted2(self, time):
super(up_3x1_shifted_sma_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._has_prev = False
self._cooldown = 0
fast = SimpleMovingAverage()
fast.Length = self._fast_period.Value
slow = SimpleMovingAverage()
slow.Length = self._slow_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast, slow, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
fv = float(fast_val)
sv = float(slow_val)
if not self._has_prev:
self._prev_fast = fv
self._prev_slow = sv
self._has_prev = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fv
self._prev_slow = sv
return
if self._prev_fast <= self._prev_slow and fv > sv and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume)
self._cooldown = 2
elif self._prev_fast >= self._prev_slow and fv < sv and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume)
self._cooldown = 2
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return up_3x1_shifted_sma_strategy()