首页
/
策略示例
在 GitHub 上查看
单均线通道突破策略
概述
单均线通道突破策略 在 StockSharp 高级 API 中重现 MetaTrader 5 专家顾问 One MA EA 的逻辑。策略绘制一条带有水平位移的移动平均线,并在其上下构建以“点”(pip)为单位的通道。当价格在同一根 K 线内先触及通道,再以开盘价突破通道外时,系统按照突破方向建立仓位,同时通过可选的止损和止盈保护自动管理风险。
主要特性:
支持多种均线计算方法(SMA、EMA、SMMA、LWMA)。
可选择输入到均线的价格类型(收盘价、开盘价、最高价、最低价、中位价、典型价、加权价)。
提供独立的均线读取位移和价格读取位移,完全对应原 EA 中的 Current Bar 设置。
使用标的证券的 PriceStep 与小数位数自动把点值转换为实际价格增量(3/5 位小数的外汇品种自动映射为经典 pip)。
交易逻辑
指标准备
按照 MaPeriod、MaMethodParam、MaShift 与 AppliedPriceType 计算移动平均线,使用订阅的 CandleType K 线数据。
将 ChannelHighPips 和 ChannelLowPips 转换为价格增量,在均线附近形成上下通道。
通过固定长度的历史缓冲区访问过去的均线值与 K 线数据(MaBarShift、PriceBarShift),以复制原始 EA 的行为。
信号判定
看涨突破 :目标 K 线的最低价位于均线与上轨之间,同时开盘价高于上轨,且当前无多头仓位 (Position <= 0),则买入。
看跌突破 :目标 K 线的最高价位于均线与下轨之间,同时开盘价低于下轨,且当前无空头仓位 (Position >= 0),则卖出。
委托数量等于设定的 TradeVolume 加上平掉反向仓位所需的数量,模拟原专家顾问在对冲账户中的行为。
风险控制
将 StopLossPips、TakeProfitPips 转换为绝对价格距离后传递给 StartProtection,为每笔仓位自动挂出止损/止盈单。
参数为零时关闭对应的保护单。
策略不会额外定义离场逻辑,仓位仅通过保护单或反向信号反转平仓。
参数说明
参数
说明
MaPeriod
移动平均线周期,必须大于 0。
MaShift
均线的水平位移(单位:K 线数量)。正值向右偏移。
MaMethodParam
均线类型(Sma、Ema、Smma、Lwma)。
AppliedPriceType
计算均线所用的价格类型。
MaBarShift
读取第几根历史均线值(0 表示当前处理的 K 线)。
PriceBarShift
读取第几根历史 K 线进行信号判定。
ChannelHighPips
上通道相对均线的距离(pip)。
ChannelLowPips
下通道相对均线的距离(pip)。
StopLossPips
止损距离(pip),0 表示不启用。
TakeProfitPips
止盈距离(pip),0 表示不启用。
TradeVolume
下单数量,对应 Strategy.Volume。
CandleType
用于计算与信号判定的 K 线类型/周期。
实现细节
Pip 转换:当证券的小数位为 3 或 5 时,pip 大小 = PriceStep * 10;否则为 PriceStep。
历史数据通过固定长度滑动窗口保存,避免直接调用指标 GetValue 方法,符合项目规范。
只处理已完成的 K 线,忽略未完成的数据以避免虚假信号。
若主程序提供图表区域,策略会绘制价格 K 线与成交记录,便于回测与监控。
使用建议
确认标的证券提供有效的 PriceStep 与 Decimals 信息;否则需手动调整 pip 参数。
根据市场与周期优化 MaPeriod、通道距离以及历史位移参数,以适配不同品种。
实盘部署时建议结合组合级别风控,因为策略在任何时刻仅持有单方向净头寸。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// One MA channel breakout strategy.
/// Places an SMA channel around price with configurable offset, trades breakouts.
/// </summary>
public class OneMaChannelBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _channelOffset;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public decimal ChannelOffset
{
get => _channelOffset.Value;
set => _channelOffset.Value = value;
}
public OneMaChannelBreakoutStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_maPeriod = Param(nameof(MaPeriod), 44)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average length", "Indicators");
_channelOffset = Param(nameof(ChannelOffset), 0.005m)
.SetGreaterThanZero()
.SetDisplay("Channel Offset", "Percentage offset for channel", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var upper = maValue * (1 + ChannelOffset);
var lower = maValue * (1 - ChannelOffset);
var close = candle.ClosePrice;
// Bullish breakout above upper channel
if (close > upper && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Bearish breakdown below lower channel
else if (close < lower && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
}
}
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
class one_ma_channel_breakout_strategy(Strategy):
def __init__(self):
super(one_ma_channel_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ma_period = self.Param("MaPeriod", 44) \
.SetDisplay("MA Period", "Moving average length", "Indicators")
self._channel_offset = self.Param("ChannelOffset", 0.005) \
.SetDisplay("Channel Offset", "Percentage offset for channel", "Indicators")
@property
def CandleType(self):
return self._candle_type.Value
@property
def MaPeriod(self):
return self._ma_period.Value
@property
def ChannelOffset(self):
return self._channel_offset.Value
def OnStarted2(self, time):
super(one_ma_channel_breakout_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self.MaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(sma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _on_process(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
mv = float(ma_value)
offset = float(self.ChannelOffset)
upper = mv * (1.0 + offset)
lower = mv * (1.0 - offset)
close = float(candle.ClosePrice)
# Bullish breakout above upper channel
if close > upper and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Bearish breakdown below lower channel
elif close < lower and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return one_ma_channel_breakout_strategy()