diff --git a/Classes/x-saherelm.xexpert.class.mq5 b/Classes/x-saherelm.xexpert.class.mq5 index ce5a88cc..3a7bef9e 100644 --- a/Classes/x-saherelm.xexpert.class.mq5 +++ b/Classes/x-saherelm.xexpert.class.mq5 @@ -233,9 +233,9 @@ class XCBaseExpert : public XSCBaseAlert * * @return ( bool ) */ - bool AllowLomg() + bool AllowLong() { - return mAllowLomg; + return mAllowLong; } /** @@ -243,9 +243,9 @@ class XCBaseExpert : public XSCBaseAlert * * @param value: Boolean ... */ - void AllowLomg(bool value) + void AllowLong(bool value) { - mAllowLomg = value; + mAllowLong = value; } /** @@ -484,7 +484,7 @@ class XCBaseExpert : public XSCBaseAlert // // Signalling ... Disabled(false); - AllowLomg(true); + AllowLong(true); AllowShort(true); // @@ -694,6 +694,36 @@ class XCBaseExpert : public XSCBaseAlert HandleReportBalance(); } + /** + * Handle Position Modified ... + * + * @param ticket: ULONG ... + * @param profit: Double ... + * @param comment: String ... + */ + virtual void HandleOnPositionModified( + const ulong ticket, + const double profit, + const string comment // + ) + { + } + + /** + * Handle Position Partially Closed ... + * + * @param ticket: ULONG ... + * @param profit: Double ... + * @param comment: String ... + */ + virtual void HandleOnPositionPartialClosed( + const ulong ticket, + const double profit, + const string comment // + ) + { + } + /** * Do All Signalling Processing Here ... */ @@ -838,7 +868,7 @@ class XCBaseExpert : public XSCBaseAlert // // Signalling ... bool mDisabled; // Force Disable Signalling ... - bool mAllowLomg; // Allow Long Signals ... + bool mAllowLong; // Allow Long Signals ... bool mAllowShort; // Allow Short Signals ... // diff --git a/Documents/BKP/x-saherelm.x-121.smc.ea.mq5 b/Documents/BKP/x-saherelm.x-121.smc.ea.mq5 new file mode 100644 index 00000000..617b5e6a --- /dev/null +++ b/Documents/BKP/x-saherelm.x-121.smc.ea.mq5 @@ -0,0 +1,832 @@ +/////////////////////////////////////////////////////// +// +// SaherElm IT Center XTest MQL5 Expert Advisor +// ------------------------------------------------- +// Name: X121SMCEA +// Description: an Exper Advisor which used Panels +// +// Maintainer: +// ------------ +// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) +// +//////////////////////////////////////////////////////x +// +// Global Properties ... +#property copyright "Copyright 2023, SaherElm IT Center" +#property link "https://saherelm.ir" +#property version "1.00" +#property description "SaherElm X121SMCEA" +#property strict + +// +#define ShortName "X121SMCEA" + +// +// Imports ... +#include "../Classes/x-saherelm.xalert.class.mq5" +#include "../Classes/x-saherelm.xtrade.class.mq5" +#include "../Helpers/x-saherelm.xcc.helper.mq5" +#include "../Helpers/x-saherelm.xct.helper.mq5" +#include "../X121SMCEA/Classes/x-121.smc.ea.panel.class.mq5" +#include "../X121SMCEA/Strategy/x-121.smc.ob.strategy.class.mq5" +#include "../X121SMCEA/Strategy/x-121.smc.supdem.strategy.class.mq5" + +// +// Inputs ... + +// +// Common ... +input group "Common"; +input long eaMagicNumber = 78692110; // Magic Number +input int eaSlippage = 10; // Slippgae +input string eaLogSuffix = ""; // Log Suffix + +// +// Symbols ... +input group "Symbols"; +input bool eaMultiSymbol = false; // Use Multi Symbol +input string eaSymbols = "EURUSDb,GBPUSDb,USDJPYb,USDCHFb,DowJones30,XAUUSDb"; // Trading Symbols + +// +// Signalling ... +input group "Signalling"; +input bool eaAllowLong = true; // Allow Long Signals +input bool eaAllowShort = true; // Allow Short Signals + +// +// Alert ... +// Here we Provider All Requirements for +// Configuring Alert Provider ... +input group "Alert"; +input bool eAEnableAlerts = true; // Enable Alerts +input bool eALogAlerts = true; // Log Alerts +input bool eAMailAlerts = false; // Mail Alerts +input bool eAPushAlerts = false; // Push Alerts +input bool eATerminalAlerts = false; // Terminal Alerts + +// +// Time Report ... +input group "Reports"; +input bool eaReportNewMonths = false; // Report New Month +input bool eaReportNewWeeks = false; // Report New Weeks +input bool eaReportNewDays = false; // Report New Days +input bool eaReportNewHours = false; // Report New Hours + +// +// Define Local Variables ... + +// +string eaLogTag; // EA Log Prefix ... +XSCAlert *eaAlert; // Alert Provider for Expert Adviser ... +XSCTrade *eaTrader; // Trader of Expert Adviser ... +XTimeTracker eaTimeTracker; // Time Tracker for Providing Reports ... +XSCXCTHelper *eaCTHelper; // Bar Timer Indicator Helper class ... +XSCXCCHelper *eaCCHelper; // Chart Styler Indicator Helper Class ... +X121CAppDialog eaMainWindow; // EA Main Dialog ... +XCX121SMCBaseStrategy *eaStrategies[]; // Registere Strategies in EA ... + +// +// Signalling Controller Conditions ... +bool eaDisableSignalling; +bool eaAllowLongSignalling; +bool eaAllowShortSignalling; + +// +// Event Handlers ... + +// +// Initialization ... +int OnInit() +{ + // + // Validate Inputs and Required Parameters + // for EA to Works fine ... + if (!ValidateInputs()) + { + return INIT_PARAMETERS_INCORRECT; + } + + // + // Initialize Timer ... + if (!InitTimer()) + { + return INIT_FAILED; + } + + // + // Initial Controller Conditions ... + InitControllerConditions(); + + // + // Initialize all Requirements ... + if (!InitEA()) + { + return INIT_FAILED; + } + + // + // Initialize all GUI Objects and Classes ... + if (!InitGUI()) + { + return INIT_FAILED; + } + + // + // Alert Initialization ... + string msg = "Initialized Successfully ..."; + eaAlert.Alert(msg); + + // + // Init Succeed ... + return INIT_SUCCEEDED; +} + +// +// DeInitialization ... +void OnDeinit(const int reason) +{ + // + // REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function + // REASON_REMOVE 1 Program removed from a chart + // REASON_RECOMPILE 2 Program recompiled + // REASON_CHARTCHANGE 3 A symbol or a chart period is changed + // REASON_CHARTCLOSE 4 Chart closed + // REASON_PARAMETERS 5 Inputs changed by a user + // REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings + // REASON_TEMPLATE 7 Another chart template applied + // REASON_INITFAILED 8 The OnInit() handler returned a non-zero value + // REASON_CLOSE 9 Terminal closed + + // + // Destroy Timer ... + DestroyTimer(); + + // + // Destroy all GUI Requirements ... + DestroyGUI(); + + // + // Alert De Initialization Succeeded ... + string msg = "De Initialized Successfully ..."; + eaAlert.Alert(msg); + + // + // Destroy all EA Requirements ... + DestroyEA(); +} + +// +// On Tick Handler ... +void OnTick() +{ + // + UpdateGUI(); + HandleReportTime(); + UpdatePositionsState(); + HandleStrategiesOnTick(); +} + +// +// Handle On Trade Event ... +void OnTrade() +{ + eaTrader.HandleOnTrade(); +} + +// +// On Timer ... +void OnTimer() +{ + // + // TODO: Implement Time Based Functionalities ... +} + +// +// Chart Event Handler ... +void OnChartEvent( + const int id, + const long &lparam, + const double &dparam, + const string &sparam // +) +{ + // + // Apply Chart Event on Main Window ... + eaMainWindow.ChartEvent(id, lparam, dparam, sparam); + + // + // Handle OnClick Event ... + if (id == CHARTEVENT_OBJECT_CLICK) + { + HandleOnClickEvent(sparam); + } +} + +// +// Custom Event Handlers ... + +/** + * Handle StopLoss Triggered Positions ... + * + * @param deal: XDeal ... + */ +void OnStopLossTriggered(const XDeal &deal) +{ + // + HandleReportBalance(); +} + +/** + * Handle Take Profit Triggered Positions ... + * + * @param deal: XDeal ... + */ +void OnTakeProfitTriggered(const XDeal &deal) +{ + // + HandleReportBalance(); +} + +/** + * Handle Force Close a Position ... + * + * @param ticket: Position Ticket ... + * @param position: XPosition ... + * @param comment: Closing Comment ... + */ +void OnPositionForceClosed( + const ulong ticket, + const XPosition &position, + const string comment // +) +{ + // + HandleReportBalance(); +} + +/** + * Do What we Want by Specific Signal ... + * + * @param signal: XSignal instance ... + * @param conditions: XStrategyConditions instance ... + */ +void OnSignalRecieved( + XSignal &signal, + X121SMCStrategyConditions &conditions // +) +{ + // + // TODO: Implement this ... + + // + ENUM_X_SIGNAL_EXECUTION_RESULT state; + eaTrader.ExecuteSignal( + signal, + state // + ); +} + +// +// Custom Functions ... + +/** + * Validate all Inputs of Expert Adviser ... + * + * @return ( bool ) + */ +bool ValidateInputs() +{ + // + bool result = false; + + // + string errMessage = ""; + + // + bool isCommonValid = ( + // + eaSlippage > 0 && + eaMagicNumber > 0 + // + ); + if (!isCommonValid) + { + // + errMessage += "common configurations error;" + "\n"; + } + + // + result = + // + // Common ... + isCommonValid && + // + // Otere ... + true + // + ; + + // + if (!result) + { + // + // Since here Logger not Initiallized, we Use Raw Print Command ... + Print(ShortName + " Errors: \n", errMessage); + } + + // + return result; +} + +/** + * Initialize Timer ... + * + * @return ( bool ) + */ +bool InitTimer() +{ + // + bool result = false; + + // + // TODO: Initialize Timer if necessary ... + // result = EventSetTimer(1); + // result = EventSetMillisecondTimer(100); + result = true; + + // + return result; +} + +/** + * Destroy Timer ... + */ +void DestroyTimer() +{ + // + // TODO: Use This ... + // EventKillTimer(); +} + +/** + * Initialize all Requirements for Working Fine ... + * + * @return ( bool ) + */ +bool InitEA() +{ + // + bool result = false; + + // + // Locally Apply Changes on Prefix ... + eaLogTag = ShortName + ">" + eaLogSuffix; + + // + // Bar Timer ... + XCTInputs ctInputs; + ctInputs.Default(); // Default Configurations ... + eaCTHelper = new XSCXCTHelper(); + result = eaCTHelper.Init( + _Symbol, + _Period, + ctInputs // + ); + if (!result) + { + return result; + } + + // + // Bar Styles ... + XCCInputs ccInputs; + ccInputs.Default(); // Default Configurations ... + eaCCHelper = new XSCXCCHelper(); + result = eaCCHelper.Init( + _Symbol, + _Period, + ccInputs // + ); + if (!result) + { + return result; + } + + // + // Create Instance of Alert Class and Configure it ... + eaAlert = new XSCAlert(); + eaAlert.SetPrefix(eaLogTag); + eaAlert.SetLogAlerts(eALogAlerts); + eaAlert.SetMailAlerts(eAMailAlerts); + eaAlert.SetPushAlerts(eAPushAlerts); + eaAlert.SetEnableAlerts(eAEnableAlerts); + eaAlert.SetTerminalAlerts(eATerminalAlerts); + + // + // Create Trader Instance and Configure it ... + eaTrader = new XSCTrade( + eaSlippage, + eaMagicNumber // + ); + eaTrader.AddOnStopLossEventHandler(OnStopLossTriggered); + eaTrader.AddOnTakeProfitEventHandler(OnTakeProfitTriggered); + eaTrader.AddOnForceClosePositionEventHandler(OnPositionForceClosed); + + // + // Symbols Parsing ... + + // + // Parsers ... + XSymbolParser _symbolParser; + + // + // Single Symbol ... + if (!eaMultiSymbol) + { + // + // Register Strategy ... + + // + // Create Class Instance ... + XCX121SMCBaseStrategy *iX121SMCStrategy; + iX121SMCStrategy = new XCX121SMCOBStrategy( + _Symbol, + _Period // + ); + + // + iX121SMCStrategy.AddXSignalEventHandler(OnSignalRecieved); + + // + ConfigureStrategy(iX121SMCStrategy); + RegisterStrategy(iX121SMCStrategy); + } + // + // Multi Symbol ... + else + { + // + // Parse Symbols ... + string symbols[]; + int symbolsCount = SplitContent( + symbols, + eaSymbols // + ); + result = IsValidSize(symbolsCount); + if (!result) + { + return result; + } + + // + for (int i = 0; i < symbolsCount; i++) + { + // + // Select Symbol ... + string iSymbol = symbols[i]; + + // + // Register Strategy Based On Symbol ... + + // + // Parse Symbol ... + bool isEURUSD = _symbolParser.IsEURUSD(_Symbol); + bool isGBPUSD = _symbolParser.IsGBPUSD(_Symbol); + bool isEURGBP = _symbolParser.IsEURGBP(_Symbol); + bool isUSDCHF = _symbolParser.IsUSDCHF(_Symbol); + bool isEURCHF = _symbolParser.IsEURCHF(_Symbol); + bool isUSDJPY = _symbolParser.IsUSDJPY(_Symbol); + bool isEURJPY = _symbolParser.IsEURJPY(_Symbol); + bool isUSDCAD = _symbolParser.IsUSDCAD(_Symbol); + bool isEURCAD = _symbolParser.IsEURCAD(_Symbol); + bool isAUDUSD = _symbolParser.IsAUDUSD(_Symbol); + bool isEURAUD = _symbolParser.IsEURAUD(_Symbol); + bool isNZDUSD = _symbolParser.IsNZDUSD(_Symbol); + bool isEURNZD = _symbolParser.IsEURNZD(_Symbol); + + // + // Check Symbol Can Register or not ... + bool canRegisterStrategy = true; + + // + if (canRegisterStrategy) + { + // + // Create Class Instance ... + XCX121SMCBaseStrategy *iX121SMCStrategy; + iX121SMCStrategy = new XCX121SMCOBStrategy( + iSymbol, + _Period // + ); + + // + iX121SMCStrategy.AddXSignalEventHandler(OnSignalRecieved); + + // + // Register Strategy ... + ConfigureStrategy(iX121SMCStrategy); + RegisterStrategy(iX121SMCStrategy); + } + } + } + + // + return result; +} + +/** + * Destroy all Initialized EA Requirements ... + */ +void DestroyEA() +{ + // + delete eaCTHelper; + delete eaCCHelper; + + // + delete eaAlert; +} + +/** + * Initialize all GUI Requirements for EA ... + * + * @return ( bool ) + */ +bool InitGUI() +{ + // + bool result = false; + + // + // Initialize Application Main Window ... + result = eaMainWindow.Create( + ChartID(), + ShortName, // Dialog Name ... + 0, // SubWindow ... + 10, // X1 ... + 35, // Y1 ... + 300, // X2 ... + 200 // Y2 ... + ); + + // + // Try To Run Main Window ... + if (result) + { + // + eaMainWindow.Configure(); + eaMainWindow.Run(); + } + + // + return result; +} + +/** + * Update GUI based on Each Ticks ... + */ +void UpdateGUI() +{ + // + // TODO: Implement this ... +} + +/** + * Destroy all Initialized GUi Requirements ... + */ +void DestroyGUI() +{ + // + eaMainWindow.Destroy(); +} + +// +// Others ... + +/** + * Update Positions States ... + */ +void UpdatePositionsState() +{ + // + // TODO: Implement this ... +} + +/** + * Handle On Click Event for GUI Components ... + * + * @param name: Object Name ... + */ +void HandleOnClickEvent(string name) +{ + // + if (!IsValid(name)) + { + return; + } + + // + bool isClicked = false; + + // + // TODO: Implement Chart Objects On Click ... + + // + if (isClicked) + { + UpdateGUI(); + } +} + +/** + * Report Account Balance ... + */ +void HandleReportBalance() +{ + // + if (!IsRunningOnTestMode()) + { + return; + } + + // + string msg = "Balance: " + ToString(eaTrader.mAccount.GetBalance()); + eaAlert.Log(msg); +} + +/** + * Time Reporting based on Inputs ... + */ +void HandleReportTime() +{ + // + // Monthly Report .... + if (eaReportNewMonths && + eaTimeTracker.IsNewMonth()) + { + // + string msg = "New Month ..."; + + // + eaAlert.Alert(msg); + } + + // + // Weekly Report .... + if (eaReportNewWeeks && + eaTimeTracker.IsNewWeek()) + { + // + string msg = "New Week ..."; + + // + eaAlert.Alert(msg); + } + + // + // Daily Report .... + if (eaReportNewDays && + eaTimeTracker.IsNewDay()) + { + // + string msg = "New Day ..."; + + // + eaAlert.Alert(msg); + } + + // + // Hourly Report .... + if (eaReportNewHours && + eaTimeTracker.IsNewHour()) + { + // + string msg = "New Hour ..."; + + // + eaAlert.Alert(msg); + } +} + +/** + * Initial EA Controller Conditions ... + */ +void InitControllerConditions() +{ + // + // Signalling Conditions ... + eaAllowLongSignalling = eaAllowLong; + eaAllowShortSignalling = eaAllowShort; + + // + eaDisableSignalling = !eaAllowLongSignalling && + !eaAllowShortSignalling; +} + +// + +/** + * Configure Startegy ... + */ +void ConfigureStrategy(XCX121SMCBaseStrategy *&strategy) +{ + // + if (strategy == NULL) + { + return; + } + + // + // Configure Alerts ... + strategy.SetAlertPrefix(eaLogTag); + strategy.SetAlertLogAlerts(eALogAlerts); + strategy.SetAlertMailAlerts(eAMailAlerts); + strategy.SetAlertPushAlerts(eAPushAlerts); + strategy.SetAlertEnableAlerts(eAEnableAlerts); + strategy.SetAlertTerminalAlerts(eATerminalAlerts); + + // + // Confiugre Signalling ... + + // + strategy.AllowLong(eaAllowLongSignalling); + strategy.AllowShort(eaAllowShortSignalling); + + // + if (eaDisableSignalling) + { + strategy.Disable(); + } + else + { + strategy.Enable(); + } + + // +} + +/** + * Register an Strategy in EA ... + * + * @param strategy: XCX121SMCBaseStrategy instance ... + */ +void RegisterStrategy(XCX121SMCBaseStrategy *strategy) +{ + // + if (strategy == NULL) + { + return; + } + + // + ArrayResize( + eaStrategies, + ArraySize(eaStrategies) + 1 // + ); + + // + eaStrategies[ArraySize(eaStrategies) - 1] = strategy; +} + +/** + * Re Configure All Registered Strategies ... + */ +void ReConfigureAllStrategies() +{ + // + int count = ArraySize(eaStrategies); + if (!IsValidSize(count)) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + // + ConfigureStrategy(eaStrategies[i]); + } +} + +/** + * Call all Registered Strategies On Tick ... + */ +void HandleStrategiesOnTick() +{ + // + int count = ArraySize(eaStrategies); + if (!IsValidSize(count)) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + // + // Call Tick Handler Function ... + eaStrategies[i].HandleTick(); + } +} + +// diff --git a/Experts/x-saherelm.x-121.smc.ea.mq5 b/Experts/x-saherelm.x-121.smc.ea.mq5 index 617b5e6a..ad448168 100644 --- a/Experts/x-saherelm.x-121.smc.ea.mq5 +++ b/Experts/x-saherelm.x-121.smc.ea.mq5 @@ -23,13 +23,7 @@ // // Imports ... -#include "../Classes/x-saherelm.xalert.class.mq5" -#include "../Classes/x-saherelm.xtrade.class.mq5" -#include "../Helpers/x-saherelm.xcc.helper.mq5" -#include "../Helpers/x-saherelm.xct.helper.mq5" -#include "../X121SMCEA/Classes/x-121.smc.ea.panel.class.mq5" -#include "../X121SMCEA/Strategy/x-121.smc.ob.strategy.class.mq5" -#include "../X121SMCEA/Strategy/x-121.smc.supdem.strategy.class.mq5" +#include "../X121SMCEA/Experts/x-121.smc.expert.class.mq5" // // Inputs ... @@ -47,6 +41,14 @@ input group "Symbols"; input bool eaMultiSymbol = false; // Use Multi Symbol input string eaSymbols = "EURUSDb,GBPUSDb,USDJPYb,USDCHFb,DowJones30,XAUUSDb"; // Trading Symbols +// +// Volume ... +input group "Volume"; +input bool eaUseDynamicVolume = false; // Use Dynamic Volume +input double eaDynamicVolumeStep = 0.01; // Increase Volume Step +input double eaDynamicVolumeBalanceFactor = 200; // Balance Factor for Generate Dynamic Volume +input double eaVolume = 0.05; // Static Volume + // // Signalling ... input group "Signalling"; @@ -58,11 +60,11 @@ input bool eaAllowShort = true; // Allow Short Signals // Here we Provider All Requirements for // Configuring Alert Provider ... input group "Alert"; -input bool eAEnableAlerts = true; // Enable Alerts -input bool eALogAlerts = true; // Log Alerts -input bool eAMailAlerts = false; // Mail Alerts -input bool eAPushAlerts = false; // Push Alerts -input bool eATerminalAlerts = false; // Terminal Alerts +input bool eaEnableAlerts = true; // Enable Alerts +input bool eaLogAlerts = true; // Log Alerts +input bool eaMailAlerts = false; // Mail Alerts +input bool eaPushAlerts = false; // Push Alerts +input bool eaTerminalAlerts = false; // Terminal Alerts // // Time Report ... @@ -75,21 +77,7 @@ input bool eaReportNewHours = false; // Report New Hours // // Define Local Variables ... -// -string eaLogTag; // EA Log Prefix ... -XSCAlert *eaAlert; // Alert Provider for Expert Adviser ... -XSCTrade *eaTrader; // Trader of Expert Adviser ... -XTimeTracker eaTimeTracker; // Time Tracker for Providing Reports ... -XSCXCTHelper *eaCTHelper; // Bar Timer Indicator Helper class ... -XSCXCCHelper *eaCCHelper; // Chart Styler Indicator Helper Class ... -X121CAppDialog eaMainWindow; // EA Main Dialog ... -XCX121SMCBaseStrategy *eaStrategies[]; // Registere Strategies in EA ... - -// -// Signalling Controller Conditions ... -bool eaDisableSignalling; -bool eaAllowLongSignalling; -bool eaAllowShortSignalling; +XC121SMCExpert eaExpert; // // Event Handlers ... @@ -114,28 +102,55 @@ int OnInit() } // - // Initial Controller Conditions ... - InitControllerConditions(); + // Configure EA Expert Class ... // - // Initialize all Requirements ... - if (!InitEA()) + eaExpert.OnSignalEventListener = OnSignalRecieved; + eaExpert.OnPositionSLEventListener = OnStopLossTriggered; + eaExpert.OnPositionTPEventListener = OnTakeProfitTriggered; + eaExpert.OnPositionForceCloseEventListener = OnPositionForceClosed; + + // + eaExpert.MagicNumber(eaMagicNumber); + eaExpert.Slippage(eaSlippage); + eaExpert.TagPrefix(eaLogSuffix); + + // + eaExpert.SetSymbol(_Symbol); + eaExpert.SetPeriod(_Period); + eaExpert.Symbols(eaSymbols); + eaExpert.MultiSymbol(eaMultiSymbol); + + // + eaExpert.AllowLong(eaAllowLong); + eaExpert.AllowShort(eaAllowShort); + + // + eaExpert.SetAlertEnableAlerts(eaEnableAlerts); + eaExpert.SetAlertLogAlerts(eaLogAlerts); + eaExpert.SetAlertMailAlerts(eaMailAlerts); + eaExpert.SetAlertPushAlerts(eaPushAlerts); + eaExpert.SetAlertTerminalAlerts(eaTerminalAlerts); + + // + eaExpert.ReportNewMonths(eaReportNewMonths); + eaExpert.ReportNewWeeks(eaReportNewWeeks); + eaExpert.ReportNewDays(eaReportNewDays); + eaExpert.ReportNewHours(eaReportNewHours); + + // + eaExpert.Volume(eaVolume); + eaExpert.UseDynamicVolume(eaUseDynamicVolume); + eaExpert.DynamicVolumeStep(eaDynamicVolumeStep); + eaExpert.DynamicVolumeBalanceFactor(eaDynamicVolumeBalanceFactor); + + // + bool isInited = eaExpert.HandleOnInit(); + if (!isInited) { return INIT_FAILED; } - // - // Initialize all GUI Objects and Classes ... - if (!InitGUI()) - { - return INIT_FAILED; - } - - // - // Alert Initialization ... - string msg = "Initialized Successfully ..."; - eaAlert.Alert(msg); - // // Init Succeed ... return INIT_SUCCEEDED; @@ -159,46 +174,28 @@ void OnDeinit(const int reason) // // Destroy Timer ... - DestroyTimer(); - - // - // Destroy all GUI Requirements ... - DestroyGUI(); - - // - // Alert De Initialization Succeeded ... - string msg = "De Initialized Successfully ..."; - eaAlert.Alert(msg); - - // - // Destroy all EA Requirements ... - DestroyEA(); + eaExpert.HandleOnDeInit(); } // // On Tick Handler ... void OnTick() { - // - UpdateGUI(); - HandleReportTime(); - UpdatePositionsState(); - HandleStrategiesOnTick(); + eaExpert.HandleOnTick(); } // // Handle On Trade Event ... void OnTrade() { - eaTrader.HandleOnTrade(); + eaExpert.HandleOnTrade(); } // // On Timer ... void OnTimer() { - // - // TODO: Implement Time Based Functionalities ... + eaExpert.HandleOnTimer(); } // @@ -210,59 +207,11 @@ void OnChartEvent( const string &sparam // ) { - // - // Apply Chart Event on Main Window ... - eaMainWindow.ChartEvent(id, lparam, dparam, sparam); - - // - // Handle OnClick Event ... - if (id == CHARTEVENT_OBJECT_CLICK) - { - HandleOnClickEvent(sparam); - } + eaExpert.HandleOnChartEvent(id, lparam, dparam, sparam); } // -// Custom Event Handlers ... - -/** - * Handle StopLoss Triggered Positions ... - * - * @param deal: XDeal ... - */ -void OnStopLossTriggered(const XDeal &deal) -{ - // - HandleReportBalance(); -} - -/** - * Handle Take Profit Triggered Positions ... - * - * @param deal: XDeal ... - */ -void OnTakeProfitTriggered(const XDeal &deal) -{ - // - HandleReportBalance(); -} - -/** - * Handle Force Close a Position ... - * - * @param ticket: Position Ticket ... - * @param position: XPosition ... - * @param comment: Closing Comment ... - */ -void OnPositionForceClosed( - const ulong ticket, - const XPosition &position, - const string comment // -) -{ - // - HandleReportBalance(); -} +// Event Listeners ... /** * Do What we Want by Specific Signal ... @@ -276,13 +225,50 @@ void OnSignalRecieved( ) { // - // TODO: Implement this ... - - // - ENUM_X_SIGNAL_EXECUTION_RESULT state; - eaTrader.ExecuteSignal( + eaExpert.HandleOnSignalRecieved( signal, - state // + conditions // + ); +} + +/** + * Handle StopLoss Triggered Positions ... + * + * @param deal: XDeal instance ... + */ +void OnStopLossTriggered(const XDeal &deal) +{ + eaExpert.HandleOnStopLossTriggered(deal); +} + +/** + * Handle Take Profit Triggered Positions ... + * + * @param deal: XDeal instance ... + */ +void OnTakeProfitTriggered(const XDeal &deal) +{ + eaExpert.HandleOnTakeProfitTriggered(deal); +} + +/** + * Handle Force Close a Position ... + * + * @param ticket: ULONG ... + * @param position: XPosition instance ... + * @param comment: String ... + */ +void OnPositionForceClosed( + const ulong ticket, + const XPosition &position, + const string comment // +) +{ + // + eaExpert.HandleOnPositionForceClosed( + ticket, + position, + comment // ); } @@ -368,465 +354,4 @@ void DestroyTimer() // EventKillTimer(); } -/** - * Initialize all Requirements for Working Fine ... - * - * @return ( bool ) - */ -bool InitEA() -{ - // - bool result = false; - - // - // Locally Apply Changes on Prefix ... - eaLogTag = ShortName + ">" + eaLogSuffix; - - // - // Bar Timer ... - XCTInputs ctInputs; - ctInputs.Default(); // Default Configurations ... - eaCTHelper = new XSCXCTHelper(); - result = eaCTHelper.Init( - _Symbol, - _Period, - ctInputs // - ); - if (!result) - { - return result; - } - - // - // Bar Styles ... - XCCInputs ccInputs; - ccInputs.Default(); // Default Configurations ... - eaCCHelper = new XSCXCCHelper(); - result = eaCCHelper.Init( - _Symbol, - _Period, - ccInputs // - ); - if (!result) - { - return result; - } - - // - // Create Instance of Alert Class and Configure it ... - eaAlert = new XSCAlert(); - eaAlert.SetPrefix(eaLogTag); - eaAlert.SetLogAlerts(eALogAlerts); - eaAlert.SetMailAlerts(eAMailAlerts); - eaAlert.SetPushAlerts(eAPushAlerts); - eaAlert.SetEnableAlerts(eAEnableAlerts); - eaAlert.SetTerminalAlerts(eATerminalAlerts); - - // - // Create Trader Instance and Configure it ... - eaTrader = new XSCTrade( - eaSlippage, - eaMagicNumber // - ); - eaTrader.AddOnStopLossEventHandler(OnStopLossTriggered); - eaTrader.AddOnTakeProfitEventHandler(OnTakeProfitTriggered); - eaTrader.AddOnForceClosePositionEventHandler(OnPositionForceClosed); - - // - // Symbols Parsing ... - - // - // Parsers ... - XSymbolParser _symbolParser; - - // - // Single Symbol ... - if (!eaMultiSymbol) - { - // - // Register Strategy ... - - // - // Create Class Instance ... - XCX121SMCBaseStrategy *iX121SMCStrategy; - iX121SMCStrategy = new XCX121SMCOBStrategy( - _Symbol, - _Period // - ); - - // - iX121SMCStrategy.AddXSignalEventHandler(OnSignalRecieved); - - // - ConfigureStrategy(iX121SMCStrategy); - RegisterStrategy(iX121SMCStrategy); - } - // - // Multi Symbol ... - else - { - // - // Parse Symbols ... - string symbols[]; - int symbolsCount = SplitContent( - symbols, - eaSymbols // - ); - result = IsValidSize(symbolsCount); - if (!result) - { - return result; - } - - // - for (int i = 0; i < symbolsCount; i++) - { - // - // Select Symbol ... - string iSymbol = symbols[i]; - - // - // Register Strategy Based On Symbol ... - - // - // Parse Symbol ... - bool isEURUSD = _symbolParser.IsEURUSD(_Symbol); - bool isGBPUSD = _symbolParser.IsGBPUSD(_Symbol); - bool isEURGBP = _symbolParser.IsEURGBP(_Symbol); - bool isUSDCHF = _symbolParser.IsUSDCHF(_Symbol); - bool isEURCHF = _symbolParser.IsEURCHF(_Symbol); - bool isUSDJPY = _symbolParser.IsUSDJPY(_Symbol); - bool isEURJPY = _symbolParser.IsEURJPY(_Symbol); - bool isUSDCAD = _symbolParser.IsUSDCAD(_Symbol); - bool isEURCAD = _symbolParser.IsEURCAD(_Symbol); - bool isAUDUSD = _symbolParser.IsAUDUSD(_Symbol); - bool isEURAUD = _symbolParser.IsEURAUD(_Symbol); - bool isNZDUSD = _symbolParser.IsNZDUSD(_Symbol); - bool isEURNZD = _symbolParser.IsEURNZD(_Symbol); - - // - // Check Symbol Can Register or not ... - bool canRegisterStrategy = true; - - // - if (canRegisterStrategy) - { - // - // Create Class Instance ... - XCX121SMCBaseStrategy *iX121SMCStrategy; - iX121SMCStrategy = new XCX121SMCOBStrategy( - iSymbol, - _Period // - ); - - // - iX121SMCStrategy.AddXSignalEventHandler(OnSignalRecieved); - - // - // Register Strategy ... - ConfigureStrategy(iX121SMCStrategy); - RegisterStrategy(iX121SMCStrategy); - } - } - } - - // - return result; -} - -/** - * Destroy all Initialized EA Requirements ... - */ -void DestroyEA() -{ - // - delete eaCTHelper; - delete eaCCHelper; - - // - delete eaAlert; -} - -/** - * Initialize all GUI Requirements for EA ... - * - * @return ( bool ) - */ -bool InitGUI() -{ - // - bool result = false; - - // - // Initialize Application Main Window ... - result = eaMainWindow.Create( - ChartID(), - ShortName, // Dialog Name ... - 0, // SubWindow ... - 10, // X1 ... - 35, // Y1 ... - 300, // X2 ... - 200 // Y2 ... - ); - - // - // Try To Run Main Window ... - if (result) - { - // - eaMainWindow.Configure(); - eaMainWindow.Run(); - } - - // - return result; -} - -/** - * Update GUI based on Each Ticks ... - */ -void UpdateGUI() -{ - // - // TODO: Implement this ... -} - -/** - * Destroy all Initialized GUi Requirements ... - */ -void DestroyGUI() -{ - // - eaMainWindow.Destroy(); -} - -// -// Others ... - -/** - * Update Positions States ... - */ -void UpdatePositionsState() -{ - // - // TODO: Implement this ... -} - -/** - * Handle On Click Event for GUI Components ... - * - * @param name: Object Name ... - */ -void HandleOnClickEvent(string name) -{ - // - if (!IsValid(name)) - { - return; - } - - // - bool isClicked = false; - - // - // TODO: Implement Chart Objects On Click ... - - // - if (isClicked) - { - UpdateGUI(); - } -} - -/** - * Report Account Balance ... - */ -void HandleReportBalance() -{ - // - if (!IsRunningOnTestMode()) - { - return; - } - - // - string msg = "Balance: " + ToString(eaTrader.mAccount.GetBalance()); - eaAlert.Log(msg); -} - -/** - * Time Reporting based on Inputs ... - */ -void HandleReportTime() -{ - // - // Monthly Report .... - if (eaReportNewMonths && - eaTimeTracker.IsNewMonth()) - { - // - string msg = "New Month ..."; - - // - eaAlert.Alert(msg); - } - - // - // Weekly Report .... - if (eaReportNewWeeks && - eaTimeTracker.IsNewWeek()) - { - // - string msg = "New Week ..."; - - // - eaAlert.Alert(msg); - } - - // - // Daily Report .... - if (eaReportNewDays && - eaTimeTracker.IsNewDay()) - { - // - string msg = "New Day ..."; - - // - eaAlert.Alert(msg); - } - - // - // Hourly Report .... - if (eaReportNewHours && - eaTimeTracker.IsNewHour()) - { - // - string msg = "New Hour ..."; - - // - eaAlert.Alert(msg); - } -} - -/** - * Initial EA Controller Conditions ... - */ -void InitControllerConditions() -{ - // - // Signalling Conditions ... - eaAllowLongSignalling = eaAllowLong; - eaAllowShortSignalling = eaAllowShort; - - // - eaDisableSignalling = !eaAllowLongSignalling && - !eaAllowShortSignalling; -} - -// - -/** - * Configure Startegy ... - */ -void ConfigureStrategy(XCX121SMCBaseStrategy *&strategy) -{ - // - if (strategy == NULL) - { - return; - } - - // - // Configure Alerts ... - strategy.SetAlertPrefix(eaLogTag); - strategy.SetAlertLogAlerts(eALogAlerts); - strategy.SetAlertMailAlerts(eAMailAlerts); - strategy.SetAlertPushAlerts(eAPushAlerts); - strategy.SetAlertEnableAlerts(eAEnableAlerts); - strategy.SetAlertTerminalAlerts(eATerminalAlerts); - - // - // Confiugre Signalling ... - - // - strategy.AllowLong(eaAllowLongSignalling); - strategy.AllowShort(eaAllowShortSignalling); - - // - if (eaDisableSignalling) - { - strategy.Disable(); - } - else - { - strategy.Enable(); - } - - // -} - -/** - * Register an Strategy in EA ... - * - * @param strategy: XCX121SMCBaseStrategy instance ... - */ -void RegisterStrategy(XCX121SMCBaseStrategy *strategy) -{ - // - if (strategy == NULL) - { - return; - } - - // - ArrayResize( - eaStrategies, - ArraySize(eaStrategies) + 1 // - ); - - // - eaStrategies[ArraySize(eaStrategies) - 1] = strategy; -} - -/** - * Re Configure All Registered Strategies ... - */ -void ReConfigureAllStrategies() -{ - // - int count = ArraySize(eaStrategies); - if (!IsValidSize(count)) - { - return; - } - - // - for (int i = 0; i < count; i++) - { - // - ConfigureStrategy(eaStrategies[i]); - } -} - -/** - * Call all Registered Strategies On Tick ... - */ -void HandleStrategiesOnTick() -{ - // - int count = ArraySize(eaStrategies); - if (!IsValidSize(count)) - { - return; - } - - // - for (int i = 0; i < count; i++) - { - // - // Call Tick Handler Function ... - eaStrategies[i].HandleTick(); - } -} - // diff --git a/X121SMCEA/Classes/x-121.smc.xtradehandler.class.mq5 b/X121SMCEA/Classes/x-121.smc.xtradehandler.class.mq5 new file mode 100644 index 00000000..c4d2e1c4 --- /dev/null +++ b/X121SMCEA/Classes/x-121.smc.xtradehandler.class.mq5 @@ -0,0 +1,3578 @@ +/////////////////////////////////////////////////////// +// +// SaherElm IT Center MQL5 Class Library +// -------------------------------------- +// Name: XCX121SMCTradeHandler +// Description: provides all based 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 + +// +// Imports ... + +// +#include "../../Classes/x-saherelm.xalert.class.mq5" +#include "../../Classes/x-saherelm.xtrade.class.mq5" +#include "../Libraries/x-121.smc.lib.mq5" + +// +// Definitions ... + +// +// Model a Trade Data ... +struct X121SMCTradeData +{ + // + 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 + + // + int trailStep; // Trail Step + bool partiallyClosed; // Partially Closed or not + datetime lastProtectedOn; // Last Protected On + + // + // Constructor ... + void X121SMCTradeData() + { + 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; + + // + trailStep = 0; + lastProtectedOn = NULL; + partiallyClosed = false; + + // + 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 X121SMCProtectedPosition +{ + // + XPosition main; // Main Position ... + X121SMCTradeData 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; + } +}; + +// +// Implementations ... + +// +// a Class For Read and Write Trade Info Data in Files ... +class X121SMCTradeCollector +{ + // + // Public ... + public: + // + // Props ... + + // + // Constructor(s) ... + void X121SMCTradeCollector( + string _path = NULL // Base Path + ) + { + // + mAccount = new XSCAccount(); + + // + if (IsValid(_path)) + { + mPath = _path; + } + else + { + mPath = "X121SMCTradeData" + "\\" + mAccount.GetCompany(); + } + } + + // + // Deconstructor ... + void ~X121SMCTradeCollector() + { + } + + // + bool IsExists(X121SMCTradeData &item) + { + // + bool result = false; + + // + int mHandler = GetFileHandlerForRead(item); + result = mHandler != INVALID_HANDLE; + FileClose(mHandler); + + // + return result; + } + + // + bool Save(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &item) + { + // + string fileName = item.GetFileName(); + + // + return GetFilePath(fileName); + } + string GetFilePath(string fileName) + { + // + string result = ""; + + // + result = + // + mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log" + // + ; + + // + return result; + } + + // + string GetSignalFilePath(X121SMCTradeData &item) + { + // + string fileName = item.GetSignalFileName(); + + // + return GetSignalFilePath(fileName); + } + string GetSignalFilePath(string fileName) + { + // + string result = ""; + + // + result = + // + mPath + "\\" + "Signals" + "\\" + fileName + ".x121.log" + // + ; + + // + return result; + } + + // + string GetConditionsFilePath(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &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 XC121SMCTradeHandler : public XSCBaseAlert +{ + // + // Public ... + public: + // + + // + // Constructor(s) ... + void XC121SMCTradeHandler(XSCTrade *trader) + { + // + mTrader = trader; + mAdditionalVolume = 0; + mCollector = new X121SMCTradeCollector(); + } + + // + // Deconstructor ... + void ~XC121SMCTradeHandler() + { + // + 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; + } + + // + // Protector Start ... + + // + bool UseForceMomentumsInProtection() + { + return mUseForceMomentumsInProtection; + } + + // + void UseForceMomentumsInProtection(bool value) + { + mUseForceMomentumsInProtection = value; + } + + // + int DelayBarBetweenTwoSignal() + { + return mDelayBarBetweenTwoSignal; + } + + // + void DelayBarBetweenTwoSignal(int value) + { + // + if (value < 0) + { + value = 0; + } + + // + mDelayBarBetweenTwoSignal = value; + } + + // + double AcceptNextSameTypeSignalWhenLastInProfit() + { + return mAcceptNextSameTypeSignalWhenLastInProfit; + } + + // + void AcceptNextSameTypeSignalWhenLastInProfit(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mAcceptNextSameTypeSignalWhenLastInProfit = value; + } + + // + // In Profit Position Protecting ... + + // + // Trail ... + + // + bool AllowTrail() + { + return mAllowTrail; + } + + // + void AllowTrail(bool value) + { + mAllowTrail = value; + } + + // + double TrailStartDistance() + { + return mTrailStartDistance; + } + + // + void TrailStartDistance(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mTrailStartDistance = value; + } + + // + double TrailStep() + { + return mTrailStep; + } + + // + void TrailStep(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mTrailStep = value; + } + + // + int RemoveTPAfterTrailedLevel() + { + return mRemoveTPAfterTrailedLevel; + } + + // + void RemoveTPAfterTrailedLevel(int value) + { + // + if (value < 0) + { + value = 0; + } + + // + mRemoveTPAfterTrailedLevel = value; + } + + // + bool OnlyTrailUnprotected() + { + return mOnlyTrailUnprotected; + } + + // + void OnlyTrailUnprotected(bool value) + { + mOnlyTrailUnprotected = value; + } + + // + // Partial Close (In Profit) ... + + // + double PartialCloseInProfitDistance() + { + return mPartialCloseInProfitDistance; + } + + // + void PartialCloseInProfitDistance(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mPartialCloseInProfitDistance = value; + } + + // + double PartialCloseInProfitVolume() + { + return mPartialCloseInProfitVolume; + } + + // + void PartialCloseInProfitVolume(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mPartialCloseInProfitVolume = value; + } + + // + // In Drawdown Positions Protecting ... + + // + // Protect ... + + // + bool AllowProtect() + { + return mAllowProtect; + } + + // + void AllowProtect(bool value) + { + mAllowProtect = value; + } + + // + double ProtectionStartDistance() + { + return mProtectionStartDistance; + } + + // + void ProtectionStartDistance(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mProtectionStartDistance = value; + } + + // + int ProtectionDelay() + { + return mProtectionDelay; + } + + // + void ProtectionDelay(int value) + { + // + if (value < 0) + { + value = 0; + } + + // + mProtectionDelay = value; + } + + // + double ProtectionVolumeMultiplier() + { + return mProtectionVolumeMultiplier; + } + + // + void ProtectionVolumeMultiplier(double value) + { + // + if (value < 1) + { + value = 1; + } + + // + mProtectionVolumeMultiplier = value; + } + + // + int MaxAllowedProtection() + { + return mMaxAllowedProtection; + } + + // + void MaxAllowedProtection(int value) + { + // + if (value < 0) + { + value = 0; + } + + // + mMaxAllowedProtection = value; + } + + // + bool UseEntryAsProtectionSL() + { + return mUseEntryAsProtectionSL; + } + + // + void UseEntryAsProtectionSL(bool value) + { + mUseEntryAsProtectionSL = value; + } + + // + bool ProtectOnConditions() + { + return mProtectOnConditions; + } + + // + void ProtectOnConditions(bool value) + { + mProtectOnConditions = value; + } + + // + bool AllowDirectionProtct() + { + return mAllowDirectionProtct; + } + + // + void AllowDirectionProtct(bool value) + { + mAllowDirectionProtct = value; + } + + // + // Partial Close (In Drawdown) ... + + // + double PartialCloseInDrawdownDistance() + { + return mPartialCloseInDrawdownDistance; + } + + // + void PartialCloseInDrawdownDistance(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mPartialCloseInDrawdownDistance = value; + } + + // + double PartialCloseInDrawdownVolume() + { + return mPartialCloseInDrawdownVolume; + } + + // + void PartialCloseInDrawdownVolume(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mPartialCloseInDrawdownVolume = 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; + } + + // + double AdditionalVolume() + { + return mAdditionalVolume; + } + + // + // Position Retrievers ... + + /** + * Retrieve Positions as XProtetedPosition structure + * @param positions: Argument 1 + * @return ( int ) + */ + int GetPositions( + X121SMCProtectedPosition &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]; + + // + X121SMCProtectedPosition 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 X121SMCTradeData ... + 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; + + // + X121SMCTradeData 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; + } + } + } + + // + X121SMCTradeData 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 ... + + /** + * Validate Signal For Execution ... + */ + bool CanExecute(XSignal &signal) + { + // + bool result = false; + + // + // Validate Signal ... + result = signal.IsValid(); + if (!result) + { + return result; + } + + // + bool isLong = IsLong(signal.type); + + // + // Chekc Signalling Enable or not ... + result = + isLong + ? mAllowLong + : mAllowShort; + if (!result) + { + return result; + } + + // + // Check Signal is Support or not ... + bool isSupport = IsSupport(signal.comment); + result = !isSupport; + if (!result) + { + return result; + } + + // + ENUM_X_POSITION_TYPES pType = ToPositionType(signal.type); + double requiredProfitForAcceptNext = AcceptNextSameTypeSignalWhenLastInProfit(); + + // + // Implement Same Signal Conditions ... + XPosition smaeSymbolPositions[]; + int sameSymbolPositionsCount = mTrader.GetPositions( + smaeSymbolPositions, + signal.symbol, + signal.provider, + signal.period, + pType, + true, // Filter By Magic ... + true // Force Clean ... + ); + XPosition youngestSame; + int youngestSameAge = GetYoungest( + youngestSame, + smaeSymbolPositions // + ); + + // + double youngestSameProfitInPoint = youngestSame.GetProfitInPoint(); + + // + result = + sameSymbolPositionsCount == 0 || + (sameSymbolPositionsCount <= 0 + ? true + : requiredProfitForAcceptNext <= 0 + ? true + : youngestSameAge >= 0 && + youngestSameProfitInPoint >= requiredProfitForAcceptNext); + + // + 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; + } + + // + // Check Policies ... + if (!ignorePolicies) + { + // + // Filter Signals if Necessary ... + result = CanExecute(signal); + if (!result) + { + // + state = X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS; + return result; + } + + // + 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 Position Delays ... + if (result && mDelayBarBetweenTwoSignal > 0) + { + // + int youngestAge = 0; + XPosition youngestPosition; + + // + // Long Positions when there are Longs ... + if (isLong && longsCount > 0) + { + // + youngestAge = GetYoungest( + youngestPosition, + longs // + ); + } + // + // Short Positions when there are Shorts ... + else if (!isLong && shortsCount > 0) + { + // + youngestAge = GetYoungest( + youngestPosition, + shorts // + ); + } + + // + // Only Check when there is must to check and conditions happens ... + if (youngestAge > 0 && youngestPosition.IsValid()) + { + // + result = youngestAge >= mDelayBarBetweenTwoSignal; + 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; + } + } + } + + // + // Add Additional Volume to Signal ... + signal.volume += mAdditionalVolume; + + // + // 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 ... + X121SMCProtectedPosition 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; + } + + // + return; + } + + // + int protectedPositions = 0; + for (int i = 0; i < pPositionsCount; i++) + { + // + // Check Position is Protectable or not ... + X121SMCProtectedPosition iProtected = pPositions[i]; + XPosition iPosition = iProtected.main; + + // + bool isProtected = HandlePositionProtecting( + iPosition, + iProtected.supports, + pPositions // + ); + if (isProtected) + { + protectedPositions++; + } + } + + // + if (protectedPositions > 0) + { + // + // Renew Data ... + Clean(pPositions); + int pPositionsCount = GetPositions( + pPositions // + ); + + // + // Validate Count ... + if (!IsValidSize(pPositionsCount)) + { + return; + } + } + } + + // + // Protected ... + protected: + // + XSCTrade *mTrader; // Instance of Trader Class + X121SMCTradeCollector *mCollector; // Instance of Trade Collector Class + + // + X121SMCTradeData mData[]; // Hold Trade Data + + // + // Hedge Related Functions ... + + /** + * Handle Hedging On Positions ... + */ + bool HandleHedge(X121SMCProtectedPosition &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++) + { + // + X121SMCProtectedPosition 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(); + double beMinProfitPerVolumeStep = HedgeBEMinProfitPerVolumeStep(); + + // + // 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) + { + // + // Check Profits ... + result = profits > 0; + if (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) + { + // + int flatPositionsCount = ArraySize(flatPositions); + result = IsValidSize(flatPositionsCount); + if (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 && + beMinProfitPerVolumeStep > 0; + if (allowBEHedge) + { + // + // Implement BE Hedge ... + + // + // Check Profits ... + result = profits > 0; + if (result) + { + // + // Calculate Required Profit ... + double volumeSteps = volumes / minVolumeStep; + double minRequiredProfit = (volumeSteps * beMinProfitPerVolumeStep) + + commissions + (-1 * swaps); + + // + // Check Profits Satisfied Required Profit's For Hedge or not ... + result = profits >= minRequiredProfit; + if (result) + { + // + int flatPositionsCount = ArraySize(flatPositions); + result = IsValidSize(flatPositionsCount); + if (result) + { + // + string comment = "EQM BE Hedge ..."; + int closed = mTrader.Close( + flatPositions, + comment // + ); + + // + result = closed > 0; + if (result) + { + // + string message = "EQM BE Hedge Close (" + ToString(flatPositionsCount) + ") Positions by: " + ToString(profits); + Alert(message); + + // + return result; + } + } + } + } + + // + return result; + } + + // + return result; + } + + /** + * Handle Protecting Position ... + */ + bool HandlePositionProtecting( + XPosition &position, // Main Position + XPosition &supports[], // Supports + X121SMCProtectedPosition &positions[] // All Positions + ) + { + // + bool result = false; + + // + // Find Position's Item Data ... + int iDX = -1; + result = HasItem( + position.ticket, + iDX // + ); + if (!result) + { + return result; + } + + // + int supportsCount = ArraySize(supports); + + // + // Handle Protection Of Protection Signals ... + if (supportsCount > 0) + { + HandleProtectionOfProtectors(supports); + } + + // + // Here we Make Sure this Position has reached Proper distance ... + + // + bool isLong = IsLong(position.type); + bool isInProfit = position.profit > 0; + datetime currentTime = TimeCurrent(); + + // + double profitInPoint = position.GetProfitInPoint(); + double pointValue = position.GetPointsValue(); + + // + // Determines there is a Freezed Position or not ... + bool hasFreezed = false; + int positionsCount = ArraySize(positions); + if (IsValidSize(positionsCount)) + { + // + for (int i = 0; i < positionsCount; i++) + { + // + int iSupportsCount = ArraySize(positions[i].supports); + + // + hasFreezed = iSupportsCount == 1 && + positions[i].main.tp == 0 && + positions[i].main.sl == 0 && + positions[i].supports[0].tp == 0 && + positions[i].supports[0].sl == 0 && + positions[i].main.volume == positions[i].supports[0].volume; + if (hasFreezed) + { + break; + } + } + } + + // + // Reading Requirements for Trailling Stops ... + bool allowTrail = AllowTrail(); + double trailStep = TrailStep(); + double trailStartDistance = TrailStartDistance(); + bool allowOnlyUnprotecteds = OnlyTrailUnprotected(); + int removeTPAfterTrailedLevel = RemoveTPAfterTrailedLevel(); + bool disableTrailWhenFreezedPosition = false; // DisableTrailWhenFreezedPosition(); + bool canTrail = isInProfit && + allowTrail && + trailStep > 0 && + profitInPoint > 0 && + trailStartDistance > 0 && + (!allowOnlyUnprotecteds + ? true + : supportsCount <= 0) && + profitInPoint >= trailStartDistance && + (!disableTrailWhenFreezedPosition + ? true + : !hasFreezed); + + // + // Reading Requirements for Partial Close ... + // In Profit ... + double partialCloseInProfitDistance = PartialCloseInProfitDistance(); + double partialCloseInProfitVolume = PartialCloseInProfitVolume(); + bool canPartialCloseInProfit = isInProfit && + profitInPoint > 0 && + !mData[iDX].partiallyClosed && + partialCloseInProfitVolume > 0 && + profitInPoint >= partialCloseInProfitDistance; + + // + // In Drawdown ... + double partialCloseInDrawdownDistance = PartialCloseInProfitDistance(); + double partialCloseInDrawdownVolume = PartialCloseInProfitVolume(); + bool canPartialCloseInDrawdown = !isInProfit && + profitInPoint < 0 && + !mData[iDX].partiallyClosed && + partialCloseInDrawdownVolume > 0 && + MathAbs(profitInPoint) >= partialCloseInDrawdownDistance; + + // + double partialCloseVolume = canPartialCloseInProfit + ? partialCloseInProfitVolume + : canPartialCloseInDrawdown + ? partialCloseInDrawdownVolume + : 0; + + // + bool canPartialClose = partialCloseVolume > 0 && + (canPartialCloseInProfit || + canPartialCloseInDrawdown); + + // + // Reading Requirement for Freezing Positions ... + + // + // Protect ... + bool allowProtect = AllowProtect(); + int protectionDelay = ProtectionDelay(); + bool protectOnConditions = ProtectOnConditions(); + int maxAllowedProtection = MaxAllowedProtection(); + bool useEntryAsProtectionSL = UseEntryAsProtectionSL(); + double protectDistance = ProtectionStartDistance(); + bool allowDirectionProtect = AllowDirectionProtct(); + double protectVolumeMultiplier = ProtectionVolumeMultiplier(); + bool canProtect = + !isInProfit && + allowProtect && + protectDistance > 0 && + protectionDelay >= 0 && + protectVolumeMultiplier >= 1 && + (maxAllowedProtection <= 0 + ? true + : supportsCount < maxAllowedProtection) && + MathAbs(profitInPoint) >= MathAbs(protectDistance); + + // + result = + canTrail || + canProtect || + canPartialClose; + if (!result) + { + return result; + } + + // + int zIndex = 0; + int cIndex = zIndex + 1; + int pIndex = cIndex + 1; + int ppIndex = pIndex + 1; + + // + // Last Bar ... + XOHCL cBar; + result = cBar.Init( + position.symbol, + position.period, + cIndex // + ); + if (!result) + { + return result; + } + + // + // Previous Bar of Last Bar ... + XOHCL pBar; + result = cBar.GetPreviousBar(pBar); + if (!result) + { + return result; + } + + // + // Last Swing Low Bar ... + XOHCL lastSwingLow; + result = cBar.FindLastSwingLow(lastSwingLow); + if (!result) + { + return result; + } + + // + // Last Swing High Bar ... + XOHCL lastSwingHigh; + result = cBar.FindLastSwingHigh(lastSwingHigh); + if (!result) + { + return result; + } + + // + // Common Conditions For Bullish and Bearish Powers ... + + // + // For Trend ... + XOHCL tmpHSW[]; + XOHCL tmpLSW[]; + + // + // Detect Bullish/Bearish Patterns and Trends ... + + // + // Bullish ... + bool hasBullishPower = cBar.HasBullishPower(); + bool hasBullishPattern = HasBullishPattern(cBar); + bool hasBullishPressure = cBar.HasBullishPressure(); + bool isBullishTrend = cBar.HasBullishTrend( + tmpHSW, + tmpLSW, + true // + ); + bool isBullishTrend1 = cBar.HasBullishTrend( + tmpHSW, + tmpLSW, + false // + ); + bool hasBullishTrend = + // + isBullishTrend + // + || + // + isBullishTrend1 + // + ; + + // + // Bearish ... + bool hasBearishPower = cBar.HasBearishPower(); + bool hasBearishPattern = HasBearishPattern(cBar); + bool hasBearishPressure = cBar.HasBearishPressure(); + bool isBearishTrend = cBar.HasBearishTrend( + tmpHSW, + tmpLSW, + true // + ); + bool isBearishTrend1 = cBar.HasBearishTrend( + tmpHSW, + tmpLSW, + false // + ); + bool hasBearishTrend = + // + isBearishTrend + // + || + // + isBearishTrend1 + // + ; + + // + // Define Conditions ... + MqlTick ticks[]; + int ticksCount = CopyTicks( + position.symbol, + ticks, + COPY_TICKS_ALL, + 0, + 5 // + ); + + // + bool isTicksBullishForLong = + // + GetTickEntry(ticks[0], POSITION_TYPE_BUY, position.symbol) > GetTickEntry(ticks[1], POSITION_TYPE_BUY, position.symbol) && + GetTickEntry(ticks[1], POSITION_TYPE_BUY, position.symbol) > GetTickEntry(ticks[2], POSITION_TYPE_BUY, position.symbol) && + GetTickEntry(ticks[2], POSITION_TYPE_BUY, position.symbol) > GetTickEntry(ticks[3], POSITION_TYPE_BUY, position.symbol) + // + ; + + // + bool isTicksBearishForLong = + // + GetTickEntry(ticks[0], POSITION_TYPE_BUY, position.symbol) < GetTickEntry(ticks[1], POSITION_TYPE_BUY, position.symbol) && + GetTickEntry(ticks[1], POSITION_TYPE_BUY, position.symbol) < GetTickEntry(ticks[2], POSITION_TYPE_BUY, position.symbol) && + GetTickEntry(ticks[2], POSITION_TYPE_BUY, position.symbol) < GetTickEntry(ticks[3], POSITION_TYPE_BUY, position.symbol) + // + ; + + // + bool isTicksBullishForShort = + // + GetTickEntry(ticks[0], POSITION_TYPE_SELL, position.symbol) > GetTickEntry(ticks[1], POSITION_TYPE_SELL, position.symbol) && + GetTickEntry(ticks[1], POSITION_TYPE_SELL, position.symbol) > GetTickEntry(ticks[2], POSITION_TYPE_SELL, position.symbol) && + GetTickEntry(ticks[2], POSITION_TYPE_SELL, position.symbol) > GetTickEntry(ticks[3], POSITION_TYPE_SELL, position.symbol) + // + ; + + // + bool isTicksBearishForShort = + // + GetTickEntry(ticks[0], POSITION_TYPE_SELL, position.symbol) < GetTickEntry(ticks[1], POSITION_TYPE_SELL, position.symbol) && + GetTickEntry(ticks[1], POSITION_TYPE_SELL, position.symbol) < GetTickEntry(ticks[2], POSITION_TYPE_SELL, position.symbol) && + GetTickEntry(ticks[2], POSITION_TYPE_SELL, position.symbol) < GetTickEntry(ticks[3], POSITION_TYPE_SELL, position.symbol) + // + ; + + // + // News Momentum Change ... + + // + double hh5 = cBar.FindHighest( + 5, + MODE_HIGH // + ); + + // + double ll5 = cBar.FindLowest( + 5, + MODE_LOW // + ); + + // + bool isForecBullishMomentum = false; + bool isForceBearishMomentum = false; + bool useForceMomentum = UseForceMomentumsInProtection(); + if (ticksCount == 5 && useForceMomentum) + { + // + isForecBullishMomentum = + // + GetTickEntry(ticks[0], POSITION_TYPE_BUY, position.symbol) >= hh5 + // + && + // + ( + // + GetTickEntry(ticks[0], POSITION_TYPE_BUY, position.symbol) > GetTickEntry(ticks[1], POSITION_TYPE_BUY, position.symbol) && + GetTickEntry(ticks[1], POSITION_TYPE_BUY, position.symbol) > GetTickEntry(ticks[2], POSITION_TYPE_BUY, position.symbol) && + GetTickEntry(ticks[2], POSITION_TYPE_BUY, position.symbol) > GetTickEntry(ticks[3], POSITION_TYPE_BUY, position.symbol) && + GetTickEntry(ticks[3], POSITION_TYPE_BUY, position.symbol) > GetTickEntry(ticks[4], POSITION_TYPE_BUY, position.symbol) + // + ) + // + ; + + // + isForceBearishMomentum = + // + GetTickEntry(ticks[0], POSITION_TYPE_SELL, position.symbol) <= ll5 + // + && + // + ( + // + GetTickEntry(ticks[0], POSITION_TYPE_SELL, position.symbol) < GetTickEntry(ticks[1], POSITION_TYPE_SELL, position.symbol) && + GetTickEntry(ticks[1], POSITION_TYPE_SELL, position.symbol) < GetTickEntry(ticks[2], POSITION_TYPE_SELL, position.symbol) && + GetTickEntry(ticks[2], POSITION_TYPE_SELL, position.symbol) < GetTickEntry(ticks[3], POSITION_TYPE_SELL, position.symbol) && + GetTickEntry(ticks[3], POSITION_TYPE_SELL, position.symbol) < GetTickEntry(ticks[4], POSITION_TYPE_SELL, position.symbol) + // + ) + // + ; + } + + // + bool hasBullishDecision = + // + cBar.IsBullishDecision() + // + ; + + // + bool hasBearishDecision = + // + cBar.IsBearishDecision() + // + ; + + // + bool hasBullishConditions = + // + ( + // + hasBullishTrend && + hasBullishDecision && + (hasBullishPower || + hasBullishPattern || + hasBullishPressure) + // + ) + // + ; + + // + bool hasBearishConditions = + // + ( + // + hasBearishTrend && + hasBearishDecision && + (hasBearishPower || + hasBearishPattern || + hasBearishPressure) + // + ) + // + ; + + // + // Do Trail ... + bool isTrailed = false; + if (canTrail) + { + // + bool hasTrailConditions = + isLong + ? (isForecBullishMomentum || + (isTicksBullishForLong && + hasBullishConditions)) + : (isForceBearishMomentum || + (isTicksBearishForShort && + hasBearishConditions)); + if (hasTrailConditions) + { + // + double entry = position.entry; + int lastTrailedStep = mData[iDX].trailStep; + + // + double requiredProfit = + lastTrailedStep == 0 + ? trailStartDistance + : trailStartDistance + (lastTrailedStep * trailStep); + + // + bool isProfitsPassed = profitInPoint > requiredProfit; + if (isProfitsPassed) + { + // + double delta = (requiredProfit * pointValue); + + // + double sl = + isLong + ? entry + delta + : entry - delta; + + // + string comment = "EQM Trail Stop ..."; + + // + bool canRemoveTP = + position.tp != 0 && + removeTPAfterTrailedLevel > 0 && + mData[iDX].trailStep == removeTPAfterTrailedLevel; + double tp = + canRemoveTP + ? 0 + : position.tp; + + // + // Try to Modify Position ... + isTrailed = mTrader.Modify( + position.ticket, + sl, + tp, + comment // + ); + if (isTrailed) + { + // + mData[iDX].trailStep++; + + // + string message = "EQM Trail (" + ToString(position.ticket) + ") Stop in Level: " + ToString(mData[iDX].trailStep) + " ..."; + Alert(message); + } + } + } + // + // Force Trail When passed half way ... + else + { + // + // Handle Force Trail ... + double requiredProfit = position.GetRewardInPoint(); + if (position.sl > 0 && + position.sl < position.entry && + profitInPoint >= requiredProfit) + { + // + double delta = requiredProfit - (requiredProfit / 3); + double sl = + isLong + ? position.entry + (delta * pointValue) + : position.entry - (delta * pointValue); + + // + string comment = "EQM Force Trail Stop ..."; + + // + // Try to Modify Position ... + isTrailed = mTrader.Modify( + position.ticket, + sl, + position.tp, + comment // + ); + if (isTrailed) + { + // + mData[iDX].trailStep++; + + // + string message = "EQM Force Trail (" + ToString(position.ticket) + ") Stop ..."; + Alert(message); + } + } + } + } + + // + // Do Partial Close ... + bool isPartialClosed = false; + if (canPartialClose) + { + // + string comment = "EQM Partial Close ..."; + + // + // Do Partial Closing ... + isPartialClosed = mTrader.ClosePartial( + position.ticket, + partialCloseVolume, + comment // + ); + if (isPartialClosed) + { + // + mData[iDX].partiallyClosed = true; + + // + string message = "EQM Partially Closed " + ToString(partialCloseVolume) + " of (" + ToString(position.ticket) + ") ..."; + Alert(message); + } + } + + // + // Protect ... + bool isProtected = false; + if (canProtect) + { + // + // Protection Conditions ... + bool hasLongProtectConditions = isForecBullishMomentum || + (isTicksBullishForLong && + hasBullishConditions); + bool hasShortProtectConditions = isForceBearishMomentum || + (isTicksBearishForShort && + hasBearishConditions); + + // + // Check Protection Delay ... + datetime currentTime = TimeCurrent(); + datetime lastProtectedOn = mData[iDX].lastProtectedOn; + bool isProtectionDelayPassed = + protectionDelay <= 0 + ? true + : !IsValid(lastProtectedOn) + ? true + : (TimeToSeconds(currentTime) - TimeToSeconds(lastProtectedOn)) > (ulong)protectionDelay; + + // + // Checking Protection Conditions ... + bool hasProtectConditions = + isProtectionDelayPassed && + (!protectOnConditions + ? true + : allowDirectionProtect + ? (hasLongProtectConditions || + hasShortProtectConditions) + : isLong + ? hasShortProtectConditions + : hasLongProtectConditions); + + // + // Check Protection Conditions Happens ... + if (hasProtectConditions) + { + // + bool isInDirectProtection = + !protectOnConditions + ? true + : isLong + ? hasShortProtectConditions + : hasLongProtectConditions; + + // + double sl = + !isInDirectProtection + ? position.sl + : useEntryAsProtectionSL + ? position.entry + : position.tp; + double tp = + !isInDirectProtection + ? position.tp + : position.sl; + double volume = protectVolumeMultiplier * position.volume; + ENUM_POSITION_TYPE type = + !isInDirectProtection + ? position.type + : GetOpposit(position.type); + double entry = GetEntry( + position.symbol, + type // + ); + ENUM_X_ORDER_MODES mode = X_ORDER_MODE_MARKET; + + // + // Prepare Signal ... + XSignal signal; + bool isPrepared = signal.Prepare( + position.symbol, + position.provider, + position.period, + type, + mode, + entry, + volume, + sl, + tp // + ); + + // + if (isPrepared) + { + // + // Generate Comment for Recover and Support ... + string comment = GenerateSupportTag(position.ticket); + + // + signal.comment = comment; + + // + ENUM_X_SIGNAL_EXECUTION_RESULT state; + isProtected = ExecuteSignal( + signal, + state, + true // Support Signal need to Ignore Policies ... + ); + if (isProtected) + { + // + // Update Last Protected On Data Handler ... + mData[iDX].lastProtectedOn = currentTime; + + // + // Alert ... + string message = "EQM Open " + (IsLong(type) ? "Long" : "Short") + " Protect Position for (" + ToString(position.ticket) + ") ..."; + Alert(message); + } + } + } + } + + // + result = + isTrailed || + isProtected || + isPartialClosed; + + // + return result; + } + + /** + * Handle Protection of Support(s) ... + * + * @param supports: Supports Array ... + */ + void HandleProtectionOfProtectors(XPosition &supports[]) + { + // + int supportsCount = ArraySize(supports); + + // + if (!IsValidSize(supportsCount)) + { + return; + } + + // + // TODO: Implement Supports Protection ... + + // + // Trailling ... + } + + // + // Private ... + private: + // + // Props ... + double mAdditionalVolume; // Volume Additional + + // + // 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 + + // + // Protector ... + + // + bool mUseForceMomentumsInProtection; // Use Force Momentums In Protection; + int mDelayBarBetweenTwoSignal; // Delay Bars Between Two Signal + double mAcceptNextSameTypeSignalWhenLastInProfit; // Accept Next Same Type Signal When Last in Profit in Point + + // + // In Profit Positions Protecting ... + + // + // Trail ... + bool mAllowTrail; // Allow Trail Stop for Positions + double mTrailStartDistance; // Specify Trail Start in Point + double mTrailStep; // Trail Step in Point + int mRemoveTPAfterTrailedLevel; // Remove Trailed Position TP after Level + bool mOnlyTrailUnprotected; // Only Trail Unprotected Positionss + + // + // Partial Close (In Profit) ... + double mPartialCloseInProfitDistance; // Specify Partial Close Profit in Point + double mPartialCloseInProfitVolume; // Specify Partial Close in Profit Volume + + // + // In Drawdown Positions Protecting ... + + // + // Protect ... + bool mAllowProtect; // Allow Proptecting + double mProtectionStartDistance; // Protection Start Drawdown in Profit + double mProtectionVolumeMultiplier; // Protection Volume Multiplier + int mMaxAllowedProtection; // Max Allowed Protection + bool mUseEntryAsProtectionSL; // Use Position Entry For SL of Protect + bool mAllowDirectionProtct; // Allow also Directional Protect + bool mProtectOnConditions; // Do Protection only when Conditions happens + int mProtectionDelay; // Delay Between two Protection in Secconds + + // + // Partial Close (In Drawdown) ... + double mPartialCloseInDrawdownDistance; // Specify Partial Close Drawdown in Point + double mPartialCloseInDrawdownVolume; // Specify Partial Close in Drawdown Volume + + // + // 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(X121SMCTradeData &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(X121SMCTradeData &item) + { + // + if (!mSaveSignals) + { + return; + } + + // + // Save Signal ... + mCollector.SaveSignal(item); + } + + // +}; + +// \ No newline at end of file diff --git a/X121SMCEA/Experts/x-121.smc.expert.class.mq5 b/X121SMCEA/Experts/x-121.smc.expert.class.mq5 index 2272c029..939e61da 100644 --- a/X121SMCEA/Experts/x-121.smc.expert.class.mq5 +++ b/X121SMCEA/Experts/x-121.smc.expert.class.mq5 @@ -22,8 +22,10 @@ // // Imports ... #include "../../Classes/x-saherelm.xexpert.class.mq5" +#include "../Classes/x-121.smc.xtradehandler.class.mq5" #include "../Strategy/x-121.smc.ob.strategy.class.mq5" #include "../Strategy/x-121.smc.supdem.strategy.class.mq5" +#include "../Strategy/x-121.smc.test.strategy.class.mq5" // // Definitions ... @@ -38,6 +40,230 @@ class XC121SMCExpert : public XCBaseExpert public: // + // + // Signal Event Listener ... + TOnStopLoss OnPositionSLEventListener; + TOnTakeProfit OnPositionTPEventListener; + TOnPartialClose OnPositionPartialCloseEventListener; + TOnModify OnPositionModifiedEventListener; + TOnForceClose OnPositionForceCloseEventListener; + + // + TOnX121SMCSignal OnSignalEventListener; + + // + // Getter(s) / Setter(s) ... + + /** + * Get Use Dynamic Volume State ... + * + * @return ( bool ) + */ + bool UseDynamicVolume() + { + return mUseDynamicVolume; + } + + /** + * Set Use Dynamic Volume State ... + * + * @param value: Boolean ... + */ + void UseDynamicVolume(bool value) + { + mUseDynamicVolume = value; + } + + /** + * Get Increase Volume Step ... + * + * @return ( double ) + */ + double DynamicVolumeStep() + { + return mDynamicVolumeStep; + } + + /** + * Set Increase Volume Step ... + * + * @param value: Double ... + */ + void DynamicVolumeStep(double value) + { + // + if (value < 0.01) + { + value = 0.01; + } + + // + mDynamicVolumeStep = value; + } + + /** + * Get Balance Factor for Generate Dynamic Volume ... + * + * @return ( double ) + */ + double DynamicVolumeBalanceFactor() + { + return mDynamicVolumeBalanceFactor; + } + + /** + * Set Balance Factor for Generate Dynamic Volume ... + * + * @param value: Double ... + */ + void DynamicVolumeBalanceFactor(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mDynamicVolumeBalanceFactor = value; + } + + /** + * Get Static Volume ... + * + * @return ( double ) + */ + double Volume() + { + return mVolume; + } + + /** + * Set Static Volume ... + * + * @param value: Double ... + */ + void Volume(double value) + { + // + if (value < 0.01) + { + value = 0.01; + } + + // + mVolume = value; + } + + // + // Actions ... + + /** + * Handle OnSignalRecieved Event ... + * + * @param signal: XSignal instance ... + * @param conditions: X121SMCStrategyConditions instance ... + */ + void HandleOnSignalRecieved( + XSignal &signal, + X121SMCStrategyConditions &conditions // + ) + { + // + // Check Signal and Conditions Validations ... + + // + if (!signal.IsValid()) + { + return; + } + + // + if (!conditions.IsValid()) + { + return; + } + + // + bool isLong = IsLong(signal.type); + + // + // Notify When a Raw (Unfiltered) Signal Recieved + // if it's Provided ... + bool mAlertRawSignals = false; + if (mAlertRawSignals) + { + // + string msg = "Raw " + (IsLong(signal.type) ? "Long" : "Short") + " Signal Recieved: (" + signal.provider + "," + signal.symbol + "," + ToString(signal.period) + ") ..."; + Alert(msg); + } + + // + // Apply Volume On Signal ... + ApplyVolumeOnSignal(signal); + + // + // Chekc Signalling Enable or not ... + bool canContinue = + isLong + ? AllowLong() + : AllowShort(); + if (!canContinue) + { + return; + } + + // + // TODO: Apply Filtering On Signal ... + bool isFiltered = false; + if (isFiltered) + { + // + mTradeHandler.RemoveSignal(signal); + return; + } + + // + // Attach Conditions Summary to Signal ... + string conditionsSummary = conditions.GenerateSummary( + false, + false, + true, + false // + ); + + // + // TODO: Retrieve Conditions Summary based on Filter ... + signal.conditions = conditionsSummary; + + // + // TODO: Check Signal Delay ... + // datetime currentTime = TimeCurrent(); + // int delay = mDelayBarBetweenTwoSignal; + // bool isDelayPassed = delay <= 0 + // ? true + // : !IsValid(lastSignalOn) + // ? true + // : MathAbs(TimeToSeconds(currentTime)) - MathAbs(TimeToSeconds(lastSignalOn)) >= ulong(delay * PeriodSeconds(_Period)); + // if (!isDelayPassed) + // { + // return; + // } + // + // Execute Signal Using Trade Handler ... + ENUM_X_SIGNAL_EXECUTION_RESULT state; + bool isExecuted = mTradeHandler.ExecuteSignal( + signal, + state // + ); + + // + if (isExecuted) + { + // lastSignalOn = currentTime; + } + } + // // Override Actions ... @@ -62,7 +288,7 @@ class XC121SMCExpert : public XCBaseExpert // // Signalling ... Disabled(false); - AllowLomg(true); + AllowLong(true); AllowShort(true); // @@ -83,6 +309,13 @@ class XC121SMCExpert : public XCBaseExpert ReportNewWeeks(false); ReportNewHours(false); ReportNewMonths(false); + + // + // Volume Default Configurations ... + Volume(0.01); + UseDynamicVolume(false); + DynamicVolumeStep(0.01); + DynamicVolumeBalanceFactor(200); } /** @@ -111,12 +344,36 @@ class XC121SMCExpert : public XCBaseExpert errMessage += "common configurations error;" + "\n"; } + // + // Checking Volume ... + bool isVolumeValid = true; + if (UseDynamicVolume()) + { + // + isVolumeValid = + DynamicVolumeStep() > 0 && + DynamicVolumeBalanceFactor() > 0; + if (!isVolumeValid) + { + errMessage += "Dynamic Volume configurations error;" + "\n"; + } + } + isVolumeValid = isVolumeValid && + Volume() > 0; + if (!isVolumeValid) + { + errMessage += "Static Volume configurations error;" + "\n"; + } + // result = // // Common ... isCommonValid && // + // Volume Coniguration ... + isVolumeValid && + // // Otere ... true // @@ -187,6 +444,89 @@ class XC121SMCExpert : public XCBaseExpert Slippage(), MagicNumber() // ); + mTrader.AddOnStopLossEventHandler(OnPositionSLEventListener); + mTrader.AddOnTakeProfitEventHandler(OnPositionTPEventListener); + mTrader.AddOnModifyPositionEventHandler(OnPositionModifiedEventListener); + mTrader.AddOnForceClosePositionEventHandler(OnPositionForceCloseEventListener); + mTrader.AddOnPartialClosePositionEventHandler(OnPositionPartialCloseEventListener); + + // + // Initialize Trade Handler ... + mTradeHandler = new XC121SMCTradeHandler(mTrader); + mTradeHandler.SaveTrades(true); + mTradeHandler.SaveSignals(true); + mTradeHandler.SaveConditions(true); + + // + // Configure Alerts ... + mTradeHandler.SetAlertPrefix(GetTag()); + mTradeHandler.SetAlertEnableAlerts(GetAlertEnableAlerts()); + mTradeHandler.SetAlertLogAlerts(GetAlertLogAlerts()); + mTradeHandler.SetAlertMailAlerts(GetAlertMailAlerts()); + mTradeHandler.SetAlertPushAlerts(GetAlertPushAlerts()); + mTradeHandler.SetAlertTerminalAlerts(GetAlertTerminalAlerts()); + + // + // TODO: Make This Configurable Later ... + + // + // Configure Trade Management ... + mTradeHandler.AllowLong(AllowLong()); + mTradeHandler.AllowShort(AllowShort()); + mTradeHandler.MaxAllowedLongs(0); + mTradeHandler.MaxAllowedShorts(0); + mTradeHandler.UseMaxAllowedTradesPerSymbol(true); + mTradeHandler.MaxDrawdownPercentForOpenTrades(5); + + // + // Configure Position Protector ... + + // + mTradeHandler.UseForceMomentumsInProtection(true); + mTradeHandler.DelayBarBetweenTwoSignal(2); + + // + // Configure Hedging ... + mTradeHandler.AllowHedge(false); + mTradeHandler.HedgeMinVolumeStep(0.01); + mTradeHandler.MinimumOpenPositionsForHEHedge(2); + mTradeHandler.HedgeHEMinProfitPerVolumeStep(0.2); + mTradeHandler.HedgeBEMinProfitPerVolumeStep(0.06); + + // + // In Profit Position Protecting ... + + // + // Trail ... + mTradeHandler.AllowTrail(false); + mTradeHandler.TrailStep(15); + mTradeHandler.TrailStartDistance(30); + mTradeHandler.OnlyTrailUnprotected(true); + mTradeHandler.RemoveTPAfterTrailedLevel(4); + + // + // Partial Close (In Profit) ... + mTradeHandler.PartialCloseInProfitDistance(0); + mTradeHandler.PartialCloseInProfitVolume(0); + + // + // In Drawdown Positions Protecting ... + + // + // Protect ... + mTradeHandler.AllowProtect(false); + mTradeHandler.ProtectionDelay(20); + mTradeHandler.MaxAllowedProtection(0); + mTradeHandler.UseEntryAsProtectionSL(true); + mTradeHandler.ProtectOnConditions(true); + mTradeHandler.AllowDirectionProtct(false); + mTradeHandler.ProtectionStartDistance(50); + mTradeHandler.ProtectionVolumeMultiplier(2); + + // + // Partial Close (In Profit) ... + mTradeHandler.PartialCloseInDrawdownDistance(0); + mTradeHandler.PartialCloseInDrawdownVolume(0); // // Parsers ... @@ -202,13 +542,13 @@ class XC121SMCExpert : public XCBaseExpert // // Create Class Instance ... XCX121SMCBaseStrategy *iX121SMCStrategy; - iX121SMCStrategy = new XCX121SMCOBStrategy( + iX121SMCStrategy = new XCX121SMCTestStrategy( _Symbol, _Period // ); // - iX121SMCStrategy.AddXSignalEventHandler(OnSignalRecieved); + iX121SMCStrategy.AddXSignalEventHandler(OnSignalEventListener); // ConfigureStrategy(iX121SMCStrategy); @@ -223,7 +563,7 @@ class XC121SMCExpert : public XCBaseExpert string symbols[]; int symbolsCount = SplitContent( symbols, - eaSymbols // + Symbols() // ); result = IsValidSize(symbolsCount); if (!result) @@ -267,13 +607,13 @@ class XC121SMCExpert : public XCBaseExpert // // Create Class Instance ... XCX121SMCBaseStrategy *iX121SMCStrategy; - iX121SMCStrategy = new XCX121SMCOBStrategy( + iX121SMCStrategy = new XCX121SMCTestStrategy( iSymbol, _Period // ); // - iX121SMCStrategy.AddXSignalEventHandler(OnSignalRecieved); + iX121SMCStrategy.AddXSignalEventHandler(OnSignalEventListener); // // Register Strategy ... @@ -287,6 +627,99 @@ class XC121SMCExpert : public XCBaseExpert return result; } + /** + * Destroy all Initialized EA Requirements ... + */ + void DestroyEA() override + { + // + delete mCTHelper; + delete mCCHelper; + delete mTradeHandler; + + // + Clean(mStrategies); + } + + /** + * Call all Registered Strategies On Tick ... + */ + void HandleStrategiesOnTick() override + { + // + int count = ArraySize(mStrategies); + if (!IsValidSize(count)) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + // + // Call Tick Handler Function ... + mStrategies[i].HandleTick(); + } + } + + // + // Event Handlers ... + + /** + * Calls When a Position's SL Triggered ... + * + * @param deal: XDeal instance ... + */ + void HandleOnStopLossTriggered(const XDeal &deal) override + { + // + // Finish ... + mTradeHandler.Finish(deal); + + // + HandleReportBalance(); + } + + /** + * Calls When a Position's TP Triggered ... + * + * @param deal: XDeal instance ... + */ + void HandleOnTakeProfitTriggered(const XDeal &deal) override + { + // + // Finish ... + mTradeHandler.Finish(deal); + + // + HandleReportBalance(); + } + + /** + * Handle Force Close a Position ... + * + * @param ticket: Position Ticket ... + * @param position: XPosition ... + * @param comment: Closing Comment ... + */ + void HandleOnPositionForceClosed( + const ulong ticket, + const XPosition &position, + const string comment // + ) override + { + // + // Finish ... + mTradeHandler.Finish( + ticket, + position, + comment // + ); + + // + HandleReportBalance(); + } + // /** @@ -324,11 +757,54 @@ class XC121SMCExpert : public XCBaseExpert // // Props ... + + // + XC121SMCTradeHandler *mTradeHandler; // Trade Handler ... XCX121SMCBaseStrategy *mStrategies[]; // Registere Strategies in EA ... // // Actions ... + /** + * Apply Volume Based On Configurations on Signal ... + * + * @param signal: XSignal instance + */ + void ApplyVolumeOnSignal(XSignal &signal) + { + // + // Prepare Signal Dynamic Volume + // if Provided ... + if (UseDynamicVolume() && + DynamicVolumeStep() > 0 && + DynamicVolumeBalanceFactor() > 0) + { + // + double dVolume = mTrader.GetDynamicVolume( + signal.symbol, + DynamicVolumeBalanceFactor(), + DynamicVolumeStep() // + ); + + // + if (dVolume > 0) + { + signal.volume = dVolume; + } + else + { + signal.volume = Volume(); + } + } + else + { + signal.volume = Volume(); + } + + // + mTradeHandler.UpdateSignal(signal); + } + /** * Configure Startegy ... */ @@ -353,7 +829,7 @@ class XC121SMCExpert : public XCBaseExpert // Confiugre Signalling ... // - strategy.AllowLong(AllowLomg()); + strategy.AllowLong(AllowLong()); strategy.AllowShort(AllowShort()); // @@ -398,7 +874,7 @@ class XC121SMCExpert : public XCBaseExpert void ReConfigureAllStrategies() { // - int count = ArraySize(eaStrategies); + int count = ArraySize(mStrategies); if (!IsValidSize(count)) { return; @@ -408,7 +884,7 @@ class XC121SMCExpert : public XCBaseExpert for (int i = 0; i < count; i++) { // - ConfigureStrategy(eaStrategies[i]); + ConfigureStrategy(mStrategies[i]); } } @@ -416,6 +892,16 @@ class XC121SMCExpert : public XCBaseExpert private: // + // + // Props ... + + // + // Volume Management ... + bool mUseDynamicVolume; // Use Dynamic Volume ... + double mDynamicVolumeStep; // Increase Volume Step ... + double mDynamicVolumeBalanceFactor; // Balance Factor for Generate Dynamic Volume ... + double mVolume; // Static Volume ... + // }; diff --git a/X121SMCEA/Strategy/x-121.smc.test.strategy.class.mq5 b/X121SMCEA/Strategy/x-121.smc.test.strategy.class.mq5 new file mode 100644 index 00000000..b1494a5f Binary files /dev/null and b/X121SMCEA/Strategy/x-121.smc.test.strategy.class.mq5 differ