Estrategia Exchange Price
Esta estrategia compara el precio de cierre actual con los precios de varias barras atrás durante dos períodos de lookback. Se abre una posición larga cuando el cambio a corto plazo supera al cambio a largo plazo; se abre una posición corta cuando ocurre el cruce opuesto.
Detalles
- Criterios de entrada: diferencia de precio a corto plazo cruzando por encima/por debajo de la diferencia a largo plazo
- Largo/Corto: Ambos
- Criterios de salida: cruce opuesto
- Stops: No
- Valores predeterminados:
ShortPeriod= 96LongPeriod= 288CandleType= velas de 8 horas
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: Diferencia de precio
- Stops: No
- Complejidad: Básico
- Marco temporal: 8 horas
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on comparing current price with prices from short and long lookback periods.
/// </summary>
public class ExchangePriceStrategy : Strategy
{
private readonly StrategyParam<int> _shortPeriod;
private readonly StrategyParam<int> _longPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _minDiffPercent;
private readonly StrategyParam<int> _cooldownBars;
private readonly List<decimal> _prices = new();
private decimal? _prevUpDiff;
private decimal? _prevDownDiff;
private int _cooldownRemaining;
public int ShortPeriod { get => _shortPeriod.Value; set => _shortPeriod.Value = value; }
public int LongPeriod { get => _longPeriod.Value; set => _longPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal MinDiffPercent { get => _minDiffPercent.Value; set => _minDiffPercent.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public ExchangePriceStrategy()
{
_shortPeriod = Param(nameof(ShortPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Short Period", "Bars for short lookback", "General");
_longPeriod = Param(nameof(LongPeriod), 48)
.SetGreaterThanZero()
.SetDisplay("Long Period", "Bars for long lookback", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_minDiffPercent = Param(nameof(MinDiffPercent), 0.0025m)
.SetDisplay("Minimum Difference %", "Minimum normalized difference between short and long deltas", "Filters");
_cooldownBars = Param(nameof(CooldownBars), 4)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prices.Clear();
_prevUpDiff = null;
_prevDownDiff = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
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;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
_prices.Add(candle.ClosePrice);
if (_prices.Count > LongPeriod + 1)
_prices.RemoveAt(0);
if (_prices.Count <= LongPeriod || _prices.Count <= ShortPeriod)
return;
var priceShort = _prices[_prices.Count - 1 - ShortPeriod];
var priceLong = _prices[_prices.Count - 1 - LongPeriod];
var upDiff = candle.ClosePrice - priceShort;
var downDiff = candle.ClosePrice - priceLong;
var diffPercent = candle.ClosePrice != 0m ? Math.Abs(upDiff - downDiff) / candle.ClosePrice : 0m;
if (_prevUpDiff is decimal prevUp && _prevDownDiff is decimal prevDown && _cooldownRemaining == 0)
{
var crossUp = prevUp <= prevDown && upDiff > downDiff && diffPercent >= MinDiffPercent;
var crossDown = prevUp >= prevDown && upDiff < downDiff && diffPercent >= MinDiffPercent;
if (crossUp && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownBars;
}
else if (crossDown && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
_prevUpDiff = upDiff;
_prevDownDiff = downDiff;
}
}
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 exchange_price_strategy(Strategy):
def __init__(self):
super(exchange_price_strategy, self).__init__()
self._short_period = self.Param("ShortPeriod", 12) \
.SetDisplay("Short Period", "Bars for short lookback", "General")
self._long_period = self.Param("LongPeriod", 48) \
.SetDisplay("Long Period", "Bars for long lookback", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._min_diff_percent = self.Param("MinDiffPercent", 0.0025) \
.SetDisplay("Minimum Difference %", "Minimum normalized difference between short and long deltas", "Filters")
self._cooldown_bars = self.Param("CooldownBars", 4) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading")
self._prices = []
self._prev_up_diff = None
self._prev_down_diff = None
self._cooldown_remaining = 0
@property
def short_period(self):
return self._short_period.Value
@property
def long_period(self):
return self._long_period.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def min_diff_percent(self):
return self._min_diff_percent.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(exchange_price_strategy, self).OnReseted()
self._prices = []
self._prev_up_diff = None
self._prev_down_diff = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(exchange_price_strategy, self).OnStarted2(time)
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
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
close = float(candle.ClosePrice)
self._prices.append(close)
if len(self._prices) > self.long_period + 1:
self._prices.pop(0)
if len(self._prices) <= self.long_period or len(self._prices) <= self.short_period:
return
price_short = self._prices[len(self._prices) - 1 - self.short_period]
price_long = self._prices[len(self._prices) - 1 - self.long_period]
up_diff = close - price_short
down_diff = close - price_long
diff_percent = abs(up_diff - down_diff) / close if close != 0 else 0.0
min_dp = float(self.min_diff_percent)
if self._prev_up_diff is not None and self._prev_down_diff is not None and self._cooldown_remaining == 0:
cross_up = self._prev_up_diff <= self._prev_down_diff and up_diff > down_diff and diff_percent >= min_dp
cross_down = self._prev_up_diff >= self._prev_down_diff and up_diff < down_diff and diff_percent >= min_dp
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
self._prev_up_diff = up_diff
self._prev_down_diff = down_diff
def CreateClone(self):
return exchange_price_strategy()