Estrategia AfterEffects
La estrategia AfterEffects se basa en la idea de que los precios del mercado pueden mostrar efectos residuales.
Calcula una señal utilizando el precio de cierre actual y las aperturas de p y 2p barras atrás:
signal = Close - 2 * Open[p] + Open[2p]
Una señal positiva abre una posición larga, mientras que una señal negativa abre una posición corta.
Configurar Random en verdadero invierte la señal.
Una vez en posición, la estrategia coloca un stop-loss a StopLoss puntos del punto de entrada.
Cuando el precio se mueve 2 * StopLoss puntos en la dirección favorable:
- si la señal cambia de signo, la posición se revierte operando con el doble del volumen;
- de lo contrario, el stop-loss se ajusta al nuevo nivel.
Detalles
- Criterios de entrada:
signal > 0para largo,signal < 0para corto. - Largo/Corto: Ambas direcciones.
- Criterios de salida: Señal opuesta o stop-loss.
- Stops: Trailing.
- Valores predeterminados:
StopLoss= 500Period= 3Random= falseVolume= 1CandleType= TimeSpan.FromMinutes(1)
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: Fórmula personalizada
- Stops: Trailing
- Complejidad: Básico
- Marco temporal: Intradía (1m)
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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()