MESA Stochastic 多周期策略
该策略使用四个不同周期的 MESA Stochastic 振荡器。当所有振荡器都位于其移动平均触发线之上时开多;当所有振荡器都位于触发线之下时开空。
参数
Length1– 第一个振荡器周期。Length2– 第二个振荡器周期。Length3– 第三个振荡器周期。Length4– 第四个振荡器周期。TriggerLength– 触发线平滑周期。CandleType– K 线时间框架。
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()