Стратегия Pivot Heiken
Стратегия объединяет дневные уровни Pivot и свечи Heikin-Ashi с опциональным трейлинг-стопом. Дневной пивот рассчитывается по максимуму, минимуму и закрытию предыдущего дня. Свечи Heikin-Ashi сглаживают шум и подчёркивают направление тренда.
Логика
- Вход в лонг: свеча Heikin-Ashi бычья и закрытие выше дневного пивота.
- Вход в шорт: свеча Heikin-Ashi медвежья и закрытие ниже дневного пивота.
- Выход: по стоп-лоссу, тейк-профиту или уровню трейлинг-стопа.
Параметры
CandleType– рабочая серия свечей.StopLossPips– расстояние стоп-лосса в пунктах.TakeProfitPips– расстояние тейк-профита в пунктах.TrailingStopPips– размер трейлинг-стопа в пунктах (0 отключает трейлинг).
Индикаторы
- Heikin-Ashi (рассчитывается внутри стратегии).
- Дневной пивот.
Примечания
- Используется высокоуровневый API с подпиской на свечи и обработчиками индикаторов.
- Подходит для торговли в обе стороны.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Pivot point and Heikin-Ashi based strategy with optional trailing stop.
/// </summary>
public class PivotHeikenStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<int> _trailingStopPips;
private decimal _pivot;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _prevClose;
private bool _dailyInitialized;
private decimal _haOpen;
private decimal _haClose;
private bool _haInitialized;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
private decimal _trailingStop;
private decimal _step;
private decimal _trailingDistance;
private int _previousDirection;
/// <summary>
/// Candle type used for trading logic.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop loss distance in pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take profit distance in pips.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trailing stop distance in pips. Set to 0 to disable.
/// </summary>
public int TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="PivotHeikenStrategy"/>.
/// </summary>
public PivotHeikenStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Working candle type", "General");
_stopLossPips = Param(nameof(StopLossPips), 50)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Stop loss distance in pips", "Risk");
_takeProfitPips = Param(nameof(TakeProfitPips), 100)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Take profit distance in pips", "Risk");
_trailingStopPips = Param(nameof(TrailingStopPips), 0)
.SetDisplay("Trailing Stop", "Trailing stop distance in pips", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_pivot = 0m;
_prevHigh = 0m;
_prevLow = 0m;
_prevClose = 0m;
_dailyInitialized = false;
_haOpen = 0m;
_haClose = 0m;
_haInitialized = false;
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
_trailingStop = 0m;
_step = 0m;
_trailingDistance = 0m;
_previousDirection = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_step = Security?.PriceStep ?? 1m;
_trailingDistance = TrailingStopPips * _step;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var dailySubscription = SubscribeCandles(TimeSpan.FromDays(1).TimeFrame());
dailySubscription.Bind(ProcessDailyCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessDailyCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_dailyInitialized)
{
_pivot = (_prevHigh + _prevLow + _prevClose) / 3m;
}
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevClose = candle.ClosePrice;
_dailyInitialized = true;
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_pivot == 0m)
return;
var haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
if (!_haInitialized)
{
_haOpen = (candle.OpenPrice + candle.ClosePrice) / 2m;
_haClose = haClose;
_haInitialized = true;
return;
}
var haOpen = (_haOpen + _haClose) / 2m;
_haOpen = haOpen;
_haClose = haClose;
var isBullish = haClose > haOpen;
var isBearish = haClose < haOpen;
var direction = isBullish ? 1 : isBearish ? -1 : 0;
if (isBullish && _previousDirection != 1 && candle.ClosePrice > _pivot && Position <= 0)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice - StopLossPips * _step;
_takePrice = _entryPrice + TakeProfitPips * _step;
_trailingStop = _stopPrice;
}
else if (isBearish && _previousDirection != -1 && candle.ClosePrice < _pivot && Position >= 0)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice + StopLossPips * _step;
_takePrice = _entryPrice - TakeProfitPips * _step;
_trailingStop = _stopPrice;
}
if (Position > 0)
{
if (candle.LowPrice <= _stopPrice || candle.LowPrice <= _trailingStop)
SellMarket();
else if (candle.HighPrice >= _takePrice)
SellMarket();
else if (TrailingStopPips > 0)
{
var newStop = candle.ClosePrice - _trailingDistance;
if (newStop > _trailingStop)
_trailingStop = newStop;
}
}
else if (Position < 0)
{
if (candle.HighPrice >= _stopPrice || candle.HighPrice >= _trailingStop)
BuyMarket();
else if (candle.LowPrice <= _takePrice)
BuyMarket();
else if (TrailingStopPips > 0)
{
var newStop = candle.ClosePrice + _trailingDistance;
if (newStop < _trailingStop)
_trailingStop = newStop;
}
}
_previousDirection = direction;
}
}
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.Strategies import Strategy
class pivot_heiken_strategy(Strategy):
def __init__(self):
super(pivot_heiken_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._stop_loss_pips = self.Param("StopLossPips", 50)
self._take_profit_pips = self.Param("TakeProfitPips", 100)
self._trailing_stop_pips = self.Param("TrailingStopPips", 0)
self._pivot = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._daily_initialized = False
self._ha_open = 0.0
self._ha_close = 0.0
self._ha_initialized = False
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._trailing_stop = 0.0
self._step = 0.0
self._trailing_distance = 0.0
self._previous_direction = 0
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, v): self._candle_type.Value = v
@property
def StopLossPips(self): return self._stop_loss_pips.Value
@StopLossPips.setter
def StopLossPips(self, v): self._stop_loss_pips.Value = v
@property
def TakeProfitPips(self): return self._take_profit_pips.Value
@TakeProfitPips.setter
def TakeProfitPips(self, v): self._take_profit_pips.Value = v
@property
def TrailingStopPips(self): return self._trailing_stop_pips.Value
@TrailingStopPips.setter
def TrailingStopPips(self, v): self._trailing_stop_pips.Value = v
def OnStarted2(self, time):
super(pivot_heiken_strategy, self).OnStarted2(time)
self._step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
self._trailing_distance = float(self.TrailingStopPips) * self._step
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
daily_sub = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromDays(1)))
daily_sub.Bind(self.ProcessDailyCandle).Start()
def ProcessDailyCandle(self, candle):
if candle.State != CandleStates.Finished: return
if self._daily_initialized:
self._pivot = (self._prev_high + self._prev_low + self._prev_close) / 3.0
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_close = float(candle.ClosePrice)
self._daily_initialized = True
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished: return
if self._pivot == 0.0: return
ha_close = (float(candle.OpenPrice) + float(candle.HighPrice) + float(candle.LowPrice) + float(candle.ClosePrice)) / 4.0
if not self._ha_initialized:
self._ha_open = (float(candle.OpenPrice) + float(candle.ClosePrice)) / 2.0
self._ha_close = ha_close
self._ha_initialized = True
return
ha_open = (self._ha_open + self._ha_close) / 2.0
self._ha_open = ha_open
self._ha_close = ha_close
is_bullish = ha_close > ha_open
is_bearish = ha_close < ha_open
direction = 1 if is_bullish else (-1 if is_bearish else 0)
close = float(candle.ClosePrice)
low = float(candle.LowPrice)
high = float(candle.HighPrice)
if is_bullish and self._previous_direction != 1 and close > self._pivot and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._stop_price = self._entry_price - float(self.StopLossPips) * self._step
self._take_price = self._entry_price + float(self.TakeProfitPips) * self._step
self._trailing_stop = self._stop_price
elif is_bearish and self._previous_direction != -1 and close < self._pivot and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._stop_price = self._entry_price + float(self.StopLossPips) * self._step
self._take_price = self._entry_price - float(self.TakeProfitPips) * self._step
self._trailing_stop = self._stop_price
if self.Position > 0:
if low <= self._stop_price or low <= self._trailing_stop:
self.SellMarket()
elif high >= self._take_price:
self.SellMarket()
elif self.TrailingStopPips > 0:
new_stop = close - self._trailing_distance
if new_stop > self._trailing_stop: self._trailing_stop = new_stop
elif self.Position < 0:
if high >= self._stop_price or high >= self._trailing_stop:
self.BuyMarket()
elif low <= self._take_price:
self.BuyMarket()
elif self.TrailingStopPips > 0:
new_stop = close + self._trailing_distance
if new_stop < self._trailing_stop: self._trailing_stop = new_stop
self._previous_direction = direction
def OnReseted(self):
super(pivot_heiken_strategy, self).OnReseted()
self._pivot = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._daily_initialized = False
self._ha_open = 0.0
self._ha_close = 0.0
self._ha_initialized = False
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._trailing_stop = 0.0
self._previous_direction = 0
def CreateClone(self):
return pivot_heiken_strategy()