修正均线突破策略
该策略使用修正均线指标。该指标基于移动平均并结合价格标准差来调整平滑程度,从而减少价格异常波动的影响。
当价格收盘价突破修正均线上方一定点数并回落到突破水平时,策略做多;当价格跌破修正均线下方并回抽到突破水平时,策略做空。止损和止盈以绝对价格点数设置。
参数
Candle Type– 用于计算的K线周期。Length– 移动平均和标准差的周期长度。MA Type– 移动平均类型(SMA、EMA、SMMA、LWMA)。Level Points– 与修正均线之间的突破距离(价格步长)。Stop Loss Points– 距离入场价的止损点数。Take Profit Points– 距离入场价的止盈点数。Enable Long– 允许开多单。Enable Short– 允许开空单。
交易逻辑
- 计算移动平均和标准差。
- 使用前一数值和方差比例构建修正均线。
- 当前一根K线收盘价超出修正均线并超过设定阈值时识别突破。
- 突破后等待下一根K线回到突破水平并按突破方向入场。
- 当出现新的突破信号时平掉相反方向的仓位。
- 应用止损和止盈保护。
说明
本策略由MQL脚本 Exp_CorrectedAverage.mq5 转换而来,仅供学习使用,在实盘使用前需进行充分测试。
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>
/// Strategy based on the Corrected Average breakout.
/// Monitors price relative to a corrected moving average and trades on breakouts.
/// </summary>
public class CorrectedAverageBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _length;
private readonly StrategyParam<int> _levelPoints;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private decimal _prevCorrected;
private decimal _prevPrevCorrected;
private decimal _prevClose;
private decimal _prevPrevClose;
private bool _isInitialized;
private decimal _level;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Length { get => _length.Value; set => _length.Value = value; }
public int LevelPoints { get => _levelPoints.Value; set => _levelPoints.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public CorrectedAverageBreakoutStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
_length = Param(nameof(Length), 12)
.SetDisplay("Length", "Period of moving average", "Indicator");
_levelPoints = Param(nameof(LevelPoints), 300)
.SetDisplay("Level Points", "Breakout distance in price steps", "Trading");
_stopLossPoints = Param(nameof(StopLossPoints), 1000)
.SetDisplay("Stop Loss Points", "Stop loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000)
.SetDisplay("Take Profit Points", "Take profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCorrected = default;
_prevPrevCorrected = default;
_prevClose = default;
_prevPrevClose = default;
_isInitialized = default;
_level = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var step = Security.PriceStep ?? 1m;
_level = LevelPoints * step;
var ma = new ExponentialMovingAverage { Length = Length };
var std = new StandardDeviation { Length = Length };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ma, std, ProcessCandle).Start();
StartProtection(
new Unit(StopLossPoints * step, UnitTypes.Absolute),
new Unit(TakeProfitPoints * step, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle, decimal maValue, decimal stdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
decimal corrected;
if (!_isInitialized)
{
corrected = maValue;
_isInitialized = true;
}
else
{
var v1 = stdValue * stdValue;
var v2 = (_prevCorrected - maValue) * (_prevCorrected - maValue);
var k = (v2 < v1 || v2 == 0m) ? 0m : 1m - (v1 / v2);
corrected = _prevCorrected + k * (maValue - _prevCorrected);
}
var buySignal = _prevPrevClose > _prevPrevCorrected + _level && _prevClose <= _prevCorrected + _level;
var sellSignal = _prevPrevClose < _prevPrevCorrected - _level && _prevClose >= _prevCorrected - _level;
if (buySignal && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (sellSignal && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevPrevCorrected = _prevCorrected;
_prevPrevClose = _prevClose;
_prevCorrected = corrected;
_prevClose = candle.ClosePrice;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class corrected_average_breakout_strategy(Strategy):
"""
Strategy based on the Corrected Average breakout.
Monitors price relative to a corrected moving average and trades on breakouts.
"""
def __init__(self):
super(corrected_average_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._length = self.Param("Length", 12) \
.SetDisplay("Length", "Period of moving average", "Indicator")
self._level_points = self.Param("LevelPoints", 300) \
.SetDisplay("Level Points", "Breakout distance in price steps", "Trading")
self._stop_loss_points = self.Param("StopLossPoints", 1000) \
.SetDisplay("Stop Loss Points", "Stop loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 2000) \
.SetDisplay("Take Profit Points", "Take profit in price steps", "Risk")
self._prev_corrected = 0.0
self._prev_prev_corrected = 0.0
self._prev_close = 0.0
self._prev_prev_close = 0.0
self._is_initialized = False
self._level = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(corrected_average_breakout_strategy, self).OnReseted()
self._prev_corrected = 0.0
self._prev_prev_corrected = 0.0
self._prev_close = 0.0
self._prev_prev_close = 0.0
self._is_initialized = False
self._level = 0.0
def OnStarted2(self, time):
super(corrected_average_breakout_strategy, self).OnStarted2(time)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
self._level = self._level_points.Value * step
ma = ExponentialMovingAverage()
ma.Length = self._length.Value
std = StandardDeviation()
std.Length = self._length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, std, self.on_process).Start()
sl_dist = self._stop_loss_points.Value * step
tp_dist = self._take_profit_points.Value * step
self.StartProtection(
Unit(float(tp_dist), UnitTypes.Absolute),
Unit(float(sl_dist), UnitTypes.Absolute)
)
def on_process(self, candle, ma_val, std_val):
if candle.State != CandleStates.Finished:
return
ma_val = float(ma_val)
std_val = float(std_val)
if not self._is_initialized:
corrected = ma_val
self._is_initialized = True
else:
v1 = std_val * std_val
diff = self._prev_corrected - ma_val
v2 = diff * diff
if v2 < v1 or v2 == 0:
k = 0.0
else:
k = 1.0 - (v1 / v2)
corrected = self._prev_corrected + k * (ma_val - self._prev_corrected)
buy_signal = (self._prev_prev_close > self._prev_prev_corrected + self._level and
self._prev_close <= self._prev_corrected + self._level)
sell_signal = (self._prev_prev_close < self._prev_prev_corrected - self._level and
self._prev_close >= self._prev_corrected - self._level)
if buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_prev_corrected = self._prev_corrected
self._prev_prev_close = self._prev_close
self._prev_corrected = corrected
self._prev_close = float(candle.ClosePrice)
def CreateClone(self):
return corrected_average_breakout_strategy()