This strategy replicates the behavior of an HFT spreader on the FORTS market. It continuously monitors the order book and places limit orders on both sides of the market to capture the bid-ask spread.
Strategy Logic
Subscribe to real-time order book updates.
When no position is open and the spread is wide enough (determined by SpreadMultiplier), the strategy places:
A buy limit order one tick above the best bid.
A sell limit order one tick below the best ask.
If a position exists and no active orders are present, it places a single limit order at the opposite side to close and reverse the position.
Orders are cancelled and replaced whenever the best prices move to keep them at the top of the book.
Parameters
SpreadMultiplier – required spread in ticks to place both buy and sell orders. Default is 4 ticks.
Volume – order volume. Default is 1 lot.
Usage Notes
Designed for instruments with small tick sizes such as futures on the FORTS exchange.
Uses only limit orders; no market orders are sent except by the protection mechanism if needed.
Ensure sufficient liquidity and low latency environment for effective operation.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that captures the spread on FORTS by trading when spread is wide enough.
/// </summary>
public class HftSpreaderForFortsStrategy : Strategy
{
private readonly StrategyParam<int> _spreadMultiplier;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Required spread in ticks to place both buy and sell orders.
/// </summary>
public int SpreadMultiplier
{
get => _spreadMultiplier.Value;
set => _spreadMultiplier.Value = value;
}
/// <summary>
/// Candle type for price feed.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="HftSpreaderForFortsStrategy"/>.
/// </summary>
public HftSpreaderForFortsStrategy()
{
_spreadMultiplier = Param(nameof(SpreadMultiplier), 4)
.SetGreaterThanZero()
.SetDisplay("Spread Multiplier", "Spread ticks required to place orders", "General")
.SetOptimize(1, 10, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Candle type for price feed", "General");
Volume = 1;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var step = Security.PriceStep ?? 1m;
var spread = candle.HighPrice - candle.LowPrice;
if (spread >= SpreadMultiplier * step)
{
if (Position == 0)
{
// Wide spread detected - buy at low, will exit when spread narrows
BuyMarket();
}
else if (Position > 0)
{
// Close long position for spread capture
SellMarket();
}
else if (Position < 0)
{
// Close short position for spread capture
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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class hft_spreader_for_forts_strategy(Strategy):
"""
HFT Spreader for FORTS: trades when candle spread is wide enough.
Opens and closes based on high-low range vs spread multiplier.
"""
def __init__(self):
super(hft_spreader_for_forts_strategy, self).__init__()
self._spread_multiplier = self.Param("SpreadMultiplier", 4) \
.SetDisplay("Spread Multiplier", "Spread ticks required to place orders", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Candle type for price feed", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(hft_spreader_for_forts_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
spread = float(candle.HighPrice) - float(candle.LowPrice)
if spread >= self._spread_multiplier.Value * step:
if self.Position == 0:
self.BuyMarket()
elif self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
def CreateClone(self):
return hft_spreader_for_forts_strategy()