La estrategia Negative Spread aprovecha los momentos excepcionales en que el mejor precio de venta cae por debajo del mejor precio de compra, creando un spread negativo.
Cuando aparece este desequilibrio de precios, la estrategia vende al mercado e intenta capturar el spread anormal.
Después de abrir la posición corta, se cierra en la siguiente actualización del libro de órdenes cuando el mercado vuelve a un estado normal.
El sistema escucha únicamente eventos del libro de órdenes y no depende de velas ni indicadores.
Se proporcionan parámetros opcionales de stop-loss y take-profit como medidas de seguridad, calculados en pips usando el tamaño del tick del instrumento.
Detalles
Criterios de entrada: BestAsk < BestBid y sin posición activa.
Largo/Corto: Solo corto.
Criterios de salida: La posición se cierra inmediatamente después de abrirse.
Stops: Stop-loss y take-profit opcionales en pips.
Valores predeterminados:
Volume = 1
TakeProfitPips = 5000
StopLossPips = 5000
Filtros:
Categoría: Arbitraje
Dirección: Corto
Indicadores: Ninguno
Stops: Opcional
Complejidad: Básico
Marco temporal: Tick
Estacionalidad: No
Redes neuronales: No
Divergencia: No
Nivel de riesgo: Alto
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 that detects price dislocations using Bollinger Bands
/// and trades mean reversion when price extends beyond bands.
/// </summary>
public class NegativeSpreadStrategy : Strategy
{
private readonly StrategyParam<int> _bbPeriod;
private readonly StrategyParam<decimal> _bbWidth;
private readonly StrategyParam<DataType> _candleType;
public int BbPeriod { get => _bbPeriod.Value; set => _bbPeriod.Value = value; }
public decimal BbWidth { get => _bbWidth.Value; set => _bbWidth.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public NegativeSpreadStrategy()
{
_bbPeriod = Param(nameof(BbPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_bbWidth = Param(nameof(BbWidth), 1.5m)
.SetDisplay("BB Width", "Bollinger Bands deviation", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bb = new BollingerBands { Length = BbPeriod, Width = BbWidth };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bb, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(1, UnitTypes.Percent),
stopLoss: new Unit(0.5m, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bb);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bbValue.IsFormed)
return;
var bb = (BollingerBandsValue)bbValue;
if (bb.UpBand is not decimal upper || bb.LowBand is not decimal lower)
return;
var close = candle.ClosePrice;
// Mean reversion: sell when above upper band, buy when below lower band
if (close > upper && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
else if (close < lower && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
}
}
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, UnitTypes, Unit
from StockSharp.Algo.Indicators import BollingerBands
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class negative_spread_strategy(Strategy):
def __init__(self):
super(negative_spread_strategy, self).__init__()
self._bb_period = self.Param("BbPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators")
self._bb_width = self.Param("BbWidth", 1.5) \
.SetDisplay("BB Width", "Bollinger Bands deviation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
@property
def BbPeriod(self):
return self._bb_period.Value
@BbPeriod.setter
def BbPeriod(self, value):
self._bb_period.Value = value
@property
def BbWidth(self):
return self._bb_width.Value
@BbWidth.setter
def BbWidth(self, value):
self._bb_width.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(negative_spread_strategy, self).OnReseted()
def OnStarted2(self, time):
super(negative_spread_strategy, self).OnStarted2(time)
bb = BollingerBands()
bb.Length = self.BbPeriod
bb.Width = self.BbWidth
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(bb, self.OnProcess).Start()
self.StartProtection(
takeProfit=Unit(1, UnitTypes.Percent),
stopLoss=Unit(0.5, UnitTypes.Percent)
)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, bb)
self.DrawOwnTrades(area)
def OnProcess(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
if not bb_value.IsFormed:
return
upper = bb_value.UpBand
lower = bb_value.LowBand
if upper is None or lower is None:
return
close = candle.ClosePrice
if close > float(upper) and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif close < float(lower) and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
def CreateClone(self):
return negative_spread_strategy()