/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Class Library // ---------------------------------------------- // Name: X121SCTradeHandler // Description: provides all Trade Handling requirements ... // // // 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 // // Imports ... #include "../Classes/x-saherelm.xtrade.class.mq5" // // Definitions ... // // an Structure for Holding Positions Data ... struct X121TradeData { // datetime time; // Issue Time (Open Position) string symbol; // Trading Symbol double entry; // Entry Price double volume; // Volume string provider; // Signaller ENUM_TIMEFRAMES period; // Trading Timeframe ENUM_X_POSITION_TYPES type; // Position Type // ulong ticket; // Position Ticket double swap; // Swap double profit; // Profit on Close double commission; // Commission double maxDrawdown; // Max Position Drawdown string message; // Close Reason datetime endTime; // End Time // XSignal signal; // Signal Object int pushers; // Signal Pushers string conditions; // Signal Conditions // // Constructor ... void XTradeInfo() { Clean(); } // // Filling Trade Handler ... bool Fill(XSignal &_signal) { // bool result = false; // Clean(); // // Check Signal Validation ... result = _signal.IsValid(); if (!result) { return result; } // signal = _signal; time = _signal.time; entry = _signal.entry; symbol = _signal.symbol; volume = _signal.volume; period = _signal.period; pushers = _signal.pushers; provider = _signal.provider; conditions = _signal.conditions; type = ToPositionType(_signal.type); // if (_signal.IsExecuted()) { ticket = _signal.positionId; } // return result; } // // Filling Trade Handler ... bool Fill(XPosition &_position) { // bool result = false; // Clean(); // // Check Signal Validation ... result = _position.IsValid(); if (!result) { return result; } // time = _position.openAt; symbol = _position.symbol; entry = _position.entry; volume = _position.volume; provider = _position.provider; period = _position.period; type = ToPositionType(_position.type); // ticket = _position.ticket; // return result; } // // Update Data ... bool Update(XPosition &_position) { // bool result = false; // result = _position.IsValid(); if (!result) { return result; } // profit = _position.profit; swap = _position.swap; // if (profit < 0 && (maxDrawdown == 0 || MathAbs(profit) > MathAbs(maxDrawdown))) { maxDrawdown = profit; } // return result; } // // Tools ... // // Cleanup ... void Clean() { // time = NULL; type = NULL; symbol = NULL; period = NULL; endTime = NULL; message = NULL; provider = NULL; // swap = 0; entry = 0; ticket = 0; profit = 0; volume = 0; commission = 0; maxDrawdown = 0; // signal.Clean(); } // // Validation ... bool IsValid() { // bool result = false; // result = // ticket > 0 && IsValid(time) && IsValid(symbol) && IsValid(period) // ; // return result; } // // Retrieve Trade Data Age ... int GetAge() { // int result = -1; // if (!IsValid(symbol) || !IsValid(time) || !IsValid(endTime) || !IsValid(period)) { return result; } // int startIndex = iBarShift( symbol, period, time, false // ); // int endIndex = iBarShift( symbol, period, endTime, false // ); // result = MathAbs(startIndex - endIndex); // return result; } // // Check Own of Trade Data ... // based on Ticket ... bool IsOwn( ulong _ticket // ) { // bool result = false; // result = _ticket == ticket; // return result; } // // Check Own of Trade Data ... // based on Symbol/Provider and Period ... bool IsOwn( string _symbol, string _provider, ENUM_TIMEFRAMES _period, ENUM_X_POSITION_TYPES _type // ) { // bool result = false; // result = // IsValid(_symbol) && IsValid(_period) && IsValid(_provider) && type != X_POSITION_TYPE_ALL && type != X_POSITION_TYPE_NONE && // type == _type && period == _period && symbol == _symbol && provider == _provider // ; // return result; } // // Data Collector ... // // Get Data File Name ... string GetFileName() { // string result = NULL; // if (!IsValid()) { return result; } // result = // symbol + "\\" + ToString(type) + "\\" + (profit >= 0 ? "Profit" : "Loss") + "\\" + ToString(ticket) + "_" + ToString(period) + "_" + ToFormatString(time) // ; // return result; } // // Get Signal File Name ... string GetSignalFileName() { // string result = NULL; // result = // symbol + "\\" + ToString(type) + "\\" + provider + "_" + ToFormatString(time) // ; // return result; } // // Convert(s) To String Representation(s) ... string ToString( bool onlySignals = false // ) { // string result = NULL; // int age = GetAge(); // result = // (onlySignals ? "" : ToString("Ticket", ticket)) + ToString("Symbol", symbol) + ToString("Period", period) + ToString("Entry", entry) + ToString("Provider", provider) + ToString("Type", ToString(type)) + ToString("Time", time) + // // Attach Trade Info ... (onlySignals ? "" : // "-------------" + "\n" + ToString("Volume", volume) + ToString("Profit", profit) + ToString("Commission", commission) + ToString("Swap", swap) + ToString("Max Drawdown", maxDrawdown) + ToString("End Time", endTime) + ToString("Age", age) + ToString("Message", message) + "" // ) + // // Attach Conditions to Signals ... (!onlySignals ? "" : // "-------------" + "\n" + ToString("Pushers", pushers) + "Conditions:" + "\n" + "-------------" + "\n" + conditions + "" // ) + // "" // ; // return result; } // }; // // Model a Position and it's Protected Positions ... struct XProtectedPosition { // XPosition main; // Main Position ... X121TradeData data; // Trade Handler Data ... // XPosition supports[]; // Support Positions ... /** * Count Supported Positions */ int CountSupports() { return ArraySize(supports); } /** * Extract All Positions into Specified Array * of Positions ... */ int ExtractPositions( XPosition &positions[] // ) { // int result = 0; // Clean(positions); // if (main.IsValid()) { // AddRef( main, positions // ); } // int supportsCount = CountSupports(); if (IsValidSize(supportsCount)) { // Copy( supports, positions, false // ); } // result = ArraySize(positions); // return result; } }; // // Implementation ... // // a Class For Read and Write Trade Info Data in Files ... class X121TradeCollector { // // Public ... public: // // Props ... // // Constructor(s) ... void X121TradeCollector( string _path = NULL // Base Path ) { // mAccount = new XSCAccount(); // if (IsValid(_path)) { mPath = _path; } else { mPath = "X121TradeData" + "\\" + mAccount.GetCompany(); } } // // Deconstructor ... void ~X121TradeCollector() { } // bool IsExists(X121TradeData &item) { // bool result = false; // int mHandler = GetFileHandlerForRead(item); result = mHandler != INVALID_HANDLE; FileClose(mHandler); // return result; } // bool Save(X121TradeData &item) { // bool result = false; // // Check info is Valid ... result = item.IsValid(); if (!result) { return result; } // string content = item.ToString(); content += "\n" + "-----------" + "\n" + "Signal: " + "\n" + "-----------" + "\n" + item.ToString(true); // result = IsValid(content); if (!result) { return result; } // int mHandler = GetFileHandlerForWrite(item); result = mHandler != INVALID_HANDLE; if (!result) { return result; } // FileWrite(mHandler, content); FileFlush(mHandler); FileClose(mHandler); // return result; } // bool SaveSignal(X121TradeData &item) { // bool result = false; // string content = item.ToString(true); result = IsValid(content); if (!result) { return result; } // int mHandler = GetSignalFileHandlerForWrite(item); result = mHandler != INVALID_HANDLE; if (!result) { return result; } // FileWrite(mHandler, content); FileFlush(mHandler); FileClose(mHandler); // return result; } // // Conditions only save for Loss Signals ... // this means the profit must be Lower than Zero ... // ans also message Contains SL ... bool SaveConditions(X121TradeData &item) { // bool result = false; // // Validate Item ... result = // item.profit < 0 && Contains("SL", item.message) // ; if (!result) { return result; } // string content = item.signal.conditions; result = IsValid(content); if (!result) { return result; } // int mHandler = GetConditionsFileHandlerForWrite(item); result = mHandler != INVALID_HANDLE; if (!result) { return result; } // FileSeek(mHandler, 0, SEEK_END); FileWrite(mHandler, content); FileFlush(mHandler); FileClose(mHandler); // return result; } // // Protected ... protected: // // Private ... private: // // Props ... // string mPath; // Base Path ... // XSCAccount *mAccount; // string GetFilePath(X121TradeData &item) { // string fileName = item.GetFileName(); // return GetFilePath(fileName); } string GetFilePath(string fileName) { // string result = ""; // result = // mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log" // ; // return result; } // string GetSignalFilePath(X121TradeData &item) { // string fileName = item.GetSignalFileName(); // return GetSignalFilePath(fileName); } string GetSignalFilePath(string fileName) { // string result = ""; // result = // mPath + "\\" + "Signals" + "\\" + fileName + ".x121.log" // ; // return result; } // string GetConditionsFilePath(X121TradeData &item) { // bool isLong = IsLong(item.type); // string fileName = item.symbol + "\\" + (isLong ? "Longs" : "Shorts"); // return GetConditionsFilePath(fileName); } string GetConditionsFilePath(string fileName) { // string result = ""; // result = // mPath + "\\" + "Conditions" + "\\" + fileName + ".x121.log" // ; // return result; } // int GetFileHandlerForRead(X121TradeData &item) { // int result = INVALID_HANDLE; // string filePath = GetFilePath(item); if (!IsValid(filePath)) { return result; } // result = FileOpen( filePath, FILE_READ | FILE_TXT // ); // return result; } int GetFileHandlerForWrite(X121TradeData &item) { // int result = INVALID_HANDLE; // string filePath = GetFilePath(item); if (!IsValid(filePath)) { return result; } // result = FileOpen( filePath, FILE_READ | FILE_WRITE | FILE_TXT // ); // return result; } // int GetSignalFileHandlerForRead(X121TradeData &item) { // int result = INVALID_HANDLE; // string filePath = GetSignalFilePath(item); if (!IsValid(filePath)) { return result; } // result = FileOpen( filePath, FILE_READ | FILE_TXT // ); // return result; } int GetSignalFileHandlerForWrite(X121TradeData &item) { // int result = INVALID_HANDLE; // string filePath = GetSignalFilePath(item); if (!IsValid(filePath)) { return result; } // result = FileOpen( filePath, FILE_READ | FILE_WRITE | FILE_TXT // ); // return result; } // int GetConditionsFileHandlerForRead(X121TradeData &item) { // int result = INVALID_HANDLE; // string filePath = GetConditionsFilePath(item); if (!IsValid(filePath)) { return result; } // result = FileOpen( filePath, FILE_READ | FILE_TXT // ); // return result; } int GetConditionsFileHandlerForWrite(X121TradeData &item) { // int result = INVALID_HANDLE; // string filePath = GetConditionsFilePath(item); if (!IsValid(filePath)) { return result; } // result = FileOpen( filePath, FILE_READ | FILE_WRITE | FILE_TXT // ); // return result; } }; // // Trade Handler Class ... class X121SCTradeHandler : public XSCBaseAlert { // // Public ... public: // // // Constructor(s) ... void X121SCTradeHandler(XSCTrade *trader) { // mTrader = trader; mCollector = new X121TradeCollector(); } // // Deconstructor ... void ~X121SCTradeHandler() { // delete mTrader; delete mCollector; } // // Getter(s) / Setter(s) ... // bool SaveSignals() { return mSaveSignals; } // void SaveSignals(bool value) { mSaveSignals = value; } // bool SaveTrades() { return mSaveTrades; } // void SaveTrades(bool value) { mSaveTrades = value; } // bool SaveConditions() { return mSaveConditions; } // void SaveConditions(bool value) { mSaveConditions = value; } // bool AllowLong() { return mAllowLong; } // void AllowLong(bool value) { mAllowLong = value; } // bool AllowShort() { return mAllowShort; } // void AllowShort(bool value) { mAllowShort = value; } // int MaxAllowedLongs() { return mMaxAllowedLongs; } // void MaxAllowedLongs(int value) { // if (value < 0) { value = 0; } // mMaxAllowedLongs = value; } // int MaxAllowedShorts() { return mMaxAllowedShorts; } // void MaxAllowedShorts(int value) { // if (value < 0) { value = 0; } // mMaxAllowedShorts = value; } // bool UseMaxAllowedTradesPerSymbol() { return mUseMaxAllowedTradesPerSymbol; } // void UseMaxAllowedTradesPerSymbol(bool value) { mUseMaxAllowedTradesPerSymbol = value; } // double MaxDrawdownPercentForOpenTrades() { return mMaxDrawdownPercentForOpenTrades; } // void MaxDrawdownPercentForOpenTrades(double value) { // if (value < 0) { value = 0; } // mMaxDrawdownPercentForOpenTrades = value; } // // Hedge Props ... // bool AllowHedge() { return mAllowHedge; } // void AllowHedge(bool value) { mAllowHedge = value; } // double HedgeMinVolumeStep() { return mHedgeMinVolumeStep; } // // Min: 0.01 // Max 0.1 void HedgeMinVolumeStep(double value) { // if (value < 0.01) { value = 0.01; } // if (value > 0.1) { value = 0.1; } // mHedgeMinVolumeStep = value; } // int MinimumOpenPositionsForHEHedge() { return mMinimumOpenPositionsForHEHedge; } // void MinimumOpenPositionsForHEHedge(int value) { // if (value < 0) { value = 0; } // mMinimumOpenPositionsForHEHedge = value; } // double HedgeHEMinProfitPerVolumeStep() { return mHedgeHEMinProfitPerVolumeStep; } // // Min: 0.01 void HedgeHEMinProfitPerVolumeStep(double value) { // if (value < 0.01) { value = 0.01; } // mHedgeHEMinProfitPerVolumeStep = value; } // double HedgeBEMinProfitPerVolumeStep() { return mHedgeBEMinProfitPerVolumeStep; } // // Min: 0.01 void HedgeBEMinProfitPerVolumeStep(double value) { // if (value < 0.01) { value = 0.01; } // mHedgeBEMinProfitPerVolumeStep = value; } // // Read Only Props ... // int MaxSameTimeTrades() { return maxSameTimeTrades; } // // Calculate Max Drawdown and it's Percent ... double GetMaxDrawdown() { // XPosition positions[]; int positionsCount = mTrader.GetPositions(positions); if (IsValidSize(positionsCount)) { double mEquity = mTrader.mAccount.GetEquity(); if (mEquity > maxDrawdown) { maxDrawdown = mEquity; } // drawdownPercent = (maxDrawdown - mEquity) / (staticBalanceForCalculateDrawdown > 0 ? staticBalanceForCalculateDrawdown : maxDrawdown) * 100; drawdownPercent = NormalizeDouble(drawdownPercent, 3); } else { // maxDrawdown = 0.0; drawdownPercent = 0.0; } // return drawdownPercent; } // // Position Retrievers ... /** * Retrieve Positions as XProtetedPosition structure * @param positions: Argument 1 * @return ( int ) */ int GetPositions( XProtectedPosition &positions[] // ) { // int result = 0; // Clean(positions); // // Retrieve Positions from Trader Class ... XPosition allPositions[]; int allPositionsCount = mTrader.GetPositions( allPositions, NULL, // All Symbols ... NULL, // All Providers ... NULL, // All Periods ... X_POSITION_TYPE_ALL, // Long and Short ... false, // Filter By Magic ... true // Force Clean ... ); if (!IsValidSize(allPositionsCount)) { return result; } // // Filter Support Positions ... XPosition mainPositions[]; XPosition supportPositions[]; for (int i = 0; i < allPositionsCount; i++) { // XPosition iPosition = allPositions[i]; // // Check Validation ... if (!iPosition.IsValid()) { continue; } // // Check Position Support or not ... bool isSupport = IsSupport(iPosition.comment); if (isSupport) { // AddRef( iPosition, supportPositions // ); continue; } // AddRef( iPosition, mainPositions // ); } // // Check Main Positions Count ... int mainPositionsCount = ArraySize(mainPositions); if (!IsValidSize(mainPositionsCount)) { return result; } // int supportPositionsCount = ArraySize(supportPositions); // for (int i = 0; i < mainPositionsCount; i++) { // XPosition iPosition = mainPositions[i]; // XProtectedPosition iProtected; iProtected.main = iPosition; Clean(iProtected.supports); // // Loop Through Support Positions To Find iPositions Support ... if (IsValidSize(supportPositionsCount)) { // for (int j = 0; j < supportPositionsCount; j++) { // XPosition jSupport = supportPositions[j]; // ulong supportParentTicket = ExtractSupportedTicket(jSupport.comment); bool isSupport = supportParentTicket > 0; if (isSupport && supportParentTicket == iPosition.ticket) { // AddRef( jSupport, iProtected.supports // ); } // ulong eqmSupportParentTicket = ExtractEQMSupportedTicket(jSupport.comment); bool isEQMSupport = eqmSupportParentTicket > 0; if (isEQMSupport && eqmSupportParentTicket == iPosition.ticket) { // AddRef( jSupport, iProtected.supports // ); } } } // // Findout X121TradeData ... int iIDX = -1; bool hasItem = HasItem( iPosition.ticket, iIDX // ); if (hasItem && IsValidIndex(iIDX)) { iProtected.data = mData[iIDX]; } // AddRef( iProtected, positions // ); } // Clean(allPositions); Clean(mainPositions); Clean(supportPositions); // result = ArraySize(positions); // return result; } // // Trade Handling Functions ... // // First Step of Trade Handling ... // Since Must Call when a Signal Executed ... void AddData( XSignal &signal, // Executed Signal double commission = 0 // Commission ) { // // Check Signal Valid ... if (!signal.IsValid()) { return; } // // Check Signal Support or Recovery ... bool isSupport = IsSupport(signal.comment); bool isRecovery = ExtractRecoveredTicket(signal.comment) > 0; // X121TradeData item; bool isFilled = item.Fill(signal); if (!isFilled) { return; } // item.commission = commission; // Add(item); // SaveSignal(item); } // void Finish(const XDeal &deal) { // int idx = -1; bool hasItem = HasItem( deal.positionId, idx // ); if (!hasItem) { return; } // // Update Item Info ... mData[idx].swap = deal.swap; mData[idx].endTime = deal.time; mData[idx].profit = deal.profit; // mData[idx].message = deal.reason == DEAL_REASON_TP ? "TP" : "SL"; // Save(idx); } // void Finish( const ulong ticket, const XPosition &position, const string comment // ) { // int idx = -1; bool hasItem = HasItem( ticket, idx // ); if (!hasItem) { return; } // // Update Item Info ... mData[idx].swap = position.swap; mData[idx].endTime = TimeCurrent(); mData[idx].profit = position.profit; // mData[idx].message = comment; // Save(idx); } // // this Method call's by a Timer, // or in OnTick for Update Positions, // Data ... void UpdateData() { // XPosition positions[]; int count = mTrader.GetPositions(positions); if (!IsValidSize(count)) { return; } // for (int i = 0; i < count; i++) { // XPosition iPosition = positions[i]; // int idx = -1; bool isExists = HasItem( iPosition.ticket, idx // ); if (!isExists) { // // Add New Data ... // // Find Executed Signal Deal History ... XDeal deals[]; double commission = 0; int dealsCount = mTrader.GetDeals( deals, iPosition.symbol, iPosition.provider, iPosition.period, NULL, DEAL_ENTRY_IN // ); if (IsValidSize(dealsCount)) { // for (int i = 0; i < dealsCount; i++) { // XDeal iDeal = deals[i]; if (iDeal.positionId == iPosition.ticket) { // commission = iDeal.commission; break; } } } // X121TradeData iData; iData.Fill( iPosition // ); // iData.ticket = iPosition.ticket; // Add(iData); } else { // // Update Exists ... mData[idx].Update(iPosition); } } // count = Count(); if (count > maxSameTimeTrades) { maxSameTimeTrades = count; } // GetMaxDrawdown(); } // void UpdateSignal(XSignal &signal) { // if (!signal.IsValid()) { return; } // int count = Count(); if (!IsValidSize(count)) { return; } // for (int i = 0; i < count; i++) { // bool isOwn = mData[i].IsOwn( signal.symbol, signal.provider, signal.period, ToPositionType(signal.type) // ); if (isOwn) { mData[i].Fill(signal); } } } // void RemoveSignal(XSignal &signal) { // if (!signal.IsValid()) { return; } // int signalIDX = -1; ENUM_X_POSITION_TYPES xType = ToPositionType(signal.type); bool hasSignal = HasItem( signal.symbol, signal.provider, signal.period, xType, signalIDX // ); if (!hasSignal || !IsValidIndex(signalIDX)) { return; } // // Remove Item From List ... ArrayRemove( mData, signalIDX, 1 // ); } // // Position Execute and Sync Functions ... /** * this Method Synchronize * all Exists Positions and parse them * and add them into mData Collection if * their not Exists ... */ void Sync() { // // TODO: Implement here ... } /** * Validate Signal For Execution ... */ bool CanExecute(XSignal &signal) { // bool result = false; // // Validate Signal ... result = signal.IsValid(); if (!result) { return result; } // // TODO: // Here we can apply Same type or Opposit Type // Signals behaviour ... // also check For Market Open ... // // Check Market ... // TODO: Fix this ... // result = mSymbolSession.CanTrade(signal.symbol); // if (!result) { // return result; // } // return result; } /** * Execute Specific Signal using Trade Handler ... */ bool ExecuteSignal( XSignal &signal, // Signal for Execution ENUM_X_SIGNAL_EXECUTION_RESULT &state, // Execution State bool ignorePolicies = false // Ignore Execution Policies ) { // bool result = false; // // Check Signal Validation ... result = signal.IsValid(); if (!result) { return result; } // // Filter Signals if Necessary ... result = CanExecute(signal); if (!result) { // state = X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS; return result; } // // Check Policies ... if (!ignorePolicies) { // bool isLong = IsLong(signal.type); // // Check Allow Trade Type ... result = isLong ? mAllowLong : mAllowShort; if (!result) { // state = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED; return result; } // // Check Trades Count ... if (mMaxAllowedLongs > 0 || mMaxAllowedShorts > 0) { // XPosition longs[]; XPosition shorts[]; // // Counting Positions ... if (mUseMaxAllowedTradesPerSymbol) { // mTrader.GetPositions( longs, shorts, signal.symbol // ); } else { // mTrader.GetPositions( longs, shorts, NULL // All Symbols ... ); } // int longsCount = ArraySize(longs); int shortsCount = ArraySize(shorts); // result = // // Long ... isLong ? mMaxAllowedLongs <= 0 ? true : longsCount < mMaxAllowedLongs // // Short ... : mMaxAllowedShorts <= 0 ? true : shortsCount < mMaxAllowedShorts // ; if (!result) { // state = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED; return result; } } // // Check Drawdown ... if (mMaxDrawdownPercentForOpenTrades > 0) { // drawdownPercent = GetMaxDrawdown(); // result = drawdownPercent <= 0 ? true : drawdownPercent < mMaxDrawdownPercentForOpenTrades; if (!result) { // state = X_SIGNAL_EXECUTION_FAILED_NO_EQUITY; return result; } } } // // Execute Signal ... result = mTrader.ExecuteSignal( signal, state // ); if (result) { // double commission = mTrader.GetPositionCommission(signal.positionId); // // Add Signal to Trade Handler ... AddData( signal, commission // ); // string msg = (signal.type == POSITION_TYPE_BUY ? "Long" : "Short") + " Signal Provided by: " + signal.provider + (signal.pushers > 1 ? " Pushers: " + ToString(signal.pushers) : "") + " on: " + signal.symbol + " in: " + ToString(signal.period) + " Executed Successfully ..."; Alert(msg); } // return result; } // // Protection Functions ... /** * Handle Positiona Protections */ void HandleProtection() { // // Retrieve Positions ... XProtectedPosition pPositions[]; int pPositionsCount = GetPositions( pPositions // ); // // Validate Count ... if (!IsValidSize(pPositionsCount)) { return; } // bool isHedged = HandleHedge(pPositions); if (isHedged) { // // Renew Data ... Clean(pPositions); int pPositionsCount = GetPositions( pPositions // ); // // Validate Count ... if (!IsValidSize(pPositionsCount)) { return; } } } // // Protected ... protected: // XSCTrade *mTrader; // Instance of Trader Class X121TradeCollector *mCollector; // Instance of Trade Collector Class // X121TradeData mData[]; // Hold Trade Data // // Hedge Related Functions ... /** * Handle Hedging On Positions ... */ bool HandleHedge(XProtectedPosition &positions[]) { // bool result = false; // // Check Hedging is Enabled Or Not ... result = AllowHedge(); if (!result) { return result; } // // Check Minimum Volume Step is Provided or not ... double minVolumeStep = HedgeMinVolumeStep(); result = minVolumeStep > 0; if (!result) { return result; } // // Check Positions ... int positionsCount = ArraySize(positions); result = IsValidSize(positionsCount); if (!result) { return result; } // // Calculate Required Info ... double swaps = 0; // Summary of Positions Swaps ... double profits = 0; // Summary of Positions and Supports Profits ... double volumes = 0; // Summary of Positions and Supports Volumes ... int supportsCount = 0; // Number of Protecting Positions ... double commissions = 0; // Summary of Positions and Supports Commissions ... XPosition flatPositions[]; // Flat Positions ... for (int i = 0; i < positionsCount; i++) { // XProtectedPosition iProtected = positions[i]; // XPosition iPositions[]; int iPositionsCount = iProtected.ExtractPositions(iPositions); if (IsValidSize(iPositionsCount)) { // Copy( iPositions, flatPositions, false // ); } // // Collect Main Positions Data ... swaps += iProtected.main.swap; profits += iProtected.main.profit; volumes += iProtected.main.volume; commissions += mTrader.GetPositionCommission(iProtected.main.ticket); // int iSupportsCount = iProtected.CountSupports(); supportsCount += iSupportsCount; // if (IsValidSize(iSupportsCount)) { // // Collectiong Supports Positions Data ... for (int j = 0; j < iSupportsCount; j++) { // XPosition jSupport = iProtected.supports[j]; // swaps += jSupport.swap; profits += jSupport.profit; volumes += jSupport.volume; // commissions += mTrader.GetPositionCommission(jSupport.ticket); } } } // // First Step is Handle HE Hedge ... // int minOpenPositions = MinimumOpenPositionsForHEHedge(); double heMinProfitPerVolumeStep = HedgeHEMinProfitPerVolumeStep(); // // Check HE Hedge Conditions ... bool allowHEHedge = minOpenPositions > 0 && heMinProfitPerVolumeStep > 0; if (allowHEHedge) { // // Implement HE Hedge ... // // Check Main Positions Count and also not Support Positions ... result = supportsCount == 0 && positionsCount >= minOpenPositions; if (!result) { return result; } // // Check Profits ... result = profits > 0; if (!result) { return result; } // // Calculate Required Profit ... double volumeSteps = volumes / minVolumeStep; double minRequiredProfit = (volumeSteps * heMinProfitPerVolumeStep) + commissions + (-1 * swaps); // // Check Profits Satisfied Required Profit's For Hedge or not ... result = profits >= minRequiredProfit; if (!result) { return result; } // int flatPositionsCount = ArraySize(flatPositions); result = IsValidSize(flatPositionsCount); if (!result) { return result; } // string comment = "EQM HE Hedge ..."; int closed = mTrader.Close( flatPositions, comment // ); // result = closed > 0; if (result) { // string message = "EQM HE Hedge Close (" + ToString(flatPositionsCount) + ") Positions by: " + ToString(profits); Alert(message); } // return result; } // // Check BE Hedge Conditions ... bool allowBEHedge = positionsCount >= 1 && supportsCount >= 1; if (allowBEHedge) { // // Implement BE Hedge ... // // Check BE Conditions ... result = supportsCount > 0; if (!result) { return result; } // // return result; } // return result; } // // Private ... private: // // Props ... // // Read Only ... int maxSameTimeTrades; // Max Same Time Trades double maxDrawdown; // Max Drawdown double drawdownPercent; // Drawdown Percent double staticBalanceForCalculateDrawdown; // Static Balance for Max Drawdown Calculation // bool mSaveSignals; // Save Signals bool mSaveTrades; // Save Trades bool mSaveConditions; // Save SL Conditions // bool mAllowLong; // Allow Long/Buy Trade Type bool mAllowShort; // Allow Short/Sell Trade Type int mMaxAllowedLongs; // Max Allowe Long/Buy Trades int mMaxAllowedShorts; // Max Allowe Short/Sell Trades bool mUseMaxAllowedTradesPerSymbol; // Use Max Allowed Trades Per Symbol double mMaxDrawdownPercentForOpenTrades; // Max Drawdown Percent for Open Trades // // Hedging Properties ... bool mAllowHedge; // Allow Protector to Hedge Positions double mHedgeMinVolumeStep; // Minimum Volume Step For Hedge int mMinimumOpenPositionsForHEHedge; // Minimum Open Positions For HE Hedge double mHedgeHEMinProfitPerVolumeStep; // Minimum Required Profit For HE Hedge Per Volume Step double mHedgeBEMinProfitPerVolumeStep; // Minimum Required Profit For BE Hedge Per Volume Step // // Common Functions ... // // Count Data ... int Count() { return ArraySize(mData); } // // Add Item ... bool Add(X121TradeData &item) { // bool result = false; // result = item.IsValid(); if (!result) { return result; } // int index = -1; if (item.ticket > 0) { // result = !HasItem( item.ticket, index // ); } else { // result = !HasItem( item.symbol, item.provider, item.period, item.type, index // ); } if (!result || IsValidIndex(index)) { // result = false; return result; } // AddRef( item, mData // ); // return result; } // // Find Item Index ... bool HasItem( ulong ticket, int &index // ) { // bool result = false; // index = -1; // int count = Count(); result = IsValidSize(count); if (!result) { return result; } // for (int i = 0; i < count; i++) { // result = mData[i].IsOwn(ticket); if (result) { // index = i; break; } } // return result; } // // Find Item Index ... bool HasItem( string symbol, string provider, ENUM_TIMEFRAMES period, ENUM_X_POSITION_TYPES type, int &index // ) { // bool result = false; // index = -1; // // Validate ... result = // IsValid(symbol) && IsValid(period) && IsValid(provider) && type != X_POSITION_TYPE_ALL && type != X_POSITION_TYPE_NONE // ; if (!result) { return result; } // int count = Count(); result = IsValidSize(count); if (!result) { return result; } // for (int i = 0; i < count; i++) { // result = mData[i].IsOwn( symbol, provider, period, type // ); if (result) { // index = i; break; } } // return result; } // void Save(int index) { // int count = Count(); if (!IsValidIndex(index) || index > count - 1) { return; } // // Save Trade ... if (mSaveTrades) { mCollector.Save(mData[index]); } // if (mSaveConditions) { mCollector.SaveConditions(mData[index]); } // // Remove Item From List ... ArrayRemove( mData, index, 1 // ); } // void SaveSignal(X121TradeData &item) { // if (!mSaveSignals) { return; } // // Save Signal ... mCollector.SaveSignal(item); } // }; //