異常逆張り戦略
このアルゴリズムは短い期間内の急激なパーセンテージ変動を検出し、それに逆らって取引します。価格が閾値を上回ると売り、価格が閾値を下回ると買います。ストップロスとテイクプロフィットはティックで設定されます。
詳細
- エントリー条件: ルックバック期間内のパーセンテージ変化が閾値を超える。
- ロング/ショート: 両方向。
- エグジット条件: ストップロスまたはテイクプロフィット。
- ストップ: はい。
- デフォルト値:
PercentageThreshold= 1LookbackMinutes= 30StopLossTicks= 100TakeProfitTicks= 200CandleType= TimeSpan.FromMinutes(1)
- フィルター:
- カテゴリ: 逆張り
- 方向: 両方
- インジケーター: 価格
- ストップ: はい
- 複雑さ: 基本
- 時間軸: イントラデイ (1m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Anomaly counter-trend strategy.
/// Measures the percentage change of the close over the last LookbackMinutes. A rise of at least PercentageThreshold sells and
/// a drop of at least PercentageThreshold buys, reversing an opposite position. Positions are closed by a stop-loss and a
/// take-profit set in ticks.
/// </summary>
public class AnomalyCounterTrendStrategy : Strategy
{
private readonly StrategyParam<decimal> _percentageThreshold;
private readonly StrategyParam<int> _lookbackMinutes;
private readonly StrategyParam<int> _stopLossTicks;
private readonly StrategyParam<int> _takeProfitTicks;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Percentage move that counts as an anomaly.
/// </summary>
public decimal PercentageThreshold
{
get => _percentageThreshold.Value;
set => _percentageThreshold.Value = value;
}
/// <summary>
/// Window of the percentage change in minutes.
/// </summary>
public int LookbackMinutes
{
get => _lookbackMinutes.Value;
set => _lookbackMinutes.Value = value;
}
/// <summary>
/// Stop-loss distance in ticks.
/// </summary>
public int StopLossTicks
{
get => _stopLossTicks.Value;
set => _stopLossTicks.Value = value;
}
/// <summary>
/// Take-profit distance in ticks.
/// </summary>
public int TakeProfitTicks
{
get => _takeProfitTicks.Value;
set => _takeProfitTicks.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AnomalyCounterTrendStrategy()
{
_percentageThreshold = Param(nameof(PercentageThreshold), 1m)
.SetGreaterThanZero()
.SetDisplay("Percentage Threshold", "Percentage move that counts as an anomaly", "Anomaly Detection");
_lookbackMinutes = Param(nameof(LookbackMinutes), 30)
.SetGreaterThanZero()
.SetDisplay("Lookback Minutes", "Window of the percentage change in minutes", "Anomaly Detection");
_stopLossTicks = Param(nameof(StopLossTicks), 100)
.SetNotNegative()
.SetDisplay("Stop Loss Ticks", "Stop-loss distance in ticks", "Risk");
_takeProfitTicks = Param(nameof(TakeProfitTicks), 200)
.SetNotNegative()
.SetDisplay("Take Profit Ticks", "Take-profit distance in ticks", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var frameMinutes = CandleType.Arg is TimeSpan tf && tf > TimeSpan.Zero ? tf.TotalMinutes : 1d;
var lookbackBars = Math.Max(1, (int)Math.Round(LookbackMinutes / frameMinutes));
var roc = new RateOfChange { Length = lookbackBars };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(roc, ProcessCandle)
.Start();
var step = Security?.PriceStep ?? 1m;
StartProtection(
TakeProfitTicks > 0 ? new Unit(TakeProfitTicks * step, UnitTypes.Absolute) : new Unit(),
StopLossTicks > 0 ? new Unit(StopLossTicks * step, UnitTypes.Absolute) : new Unit(),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, roc);
}
}
private void ProcessCandle(ICandleMessage candle, decimal change)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (change >= PercentageThreshold && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (change <= -PercentageThreshold && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import RateOfChange
from StockSharp.Algo.Strategies import Strategy
class anomaly_counter_trend_strategy(Strategy):
"""
Anomaly counter-trend strategy.
Measures the percentage change of the close over the last LookbackMinutes. A rise of at least PercentageThreshold sells and
a drop of at least PercentageThreshold buys, reversing an opposite position. Positions are closed by a stop-loss and a
take-profit set in ticks.
"""
def __init__(self):
super(anomaly_counter_trend_strategy, self).__init__()
self._percentage_threshold = self.Param("PercentageThreshold", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("Percentage Threshold", "Percentage move that counts as an anomaly", "Anomaly Detection")
self._lookback_minutes = self.Param("LookbackMinutes", 30) \
.SetGreaterThanZero() \
.SetDisplay("Lookback Minutes", "Window of the percentage change in minutes", "Anomaly Detection")
self._stop_loss_ticks = self.Param("StopLossTicks", 100) \
.SetNotNegative() \
.SetDisplay("Stop Loss Ticks", "Stop-loss distance in ticks", "Risk")
self._take_profit_ticks = self.Param("TakeProfitTicks", 200) \
.SetNotNegative() \
.SetDisplay("Take Profit Ticks", "Take-profit distance in ticks", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnStarted2(self, time):
super(anomaly_counter_trend_strategy, self).OnStarted2(time)
frame = self.CandleType.Arg
frame_minutes = frame.TotalMinutes if isinstance(frame, TimeSpan) and frame > TimeSpan.Zero else 1.0
lookback_bars = max(1, int(round(self._lookback_minutes.Value / frame_minutes)))
roc = RateOfChange()
roc.Length = lookback_bars
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(roc, self._process_candle).Start()
step = self.Security.PriceStep if self.Security is not None and self.Security.PriceStep is not None else Decimal(1)
take_ticks = self._take_profit_ticks.Value
stop_ticks = self._stop_loss_ticks.Value
self.StartProtection(
Unit(step * take_ticks, UnitTypes.Absolute) if take_ticks > 0 else Unit(),
Unit(step * stop_ticks, UnitTypes.Absolute) if stop_ticks > 0 else Unit(),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, roc)
def _process_candle(self, candle, change_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
change = float(change_value)
threshold = float(self._percentage_threshold.Value)
if change >= threshold and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif change <= -threshold and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return anomaly_counter_trend_strategy()