770 lines
No EOL
30 KiB
MQL5
770 lines
No EOL
30 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| ERMT_6.9-DualDash.mq5|
|
|
//| Enterprise Risk Management Terminal v6.9 |
|
|
//| Complete Working Implementation |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2024, Institutional Trading Systems"
|
|
#property link "https://example.com"
|
|
#property version "6.903"
|
|
#property description "Enterprise Risk Management Terminal - Dual Dashboard Edition"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Include Files |
|
|
//+------------------------------------------------------------------+
|
|
//--- Include base MQL5 classes
|
|
#include <Object.mqh>
|
|
#include <Trade/Trade.mqh>
|
|
|
|
//--- Include system modules
|
|
#include "Modules/DataTypes.mqh"
|
|
#include "Modules/Utilities.mqh"
|
|
#include "Modules/RiskManager.mqh"
|
|
#include "Modules/TradeManager.mqh"
|
|
#include "Modules/TechnicalAnalysis.mqh"
|
|
#include "Modules/EntrySystem.mqh"
|
|
#include "Modules/Dashboard.mqh" // Original Dashboard
|
|
#include "Modules/Dashboard_SelfContained.mqh" // New Self-Contained Dashboard
|
|
#include "Modules/ExternalTradeManager.mqh"
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Input Parameters - Complete Set |
|
|
//+------------------------------------------------------------------+
|
|
// Risk Management Parameters
|
|
input double InpMaxRiskPerTrade = 1.0; // Maximum Risk Per Trade (%)
|
|
input double InpMaxDailyLoss = 5.0; // Maximum Daily Loss (%)
|
|
input double InpMaxDrawdown = 20.0; // Maximum Drawdown (%)
|
|
input double InpMaxLeverage = 10.0; // Maximum Leverage
|
|
input int InpMaxPositions = 5; // Maximum Concurrent Positions
|
|
input double InpMaxCorrelation = 0.7; // Maximum Correlation Threshold
|
|
|
|
// Trade Management Parameters
|
|
input ENUM_TRADE_MANAGEMENT_MODE InpManagementMode = MANAGEMENT_BASIC; // Management Mode
|
|
input ENUM_SL_MODE InpSLMode = SL_ATR; // Stop Loss Mode
|
|
input ENUM_TP_MODE InpTPMode = TP_RISK_REWARD; // Take Profit Mode
|
|
input double InpRiskRewardRatio = 2.0; // Risk/Reward Ratio
|
|
input bool InpUseTrailingStop = true; // Use Trailing Stop
|
|
input double InpTrailingDistance = 50; // Trailing Distance (points)
|
|
input bool InpMoveToBreakeven = true; // Move to Breakeven
|
|
input double InpBreakevenTrigger = 30; // Breakeven Trigger (points)
|
|
|
|
// Position Sizing Parameters
|
|
input ENUM_POSITION_SIZING InpSizingMethod = POSITION_SIZE_RISK; // Position Sizing Method
|
|
input double InpFixedLotSize = 0.01; // Fixed Lot Size
|
|
input double InpRiskPercent = 1.0; // Risk Percentage for Sizing
|
|
|
|
// Entry System Parameters
|
|
input ENUM_ENTRY_MODE InpEntryMode = ENTRY_MANUAL; // Entry Mode
|
|
input ENUM_ENTRY_STRATEGY InpEntryStrategy = ENTRY_MA_CROSS; // Entry Strategy
|
|
input int InpMAPeriodFast = 10; // Fast MA Period
|
|
input int InpMAPeriodSlow = 20; // Slow MA Period
|
|
input int InpMomentumPeriod = 14; // Momentum Period
|
|
input double InpBreakoutThreshold = 1.5; // Breakout Threshold (ATR)
|
|
input int InpMinSignalStrength = 5; // Minimum Signal Strength (1-10)
|
|
|
|
// Dashboard Parameters
|
|
input int InpOriginalDashboardX = 10; // Original Dashboard X Position
|
|
input int InpOriginalDashboardY = 30; // Original Dashboard Y Position
|
|
input int InpDualDashboardX = 350; // Dual Dashboard X Position
|
|
input int InpDualDashboardY = 30; // Dual Dashboard Y Position
|
|
input int InpDashboardWidth = 320; // Dashboard Width
|
|
input int InpDashboardHeight = 200; // Dashboard Height
|
|
input bool InpShowOriginalDashboard = true; // Show Original Dashboard
|
|
input bool InpShowDualDashboard = true; // Show Dual Dashboard
|
|
input ENUM_DASH_THEME InpDashboardTheme = DASH_THEME_DARK; // Dashboard Theme
|
|
|
|
// System Parameters
|
|
input int InpMagicNumber = 123456; // Magic Number
|
|
input string InpCommentPrefix = "ERMT_"; // Trade Comment Prefix
|
|
input int InpUpdateFrequency = 1; // Update Frequency (seconds)
|
|
input bool InpEnableAlerts = true; // Enable Alerts
|
|
input bool InpEnableNotifications = false; // Enable Push Notifications
|
|
input bool InpEnableEmails = false; // Enable Email Alerts
|
|
input ENUM_LOG_LEVEL InpLogLevel = LOG_INFO; // Logging Level
|
|
|
|
// Time Filter Parameters
|
|
input bool InpUseTimeFilter = false; // Use Time Filter
|
|
input int InpStartHour = 8; // Trading Start Hour
|
|
input int InpEndHour = 20; // Trading End Hour
|
|
input bool InpTradeOnMonday = true; // Trade on Monday
|
|
input bool InpTradeOnFriday = true; // Trade on Friday
|
|
input bool InpCloseOnFriday = false; // Close All on Friday
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Global Variables and Objects |
|
|
//+------------------------------------------------------------------+
|
|
// Core System Objects
|
|
CRiskManager g_RiskManager; // Risk manager instance
|
|
CDashboardSelfContained g_Dashboard; // Original dashboard
|
|
CDashboardSelfContained g_DualDashboard; // Dual dashboard
|
|
CUtilities* g_pUtils; // Utilities pointer
|
|
|
|
// Data Storage
|
|
ManagedTrade g_ManagedTrades[]; // Array of managed trades
|
|
RiskMetrics g_RiskMetrics; // Current risk metrics
|
|
PerformanceMetrics g_Performance; // Performance metrics
|
|
MarketConditions g_MarketConditions; // Current market conditions
|
|
|
|
// System State Variables
|
|
bool g_IsInitialized = false; // System initialized flag
|
|
bool g_IsTrading = false; // Trading enabled flag
|
|
bool g_InPosition = false; // Currently in position flag
|
|
datetime g_LastUpdateTime = 0; // Last update timestamp
|
|
datetime g_LastTradeTime = 0; // Last trade timestamp
|
|
int g_ConsecutiveLosses = 0; // Consecutive losses counter
|
|
double g_DailyProfit = 0; // Today's profit/loss
|
|
double g_SessionHigh = 0; // Session high balance
|
|
double g_SessionLow = 0; // Session low balance
|
|
|
|
// Performance Tracking
|
|
double g_StartingBalance = 0; // Starting balance
|
|
double g_PeakBalance = 0; // Peak balance for DD calc
|
|
int g_TotalTrades = 0; // Total trades counter
|
|
int g_WinningTrades = 0; // Winning trades counter
|
|
int g_LosingTrades = 0; // Losing trades counter
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// Initialize logging
|
|
CLogger::Init(InpLogLevel, true, true);
|
|
CLogger::Log(LOG_INFO, "===========================================");
|
|
CLogger::Log(LOG_INFO, "ERMT 6.9 Dual Dashboard - Initializing");
|
|
CLogger::Log(LOG_INFO, "===========================================");
|
|
|
|
// Create utilities instance
|
|
g_pUtils = new CUtilities();
|
|
if(g_pUtils == NULL)
|
|
{
|
|
CLogger::Log(LOG_ERROR, "Failed to create Utilities instance");
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
// Initialize Risk Parameters
|
|
RiskParameters riskParams;
|
|
riskParams.maxRiskPerTrade = InpMaxRiskPerTrade;
|
|
riskParams.maxDailyRisk = InpMaxDailyLoss;
|
|
riskParams.maxDrawdown = InpMaxDrawdown;
|
|
riskParams.maxLeverage = InpMaxLeverage;
|
|
riskParams.maxPositions = InpMaxPositions;
|
|
riskParams.maxCorrelation = InpMaxCorrelation;
|
|
riskParams.marginCallLevel = 50.0;
|
|
riskParams.stopOutLevel = 30.0;
|
|
riskParams.useVolatilityAdjustment = true;
|
|
riskParams.volatilityMultiplier = 1.5;
|
|
riskParams.useTimeFilter = InpUseTimeFilter;
|
|
riskParams.startHour = InpStartHour;
|
|
riskParams.endHour = InpEndHour;
|
|
riskParams.closeOnFriday = InpCloseOnFriday;
|
|
riskParams.tradeOnMonday = InpTradeOnMonday;
|
|
|
|
// Initialize Risk Manager
|
|
if(!g_RiskManager.Initialize(g_pUtils, riskParams))
|
|
{
|
|
CLogger::Log(LOG_ERROR, "Failed to initialize Risk Manager");
|
|
delete g_pUtils;
|
|
return INIT_FAILED;
|
|
}
|
|
|
|
// Initialize Dashboards
|
|
if(InpShowOriginalDashboard)
|
|
{
|
|
if(!g_Dashboard.Create("Original", InpOriginalDashboardX, InpOriginalDashboardY,
|
|
InpDashboardWidth, InpDashboardHeight))
|
|
{
|
|
CLogger::Log(LOG_WARNING, "Failed to create original dashboard");
|
|
}
|
|
else
|
|
{
|
|
g_Dashboard.SetTheme(InpDashboardTheme);
|
|
CLogger::Log(LOG_INFO, "Original dashboard created successfully");
|
|
}
|
|
}
|
|
|
|
if(InpShowDualDashboard)
|
|
{
|
|
if(!g_DualDashboard.Create("Dual", InpDualDashboardX, InpDualDashboardY,
|
|
InpDashboardWidth, InpDashboardHeight))
|
|
{
|
|
CLogger::Log(LOG_WARNING, "Failed to create dual dashboard");
|
|
}
|
|
else
|
|
{
|
|
g_DualDashboard.SetTheme(InpDashboardTheme);
|
|
g_DualDashboard.SetCompact(true); // Make dual dashboard compact
|
|
CLogger::Log(LOG_INFO, "Dual dashboard created successfully");
|
|
}
|
|
}
|
|
|
|
// Initialize tracking variables
|
|
g_StartingBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
g_PeakBalance = g_StartingBalance;
|
|
g_SessionHigh = g_StartingBalance;
|
|
g_SessionLow = g_StartingBalance;
|
|
g_LastUpdateTime = TimeCurrent();
|
|
|
|
// Initialize arrays
|
|
ArrayResize(g_ManagedTrades, 0);
|
|
|
|
// Set timer for updates
|
|
EventSetTimer(InpUpdateFrequency);
|
|
|
|
// Set initialization flag
|
|
g_IsInitialized = true;
|
|
g_IsTrading = true;
|
|
|
|
CLogger::Log(LOG_INFO, StringFormat("Starting Balance: %.2f", g_StartingBalance));
|
|
CLogger::Log(LOG_INFO, "ERMT 6.9 Initialized Successfully");
|
|
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
// Kill timer
|
|
EventKillTimer();
|
|
|
|
// Log final statistics
|
|
LogFinalStatistics();
|
|
|
|
// Destroy dashboards
|
|
if(InpShowOriginalDashboard)
|
|
{
|
|
g_Dashboard.Destroy();
|
|
}
|
|
|
|
if(InpShowDualDashboard)
|
|
{
|
|
g_DualDashboard.Destroy();
|
|
}
|
|
|
|
// Clean up Risk Manager
|
|
g_RiskManager.Deinitialize();
|
|
|
|
// Delete utilities
|
|
if(g_pUtils != NULL)
|
|
{
|
|
delete g_pUtils;
|
|
g_pUtils = NULL;
|
|
}
|
|
|
|
// Log shutdown reason
|
|
string reasonStr = GetDeInitReasonText(reason);
|
|
CLogger::Log(LOG_INFO, "ERMT 6.9 Shutdown: " + reasonStr);
|
|
|
|
// Deinitialize logger
|
|
CLogger::Deinit();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
if(!g_IsInitialized || !g_IsTrading) return;
|
|
|
|
// Check if market is open
|
|
if(!IsMarketOpen()) return;
|
|
|
|
// Update market conditions
|
|
UpdateMarketConditions();
|
|
|
|
// Update managed trades
|
|
UpdateManagedTrades();
|
|
|
|
// Check for risk events
|
|
CheckRiskEvents();
|
|
|
|
// Process existing positions
|
|
ProcessExistingPositions();
|
|
|
|
// Check for new entry signals (if enabled)
|
|
if(InpEntryMode != ENTRY_MANUAL)
|
|
{
|
|
CheckEntrySignals();
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Timer function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTimer()
|
|
{
|
|
if(!g_IsInitialized) return;
|
|
|
|
// Update performance metrics
|
|
UpdatePerformanceMetrics();
|
|
|
|
// Update risk metrics
|
|
g_RiskMetrics = g_RiskManager.GetCurrentRiskMetrics();
|
|
|
|
// Update dashboards
|
|
UpdateDashboards();
|
|
|
|
// Check for daily reset
|
|
CheckDailyReset();
|
|
|
|
g_LastUpdateTime = TimeCurrent();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Market Conditions |
|
|
//+------------------------------------------------------------------+
|
|
void UpdateMarketConditions()
|
|
{
|
|
g_MarketConditions.volatility = g_RiskManager.GetVolatility(_Symbol);
|
|
g_MarketConditions.atr = g_RiskManager.GetATR(_Symbol, PERIOD_CURRENT, 14);
|
|
g_MarketConditions.spread = (SymbolInfoDouble(_Symbol, SYMBOL_ASK) -
|
|
SymbolInfoDouble(_Symbol, SYMBOL_BID)) /
|
|
SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
|
|
// Determine market condition
|
|
if(g_MarketConditions.volatility > g_MarketConditions.atr * 1.5)
|
|
{
|
|
g_MarketConditions.condition = MARKET_VOLATILE;
|
|
}
|
|
else if(g_MarketConditions.volatility < g_MarketConditions.atr * 0.5)
|
|
{
|
|
g_MarketConditions.condition = MARKET_QUIET;
|
|
}
|
|
else
|
|
{
|
|
g_MarketConditions.condition = MARKET_RANGING;
|
|
}
|
|
|
|
g_MarketConditions.lastUpdate = TimeCurrent();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Managed Trades |
|
|
//+------------------------------------------------------------------+
|
|
void UpdateManagedTrades()
|
|
{
|
|
ArrayResize(g_ManagedTrades, 0);
|
|
int count = 0;
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(PositionSelectByTicket(PositionGetTicket(i)))
|
|
{
|
|
if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
|
|
{
|
|
ManagedTrade trade;
|
|
trade.ticket = PositionGetInteger(POSITION_TICKET);
|
|
trade.symbol = PositionGetString(POSITION_SYMBOL);
|
|
trade.type = (int)PositionGetInteger(POSITION_TYPE);
|
|
trade.volume = PositionGetDouble(POSITION_VOLUME);
|
|
trade.openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
trade.stopLoss = PositionGetDouble(POSITION_SL);
|
|
trade.takeProfit = PositionGetDouble(POSITION_TP);
|
|
trade.profit = PositionGetDouble(POSITION_PROFIT);
|
|
trade.commission = PositionGetDouble(POSITION_COMMISSION);
|
|
trade.swap = PositionGetDouble(POSITION_SWAP);
|
|
trade.openTime = (datetime)PositionGetInteger(POSITION_TIME);
|
|
trade.magic = (int)PositionGetInteger(POSITION_MAGIC);
|
|
trade.comment = PositionGetString(POSITION_COMMENT);
|
|
trade.isManaged = true;
|
|
trade.slMode = InpSLMode;
|
|
trade.tpMode = InpTPMode;
|
|
|
|
ArrayResize(g_ManagedTrades, count + 1);
|
|
g_ManagedTrades[count] = trade;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_InPosition = (count > 0);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check Risk Events |
|
|
//+------------------------------------------------------------------+
|
|
void CheckRiskEvents()
|
|
{
|
|
if(g_RiskManager.IsRiskLimitExceeded())
|
|
{
|
|
ENUM_RISK_ACTION action = g_RiskManager.EvaluateRiskAction();
|
|
|
|
if(action != ACTION_NONE)
|
|
{
|
|
g_RiskManager.ExecuteRiskAction(action);
|
|
|
|
// Stop trading if critical
|
|
if(action == ACTION_CLOSE_ALL || action == ACTION_STOP_TRADING)
|
|
{
|
|
g_IsTrading = false;
|
|
CloseAllPositions();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Process Existing Positions |
|
|
//+------------------------------------------------------------------+
|
|
void ProcessExistingPositions()
|
|
{
|
|
for(int i = 0; i < ArraySize(g_ManagedTrades); i++)
|
|
{
|
|
// Process trailing stop
|
|
if(InpUseTrailingStop)
|
|
{
|
|
ProcessTrailingStop(g_ManagedTrades[i]);
|
|
}
|
|
|
|
// Process breakeven
|
|
if(InpMoveToBreakeven && !g_ManagedTrades[i].isBreakeven)
|
|
{
|
|
ProcessBreakeven(g_ManagedTrades[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Process Trailing Stop |
|
|
//+------------------------------------------------------------------+
|
|
void ProcessTrailingStop(ManagedTrade &trade)
|
|
{
|
|
if(!PositionSelectByTicket(trade.ticket)) return;
|
|
|
|
double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
|
|
double currentSL = PositionGetDouble(POSITION_SL);
|
|
double point = SymbolInfoDouble(trade.symbol, SYMBOL_POINT);
|
|
double trailDistance = InpTrailingDistance * point;
|
|
|
|
bool isBuy = (trade.type == POSITION_TYPE_BUY);
|
|
double newSL = 0;
|
|
|
|
if(isBuy)
|
|
{
|
|
newSL = currentPrice - trailDistance;
|
|
if(newSL > currentSL && newSL < currentPrice)
|
|
{
|
|
ModifyPosition(trade.ticket, newSL, trade.takeProfit);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newSL = currentPrice + trailDistance;
|
|
if((currentSL == 0 || newSL < currentSL) && newSL > currentPrice)
|
|
{
|
|
ModifyPosition(trade.ticket, newSL, trade.takeProfit);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Process Breakeven |
|
|
//+------------------------------------------------------------------+
|
|
void ProcessBreakeven(ManagedTrade &trade)
|
|
{
|
|
if(!PositionSelectByTicket(trade.ticket)) return;
|
|
|
|
double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
|
|
double openPrice = trade.openPrice;
|
|
double point = SymbolInfoDouble(trade.symbol, SYMBOL_POINT);
|
|
double triggerDistance = InpBreakevenTrigger * point;
|
|
|
|
bool isBuy = (trade.type == POSITION_TYPE_BUY);
|
|
|
|
if(isBuy)
|
|
{
|
|
if(currentPrice >= openPrice + triggerDistance)
|
|
{
|
|
double newSL = openPrice + point; // Small profit to cover costs
|
|
if(trade.stopLoss < newSL)
|
|
{
|
|
if(ModifyPosition(trade.ticket, newSL, trade.takeProfit))
|
|
{
|
|
trade.isBreakeven = true;
|
|
CLogger::Log(LOG_INFO, StringFormat("Position %d moved to breakeven", trade.ticket));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(currentPrice <= openPrice - triggerDistance)
|
|
{
|
|
double newSL = openPrice - point;
|
|
if(trade.stopLoss == 0 || trade.stopLoss > newSL)
|
|
{
|
|
if(ModifyPosition(trade.ticket, newSL, trade.takeProfit))
|
|
{
|
|
trade.isBreakeven = true;
|
|
CLogger::Log(LOG_INFO, StringFormat("Position %d moved to breakeven", trade.ticket));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Modify Position |
|
|
//+------------------------------------------------------------------+
|
|
bool ModifyPosition(ulong ticket, double sl, double tp)
|
|
{
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
|
|
ZeroMemory(request);
|
|
ZeroMemory(result);
|
|
|
|
request.action = TRADE_ACTION_SLTP;
|
|
request.position = ticket;
|
|
request.sl = NormalizeDouble(sl, _Digits);
|
|
request.tp = NormalizeDouble(tp, _Digits);
|
|
|
|
if(!OrderSend(request, result))
|
|
{
|
|
CLogger::Log(LOG_ERROR, StringFormat("Failed to modify position %d: %s",
|
|
ticket, GetLastErrorString()));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check Entry Signals |
|
|
//+------------------------------------------------------------------+
|
|
void CheckEntrySignals()
|
|
{
|
|
// This is a placeholder for entry signal logic
|
|
// Implement based on selected strategy
|
|
if(!g_RiskManager.CanOpenNewPosition(_Symbol, 0.01)) return;
|
|
|
|
// Entry logic would go here based on InpEntryStrategy
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Performance Metrics |
|
|
//+------------------------------------------------------------------+
|
|
void UpdatePerformanceMetrics()
|
|
{
|
|
double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
|
|
// Update account values
|
|
g_Performance.currentBalance = currentBalance;
|
|
g_Performance.currentEquity = currentEquity;
|
|
g_Performance.startingBalance = g_StartingBalance;
|
|
g_Performance.netProfit = currentBalance - g_StartingBalance;
|
|
|
|
// Update peak balance
|
|
if(currentBalance > g_PeakBalance)
|
|
{
|
|
g_PeakBalance = currentBalance;
|
|
}
|
|
g_Performance.peakBalance = g_PeakBalance;
|
|
|
|
// Calculate drawdown
|
|
if(g_PeakBalance > 0)
|
|
{
|
|
g_Performance.currentDrawdown = g_PeakBalance - currentBalance;
|
|
g_Performance.currentDrawdownPercent = (g_Performance.currentDrawdown / g_PeakBalance) * 100;
|
|
}
|
|
|
|
// Update trade statistics
|
|
g_Performance.totalTrades = g_TotalTrades;
|
|
g_Performance.winningTrades = g_WinningTrades;
|
|
g_Performance.losingTrades = g_LosingTrades;
|
|
|
|
// Calculate win rate
|
|
if(g_TotalTrades > 0)
|
|
{
|
|
g_Performance.winRate = ((double)g_WinningTrades / g_TotalTrades) * 100;
|
|
}
|
|
|
|
g_Performance.lastUpdateTime = TimeCurrent();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Dashboards |
|
|
//+------------------------------------------------------------------+
|
|
void UpdateDashboards()
|
|
{
|
|
if(InpShowOriginalDashboard)
|
|
{
|
|
g_Dashboard.Update(g_RiskMetrics, g_Performance);
|
|
}
|
|
|
|
if(InpShowDualDashboard)
|
|
{
|
|
g_DualDashboard.Update(g_RiskMetrics, g_Performance);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check Daily Reset |
|
|
//+------------------------------------------------------------------+
|
|
void CheckDailyReset()
|
|
{
|
|
static datetime lastResetDate = 0;
|
|
|
|
MqlDateTime current;
|
|
TimeToStruct(TimeCurrent(), current);
|
|
|
|
datetime currentDate = StringToTime(StringFormat("%04d.%02d.%02d",
|
|
current.year, current.mon, current.day));
|
|
|
|
if(currentDate != lastResetDate)
|
|
{
|
|
g_DailyProfit = 0;
|
|
lastResetDate = currentDate;
|
|
CLogger::Log(LOG_INFO, "Daily metrics reset");
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if Market is Open |
|
|
//+------------------------------------------------------------------+
|
|
bool IsMarketOpen()
|
|
{
|
|
if(!InpUseTimeFilter) return true;
|
|
|
|
MqlDateTime current;
|
|
TimeToStruct(TimeCurrent(), current);
|
|
|
|
// Check day of week
|
|
if(current.day_of_week == 0 || current.day_of_week == 6) return false;
|
|
if(current.day_of_week == 1 && !InpTradeOnMonday) return false;
|
|
if(current.day_of_week == 5 && !InpTradeOnFriday) return false;
|
|
|
|
// Check time
|
|
if(current.hour < InpStartHour || current.hour >= InpEndHour) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close All Positions |
|
|
//+------------------------------------------------------------------+
|
|
void CloseAllPositions()
|
|
{
|
|
CLogger::Log(LOG_WARNING, "Closing all positions");
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(PositionSelectByTicket(PositionGetTicket(i)))
|
|
{
|
|
if(PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
|
|
{
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
|
|
ZeroMemory(request);
|
|
ZeroMemory(result);
|
|
|
|
request.action = TRADE_ACTION_DEAL;
|
|
request.position = PositionGetInteger(POSITION_TICKET);
|
|
request.symbol = PositionGetString(POSITION_SYMBOL);
|
|
request.volume = PositionGetDouble(POSITION_VOLUME);
|
|
request.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ?
|
|
ORDER_TYPE_SELL : ORDER_TYPE_BUY;
|
|
request.price = (request.type == ORDER_TYPE_SELL) ?
|
|
SymbolInfoDouble(request.symbol, SYMBOL_BID) :
|
|
SymbolInfoDouble(request.symbol, SYMBOL_ASK);
|
|
request.deviation = 10;
|
|
request.magic = InpMagicNumber;
|
|
|
|
OrderSend(request, result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get Deinit Reason Text |
|
|
//+------------------------------------------------------------------+
|
|
string GetDeInitReasonText(int reason)
|
|
{
|
|
switch(reason)
|
|
{
|
|
case REASON_PROGRAM: return "Program terminated";
|
|
case REASON_REMOVE: return "Program removed from chart";
|
|
case REASON_RECOMPILE: return "Program recompiled";
|
|
case REASON_CHARTCHANGE: return "Chart symbol or period changed";
|
|
case REASON_CHARTCLOSE: return "Chart closed";
|
|
case REASON_PARAMETERS: return "Parameters changed";
|
|
case REASON_ACCOUNT: return "Account changed";
|
|
default: return "Unknown reason";
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get Last Error String |
|
|
//+------------------------------------------------------------------+
|
|
string GetLastErrorString()
|
|
{
|
|
int error = GetLastError();
|
|
return StringFormat("Error %d: %s", error, ErrorDescription(error));
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Error Description |
|
|
//+------------------------------------------------------------------+
|
|
string ErrorDescription(int error_code)
|
|
{
|
|
// Add common error descriptions
|
|
switch(error_code)
|
|
{
|
|
case 0: return "No error";
|
|
case 4756: return "Trade request sending failed";
|
|
case 4757: return "Trade request canceled by timeout";
|
|
case 10004: return "Requote";
|
|
case 10006: return "Request rejected";
|
|
case 10007: return "Request canceled by trader";
|
|
case 10010: return "Only part of the request was completed";
|
|
case 10011: return "Request processing error";
|
|
case 10012: return "Request canceled by timeout";
|
|
case 10013: return "Invalid request";
|
|
case 10014: return "Invalid volume";
|
|
case 10015: return "Invalid price";
|
|
case 10016: return "Invalid stops";
|
|
case 10017: return "Trade disabled";
|
|
case 10018: return "Market closed";
|
|
case 10019: return "Not enough money";
|
|
case 10020: return "Prices changed";
|
|
case 10021: return "No quotes to process request";
|
|
case 10022: return "Invalid expiration date";
|
|
case 10023: return "Order state changed";
|
|
case 10024: return "Too frequent requests";
|
|
case 10025: return "No changes in request";
|
|
case 10026: return "Autotrading disabled by server";
|
|
case 10027: return "Autotrading disabled by client terminal";
|
|
default: return "Unknown error";
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Log Final Statistics |
|
|
//+------------------------------------------------------------------+
|
|
void LogFinalStatistics()
|
|
{
|
|
double finalBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
double totalProfit = finalBalance - g_StartingBalance;
|
|
double profitPercent = (g_StartingBalance > 0) ?
|
|
(totalProfit / g_StartingBalance) * 100 : 0;
|
|
|
|
CLogger::Log(LOG_INFO, "===========================================");
|
|
CLogger::Log(LOG_INFO, "FINAL STATISTICS");
|
|
CLogger::Log(LOG_INFO, "===========================================");
|
|
CLogger::Log(LOG_INFO, StringFormat("Starting Balance: %.2f", g_StartingBalance));
|
|
CLogger::Log(LOG_INFO, StringFormat("Final Balance: %.2f", finalBalance));
|
|
CLogger::Log(LOG_INFO, StringFormat("Total Profit/Loss: %.2f (%.2f%%)",
|
|
totalProfit, profitPercent));
|
|
CLogger::Log(LOG_INFO, StringFormat("Total Trades: %d", g_TotalTrades));
|
|
CLogger::Log(LOG_INFO, StringFormat("Winning Trades: %d", g_WinningTrades));
|
|
CLogger::Log(LOG_INFO, StringFormat("Losing Trades: %d", g_LosingTrades));
|
|
CLogger::Log(LOG_INFO, StringFormat("Win Rate: %.2f%%", g_Performance.winRate));
|
|
CLogger::Log(LOG_INFO, StringFormat("Max Drawdown: %.2f%%",
|
|
g_Performance.maxDrawdownPercent));
|
|
CLogger::Log(LOG_INFO, "===========================================");
|
|
} |