Estrategia de Ángulo LSMA
Esta estrategia utiliza el ángulo de la Media Móvil de Mínimos Cuadrados (LSMA) para detectar la dirección de la tendencia. El ángulo se aproxima mediante la diferencia entre dos valores de LSMA separados por un número configurable de barras.
- Entrada larga: el ángulo LSMA sube por encima del umbral positivo.
- Salida larga: el ángulo vuelve por debajo del umbral positivo.
- Entrada corta: el ángulo LSMA cae por debajo del umbral negativo.
- Salida corta: el ángulo vuelve por encima del umbral negativo.
Parámetros
LSMA Period: longitud para el cálculo de LSMA.Angle Threshold: valor absoluto que define la zona neutral alrededor de cero.Start Shift: barra más antigua utilizada para calcular el ángulo.End Shift: barra más reciente utilizada para calcular el ángulo.Candle Type: tipo de datos de vela para el cálculo.
Notas
- Los valores del ángulo se escalan a puntos según el instrumento (1000 para pares JPY, de lo contrario 100000).
- Funciona solo en velas completadas.
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>
/// LSMA Angle based strategy.
/// Opens long when the LSMA slope rises above a threshold and short when it falls below.
/// </summary>
public class LsmaAngleStrategy : Strategy
{
private readonly StrategyParam<int> _lsmaPeriod;
private readonly StrategyParam<decimal> _slopeThreshold;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevLsma;
private decimal _prevSlope;
public int LsmaPeriod { get => _lsmaPeriod.Value; set => _lsmaPeriod.Value = value; }
public decimal SlopeThreshold { get => _slopeThreshold.Value; set => _slopeThreshold.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public LsmaAngleStrategy()
{
_lsmaPeriod = Param(nameof(LsmaPeriod), 25)
.SetGreaterThanZero()
.SetDisplay("LSMA Period", "LSMA calculation length", "Indicator");
_slopeThreshold = Param(nameof(SlopeThreshold), 0.05m)
.SetDisplay("Slope Threshold", "Percentage slope threshold", "Indicator");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_prevLsma = null;
_prevSlope = 0m;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var lsma = new LinearReg { Length = LsmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(lsma, (candle, lsmaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (_prevLsma is null)
{
_prevLsma = lsmaValue;
return;
}
// Calculate slope as percentage change
var slope = _prevLsma.Value != 0 ? (lsmaValue - _prevLsma.Value) / _prevLsma.Value * 100m : 0m;
var wasUp = _prevSlope > SlopeThreshold;
var wasDown = _prevSlope < -SlopeThreshold;
var isUp = slope > SlopeThreshold;
var isDown = slope < -SlopeThreshold;
if (!wasUp && isUp && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (!wasDown && isDown && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
else if (wasUp && !isUp && Position > 0)
{
SellMarket();
}
else if (wasDown && !isDown && Position < 0)
{
BuyMarket();
}
_prevSlope = slope;
_prevLsma = lsmaValue;
})
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, lsma);
DrawOwnTrades(area);
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import LinearReg
from StockSharp.Algo.Strategies import Strategy
class lsma_angle_strategy(Strategy):
def __init__(self):
super(lsma_angle_strategy, self).__init__()
self._lsma_period = self.Param("LsmaPeriod", 25) \
.SetDisplay("LSMA Period", "LSMA calculation length", "Indicator")
self._slope_threshold = self.Param("SlopeThreshold", 0.05) \
.SetDisplay("Slope Threshold", "Percentage slope threshold", "Indicator")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_lsma = None
self._prev_slope = 0.0
@property
def lsma_period(self):
return self._lsma_period.Value
@property
def slope_threshold(self):
return self._slope_threshold.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(lsma_angle_strategy, self).OnReseted()
self._prev_lsma = None
self._prev_slope = 0.0
def OnStarted2(self, time):
super(lsma_angle_strategy, self).OnStarted2(time)
lsma = LinearReg()
lsma.Length = self.lsma_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(lsma, self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, lsma)
self.DrawOwnTrades(area)
def process_candle(self, candle, lsma_value):
if candle.State != CandleStates.Finished:
return
lsma_value = float(lsma_value)
if self._prev_lsma is None:
self._prev_lsma = lsma_value
return
slope = (lsma_value - self._prev_lsma) / self._prev_lsma * 100.0 if self._prev_lsma != 0 else 0.0
threshold = float(self.slope_threshold)
was_up = self._prev_slope > threshold
was_down = self._prev_slope < -threshold
is_up = slope > threshold
is_down = slope < -threshold
if not was_up and is_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif not was_down and is_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif was_up and not is_up and self.Position > 0:
self.SellMarket()
elif was_down and not is_down and self.Position < 0:
self.BuyMarket()
self._prev_slope = slope
self._prev_lsma = lsma_value
def CreateClone(self):
return lsma_angle_strategy()