VininI Trend LRMA戦略
確定終値の移動線形回帰の終点 L を追跡します。既定値は CandleType = 4時間、Period = 13、UpLevel = 10、DnLevel = -10、Mode = Breakdown。指標形成と売買許可を待ちます。
Breakdown の閾値は相対オシレーター O = 100 × (Close − L) / L の百分率です。previousO <= UpLevel && O > UpLevel で買い、previousO >= DnLevel && O < DnLevel で売ります。LRMA の絶対価格水準ではありません。
Twist は回帰終点の傾きが負方向から正方向になれば買い、逆なら売ります。横ばい区間は直前のゼロでない方向を保持します。
買いは Position <= 0、売りは Position >= 0 で許可します。反対の約定済み建玉を成行で閉じてから、新方向に Volume を建てます。同方向シグナルで積み増しません。損切りや利確はありません。
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>
/// LRMA trend reversal with selectable percentage-deviation threshold crossing
/// or LRMA-slope reversal. Zero protection is intentional and documented.
/// </summary>
public class VininITrendLrmaStrategy : Strategy
{
public enum SignalModes
{
Breakdown,
Twist,
}
private readonly StrategyParam<int> _period;
private readonly StrategyParam<decimal> _upLevel;
private readonly StrategyParam<decimal> _dnLevel;
private readonly StrategyParam<SignalModes> _mode;
private readonly StrategyParam<DataType> _candleType;
private decimal? _previousOscillator;
private decimal? _previousLrma;
private decimal? _previousSlope;
public int Period { get => _period.Value; set => _period.Value = value; }
public decimal UpLevel { get => _upLevel.Value; set => _upLevel.Value = value; }
public decimal DnLevel { get => _dnLevel.Value; set => _dnLevel.Value = value; }
public SignalModes Mode { get => _mode.Value; set => _mode.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public VininITrendLrmaStrategy()
{
_period = Param(nameof(Period), 13).SetGreaterThanZero()
.SetDisplay("LRMA Period", "Linear regression lookback", "Indicators");
_upLevel = Param(nameof(UpLevel), 10m)
.SetDisplay("Upper Level", "Upper percent deviation trigger for Breakdown", "Signals");
_dnLevel = Param(nameof(DnLevel), -10m)
.SetDisplay("Lower Level", "Lower percent deviation trigger for Breakdown", "Signals");
_mode = Param(nameof(Mode), SignalModes.Breakdown)
.SetDisplay("Signal Mode", "Breakdown uses percent deviation; Twist uses LRMA slope reversal", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_previousOscillator = null;
_previousLrma = null;
_previousSlope = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previousOscillator = null;
_previousLrma = null;
_previousSlope = null;
var lrma = new LinearReg { Length = Period };
SubscribeCandles(CandleType).Bind(lrma, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal lrma)
{
if (candle.State != CandleStates.Finished || !IsFormed || lrma == 0m)
return;
var oscillator = (candle.ClosePrice - lrma) / lrma * 100m;
var slope = _previousLrma.HasValue ? lrma - _previousLrma.Value : (decimal?)null;
var buy = false;
var sell = false;
switch (Mode)
{
case SignalModes.Breakdown:
if (_previousOscillator is decimal prev)
{
buy = prev <= UpLevel && oscillator > UpLevel;
sell = prev >= DnLevel && oscillator < DnLevel;
}
break;
case SignalModes.Twist:
if (_previousSlope is decimal old && slope is decimal current)
{
buy = old < 0m && current > 0m;
sell = old > 0m && current < 0m;
}
break;
default:
throw new ArgumentOutOfRangeException(nameof(Mode), Mode, "Unsupported LRMA signal mode.");
}
_previousOscillator = oscillator;
_previousLrma = lrma;
if (slope is < 0m or > 0m)
_previousSlope = slope;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var position = Position;
if (buy && position <= 0m)
{
if (position < 0m)
BuyMarket(-position);
BuyMarket(Volume);
}
else if (sell && position >= 0m)
{
if (position > 0m)
SellMarket(position);
SellMarket(Volume);
}
}
}
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 vinin_i_trend_lrma_strategy(Strategy):
"""LRMA percent-deviation breakdown or LRMA-slope twist, with no stop."""
BREAKDOWN = 0
TWIST = 1
def __init__(self):
super(vinin_i_trend_lrma_strategy, self).__init__()
self._period = self.Param("Period", 13).SetDisplay("LRMA Period", "Linear regression lookback", "Indicators")
self._up_level = self.Param("UpLevel", 10.0).SetDisplay("Upper Level", "Percent deviation trigger", "Signals")
self._dn_level = self.Param("DnLevel", -10.0).SetDisplay("Lower Level", "Percent deviation trigger", "Signals")
self._mode = self.Param("Mode", self.BREAKDOWN).SetDisplay("Signal Mode", "0=Breakdown, 1=Twist", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Type of candles", "General")
self._reset_signals()
@property
def Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
@property
def UpLevel(self):
return self._up_level.Value
@UpLevel.setter
def UpLevel(self, value):
self._up_level.Value = value
@property
def DnLevel(self):
return self._dn_level.Value
@DnLevel.setter
def DnLevel(self, value):
self._dn_level.Value = value
@property
def Mode(self):
return self._mode.Value
@Mode.setter
def Mode(self, value):
self._mode.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _reset_signals(self):
self._previous_oscillator = None
self._previous_lrma = None
self._previous_slope = None
def OnReseted(self):
super(vinin_i_trend_lrma_strategy, self).OnReseted()
self._reset_signals()
def OnStarted2(self, time):
super(vinin_i_trend_lrma_strategy, self).OnStarted2(time)
self._reset_signals()
lrma = LinearReg()
lrma.Length = self.Period
self.SubscribeCandles(self.CandleType).Bind(lrma, self.ProcessCandle).Start()
def ProcessCandle(self, candle, lrma_value):
if candle.State != CandleStates.Finished or not self.IsFormed:
return
lrma = float(lrma_value)
if lrma == 0:
return
oscillator = (float(candle.ClosePrice) - lrma) / lrma * 100.0
slope = lrma - self._previous_lrma if self._previous_lrma is not None else None
buy = sell = False
mode = int(self.Mode)
if mode == self.BREAKDOWN:
if self._previous_oscillator is not None:
buy = self._previous_oscillator <= float(self.UpLevel) < oscillator
sell = self._previous_oscillator >= float(self.DnLevel) > oscillator
elif mode == self.TWIST:
if self._previous_slope is not None and slope is not None:
buy = self._previous_slope < 0 < slope
sell = self._previous_slope > 0 > slope
else:
raise ValueError("Unsupported LRMA signal mode: %s" % mode)
self._previous_oscillator = oscillator
self._previous_lrma = lrma
if slope is not None and slope != 0:
self._previous_slope = slope
if not self.IsFormedAndOnlineAndAllowTrading():
return
position = self.Position
if buy and position <= 0:
if position < 0:
self.BuyMarket(-position)
self.BuyMarket(self.Volume)
elif sell and position >= 0:
if position > 0:
self.SellMarket(position)
self.SellMarket(self.Volume)
def CreateClone(self):
return vinin_i_trend_lrma_strategy()