Sma Pullback Atr Exits
取引規則
確定足で終値 SMA 2本と Wilder ATR が形成済みの場合、終値 < 高速 SMA かつ高速 > 低速なら買い、終値 > 高速かつ高速 < 低速なら売ります。等値は入場しません。空の状態から Volume を建て、未完了注文を待ちます。シグナル時に SL = ATR×AtrMultiplierSl、TP = ATR×AtrMultiplierTp を固定します。ネイティブのローカル成行保護が実約定に距離を固定し Bid/Ask を監視します。後の ATR では変更しません。保護が全残高を決済し、反対シグナルによる保有中の反転はありません。
パラメーター
| パラメーター | 初期値 |
|---|---|
CandleType |
TimeSpan.FromMinutes(30).TimeFrame() |
FastSmaLength |
8 |
SlowSmaLength |
30 |
AtrLength |
14 |
AtrMultiplierSl |
1.2 |
AtrMultiplierTp |
2 |
検証
両言語を同梱 BTC/TON、独立式、注文方向・数量、実約定、保有、出口で検証します。日付付き価格・評価・暦データは機構検証で、OHLC・出来高の日付移動は複数年の市場履歴ではありません。2区分、TopN=1、SMA=2 は明示的な小規模テスト設定です。長期窓と元の初期値を別に確認し、収益は保証しません。
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>SMA pullbacks with entry-frozen ATR distances and native quote exits.</summary>
public class SmaPullbackAtrExitsStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastSmaLength;
private readonly StrategyParam<int> _slowSmaLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _atrMultiplierSl;
private readonly StrategyParam<decimal> _atrMultiplierTp;
private Order _pending;
private SimpleMovingAverage _fast;
private SimpleMovingAverage _slow;
private AverageTrueRange _atr;
/// <summary>Finished signal candle type.</summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>Fast close SMA length.</summary>
public int FastSmaLength { get => _fastSmaLength.Value; set => _fastSmaLength.Value = value; }
/// <summary>Slow close SMA length.</summary>
public int SlowSmaLength { get => _slowSmaLength.Value; set => _slowSmaLength.Value = value; }
/// <summary>Wilder true-range window.</summary>
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <summary>Frozen ATR stop-loss multiplier.</summary>
public decimal AtrMultiplierSl { get => _atrMultiplierSl.Value; set => _atrMultiplierSl.Value = value; }
/// <summary>Frozen ATR take-profit multiplier.</summary>
public decimal AtrMultiplierTp { get => _atrMultiplierTp.Value; set => _atrMultiplierTp.Value = value; }
/// <summary>Initialize the strategy parameters.</summary>
public SmaPullbackAtrExitsStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("CandleType", "Finished signal candle type.", "Parameters");
_fastSmaLength = Param(nameof(FastSmaLength), 8).SetRange(1, int.MaxValue)
.SetDisplay("FastSmaLength", "Fast close SMA length.", "Parameters");
_slowSmaLength = Param(nameof(SlowSmaLength), 30).SetRange(2, int.MaxValue)
.SetDisplay("SlowSmaLength", "Slow close SMA length.", "Parameters");
_atrLength = Param(nameof(AtrLength), 14).SetRange(1, int.MaxValue)
.SetDisplay("AtrLength", "Wilder true-range window.", "Parameters");
_atrMultiplierSl = Param(nameof(AtrMultiplierSl), 1.2m).SetRange(0.000001m, decimal.MaxValue)
.SetDisplay("AtrMultiplierSl", "Frozen ATR stop-loss multiplier.", "Parameters");
_atrMultiplierTp = Param(nameof(AtrMultiplierTp), 2m).SetRange(0.000001m, decimal.MaxValue)
.SetDisplay("AtrMultiplierTp", "Frozen ATR take-profit multiplier.", "Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_pending = null;
_fast = null;
_slow = null;
_atr = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
if (FastSmaLength >= SlowSmaLength) throw new InvalidOperationException("FastSmaLength must be smaller than SlowSmaLength.");
base.OnStarted2(time);
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quote = new Subscription(DataType.Level1, Security);
quote.MarketData.BuildField = field;
SubscribeLevel1(quote).Start();
}
_fast = new SimpleMovingAverage { Length = FastSmaLength };
_slow = new SimpleMovingAverage { Length = SlowSmaLength };
_atr = new AverageTrueRange { Length = AtrLength };
SubscribeCandles(CandleType).Bind(_fast, _slow, _atr, ProcessCandle).Start();
}
private bool IsPending => _pending is not null && _pending.State is not (OrderStates.Done or OrderStates.Failed);
private void Submit(Sides side, decimal quantity, string comment)
{
_pending = CreateOrder(side, 0m, quantity);
_pending.Type = OrderTypes.Market;
_pending.Comment = comment;
RegisterOrder(_pending);
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal atr)
{
if (candle.State != CandleStates.Finished || !_fast.IsFormed || !_slow.IsFormed || !_atr.IsFormed
|| atr <= 0m || Position != 0m || IsPending || !IsFormedAndOnlineAndAllowTrading()) return;
Sides? side = candle.ClosePrice < fast && fast > slow ? Sides.Buy
: candle.ClosePrice > fast && fast < slow ? Sides.Sell : null;
if (side is null) return;
StartProtection(new Unit(atr * AtrMultiplierTp, UnitTypes.Absolute), new Unit(atr * AtrMultiplierSl, UnitTypes.Absolute),
useMarketOrders: true, isLocalStop: true);
Submit(side.Value, Volume, "0487Entry");
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.BusinessEntities")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import DateTime, Decimal, Math, TimeSpan, InvalidOperationException
from System.Globalization import CultureInfo, DateTimeStyles
from System.IO import File
from StockSharp.Algo.Indicators import ExponentialMovingAverage, SimpleMovingAverage, AverageTrueRange, AlligatorLine, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from StockSharp.BusinessEntities import Subscription
from StockSharp.Messages import Level1Fields, ITickTradeMessage, CandleStates, DataType, OrderStates, OrderTypes, Sides, Unit, UnitTypes
class sma_pullback_atr_exits_strategy(Strategy):
"""SMA pullbacks with entry-frozen ATR distances and native quote exits."""
def __init__(self):
super(sma_pullback_atr_exits_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("CandleType", "Finished signal candle type.", "Parameters")
self._fast_sma_length = self.Param("FastSmaLength", 8).SetRange(1, 2147483647) \
.SetDisplay("FastSmaLength", "Fast close SMA length.", "Parameters")
self._slow_sma_length = self.Param("SlowSmaLength", 30).SetRange(2, 2147483647) \
.SetDisplay("SlowSmaLength", "Slow close SMA length.", "Parameters")
self._atr_length = self.Param("AtrLength", 14).SetRange(1, 2147483647) \
.SetDisplay("AtrLength", "Wilder true-range window.", "Parameters")
self._atr_multiplier_sl = self.Param("AtrMultiplierSl", Decimal(1.2)).SetRange(Decimal(0.000001), Decimal.MaxValue) \
.SetDisplay("AtrMultiplierSl", "Frozen ATR stop-loss multiplier.", "Parameters")
self._atr_multiplier_tp = self.Param("AtrMultiplierTp", Decimal(2)).SetRange(Decimal(0.000001), Decimal.MaxValue) \
.SetDisplay("AtrMultiplierTp", "Frozen ATR take-profit multiplier.", "Parameters")
self._clear_state()
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastSmaLength(self):
return self._fast_sma_length.Value
@FastSmaLength.setter
def FastSmaLength(self, value):
self._fast_sma_length.Value = value
@property
def SlowSmaLength(self):
return self._slow_sma_length.Value
@SlowSmaLength.setter
def SlowSmaLength(self, value):
self._slow_sma_length.Value = value
@property
def AtrLength(self):
return self._atr_length.Value
@AtrLength.setter
def AtrLength(self, value):
self._atr_length.Value = value
@property
def AtrMultiplierSl(self):
return self._atr_multiplier_sl.Value
@AtrMultiplierSl.setter
def AtrMultiplierSl(self, value):
self._atr_multiplier_sl.Value = value
@property
def AtrMultiplierTp(self):
return self._atr_multiplier_tp.Value
@AtrMultiplierTp.setter
def AtrMultiplierTp(self, value):
self._atr_multiplier_tp.Value = value
def _clear_state(self):
self._pending = None
self._fast = None
self._slow = None
self._atr = None
def OnReseted(self):
super(sma_pullback_atr_exits_strategy, self).OnReseted()
self._clear_state()
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnStarted2(self, time):
if self.FastSmaLength >= self.SlowSmaLength:
raise InvalidOperationException("FastSmaLength must be smaller than SlowSmaLength.")
super(sma_pullback_atr_exits_strategy, self).OnStarted2(time)
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quote = Subscription(DataType.Level1, self.Security)
quote.MarketData.BuildField = field
self.SubscribeLevel1(quote).Start()
self._fast = SimpleMovingAverage()
self._fast.Length = self.FastSmaLength
self._slow = SimpleMovingAverage()
self._slow.Length = self.SlowSmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
self.SubscribeCandles(self.CandleType).Bind(self._fast, self._slow, self._atr, self._process_candle).Start()
def _is_pending(self):
return self._pending is not None and self._pending.State not in (OrderStates.Done, OrderStates.Failed)
def _submit(self, side, quantity, comment):
self._pending = self.CreateOrder(side, Decimal(0), quantity)
self._pending.Type = OrderTypes.Market
self._pending.Comment = comment
self.RegisterOrder(self._pending)
@staticmethod
def _process(indicator, candle):
value = CandleIndicatorValue(indicator, candle)
value.IsFinal = True
result = indicator.Process(value)
return None if result.IsEmpty else result.GetValue[Decimal](None)
def _process_candle(self, candle, fast, slow, atr):
if candle.State != CandleStates.Finished or not self._fast.IsFormed or not self._slow.IsFormed or not self._atr.IsFormed or atr <= 0 or self.Position != 0 or self._is_pending() or not self.IsFormedAndOnlineAndAllowTrading():
return
side = Sides.Buy if candle.ClosePrice < fast and fast > slow else Sides.Sell if candle.ClosePrice > fast and fast < slow else None
if side is None:
return
self.StartProtection(Unit(atr * self.AtrMultiplierTp, UnitTypes.Absolute), Unit(atr * self.AtrMultiplierSl, UnitTypes.Absolute), useMarketOrders=True, isLocalStop=True)
self._submit(side, self.Volume, "0487Entry")
def CreateClone(self):
return sma_pullback_atr_exits_strategy()