Linear On MACD
Стратегия объединяет сигналы MACD по цене и объёму с линейной регрессией.
Детали
- Условия входа: длинная при одновременном бычьем MACD по цене и объёму и когда регрессионная цена между open и close; короткая при обратных условиях.
- Лонг/Шорт: оба направления.
- Условия выхода: обратный сигнал.
- Стопы: нет.
- Значения по умолчанию:
FastLength= 12SlowLength= 26SignalLength= 9Lookback= 21RiskHigh= falseCandleType= TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Тренд
- Направление: оба
- Индикаторы: MACD, Линейная регрессия
- Стопы: нет
- Сложность: средняя
- Таймфрейм: внутридневной
- Сезонность: нет
- Нейросети: нет
- Дивергенция: нет
- Уровень риска: переменный
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 combining MACD on price and volume based data with linear regression.
/// </summary>
public class LinearOnMacdStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<DataType> _candleType;
private OnBalanceVolume _obv = null!;
private MovingAverageConvergenceDivergenceSignal _obvMacd = null!;
private MovingAverageConvergenceDivergenceSignal _priceMacd = null!;
private LinearRegression _priceReg = null!;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public int SignalLength { get => _signalLength.Value; set => _signalLength.Value = value; }
public int Lookback { get => _lookback.Value; set => _lookback.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public LinearOnMacdStrategy()
{
_fastLength = Param(nameof(FastLength), 70);
_slowLength = Param(nameof(SlowLength), 200);
_signalLength = Param(nameof(SignalLength), 50);
_lookback = Param(nameof(Lookback), 140);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame());
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_obv = new OnBalanceVolume();
_obvMacd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastLength },
LongMa = { Length = SlowLength },
},
SignalMa = { Length = SignalLength }
};
_priceMacd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastLength },
LongMa = { Length = SlowLength },
},
SignalMa = { Length = SignalLength }
};
_priceReg = new LinearRegression { Length = Lookback };
var dummyEma = new ExponentialMovingAverage { Length = 10 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_obv, dummyEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private static DecimalIndicatorValue CreateFinalValue(IIndicator ind, decimal value, DateTime time)
{
var v = new DecimalIndicatorValue(ind, value, time);
v.IsFinal = true;
return v;
}
private void ProcessCandle(ICandleMessage candle, decimal obvValue, decimal dummyValue)
{
if (candle.State != CandleStates.Finished)
return;
var obvMacdResult = _obvMacd.Process(CreateFinalValue(_obvMacd, obvValue, candle.ServerTime));
var priceMacdResult = _priceMacd.Process(CreateFinalValue(_priceMacd, candle.ClosePrice, candle.ServerTime));
var regResult = _priceReg.Process(CreateFinalValue(_priceReg, candle.ClosePrice, candle.ServerTime));
if (!_obvMacd.IsFormed || !_priceMacd.IsFormed || !_priceReg.IsFormed)
return;
if (obvMacdResult is not IMovingAverageConvergenceDivergenceSignalValue obvMacdTyped)
return;
if (priceMacdResult is not IMovingAverageConvergenceDivergenceSignalValue priceMacdTyped)
return;
if (regResult is not ILinearRegressionValue regTyped)
return;
if (obvMacdTyped.Macd is not decimal obvMacd ||
obvMacdTyped.Signal is not decimal obvSignal ||
priceMacdTyped.Macd is not decimal priceMacd ||
priceMacdTyped.Signal is not decimal priceSignal ||
regTyped.LinearReg is not decimal predicted)
return;
var longCondition = priceMacd > priceSignal && obvMacd > obvSignal && candle.ClosePrice > predicted;
var shortCondition = obvMacd < obvSignal && priceMacd < priceSignal && candle.ClosePrice < predicted;
if (longCondition && Position <= 0)
BuyMarket();
else if (shortCondition && Position >= 0)
SellMarket();
}
}
import clr
import math
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 (
OnBalanceVolume,
MovingAverageConvergenceDivergenceSignal,
LinearRegression,
ExponentialMovingAverage,
)
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class linear_on_macd_strategy(Strategy):
def __init__(self):
super(linear_on_macd_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 70) \
.SetDisplay("Fast Length", "MACD fast period", "General")
self._slow_length = self.Param("SlowLength", 200) \
.SetDisplay("Slow Length", "MACD slow period", "General")
self._signal_length = self.Param("SignalLength", 50) \
.SetDisplay("Signal Length", "MACD signal period", "General")
self._lookback = self.Param("Lookback", 140) \
.SetDisplay("Lookback", "Linear regression lookback", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(linear_on_macd_strategy, self).OnStarted2(time)
self._obv = OnBalanceVolume()
self._obv_macd = MovingAverageConvergenceDivergenceSignal()
self._obv_macd.Macd.ShortMa.Length = self._fast_length.Value
self._obv_macd.Macd.LongMa.Length = self._slow_length.Value
self._obv_macd.SignalMa.Length = self._signal_length.Value
self._price_macd = MovingAverageConvergenceDivergenceSignal()
self._price_macd.Macd.ShortMa.Length = self._fast_length.Value
self._price_macd.Macd.LongMa.Length = self._slow_length.Value
self._price_macd.SignalMa.Length = self._signal_length.Value
self._price_reg = LinearRegression()
self._price_reg.Length = self._lookback.Value
dummy_ema = ExponentialMovingAverage()
dummy_ema.Length = 10
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._obv, dummy_ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def OnProcess(self, candle, obv_value, dummy_value):
if candle.State != CandleStates.Finished:
return
obv_macd_result = process_float(self._obv_macd, obv_value, candle.ServerTime, True)
price_macd_result = process_float(self._price_macd, float(candle.ClosePrice), candle.ServerTime, True)
reg_result = process_float(self._price_reg, float(candle.ClosePrice), candle.ServerTime, True)
if not self._obv_macd.IsFormed or not self._price_macd.IsFormed or not self._price_reg.IsFormed:
return
obv_macd_val = obv_macd_result.Macd
obv_signal_val = obv_macd_result.Signal
price_macd_val = price_macd_result.Macd
price_signal_val = price_macd_result.Signal
reg_lr = reg_result.LinearReg
if obv_macd_val is None or obv_signal_val is None:
return
if price_macd_val is None or price_signal_val is None:
return
if reg_lr is None:
return
obv_m = float(obv_macd_val)
obv_s = float(obv_signal_val)
price_m = float(price_macd_val)
price_s = float(price_signal_val)
predicted = float(reg_lr)
close = float(candle.ClosePrice)
long_cond = price_m > price_s and obv_m > obv_s and close > predicted
short_cond = obv_m < obv_s and price_m < price_s and close < predicted
if long_cond and self.Position <= 0:
self.BuyMarket()
elif short_cond and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return linear_on_macd_strategy()