Estrategia Genie Pivot Fijo
Esta estrategia implementa el sistema de scalping de reversión en puntos pivote "Genie" originalmente escrito en MQL4. Escanea las últimas ocho velas para detectar reversiones bruscas en puntos pivote. Una operación larga se activa cuando siete mínimos consecutivos disminuyen y la vela actual forma un mínimo más alto mientras cierra por encima del máximo anterior. Una operación corta se activa cuando siete máximos consecutivos aumentan y la vela actual forma un máximo más bajo mientras cierra por debajo del mínimo anterior.
La estrategia usa un tamaño de posición fijo (Strategy.Volume) y aplica tanto un stop trailing como un take profit medidos en unidades de precio absolutas. Estos parámetros pueden optimizarse y permiten capturar reversiones rápidas mientras se protegen las ganancias abiertas.
Detalles
- Criterios de entrada:
- Largo:
Low[7] > Low[6] > ... > Low[1] && Low[1] < Low[0] && High[1] < Close[0].
- Corto:
High[7] < High[6] < ... < High[1] && High[1] > High[0] && Low[1] > Close[0].
- Largo/Corto: Ambos.
- Criterios de salida:
- El stop trailing o el take profit se alcanza.
- Stops:
- Take-profit: distancia absoluta desde la entrada.
- Stop trailing: distancia absoluta, que sigue a medida que la operación se mueve a favor.
- Valores predeterminados:
TakeProfit = 500.
TrailingStop = 200.
CandleType = 1 minuto.
- Filtros:
- Categoría: Reversión.
- Dirección: Ambos.
- Indicadores: Ninguno.
- Stops: Sí.
- Complejidad: Simple.
- Marco temporal: Corto plazo.
- Estacionalidad: No.
- Redes neuronales: No.
- Divergencia: No.
- Nivel de riesgo: Medio.
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>
/// Genie pivot reversal strategy.
/// Enters long after a sequence of falling lows followed by an upside breakout.
/// Enters short after a sequence of rising highs followed by a downside breakout.
/// Applies trailing stop and take-profit in absolute price units.
/// </summary>
public class GeniePivotFixedStrategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private readonly decimal[] _lows = new decimal[8];
private readonly decimal[] _highs = new decimal[8];
private int _stored;
private int _cooldownRemaining;
/// <summary>
/// Take-profit distance in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Trailing stop distance in price units.
/// </summary>
public decimal TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
/// <summary>
/// Candle type used for analysis.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of completed candles to wait after a position change.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes parameters.
/// </summary>
public GeniePivotFixedStrategy()
{
_takeProfit = Param(nameof(TakeProfit), 500m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Target profit in price units", "Risk Management")
.SetOptimize(100m, 1000m, 100m);
_trailingStop = Param(nameof(TrailingStop), 200m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop", "Trailing stop distance in price units", "Risk Management")
.SetOptimize(50m, 500m, 50m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for candles", "General");
_cooldownBars = Param(nameof(CooldownBars), 4)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Risk Management");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stored = 0;
_cooldownRemaining = 0;
Array.Clear(_lows);
Array.Clear(_highs);
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
stopLoss: new Unit(TrailingStop, UnitTypes.Absolute),
isStopTrailing: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
for (var i = 7; i > 0; i--)
{
_lows[i] = _lows[i - 1];
_highs[i] = _highs[i - 1];
}
_lows[0] = candle.LowPrice;
_highs[0] = candle.HighPrice;
if (_stored < 8)
_stored++;
if (_stored < 5)
return;
if (_cooldownRemaining > 0)
return;
var buySeq = _lows[4] > _lows[3] && _lows[3] > _lows[2] && _lows[2] > _lows[1];
if (buySeq && _lows[1] < _lows[0] && _highs[1] < candle.ClosePrice && candle.ClosePrice > candle.OpenPrice)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
_cooldownRemaining = CooldownBars;
return;
}
var sellSeq = _highs[4] < _highs[3] && _highs[3] < _highs[2] && _highs[2] < _highs[1];
if (sellSeq && _highs[1] > _highs[0] && _lows[1] > candle.ClosePrice && candle.ClosePrice < candle.OpenPrice)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class genie_pivot_fixed_strategy(Strategy):
def __init__(self):
super(genie_pivot_fixed_strategy, self).__init__()
self._take_profit = self.Param("TakeProfit", 500.0) \
.SetGreaterThanZero() \
.SetDisplay("Take Profit", "Target profit in price units", "Risk Management") \
.SetOptimize(100.0, 1000.0, 100.0)
self._trailing_stop = self.Param("TrailingStop", 200.0) \
.SetGreaterThanZero() \
.SetDisplay("Trailing Stop", "Trailing stop distance in price units", "Risk Management") \
.SetOptimize(50.0, 500.0, 50.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for candles", "General")
self._cooldown_bars = self.Param("CooldownBars", 4) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Risk Management")
self._lows = [0.0] * 8
self._highs = [0.0] * 8
self._stored = 0
self._cooldown_remaining = 0
@property
def take_profit(self):
return self._take_profit.Value
@property
def trailing_stop(self):
return self._trailing_stop.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(genie_pivot_fixed_strategy, self).OnReseted()
self._stored = 0
self._cooldown_remaining = 0
self._lows = [0.0] * 8
self._highs = [0.0] * 8
def OnStarted2(self, time):
super(genie_pivot_fixed_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
self.StartProtection(
Unit(float(self.take_profit), UnitTypes.Absolute),
Unit(float(self.trailing_stop), UnitTypes.Absolute),
True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
# Shift arrays
for i in range(7, 0, -1):
self._lows[i] = self._lows[i - 1]
self._highs[i] = self._highs[i - 1]
self._lows[0] = float(candle.LowPrice)
self._highs[0] = float(candle.HighPrice)
if self._stored < 8:
self._stored += 1
if self._stored < 5:
return
if self._cooldown_remaining > 0:
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
# Buy sequence: falling lows [4] > [3] > [2] > [1]
buy_seq = self._lows[4] > self._lows[3] and self._lows[3] > self._lows[2] and self._lows[2] > self._lows[1]
if buy_seq and self._lows[1] < self._lows[0] and self._highs[1] < close and close > open_price:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
return
# Sell sequence: rising highs [4] < [3] < [2] < [1]
sell_seq = self._highs[4] < self._highs[3] and self._highs[3] < self._highs[2] and self._highs[2] < self._highs[1]
if sell_seq and self._highs[1] > self._highs[0] and self._lows[1] > close and close < open_price:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
def CreateClone(self):
return genie_pivot_fixed_strategy()