Estrategia GRIM309 CallPut
La Estrategia GRIM309 CallPut opera basándose en la alineación de múltiples EMA con un sistema de advertencia. Las posiciones largas entran cuando las EMA de corto plazo confirman una tendencia alcista y la EMA5 sube por encima de la EMA10. Las posiciones cortas entran en condiciones opuestas. Un período de enfriamiento impide la reentrada inmediata tras un cierre. Una advertencia adicional activa salidas anticipadas cuando el diferencial EMA5-EMA10 se contrae rápidamente.
Detalles
- Datos: Velas de precio.
- Criterios de entrada:
- Largo: EMA10 sobre EMA20, precio sobre EMA50, EMA5 sube sobre EMA10, sin posición y enfriamiento cumplido.
- Corto: EMA10 bajo EMA20, precio bajo EMA50, EMA5 cae bajo EMA10, sin posición y enfriamiento cumplido.
- Criterios de salida: Precio cruzando EMA15 o señal de advertencia.
- Stops: Ninguno.
- Valores predeterminados:
Ema5Length= 5Ema10Length= 10Ema15Length= 15Ema20Length= 20Ema50Length= 50Ema200Length= 200CooldownBars= 2
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Largo y Corto
- Indicadores: EMA
- Complejidad: Moderado
- Nivel de riesgo: Medio
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>
/// GRIM309 CallPut strategy.
/// Goes long when EMA10 is above EMA20, price is above EMA50 and a rising EMA5 is above EMA10; goes short on the mirrored
/// conditions. Entries need a flat position and at least CooldownBars candles since the last exit. A position closes when price
/// crosses EMA15 against it or when the warning fires: the EMA5-EMA10 spread in the trade's direction shrinks to less than half
/// of its previous value.
/// </summary>
public class Grim309CallPutStrategy : Strategy
{
private readonly StrategyParam<int> _ema5Length;
private readonly StrategyParam<int> _ema10Length;
private readonly StrategyParam<int> _ema15Length;
private readonly StrategyParam<int> _ema20Length;
private readonly StrategyParam<int> _ema50Length;
private readonly StrategyParam<int> _ema200Length;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevEma5;
private decimal? _prevSpread;
private int _barsSinceExit;
/// <summary>
/// Length of the fastest EMA.
/// </summary>
public int Ema5Length
{
get => _ema5Length.Value;
set => _ema5Length.Value = value;
}
/// <summary>
/// Length of the EMA paired with EMA5.
/// </summary>
public int Ema10Length
{
get => _ema10Length.Value;
set => _ema10Length.Value = value;
}
/// <summary>
/// Length of the exit EMA.
/// </summary>
public int Ema15Length
{
get => _ema15Length.Value;
set => _ema15Length.Value = value;
}
/// <summary>
/// Length of the EMA compared with EMA10.
/// </summary>
public int Ema20Length
{
get => _ema20Length.Value;
set => _ema20Length.Value = value;
}
/// <summary>
/// Length of the trend EMA price is compared with.
/// </summary>
public int Ema50Length
{
get => _ema50Length.Value;
set => _ema50Length.Value = value;
}
/// <summary>
/// Length of the long-term EMA shown on the chart.
/// </summary>
public int Ema200Length
{
get => _ema200Length.Value;
set => _ema200Length.Value = value;
}
/// <summary>
/// Candles to wait after an exit before a new entry.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public Grim309CallPutStrategy()
{
_ema5Length = Param(nameof(Ema5Length), 5)
.SetGreaterThanZero()
.SetDisplay("EMA5 Length", "Length of the fastest EMA", "Indicators");
_ema10Length = Param(nameof(Ema10Length), 10)
.SetGreaterThanZero()
.SetDisplay("EMA10 Length", "Length of the EMA paired with EMA5", "Indicators");
_ema15Length = Param(nameof(Ema15Length), 15)
.SetGreaterThanZero()
.SetDisplay("EMA15 Length", "Length of the exit EMA", "Indicators");
_ema20Length = Param(nameof(Ema20Length), 20)
.SetGreaterThanZero()
.SetDisplay("EMA20 Length", "Length of the EMA compared with EMA10", "Indicators");
_ema50Length = Param(nameof(Ema50Length), 50)
.SetGreaterThanZero()
.SetDisplay("EMA50 Length", "Length of the trend EMA", "Indicators");
_ema200Length = Param(nameof(Ema200Length), 200)
.SetGreaterThanZero()
.SetDisplay("EMA200 Length", "Length of the long-term EMA", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 2)
.SetNotNegative()
.SetDisplay("Cooldown Bars", "Candles to wait after an exit before a new entry", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_prevEma5 = null;
_prevSpread = null;
_barsSinceExit = int.MaxValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var ema5 = new ExponentialMovingAverage { Length = Ema5Length };
var ema10 = new ExponentialMovingAverage { Length = Ema10Length };
var ema15 = new ExponentialMovingAverage { Length = Ema15Length };
var ema20 = new ExponentialMovingAverage { Length = Ema20Length };
var ema50 = new ExponentialMovingAverage { Length = Ema50Length };
var ema200 = new ExponentialMovingAverage { Length = Ema200Length };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema5, ema10, ema15, ema20, ema50, ema200, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema5);
DrawIndicator(area, ema10);
DrawIndicator(area, ema15);
DrawIndicator(area, ema20);
DrawIndicator(area, ema50);
DrawIndicator(area, ema200);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal ema5, decimal ema10, decimal ema15, decimal ema20, decimal ema50, decimal ema200)
{
if (candle.State != CandleStates.Finished)
return;
if (_barsSinceExit != int.MaxValue)
_barsSinceExit++;
var prevEma5 = _prevEma5;
var prevSpread = _prevSpread;
var spread = ema5 - ema10;
_prevEma5 = ema5;
_prevSpread = spread;
if (prevEma5 is not decimal lastEma5 || prevSpread is not decimal lastSpread)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (Position > 0)
{
var warning = lastSpread > 0 && spread < lastSpread / 2;
if (close < ema15 || warning)
{
SellMarket(Position);
_barsSinceExit = 0;
}
return;
}
if (Position < 0)
{
var warning = lastSpread < 0 && spread > lastSpread / 2;
if (close > ema15 || warning)
{
BuyMarket(-Position);
_barsSinceExit = 0;
}
return;
}
if (_barsSinceExit < CooldownBars)
return;
if (ema10 > ema20 && close > ema50 && ema5 > ema10 && ema5 > lastEma5)
BuyMarket(Volume);
else if (ema10 < ema20 && close < ema50 && ema5 < ema10 && ema5 < lastEma5)
SellMarket(Volume);
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class grim309_call_put_strategy(Strategy):
"""
GRIM309 CallPut strategy.
Goes long when EMA10 is above EMA20, price is above EMA50 and a rising EMA5 is above EMA10; goes short on the mirrored
conditions. Entries need a flat position and at least CooldownBars candles since the last exit. A position closes when price
crosses EMA15 against it or when the warning fires: the EMA5-EMA10 spread in the trade's direction shrinks to less than half
of its previous value.
"""
def __init__(self):
super(grim309_call_put_strategy, self).__init__()
self._ema5_length = self.Param("Ema5Length", 5).SetGreaterThanZero().SetDisplay("EMA5 Length", "Length of the fastest EMA", "Indicators")
self._ema10_length = self.Param("Ema10Length", 10).SetGreaterThanZero().SetDisplay("EMA10 Length", "Length of the EMA paired with EMA5", "Indicators")
self._ema15_length = self.Param("Ema15Length", 15).SetGreaterThanZero().SetDisplay("EMA15 Length", "Length of the exit EMA", "Indicators")
self._ema20_length = self.Param("Ema20Length", 20).SetGreaterThanZero().SetDisplay("EMA20 Length", "Length of the EMA compared with EMA10", "Indicators")
self._ema50_length = self.Param("Ema50Length", 50).SetGreaterThanZero().SetDisplay("EMA50 Length", "Length of the trend EMA", "Indicators")
self._ema200_length = self.Param("Ema200Length", 200).SetGreaterThanZero().SetDisplay("EMA200 Length", "Length of the long-term EMA", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 2).SetNotNegative().SetDisplay("Cooldown Bars", "Candles to wait after an exit before a new entry", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_ema5 = None
self._prev_spread = None
self._bars_since_exit = None
def OnReseted(self):
super(grim309_call_put_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(grim309_call_put_strategy, self).OnStarted2(time)
self._reset_state()
def ema(length):
indicator = ExponentialMovingAverage()
indicator.Length = length
return indicator
ema5 = ema(self._ema5_length.Value)
ema10 = ema(self._ema10_length.Value)
ema15 = ema(self._ema15_length.Value)
ema20 = ema(self._ema20_length.Value)
ema50 = ema(self._ema50_length.Value)
ema200 = ema(self._ema200_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema5, ema10, ema15, ema20, ema50, ema200, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
for indicator in (ema5, ema10, ema15, ema20, ema50, ema200):
self.DrawIndicator(area, indicator)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema5_value, ema10_value, ema15_value, ema20_value, ema50_value, ema200_value):
if candle.State != CandleStates.Finished:
return
if self._bars_since_exit is not None:
self._bars_since_exit += 1
ema5 = float(ema5_value)
ema10 = float(ema10_value)
ema15 = float(ema15_value)
ema20 = float(ema20_value)
ema50 = float(ema50_value)
prev_ema5 = self._prev_ema5
prev_spread = self._prev_spread
spread = ema5 - ema10
self._prev_ema5 = ema5
self._prev_spread = spread
if prev_ema5 is None or prev_spread is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
if self.Position > 0:
warning = prev_spread > 0 and spread < prev_spread / 2.0
if close < ema15 or warning:
self.SellMarket(self.Position)
self._bars_since_exit = 0
return
if self.Position < 0:
warning = prev_spread < 0 and spread > prev_spread / 2.0
if close > ema15 or warning:
self.BuyMarket(-self.Position)
self._bars_since_exit = 0
return
if self._bars_since_exit is not None and self._bars_since_exit < self._cooldown_bars.Value:
return
if ema10 > ema20 and close > ema50 and ema5 > ema10 and ema5 > prev_ema5:
self.BuyMarket(self.Volume)
elif ema10 < ema20 and close < ema50 and ema5 < ema10 and ema5 < prev_ema5:
self.SellMarket(self.Volume)
def CreateClone(self):
return grim309_call_put_strategy()