オープンドライブ戦略
オープンドライブとは、オーバーナイトのニュースを契機に、寄り付きから即座に強い方向性を持った動きが生じることを指します。 トレーダーは最初の数分間で大きな出来高と持続するモメンタムを確認します。
テストでは年平均リターンが約118%であることが示されています。株式市場で最も効果的です。
この戦略はそのモメンタムに乗り、寄り付きレンジ内でロングまたはショートにエントリーし、価格が伸びるにつれてトレーリングストップを引き上げます。
勢いが止まった場合は素早くポジションを閉じ、荒い寄り付きでの損失を最小限に抑えます。
詳細
- エントリー条件: インジケーターシグナル
- ロング/ショート: 両方
- エグジット条件: ストップロスまたは逆シグナル
- ストップ: はい、パーセントベース
- デフォルト値:
CandleType= 15 minuteStopLossPercent= 2VolumePeriod= 20 市場は24時間取引のため、セッションはUTCの1日とします。 その日の最初の足の出来高が直前VolumePeriod本の平均を上回ればオープニング・ドライブとみなし、その終値で同じ方向にエントリーします。ポジションと逆に引けた最初の足で決済し、パーセントストップは価格に追随します。
- フィルター:
- カテゴリ: イントラデイ
- 方向: 両方
- インジケーター: Price Action
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Open Drive strategy.
/// The market trades around the clock, so the session is the UTC day.
/// The first candle of each UTC day is the opening drive when its volume exceeds the average of the previous VolumePeriod candles;
/// the strategy joins its direction at its close. The position closes on the first candle that closes against it, when the drive stalls,
/// and a trailing percent stop follows it as price extends.
/// </summary>
public class OpenDriveStrategy : Strategy
{
private readonly StrategyParam<int> _volumePeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private DateTime? _day;
private readonly List<decimal> _volumes = [];
/// <summary>
/// Previous candles the opening volume is compared with.
/// </summary>
public int VolumePeriod
{
get => _volumePeriod.Value;
set => _volumePeriod.Value = value;
}
/// <summary>
/// Trailing stop loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public OpenDriveStrategy()
{
_volumePeriod = Param(nameof(VolumePeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Volume Period", "Previous candles the opening volume is compared with", "Session");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Trailing stop loss percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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 OnReseted()
{
base.OnReseted();
_day = null;
_volumes.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_day = null;
_volumes.Clear();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), isStopTrailing: true, useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ClosePosition()
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var day = candle.OpenTime.Date;
var firstOfDay = _day != day;
if (firstOfDay)
_day = day;
var average = _volumes.Count == VolumePeriod ? _volumes.Average() : (decimal?)null;
_volumes.Add(candle.TotalVolume);
if (_volumes.Count > VolumePeriod)
_volumes.RemoveAt(0);
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (Position > 0)
{
if (close < candle.OpenPrice)
SellMarket(Position);
}
else if (Position < 0)
{
if (close > candle.OpenPrice)
BuyMarket(-Position);
}
else if (firstOfDay && average is decimal avgVolume && candle.TotalVolume > avgVolume)
{
if (close > candle.OpenPrice)
BuyMarket(Volume);
else if (close < candle.OpenPrice)
SellMarket(Volume);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class open_drive_strategy(Strategy):
"""
Open Drive strategy.
The market trades around the clock, so the session is the UTC day.
The first candle of each UTC day is the opening drive when its volume exceeds the average of the previous VolumePeriod candles;
the strategy joins its direction at its close. The position closes on the first candle that closes against it, when the drive stalls,
and a trailing percent stop follows it as price extends.
"""
def __init__(self):
super(open_drive_strategy, self).__init__()
self._volume_period = self.Param("VolumePeriod", 20).SetGreaterThanZero().SetDisplay("Volume Period", "Previous candles the opening volume is compared with", "Session")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Trailing stop loss percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._day = None
self._volumes = []
def OnReseted(self):
super(open_drive_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(open_drive_strategy, self).OnStarted2(time)
self._reset_state()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), isStopTrailing=True, useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _close_position(self):
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(-self.Position)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
day = candle.OpenTime.Date
first_of_day = self._day is None or self._day != day
if first_of_day:
self._day = day
period = self._volume_period.Value
average = None
if len(self._volumes) == period:
total = Decimal(0)
for volume in self._volumes:
total += volume
average = total / Decimal(period)
self._volumes.append(candle.TotalVolume)
if len(self._volumes) > period:
self._volumes.pop(0)
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
if self.Position > 0:
if close < candle.OpenPrice:
self.SellMarket(self.Position)
elif self.Position < 0:
if close > candle.OpenPrice:
self.BuyMarket(-self.Position)
elif first_of_day and average is not None and candle.TotalVolume > average:
if close > candle.OpenPrice:
self.BuyMarket(self.Volume)
elif close < candle.OpenPrice:
self.SellMarket(self.Volume)
def CreateClone(self):
return open_drive_strategy()