Estratégia AfterEffects
A estratégia AfterEffects é baseada na ideia de que os preços de mercado podem mostrar efeitos residuais.
Ela calcula um sinal usando o preço de fechamento atual e as aberturas de p e 2p barras atrás:
signal = Close - 2 * Open[p] + Open[2p]
Um sinal positivo abre uma posição comprada, enquanto um sinal negativo abre uma posição vendida.
Configurar Random como verdadeiro inverte o sinal.
Uma vez em posição, a estratégia coloca um stop-loss a StopLoss pontos da entrada.
Quando o preço se move 2 * StopLoss pontos na direção favorável:
- se o sinal muda de sinal, a posição é revertida negociando com o dobro do volume;
- caso contrário, o stop-loss é ajustado para o novo nível.
Detalhes
- Critérios de entrada:
signal > 0para comprado,signal < 0para vendido. - Comprado/Vendido: Ambas as direções.
- Critérios de saída: Sinal oposto ou stop-loss.
- Stops: Trailing.
- Valores padrão:
StopLoss= 500Period= 3Random= falseVolume= 1CandleType= TimeSpan.FromMinutes(1)
- Filtros:
- Categoria: Tendência
- Direção: Ambos
- Indicadores: Fórmula personalizada
- Stops: Trailing
- Complexidade: Básico
- Período: Intradiário (1m)
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
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>
/// Strategy based on aftereffects in price series.
/// It evaluates a custom signal from historical opens and the current close.
/// </summary>
public class AfterEffectsStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<int> _period;
private readonly StrategyParam<bool> _random;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<decimal> _pQueue = new();
private readonly Queue<decimal> _twoPQueue = new();
private decimal _openP;
private decimal _open2P;
private decimal _stopPrice;
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public bool Random { get => _random.Value; set => _random.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public AfterEffectsStrategy()
{
_stopLoss = Param(nameof(StopLoss), 500m)
.SetDisplay("Stop Loss", "Stop Loss distance", "General");
_period = Param(nameof(Period), 8)
.SetDisplay("Bar Period", "Period of bars for signal", "General");
_random = Param(nameof(Random), false)
.SetDisplay("Random Range", "Invert signal", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle Type", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_pQueue.Clear();
_twoPQueue.Clear();
_openP = 0m;
_open2P = 0m;
_stopPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pQueue.Clear();
_twoPQueue.Clear();
_openP = 0m;
_open2P = 0m;
_stopPrice = 0m;
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;
_pQueue.Enqueue(candle.OpenPrice);
if (_pQueue.Count > Period)
{
_openP = _pQueue.Dequeue();
_twoPQueue.Enqueue(_openP);
if (_twoPQueue.Count > Period)
_open2P = _twoPQueue.Dequeue();
}
if (_twoPQueue.Count < Period)
return;
var signal = candle.ClosePrice - 2m * _openP + _open2P;
if (Random)
signal = -signal;
if (Position == 0)
{
if (signal > 0m)
{
BuyMarket();
_stopPrice = candle.ClosePrice - StopLoss;
}
else
{
SellMarket();
_stopPrice = candle.ClosePrice + StopLoss;
}
return;
}
if (Position > 0)
{
if (candle.ClosePrice <= _stopPrice)
{
if (signal < 0m)
{
// Reverse to short
SellMarket();
SellMarket();
_stopPrice = candle.ClosePrice + StopLoss;
}
else
{
// Just exit
SellMarket();
}
}
else
{
_stopPrice = Math.Max(_stopPrice, candle.ClosePrice - StopLoss);
}
}
else if (Position < 0)
{
if (candle.ClosePrice >= _stopPrice)
{
if (signal > 0m)
{
// Reverse to long
BuyMarket();
BuyMarket();
_stopPrice = candle.ClosePrice - StopLoss;
}
else
{
// Just exit
BuyMarket();
}
}
else
{
_stopPrice = Math.Min(_stopPrice, candle.ClosePrice + StopLoss);
}
}
}
}
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.Strategies import Strategy
class after_effects_strategy(Strategy):
def __init__(self):
super(after_effects_strategy, self).__init__()
self._stop_loss = self.Param("StopLoss", 500.0) \
.SetDisplay("Stop Loss", "Stop Loss distance", "General")
self._period = self.Param("Period", 8) \
.SetDisplay("Bar Period", "Period of bars for signal", "General")
self._random = self.Param("Random", False) \
.SetDisplay("Random Range", "Invert signal", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle Type", "General")
self._p_queue = []
self._two_p_queue = []
self._open_p = 0.0
self._open_2p = 0.0
self._stop_price = 0.0
@property
def stop_loss(self):
return self._stop_loss.Value
@property
def period(self):
return self._period.Value
@property
def random(self):
return self._random.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(after_effects_strategy, self).OnReseted()
self._p_queue = []
self._two_p_queue = []
self._open_p = 0.0
self._open_2p = 0.0
self._stop_price = 0.0
def OnStarted2(self, time):
super(after_effects_strategy, self).OnStarted2(time)
self._p_queue = []
self._two_p_queue = []
self._open_p = 0.0
self._open_2p = 0.0
self._stop_price = 0.0
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
per = int(self.period)
sl = float(self.stop_loss)
close = float(candle.ClosePrice)
self._p_queue.append(float(candle.OpenPrice))
if len(self._p_queue) > per:
self._open_p = self._p_queue.pop(0)
self._two_p_queue.append(self._open_p)
if len(self._two_p_queue) > per:
self._open_2p = self._two_p_queue.pop(0)
if len(self._two_p_queue) < per:
return
signal = close - 2.0 * self._open_p + self._open_2p
if self.random:
signal = -signal
if self.Position == 0:
if signal > 0.0:
self.BuyMarket()
self._stop_price = close - sl
else:
self.SellMarket()
self._stop_price = close + sl
return
if self.Position > 0:
if close <= self._stop_price:
if signal < 0.0:
self.SellMarket()
self.SellMarket()
self._stop_price = close + sl
else:
self.SellMarket()
else:
self._stop_price = max(self._stop_price, close - sl)
elif self.Position < 0:
if close >= self._stop_price:
if signal > 0.0:
self.BuyMarket()
self.BuyMarket()
self._stop_price = close - sl
else:
self.BuyMarket()
else:
self._stop_price = min(self._stop_price, close + sl)
def CreateClone(self):
return after_effects_strategy()