Esta estratégia coloca um par de ordens stop pendentes ao redor do preço atual e as gerencia conforme o mercado evolui. É destinada à negociação durante divulgações de notícias onde movimentos bruscos são esperados.
Como funciona
Quando sem posição, a estratégia coloca:
Uma ordem de buy stop em Ask + Step.
Uma ordem de sell stop em Bid - Step.
Ordens pendentes são reprecificadas a cada TimeModify segundos se o mercado se mover pelo menos StepTrail.
Quando uma ordem é executada, a ordem pendente oposta é cancelada.
Um stop loss protetor e um take profit opcional são criados com base no preço de entrada.
O stop loss pode ser movido para o ponto de equilíbrio após um lucro definido e depois seguir o preço à medida que avança.
A estratégia opera com dados de Nível1 e não depende de nenhum indicador.
Parâmetros
Parâmetro
Padrão
Descrição
Step
10
Distância em ticks para colocar as ordens stop pendentes.
StopLoss
10
Stop loss inicial em ticks.
TakeProfit
50
Take profit em ticks (0 desativa).
TrailingStop
10
Distância do trailing stop em ticks.
TrailingStart
0
Lucro em ticks antes de ativar o trailing.
StepTrail
2
Mudança mínima no preço do stop (em ticks) para enviar uma nova ordem stop.
BreakEven
false
Mover o stop para a entrada ao atingir MinProfitBreakEven.
MinProfitBreakEven
0
Lucro em ticks necessário para mover o stop ao ponto de equilíbrio.
TimeModify
30
Segundos entre tentativas de reprecificação de ordens pendentes.
Notas
Ordens são gerenciadas usando a API de alto nível do StockSharp.
A estratégia cancela ordens protetoras quando a posição é fechada.
Apenas a versão em C# é fornecida; nenhuma implementação em Python está incluída.
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>
/// News-style volatility breakout strategy.
/// Enters on ATR expansion with momentum confirmation via EMA.
/// </summary>
public class NewsPendingOrdersStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMult;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevAtr;
private decimal _entryPrice;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal AtrMult { get => _atrMult.Value; set => _atrMult.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public NewsPendingOrdersStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period", "Indicators");
_atrMult = Param(nameof(AtrMult), 1.5m)
.SetDisplay("ATR Mult", "ATR expansion multiplier", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevAtr = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var atr = new StandardDeviation { Length = AtrPeriod };
SubscribeCandles(CandleType).Bind(ema, atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal atr)
{
if (candle.State != CandleStates.Finished) return;
if (_prevAtr <= 0) { _prevAtr = atr; return; }
var close = candle.ClosePrice;
var bodySize = Math.Abs(candle.ClosePrice - candle.OpenPrice);
// Volatility expansion: big body candle relative to stddev
var expansion = bodySize > atr * 0.5m;
if (expansion && close > ema && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_entryPrice = close;
}
else if (expansion && close < ema && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_entryPrice = close;
}
// Exit long
else if (Position > 0)
{
if (close < ema || (_entryPrice > 0 && close <= _entryPrice - atr * 2))
{
SellMarket();
_entryPrice = 0;
}
}
// Exit short
else if (Position < 0)
{
if (close > ema || (_entryPrice > 0 && close >= _entryPrice + atr * 2))
{
BuyMarket();
_entryPrice = 0;
}
}
_prevAtr = atr;
}
}
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 StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class news_pending_orders_strategy(Strategy):
def __init__(self):
super(news_pending_orders_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 10) \
.SetDisplay("EMA Period", "EMA trend period", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period", "Indicators")
self._atr_mult = self.Param("AtrMult", 1.5) \
.SetDisplay("ATR Mult", "ATR expansion multiplier", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_atr = 0.0
self._entry_price = 0.0
@property
def ema_period(self):
return self._ema_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def atr_mult(self):
return self._atr_mult.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(news_pending_orders_strategy, self).OnReseted()
self._prev_atr = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(news_pending_orders_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
atr = StandardDeviation()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, atr, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, ema, atr):
if candle.State != CandleStates.Finished:
return
if self._prev_atr <= 0:
self._prev_atr = atr
return
close = candle.ClosePrice
body_size = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
# Volatility expansion: big body candle relative to stddev
expansion = body_size > atr * 0.5
if expansion and close > ema and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
elif expansion and close < ema and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
# Exit long
elif self.Position > 0:
if close < ema or (self._entry_price > 0 and close <= self._entry_price - atr * 2):
self.SellMarket()
self._entry_price = 0
# Exit short
elif self.Position < 0:
if close > ema or (self._entry_price > 0 and close >= self._entry_price + atr * 2):
self.BuyMarket()
self._entry_price = 0
self._prev_atr = atr
def CreateClone(self):
return news_pending_orders_strategy()