A Estratégia de Formação de Mercado BitexOne reproduz o robô de cotação assíncrono do código fonte original
BITEX.ONE MarketMaker.mq5. O algoritmo coloca continuamente pares de ordens limite em torno de um preço de referência e
mantém um número igual de níveis nos lados de oferta e demanda. A estratégia foi reescrita para StockSharp usando a API de
alto nível: o gerenciamento de cotações é impulsionado por assinaturas do livro de ordens e nível 1, enquanto a
normalização de risco e volume depende dos metadados do instrumento (PriceStep, VolumeStep e MinVolume).
Lógica de Trading
Determinar o preço líder do PriceSource selecionado. Por padrão a estratégia espera preços mark, mas pode usar o
livro de ordens principal ou um instrumento auxiliar (índice ou símbolo mark) via o parâmetro LeadSecurity.
Calcular a distância entre níveis de preço como ShiftCoefficient * lead price e criar uma escada simétrica de cotações
acima e abaixo da referência.
Limitar a exposição total em cada lado a MaxVolumePerLevel * LevelCount. As operações executadas reduzem imediatamente
o volume disponível para que a grade sempre reflita a posição atual.
Normalizar preços e volumes usando o tamanho de tick do instrumento e o passo de volume. O algoritmo cancela ordens
desatualizadas e registra novas quando o preço ou volume derivam além da tolerância herdada do código MQL original
(limiar de preço de 0,05% e limiar de volume de meio passo).
Todas as ordens ativas são canceladas durante eventos de stop/reset para manter o livro limpo.
Parâmetros
MaxVolumePerLevel – volume máximo cotado em qualquer nível de preço único. Afeta ambos os lados do livro e age como
limite quando a posição atual cresce.
ShiftCoefficient – offset relativo do preço líder aplicado para cada nível incremental (leadPrice ± shift * levelIndex).
LevelCount – número de níveis de cotação por lado. Cada nível cria uma ordem limite de compra e uma de venda.
PriceSource – valor enumerado (OrderBook, MarkPrice, IndexPrice) definindo de onde o preço de referência se origina.
LeadSecurity – instrumento opcional usado quando preços mark ou de índice externos são necessários. Se omitido, o
instrumento de estratégia principal fornece a referência.
Notas de Conversão
O gerenciamento assíncrono de ordens do MetaTrader (SendAsync/ModifyAsync/RemoveOrderAsync) é mapeado para os helpers
BuyLimit/SellLimit do StockSharp combinados com cancelamento explícito quando as tolerâncias são excedidas.
A lógica de balanceamento de posição (max_pos * level_count ± position) é preservada para manter a escada centralizada
e consciente do risco.
A seleção do preço líder imita a lógica de sufixos do robô original (symbol, symbolm, symboli) permitindo um
LeadSecurity personalizado combinado com uma dica PriceSource.
As verificações periódicas impulsionadas por temporizador em MQL são substituídas por atualizações reativas acionadas por
mensagens do livro de ordens/nível 1 e eventos de portfólio.
Notas de Uso
Certifique-se de que o adaptador conectado fornece profundidade de mercado ou dados de nível 1 tanto para o símbolo de
trading quanto para o LeadSecurity opcional.
Quando usar feeds mark ou de índice, assine os instrumentos correspondentes antes de iniciar a estratégia para que o
preço líder fique disponível imediatamente.
Considere habilitar proteção de portfólio ou gerenciamento de risco adicional no ambiente de hospedagem se a exchange
exigir proporções rígidas de cotação para operação.
A estratégia não começa a cotar até que um preço líder positivo seja recebido; verifique a conectividade se nenhuma
ordem aparecer após a inicialização.
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>
/// BitexOne market maker strategy using SMA mean-reversion approach.
/// Buys when price drops below lower band, sells when above upper band.
/// </summary>
public class BitexOneMarketMakerStrategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private SimpleMovingAverage _sma;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// SMA period.
/// </summary>
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="BitexOneMarketMakerStrategy"/> class.
/// </summary>
public BitexOneMarketMakerStrategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "SMA period for mean reversion", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_sma, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_sma.IsFormed)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 100;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 100;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 100;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 100;
return;
}
}
// Mean reversion around SMA
var deviation = smaValue * 0.008m;
if (close < smaValue - deviation && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 100;
}
else if (close > smaValue + deviation && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 100;
}
}
}
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.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bitex_one_market_maker_strategy(Strategy):
def __init__(self):
super(bitex_one_market_maker_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 100) \
.SetDisplay("SMA Period", "SMA period for mean reversion", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._sma = None
self._entry_price = 0.0
self._cooldown = 0
@property
def sma_period(self):
return self._sma_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(bitex_one_market_maker_strategy, self).OnReseted()
self._sma = None
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(bitex_one_market_maker_strategy, self).OnStarted2(time)
self._sma = SimpleMovingAverage()
self._sma.Length = self.sma_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._sma, self._process_candle)
subscription.Start()
def _process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
sma_val = float(sma_value)
if not self._sma.IsFormed:
return
if self._cooldown > 0:
self._cooldown -= 1
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
# Check SL/TP
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
return
# Mean reversion around SMA
deviation = sma_val * 0.008
if close < sma_val - deviation and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif close > sma_val + deviation and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
def CreateClone(self):
return bitex_one_market_maker_strategy()