Open Drive Strategy
Open Drive refers to strong directional movement right from the opening bell, often after an overnight news catalyst. Traders look for heavy volume and sustained momentum in the first few minutes.
Testing indicates an average annual return of about 118%. It performs best in the stocks market.
The strategy joins that momentum, entering long or short within the opening range and trailing a stop as price extends.
Positions close quickly if the drive stalls, keeping losses small during choppy opens.
Details
- Entry Criteria: indicator signal
- Long/Short: Both
- Exit Criteria: stop-loss or opposite signal
- Stops: Yes, percent based
- Default Values:
CandleType= 15 minuteStopLoss= 2%
- Filters:
- Category: Intraday
- Direction: Both
- Indicators: Price Action
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Intraday
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
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>
/// Implementation of Open Drive trading strategy.
/// Trades on strong gap openings relative to previous close using ATR filter and MA trend.
/// </summary>
public class OpenDriveStrategy : Strategy
{
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevClosePrice;
private decimal _atrValue;
private int _cooldown;
/// <summary>
/// ATR multiplier for gap size.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Moving average period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Candle type for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="OpenDriveStrategy"/>.
/// </summary>
public OpenDriveStrategy()
{
_atrMultiplier = Param(nameof(AtrMultiplier), 0.3m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Multiplier for ATR to define gap size", "Strategy");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period for ATR calculation", "Strategy");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average period for trend confirmation", "Strategy");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for strategy", "Strategy");
_cooldownBars = Param(nameof(CooldownBars), 30)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClosePrice = 0;
_atrValue = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = MaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(3, UnitTypes.Percent),
stopLoss: new Unit(2, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
// indicators checked via Bind
_atrValue = atrValue;
var close = candle.ClosePrice;
var open = candle.OpenPrice;
if (_cooldown > 0)
{
_cooldown--;
_prevClosePrice = close;
return;
}
// Detect strong momentum candle (body exceeds ATR * multiplier)
if (_prevClosePrice > 0 && atrValue > 0)
{
var body = close - open;
var bodySize = Math.Abs(body);
if (bodySize > atrValue * AtrMultiplier && Position == 0)
{
// Bullish momentum + above MA = Buy
if (body > 0 && close > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Bearish momentum + below MA = Sell short
else if (body < 0 && close < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
}
}
_prevClosePrice = close;
}
}
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 SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class open_drive_strategy(Strategy):
"""
Open Drive trading strategy.
Trades on strong momentum candles (body exceeds ATR * multiplier).
Bullish momentum + above MA = Buy, Bearish momentum + below MA = Sell.
"""
def __init__(self):
super(open_drive_strategy, self).__init__()
self._atr_multiplier = self.Param("AtrMultiplier", 0.3).SetDisplay("ATR Multiplier", "Multiplier for ATR to define gap size", "Strategy")
self._atr_period = self.Param("AtrPeriod", 14).SetDisplay("ATR Period", "Period for ATR calculation", "Strategy")
self._ma_period = self.Param("MaPeriod", 20).SetDisplay("MA Period", "Moving average period for trend confirmation", "Strategy")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles for strategy", "Strategy")
self._cooldown_bars = self.Param("CooldownBars", 30).SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._prev_close_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(open_drive_strategy, self).OnReseted()
self._prev_close_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(open_drive_strategy, self).OnStarted2(time)
self._prev_close_price = 0.0
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val, atr_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
sma = float(sma_val)
atr = float(atr_val)
cd = self._cooldown_bars.Value
mult = float(self._atr_multiplier.Value)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_close_price = close
return
# Detect strong momentum candle (body exceeds ATR * multiplier)
if self._prev_close_price > 0 and atr > 0:
body = close - open_price
body_size = abs(body)
if body_size > atr * mult and self.Position == 0:
# Bullish momentum + above MA = Buy
if body > 0 and close > sma:
self.BuyMarket()
self._cooldown = cd
# Bearish momentum + below MA = Sell short
elif body < 0 and close < sma:
self.SellMarket()
self._cooldown = cd
self._prev_close_price = close
def CreateClone(self):
return open_drive_strategy()