Simple News Strategy
This strategy places pending stop orders around a specified news time to capture sharp moves caused by news releases.
How it works
- Starting five minutes before
NewsTime, the strategy submits pairs of buy stop and sell stop orders. - The first pair is placed
Distancepips away from the current ask and bid prices. - Additional pairs are offset by
Deltapips from the previous ones for a total ofDealspairs. - Ten minutes after the news release the strategy cancels any orders that have not been triggered.
- When a position is opened, the strategy monitors stop-loss, take-profit and trailing stop levels. If any level is reached the position is closed.
Parameters
NewsTime– moment of the news release.Deals– number of buy/sell stop pairs.Delta– spacing between orders in pips.Distance– distance from current price for the first pair in pips.StopLoss– initial stop-loss in pips.Trail– trailing stop in pips.TakeProfit– take-profit in pips.Volume– order volume.
Notes
The strategy does not rely on indicators and works purely with level1 data. It is intended for demonstration purposes and may require adjustments for real trading.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// News-style volatility breakout strategy.
/// Monitors ATR for volatility expansion and trades breakouts
/// when price moves beyond recent range.
/// </summary>
public class SimpleNewsStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private decimal _prevAtr;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _entryPrice;
private bool _hasPrev;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
public SimpleNewsStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "ATR period for volatility", "Parameters");
_atrMultiplier = Param(nameof(AtrMultiplier), 1.0m)
.SetDisplay("ATR Multiplier", "Multiplier for breakout distance", "Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevAtr = 0;
_prevHigh = 0;
_prevLow = 0;
_entryPrice = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevAtr = 0;
_prevHigh = 0;
_prevLow = 0;
_entryPrice = 0;
_hasPrev = false;
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue atrInd)
{
if (candle.State != CandleStates.Finished)
return;
if (!atrInd.IsFormed)
return;
var atrValue = atrInd.ToDecimal();
var price = candle.ClosePrice;
// Exit logic
if (Position > 0 && _entryPrice > 0)
{
if (price <= _entryPrice - atrValue * 2m || price >= _entryPrice + atrValue * 3m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (price >= _entryPrice + atrValue * 2m || price <= _entryPrice - atrValue * 3m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (!_hasPrev)
{
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevAtr = atrValue;
_hasPrev = true;
return;
}
// Entry: breakout above previous high or below previous low
if (Position == 0)
{
var breakoutDist = atrValue * AtrMultiplier;
if (price > _prevHigh + breakoutDist)
{
BuyMarket();
_entryPrice = price;
}
else if (price < _prevLow - breakoutDist)
{
SellMarket();
_entryPrice = price;
}
}
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevAtr = atrValue;
}
}
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 AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class simple_news_strategy(Strategy):
def __init__(self):
super(simple_news_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period for volatility", "Parameters")
self._atr_multiplier = self.Param("AtrMultiplier", 1.0) \
.SetDisplay("ATR Multiplier", "Multiplier for breakout distance", "Parameters")
self._prev_atr = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._has_prev = False
@property
def candle_type(self):
return self._candle_type.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def atr_multiplier(self):
return self._atr_multiplier.Value
def OnReseted(self):
super(simple_news_strategy, self).OnReseted()
self._prev_atr = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(simple_news_strategy, self).OnStarted2(time)
self._prev_atr = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._has_prev = False
atr = AverageTrueRange()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(atr, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def process_candle(self, candle, atr_ind):
if candle.State != CandleStates.Finished:
return
if not atr_ind.IsFormed:
return
atr_value = float(atr_ind)
price = float(candle.ClosePrice)
# Exit logic
if self.Position > 0 and self._entry_price > 0:
if price <= self._entry_price - atr_value * 2.0 or price >= self._entry_price + atr_value * 3.0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0 and self._entry_price > 0:
if price >= self._entry_price + atr_value * 2.0 or price <= self._entry_price - atr_value * 3.0:
self.BuyMarket()
self._entry_price = 0.0
if not self._has_prev:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_atr = atr_value
self._has_prev = True
return
# Entry: breakout above previous high or below previous low
if self.Position == 0:
breakout_dist = atr_value * float(self.atr_multiplier)
if price > self._prev_high + breakout_dist:
self.BuyMarket()
self._entry_price = price
elif price < self._prev_low - breakout_dist:
self.SellMarket()
self._entry_price = price
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_atr = atr_value
def CreateClone(self):
return simple_news_strategy()