Стратегия MESA Stochastic Multi Length
Стратегия использует четыре осциллятора MESA Stochastic с различными периодами. Лонг открывается, когда все четыре осциллятора находятся выше своих сглаженных триггеров. Шорт открывается, когда все четыре осциллятора ниже триггеров.
Параметры
Length1– период первого осциллятора.Length2– период второго осциллятора.Length3– период третьего осциллятора.Length4– период четвертого осциллятора.TriggerLength– период сглаживания триггеров.CandleType– таймфрейм свечей.
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;
public class MesaStochasticMultiLengthStrategy : Strategy
{
private readonly StrategyParam<int> _length1;
private readonly StrategyParam<int> _length2;
private readonly StrategyParam<decimal> _upperLevel;
private readonly StrategyParam<decimal> _lowerLevel;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _prices = new();
private decimal _prevStoch1;
private decimal _prevStoch2;
private int _barsFromSignal;
public int Length1 { get => _length1.Value; set => _length1.Value = value; }
public int Length2 { get => _length2.Value; set => _length2.Value = value; }
public decimal UpperLevel { get => _upperLevel.Value; set => _upperLevel.Value = value; }
public decimal LowerLevel { get => _lowerLevel.Value; set => _lowerLevel.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MesaStochasticMultiLengthStrategy()
{
_length1 = Param(nameof(Length1), 50)
.SetGreaterThanZero()
.SetDisplay("Length 1", "Primary stochastic length", "General");
_length2 = Param(nameof(Length2), 14)
.SetGreaterThanZero()
.SetDisplay("Length 2", "Secondary stochastic length", "General");
_upperLevel = Param(nameof(UpperLevel), 0.60m)
.SetDisplay("Upper Level", "Upper signal level", "General");
_lowerLevel = Param(nameof(LowerLevel), 0.40m)
.SetDisplay("Lower Level", "Lower signal level", "General");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 10)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candles timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prices.Clear();
_prevStoch1 = 0.5m;
_prevStoch2 = 0.5m;
_barsFromSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_prices.Clear();
_prevStoch1 = 0.5m;
_prevStoch2 = 0.5m;
_barsFromSignal = SignalCooldownBars;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var price = (candle.HighPrice + candle.LowPrice) / 2m;
_prices.Add(price);
var maxLen = Math.Max(Length1, Length2);
if (_prices.Count > maxLen + 10)
_prices.RemoveAt(0);
if (_prices.Count < maxLen)
return;
var stoch1 = CalcStochastic(_prices, Length1);
var stoch2 = CalcStochastic(_prices, Length2);
var up = stoch1 > UpperLevel && stoch2 > UpperLevel && _prevStoch1 <= UpperLevel;
var down = stoch1 < LowerLevel && stoch2 < LowerLevel && _prevStoch1 >= LowerLevel;
_barsFromSignal++;
if (_barsFromSignal >= SignalCooldownBars && up && Position <= 0)
{
BuyMarket();
_barsFromSignal = 0;
}
else if (_barsFromSignal >= SignalCooldownBars && down && Position >= 0)
{
SellMarket();
_barsFromSignal = 0;
}
_prevStoch1 = stoch1;
_prevStoch2 = stoch2;
}
private static decimal CalcStochastic(List<decimal> prices, int length)
{
var count = prices.Count;
if (count < length) return 0.5m;
var high = decimal.MinValue;
var low = decimal.MaxValue;
for (int i = count - length; i < count; i++)
{
if (prices[i] > high) high = prices[i];
if (prices[i] < low) low = prices[i];
}
if (high == low) return 0.5m;
return (prices[count - 1] - low) / (high - low);
}
}
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.Strategies import Strategy
class mesa_stochastic_multi_length_strategy(Strategy):
def __init__(self):
super(mesa_stochastic_multi_length_strategy, self).__init__()
self._length1 = self.Param("Length1", 50) \
.SetGreaterThanZero() \
.SetDisplay("Length 1", "Primary stochastic length", "General")
self._length2 = self.Param("Length2", 14) \
.SetGreaterThanZero() \
.SetDisplay("Length 2", "Secondary stochastic length", "General")
self._upper_level = self.Param("UpperLevel", 0.60) \
.SetDisplay("Upper Level", "Upper signal level", "General")
self._lower_level = self.Param("LowerLevel", 0.40) \
.SetDisplay("Lower Level", "Lower signal level", "General")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 10) \
.SetGreaterThanZero() \
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candles timeframe", "General")
self._prices = []
self._prev_stoch1 = 0.5
self._prev_stoch2 = 0.5
self._bars_from_signal = 0
@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(mesa_stochastic_multi_length_strategy, self).OnReseted()
self._prices = []
self._prev_stoch1 = 0.5
self._prev_stoch2 = 0.5
self._bars_from_signal = 0
def OnStarted2(self, time):
super(mesa_stochastic_multi_length_strategy, self).OnStarted2(time)
self._prices = []
self._prev_stoch1 = 0.5
self._prev_stoch2 = 0.5
self._bars_from_signal = self._signal_cooldown_bars.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.OnProcess).Start()
def _calc_stochastic(self, prices, length):
count = len(prices)
if count < length:
return 0.5
high = -999999999.0
low = 999999999.0
start = count - length
for i in range(start, count):
if prices[i] > high:
high = prices[i]
if prices[i] < low:
low = prices[i]
if high == low:
return 0.5
return (prices[count - 1] - low) / (high - low)
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
price = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
self._prices.append(price)
max_len = max(self._length1.Value, self._length2.Value)
if len(self._prices) > max_len + 10:
self._prices.pop(0)
if len(self._prices) < max_len:
return
stoch1 = self._calc_stochastic(self._prices, self._length1.Value)
stoch2 = self._calc_stochastic(self._prices, self._length2.Value)
ul = float(self._upper_level.Value)
ll = float(self._lower_level.Value)
up = stoch1 > ul and stoch2 > ul and self._prev_stoch1 <= ul
down = stoch1 < ll and stoch2 < ll and self._prev_stoch1 >= ll
self._bars_from_signal += 1
cd = self._signal_cooldown_bars.Value
if self._bars_from_signal >= cd and up and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif self._bars_from_signal >= cd and down and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
self._prev_stoch1 = stoch1
self._prev_stoch2 = stoch2
def CreateClone(self):
return mesa_stochastic_multi_length_strategy()