在 GitHub 上查看
Martingale VI Hybrid 策略(C#)
概述
Martingale VI Hybrid 策略将原始的 MetaTrader 专家顾问转换为 StockSharp 高级 API。策略采用快/慢均线过滤与 MACD 组合信号,并通过马丁格尔倍量方式分批进场。当价格按照固定点差距离逆向运动时,会继续加仓,并把所有订单的止盈统一在最新订单定义的位置。同时支持以货币金额、账户初始权益百分比以及资金回撤式移动止盈等多种总体退出条件。
交易逻辑
- 信号过滤:使用上一根 K 线的快慢 SMA 与 MACD 主线/信号线关系。若快 SMA 在慢 SMA 之上且 MACD 主线低于信号线,则触发多头循环;反之若快 SMA 在慢 SMA 之下且 MACD 主线高于信号线,则触发空头循环。
- 初始建仓:当出现新的循环并且没有持仓时,按照
Initial Volume 参数下单市场单。
- 马丁加仓:持仓期间跟踪最近的入场价。当价格逆势走出
Pip Step 点后,按照“上一单手数 × Volume Multiplier”再次下单。开放订单数量受 Max Trades 限制;若达到上限且 Close Max Orders 为真,则立即平掉全部仓位。
- 统一止盈:每次加仓后都根据最新入场价和
Take Profit (pips) 重新计算整个组合的止盈价位。当 K 线最高价(多头)或最低价(空头)触及该价位时,全部订单一并平仓。
- 整体退出:
- 启用
Use Money TP 时,浮动盈亏达到 Money TP 即平仓。
- 启用
Use Percent TP 时,浮动盈亏达到账户初始权益的 Percent TP 百分比即平仓。
- 启用
Enable Trailing 时,当利润超过 Trailing Activation 后开始以金额为单位的移动止盈,一旦利润回撤 Trailing Drawdown 即平仓。
参数说明
| 参数 |
说明 |
Candle Type |
指标计算所使用的主时间框架。 |
Fast MA, Slow MA |
定义趋势过滤的快慢简单移动平均周期。 |
MACD Fast, MACD Slow, MACD Signal |
MACD 指标的参数设置。 |
Initial Volume |
每个马丁循环的首单手数。 |
Volume Multiplier |
每次加仓的手数倍数。 |
Max Trades |
马丁循环中允许同时存在的最大订单数量。 |
Take Profit (pips) |
每单止盈距离,最新订单决定全局止盈价。 |
Pip Step |
价格逆势移动多少点后触发下一次加仓。 |
Use Money TP, Money TP |
是否启用以及设置货币金额止盈目标。 |
Use Percent TP, Percent TP |
是否启用以及设置初始权益百分比止盈目标。 |
Enable Trailing, Trailing Activation, Trailing Drawdown |
资金型移动止盈的相关参数。 |
Close Max Orders |
达到最大订单数时是否立即全部平仓。 |
风险控制
- 同时提供金额和百分比两种全局止盈,便于提前锁定收益。
- 资金型移动止盈可在盈利阶段限制最大回撤。
Max Trades 限制了马丁格尔的加仓次数,配合 Close Max Orders 可以在到达阈值时强制退出,避免仓位无限放大。
实现细节
- 使用 StockSharp 高级
SubscribeCandles 订阅 API,通过 BindEx 绑定 MACD,同时在回调中手动处理 SMA 值。
- 根据品种的最小价格变动步长自动推导点值,兼容 5 位和 3 位报价。
- 利用
Security.PriceStep、Security.StepPrice 以及 PositionAvgPrice 计算浮动盈亏,需要标的提供相应元数据。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Martingale VI Hybrid strategy (simplified).
/// Uses SMA crossover for entries.
/// </summary>
public class MartingaleViHybridStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public MartingaleViHybridStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_fastPeriod = Param(nameof(FastPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast SMA", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow SMA", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
decimal prevFast = 0, prevSlow = 0;
bool hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, (ICandleMessage candle, decimal fastValue, decimal slowValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevFast = fastValue;
prevSlow = slowValue;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevFast = fastValue;
prevSlow = slowValue;
return;
}
if (prevFast <= prevSlow && fastValue > slowValue && Position <= 0)
BuyMarket();
else if (prevFast >= prevSlow && fastValue < slowValue && Position >= 0)
SellMarket();
prevFast = fastValue;
prevSlow = slowValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
}
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 martingale_vi_hybrid_strategy(Strategy):
def __init__(self):
super(martingale_vi_hybrid_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._fast_period = self.Param("FastPeriod", 8) \
.SetDisplay("Fast Period", "Fast SMA", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow Period", "Slow SMA", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(martingale_vi_hybrid_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(martingale_vi_hybrid_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self._has_prev:
self._prev_fast = fv
self._prev_slow = sv
self._has_prev = True
return
if self._prev_fast <= self._prev_slow and fv > sv and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fv < sv and self.Position >= 0:
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return martingale_vi_hybrid_strategy()