Esta carpeta proporciona un andamio mínimo para construir ideas de trading personalizadas.
La estrategia solo calcula una media móvil exponencial y expone una amplia gama de
parámetros comunes: habilitación de operaciones largas o cortas, take profit y stop loss
opcionales, y rangos de optimización. Los desarrolladores pueden insertar su propia lógica
de entrada y salida dentro de los marcadores de posición para prototipar rápidamente
nuevos sistemas.
La plantilla también demuestra cómo iniciar el módulo de protección integrado con
objetivos basados en porcentajes, facilitando la experimentación con diferentes
configuraciones de riesgo. Dado que no se incluyen señales reales, este script no está
destinado a ser operado tal como está, sino más bien a servir como punto de partida para
investigación adicional.
Detalles
Criterios de entrada: No implementados – reemplazar con reglas personalizadas.
Largo/Corto: Configurable mediante parámetros.
Criterios de salida: No implementados – reemplazar con reglas personalizadas.
Stops: Take profit y stop loss porcentuales opcionales gestionados por el módulo de protección.
Valores predeterminados:
Longitud EMA = 10.
Take profit = 1.2%, Stop loss = 1.8% (deshabilitado por defecto).
Filtros:
Categoría: Plantilla
Dirección: Configurable
Indicadores: EMA
Stops: Opcional
Complejidad: Bajo
Marco temporal: Cualquiera
Estacionalidad: No
Redes neuronales: No
Divergencia: No
Nivel de riesgo: Definido por el usuario
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>
/// Strategy Base - EMA trend following strategy.
/// Buys when price crosses above EMA, sells when price crosses below EMA.
/// </summary>
public class StratBaseStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _ema;
private decimal _prevClose;
private decimal _prevEma;
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 StratBaseStrategy()
{
_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 period", "Moving Averages");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait 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;
_prevEma = 0;
_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)
{
_prevClose = candle.ClosePrice;
_prevEma = emaVal;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevClose = candle.ClosePrice;
_prevEma = emaVal;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = candle.ClosePrice;
_prevEma = emaVal;
return;
}
if (_prevClose == 0 || _prevEma == 0)
{
_prevClose = candle.ClosePrice;
_prevEma = emaVal;
return;
}
// Price crosses above EMA
var crossUp = candle.ClosePrice > emaVal && _prevClose <= _prevEma;
// Price crosses below EMA
var crossDown = candle.ClosePrice < emaVal && _prevClose >= _prevEma;
if (crossUp && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
else if (crossDown && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
_prevClose = candle.ClosePrice;
_prevEma = emaVal;
}
}
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 strat_base_strategy(Strategy):
"""Strategy Base - EMA trend following strategy."""
def __init__(self):
super(strat_base_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 period", "Moving Averages")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._ema = None
self._prev_close = 0.0
self._prev_ema = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(strat_base_strategy, self).OnReseted()
self._ema = None
self._prev_close = 0.0
self._prev_ema = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(strat_base_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:
self._prev_close = float(candle.ClosePrice)
self._prev_ema = float(ema_val)
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_close = float(candle.ClosePrice)
self._prev_ema = float(ema_val)
return
close = float(candle.ClosePrice)
ema = float(ema_val)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_ema = ema
return
if self._prev_close == 0.0 or self._prev_ema == 0.0:
self._prev_close = close
self._prev_ema = ema
return
cooldown = int(self._cooldown_bars.Value)
cross_up = close > ema and self._prev_close <= self._prev_ema
cross_down = close < ema and self._prev_close >= self._prev_ema
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
self._prev_close = close
self._prev_ema = ema
def CreateClone(self):
return strat_base_strategy()