Estrategia PPO Nube
Esta estrategia de momentum opera los cruces entre el Oscilador de Precio Porcentual (PPO) y su línea de señal. Una posición larga se abre cuando el PPO cruza por encima de su línea de señal, mientras que una posición corta se abre en el cruce opuesto. Las posiciones existentes pueden cerrarse opcionalmente en la señal contraria. La estrategia opera en un único marco temporal.
Detalles
- Criterios de entrada:
- Largo:
PPO cruza por encima de la señal. - Corto:
PPO cruza por debajo de la señal.
- Largo:
- Largo/Corto: Ambos.
- Criterios de salida:
- Largo:
PPO cruza por debajo de la señal(opcional). - Corto:
PPO cruza por encima de la señal(opcional).
- Largo:
- Stops: Ninguno.
- Valores predeterminados:
Fast Period= 12.Slow Period= 26.Signal Period= 9.
- Filtros:
- Categoría: Momentum
- Dirección: Ambos
- Indicadores: Único
- Stops: No
- Complejidad: Básico
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Bajo
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>
/// Strategy based on PPO main and signal line crossovers.
/// PPO = ((ShortEMA - LongEMA) / LongEMA) * 100
/// Signal = EMA(PPO, signalPeriod)
/// Buy when PPO crosses above signal, sell when below.
/// </summary>
public class PpoCloudStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _signalPeriod;
private decimal _prevPpo;
private decimal _prevSignal;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int SignalPeriod { get => _signalPeriod.Value; set => _signalPeriod.Value = value; }
public PpoCloudStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA length", "PPO");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA length", "PPO");
_signalPeriod = Param(nameof(SignalPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Signal Period", "Signal EMA length", "PPO");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_prevPpo = 0;
_prevSignal = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevPpo = 0;
_prevSignal = 0;
_hasPrev = false;
var ppo = new PPO { ShortPeriod = FastPeriod, LongPeriod = SlowPeriod };
var signalEma = new ExponentialMovingAverage { Length = SignalPeriod };
Indicators.Add(signalEma);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ppo, ProcessCandle).Start();
void ProcessCandle(ICandleMessage candle, decimal ppoValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!ppo.IsFormed)
return;
// Compute signal line as EMA of PPO
var sigResult = signalEma.Process(ppoValue, candle.CloseTime, true);
if (!signalEma.IsFormed)
return;
var signalValue = sigResult.GetValue<decimal>();
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevPpo = ppoValue;
_prevSignal = signalValue;
_hasPrev = true;
return;
}
if (_hasPrev)
{
var crossUp = _prevPpo <= _prevSignal && ppoValue > signalValue;
var crossDown = _prevPpo >= _prevSignal && ppoValue < signalValue;
if (crossUp && Position <= 0)
BuyMarket();
else if (crossDown && Position >= 0)
SellMarket();
}
_prevPpo = ppoValue;
_prevSignal = signalValue;
_hasPrev = true;
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ppo);
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
from StockSharp.Algo.Indicators import PPO, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class ppo_cloud_strategy(Strategy):
def __init__(self):
super(ppo_cloud_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast EMA length", "PPO")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow Period", "Slow EMA length", "PPO")
self._signal_period = self.Param("SignalPeriod", 9) \
.SetDisplay("Signal Period", "Signal EMA length", "PPO")
self._prev_ppo = 0.0
self._prev_signal = 0.0
self._has_prev = False
self._ppo = None
self._signal_ema = None
@property
def candle_type(self):
return self._candle_type.Value
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def signal_period(self):
return self._signal_period.Value
def OnReseted(self):
super(ppo_cloud_strategy, self).OnReseted()
self._prev_ppo = 0.0
self._prev_signal = 0.0
self._has_prev = False
self._ppo = None
self._signal_ema = None
def OnStarted2(self, time):
super(ppo_cloud_strategy, self).OnStarted2(time)
self._prev_ppo = 0.0
self._prev_signal = 0.0
self._has_prev = False
self._ppo = PPO()
self._ppo.ShortPeriod = self.fast_period
self._ppo.LongPeriod = self.slow_period
self._signal_ema = ExponentialMovingAverage()
self._signal_ema.Length = self.signal_period
self.Indicators.Add(self._signal_ema)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ppo, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ppo)
self.DrawOwnTrades(area)
def process_candle(self, candle, ppo_value):
if candle.State != CandleStates.Finished:
return
if not self._ppo.IsFormed:
return
ppo_value = float(ppo_value)
sig_result = process_float(self._signal_ema, ppo_value, candle.CloseTime, True)
if not self._signal_ema.IsFormed:
return
signal_value = float(sig_result)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_ppo = ppo_value
self._prev_signal = signal_value
self._has_prev = True
return
if self._has_prev:
cross_up = self._prev_ppo <= self._prev_signal and ppo_value > signal_value
cross_down = self._prev_ppo >= self._prev_signal and ppo_value < signal_value
if cross_up and self.Position <= 0:
self.BuyMarket()
elif cross_down and self.Position >= 0:
self.SellMarket()
self._prev_ppo = ppo_value
self._prev_signal = signal_value
self._has_prev = True
def CreateClone(self):
return ppo_cloud_strategy()