2026-09-06 20:42:03 -05:00
//+------------------------------------------------------------------+
//| LoteSizeCalc.mqh |
//| Copyright 2025, Niquel Mendoza. |
//| https://www.mql5.com/es/users/nique_372/news |
//+------------------------------------------------------------------+
# property copyright " Copyright 2025, Niquel Mendoza. "
# property link " https://www.mql5.com/es/users/nique_372/news "
# property strict
# ifndef MQLARTICLES_RM_LOTSIZECALC_MQH
# define MQLARTICLES_RM_LOTSIZECALC_MQH
/ / + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
//| |
//+------------------------------------------------------------------+
# include <Trade/Trade.mqh>
# include "RM_Defines.mqh"
2026-09-12 19:41:52 -05:00
# include "..\\Utils\\FA\\SimpleLogger.mqh"
2026-09-06 20:42:03 -05:00
# include "..\\Utils\\FA\\Events.mqh"
2026-09-12 19:41:52 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
namespace TSN
{
//+------------------------------------------------------------------+
//| Defines |
//+------------------------------------------------------------------+
/*
2025.08.06 16:50:41.989 GetID (XAUUSD,M1) ORDER_TYPE_BUY = 0
2025.08.06 16:50:41.989 GetID (XAUUSD,M1) ORDER_TYPE_SELL = 1
2025.08.06 16:50:41.989 GetID (XAUUSD,M1) ORDER_TYPE_BUY_LIMIT = 2
2025.08.06 16:50:41.989 GetID (XAUUSD,M1) ORDER_TYPE_SELL_LIMIT = 3
2025.08.06 16:50:41.989 GetID (XAUUSD,M1) ORDER_TYPE_BUY_STOP = 4
2025.08.06 16:50:41.989 GetID (XAUUSD,M1) ORDER_TYPE_SELL_STOP = 5
2025.08.06 16:50:41.989 GetID (XAUUSD,M1) ORDER_TYPE_BUY_STOP_LIMIT = 6
2025.08.06 16:50:41.989 GetID (XAUUSD,M1) ORDER_TYPE_SELL_STOP_LIMIT = 7
2025.08.06 16:50:41.989 GetID (XAUUSD,M1) ORDER_TYPE_CLOSE_BY = 8
*/
# define MqlArticlesOrderTToOrder(v) ENUM_ORDER_TYPE ( v = = ORDER_TYPE_CLOSE_BY ? WRONG_VALUE : ( v & 1 ) )
/*
const ENUM_ORDER_TYPE MarketOrderToOrderType[9] =
{
ORDER_TYPE_BUY,
ORDER_TYPE_SELL,
ORDER_TYPE_BUY,
ORDER_TYPE_SELL,
ORDER_TYPE_BUY,
ORDER_TYPE_SELL,
ORDER_TYPE_BUY,
ORDER_TYPE_SELL,
WRONG_VALUE
};*/
2026-09-06 20:42:03 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
typedef double ( * PriceCalculatorFunc ) ( double price , ulong deviation , ulong stop_limit , double point_value , int8_t digits ) ;
2026-09-12 19:41:52 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-09-06 20:42:03 -05:00
namespace GetLoteFunctions
{
//+------------------------------------------------------------------+
inline double PriceCal_CalcMarketPrice ( double price , ulong deviation , ulong stop_limit , double point_value , int8_t digits )
{
return price ;
}
//+------------------------------------------------------------------+
inline double PriceCal_CalcBuyLimitPrice ( double price , ulong deviation , ulong stop_limit , double point_value , int8_t digits )
{
return NormalizeDouble ( price - deviation * point_value , digits ) ;
}
//+------------------------------------------------------------------+
inline double PriceCal_CalcSellLimitPrice ( double price , ulong deviation , ulong stop_limit , double point_value , int8_t digits )
{
return NormalizeDouble ( price + deviation * point_value , digits ) ;
}
//+------------------------------------------------------------------+
inline double PriceCal_CalcBuyStopPrice ( double price , ulong deviation , ulong stop_limit , double point_value , int8_t digits )
{
return NormalizeDouble ( price + deviation * point_value , digits ) ;
}
//+------------------------------------------------------------------+
inline double PriceCal_CalcSellStopPrice ( double price , ulong deviation , ulong stop_limit , double point_value , int8_t digits )
{
return NormalizeDouble ( price - deviation * point_value , digits ) ;
}
//+------------------------------------------------------------------+
inline double PriceCal_CalcBuyStopLimitPrice ( double price , ulong deviation , ulong stop_limit , double point_value , int8_t digits )
{
return NormalizeDouble ( price + deviation * point_value - stop_limit * point_value , digits ) ;
}
//+------------------------------------------------------------------+
inline double PriceCal_CalcSellStopLimitPrice ( double price , ulong deviation , ulong stop_limit , double point_value , int8_t digits )
{
return NormalizeDouble ( price - deviation * point_value + stop_limit * point_value , digits ) ;
}
}
2026-09-12 19:41:52 -05:00
//+------------------------------------------------------------------+
const PriceCalculatorFunc OrderTypeToPriceCalculator [ 9 ] =
{
GetLoteFunctions : : PriceCal_CalcMarketPrice ,
GetLoteFunctions : : PriceCal_CalcMarketPrice ,
GetLoteFunctions : : PriceCal_CalcBuyLimitPrice ,
GetLoteFunctions : : PriceCal_CalcSellLimitPrice ,
GetLoteFunctions : : PriceCal_CalcBuyStopPrice ,
GetLoteFunctions : : PriceCal_CalcSellStopPrice ,
GetLoteFunctions : : PriceCal_CalcBuyStopLimitPrice ,
GetLoteFunctions : : PriceCal_CalcSellStopLimitPrice ,
GetLoteFunctions : : PriceCal_CalcMarketPrice
} ;
2026-09-06 20:42:03 -05:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CGetLote : public CLoggerBase
{
private :
string symbol ;
double volumeStep ;
double minLote ;
double maxLote ;
double volume_limit ;
double point_value ;
int8_t digits ;
double ganancia_por_tick_real ;
public :
CGetLote ( string _symbol ) ;
//--- Update
void Update ( ) ;
//--- Symbol
void SetSymbol ( string new_value ) ;
inline string GetSymbol ( ) const { return symbol ; }
//---
inline double MinVolume ( ) const { return minLote ; }
inline double MaxVolume ( ) const { return maxLote ; }
//--- General functions
// Calcula el stop loss ideal en puntos, basandose en el riesgo de operacion y en un lote especificado
// Calculates the ideal stop loss in points, based on the transaction risk and a specified lot
long CalculateSLWithLot ( double risk_per_operation , double entry_price , double lot , ulong deviation , ulong stop_limit ) ;
// Transforma dinero a puntos..
// Transform money to points
long MoneyToPoints ( const ENUM_ORDER_TYPE type , double risk_per_operation , double entry_price , double & chosen_lot , ulong deviation , ulong stop_limit ) ;
// Calcula el lote en base al riesgo por operacion y el stop loss en puntos
// Calculate the lot based on the risk per operation and the stop loss in points
double GetLoteByRiskPerOperationAndSL ( double glot , double max_risk_per_operation , double & new_risk_per_operation , long StopLoss ) ;
// Obtiene el lote maximo que se puede ejecutar
// Get the maximum batch that can be executed
double GetMaxLote ( ENUM_ORDER_TYPE type , double entry_price , ulong deviation , ulong stop_limit ) ;
// Obtiene el lote maximo que se puede comprar por el riesgo por operacion en USD (no %, osea un riesgo ya calculado)
// Gets the maximum lot that can be purchased for the risk per operation in USD (not %, i.e. a risk already calculated)
double GetLoteByRiskPerOperation ( ENUM_ORDER_TYPE type , double risk_per_operation , double entry_price , ulong deviation , ulong stop_limit ) ;
} ;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CGetLote : : CGetLote ( string _symbol )
{
this . symbol = _symbol ;
Update ( ) ;
}
//+---------------------------------------------------------------------+
//| Determine the optimal lot size based on risk and current conditions |
//+---------------------------------------------------------------------+
double CGetLote : : GetLoteByRiskPerOperationAndSL ( double glot , double max_risk_per_operation , double & new_risk_per_operation , long StopLoss )
{
new_risk_per_operation = 0.0 ;
double nlot = 0.00 ;
//---
if ( glot < = 0 )
{
LogError ( StringFormat ( " Lote maximo invalido: %.2f " , glot ) , FUNCION_ACTUAL ) ;
return 0.00 ;
}
if ( StopLoss < = 0 )
{
LogError ( StringFormat ( " Stop Loss distance is less than or equal to zero, now correct the stoploss distance: %d " , StopLoss ) , FUNCION_ACTUAL ) ;
return nlot ;
}
if ( max_risk_per_operation < = 0 )
{
LogError ( StringFormat ( " El riesgo maximo por operacion %.2f es invalido " , max_risk_per_operation ) , FUNCION_ACTUAL ) ;
return nlot ;
}
//---
long spread = SymbolInfoInteger ( symbol , SYMBOL_SPREAD ) ;
double rpo = glot * ( ganancia_por_tick_real * ( spread + 1 + StopLoss ) ) ;
if ( rpo > max_risk_per_operation )
{
double new_lot = ( max_risk_per_operation / rpo ) * glot ;
new_lot = RoundToStep ( new_lot , volumeStep ) ;
new_risk_per_operation = new_lot * ( ganancia_por_tick_real * ( spread + 1 + StopLoss ) ) ;
nlot = new_lot ;
}
else
{
new_risk_per_operation = rpo ;
nlot = glot ;
}
//---
if ( nlot > maxLote )
nlot = maxLote ;
LogInfo ( StringFormat ( " Risk per operation(%.2f) = %.2f * ( %f * ( %i + 1 + %i))) " , new_risk_per_operation , nlot , ganancia_por_tick_real , spread , StopLoss ) , FUNCION_ACTUAL ) ;
//---
if ( nlot < = 0.00 )
LogWarning ( StringFormat ( " The lot %.2f is invalid, the risk %.2f increases or the sl %d decreases " , nlot , max_risk_per_operation , StopLoss ) , FUNCION_ACTUAL ) ;
else
LogInfo ( StringFormat ( " For the stop loss %i in points and risk per operation: %.2f, the ideal lot is: %.2f " , StopLoss , max_risk_per_operation , nlot ) , FUNCION_ACTUAL ) ;
return nlot ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
long CGetLote : : CalculateSLWithLot ( double risk_per_operation , double entry_price , double lot , ulong deviation , ulong stop_limit )
{
//--- Check lot
if ( lot < = 0 )
{
LogError ( StringFormat ( " No se pudo calcular lote valido para el riesgo especificado = %.2f " , risk_per_operation ) , FUNCION_ACTUAL ) ;
return 0 ;
}
//--- Calculamos el stop loss en base al riesgo por operacion y al lote
const long spread = ( long ) SymbolInfoInteger ( symbol , SYMBOL_SPREAD ) ;
const double result = ( risk_per_operation / ( lot * ganancia_por_tick_real ) ) - spread - 1 ;
const long ideal_sl = long ( MathRound ( result ) ) ;
//--- Check SL
if ( ideal_sl < = 0 )
{
LogError ( StringFormat ( " Stop loss %i invalid, please increase the risk per trade %.2f " , ideal_sl , risk_per_operation ) , FUNCION_ACTUAL ) ;
return 0 ;
}
return ideal_sl ;
}
//+-----------------------------------------------------------------------+
//| Calculate the stop loss distance in points based on risk and lot size |
//+-----------------------------------------------------------------------+
long CGetLote : : MoneyToPoints ( const ENUM_ORDER_TYPE type , double money , double entry_price , double & chosen_lot , ulong deviation , ulong stop_limit )
{
chosen_lot = GetLoteByRiskPerOperation ( type , money , entry_price , deviation , stop_limit ) ;
if ( chosen_lot < = 0 )
{
LogError ( StringFormat ( " No se pudo calcular lote valido para el dinero especificado = %.2f " , money ) , FUNCION_ACTUAL ) ;
return 0 ;
}
long spread = ( long ) SymbolInfoInteger ( symbol , SYMBOL_SPREAD ) ;
double result = ( money / ( chosen_lot * ganancia_por_tick_real ) ) - spread - 1 ;
long ideal_sl = long ( MathRound ( result ) ) ;
if ( ideal_sl < = 0 )
{
LogError ( StringFormat ( " Stop loss %i invalid, please increase the money = %.2f " , ideal_sl , money ) , FUNCION_ACTUAL ) ;
return 0 ;
}
return ideal_sl ;
}
//+--------------------------------------------------------------------+
//| Function to obtain the ideal lot based on your risk per operation |
//+--------------------------------------------------------------------+
// risk_per_operation in USD, not %
double CGetLote : : GetLoteByRiskPerOperation ( ENUM_ORDER_TYPE type , double risk_per_operation , double entry_price , ulong deviation , ulong stop_limit )
{
double price = OrderTypeToPriceCalculator [ type ] ( entry_price , deviation , stop_limit , point_value , digits ) ;
double margin_free = AccountInfoDouble ( ACCOUNT_MARGIN_FREE ) ;
//---
if ( margin_free < = 0.00 )
{
LogError ( StringFormat ( " Free margin of %+.2f is invalid, you cannot open trades right now " , margin_free ) , FUNCION_ACTUAL ) ;
return 0.0 ;
}
//---
double margin = EMPTY_VALUE ;
const static double VOLUME = 1.0 ;
ResetLastError ( ) ;
2026-09-12 19:41:52 -05:00
if ( ! OrderCalcMargin ( MqlArticlesOrderTToOrder ( type ) , symbol , VOLUME , price , margin ) )
2026-09-06 20:42:03 -05:00
{
LogError ( StringFormat ( " OrderCalcMargin() failed. Error = %d " , GetLastError ( ) ) , FUNCION_ACTUAL ) ;
return 0.0 ;
}
//---
if ( risk_per_operation > = margin_free )
risk_per_operation = margin_free ;
//---
double result = MathFloor ( ( risk_per_operation / margin ) / volumeStep ) * volumeStep ;
if ( result < minLote )
{
LogError ( StringFormat ( " The lot %.2f is invalid, the risk %.2f increases, margin = %.2f, vol step = %.2f " ,
result , risk_per_operation , margin , volumeStep ) , FUNCION_ACTUAL ) ;
return result ;
}
if ( result > = maxLote )
return maxLote ;
if ( result > = volume_limit & & volume_limit > 0.00 )
return volume_limit ;
//---
LogInfo ( StringFormat ( " The ideal lot for %.2f risk per trade is %.2f lots, margin for 1 lote: %.2f " , risk_per_operation , result , margin ) , FUNCION_ACTUAL ) ;
return result ;
}
//+------------------------------------------------------------------+
double CGetLote : : GetMaxLote ( ENUM_ORDER_TYPE type , double entry_price , ulong deviation , ulong stop_limit )
{
//double price, ulong deviation, ulong stop_limit, double point_value, int8_t digits
double price = OrderTypeToPriceCalculator [ type ] ( entry_price , deviation , stop_limit , point_value , digits ) ;
double margin_free = AccountInfoDouble ( ACCOUNT_MARGIN_FREE ) ;
//---
if ( margin_free < = 0.00 )
{
LogError ( StringFormat ( " Free margin of %+.2f is invalid, you cannot open trades right now " , margin_free ) , FUNCION_ACTUAL ) ;
return 0.0 ;
}
//---
double margin = EMPTY_VALUE ;
ResetLastError ( ) ;
2026-09-12 19:41:52 -05:00
if ( ! OrderCalcMargin ( MqlArticlesOrderTToOrder ( type ) , symbol , 1.0 , price , margin ) )
2026-09-06 20:42:03 -05:00
{
LogError ( StringFormat ( " OrderCalcMargin() failed. Error = %d " , GetLastError ( ) ) , FUNCION_ACTUAL ) ;
return 0.0 ;
}
//---
double result = MathFloor ( ( margin_free / margin ) / volumeStep ) * volumeStep ;
//---
if ( result < minLote )
return 0.0 ;
if ( result > = maxLote )
return maxLote ;
if ( result > = volume_limit & & volume_limit > 0.00 )
return volume_limit ;
//---
LogInfo ( StringFormat ( " Lote maximo = %.2f >> precio = %.*f >> margen(1) = %.2f " , result , digits , entry_price , margin ) , FUNCION_ACTUAL ) ;
return result ;
}
//+------------------------------------------------------------------+
void CGetLote : : Update ( void )
{
this . volumeStep = SymbolInfoDouble ( symbol , SYMBOL_VOLUME_STEP ) ;
this . minLote = SymbolInfoDouble ( symbol , SYMBOL_VOLUME_MIN ) ;
this . maxLote = SymbolInfoDouble ( symbol , SYMBOL_VOLUME_MAX ) ;
this . volume_limit = SymbolInfoDouble ( symbol , SYMBOL_VOLUME_LIMIT ) ;
this . digits = ( int8_t ) SymbolInfoInteger ( symbol , SYMBOL_DIGITS ) ;
this . point_value = SymbolInfoDouble ( symbol , SYMBOL_POINT ) ;
//---
2026-09-12 19:41:52 -05:00
const ENUM_SYMBOL_CALC_MODE calc_mode = ( ENUM_SYMBOL_CALC_MODE ) SymbolInfoInteger ( symbol , SYMBOL_TRADE_CALC_MODE ) ; ;
2026-09-06 20:42:03 -05:00
switch ( calc_mode )
{
case SYMBOL_CALC_MODE_FOREX :
case SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE :
this . ganancia_por_tick_real = SymbolInfoDouble ( symbol , SYMBOL_TRADE_TICK_VALUE ) ;
break ;
case SYMBOL_CALC_MODE_CFD :
case SYMBOL_CALC_MODE_CFDINDEX :
case SYMBOL_CALC_MODE_CFDLEVERAGE :
this . ganancia_por_tick_real = SymbolInfoDouble ( symbol , SYMBOL_TRADE_CONTRACT_SIZE ) * SymbolInfoDouble ( symbol , SYMBOL_TRADE_TICK_SIZE ) ;
break ;
default :
this . ganancia_por_tick_real = SymbolInfoDouble ( symbol , SYMBOL_TRADE_CONTRACT_SIZE ) * SymbolInfoDouble ( symbol , SYMBOL_TRADE_TICK_SIZE ) ;
break ;
}
}
//+------------------------------------------------------------------+
void CGetLote : : SetSymbol ( string new_value )
{
this . symbol = new_value ;
Update ( ) ;
}
2026-09-12 19:41:52 -05:00
}
2026-09-06 20:42:03 -05:00
//+------------------------------------------------------------------+
# endif // MQLARTICLES_RM_LOTSIZECALC_MQH
//+------------------------------------------------------------------+