Стратегия Авелланеда-Стойкова Халеда Тамиама
Реализует модель маркет-мейкинга Авелланеда–Стойкова. Стратегия рассчитывает котировки bid и ask на основе двух последних закрытий и открывает позиции при отклонении цены от этих уровней на заданную величину.
Детали
- Условия входа:
- Лонг:
close < bidQuote - M - Шорт:
close > askQuote + M
- Лонг:
- Лонг/Шорт: обе стороны.
- Условия выхода: противоположный сигнал.
- Стопы: нет.
- Значения по умолчанию:
Gamma= 2Sigma= 8T= 0.0833K= 5M= 0.5Fee= 0
- Фильтры:
- Категория: Маркет-мейкинг
- Направление: Оба
- Индикаторы: Нет
- Стопы: Нет
- Сложность: Низкая
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Khaled Tamim's Avellaneda-Stoikov strategy.
/// </summary>
public class KhaledTamimsAvellanedaStoikovStrategy : Strategy
{
private readonly StrategyParam<decimal> _gamma;
private readonly StrategyParam<decimal> _sigma;
private readonly StrategyParam<decimal> _t;
private readonly StrategyParam<decimal> _k;
private readonly StrategyParam<decimal> _m;
private readonly StrategyParam<decimal> _fee;
private readonly StrategyParam<int> _maxEntries;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private bool _isFirst = true;
private int _entriesExecuted;
private int _barsSinceSignal;
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public KhaledTamimsAvellanedaStoikovStrategy()
{
_gamma = Param("Gamma", 2m).SetDisplay("Gamma", "Gamma", "General");
_sigma = Param("Sigma", 8m).SetDisplay("Sigma", "Sigma", "General");
_t = Param("T", 0.0833m).SetDisplay("T", "T", "General");
_k = Param("K", 5m).SetDisplay("K", "K", "General");
_m = Param("M", 0.5m).SetDisplay("M", "M", "General");
_fee = Param("Fee", 0m).SetDisplay("Fee", "Fee", "General");
_maxEntries = Param("Max Entries", 45).SetDisplay("Max Entries", "Maximum entries per run", "Risk");
_cooldownBars = Param("Cooldown Bars", 12000).SetDisplay("Cooldown Bars", "Minimum bars between entries", "Risk");
_candleType = Param("Candle type", TimeSpan.FromMinutes(1).TimeFrame()).SetDisplay("Candle Type", "Candle Type", "General");
}
public decimal Gamma { get => _gamma.Value; set => _gamma.Value = value; }
public decimal Sigma { get => _sigma.Value; set => _sigma.Value = value; }
public decimal T { get => _t.Value; set => _t.Value = value; }
public decimal K { get => _k.Value; set => _k.Value = value; }
public decimal M { get => _m.Value; set => _m.Value = value; }
public decimal Fee { get => _fee.Value; set => _fee.Value = value; }
public int MaxEntries { get => _maxEntries.Value; set => _maxEntries.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0m;
_isFirst = true;
_entriesExecuted = 0;
_barsSinceSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entriesExecuted = 0;
_barsSinceSignal = CooldownBars;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_barsSinceSignal++;
if (_isFirst)
{
_prevClose = candle.ClosePrice;
_isFirst = false;
return;
}
var midPrice = (candle.ClosePrice + _prevClose) / 2m;
_prevClose = candle.ClosePrice;
var sqrtTerm = Gamma * Sigma * Sigma * T;
var bidQuote = midPrice - K * sqrtTerm - (midPrice * Fee);
var askQuote = midPrice + K * sqrtTerm + (midPrice * Fee);
var longCondition = candle.ClosePrice < bidQuote - M;
var shortCondition = candle.ClosePrice > askQuote + M;
if (_entriesExecuted >= MaxEntries || _barsSinceSignal < CooldownBars)
return;
if (longCondition && Position <= 0)
{
BuyMarket();
_entriesExecuted++;
_barsSinceSignal = 0;
}
else if (shortCondition && Position >= 0)
{
SellMarket();
_entriesExecuted++;
_barsSinceSignal = 0;
}
}
}
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 khaled_tamims_avellaneda_stoikov_strategy(Strategy):
def __init__(self):
super(khaled_tamims_avellaneda_stoikov_strategy, self).__init__()
self._gamma = self.Param("Gamma", 2.0) \
.SetDisplay("Gamma", "Gamma", "General")
self._sigma = self.Param("Sigma", 8.0) \
.SetDisplay("Sigma", "Sigma", "General")
self._t = self.Param("T", 0.0833) \
.SetDisplay("T", "T", "General")
self._k = self.Param("K", 5.0) \
.SetDisplay("K", "K", "General")
self._m = self.Param("M", 0.5) \
.SetDisplay("M", "M", "General")
self._fee = self.Param("Fee", 0.0) \
.SetDisplay("Fee", "Fee", "General")
self._max_entries = self.Param("MaxEntries", 45) \
.SetDisplay("Max Entries", "Maximum entries per run", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 12000) \
.SetDisplay("Cooldown Bars", "Minimum bars between entries", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Candle Type", "General")
self._prev_close = 0.0
self._is_first = True
self._entries_executed = 0
self._bars_since_signal = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(khaled_tamims_avellaneda_stoikov_strategy, self).OnReseted()
self._prev_close = 0.0
self._is_first = True
self._entries_executed = 0
self._bars_since_signal = 0
def OnStarted2(self, time):
super(khaled_tamims_avellaneda_stoikov_strategy, self).OnStarted2(time)
self._entries_executed = 0
self._bars_since_signal = self._cooldown_bars.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.OnProcess).Start()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
self._bars_since_signal += 1
close = float(candle.ClosePrice)
if self._is_first:
self._prev_close = close
self._is_first = False
return
mid_price = (close + self._prev_close) / 2.0
self._prev_close = close
gamma = float(self._gamma.Value)
sigma = float(self._sigma.Value)
t = float(self._t.Value)
k = float(self._k.Value)
m = float(self._m.Value)
fee = float(self._fee.Value)
sqrt_term = gamma * sigma * sigma * t
bid_quote = mid_price - k * sqrt_term - mid_price * fee
ask_quote = mid_price + k * sqrt_term + mid_price * fee
long_cond = close < bid_quote - m
short_cond = close > ask_quote + m
if self._entries_executed >= self._max_entries.Value or self._bars_since_signal < self._cooldown_bars.Value:
return
if long_cond and self.Position <= 0:
self.BuyMarket()
self._entries_executed += 1
self._bars_since_signal = 0
elif short_cond and self.Position >= 0:
self.SellMarket()
self._entries_executed += 1
self._bars_since_signal = 0
def CreateClone(self):
return khaled_tamims_avellaneda_stoikov_strategy()