Estrategia de Cruce Lineal
Esta estrategia calcula una regresión lineal del precio basada en el volumen para producir un precio predicho. Se abre una posición larga cuando el precio predicho cruza por encima de su media móvil ponderada y la línea MACD sube por encima de su señal. Se abre una posición corta cuando la línea MACD cae por debajo de su señal y los mínimos recientes son decrecientes.
Detalles
- Criterios de entrada:
- Largo: El precio predicho cruza por encima de su WMA y el MACD sube por encima de la señal.
- Corto: El MACD cae por debajo de la señal y los mínimos hacen mínimos más bajos.
- Largo/Corto: Ambos lados.
- Criterios de salida: Ninguno; las posiciones se invierten con señales opuestas.
- Stops: No.
- Valores predeterminados:
Length= 21.LinearLength= 9.CandleType= TimeSpan.FromMinutes(5).TimeFrame().
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: Linear Regression, WMA, MACD
- Stops: No
- Complejidad: Moderado
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- 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>
/// Strategy that uses linear regression slope crossover with MACD confirmation.
/// Goes long when regression slope turns positive and MACD above signal.
/// Goes short when regression slope turns negative and MACD below signal.
/// </summary>
public class LinearCrossTradingStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _slopeThresholdPercent;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevSlope;
private bool _prevSlopeSet;
private int _barsFromSignal;
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
public decimal SlopeThresholdPercent
{
get => _slopeThresholdPercent.Value;
set => _slopeThresholdPercent.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public LinearCrossTradingStrategy()
{
_length = Param(nameof(Length), 21)
.SetGreaterThanZero()
.SetDisplay("Regression Length", "Number of bars for linear regression", "Indicator");
_slopeThresholdPercent = Param(nameof(SlopeThresholdPercent), 0.02m)
.SetGreaterThanZero()
.SetDisplay("Slope Threshold %", "Minimum normalized slope for signals", "Indicator");
_cooldownBars = Param(nameof(CooldownBars), 60)
.SetGreaterThanZero()
.SetDisplay("Cooldown Bars", "Minimum bars between entries", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(10).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSlope = 0m;
_prevSlopeSet = false;
_barsFromSignal = int.MaxValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var linReg = new LinearReg { Length = Length };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(linReg, OnProcess).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, linReg);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal linRegValue)
{
if (candle.State != CandleStates.Finished)
return;
var closePrice = candle.ClosePrice;
if (closePrice <= 0)
return;
var slope = (closePrice - linRegValue) / closePrice * 100m;
if (!_prevSlopeSet)
{
_prevSlope = slope;
_prevSlopeSet = true;
return;
}
_barsFromSignal++;
if (_barsFromSignal >= CooldownBars)
{
if (_prevSlope <= SlopeThresholdPercent && slope > SlopeThresholdPercent && Position <= 0)
{
BuyMarket();
_barsFromSignal = 0;
}
else if (_prevSlope >= -SlopeThresholdPercent && slope < -SlopeThresholdPercent && Position >= 0)
{
SellMarket();
_barsFromSignal = 0;
}
}
_prevSlope = slope;
}
}
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 LinearReg
from StockSharp.Algo.Strategies import Strategy
class linear_cross_trading_strategy(Strategy):
def __init__(self):
super(linear_cross_trading_strategy, self).__init__()
self._length = self.Param("Length", 21) \
.SetGreaterThanZero() \
.SetDisplay("Regression Length", "Number of bars for linear regression", "Indicator")
self._slope_threshold = self.Param("SlopeThresholdPercent", 0.02) \
.SetGreaterThanZero() \
.SetDisplay("Slope Threshold Pct", "Minimum normalized slope for signals", "Indicator")
self._cooldown_bars = self.Param("CooldownBars", 60) \
.SetGreaterThanZero() \
.SetDisplay("Cooldown Bars", "Minimum bars between entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(10))) \
.SetDisplay("Candle Type", "Type of candles for strategy", "General")
self._prev_slope = 0.0
self._prev_slope_set = False
self._bars_from_signal = 999999
@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(linear_cross_trading_strategy, self).OnReseted()
self._prev_slope = 0.0
self._prev_slope_set = False
self._bars_from_signal = 999999
def OnStarted2(self, time):
super(linear_cross_trading_strategy, self).OnStarted2(time)
lin_reg = LinearReg()
lin_reg.Length = self._length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(lin_reg, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, lin_reg)
self.DrawOwnTrades(area)
def OnProcess(self, candle, lin_reg_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
if close <= 0.0:
return
lrv = float(lin_reg_val)
slope = (close - lrv) / close * 100.0
if not self._prev_slope_set:
self._prev_slope = slope
self._prev_slope_set = True
return
self._bars_from_signal += 1
th = float(self._slope_threshold.Value)
cd = self._cooldown_bars.Value
if self._bars_from_signal >= cd:
if self._prev_slope <= th and slope > th and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif self._prev_slope >= -th and slope < -th and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
self._prev_slope = slope
def CreateClone(self):
return linear_cross_trading_strategy()