Estrategia Pivot Heiken
Estrategia que combina puntos pivote diarios con velas Heikin-Ashi y un trailing stop opcional. El pivote diario se calcula a partir del máximo, mínimo y cierre del día anterior. El suavizado de Heikin-Ashi filtra el ruido del precio y destaca la dirección de la tendencia.
Lógica
- Entrada larga: La vela Heikin-Ashi es alcista y el cierre está por encima del pivote diario.
- Entrada corta: La vela Heikin-Ashi es bajista y el cierre está por debajo del pivote diario.
- Salida: La posición sale en el nivel de stop loss, take profit o trailing stop.
Parámetros
CandleType– serie de velas de trabajo.StopLossPips– distancia del stop loss en pips.TakeProfitPips– distancia del take profit en pips.TrailingStopPips– distancia del trailing stop en pips (0 desactiva el trailing).
Indicadores
- Heikin-Ashi (calculado internamente).
- Punto pivote diario.
Notas
- Usa la API de alto nivel con suscripciones de velas y enlace de indicadores.
- Adecuada para operaciones tanto largas como cortas.
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()