Estrategia de MA Ajustable y Extremidades Alternadas
La estrategia utiliza Bandas de Bollinger para emular la Media Móvil Ajustable con extremidades alternadas. Se abre una posición larga cuando el precio rompe por encima de la banda superior, mientras que se abre una posición corta cuando el precio cae por debajo de la banda inferior. El estado de sobrepaso se alterna, evitando operaciones consecutivas en la misma dirección.
Detalles
- Criterios de entrada:
- Ir largo cuando el máximo de la vela cruza por encima de la banda superior.
- Ir corto cuando el mínimo de la vela cruza por debajo de la banda inferior.
- Criterios de salida:
- Ruptura de la banda opuesta.
- Indicadores: Bandas de Bollinger (SMA + desviación estándar).
- Valores predeterminados:
- Length = 50
- Multiplier = 2
- Filtros:
- Categoría: Ruptura
- Dirección: Ambos
- Marco temporal: Corto/medio plazo
- 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>
/// Adjustable MA and Alternating Extremities Strategy.
/// Buys when price crosses above the upper Bollinger Band and sells when it crosses below the lower band.
/// </summary>
public class AdjustableMaAlternatingExtremitiesStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private bool? _isUpper;
private int _cooldownRemaining;
public int Length { get => _length.Value; set => _length.Value = value; }
public decimal Multiplier { get => _multiplier.Value; set => _multiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public AdjustableMaAlternatingExtremitiesStrategy()
{
_length = Param(nameof(Length), 50)
.SetGreaterThanZero()
.SetDisplay("Length", "Periods for Bollinger Bands", "General")
.SetOptimize(20, 100, 10);
_multiplier = Param(nameof(Multiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "Bollinger band width", "General")
.SetOptimize(1m, 5m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 10)
.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();
_isUpper = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bands = new BollingerBands { Length = Length, Width = Multiplier };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bands, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bands);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var bb = (BollingerBandsValue)bbValue;
if (bb.UpBand is not decimal upper || bb.LowBand is not decimal lower)
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
if (candle.HighPrice > upper && _isUpper != true)
{
_isUpper = true;
if (Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
}
else if (candle.LowPrice < lower && _isUpper != false)
{
_isUpper = false;
if (Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
}
}
}
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 BollingerBands
from StockSharp.Algo.Strategies import Strategy
class adjustable_ma_alternating_extremities_strategy(Strategy):
def __init__(self):
super(adjustable_ma_alternating_extremities_strategy, self).__init__()
self._length = self.Param("Length", 50) \
.SetGreaterThanZero() \
.SetDisplay("Length", "Periods for Bollinger Bands", "General")
self._multiplier = self.Param("Multiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Multiplier", "Bollinger band width", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._is_upper = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adjustable_ma_alternating_extremities_strategy, self).OnReseted()
self._is_upper = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(adjustable_ma_alternating_extremities_strategy, self).OnStarted2(time)
bands = BollingerBands()
bands.Length = int(self._length.Value)
bands.Width = self._multiplier.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bands, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bands)
self.DrawOwnTrades(area)
def _on_process(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if bb_value.UpBand is None or bb_value.LowBand is None:
return
upper = float(bb_value.UpBand)
lower = float(bb_value.LowBand)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
cooldown = int(self._cooldown_bars.Value)
if high > upper and self._is_upper is not True:
self._is_upper = True
if self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif low < lower and self._is_upper is not False:
self._is_upper = False
if self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
def CreateClone(self):
return adjustable_ma_alternating_extremities_strategy()