Color Zerolag Momentum OSMA 戦略
この戦略は5つのMomentum計算を使用してカスタムのゼロラグMomentum OSMAオシレーターを構築します。 2本前のオシレーター値が3本前の値より低い場合、トレンドは上昇と見なされます。 この場合、ショートポジションがクローズされ、直近の値が2本前の値を上回っていれば新しいロングポジションが開かれることがあります。 2本前の値が3本前の値より高い場合、トレンドは下降であり、ロングポジションがクローズされ、最後の値が2本前の値を下回っていればショートが開かれることがあります。
パラメーター
Smoothing1– 低速トレンドの最初の平滑化係数。Smoothing2– OSMA ラインの2番目の平滑化係数。Factor1-5– 各Momentumコンポーネントに適用される重み。MomentumPeriod1-5– Momentumインジケーターの期間。CandleType– 計算用のローソク足の時間軸。BuyOpen– ロングポジションの開設を許可。SellOpen– ショートポジションの開設を許可。BuyClose– ロングポジションのクローズを許可。SellClose– ショートポジションのクローズを許可。
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>Five weighted Momentum indicators, two OSMA smoothing stages and the documented t-2/t-3 signal.</summary>
public class ColorZerolagMomentumOsmaStrategy : Strategy
{
private readonly StrategyParam<int>[] _periods = new StrategyParam<int>[5];
private readonly StrategyParam<decimal>[] _factors = new StrategyParam<decimal>[5];
private readonly StrategyParam<int> _smoothing1, _smoothing2;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _buyOpen, _sellOpen, _buyClose, _sellClose;
private readonly Queue<decimal> _closes = new();
private decimal? _slow, _smoothed, _last, _prev2, _prev3;
private Order _pending;
public int MomentumPeriod1 { get => _periods[0].Value; set => _periods[0].Value = value; }
public int MomentumPeriod2 { get => _periods[1].Value; set => _periods[1].Value = value; }
public int MomentumPeriod3 { get => _periods[2].Value; set => _periods[2].Value = value; }
public int MomentumPeriod4 { get => _periods[3].Value; set => _periods[3].Value = value; }
public int MomentumPeriod5 { get => _periods[4].Value; set => _periods[4].Value = value; }
public decimal Factor1 { get => _factors[0].Value; set => _factors[0].Value = value; }
public decimal Factor2 { get => _factors[1].Value; set => _factors[1].Value = value; }
public decimal Factor3 { get => _factors[2].Value; set => _factors[2].Value = value; }
public decimal Factor4 { get => _factors[3].Value; set => _factors[3].Value = value; }
public decimal Factor5 { get => _factors[4].Value; set => _factors[4].Value = value; }
public int Smoothing1 { get => _smoothing1.Value; set => _smoothing1.Value = value; }
public int Smoothing2 { get => _smoothing2.Value; set => _smoothing2.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public bool BuyOpen { get => _buyOpen.Value; set => _buyOpen.Value = value; }
public bool SellOpen { get => _sellOpen.Value; set => _sellOpen.Value = value; }
public bool BuyClose { get => _buyClose.Value; set => _buyClose.Value = value; }
public bool SellClose { get => _sellClose.Value; set => _sellClose.Value = value; }
public ColorZerolagMomentumOsmaStrategy()
{
var periods = new[] { 5, 8, 13, 21, 34 };
for (var i = 0; i < 5; i++)
{
_periods[i] = Param("MomentumPeriod" + (i + 1), periods[i]).SetGreaterThanZero()
.SetDisplay("Momentum period " + (i + 1), "Price difference lookback", "Indicator");
_factors[i] = Param("Factor" + (i + 1), 1m)
.SetDisplay("Factor " + (i + 1), "Component weight", "Indicator");
}
_smoothing1 = Param(nameof(Smoothing1), 9).SetGreaterThanZero()
.SetDisplay("Smoothing 1", "Slow trend EMA", "Indicator");
_smoothing2 = Param(nameof(Smoothing2), 5).SetGreaterThanZero()
.SetDisplay("Smoothing 2", "OSMA EMA", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Signal candles", "General");
_buyOpen = Param(nameof(BuyOpen), true).SetDisplay("Buy Open", "Enable long entries", "Trading");
_sellOpen = Param(nameof(SellOpen), true).SetDisplay("Sell Open", "Enable short entries", "Trading");
_buyClose = Param(nameof(BuyClose), true).SetDisplay("Buy Close", "Enable long exits", "Trading");
_sellClose = Param(nameof(SellClose), true).SetDisplay("Sell Close", "Enable short exits", "Trading");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
private void ResetHistory()
{
_closes.Clear();
_slow = _smoothed = _last = _prev2 = _prev3 = null;
_pending = null;
}
protected override void OnReseted()
{
base.OnReseted();
ResetHistory();
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetHistory();
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var max = _periods.Max(p => p.Value);
_closes.Enqueue(candle.ClosePrice);
if (_closes.Count > max + 1)
_closes.Dequeue();
if (_closes.Count < max + 1)
return;
var closes = _closes.ToArray();
var fast = 0m;
for (var i = 0; i < 5; i++)
{
// Matches StockSharp Momentum: close[t] - close[t - period].
var period = _periods[i].Value;
fast += (candle.ClosePrice - closes[closes.Length - period - 1]) * _factors[i].Value;
}
var slow = _slow is decimal oldSlow
? oldSlow + 2m / (Smoothing1 + 1m) * (fast - oldSlow)
: fast;
var rawOsma = fast - slow;
var osma = _smoothed is decimal oldOsma
? oldOsma + 2m / (Smoothing2 + 1m) * (rawOsma - oldOsma)
: rawOsma;
// The README refers to t-2 against t-3, then t against t-2.
var buy = _prev3 is decimal third && _prev2 is decimal second
&& second < third && osma > second;
var sell = _prev3 is decimal thirdDown && _prev2 is decimal secondDown
&& secondDown > thirdDown && osma < secondDown;
_prev3 = _prev2;
_prev2 = _last;
_last = _smoothed = osma;
_slow = slow;
if (!IsFormedAndOnlineAndAllowTrading()
|| (_pending is not null && _pending.State is not (OrderStates.Done or OrderStates.Failed)))
return;
if (buy)
{
if (Position < 0m && SellClose)
_pending = BuyMarket(-Position + (BuyOpen ? Volume : 0m));
else if (Position == 0m && BuyOpen)
_pending = BuyMarket(Volume);
}
else if (sell)
{
if (Position > 0m && BuyClose)
_pending = SellMarket(Position + (SellOpen ? Volume : 0m));
else if (Position == 0m && SellOpen)
_pending = SellMarket(Volume);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, OrderStates
from StockSharp.Algo.Strategies import Strategy
class color_zerolag_momentum_osma_strategy(Strategy):
"""Weighted five momentum calculations with the documented t-2/t-3 OSMA turning signal."""
def __init__(self):
super(color_zerolag_momentum_osma_strategy, self).__init__()
self._periods = []
self._factors = []
for index, period in enumerate((5, 8, 13, 21, 34), start=1):
self._periods.append(self.Param("MomentumPeriod" + str(index), period).SetGreaterThanZero()
.SetDisplay("Momentum Period " + str(index), "Price difference lookback", "Indicator"))
self._factors.append(self.Param("Factor" + str(index), 1.0)
.SetDisplay("Factor " + str(index), "Component weight", "Indicator"))
self._smoothing1 = self.Param("Smoothing1", 9).SetGreaterThanZero() \
.SetDisplay("Smoothing 1", "Slow trend EMA", "Indicator")
self._smoothing2 = self.Param("Smoothing2", 5).SetGreaterThanZero() \
.SetDisplay("Smoothing 2", "OSMA EMA", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Signal candles", "General")
self._buy_open = self.Param("BuyOpen", True).SetDisplay("Buy Open", "Enable long entries", "Trading")
self._sell_open = self.Param("SellOpen", True).SetDisplay("Sell Open", "Enable short entries", "Trading")
self._buy_close = self.Param("BuyClose", True).SetDisplay("Buy Close", "Enable long exits", "Trading")
self._sell_close = self.Param("SellClose", True).SetDisplay("Sell Close", "Enable short exits", "Trading")
self._reset_history()
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _reset_history(self):
self._closes = []
self._slow = self._smoothed = self._last = self._prev2 = self._prev3 = None
self._pending = None
def OnReseted(self):
super(color_zerolag_momentum_osma_strategy, self).OnReseted()
self._reset_history()
def OnStarted2(self, time):
super(color_zerolag_momentum_osma_strategy, self).OnStarted2(time)
self._reset_history()
self.SubscribeCandles(self.CandleType).Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
max_period = max(int(p.Value) for p in self._periods)
close = float(candle.ClosePrice)
self._closes.append(close)
if len(self._closes) > max_period + 1:
self._closes.pop(0)
if len(self._closes) < max_period + 1:
return
fast = 0.0
for param, factor in zip(self._periods, self._factors):
period = int(param.Value)
fast += (close - self._closes[-period-1]) * float(factor.Value)
slow = fast if self._slow is None else self._slow + 2.0 / (int(self._smoothing1.Value) + 1.0) * (fast - self._slow)
raw_osma = fast - slow
osma = raw_osma if self._smoothed is None else self._smoothed + 2.0 / (int(self._smoothing2.Value) + 1.0) * (raw_osma - self._smoothed)
buy = self._prev3 is not None and self._prev2 < self._prev3 and osma > self._prev2
sell = self._prev3 is not None and self._prev2 > self._prev3 and osma < self._prev2
self._prev3, self._prev2, self._last = self._prev2, self._last, osma
self._smoothed, self._slow = osma, slow
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._pending is not None and self._pending.State not in (OrderStates.Done, OrderStates.Failed):
return
if buy:
if self.Position < 0 and self._sell_close.Value:
self._pending = self.BuyMarket(-self.Position + (self.Volume if self._buy_open.Value else 0))
elif self.Position == 0 and self._buy_open.Value:
self._pending = self.BuyMarket(self.Volume)
elif sell:
if self.Position > 0 and self._buy_close.Value:
self._pending = self.SellMarket(self.Position + (self.Volume if self._sell_open.Value else 0))
elif self.Position == 0 and self._sell_open.Value:
self._pending = self.SellMarket(self.Volume)
def CreateClone(self):
return color_zerolag_momentum_osma_strategy()