Estrategia de Vela de 30 Minutos
Este enfoque compara el precio de apertura de la vela actual de 30 minutos con el cierre de la vela anterior. Si una nueva vela abre por encima del cierre anterior, se abre una posición larga. Cuando ya se está largo y la siguiente vela abre por debajo del cierre anterior, la estrategia se invierte a una posición corta. Todas las posiciones abiertas se cierran un minuto antes de que termine la vela actual.
Detalles
- Criterios de entrada:
- Largo: apertura de la vela actual > cierre de la vela anterior.
- Corto: apertura de la vela actual < cierre de la vela anterior mientras se mantiene una posición larga.
- Largo/Corto: Ambos lados.
- Criterios de salida:
- Cerrar cualquier posición un minuto antes de que cierre la vela.
- Stops: Ninguno.
- Valores predeterminados:
CandleType= TimeSpan.FromMinutes(30).TimeFrame().
- Filtros:
- Categoría: Momentum
- Dirección: Ambos
- Indicadores: Price action
- Stops: No
- Complejidad: Básico
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// 30 Minute Candle Strategy.
/// Compares current close with previous close.
/// Buys when close > previous close, sells when close < previous close.
/// Uses EMA as trend filter.
/// </summary>
public class ThirtyMinuteCandleStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _ema;
private decimal _prevClose;
private bool _hasPrevClose;
private int _cooldownRemaining;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public ThirtyMinuteCandleStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA trend filter period", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 15)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_prevClose = 0;
_hasPrevClose = false;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal emaVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed)
return;
var close = candle.ClosePrice;
if (!_hasPrevClose)
{
_prevClose = close;
_hasPrevClose = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevClose = close;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
return;
}
// Buy: close > previous close + above EMA
if (close > _prevClose && close > emaVal && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: close < previous close + below EMA
else if (close < _prevClose && close < emaVal && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long: close drops below EMA
else if (Position > 0 && close < emaVal)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short: close rises above EMA
else if (Position < 0 && close > emaVal)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevClose = close;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class thirty_minute_candle_strategy(Strategy):
"""30 Minute Candle Strategy."""
def __init__(self):
super(thirty_minute_candle_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA trend filter period", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 15) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._ema = None
self._prev_close = 0
self._has_prev = False
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(thirty_minute_candle_strategy, self).OnReseted()
self._ema = None
self._prev_close = 0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(thirty_minute_candle_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = int(self._ema_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_val):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed:
return
close = float(candle.ClosePrice)
ema_v = float(ema_val)
if not self._has_prev:
self._prev_close = close
self._has_prev = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_close = close
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
return
cooldown = int(self._cooldown_bars.Value)
if close > self._prev_close and close > ema_v and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif close < self._prev_close and close < ema_v and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and close < ema_v:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and close > ema_v:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_close = close
def CreateClone(self):
return thirty_minute_candle_strategy()