//////////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 XMAP Signal Provider Library // -------------------------------------------------------- // Name: XMAPSignalProvider // Description: XMAP based signal provider ... // // // 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 // // START Inputs ... // #include "x-saherelm.xmap.provider.inputs.lib.mq5" // // END Inputs ... // // // Include Common Library ... #include "x-saherelm.common.lib.mq5" // // Include Logger Library ... #include "x-saherelm.log.lib.mq5" // // Include Alert Library ... #include "x-saherelm.alert.lib.mq5" // // Include Draw Library ... #include "x-saherelm.draw.lib.mq5" // // Include Class Libraries ... #include "x-saherelm.class.lib.mq5" // // START Global Definitions: Variables, Properties and etc ... // // XCTrade *xMAPTrader; XCAccountInfo xMAPAccountInfo; // #include "x-saherelm.xmap.provider.indicators.lib.mq5" // // XOHCL xMAPCandles[]; // double xMAPDeposit = 0; double xMAPBalance = 0; double xMAPFreeMargin = 0; double xMAPMaxDrawDownPerTrade = 0; double xMAPGridPriceDistance = 0; double xMAPFreeMarginForOpenTrades = 0; double xMAPBalanceForOpenTrades = 0; // // END Global Definitions: Variables, Properties and etc ... // // // START Provided Functions ... // // // Initial Library if required ... bool OnInInitXMAPSignalProviderLibrary() { // bool result = false; // // Validate Inputs ... result = XMAPValidateInputs(); if (!result) { return result; } // // Init Indicators ... result = XMAPInitIndicators(); if (!result) { return result; } // // Calculations ... // // Calculate Account Deposit ... xMAPDeposit = xMAPAccountInfo.GetInitialBalance(); // // Calculate Max DrawDown Per Trade ... if (xMAPMaxDrawDownPerTradeFactor > 0) { xMAPMaxDrawDownPerTrade = xMAPDeposit * xMAPMaxDrawDownPerTradeFactor; } // // Calculate Grid Distance Price ... if (xMAPGridPriceDistanceInPips > 0) { xMAPGridPriceDistance = PipsToPrice(xMAPGridPriceDistanceInPips); } // // Calculate Min Balance for Trades ... if (xMAPBalanceFactorForOpenTrades > 0) { xMAPBalanceForOpenTrades = xMAPBalanceFactorForOpenTrades * xMAPDeposit; } // // Claculate Min Free Marging for Trades ... if (xMAPFreeMarginFactorForOpenTrades > 0) { xMAPFreeMarginForOpenTrades = xMAPFreeMarginFactorForOpenTrades * xMAPDeposit; } // // Make XCTrader instance ... xMAPTrader = new XCTrade( _Symbol, xMAPSlippage, xMAPMagicNumber); // result = true; // // Logging State ... string message = "Initializion of (" + "XMAP Provider" + " _ " + _Symbol + " _ " + EnumToString(_Period) + ") Succeeded ..."; LogMessage(message); // return result; } // // DeInitial Library if required ... void OnDeinitXMAPSignalProviderLibrary() { // // Release Handlers ... XMAPReleaseIndicators(); // // Logging State ... string message = "De Initializion of (" + "XMAP Provider" + ") Succeeded ..."; LogMessage(message); } // // this is a Globally Function which do all of // checkings and positions handling ... void HandleXMAPSignalProviderTick() { // // Update account Balance ... xMAPBalance = xMAPAccountInfo.GetBalance(); xMAPFreeMargin = xMAPAccountInfo.GetFreeMargin(); // // Reading Indicator Buffers ... XMAPHandleReadingBuffers(); if (xMAPCalculatedBars < xMAPMaxLengthOfInputs) { return; } // // Handle Open Trades ... XMAPHandleOpenTrades(); // // Handle Close Trades ... XMAPHandleCloseTrades(); // // XMAPLogTrades(); } // // Handle Open Trades ... void XMAPHandleOpenTrades() { // // Count Max Open Trades ... bool canOpenPrimaryTrades = true; if (xMAPMaxAllowedPrimaryTrades > 0) { // int count = XMAPCountPrimaryTrades(); canOpenPrimaryTrades = !(count >= xMAPMaxAllowedPrimaryTrades); } // // Check Free Margin ... if (xMAPFreeMargin <= xMAPFreeMarginForOpenTrades) { return; } // // Check Account Balance ... if (xMAPBalance <= xMAPBalanceForOpenTrades) { return; } // if (canOpenPrimaryTrades) { // // Primary Long Trade ... if (xMAPAllowLongTrades) { // XSignal primaryLongSignal = {}; bool isPrimaryLongSignalExecuted = XMAPHandlePrimaryLong(primaryLongSignal); if (isPrimaryLongSignalExecuted) { } } // // Primary Short Trade ... if (xMAPAllowShortTrades) { // XSignal primaryShortSignal = {}; bool isPrimaryShortSignalExecuted = XMAPHandlePrimaryShort(primaryShortSignal); if (isPrimaryShortSignalExecuted) { } } } // if (xMAPGridPriceDistance > 0 && xMAPMaxAllowedGridTrades > 0) { // bool isGridLongTradeOpened = XMAPHandleGridLong(); if (isGridLongTradeOpened) { } } } // // Handle Close Trades ... void XMAPHandleCloseTrades() { // // Handle Long/Buy Close ... if (xMAPTrader.CountLongs() > 0 && XMAPCanCloseLongTrades()) { // XSignal closed[]; xMAPTrader.CloseLongPositions(closed); bool hasClosed = ArraySize(closed) > 0; if (hasClosed) { // string message = "XMAP Closing Long Trades ..."; if (xMAPEnableAlerts) { SendAlert(message); } else { LogMessage(message); } } } // // Handle Short/Sell Close ... if (xMAPTrader.CountShorts() > 0 && XMAPCanCloseShortTrades()) { // XSignal closed[]; xMAPTrader.CloseShortPositions(closed); bool hasClosed = ArraySize(closed) > 0; if (hasClosed) { // string message = "XMAP Closing Short Trades ..."; if (xMAPEnableAlerts) { SendAlert(message); } else { LogMessage(message); } } } // // MaxDrawDown Trades ... if (xMAPMaxDrawDownPerTrade > 0) { // XSignal inDrawDownPrimarySignals[]; XSignal closed[]; XMAPGetInDrawDownPrimaryTrades(inDrawDownPrimarySignals); for (int i = 0; i < ArraySize(inDrawDownPrimarySignals); i++) { // XSignal inDP = inDrawDownPrimarySignals[i]; // double drawDown = -1 * inDP.profit; // bool canClose = drawDown >= xMAPMaxDrawDownPerTrade; if (!canClose) { continue; } // bool isClosed = xMAPTrader.Close(inDP.ticket); if (isClosed) { // Add( inDP, closed); // // Check and Close Belongs to Primary Grid Trades ... // XSignal grids[]; // XMAPGetGridTradesOfPrimary(inDP, grids); // int gridCount = ArraySize(grids); // if (gridCount == 0) { // continue; // } // // // for (int j = 0; j < gridCount; j++) { // // // XSignal grid = grids[j]; // bool isGridClosed = xMAPTrader.Close(grid.ticket); // } } } xMAPTrader.CloseInDrawDownTrades( xMAPMaxDrawDownPerTrade, X_SIGNAL_UNKNOWN, closed); bool hasClosed = ArraySize(closed) > 0; if (hasClosed) { // string message = "Close Max DrawDown Trades ..."; if (xMAPEnableAlerts) { SendAlert(message); } else { LogMessage(message); } } } // // Close Old Primary Trades ... if (xMAPMaxPrimaryTradeAge > 0) { // XSignal trades[]; xMAPTrader.GetLongTimeTrades( xMAPMaxPrimaryTradeAge, _Period, trades); int count = ArraySize(trades); if (count > 0) { // // Lopp ... for (int i = 0; i < count; i++) { // XSignal trade = trades[i]; if (!XMAPIsPrimaryTrade(trade)) { continue; } // bool isClosed = xMAPTrader.Close(trade.ticket); if (isClosed) { // string message = "Primary: " + (string)trade.ticket + " Closed because of Long Time Passed ..."; if (xMAPEnableAlerts) { SendAlert(message); } else { LogMessage(message); } } } } } // // Close Old Grid Trades ... if (xMAPMaxGridTradeAge > 0) { // XSignal trades[]; xMAPTrader.GetLongTimeTrades( xMAPMaxGridTradeAge, _Period, trades); int count = ArraySize(trades); if (count > 0) { // // Lopp ... for (int i = 0; i < count; i++) { // XSignal trade = trades[i]; if (!XMAPIsGridTrade(trade)) { continue; } // bool isClosed = xMAPTrader.Close(trade.ticket); if (isClosed) { // string message = "Grid: " + (string)trade.ticket + " Closed because of Long Time Passed ..."; if (xMAPEnableAlerts) { SendAlert(message); } else { LogMessage(message); } } } } } } // // Handle Long/Buy for Primary Trades... bool XMAPHandlePrimaryLong( XSignal &signal, // return structure if signal founded bool doTrade = true // do trade on signal ) { // bool result = false; // ResetLastError(); // // && xMAPTrader.CountLongs() == 0 ... if (XMAPHasPrimaryLongSignal()) { // double entry = GetAsk(); double volume = XMAPCalculateVolume(); // signal.entry = entry; signal.volume = volume; signal.symbol = _Symbol; signal.type = X_SIGNAL_LONG; signal.magicNumber = xMAPMagicNumber; signal.time = iTime(_Symbol, _Period, 0); signal.comment = "Primary_XMAP_Long"; // // Setting TP ... signal.tp = XMAPCalculateTP(signal.type); if (signal.tp == entry) { // result = false; return result; } // if (enableXMAPProvider && xMAPAllowLongTrades && doTrade) { // // Execute Signal ... result = xMAPTrader.ExecuteSignal(signal); // // Since this means an error happens, we have to log this error ... if (!result) { string errMessage = "failed to execute Long signal: " + (string)GetLastError(); LogMessage(errMessage); } } else { result = false; } } // // Handle Alerts ... if (result) { // if (xMAPEnableAlerts) { SendAlert(signal, true); } else { LogExecutedSignal(signal); } } // return result; } // // Handle Short/Sell for Primary Trades... bool XMAPHandlePrimaryShort( XSignal &signal, // return structure if signal founded bool doTrade = true // do trade on signal ) { // bool result = false; // ResetLastError(); // if (XMAPHasPrimaryShortSignal() && xMAPTrader.CountShorts() == 0) { // double entry = GetBid(); double volume = XMAPCalculateVolume(); // signal.entry = entry; signal.volume = volume; signal.symbol = _Symbol; signal.type = X_SIGNAL_SHORT; signal.magicNumber = xMAPMagicNumber; signal.time = iTime(_Symbol, _Period, 0); signal.comment = "Primary_XMAP_Short"; // if (enableXMAPProvider && xMAPAllowShortTrades && doTrade) { // // Execute Signal ... result = xMAPTrader.ExecuteSignal(signal); // // Since this means an error happens, we have to log this error ... if (!result) { string errMessage = "failed to execute Short signal: " + (string)GetLastError(); LogMessage(errMessage); } } else { result = false; } } // // Handle Alerts ... if (result) { // if (xMAPEnableAlerts) { SendAlert(signal, true); } else { LogExecutedSignal(signal); } } // return result; } // // Handle Long Grid Trades ... bool XMAPHandleGridLong() { // bool result = false; // // Check Grid Long Conditions ... result = XMAPHasGridLongSignal(); if (!result) { return result; } // // Retrieve all In DrawDown Primary Trades ... XSignal inDrawDownPrimaryTrades[]; XMAPGetInDrawDownPrimaryTrades(inDrawDownPrimaryTrades); int count = ArraySize(inDrawDownPrimaryTrades); if (count == 0) { return result; } // for (int i = 0; i < count; i++) { // XSignal primary = inDrawDownPrimaryTrades[i]; if (primary.type != X_SIGNAL_LONG) { continue; } // // Check Grids of Primary ... XSignal grids[]; XMAPGetGridTradesOfPrimary(primary, grids); int gridsCount = ArraySize(grids); if (gridsCount >= xMAPMaxAllowedGridTrades) { continue; } // XSignal firstGrid = XMAPGetFirstGridOfPrimary(primary); XSignal latestGrid = XMAPGetLastGridOfPrimary(primary); bool hasFirsttGrid = IsValid(firstGrid, xMAPMagicNumber) && XMAPIsGridTrade(firstGrid); bool hasLatestGrid = IsValid(latestGrid, xMAPMagicNumber) && XMAPIsGridTrade(latestGrid); // double entry = GetEntry(primary.type); double latestEntry = hasLatestGrid ? latestGrid.entry : primary.entry; double entryPriceDiff = (latestEntry - entry); double gridDistancePrice = xMAPGridPriceDistance; if (entryPriceDiff >= gridDistancePrice) { // // Here we Can Create a Grid Signal ... XSignal grid = XMAPGenerateGridSignal(primary, latestGrid); if (!IsValid(grid, xMAPMagicNumber)) { continue; } // ResetLastError(); bool isGridExecuted = xMAPTrader.ExecuteSignal(grid); if (isGridExecuted && !result) { // // if (gridsCount > 1 && hasFirsttGrid) // { // // // bool isClosed = xMAPTrader.Close(firstGrid.ticket); // } // // bool isClosed = xMAPTrader.Close(primary.ticket); // result = true; } } } // return result; } // // Check Market Conditions to find Long Primary Signals ... bool XMAPHasPrimaryLongSignal() { // bool result = false; // bool isXCHMAHasSignal = XTMCanOpenLongTrades(); // result = // isXCHMAHasSignal // ; // return result; } // // Check Market Conditions to find Short Primary Signals ... bool XMAPHasPrimaryShortSignal() { // bool result = false; // // Check Sell/Short Conditions ... // return result; } // // Has Long Grid Signal ... bool XMAPHasGridLongSignal() { // bool result = false; // // Get Long Conditions ... result = XTMCanOpenLongTrades(); // return result; } // // Check Market Conditions for closing Long Trades ... bool XMAPCanCloseLongTrades() { // bool result = false; // result = XTMCanCloseLongTrades(); // return result; } // // Check Market Conditions for closing Short Trades ... bool XMAPCanCloseShortTrades() { // bool result = false; // return result; } // // Calculating Volume for Tradings ... double XMAPCalculateVolume() { // double result = xMAPStaticVolume; // if (!xMAPUseDynamicVolume) // { // return result; // } // // // double accountBalance = xMAPAccountInfo.GetBalance(); // double balanceIncreased = xMAPBalanceIncreased; // double volumeIncreased = xMAPVolumeIncreased; // // // result = (volumeIncreased * accountBalance) / balanceIncreased; // // Normalize Volume ... result = NormalizeDouble(result, 2); // double maxAvailableVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double minAvailableVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); // // Validate Result ... if (result > maxAvailableVolume) { result = maxAvailableVolume; } else if (result < minAvailableVolume) { result = minAvailableVolume; } // return result; } // // END Provided Functions ... // // // START Private Functions ... // // // Calculate Signal TP ... double XMAPCalculateTP( ENUM_X_SIGNAL_TYPE type // Position Type ) { // double result = 0; // // Validate Args ... if (type == X_SIGNAL_UNKNOWN) { return result; } // double ask = GetAsk(); double bid = GetBid(); double spread = GetSpread(); // bool isLong = type == X_SIGNAL_LONG; // double entry = isLong ? ask : bid; // // double swingLow = XOBDGetSmallestSwingLow( // entry, // numberOfSwings); // double swingHigh = XOBDGetBiggestSwingHigh( // entry, // numberOfSwings); // // Find Risk .. // double reward = PipsToPrice(xMAPMinRewardPerTradeInPips) + spread; // swingHigh - entry; // PipsToPrice(5) + spread; // result = isLong ? entry + reward : entry - reward; // // // if (result == entry) // { // result += PipsToPrice(1); // } // return result; } // // Check a Signal is Primary or not... bool XMAPIsPrimaryTrade(XSignal &trade) { // bool result = false; // int titlePosition = StringFind(trade.comment, "Primary"); result = titlePosition >= 0; // return result; } // // Check a Signal is Grid or not... bool XMAPIsGridTrade(XSignal &trade) { // bool result = false; // int titlePosition = StringFind(trade.comment, "Grid"); result = titlePosition >= 0; // return result; } // // Retrieve Primary Trades ... void XMAPGetPrimaryTrades(XSignal &result[]) { // CleanBuffer(result); // XSignal trades[]; xMAPTrader.GetAllPositions(trades); int count = ArraySize(trades); if (count == 0) { return; } // for (int i = 0; i < count; i++) { // XSignal trade = trades[i]; if (!XMAPIsPrimaryTrade(trade)) { continue; } // Add(trade, result); } } int XMAPCountPrimaryTrades() { // int result = 0; // XSignal trades[]; XMAPGetPrimaryTrades(trades); result = ArraySize(trades); // return result; } // // Retrieve PrGridimary Trades ... void XMAPGetGridTrades(XSignal &result[]) { // CleanBuffer(result); // XSignal trades[]; xMAPTrader.GetAllPositions(trades); int count = ArraySize(trades); if (count == 0) { return; } // for (int i = 0; i < count; i++) { // XSignal trade = trades[i]; if (!XMAPIsGridTrade(trade)) { continue; } // Add(trade, result); } } int XMAPCountGridTrades() { // int result = 0; // XSignal trades[]; XMAPGetGridTrades(trades); result = ArraySize(trades); // return result; } // // Retrieve In DrawDown Primary Trades ... void XMAPGetInDrawDownPrimaryTrades(XSignal &result[]) { // CleanBuffer(result); // XSignal primaryTrades[]; XMAPGetPrimaryTrades(primaryTrades); int count = ArraySize(primaryTrades); if (count == 0) { return; } // for (int i = 0; i < count; i++) { // XSignal primary = primaryTrades[i]; if (primary.profit > 0) { continue; } // Add(primary, result); } } // // Count In DrawDown Primary Trades ... int XMAPCountInDrawDownPrimaryTrades() { // int result = 0; // XSignal trades[]; XMAPGetInDrawDownPrimaryTrades(trades); result = ArraySize(trades); // return result; } // // Retrieve In Profit Primary Trades ... void XMAPGetInProfitPrimaryTrades(XSignal &result[]) { // CleanBuffer(result); // XSignal primaryTrades[]; XMAPGetPrimaryTrades(primaryTrades); int count = ArraySize(primaryTrades); if (count == 0) { return; } // for (int i = 0; i < count; i++) { // XSignal primary = primaryTrades[i]; if (primary.profit <= 0) { continue; } // Add(primary, result); } } // // Count In Profit Primary Trades ... int XMAPCountInProfitPrimaryTrades() { // int result = 0; // XSignal trades[]; XMAPGetInProfitPrimaryTrades(trades); result = ArraySize(trades); // return result; } // // Generate a Grid Trades Comment based on a Primary Trade ... string XMAPGetGridTradeComment(XSignal &primary) { // string result = ""; // if (!XMAPIsPrimaryTrade(primary)) { return result; } // result = "Grid_" + GetSignalType(primary) + "_Ticket(" + (string)primary.ticket + ")"; // return result; } // // Check a Grid Trade Belongs to a Primary Trade or not ... bool XMAPIsGridBelongsToPrimary( XSignal &grid, // grid signal ... XSignal &primary // primary signal ... ) { // bool result = false; // if ( !XMAPIsGridTrade(grid) || grid.type != primary.type || !XMAPIsPrimaryTrade(primary)) { return result; } // string comment = XMAPGetGridTradeComment(primary); if (StringLen(comment) == 0) { return result; } // int titlePosition = StringFind(grid.comment, comment); result = titlePosition >= 0; // return result; } // // Retrieve Grid Trades which belongs to an Specified Primery Trades ... void XMAPGetGridTradesOfPrimary( XSignal &primary, // primary trade XSignal &result[] // hold results ) { // CleanBuffer(result); // string comment = XMAPGetGridTradeComment(primary); if (StringLen(comment) == 0) { return; } // // Get all Grid Trades ... XSignal grids[]; XMAPGetGridTrades(grids); int count = ArraySize(grids); if (count == 0) { return; } // for (int i = 0; i < count; i++) { // XSignal grid = grids[i]; bool isBelongsToPrimary = XMAPIsGridBelongsToPrimary( grid, primary); if (!isBelongsToPrimary) { continue; } // Add( grid, result); } } // // Find Last Grid Trade of a Primary Trade ... XSignal XMAPGetLastGridOfPrimary(XSignal &primary) { // XSignal result = {}; // if (!XMAPIsPrimaryTrade(primary)) { return result; } // XSignal grids[]; XMAPGetGridTradesOfPrimary(primary, grids); int count = ArraySize(grids); if (count == 0) { return result; } // for (int i = 0; i < count; i++) { // XSignal grid = grids[i]; // if (!IsValid(result, xMAPMagicNumber)) { result = grid; } // if (IsLong(primary)) { result = grid.entry < result.entry ? grid : result; } else if (IsShort(primary)) { result = grid.entry > result.entry ? grid : result; } else { continue; } } // return result; } // // Find First Grid Trade of a Primary Trade ... XSignal XMAPGetFirstGridOfPrimary(XSignal &primary) { // XSignal result = {}; // if (!XMAPIsPrimaryTrade(primary)) { return result; } // XSignal grids[]; XMAPGetGridTradesOfPrimary(primary, grids); int count = ArraySize(grids); if (count == 0) { return result; } // for (int i = 0; i < count; i++) { // XSignal grid = grids[i]; // if (!IsValid(result, xMAPMagicNumber)) { result = grid; } // if (IsLong(primary)) { result = grid.entry > result.entry ? grid : result; } else if (IsShort(primary)) { result = grid.entry < result.entry ? grid : result; } else { continue; } } // return result; } // // Prepare and Generate XSignal instance for Grid Trades ... XSignal XMAPGenerateGridSignal( XSignal &primary, // Main Primary Trade XSignal &grid // Latest Grid Trade which Belongs to Primary ) { // XSignal result = {}; // if (!XMAPIsPrimaryTrade(primary)) { return result; } // bool hasLatestGrid = IsValid(grid, xMAPMagicNumber) && XMAPIsGridBelongsToPrimary( grid, primary); // double gridThreshold = 7; // double gridVolumeMultiplier = gridsCount < gridThreshold ? 2 : 1; // double girdVolumeBase = gridsCount < gridThreshold ? grid.volume : primary.volume; // double volume = NormalizeDouble(girdVolumeBase * gridVolumeMultiplier, 2); // bool isLong = primary.type == X_SIGNAL_LONG; // double entry = GetEntry(primary.type); string comment = XMAPGetGridTradeComment(primary); double volume = NormalizeDouble((hasLatestGrid ? grid.volume : primary.volume) * xMAPGridVolumeMultiplier, 2); // if (volume >= 7 * primary.volume) // { // volume = primary.volume; // } // double reward = NormalizeDouble(MathAbs((hasLatestGrid ? grid.entry : primary.tp) - entry) / 3, _Digits); double tp = entry + reward; // result.tp = tp; result.entry = entry; result.sl = primary.sl; result.volume = volume; result.comment = comment; result.type = primary.type; result.magicNumber = primary.magicNumber; result.time = iTime(_Symbol, _Period, 0); // return result; } // // END Private Functions ... //