Estrategia Improvisando
Improvisando combina un filtro de tendencia EMA básico con oscilaciones del RSI. El objetivo es seguir la dirección prevaleciente indicada por la EMA mientras se entra solo cuando el RSI cruza la línea neutral de 50. El diseño original también experimentó con momentum estilo MACD, pero esta versión simplificada se centra en la claridad y la facilidad de ajuste.
El usuario puede habilitar operaciones largas y/o cortas por separado.
Detalles
- Criterios de entrada:
- Largo:
Close > EMAyRSI > 50 - Corto:
Close < EMAyRSI < 50
- Largo:
- Largo/Corto: Configurable
- Criterios de salida:
- Señal opuesta
- Stops: Ninguno
- Valores predeterminados:
EmaLength= 10RsiLength= 14
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Configurable
- Indicadores: EMA, RSI
- Stops: No
- Complejidad: Bajo
- Marco temporal: Corto plazo
- 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>
/// Improvisando Strategy.
/// Uses EMA trend + RSI filter + candle pattern (engulfing) for entries.
/// Exits via take profit or EMA crossback.
/// </summary>
public class ImprovisandoStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _ema;
private RelativeStrengthIndex _rsi;
private decimal _prevClose;
private decimal _prevOpen;
private int _cooldownRemaining;
private decimal? _entryPrice;
public ImprovisandoStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period", "Moving Averages");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_cooldownBars = Param(nameof(CooldownBars), 15)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_rsi = null;
_prevClose = 0;
_prevOpen = 0;
_cooldownRemaining = 0;
_entryPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaLength };
_rsi = new RelativeStrengthIndex { Length = RsiLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, _rsi, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal ema, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed || !_rsi.IsFormed)
{
_prevClose = candle.ClosePrice;
_prevOpen = candle.OpenPrice;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevClose = candle.ClosePrice;
_prevOpen = candle.OpenPrice;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = candle.ClosePrice;
_prevOpen = candle.OpenPrice;
return;
}
var close = candle.ClosePrice;
var open = candle.OpenPrice;
// Engulfing patterns
var prevBearish = _prevClose < _prevOpen && _prevClose > 0;
var prevBullish = _prevClose > _prevOpen && _prevClose > 0;
// Bullish engulfing: previous red, current green, close above prev open
var buyPattern = prevBearish && close > open && close > _prevOpen;
// Bearish engulfing: previous green, current red, close below prev open
var sellPattern = prevBullish && close < open && close < _prevOpen;
// Exit long: price crosses below EMA or take profit
if (Position > 0)
{
var tp = _entryPrice.HasValue && close > _entryPrice.Value * 1.02m;
if (close < ema || tp)
{
SellMarket(Math.Abs(Position));
_entryPrice = null;
_cooldownRemaining = CooldownBars;
_prevClose = close;
_prevOpen = open;
return;
}
}
// Exit short: price crosses above EMA or take profit
else if (Position < 0)
{
var tp = _entryPrice.HasValue && close < _entryPrice.Value * 0.98m;
if (close > ema || tp)
{
BuyMarket(Math.Abs(Position));
_entryPrice = null;
_cooldownRemaining = CooldownBars;
_prevClose = close;
_prevOpen = open;
return;
}
}
// Buy: engulfing + above EMA + RSI not overbought
if (buyPattern && close > ema && rsi < 65 && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_entryPrice = close;
_cooldownRemaining = CooldownBars;
}
// Sell: engulfing + below EMA + RSI not oversold
else if (sellPattern && close < ema && rsi > 35 && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_entryPrice = close;
_cooldownRemaining = CooldownBars;
}
_prevClose = close;
_prevOpen = open;
}
}
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, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class improvisando_strategy(Strategy):
"""Improvisando Strategy. EMA trend + RSI filter + engulfing pattern."""
def __init__(self):
super(improvisando_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA period", "Moving Averages")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._cooldown_bars = self.Param("CooldownBars", 15) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._ema = None
self._rsi = None
self._prev_close = 0.0
self._prev_open = 0.0
self._cooldown_remaining = 0
self._entry_price = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(improvisando_strategy, self).OnReseted()
self._ema = None
self._rsi = None
self._prev_close = 0.0
self._prev_open = 0.0
self._cooldown_remaining = 0
self._entry_price = None
def OnStarted2(self, time):
super(improvisando_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = int(self._ema_length.Value)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema, self._rsi, 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, rsi_val):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed or not self._rsi.IsFormed:
self._prev_close = float(candle.ClosePrice)
self._prev_open = float(candle.OpenPrice)
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_close = float(candle.ClosePrice)
self._prev_open = float(candle.OpenPrice)
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = float(candle.ClosePrice)
self._prev_open = float(candle.OpenPrice)
return
close = float(candle.ClosePrice)
opn = float(candle.OpenPrice)
ema = float(ema_val)
rsi = float(rsi_val)
cooldown = int(self._cooldown_bars.Value)
prev_bearish = self._prev_close < self._prev_open and self._prev_close > 0
prev_bullish = self._prev_close > self._prev_open and self._prev_close > 0
buy_pattern = prev_bearish and close > opn and close > self._prev_open
sell_pattern = prev_bullish and close < opn and close < self._prev_open
# Exit long
if self.Position > 0:
tp = self._entry_price is not None and close > self._entry_price * 1.02
if close < ema or tp:
self.SellMarket(Math.Abs(self.Position))
self._entry_price = None
self._cooldown_remaining = cooldown
self._prev_close = close
self._prev_open = opn
return
# Exit short
elif self.Position < 0:
tp = self._entry_price is not None and close < self._entry_price * 0.98
if close > ema or tp:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = None
self._cooldown_remaining = cooldown
self._prev_close = close
self._prev_open = opn
return
# Buy: engulfing + above EMA + RSI not overbought
if buy_pattern and close > ema and rsi < 65 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
# Sell: engulfing + below EMA + RSI not oversold
elif sell_pattern and close < ema and rsi > 35 and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
self._prev_close = close
self._prev_open = opn
def CreateClone(self):
return improvisando_strategy()