Estrategia de Pirámide Rijfie
Esta estrategia abre una posición larga inicial cuando el oscilador Stochastic cruza por encima de un nivel bajo configurable. Luego agrega nuevas posiciones cada vez que el precio baja un porcentaje fijo mientras se mantiene por encima de un filtro EMA y un precio mínimo. Un temporizador opcional puede cerrar todas las posiciones en un momento especificado.
Parámetros
- Tipo de vela
- Nivel bajo del Stochastic
- Precio máximo para la primera entrada
- Precio mínimo permitido
- Período EMA
- Nivel de paso en porcentaje
- Cerrar posiciones a la hora
- Hora de cierre
- Minuto de cierre
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>
/// Pyramid style strategy that buys additional lots as price drops by a fixed percentage.
/// Uses Stochastic oscillator oversold signal for initial entry.
/// </summary>
public class RijfiePyramidStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _lowLevel;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _stepLevel;
private readonly StrategyParam<decimal> _takeProfitPct;
private StochasticOscillator _stochastic;
private decimal _nextBuyPrice;
private decimal? _prevK;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal LowLevel
{
get => _lowLevel.Value;
set => _lowLevel.Value = value;
}
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public decimal StepLevel
{
get => _stepLevel.Value;
set => _stepLevel.Value = value;
}
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
public RijfiePyramidStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_lowLevel = Param(nameof(LowLevel), 20m)
.SetDisplay("Stochastic Low", "Oversold threshold", "Parameters");
_maPeriod = Param(nameof(MaPeriod), 10)
.SetDisplay("EMA Period", "EMA length", "Parameters")
.SetGreaterThanZero();
_stepLevel = Param(nameof(StepLevel), 1m)
.SetDisplay("Step Level", "Percent drop for next buy", "Parameters");
_takeProfitPct = Param(nameof(TakeProfitPct), 2m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stochastic = default;
_nextBuyPrice = 0;
_prevK = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_stochastic = new StochasticOscillator();
var ema = new ExponentialMovingAverage { Length = MaPeriod };
Indicators.Add(_stochastic);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, (candle, emaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
var stochResult = _stochastic.Process(candle);
if (!stochResult.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var stochVal = (StochasticOscillatorValue)stochResult;
if (stochVal.K is not decimal k)
return;
var price = candle.ClosePrice;
// Initial buy when Stochastic crosses above the low level
if (_prevK.HasValue && _prevK.Value < LowLevel && k >= LowLevel && Position == 0)
{
BuyMarket();
_nextBuyPrice = price * (1m - StepLevel / 100m);
}
// Additional buys when price drops below the threshold but stays above EMA
else if (Position > 0 && _nextBuyPrice > 0 && price <= _nextBuyPrice && price > emaValue)
{
BuyMarket();
_nextBuyPrice = price * (1m - StepLevel / 100m);
}
// Exit on Stochastic overbought
if (Position > 0 && k > 80m)
{
SellMarket();
_nextBuyPrice = 0;
}
_prevK = k;
})
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(TakeProfitPct * 2, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
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.Indicators import StochasticOscillator, ExponentialMovingAverage, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class rijfie_pyramid_strategy(Strategy):
def __init__(self):
super(rijfie_pyramid_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._low_level = self.Param("LowLevel", 20.0) \
.SetDisplay("Stochastic Low", "Oversold threshold", "Parameters")
self._ma_period = self.Param("MaPeriod", 10) \
.SetDisplay("EMA Period", "EMA length", "Parameters")
self._step_level = self.Param("StepLevel", 1.0) \
.SetDisplay("Step Level", "Percent drop for next buy", "Parameters")
self._take_profit_pct = self.Param("TakeProfitPct", 2.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._stochastic = None
self._next_buy_price = 0.0
self._prev_k = None
@property
def candle_type(self):
return self._candle_type.Value
@property
def low_level(self):
return self._low_level.Value
@property
def ma_period(self):
return self._ma_period.Value
@property
def step_level(self):
return self._step_level.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
def OnReseted(self):
super(rijfie_pyramid_strategy, self).OnReseted()
self._stochastic = None
self._next_buy_price = 0.0
self._prev_k = None
def OnStarted2(self, time):
super(rijfie_pyramid_strategy, self).OnStarted2(time)
self._stochastic = StochasticOscillator()
ema = ExponentialMovingAverage()
ema.Length = self.ma_period
self.Indicators.Add(self._stochastic)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.on_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(float(self.take_profit_pct) * 2, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def on_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
cv = CandleIndicatorValue(self._stochastic, candle)
stoch_result = self._stochastic.Process(cv)
if not stoch_result.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
k = stoch_result.K
if k is None:
return
k = float(k)
ema_value = float(ema_value)
price = float(candle.ClosePrice)
if self._prev_k is not None and self._prev_k < float(self.low_level) and k >= float(self.low_level) and self.Position == 0:
self.BuyMarket()
self._next_buy_price = price * (1.0 - float(self.step_level) / 100.0)
elif self.Position > 0 and self._next_buy_price > 0 and price <= self._next_buy_price and price > ema_value:
self.BuyMarket()
self._next_buy_price = price * (1.0 - float(self.step_level) / 100.0)
if self.Position > 0 and k > 80.0:
self.SellMarket()
self._next_buy_price = 0.0
self._prev_k = k
def CreateClone(self):
return rijfie_pyramid_strategy()