Estrategia de Arcoíris de Medias Móviles (Stormer)
Esta estrategia traza un arcoíris de doce medias móviles. Las operaciones se abren cuando se confirma la tendencia y el precio toca una de las medias.
Una posición larga se abre cuando el precio marca un nuevo máximo, todas las medias centrales apuntan hacia arriba y la vela cierra por encima de la media de todas las medias. Una posición corta se abre cuando ocurren las condiciones opuestas.
El stop loss se fija en la media móvil tocada anteriormente. El take profit se calcula como un múltiplo de la distancia entre el precio de entrada y el stop loss.
Detalles
- Indicadores: 12 medias móviles de tipo configurable.
- Largo: Tendencia alcista, nuevo máximo y precio de toque previo.
- Corto: Tendencia bajista, nuevo mínimo y precio de toque previo.
- Salidas: Stop loss en la media tocada, objetivo = entrada ± distancia * factor. Salida de reversión opcional cuando la tendencia muestra señales de giro.
- Parámetros: tipo de media móvil, longitudes, factor objetivo, opciones de reversión.
- Marco temporal: Cualquiera.
using System;
using System.Linq;
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>
/// Moving Average Rainbow (Stormer) strategy.
/// Uses multiple EMA rainbow for trend confirmation.
/// Enters long when price is above all MAs (bullish alignment), short when below.
/// </summary>
public class MovingAverageRainbowStormerStrategy : Strategy
{
private readonly StrategyParam<decimal> _targetFactor;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _minTrendSpreadPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private bool _prevBullish;
private bool _prevBearish;
private int _barIndex;
private int _lastSignalBar = -1000000;
public decimal TargetFactor { get => _targetFactor.Value; set => _targetFactor.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public decimal MinTrendSpreadPercent { get => _minTrendSpreadPercent.Value; set => _minTrendSpreadPercent.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MovingAverageRainbowStormerStrategy()
{
_targetFactor = Param(nameof(TargetFactor), 2m);
_cooldownBars = Param(nameof(CooldownBars), 40).SetGreaterThanZero();
_minTrendSpreadPercent = Param(nameof(MinTrendSpreadPercent), 0.05m).SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(10).TimeFrame());
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_prevBullish = false;
_prevBearish = false;
_barIndex = 0;
_lastSignalBar = -1000000;
var ma3 = new ExponentialMovingAverage { Length = 3 };
var ma8 = new ExponentialMovingAverage { Length = 8 };
var ma20 = new ExponentialMovingAverage { Length = 20 };
var ma50 = new ExponentialMovingAverage { Length = 50 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ma3, ma8, ma20, ma50, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ma3, decimal ma8, decimal ma20, decimal ma50)
{
if (candle.State != CandleStates.Finished)
return;
_barIndex++;
var close = candle.ClosePrice;
var bullishAlignment = ma3 > ma8 && ma8 > ma20 && ma20 > ma50;
var bearishAlignment = ma3 < ma8 && ma8 < ma20 && ma20 < ma50;
var trendSpreadPercent = close != 0m ? Math.Abs(ma3 - ma50) / close * 100m : 0m;
var canSignal = _barIndex - _lastSignalBar >= CooldownBars;
var bullishSignal = bullishAlignment && trendSpreadPercent >= MinTrendSpreadPercent;
var bearishSignal = bearishAlignment && trendSpreadPercent >= MinTrendSpreadPercent;
if (canSignal && bullishSignal && close > ma3 && Position <= 0)
{
BuyMarket();
_entryPrice = close;
_lastSignalBar = _barIndex;
}
else if (canSignal && bearishSignal && close < ma3 && Position >= 0)
{
SellMarket();
_entryPrice = close;
_lastSignalBar = _barIndex;
}
if (canSignal && Position > 0 && _entryPrice > 0)
{
var risk = _entryPrice - ma20;
if (risk > 0)
{
var target = _entryPrice + risk * TargetFactor;
if (close >= target || close < ma20)
{
SellMarket();
_lastSignalBar = _barIndex;
}
}
else if (close < ma8)
{
SellMarket();
_lastSignalBar = _barIndex;
}
}
else if (canSignal && Position < 0 && _entryPrice > 0)
{
var risk = ma20 - _entryPrice;
if (risk > 0)
{
var target = _entryPrice - risk * TargetFactor;
if (close <= target || close > ma20)
{
BuyMarket();
_lastSignalBar = _barIndex;
}
}
else if (close > ma8)
{
BuyMarket();
_lastSignalBar = _barIndex;
}
}
_prevBullish = bullishAlignment;
_prevBearish = bearishAlignment;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_prevBullish = false;
_prevBearish = false;
_barIndex = 0;
_lastSignalBar = -1000000;
}
}
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 moving_average_rainbow_stormer_strategy(Strategy):
def __init__(self):
super(moving_average_rainbow_stormer_strategy, self).__init__()
self._target_factor = self.Param("TargetFactor", 2.0)
self._cooldown_bars = self.Param("CooldownBars", 40) \
.SetGreaterThanZero()
self._min_trend_spread_percent = self.Param("MinTrendSpreadPercent", 0.05) \
.SetGreaterThanZero()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(10)))
self._entry_price = 0.0
self._bar_index = 0
self._last_signal_bar = -1000000
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(moving_average_rainbow_stormer_strategy, self).OnReseted()
self._entry_price = 0.0
self._bar_index = 0
self._last_signal_bar = -1000000
def OnStarted2(self, time):
super(moving_average_rainbow_stormer_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._bar_index = 0
self._last_signal_bar = -1000000
self._ma3 = ExponentialMovingAverage()
self._ma3.Length = 3
self._ma8 = ExponentialMovingAverage()
self._ma8.Length = 8
self._ma20 = ExponentialMovingAverage()
self._ma20.Length = 20
self._ma50 = ExponentialMovingAverage()
self._ma50.Length = 50
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ma3, self._ma8, self._ma20, self._ma50, self.OnProcess).Start()
def OnProcess(self, candle, ma3, ma8, ma20, ma50):
if candle.State != CandleStates.Finished:
return
self._bar_index += 1
close = float(candle.ClosePrice)
m3 = float(ma3)
m8 = float(ma8)
m20 = float(ma20)
m50 = float(ma50)
bullish_alignment = m3 > m8 and m8 > m20 and m20 > m50
bearish_alignment = m3 < m8 and m8 < m20 and m20 < m50
trend_spread_percent = abs(m3 - m50) / close * 100.0 if close != 0.0 else 0.0
cd = self._cooldown_bars.Value
can_signal = self._bar_index - self._last_signal_bar >= cd
min_spread = float(self._min_trend_spread_percent.Value)
bullish_signal = bullish_alignment and trend_spread_percent >= min_spread
bearish_signal = bearish_alignment and trend_spread_percent >= min_spread
tf = float(self._target_factor.Value)
if can_signal and bullish_signal and close > m3 and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._last_signal_bar = self._bar_index
elif can_signal and bearish_signal and close < m3 and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._last_signal_bar = self._bar_index
if can_signal and self.Position > 0 and self._entry_price > 0.0:
risk = self._entry_price - m20
if risk > 0.0:
target = self._entry_price + risk * tf
if close >= target or close < m20:
self.SellMarket()
self._last_signal_bar = self._bar_index
elif close < m8:
self.SellMarket()
self._last_signal_bar = self._bar_index
elif can_signal and self.Position < 0 and self._entry_price > 0.0:
risk = m20 - self._entry_price
if risk > 0.0:
target = self._entry_price - risk * tf
if close <= target or close > m20:
self.BuyMarket()
self._last_signal_bar = self._bar_index
elif close > m8:
self.BuyMarket()
self._last_signal_bar = self._bar_index
def CreateClone(self):
return moving_average_rainbow_stormer_strategy()