/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Class Library // -------------------------------------- // Name: XClass // Description: provides all classes for use ... // // // Maintainer: // ------------ // Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) // ////////////////////////////////////////////////////// // // Global Properties ... #property library #property copyright "Copyright 2023, SaherElm IT Center" #property link "https://www.saherelm.ir" #property version "1.00" #property strict // #include #include #include #include #include "x-saherelm.draw.lib.mq5" #include "x-saherelm.models.lib.mq5" #include "x-saherelm.common.lib.mq5" #include // // START Model Definitions ... // // string X_SIGNAL_TP = "TP"; string X_SIGNAL_SL = "SL"; string X_SIGNAL_TYPE = "TY"; string X_SIGNAL_TICKET = "T"; string X_SIGNAL_RISKFREE_STEP = "RFS"; string X_SIGNAL_RISKFREE_RATE = "RFR"; // struct XTraderHandlerResult { // // List Of Closed On SL Signals ... XSignal sl[]; // // List Of Closed On TP Signals ... XSignal tp[]; // // List Of Risk Free Signals ... XSignal rf[]; }; // // END Model Definitions ... // // // START Overrides ... // class XSCTrade : public CTrade { public: // // Modify Specific Position, by Specific Comment ... bool PositionModify( const string symbol, // Symbol const double sl, // Stop Loss const double tp, // Take Profit const string comment = "" // Comment For Modify ) { // // check stopped if (IsStopped(__FUNCTION__)) { return (false); } // // check position existence if (!SelectPosition(symbol)) { return (false); } // // clean ClearStructures(); // // setting request m_request.action = TRADE_ACTION_SLTP; m_request.symbol = symbol; m_request.magic = m_magic; m_request.sl = sl; m_request.tp = tp; m_request.position = PositionGetInteger(POSITION_TICKET); // // Specified Comment ... if (StringLen(comment) > 0) { m_request.comment = comment; } // // action and return the result return (OrderSend(m_request, m_result)); } bool PositionModify( const ulong ticket, // Position Ticket const double sl, // Stop Loss const double tp, // Take Profit const string comment = "" // Comment For Modify ) { // // check stopped if (IsStopped(__FUNCTION__)) { return (false); } // // check position existence if (!PositionSelectByTicket(ticket)) { return (false); } // // clean ClearStructures(); // // setting request m_request.action = TRADE_ACTION_SLTP; m_request.position = ticket; m_request.symbol = PositionGetString(POSITION_SYMBOL); m_request.magic = m_magic; m_request.sl = sl; m_request.tp = tp; // // Specified Comment ... if (StringLen(comment) > 0) { m_request.comment = comment; } // // action and return the result return (OrderSend(m_request, m_result)); } // // Close Specific Position, by Specific Comment ... bool PositionClose( const string symbol, // Symbol const ulong deviation, // Deviation const string comment = "" // Comment For Close ) { // bool partial_close = false; int retry_count = 10; uint retcode = TRADE_RETCODE_REJECT; // // check stopped if (IsStopped(__FUNCTION__)) { return (false); } // // clean ClearStructures(); // // check filling if (!FillingCheck(symbol)) { return (false); } // do { // // check if (SelectPosition(symbol)) { // if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { // // prepare request for close BUY position m_request.type = ORDER_TYPE_SELL; m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID); } else { // // prepare request for close SELL position m_request.type = ORDER_TYPE_BUY; m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK); } } else { // // position not found m_result.retcode = retcode; return (false); } // // setting request m_request.action = TRADE_ACTION_DEAL; m_request.symbol = symbol; m_request.volume = PositionGetDouble(POSITION_VOLUME); m_request.magic = m_magic; m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation; m_request.position = PositionGetInteger(POSITION_TICKET); // // Specify Comment ... m_request.comment = comment; // // check volume double max_volume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); if (m_request.volume > max_volume) { // m_request.volume = max_volume; partial_close = true; } else { partial_close = false; } // // hedging? just send order if (IsHedging()) { return (OrderSend(m_request, m_result)); } // // order send if (!OrderSend(m_request, m_result)) { // if (--retry_count != 0) { continue; } // if (retcode == TRADE_RETCODE_DONE_PARTIAL) { m_result.retcode = retcode; } // return (false); } // //--- WARNING. If position volume exceeds the maximum volume allowed for deal, //--- and when the asynchronous trade mode is on, for safety reasons, position is closed not completely, //--- but partially. It is decreased by the maximum volume allowed for deal. if (m_async_mode) { break; } // retcode = TRADE_RETCODE_DONE_PARTIAL; if (partial_close) { Sleep(1000); } } while (partial_close); // // succeed return (true); } bool PositionClose( const ulong ticket, // Position Ticket const ulong deviation, // Deviation const string comment = "" // Comment For Close ) { // // check stopped if (IsStopped(__FUNCTION__)) { return (false); } // // check position existence if (!PositionSelectByTicket(ticket)) { return (false); } // string symbol = PositionGetString(POSITION_SYMBOL); // // clean ClearStructures(); // // check filling if (!FillingCheck(symbol)) { return (false); } // // check if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { // // prepare request for close BUY position m_request.type = ORDER_TYPE_SELL; m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID); } else { // // prepare request for close SELL position m_request.type = ORDER_TYPE_BUY; m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK); } // // setting request m_request.action = TRADE_ACTION_DEAL; m_request.position = ticket; m_request.symbol = symbol; m_request.volume = PositionGetDouble(POSITION_VOLUME); m_request.magic = m_magic; m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation; // // Specify Comment ... m_request.comment = comment; // // close position return (OrderSend(m_request, m_result)); } // // Partial Close Specific Position, by Specific Comment ... bool PositionClosePartial( const string symbol, // Symbol const double volume, // Partial Closing Volume const ulong deviation, // Deviation const string comment = "" // Comment For Close ) { // uint retcode = TRADE_RETCODE_REJECT; // // check stopped if (IsStopped(__FUNCTION__)) { return (false); } // // for hedging mode only if (!IsHedging()) { return (false); } // // clean ClearStructures(); // // check filling if (!FillingCheck(symbol)) { return (false); } // // check if (SelectPosition(symbol)) { // if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { // // prepare request for close BUY position m_request.type = ORDER_TYPE_SELL; m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID); } else { // // prepare request for close SELL position m_request.type = ORDER_TYPE_BUY; m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK); } } else { // // position not found m_result.retcode = retcode; return (false); } // // check volume double position_volume = PositionGetDouble(POSITION_VOLUME); if (position_volume > volume) { position_volume = volume; } // // setting request m_request.action = TRADE_ACTION_DEAL; m_request.symbol = symbol; m_request.volume = position_volume; m_request.magic = m_magic; m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation; m_request.position = PositionGetInteger(POSITION_TICKET); // // Specifieng Comment ... m_request.comment = comment; // // hedging? just send order return (OrderSend(m_request, m_result)); } bool PositionClosePartial( const ulong ticket, // Position Ticket const double volume, // Partial Closing Volume const ulong deviation, // Deviation const string comment = "" // Comment For Close ) { // // check stopped if (IsStopped(__FUNCTION__)) { return (false); } // // for hedging mode only if (!IsHedging()) { return (false); } // // check position existence if (!PositionSelectByTicket(ticket)) { return (false); } // string symbol = PositionGetString(POSITION_SYMBOL); // // clean ClearStructures(); // // check filling if (!FillingCheck(symbol)) { return (false); } // // check if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { // // prepare request for close BUY position m_request.type = ORDER_TYPE_SELL; m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID); } else { // // prepare request for close SELL position m_request.type = ORDER_TYPE_BUY; m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK); } // // check volume double position_volume = PositionGetDouble(POSITION_VOLUME); if (position_volume > volume) { position_volume = volume; } // // setting request m_request.action = TRADE_ACTION_DEAL; m_request.position = ticket; m_request.symbol = symbol; m_request.volume = position_volume; m_request.magic = m_magic; m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation; // // Specifieng Comment ... m_request.comment = comment; // // close position return (OrderSend(m_request, m_result)); } }; // // END Overrides ... // // // XSaherElm Account Info Class ... class XCAccountInfo { // // Public properties ... public: // // Constructro ... void XCAccountInfo() { // mAccountInfo = new CAccountInfo(); // mInitialBalance = mAccountInfo.Balance(); } // // Deconstructor ... void ~XCAccountInfo() { // mInitialBalance = 0; } // // User Account ... long GetUserAccount() { return mAccountInfo.Login(); } // // Account Leverage ... long GetLeverage() { return mAccountInfo.Leverage(); } // // Get Trade Expert State ... bool CanExpertTrade() { return mAccountInfo.TradeExpert(); } // // User Account Trade Mopde (ENUM_ACCOUNT_TRADE_MODE) ... ENUM_ACCOUNT_TRADE_MODE GetTradeMode() { return mAccountInfo.TradeMode(); } // // Get Account Balance ... double GetBalance() { return mAccountInfo.Balance(); } // // Get Initial Deposit Balance ... double GetInitialBalance() { return mInitialBalance; } // // Get the amount of give Credit ... double GetCredit() { return mAccountInfo.Credit(); } // // Get the amount of current Profit on account ... double GetProfit() { return mAccountInfo.Profit(); } // // Get the amount of current Equity on account ... double GetEquity() { return mAccountInfo.Equity(); } // // Get the amount of reserved Margin ... double GetMargin() { return mAccountInfo.Margin(); } // // Get the amount of free Margin ... double GetFreeMargin() { return mAccountInfo.FreeMargin(); } // // Get the Level of Margin ... double GetMarginLevel() { return mAccountInfo.MarginLevel(); } // // Get the Level Of Margin for a Deposit ... double GetMarginCall() { return mAccountInfo.MarginCall(); } // // Get the Level of Margin for Stop out ... double GetMarginStopOut() { return mAccountInfo.MarginStopOut(); } // // Get the Client Name ... string GetName() { return mAccountInfo.Name(); } // // Get the Trade Server Name ... string GetServerName() { return mAccountInfo.Server(); } // // Get deposit Currency Name ... string GetCurrency() { return mAccountInfo.Currency(); } // // Get the Company Name that serves an Account ... string GetCompany() { return mAccountInfo.Company(); } // // Calculate Profits for the current account based on passed parameters ... double CalculateTradeProfit( const string symbol, // trading symbol ENUM_ORDER_TYPE type, // order type double volume, // volume double entry, // open price double exit // close price ) { // double result = mAccountInfo.OrderProfitCheck( symbol, type, volume, entry, exit); // return result; } // // Calculate amount of margin which required for trade operation ... double CalculateMarging( const string symbol, // trading symbol ENUM_ORDER_TYPE type, // order type double volume, // volume double entry // open price ) { // double result = mAccountInfo.MarginCheck( symbol, type, volume, entry); // return result; } // // Calculate amount of free margin left after trade operation ... double CalculateFreeMarging( const string symbol, // trading symbol ENUM_ORDER_TYPE type, // order type double volume, // volume double entry // open price ) { // double result = mAccountInfo.FreeMarginCheck( symbol, type, volume, entry); // return result; } // // Calculate the Maximum possible volume of trade operation ... double CalculateMaxVolume( const string symbol, // trading symbol ENUM_ORDER_TYPE type, // order type double entry, // open price double percent = 100 // percent of available margin ) { // double result = mAccountInfo.MaxLotCheck( symbol, type, entry, percent); // return result; } // // Protected properties ... protected: // // Private properties ... private: // // Initial Account Balance ... double mInitialBalance; // // Account Info ... CAccountInfo mAccountInfo; }; // // START Global Definitions: Variables, Properties and etc ... // // // XSaherElm Trade Class ... class XCTrade { // // all public features ... public: // // Constructor ... void XCTrade( string tag, // Specify a Tag for Trader instance string symbol, // Specify Trader Symbol int slippage, // Specify Slippage ulong magicNumber, // Specify Magic Number int maxAllowedTrades, // Specify Max Allowed Trades double minBallanceForOpenTrades, // Specify Minimum Balance for Open Trades double minAllowedFreeMarginForOpenTrades // Specify Minimum Free Marging for Open Trades ) { // mTag = tag; mSymbol = symbol; mSlippage = slippage; mMagicNumber = magicNumber; mMaxAllowedTrades = maxAllowedTrades; mMinBallanceForOpenTrades = minBallanceForOpenTrades; mMinAllowedFreeMarginForOpenTrades = minAllowedFreeMarginForOpenTrades; // mTrader = new XSCTrade(); // mTrader.SetAsyncMode(false); mTrader.SetDeviationInPoints(mSlippage); mTrader.SetExpertMagicNumber(mMagicNumber); } // // Deconstructor ... void ~XCTrade() { } // // START Count ... // // // Count Open Positions ... int Count() { // int result = 0; // int total = PositionsTotal(); for (int i = 0; i < total; i++) { // if (!mPositionInfo.SelectByIndex(i)) { continue; } // if (mPositionInfo.Magic() != mMagicNumber) { continue; } // if (mPositionInfo.Symbol() != mSymbol) { continue; } // result++; } // return result; } // // Count Longs ... int CountLongs() { int result = CountByType(X_SIGNAL_LONG); return result; } // // Count Shorts ... int CountShorts() { int result = CountByType(X_SIGNAL_SHORT); return result; } // // Count Specific Type Of Positions ... int CountByType(ENUM_POSITION_TYPE type) { // int result = 0; // int total = PositionsTotal(); for (int i = 0; i < total; i++) { // if (!mPositionInfo.SelectByIndex(i)) { continue; } // if (mPositionInfo.Symbol() != mSymbol) { continue; } // if (mPositionInfo.PositionType() != type) { continue; } // result++; } // return result; } // // Count By Type ... int CountByType(ENUM_X_SIGNAL_TYPE type) { // // Validate Args ... ENUM_POSITION_TYPE mType = POSITION_TYPE_BUY; if (type == X_SIGNAL_LONG) { mType = POSITION_TYPE_BUY; } else if (type == X_SIGNAL_SHORT) { mType = POSITION_TYPE_SELL; } // int result = CountByType(mType); return result; } // // END Count ... // // // START Retrieve ... // // // Retrieve Specified Position by it's Ticket ... XSignal GetSignal( ulong ticket // Specified Ticket ) { // XSignal result = {}; // result = PositionToSignal(ticket); // return result; } // // Retrieve All Positions ... void GetSignals( XSignal &result[] // Holds Result ... ) { // CleanBuffer(result); // int totalPositions = PositionsTotal(); for (int i = 0; i < totalPositions; i++) { // if (!mPositionInfo.SelectByIndex(i)) { continue; } // if (mPositionInfo.Magic() != mMagicNumber) { continue; } // if (mPositionInfo.Symbol() != mSymbol) { continue; } // // Position to Signal ... XSignal signal = PositionToSignal(i); Add( signal, result); } } // // Retrieve Type Specific Signals ... void GetSignals( XSignal &result[], // Holds Result ... ENUM_X_SIGNAL_TYPE type // Specified Sginal Type ... ) { // CleanBuffer(result); // XSignal signals[]; GetSignals(signals); // int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // // Loop ... for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // if (type == X_SIGNAL_UNKNOWN) { Add( iSignal, result); } else if (iSignal.type == type) { Add( iSignal, result); } } } // // Get All Trades Which Candle Passed after Open ... void GetOldSignals( XSignal &result[], // Holds Result int life, // Max Candle Passed after Trades Open ENUM_TIMEFRAMES period = NULL, // Calculate Candles based on time frame ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions ) { // CleanBuffer(result); // // Normalize Period ... if (period == NULL) { period = _Period; } // XSignal signals[]; GetSignals( signals, type); int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // // Current Time Frame Candle Index ... int currentBarIndex = 0; // // Loop through Positions ... for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // // Retrieve Trade Open Bar Index based on Current Period ... int tradeOpenBarIndex = iBarShift( mSymbol, period, iSignal.time); // // Calculate Trade Life ... int diff = MathAbs(currentBarIndex - tradeOpenBarIndex); // bool isPassed = diff >= life; if (isPassed) { // Add( iSignal, result); } } } // // Get In Profit Signals ... void GetInProfitSignals( XSignal &result[], // Holds Result double minProfit = 0, // Minimum Profit To Close Trades ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions ) { // CleanBuffer(result); // XSignal signals[]; GetSignals( signals, type); int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // // Loop ... for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // bool isPassed = minProfit <= 0 && iSignal.profit > 0 ? true : iSignal.profit >= minProfit; if (isPassed) { Add( iSignal, result); } } } // // Get In Drawdown Signals ... void GetInDrawdownSignals( XSignal &result[], // Holds Result double maxDrawDown = 0, // Minimum Profit To Close Trades ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions ) { // CleanBuffer(result); // XSignal signals[]; GetSignals( signals, type); int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // // Loop ... for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // bool isPassed = maxDrawDown <= 0 && iSignal.profit < 0 ? true : iSignal.profit >= (-1 * maxDrawDown); if (isPassed) { Add( iSignal, result); } } } // // END Retrieve ... // // // START Signal Execution Actions ... // // // Execute a Signal in Market ... ENUM_EXECUTION_SIGNAL_RESULT ExecuteSignal( XSignal &signal, // Specified Signal bool ignoreTPSL = false, // Ignore Signal TP and SL for Virtal Handling bool ignoreNumberOfTraes = true, // Ignore Number of Trades bool checkAccountConditions = true // Check Account Balance, Equity and Free Margin for Signal Execution ) { // ENUM_EXECUTION_SIGNAL_RESULT result = X_TRADER_UNKNOWN_ERROR; // // Validate Signal ... bool isValid = IsValidSignal(signal); if (!isValid) { // result = X_TRADER_INVALID_SIGNAL_ERROR; return result; } // // Check Account Equity ... bool isEquityPassed = !checkAccountConditions ? true : IsEquityReadyForTrade(); if (!isEquityPassed) { // result = X_TRADER_NOT_ENOUGH_EQUITY_ERROR; return result; } // // Check Account Balance ... bool isBalancePassed = !checkAccountConditions ? true : IsBalanceReadyForTrade(); if (!isBalancePassed) { // result = X_TRADER_NOT_ENOUGH_BALANCE_ERROR; return result; } // // Check Account FreeMargin ... bool isFreeMarginPassed = !checkAccountConditions ? true : IsFreeMarginReadyForTrade(); if (!isFreeMarginPassed) { // result = X_TRADER_NOT_ENOUGH_MARIGIN_ERROR; return result; } // // Check Number of Open Trades ... int openTradesCount = Count(); bool isTradeCountPassed = ignoreNumberOfTraes ? true : openTradesCount < mMaxAllowedTrades; if (!isTradeCountPassed) { // result = X_TRADER_MAX_TRADES_REACHED_ERROR; return result; } // bool isExecuted = signal.type == X_SIGNAL_LONG ? Buy( signal.volume, signal.entry, ignoreTPSL ? 0 : signal.sl, ignoreTPSL ? 0 : signal.tp, signal.comment) : signal.type == X_SIGNAL_SHORT ? Sell( signal.volume, signal.entry, ignoreTPSL ? 0 : signal.sl, ignoreTPSL ? 0 : signal.tp, signal.comment) : false; if (isExecuted) { // ulong ticket = PositionGetTicket(PositionsTotal() - 1); // signal.id = ticket; signal.ticket = ticket; // AddOrUpdateSignalInfo(signal); // result = X_TRADER_SUCCEED_EXECUTION; } // return result; } // // This Function, Handling Following Works on Open Positions: // - Close On SL if Reached ... // - Close On TP if Reached ... // - Make Risk Free Trades ... void HandleSignals( XTraderHandlerResult &result, // Holds Result bool ignoreTPSL = true, // Ignore Signal TP and SL for Virtal Handling bool ignoreRiskFree = false // Ignore Risk Free Trdaes ) { // // Cleanup Result ... CleanBuffer(result.sl); CleanBuffer(result.tp); CleanBuffer(result.rf); // // Reading Open Positions ... XSignal signals[]; GetSignals(signals); int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // double deviation = mSlippage * GetPoints(mSymbol); // // Loop ... for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // double entry = GetEntry(iSignal.symbol, iSignal.type); double exit = GetExit(iSignal.symbol, iSignal.type); double appliedTPPrice = iSignal.tp > 0 ? iSignal.type == X_SIGNAL_LONG ? iSignal.tp - deviation : iSignal.tp + deviation : 0; double appliedSLPrice = iSignal.sl > 0 ? iSignal.type == X_SIGNAL_LONG ? iSignal.sl + deviation : iSignal.sl - deviation : 0; // // Handle SL ... if ( // !ignoreTPSL // && // iSignal.sl > 0 // && // exit <= appliedSLPrice // ) { // string comment = GenerateSignalSLComment(iSignal); // bool isClosed = Close( iSignal.ticket, comment); if (isClosed) { Add( iSignal, result.sl); } } // // Handle TP ... if ( // !ignoreTPSL // && // iSignal.tp > 0 // && // exit >= appliedTPPrice // ) { // string comment = GenerateSignalTPComment(iSignal); // bool isClosed = Close( iSignal.ticket, comment); if (isClosed) { Add( iSignal, result.tp); } } // // Handle Risk Free ... if ( // !ignoreRiskFree // && // iSignal.riskFreeStep > 0 // && // iSignal.riskFreeRate > 0 // ) { // // Base Price is ENTRY, then Last Level, in each level SL is Last Level ... int level = 1; bool isLong = iSignal.type == X_SIGNAL_LONG; double stepPrice = PipsToPrice(iSignal.riskFreeStep); double basePrice = isLong ? iSignal.sl > iSignal.entry ? iSignal.sl : iSignal.entry : iSignal.sl < iSignal.entry ? iSignal.sl : iSignal.entry; double priceLevel = (exit - basePrice) / stepPrice; // if (priceLevel > 1) { // while (priceLevel > level && priceLevel > level + 1) { level++; } // double baseRiskFreeLevelExitPrice = (level * stepPrice); double riskFreeLevelExitPrice = isLong ? (basePrice + baseRiskFreeLevelExitPrice) - deviation : (basePrice - baseRiskFreeLevelExitPrice) + deviation; // bool canRiskFree = isLong ? exit >= riskFreeLevelExitPrice : exit <= riskFreeLevelExitPrice; // if (canRiskFree) { // double volume = iSignal.volume; double riskFreeRate = iSignal.riskFreeRate; double riskFreeVolume = riskFreeRate * volume; riskFreeVolume = NormalizeVolume(riskFreeVolume); // double spread = GetSpread(iSignal.symbol); double slDistance = spread; double sl = isLong ? riskFreeLevelExitPrice - slDistance : riskFreeLevelExitPrice + slDistance; // double tp = iSignal.tp += stepPrice; // string comment = GenerateSignalRFComment(iSignal, (int)level); // ResetLastError(); // bool isClosedPartial = ClosePartial( iSignal.ticket, riskFreeVolume, comment); // if (isClosedPartial) { // bool isModified = Modify( iSignal.ticket, sl, tp); // if (isModified) { // Add( iSignal, result.rf); // AddOrUpdateSignalInfo(iSignal); } } } } // // // // Check TP Step Price ... // double stepPrice = PipsToPrice(iSignal.riskFreeStep); // double level = MathAbs(exit - iSignal.entry) / stepPrice; // double roundedLevel = MathRound(level); // if (level > 1 && (roundedLevel - level) < 0) // { // // // level = roundedLevel; // // // double basePrice = iSignal.sl > 0 ? iSignal.sl : iSignal.entry; // double step = (level * stepPrice); // step = // iSignal.type == X_SIGNAL_LONG // ? (basePrice + step) - deviation // : (basePrice - step) + deviation; // // // bool canRiskFree = // iSignal.type == X_SIGNAL_LONG // ? exit >= step // : exit <= step; // // // if (canRiskFree) // { // } // } } } } // // Close All Trades ... void CloseSignals( XSignal &result[], // Holds Result ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions ) { // CleanBuffer(result); // XSignal signals[]; GetSignals( signals, type); int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // string comment = GenerateSignalForceCloseComment(iSignal); // bool isClosed = Close( iSignal.ticket, comment); if (isClosed) { Add( iSignal, result); } } } // // Close Pyramid Signals ... void CloseInPyramidSignals( XSignal &result[], // Holds Result double pyramid = 0, // Close Pyramid Profit ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions ) { // CleanBuffer(result); // XSignal signals[]; GetSignals( signals, type); int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // double profit = 0; for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // profit += iSignal.profit; } // // Normalize Pyramid ... if (pyramid < 0) { pyramid = 0; } // if (profit > pyramid) { CloseSignals(result); } } // // Close All Trades Which Candle Passed after Open ... void CloseOldSignals( XSignal &result[], // Holds Result int life, // Max Candle Passed after Trades Open ENUM_TIMEFRAMES period = NULL, // Calculate Candles based on time frame ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions ) { // CleanBuffer(result); // XSignal signals[]; GetOldSignals( signals, life, period, type); int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // string comment = GenerateSignalAgeComment(iSignal); // bool isClosed = Close( iSignal.ticket, comment); if (isClosed) { Add( iSignal, result); } } } // // Close In Profit Signals ... void CloseInProfitSignals( XSignal &result[], // Holds Result double minProfit = 0, // Minimum Profit To Close Trades ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions ) { // CleanBuffer(result); // XSignal signals[]; GetInProfitSignals( signals, minProfit, type); int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // string comment = GenerateSignalProfitComment(iSignal); // bool isClosed = Close( iSignal.ticket, comment); if (isClosed) { Add( iSignal, result); } } } // // Close In Drawdown Signals ... void CloseInDrawdownSignals( XSignal &result[], // Holds Result double maxDrawDown = 0, // Minimum Profit To Close Trades ENUM_X_SIGNAL_TYPE type = X_SIGNAL_UNKNOWN // Specify Type of Positions ) { // CleanBuffer(result); // XSignal signals[]; GetInDrawdownSignals( signals, maxDrawDown, type); int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // string comment = GenerateSignalProfitComment(iSignal); // bool isClosed = Close( iSignal.ticket, comment); if (isClosed) { Add( iSignal, result); } } } // // END Signal Execution Actions ... // // // START Actions ... // // // Force Open a Buy/Long Position ... bool Buy( double volume, // position volume double price, // execution price double sl = 0, // stop loss price double tp = 0, // take profit price const string comment = "" // comment ) { // bool result = false; // result = mTrader.Buy( volume, mSymbol, price, sl, tp, comment); // return result; } // // Force Open a Sell/Short Position ... bool Sell( double volume, // position volume double price, // execution price double sl = 0, // stop loss price double tp = 0, // take profit price const string comment = "" // comment ) { // bool result = false; // result = mTrader.Sell( volume, mSymbol, price, sl, tp, comment); // return result; } // // Modify Position ... bool Modify( const ulong ticket, // position ticket double sl = 0, // stop loss price double tp = 0, // take profit const string comment = "" // comment ) { // bool result = false; // // Validate Args ... if (tp <= 0 && sl <= 0) { result = false; return result; } // result = mTrader.PositionModify( ticket, sl, tp, comment); // return result; } // // Close Position By Ticket ... bool Close( ulong ticket, // Position Ticket string comment = "" // Close Position By Specific Comment ) { // bool result = false; // // Try to Select Position ... if (!mPositionInfo.SelectByTicket(ticket)) { return result; } // // Close Position By Specific Comment ... result = mTrader.PositionClose( ticket, mSlippage, comment); if (result) { RemoveSignal(ticket); } // return result; } // // Close Partial By Ticket ... bool ClosePartial( ulong ticket, // Position Ticket double volume, // Volume Size for Partial Closing string comment = "" // Close Position By Specific Comment ) { // bool result = false; // if (!mPositionInfo.SelectByTicket(ticket)) { return result; } // result = mTrader.PositionClosePartial( ticket, volume, mSlippage, comment); // return result; } // // END Actions ... // // // START Account Related ... // // // Check Account Has Minimum Balance For Trade ... bool IsBalanceReadyForTrade() { // bool result = false; // result = mAccountInfo.GetBalance() >= mMinBallanceForOpenTrades; // return result; } // // Check Free Margin Has Enough For Trade ... bool IsFreeMarginReadyForTrade() { // bool result = false; // double freeMargin = mAccountInfo.GetFreeMargin(); // result = freeMargin >= mMinAllowedFreeMarginForOpenTrades; // return result; } // // Check Equity Has Enough For Trade ... bool IsEquityReadyForTrade() { // bool result = false; // double equity = mAccountInfo.GetEquity(); // result = equity >= mMinAllowedFreeMarginForOpenTrades; // return result; } // // END Account Related ... // // // START Signal Related ... // // // Validate Signal ... bool IsValidSignal( XSignal &signal // Specified Signal for Execution ) { // bool result = false; // // Validate Signal Type ... result = signal.type != X_SIGNAL_UNKNOWN; if (!result) { return result; } // // Check Symbol ... result = StringLen(signal.symbol) == 0 ? true : signal.symbol == mSymbol; if (!result) { return result; } // // Check magic number ... result = signal.magicNumber <= 0 ? true : signal.magicNumber == mMagicNumber; if (!result) { return result; } // // Check Entry ... result = signal.entry > 0; if (!result) { return result; } // // Check SL ... result = signal.sl == 0 ? true : signal.type == X_SIGNAL_LONG ? signal.sl < signal.entry : signal.sl > signal.entry; if (!result) { return result; } // // Check TP ... result = signal.tp == 0 ? true : signal.type == X_SIGNAL_LONG ? signal.tp > signal.entry : signal.tp < signal.entry; if (!result) { return result; } // // Check Volume ... result = signal.volume > 0; if (!result) { return result; } // // Check Time ... result = signal.time > 0; if (!result) { return result; } // return result; } // // Get Signal InDirection (Reversal) Type ... ENUM_X_SIGNAL_TYPE GetSignalIndirectionType( XSignal &signal // Specified Signal ) { // ENUM_X_SIGNAL_TYPE result = X_SIGNAL_UNKNOWN; // switch (signal.type) { // case X_SIGNAL_LONG: result = X_SIGNAL_SHORT; break; // case X_SIGNAL_SHORT: result = X_SIGNAL_LONG; break; } // return result; } // // Calculate SignalAge ... int GetSignalAge( datetime time // Signal Execution Time ) { // int result = 0; // // Validate Arg ... if (time <= 0) { return result; } // int sIndex = iBarShift( mSymbol, _Period, time); // datetime cTime = iTime( mSymbol, _Period, 0); int cIndex = iBarShift( mSymbol, _Period, cTime); // result = MathAbs(cIndex - sIndex); // return result; } int GetSignalAge( datetime time, // Signal Execution Time ENUM_TIMEFRAMES period // Specify Time Frame ) { // int result = 0; // // Validate Arg ... if (time <= 0) { return result; } // int sIndex = iBarShift( mSymbol, period, time); // datetime cTime = iTime( mSymbol, period, 0); int cIndex = iBarShift( mSymbol, _Period, cTime); // result = MathAbs(cIndex - sIndex); // return result; } int GetSignalAge( XSignal &signal // Specified Signal ) { // int result = 0; // // Validate Arg ... if ( signal.time <= 0 || !IsValid(signal, mMagicNumber)) { return result; } // result = GetSignalAge(signal.time); // return result; } int GetSignalAge( XSignal &signal, // Specify Signal ENUM_TIMEFRAMES period // Specify Time Frame ) { // int result = 0; // // Validate Arg ... if ( signal.time <= 0 || !IsValid(signal, mMagicNumber)) { return result; } // result = GetSignalAge( signal.time, period); // return result; } // // Prepare a Signall ... XSignal GenerateSignal( ENUM_X_SIGNAL_TYPE type, // Signal Type double tp = 0, // Take Profit double sl = 0, // Stop Loss double volume = 0, // Volume double riskFreeStep = 0, // Risk Free Step Price double riskFreeRate = 0 // Risk Free Rate ) { // XSignal result = {}; // // Validate Arg ... if (type == X_SIGNAL_UNKNOWN) { return result; } // // Check Signal Type ... bool isLong = type == X_SIGNAL_LONG; // // Retrieve entry Price ... double entry = GetEntry(mSymbol, type); double spread = GetSpread(mSymbol); // result.tp = tp; result.sl = sl; result.type = type; result.entry = entry; result.symbol = mSymbol; result.magicNumber = mMagicNumber; result.riskFreeStep = riskFreeStep; result.riskFreeRate = riskFreeRate; result.time = iTime(mSymbol, _Period, 0); result.volume = NormalizeVolume(volume, mSymbol); // // Since we Use signal instance to Generate Comment, this must be at the end ... result.comment = GenerateSignalComment(result); // return result; } // // Generate Comments for Specific Signal ... string GenerateSignalComment( ENUM_X_SIGNAL_TYPE type, // Specify Kind Signal Type double volume // Signal Volume ) { // string result = ""; // string typeStr = GetSignalType(type); if ( volume <= 0 || StringLen(typeStr) == 0) { return result; } // volume = NormalizeVolume(volume, mSymbol); // result = mTag + "_" + typeStr; // return result; } string GenerateSignalComment( XSignal &signal // Specified Signal ) { // string result = GenerateSignalComment( signal.type, signal.volume // ); // return result; } // // Generate Signal RF TP Comment ... string GenerateSignalRFComment( XSignal &signal, // Specified Signal int level // Which Step of RFS ) { // string result = ""; // // Validate Signal and it's Ticket ... if (!IsValidSignal(signal) || signal.ticket <= 0) { return result; } // double exit = GetExit(signal.symbol, signal.type); // result = // X_SIGNAL_RISKFREE_STEP + "(" + (string)signal.ticket + ")_" + (string)level + ": " + string(exit); // ; // return result; } // // Generate Signal TP Comment ... string GenerateSignalTPComment( XSignal &signal // Specified Signal ) { // string result = ""; // // Validate Signal and it's Ticket ... if (!IsValidSignal(signal) || signal.ticket <= 0) { return result; } // double exit = GetExit(signal.symbol, signal.type); // result = // X_SIGNAL_TP + "(" + (string)signal.ticket + "): " + string(exit); // ; // return result; } // // Generate Signal SL Comment ... string GenerateSignalSLComment( XSignal &signal // Specified Signal ) { // string result = ""; // // Validate Signal and it's Ticket ... if (!IsValidSignal(signal) || signal.ticket <= 0) { return result; } // double exit = GetExit(signal.symbol, signal.type); // result = // X_SIGNAL_SL + "(" + (string)signal.ticket + "): " + string(exit); // ; // return result; } // // Generate Signal Profit Comment ... string GenerateSignalProfitComment( XSignal &signal // Specified Signal ) { // string result = ""; // // Validate Signal and it's Ticket ... if (!IsValidSignal(signal) || signal.ticket <= 0) { return result; } // result = // "Profit(" + (string)signal.ticket + "): " + string(signal.profit); // ; // return result; } // // Generate Signal Force Close Comment ... string GenerateSignalForceCloseComment( XSignal &signal // Specified Signal ) { // string result = ""; // // Validate Signal and it's Ticket ... if (!IsValidSignal(signal) || signal.ticket <= 0) { return result; } // result = // "FC(" + (string)signal.ticket + "): " + string(signal.profit); // ; // return result; } // // Generate Signal Age Comment ... string GenerateSignalAgeComment( XSignal &signal // Specified Signal ) { // string result = ""; // // Validate Signal and it's Ticket ... if (!IsValidSignal(signal) || signal.ticket <= 0) { return result; } // int age = GetSignalAge(signal); // result = // "(" + (string)signal.ticket + ") A: " + (string)age + ", P:" + string(signal.profit); // ; // return result; } // // Generate a Signal Object name for Draw Events On Chart ... string GenerateSignalObjectName( ulong ticket // Specify Ticket ) { // string result = ""; // // Validate Arg ... if (ticket <= 0) { return result; } // // TICKET ... // it is very importants since they hold position info ... result += "T(" + (string)ticket + ")"; // return result; } string GenerateSignalObjectName( XSignal &signal // Specified Signal ) { // string result = ""; // // Validate Arg ... if (!IsValid(signal, mMagicNumber)) { return result; } // result = GenerateSignalObjectName(signal.ticket); // return result; } // // Genrate a Signal Event Object Name ... string GenerateSignalEventObjectName( ulong ticket // Specify Ticket ) { // string result = ""; // result = GenerateSignalObjectName(ticket) + "_Event"; // return result; } string GenerateSignalEventObjectName( XSignal &signal // Specified Signal ) { // string result = ""; // result = GenerateSignalEventObjectName(signal.ticket); // return result; } // // Convert a Signal to String Representation ... string ToString( XSignal &signal // Specified Signal ) { // string result = ""; // if (!IsValid(signal, mMagicNumber) || signal.ticket <= 0) { return result; } // // TICKET ... // it is very importants since they hold position info ... result += X_SIGNAL_TICKET + "(" + (string)signal.ticket + ")"; // // TYPE ... string typeStr = GetSignalType(signal.type); result += "_" + X_SIGNAL_TYPE + "(" + typeStr + ")"; // // TP ... result += "_" + X_SIGNAL_TP + "(" + (string)signal.tp + ")"; // // SL ... result += "_" + X_SIGNAL_SL + "(" + (string)signal.sl + ")"; // // RFS ... result += "_" + X_SIGNAL_RISKFREE_STEP + "(" + (string)signal.riskFreeStep + ")"; // // RFR ... result += "_" + X_SIGNAL_RISKFREE_RATE + "(" + (string)signal.riskFreeRate + ")"; // return result; } // // Convert an string to signal ... XSignal FromString( string represent // String Represenation of a Signal ) { // XSignal result = {}; // // Validate Arg ... if (StringLen(represent) == 0) { return result; } // // TICKET ... string ticketStr = ExtractString( represent, X_SIGNAL_TICKET + "(", ")"); if (StringLen(ticketStr) > 0) { result.ticket = (ulong)ticketStr; } // // TYPE ... string typeStr = ExtractString( represent, "_" + X_SIGNAL_TYPE + "(", ")"); if (StringLen(typeStr) > 0) { // ENUM_X_SIGNAL_TYPE type = GetSignalType(typeStr); result.type = type; } // // TP ... string tpStr = ExtractString( represent, "_" + X_SIGNAL_TP + "(", ")"); if (StringLen(tpStr) > 0) { // double tp = (double)tpStr; result.tp = tp; } // // SL ... string slStr = ExtractString( represent, "_" + X_SIGNAL_SL + "(", ")"); if (StringLen(slStr) > 0) { // double sl = (double)slStr; result.sl = sl; } // // RFS ... string rfsStr = ExtractString( represent, "_" + X_SIGNAL_RISKFREE_STEP + "(", ")"); if (StringLen(rfsStr) > 0) { // double rfs = (double)rfsStr; result.riskFreeStep = rfs; } // // RFR ... string rfrStr = ExtractString( represent, "_" + X_SIGNAL_RISKFREE_RATE + "(", ")"); if (StringLen(rfrStr) > 0) { // double rfr = (double)rfrStr; result.riskFreeRate = rfr; } // return result; } void FromString( XSignal &result, // Holds Result string represent // String Represenation of a Signal ) { // // Validate Arg ... if (StringLen(represent) == 0) { return; } // XSignal fs = FromString(represent); // // Fillisg Result by Retrieved Values ... result.tp = fs.tp; result.sl = fs.sl; result.type = fs.type; result.ticket = fs.ticket; result.riskFreeStep = fs.riskFreeStep; result.riskFreeRate = fs.riskFreeRate; } // // END Signal Related ... // // // all protected features ... protected: // // all private features ... private: // // Specified Unique Tag ... string mTag; // // which Symbol ... string mSymbol; // // using deviation, it must be in Point ... int mSlippage; // // using magic number ... ulong mMagicNumber; // // Specify Max Allowed Trades int mMaxAllowedTrades; // // Minimum Free Marging for Open Trades double mMinAllowedFreeMarginForOpenTrades; // // Minimum Balance for Open Trades double mMinBallanceForOpenTrades; // // using CTrade instance ... XSCTrade mTrader; // // An Instance Of XCAccount Info for Handling Account Related Actions ... XCAccountInfo mAccountInfo; // // using PositionInfo instance ... CPositionInfo mPositionInfo; // // START Signal Private ... // // // Add Or Update Signal ... void AddOrUpdateSignalInfo( XSignal &signal // Specified Signal ) { // string name = GenerateSignalEventObjectName(signal); // bool isExists = IsDrawExists(name); if (isExists) { RemoveSignal(signal.ticket); } // DrawSignalEvent(signal); } // // Retrieve Signal String From Chart Event ... string RetrieveSignalContent( ulong ticket, // Specify Position Ticket int chartId = -1, // Specify Chart ID int subWindow = -1 // Specify SubWindow ID ) { // string result = ""; // // Normalize Chart and SubWindow ID ... if (chartId == -1) { chartId = 0; } if (subWindow == -1) { subWindow = 0; } // // Validate Args ... if (ticket <= 0) { return result; } // // Get Signal Event Name ... string sObjName = GenerateSignalEventObjectName(ticket); if (StringLen(sObjName) == 0) { return result; } // // Check Signal Event Exists ... bool isExists = IsDrawExists(sObjName); if (!isExists) { return result; } // // Loop Through Objects ... int count = ObjectsTotal( chartId, subWindow); for (int i = count - 1; i >= 0; i--) { // // Retrieve Object Name ... string objectName = ObjectName(chartId, i, subWindow); // // Find Signal Ticket Position in Object Name ... int stPos = StringFind( objectName, sObjName); if (stPos < 0) { continue; } // // Retrieve Object Type and Check it Event ... int oType = (int)ObjectGetInteger(chartId, objectName, OBJPROP_TYPE); if (oType != OBJ_EVENT) { continue; } // // Retrieve Object Text as String Representation OF Signal ... result = ObjectGetString(chartId, objectName, OBJPROP_TEXT); break; } // return result; } XSignal RetrieveSignal( ulong ticket // Specify Position Ticket ) { // XSignal result = {}; // if (ticket <= 0) { return result; } // string signalContent = RetrieveSignalContent(ticket); result = FromString( signalContent); // return result; } void RetrieveSignal( XSignal &signal // Specified Signal ) { // string signalContent = RetrieveSignalContent(signal.ticket); // FromString( signal, signalContent); } // // Draw a Signal on Screen ... void DrawSignalEvent( XSignal &signal, // Specify Draw which Signal on Screen color longColor = clrAqua, // Long Signal Object Color color shortColor = clrFuchsia, // Short Signal Object Color int chartId = -1, // Specify Chart ID int subWindow = -1 // Specify SubWindow ID ) { // // Validate Args ... if ( signal.ticket <= 0 || !IsValid(signal, mMagicNumber) || signal.type == X_SIGNAL_UNKNOWN) { return; } // // Normalize Chart and SubWindow ID ... if (chartId == -1) { chartId = 0; } if (subWindow == -1) { subWindow = 0; } // bool isLong = signal.type == X_SIGNAL_LONG; // // Simply Draw an Arrow on Chart for Representing Signal Info ... string name = GenerateSignalEventObjectName(signal); string content = ToString(signal); if (StringLen(content) == 0) { return; } // color signalColor = isLong ? longColor : shortColor; // // Create an Event On Chart ... bool isCreated = EventCreate( chartId, name, subWindow, content, signal.time, signalColor); if (isCreated) { // DrawSignalShape( signal, longColor, shortColor); } } // // Remove Signal Object from Chart ... bool RemoveSignal( ulong ticket, // Specify Position Ticket int chartId = -1 // Specify Chart ID ) { // bool result = false; // // Validate Arg ... if (ticket <= 0) { return result; } // // Normalize Chart Id ... if (chartId <= 0) { chartId = 0; } // // Generate Signal Object Name ... string signalObjectName = GenerateSignalEventObjectName(ticket); // // Remove Object from Chart ... result = RemoveDraw(signalObjectName); // return result; } bool RemoveSignal( XSignal &signal, // Specify Signal int chartId = -1 // Specify Chart ID ) { // bool result = false; // result = RemoveSignal( signal.ticket, chartId); // return result; } // // Converts a Position to Signal ... // // Convert Position to Signal by Index ... XSignal PositionToSignal(int index) { // XSignal result = {}; // if (!mPositionInfo.SelectByIndex(index)) { return result; } // ulong ticket = mPositionInfo.Ticket(); result = PositionToSignal(ticket); // return result; } // // Position To Signal by Ticket ... XSignal PositionToSignal(ulong ticket) { // XSignal result = {}; // if (!mPositionInfo.SelectByTicket(ticket)) { return result; } // string signalContent = RetrieveSignalContent(ticket); if (StringLen(signalContent) > 0) { // // Fill Additional Contents ... FromString( result, signalContent); } // // Ticket ... result.ticket = mPositionInfo.Ticket(); // // ID ... result.id = mPositionInfo.Identifier(); // // Symbol ... result.symbol = mPositionInfo.Symbol(); // // Type ... ENUM_POSITION_TYPE pType = (ENUM_POSITION_TYPE)mPositionInfo.PositionType(); ENUM_X_SIGNAL_TYPE type = pType == POSITION_TYPE_BUY ? X_SIGNAL_LONG : X_SIGNAL_SHORT; result.type = type; // // MagicNumber ... result.magicNumber = mPositionInfo.Magic(); // // Entry/Open Price ... result.entry = mPositionInfo.PriceOpen(); // // Take Profit ... result.tp = result.tp <= 0 && mPositionInfo.TakeProfit() > 0 ? mPositionInfo.TakeProfit() : result.tp; // // Stop Loss ... result.sl = result.sl <= 0 && mPositionInfo.StopLoss() > 0 ? mPositionInfo.StopLoss() : result.sl; // // Profit ... result.profit = mPositionInfo.Profit(); // // Time ... result.time = mPositionInfo.Time(); // // Volume ... result.volume = mPositionInfo.Volume(); // // Comment ... result.comment = mPositionInfo.Comment(); // return result; } // // END Signal Private ... // }; // // END Global Definitions: Variables, Properties and etc ... //