La Estrategia de calculadora de comisiones es una estrategia de utilidad que refleja el script original MetaTrader. Envía una única orden discrecional utilizando el modo de ejecución seleccionado (mercado, límite o parada) y mide la comisión del corredor aplicada a cada ejecución resultante. La estrategia almacena la comisión acumulada e imprime un informe final con el saldo inicial, las tarifas totales y el saldo de tarifas ajustadas cuando finaliza.
A diferencia de las estrategias convencionales basadas en señales, no se requieren datos ni indicadores de mercado. La estrategia se centra en la contabilidad automatizada de tarifas para ejecuciones manuales o semimanuales.
Lógica de trading
Cuando comienza la estrategia, captura el saldo inicial de la cartera y configura el volumen comercial predeterminado.
Los niveles de protección de pérdidas y toma de ganancias opcionales se activan a través de StartProtection cuando tanto el precio de entrada como el precio objetivo son válidos. Las distancias se calculan en unidades de precio absoluto, imitando la implementación de MQL.
El modo de orden configurado se ejecuta exactamente una vez. Si los parámetros son inconsistentes (por ejemplo, falta el precio de entrada para las órdenes limitadas), la estrategia registra el problema y omite el envío de la orden.
Cada operación propia recibida a través de OnNewMyTrade se procesa para calcular la comisión utilizando la tasa porcentual configurada.
La estrategia agrega todas las comisiones, recuerda la tarifa más reciente y registra un resumen detallado al detenerse.
La implementación supone que la tarifa del corredor es proporcional a price × volume × commissionRate / 100. Ajuste la tarifa para que coincida con el lugar que se está modelando.
Parámetros
Nombre
Predeterminado
Descripción
Quantity
0.001
Volumen comercial enviado por métodos auxiliares (BuyMarket, SellLimit, etc.).
EntryPrice
31365
Precio utilizado para órdenes límite o stop y para calcular distancias de protección.
StopLossPrice
31200
Precio que define la distancia del stop-loss. Una distancia no positiva desactiva la protección stop-loss.
TakeProfitPrice
32100
Precio que define la distancia de toma de ganancias. Una distancia no positiva desactiva la protección de obtención de beneficios.
CommissionRate
0.04
Tasa de comisión expresada como porcentaje del nocional negociado.
Mode
None
Tipo de orden a ejecutar cuando comience la estrategia. Opciones: None, MarketBuy, MarketSell, BuyLimit, SellLimit, BuyStop, SellStop.
Notas y mejores prácticas
Iniciar la estrategia en una cartera que admita la colocación de pedidos manuales; no se requieren suscripciones de datos.
Asegúrese de que el modelo de comisión del corredor coincida con el parámetro CommissionRate para evitar subestimar o sobreestimar las tarifas.
Para órdenes pendientes, establezca EntryPrice en un nivel válido antes de lanzar la estrategia; de lo contrario el pedido no se envía.
Cuando los niveles de protección están habilitados, la estrategia indica al conector que utilice las salidas del mercado al activarse para imitar fielmente el comportamiento original de MQL.
Informe de resultados
Cuando se invoca OnStopped, la estrategia registra:
Instantánea del saldo inicial (tomada cuando comenzó la estrategia).
Tarifas de corretaje agregadas para todas las operaciones procesadas.
Saldo final ajustado restando las cuotas acumuladas.
Esto hace que la estrategia sea muy adecuada para análisis rápidos de hipótesis y para validar los cronogramas de comisiones de los corredores durante las pruebas retrospectivas.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Commission Calculator strategy: CCI level crossover.
/// Buys when CCI crosses above -100 (oversold exit), sells when CCI crosses below 100 (overbought exit).
/// </summary>
public class CommissionCalculatorStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _prevCci;
private int _candlesSinceTrade;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public CommissionCalculatorStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_period = Param(nameof(Period), 30)
.SetGreaterThanZero()
.SetDisplay("Period", "CCI period", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCci = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCci = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
var cci = new CommodityChannelIndex { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(cci, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (_hasPrev)
{
if (_prevCci < -100 && cciValue >= -100 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (_prevCci > 100 && cciValue <= 100 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
_prevCci = cciValue;
_hasPrev = true;
}
}
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 CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class commission_calculator_strategy(Strategy):
def __init__(self):
super(commission_calculator_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._period = self.Param("Period", 30)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 4)
self._prev_cci = 0.0
self._candles_since_trade = 4
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(commission_calculator_strategy, self).OnReseted()
self._prev_cci = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
def OnStarted2(self, time):
super(commission_calculator_strategy, self).OnStarted2(time)
self._prev_cci = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
cci = CommodityChannelIndex()
cci.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(cci, self._process_candle).Start()
def _process_candle(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
cci_val = float(cci_value)
if self._has_prev:
if self._prev_cci < -100 and cci_val >= -100 and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif self._prev_cci > 100 and cci_val <= 100 and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
self._prev_cci = cci_val
self._has_prev = True
def CreateClone(self):
return commission_calculator_strategy()