Pivot Heiken Strategy
Strategy combining daily pivot points with Heikin-Ashi candles and an optional trailing stop. The daily pivot is calculated from the previous day's high, low and close. Heikin-Ashi smoothing filters price noise and highlights trend direction.
Logic
- Long entry: Heikin-Ashi candle is bullish and the close is above the daily pivot.
- Short entry: Heikin-Ashi candle is bearish and the close is below the daily pivot.
- Exit: Position exits at stop loss, take profit, or trailing stop level.
Parameters
CandleType– working candle series.StopLossPips– stop loss distance in pips.TakeProfitPips– take profit distance in pips.TrailingStopPips– trailing stop distance in pips (0 disables trailing).
Indicators
- Heikin-Ashi (calculated internally).
- Daily pivot point.
Notes
- Uses high-level API with candle subscriptions and indicator binding.
- Suitable for both long and short trading.
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()