Parallel Strategies 策略 (中文)
基于 Heikin Ashi 和 MACD 的突破系统,可做多也可做空。当 Heikin Ashi 趋势反转并且价格突破 Donchian 通道,同时 MACD 方向一致时进场。
Heikin Ashi 用于识别趋势方向,Donchian 通道用于寻找突破,MACD 过滤动量不足的信号。
适合寻找趋势反转后早期突破的交易者,主要用于日内周期。
细节
- 入场条件:
- 多头:
趋势转多 && Close > DonchianHigh && MACD > Signal - 空头:
趋势转空 && Close < DonchianLow && MACD < Signal
- 多头:
- 多空方向:双向
- 出场条件:
- 相反的突破信号
- 止损:未设置
- 默认参数:
DonchianPeriod= 5MacdFast= 12MacdSlow= 26MacdSignal= 9CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- 过滤:
- 类型:突破
- 方向:双向
- 指标:Heikin Ashi、Donchian Channel、MACD
- 止损:无
- 复杂度:中等
- 周期:日内
- 季节性:无
- 神经网络:无
- 背离:无
- 风险等级:中等
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 combining Heikin Ashi trend reversals with Donchian Channel breakouts and MACD confirmation.
/// </summary>
public class ParallelStrategiesStrategy : Strategy
{
private readonly StrategyParam<int> _donchianPeriod;
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevUpper;
private decimal? _prevLower;
private int? _prevTrend;
// Heikin Ashi state
private decimal _haOpen;
private decimal _haClose;
private bool _haInitialized;
public int DonchianPeriod { get => _donchianPeriod.Value; set => _donchianPeriod.Value = value; }
public int MacdFast { get => _macdFast.Value; set => _macdFast.Value = value; }
public int MacdSlow { get => _macdSlow.Value; set => _macdSlow.Value = value; }
public int MacdSignal { get => _macdSignal.Value; set => _macdSignal.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ParallelStrategiesStrategy()
{
_donchianPeriod = Param(nameof(DonchianPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Donchian Period", "Lookback for breakout calculation", "Indicators");
_macdFast = Param(nameof(MacdFast), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Fast EMA period", "Indicators");
_macdSlow = Param(nameof(MacdSlow), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Slow EMA period", "Indicators");
_macdSignal = Param(nameof(MacdSignal), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Signal line period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevUpper = null;
_prevLower = null;
_prevTrend = null;
_haOpen = 0;
_haClose = 0;
_haInitialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var donchian = new DonchianChannels { Length = DonchianPeriod };
var macd = new MovingAverageConvergenceDivergenceSignal(
new MovingAverageConvergenceDivergence(
new ExponentialMovingAverage { Length = MacdSlow },
new ExponentialMovingAverage { Length = MacdFast }),
new ExponentialMovingAverage { Length = MacdSignal });
SubscribeCandles(CandleType)
.BindEx(donchian, macd, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue donchianValue, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
var dc = (IDonchianChannelsValue)donchianValue;
var macdV = (IMovingAverageConvergenceDivergenceSignalValue)macdValue;
if (dc.UpperBand is not decimal upper || dc.LowerBand is not decimal lower)
return;
if (macdV.Macd is not decimal macdLine || macdV.Signal is not decimal signalLine)
return;
// Compute Heikin Ashi manually
var haCloseNew = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
decimal haOpenNew;
if (!_haInitialized)
{
haOpenNew = (candle.OpenPrice + candle.ClosePrice) / 2m;
_haInitialized = true;
}
else
{
haOpenNew = (_haOpen + _haClose) / 2m;
}
_haOpen = haOpenNew;
_haClose = haCloseNew;
var trend = haOpenNew < haCloseNew ? 1 : -1;
if (_prevTrend is int prevTrend)
{
if (trend > 0 && prevTrend < 0 && macdLine > signalLine && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (trend < 0 && prevTrend > 0 && macdLine < signalLine && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
_prevUpper = upper;
_prevLower = lower;
_prevTrend = trend;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (
DonchianChannels, ExponentialMovingAverage,
MovingAverageConvergenceDivergence, MovingAverageConvergenceDivergenceSignal
)
from StockSharp.Algo.Strategies import Strategy
class parallel_strategies_strategy(Strategy):
def __init__(self):
super(parallel_strategies_strategy, self).__init__()
self._donchian_period = self.Param("DonchianPeriod", 5) \
.SetDisplay("Donchian Period", "Lookback for breakout calculation", "Indicators")
self._macd_fast = self.Param("MacdFast", 12) \
.SetDisplay("MACD Fast", "Fast EMA period", "Indicators")
self._macd_slow = self.Param("MacdSlow", 26) \
.SetDisplay("MACD Slow", "Slow EMA period", "Indicators")
self._macd_signal = self.Param("MacdSignal", 9) \
.SetDisplay("MACD Signal", "Signal line period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for candles", "General")
self._prev_upper = None
self._prev_lower = None
self._prev_trend = None
self._ha_open = 0.0
self._ha_close = 0.0
self._ha_initialized = False
@property
def DonchianPeriod(self):
return self._donchian_period.Value
@DonchianPeriod.setter
def DonchianPeriod(self, value):
self._donchian_period.Value = value
@property
def MacdFast(self):
return self._macd_fast.Value
@MacdFast.setter
def MacdFast(self, value):
self._macd_fast.Value = value
@property
def MacdSlow(self):
return self._macd_slow.Value
@MacdSlow.setter
def MacdSlow(self, value):
self._macd_slow.Value = value
@property
def MacdSignal(self):
return self._macd_signal.Value
@MacdSignal.setter
def MacdSignal(self, value):
self._macd_signal.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(parallel_strategies_strategy, self).OnStarted2(time)
donchian = DonchianChannels()
donchian.Length = self.DonchianPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.MacdSlow
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.MacdFast
macd_core = MovingAverageConvergenceDivergence(slow_ema, fast_ema)
sig_ema = ExponentialMovingAverage()
sig_ema.Length = self.MacdSignal
macd = MovingAverageConvergenceDivergenceSignal(macd_core, sig_ema)
self.SubscribeCandles(self.CandleType) \
.BindEx(donchian, macd, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, donchian_value, macd_value):
if candle.State != CandleStates.Finished:
return
upper_raw = donchian_value.UpperBand
lower_raw = donchian_value.LowerBand
if upper_raw is None or lower_raw is None:
return
macd_raw = macd_value.Macd
signal_raw = macd_value.Signal
if macd_raw is None or signal_raw is None:
return
upper = float(upper_raw)
lower = float(lower_raw)
macd_line = float(macd_raw)
signal_line = float(signal_raw)
ha_close_new = (float(candle.OpenPrice) + float(candle.HighPrice)
+ float(candle.LowPrice) + float(candle.ClosePrice)) / 4.0
if not self._ha_initialized:
ha_open_new = (float(candle.OpenPrice) + float(candle.ClosePrice)) / 2.0
self._ha_initialized = True
else:
ha_open_new = (self._ha_open + self._ha_close) / 2.0
self._ha_open = ha_open_new
self._ha_close = ha_close_new
trend = 1 if ha_open_new < ha_close_new else -1
if self._prev_trend is not None:
if trend > 0 and self._prev_trend < 0 and macd_line > signal_line and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif trend < 0 and self._prev_trend > 0 and macd_line < signal_line and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_upper = upper
self._prev_lower = lower
self._prev_trend = trend
def OnReseted(self):
super(parallel_strategies_strategy, self).OnReseted()
self._prev_upper = None
self._prev_lower = None
self._prev_trend = None
self._ha_open = 0.0
self._ha_close = 0.0
self._ha_initialized = False
def CreateClone(self):
return parallel_strategies_strategy()