//+------------------------------------------------------------------+ //| MoneyFixedLot.mqh | //| Copyright 2000-2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "..\Expert\ExpertMoneyCustom.mqh" // wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Trading with fixed trade volume | //| Type=Money | //| Name=FixLot | //| Class=CMoneyFixedLot | //| Page= | //| Parameter=Percent,double,10.0,Percent | //| Parameter=Lots,double,0.1,Fixed volume | //+------------------------------------------------------------------+ // wizard description end //+------------------------------------------------------------------+ //| Class CMoneyFixedLot. | //| Purpose: Class of money management with fixed lot. | //| Derives from class CExpertMoney. | //+------------------------------------------------------------------+ class CMoneyFixedLot : public CExpertMoneyCustom { protected: //--- input parameters double m_lots; //--- declared ahead of the inline CheckOpenLong/Short bodies below that call it double CheckedLot(ENUM_ORDER_TYPE type); public: //--- void Lots(double lots) { m_lots=lots; } virtual bool ValidationSettings(void); //--- The fixed lot from the input is a REQUEST, not a guarantee: it still has to clear the //--- broker's volume grid, the per-symbol SYMBOL_VOLUME_LIMIT and free margin before it can be //--- sent (article 2555 #2/#3/#5). Returning m_lots raw - as this class used to - meant a //--- Money_FixLot_Lots that a small/loaded account cannot margin was handed straight to //--- OrderSend() and rejected server-side with "not enough money" every single signal. virtual double CheckOpenLong(double price,double sl) { return(CheckedLot(ORDER_TYPE_BUY)); } virtual double CheckOpenShort(double price,double sl) { return(CheckedLot(ORDER_TYPE_SELL)); } }; //+------------------------------------------------------------------+ //| Run the configured fixed lot through the shared pre-OrderSend | //| volume gate; 0.0 means "no legal volume right now", which | //| CExpert::OpenLong/OpenShort treat as "do not trade this tick". | //+------------------------------------------------------------------+ double CMoneyFixedLot::CheckedLot(ENUM_ORDER_TYPE type) { double lot = m_lots; string description; if(!ValidateLotForTrade(MoneySymbol(), lot, type, description)) return(0.0); return(lot); } //+------------------------------------------------------------------+ //| Validation settings protected data. | //+------------------------------------------------------------------+ bool CMoneyFixedLot::ValidationSettings(void) { if(!CExpertMoney::ValidationSettings()) return(false); //--- initial data checks if(m_lotsm_symbol.LotsMax()) { printf(__FUNCTION__+": lots amount must be in the range from %f to %f",m_symbol.LotsMin(),m_symbol.LotsMax()); return(false); } //--- article 2555 #10: LotsStep() is 0 until the broker syncs SYMBOL_VOLUME_STEP, and the //--- multiple-of-step test below divides by it twice if(m_symbol.LotsStep()<=0.0) { printf(__FUNCTION__+": SYMBOL_VOLUME_STEP for %s is not available yet (%f)",m_symbol.Name(),m_symbol.LotsStep()); return(false); } if(MathAbs(m_lots/m_symbol.LotsStep()-MathRound(m_lots/m_symbol.LotsStep()))>1.0E-10) { printf(__FUNCTION__+": lots amount is not corresponding with lot step %f",m_symbol.LotsStep()); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+