在 GitHub 上查看
ASCV 枢轴突破策略
概述
ASCV 枢轴突破策略是 MetaTrader 4 专家顾问 “ASCV”(Avpb.mq4)在 StockSharp 平台上的高级移植版本。原始 EA 同时依赖两个封闭指标(ASCTrend1sig 与 BrainTrend1Sig)、标准差过滤器、日内枢轴水平以及成交量加速来在限定时段内捕捉突破延续。由于专有指标无法直接移植,本策略使用移动平均、随机指标以及日枢轴的组合来复现其决策流程,并完整保留仓位管理和保护逻辑。
交易逻辑
- 交易时段过滤:仅在设定的开始/结束小时之间允许开仓(默认 02:00–20:00)。每到整点会重置当小时的入场标志,对应 MQL 中
Minute()==0 的处理。
- 波动率门槛:所选时间框架上的标准差必须高于阈值,模拟原版
iStdDev 对活跃行情的要求。
- 趋势确认:快慢两条简单移动平均线替代 ASCTrend/BrainTrend 的方向判断。做多需满足快速均线高于慢速均线且收盘价高于当日枢轴;做空条件相反。
- 动量确认:随机指标保证突破时
%K-%D 为正(多头)或为负(空头)。该差值的绝对值同时作为动量退出条件,对应原始 EA 中的 dstob/dstos 逻辑。
- 成交量加速:当前 K 线成交量需相对前一根增加至少阈值(默认 30),重现
Volume[0]-Volume[1] 检查。
- 下单方式:使用固定手数的市价单
BuyMarket / SellMarket,每个小时内同方向仅允许一次入场。
- 止损与止盈:优先将止损设置在最近的枢轴支撑/阻力(S1/S2 或 R1/R2)。若距离不足,则使用以价格步长表示的备用距离。止盈目标遵循同样的层级:多头优先 R2→R1→Pivot,空头优先 S2→S1→Pivot,缺失时退化为备用距离。
- 动态管理:随机指标差值触发动量式离场,同时以价格步长计算的跟踪止损复制 EA 的移动止损行为。
参数
| 参数 |
说明 |
默认值 |
CandleType |
用于指标与信号的蜡烛类型。 |
15 分钟 K 线 |
StartHour / EndHour |
允许交易的起止小时(含边界)。 |
2 / 20 |
FastMaLength |
快速 SMA 长度。 |
10 |
SlowMaLength |
慢速 SMA 长度。 |
40 |
StdDevLength |
标准差窗口长度。 |
10 |
StdDevThreshold |
最小标准差阈值。 |
0.0005 |
VolumeDeltaThreshold |
当前与上一根 K 线成交量的最小增量。 |
30 |
StochasticKPeriod / StochasticDPeriod / StochasticSlowing |
随机指标周期参数。 |
5 / 3 / 3 |
StochasticExitDelta |
触发动量离场的 %K-%D 绝对阈值。 |
5 |
TrailingStopSteps |
跟踪止损距离(以价格步长计)。 |
30 |
MinPivotDistanceSteps |
使用枢轴目标时要求的最小距离。 |
50 |
StopFallbackSteps |
枢轴过近时的备用止损距离。 |
33 |
TakeProfitBufferSteps |
枢轴过近时的备用止盈距离。 |
50 |
OrderVolume |
每次下单的数量。 |
1 |
所有距离均以交易品种的价格步长表示,便于适配不同交易所的最小变动价位。
实现说明
- 采用
SubscribeCandles().BindEx(...) 的高阶 API 结构,未将指标添加到 Strategy.Indicators 中,符合项目规范。
- 枢轴水平在每日切换时根据上一交易日的最高价、最低价与收盘价重新计算。首个交易日仅收集数据,第二天才开始信号判断。
- 调用了
StartProtection() 以在断线时自动保护仓位,与 EA 的安全机制一致。
- C# 代码中提供了英文注释和 XML 文档,说明各逻辑与原始 MQL 的对应关系。
- 止损与止盈通过
SetStopLoss 和 SetTakeProfit 下发,内部自动转换为步长单位,兼容不同合约规格。
使用建议
- 建议在提供真实成交量的品种上运行策略,成交量过滤是核心条件之一。
- 优化时优先关注波动率 (
StdDevThreshold) 和成交量 (VolumeDeltaThreshold) 参数,它们对信号密度影响最大。
- 对波动较大的品种可适当增大
MinPivotDistanceSteps,防止过早触发止盈。
- 若随机指标差值过于敏感,可增大
StochasticExitDelta 让跟踪止损成为主要的离场方式。
文件列表
CS/AscvStrategy.cs —— 策略实现。
README.md —— 英文说明。
README_ru.md —— 俄文说明。
README_zh.md —— 中文说明(当前文件)。
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>
/// ASCV strategy - fast/slow EMA crossover with trend filter.
/// Buys on bullish crossover when close is above slow EMA.
/// Sells on bearish crossover when close is below slow EMA.
/// </summary>
public class AscvStrategy : 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;
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 AscvStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 8)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
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;
}
var crossUp = _prevFast <= _prevSlow && fast > slow;
var crossDown = _prevFast >= _prevSlow && fast < slow;
if (crossUp && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (crossDown && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_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 ascv_strategy(Strategy):
def __init__(self):
super(ascv_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 8) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ascv_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(ascv_strategy, self).OnStarted2(time)
self._has_prev = False
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
cross_up = self._prev_fast <= self._prev_slow and fast_val > slow_val
cross_down = self._prev_fast >= self._prev_slow and fast_val < slow_val
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return ascv_strategy()