Back to the Future戦略
このモメンタム戦略は、現在の終値を指定された分数前の価格と比較します。価格が過去の価格に対して定義されたしきい値を超えて上昇した場合、システムはロングポジションをオープンします。逆に、価格がマイナスのしきい値を下回った場合、ショートポジションをオープンします。このアプローチは、過去の価格から大きく離れた強い動きが新興トレンドを示すという前提に基づいています。
この戦略は完成したローソク足で動作し、StockSharpがサポートする任意の銘柄と時間軸に対応しています。組み込みのテイクプロフィットとストップロスのレベルがポジションオープン後のリスクを管理します。過去の価格のキューが価格差を評価するためのローリング履歴を維持します。
詳細
- エントリー条件:
- ロング:
Close(t) - Close(t-Δ) > BarSize. - ショート:
Close(t) - Close(t-Δ) < -BarSize.
- ロング:
- ロング/ショート: 両方。
- エグジット条件:
- ロング:
Close >= Entry + TakeProfitまたはClose <= Entry - StopLoss. - ショート:
Close <= Entry - TakeProfitまたはClose >= Entry + StopLoss.
- ロング:
- ストップ: あり、価格単位での固定テイクプロフィットとストップロス。
- デフォルト値:
BarSize = 0.25HistoryMinutes = 60TakeProfit = 10StopLoss = 5000
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: 両方
- インジケーター: なし
- ストップ: あり
- 複雑さ: シンプル
- 時間軸: 短期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Compares current price with the price from a past moment and trades on large deviations.
/// </summary>
public class BackToTheFutureStrategy : Strategy
{
private readonly StrategyParam<decimal> _barSize;
private readonly StrategyParam<int> _historyMinutes;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<(DateTimeOffset time, decimal price)> _history = new();
private decimal _entryPrice;
private int _barsSinceTrade;
/// <summary>
/// Price difference threshold.
/// </summary>
public decimal BarSize
{
get => _barSize.Value;
set => _barSize.Value = value;
}
/// <summary>
/// Minutes to look back for comparison.
/// </summary>
public int HistoryMinutes
{
get => _historyMinutes.Value;
set => _historyMinutes.Value = value;
}
/// <summary>
/// Take profit distance in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Stop loss distance in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Bars to wait after a completed trade.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Type of candles to process.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="BackToTheFutureStrategy"/>.
/// </summary>
public BackToTheFutureStrategy()
{
_barSize = Param(nameof(BarSize), 1500m)
.SetGreaterThanZero()
.SetDisplay("Price Difference", "Threshold to trigger trades", "General")
;
_historyMinutes = Param(nameof(HistoryMinutes), 240)
.SetGreaterThanZero()
.SetDisplay("History Minutes", "Minutes back for price comparison", "General")
;
_takeProfit = Param(nameof(TakeProfit), 1500m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Distance from entry", "Risk")
;
_stopLoss = Param(nameof(StopLoss), 2000m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Distance from entry", "Risk")
;
_cooldownBars = Param(nameof(CooldownBars), 2)
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_history.Clear();
_entryPrice = 0m;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_history.Enqueue((candle.CloseTime, candle.ClosePrice));
var minTime = candle.CloseTime - TimeSpan.FromMinutes(HistoryMinutes);
while (_history.Count > 0 && _history.Peek().time < minTime)
_history.Dequeue();
if (_history.Count == 0)
return;
if (_barsSinceTrade < CooldownBars)
_barsSinceTrade++;
var oldest = _history.Peek().price;
var diff = candle.ClosePrice - oldest;
if (Position > 0)
{
if (candle.ClosePrice >= _entryPrice + TakeProfit || candle.ClosePrice <= _entryPrice - StopLoss)
{
SellMarket(Position);
_barsSinceTrade = 0;
}
}
else if (Position < 0)
{
if (candle.ClosePrice <= _entryPrice - TakeProfit || candle.ClosePrice >= _entryPrice + StopLoss)
{
BuyMarket(-Position);
_barsSinceTrade = 0;
}
}
else if (_barsSinceTrade >= CooldownBars)
{
if (diff > BarSize)
{
var volume = Volume + (Position < 0 ? -Position : 0m);
BuyMarket(volume);
_entryPrice = candle.ClosePrice;
_barsSinceTrade = 0;
}
else if (diff < -BarSize)
{
var volume = Volume + (Position > 0 ? Position : 0m);
SellMarket(volume);
_entryPrice = candle.ClosePrice;
_barsSinceTrade = 0;
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from System.Collections.Generic import Queue
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class back_to_the_future_strategy(Strategy):
def __init__(self):
super(back_to_the_future_strategy, self).__init__()
self._bar_size = self.Param("BarSize", 1500.0) \
.SetDisplay("Price Difference", "Threshold to trigger trades", "General")
self._history_minutes = self.Param("HistoryMinutes", 240) \
.SetDisplay("History Minutes", "Minutes back for price comparison", "General")
self._take_profit = self.Param("TakeProfit", 1500.0) \
.SetDisplay("Take Profit", "Distance from entry", "Risk")
self._stop_loss = self.Param("StopLoss", 2000.0) \
.SetDisplay("Stop Loss", "Distance from entry", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 2) \
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._history = []
self._entry_price = 0.0
self._bars_since_trade = 0
@property
def BarSize(self):
return self._bar_size.Value
@BarSize.setter
def BarSize(self, value):
self._bar_size.Value = value
@property
def HistoryMinutes(self):
return self._history_minutes.Value
@HistoryMinutes.setter
def HistoryMinutes(self, value):
self._history_minutes.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(back_to_the_future_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
close_price = float(candle.ClosePrice)
close_time = candle.CloseTime
self._history.append((close_time, close_price))
min_time = close_time - TimeSpan.FromMinutes(self.HistoryMinutes)
while len(self._history) > 0 and self._history[0][0] < min_time:
self._history.pop(0)
if len(self._history) == 0:
return
if self._bars_since_trade < self.CooldownBars:
self._bars_since_trade += 1
oldest = self._history[0][1]
diff = close_price - oldest
pos = self.Position
tp = float(self.TakeProfit)
sl = float(self.StopLoss)
bar_size = float(self.BarSize)
if pos > 0:
if close_price >= self._entry_price + tp or close_price <= self._entry_price - sl:
self.SellMarket(pos)
self._bars_since_trade = 0
elif pos < 0:
if close_price <= self._entry_price - tp or close_price >= self._entry_price + sl:
self.BuyMarket(-pos)
self._bars_since_trade = 0
elif self._bars_since_trade >= self.CooldownBars:
if diff > bar_size:
vol = float(self.Volume) + (float(-pos) if pos < 0 else 0.0)
self.BuyMarket(vol)
self._entry_price = close_price
self._bars_since_trade = 0
elif diff < -bar_size:
vol = float(self.Volume) + (float(pos) if pos > 0 else 0.0)
self.SellMarket(vol)
self._entry_price = close_price
self._bars_since_trade = 0
def OnReseted(self):
super(back_to_the_future_strategy, self).OnReseted()
self._history = []
self._entry_price = 0.0
self._bars_since_trade = self.CooldownBars
def CreateClone(self):
return back_to_the_future_strategy()