平滑烛线反转策略
Heikin-Ashi 烛线能够平滑噪声并突出趋势方向。当连续的看跌 HA 烛线转为看涨,或反之,可能表示动能变化。本策略交易这种颜色翻转,并用百分比止损保护。
测试表明年均收益约为 145%,该策略在加密市场表现最佳。
策略根据普通K线计算 Heikin-Ashi 值。当 HA 收盘价上穿开盘价并结束此前的看跌序列时做多;在看涨序列后下穿则做空。止损按固定百分比设置。
该方法在震荡行情下简单有效,因为普通蜡烛常带来噪声。
细节
- 入场条件:Heikin-Ashi 烛线颜色翻转。
- 多/空:双向。
- 退出条件:止损。
- 止损:有,按百分比。
- 默认值:
CandleType= 15 分钟StopLossPercent= 2
- 过滤条件:
- 类别: 反转
- 方向: 双向
- 指标: Heikin-Ashi
- 止损: 有
- 复杂度: 基础
- 时间框架: 日内
- 季节性: 无
- 神经网络: 无
- 背离: 无
- 风险级别: 中等
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>
/// Heikin Ashi Reversal strategy.
/// Computes Heikin-Ashi candles from regular candles. A bullish Heikin-Ashi candle after bearish ones turns the position long,
/// a bearish one after bullish ones turns it short; a percent stop limits the loss.
/// </summary>
public class HeikinAshiReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _haOpen;
private decimal _haClose;
private bool? _prevBullish;
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HeikinAshiReversalStrategy()
{
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_haOpen = null;
_haClose = default;
_prevBullish = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_haOpen = null;
_haClose = default;
_prevBullish = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4;
var haOpen = _haOpen is decimal prevOpen
? (prevOpen + _haClose) / 2
: (candle.OpenPrice + candle.ClosePrice) / 2;
_haOpen = haOpen;
_haClose = haClose;
// A Heikin-Ashi candle without a body keeps the previous color.
if (haClose == haOpen)
return;
var isBullish = haClose > haOpen;
var wasBullish = _prevBullish;
_prevBullish = isBullish;
if (wasBullish is not bool previous || previous == isBullish || !IsFormedAndOnlineAndAllowTrading())
return;
if (isBullish && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (!isBullish && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class heikin_ashi_reversal_strategy(Strategy):
"""
Heikin Ashi Reversal strategy.
Computes Heikin-Ashi candles from regular candles. A bullish Heikin-Ashi candle after bearish ones turns the position long,
a bearish one after bullish ones turns it short; a percent stop limits the loss.
"""
def __init__(self):
super(heikin_ashi_reversal_strategy, self).__init__()
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._ha_open = None
self._ha_close = Decimal(0)
self._prev_bullish = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(heikin_ashi_reversal_strategy, self).OnReseted()
self._ha_open = None
self._ha_close = Decimal(0)
self._prev_bullish = None
def OnStarted2(self, time):
super(heikin_ashi_reversal_strategy, self).OnStarted2(time)
self._ha_open = None
self._ha_close = Decimal(0)
self._prev_bullish = None
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
ha_close = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / Decimal(4)
if self._ha_open is None:
ha_open = (candle.OpenPrice + candle.ClosePrice) / Decimal(2)
else:
ha_open = (self._ha_open + self._ha_close) / Decimal(2)
self._ha_open = ha_open
self._ha_close = ha_close
# A Heikin-Ashi candle without a body keeps the previous color.
if ha_close == ha_open:
return
is_bullish = ha_close > ha_open
was_bullish = self._prev_bullish
self._prev_bullish = is_bullish
if was_bullish is None or was_bullish == is_bullish or not self.IsFormedAndOnlineAndAllowTrading():
return
if is_bullish and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
elif not is_bullish and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
def CreateClone(self):
return heikin_ashi_reversal_strategy()