608 lines
24 KiB
Plaintext
608 lines
24 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| PerformanceEvaluator.mqh |
|
|
//| Copyright 2025, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Performance Metrics Structure |
|
|
//+------------------------------------------------------------------+
|
|
struct StrategyPerformance {
|
|
string strategyName;
|
|
string symbol; // Store symbol to determine if it's a stock
|
|
int magicNumber;
|
|
double initialLotSize;
|
|
double currentLotSize;
|
|
double quarterProfit;
|
|
double quarterTrades;
|
|
double quarterWins;
|
|
double quarterLosses;
|
|
double maxDrawdown;
|
|
double winRate;
|
|
datetime quarterStart;
|
|
datetime quarterEnd;
|
|
bool isActive;
|
|
bool inPenaltyMode; // True if strategy is in penalty (worst performer)
|
|
double lotSizeBeforePenalty; // Store lot size before penalty
|
|
datetime penaltyStartTime; // When penalty started
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Global Performance Tracking |
|
|
//+------------------------------------------------------------------+
|
|
StrategyPerformance strategyPerformances[];
|
|
int totalStrategies = 0;
|
|
datetime lastMonthCheck = 0;
|
|
datetime currentMonthStart = 0;
|
|
datetime currentMonthEnd = 0;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Performance Adjustment Parameters |
|
|
//+------------------------------------------------------------------+
|
|
input group "=== Performance Evaluation Settings ==="
|
|
input bool PE_EnableAutoAdjustment = true; // Enable automatic lot size adjustment
|
|
input double PE_LotSizeIncreasePercent = 10.0; // % increase for top-ranked strategies
|
|
input double PE_LotSizeDecreasePercent = 10.0; // % decrease for bottom-ranked strategies
|
|
input double PE_MinLotSize = 0.01; // Minimum lot size for forex/crypto
|
|
input double PE_MinLotSizeStocks = 5.0; // Minimum lot size for stocks (5-10 range)
|
|
input double PE_MaxLotSize = 100.0; // Maximum lot size after adjustment
|
|
input int PE_TopPerformersCount = 3; // Number of top strategies to increase lot size
|
|
input int PE_BottomPerformersCount = 3; // Number of bottom strategies to decrease lot size
|
|
input bool PE_UseWinRateWeight = true; // Consider win rate in ranking (50% profit, 50% win rate)
|
|
input bool PE_EnableBlitzPlay = true; // Enable blitz play: worst performer gets minimum lot size penalty
|
|
input bool PE_EnableLogging = true; // Enable performance logging
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize Performance Tracking |
|
|
//+------------------------------------------------------------------+
|
|
void InitPerformanceTracking()
|
|
{
|
|
// Calculate current month dates
|
|
MqlDateTime dt;
|
|
TimeToStruct(TimeCurrent(), dt);
|
|
|
|
// Determine month start (first day of current month)
|
|
dt.day = 1;
|
|
dt.hour = 0;
|
|
dt.min = 0;
|
|
dt.sec = 0;
|
|
currentMonthStart = StructToTime(dt);
|
|
|
|
// Calculate month end (first day of next month - 1 second)
|
|
dt.mon += 1;
|
|
if(dt.mon > 12)
|
|
{
|
|
dt.mon = 1;
|
|
dt.year++;
|
|
}
|
|
currentMonthEnd = StructToTime(dt) - 1; // End of last day of month
|
|
|
|
lastMonthCheck = TimeCurrent();
|
|
|
|
if(PE_EnableLogging)
|
|
{
|
|
Print("Performance Evaluator: Initialized");
|
|
Print("Current Month Start: ", TimeToString(currentMonthStart));
|
|
Print("Current Month End: ", TimeToString(currentMonthEnd));
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if Symbol is a Stock |
|
|
//+------------------------------------------------------------------+
|
|
bool IsStockSymbol(string symbol)
|
|
{
|
|
// Check if symbol contains common stock indicators
|
|
if(StringFind(symbol, ".US") >= 0) return true;
|
|
if(StringFind(symbol, "NASDAQ:") >= 0) return true;
|
|
if(StringFind(symbol, "NYSE:") >= 0) return true;
|
|
|
|
// Note: Symbol category check removed to avoid enum conversion issues
|
|
// String-based checks (.US, NASDAQ:, NYSE:, common tickers) are sufficient
|
|
|
|
// Common stock tickers (without .US suffix)
|
|
string commonStocks[] = {"AAPL", "NVDA", "TSLA", "GOOGL", "AMZN", "META", "AMD", "NFLX"};
|
|
for(int i = 0; i < ArraySize(commonStocks); i++)
|
|
{
|
|
if(StringFind(symbol, commonStocks[i]) == 0) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get Minimum Lot Size for Symbol |
|
|
//+------------------------------------------------------------------+
|
|
double GetMinLotSizeForSymbol(string symbol)
|
|
{
|
|
if(IsStockSymbol(symbol))
|
|
return PE_MinLotSizeStocks;
|
|
else
|
|
return PE_MinLotSize;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Register Strategy for Performance Tracking |
|
|
//+------------------------------------------------------------------+
|
|
void RegisterStrategy(string strategyName, int magicNumber, double initialLotSize, string symbol = "")
|
|
{
|
|
// Check if strategy already registered
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].strategyName == strategyName &&
|
|
strategyPerformances[i].magicNumber == magicNumber)
|
|
{
|
|
if(PE_EnableLogging)
|
|
Print("Performance Evaluator: Strategy '", strategyName, "' already registered");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Add new strategy
|
|
int newSize = ArraySize(strategyPerformances) + 1;
|
|
ArrayResize(strategyPerformances, newSize);
|
|
|
|
strategyPerformances[newSize - 1].strategyName = strategyName;
|
|
strategyPerformances[newSize - 1].symbol = symbol;
|
|
strategyPerformances[newSize - 1].magicNumber = magicNumber;
|
|
strategyPerformances[newSize - 1].initialLotSize = initialLotSize;
|
|
// Start with minimum lot size for safety (symbol-specific minimum)
|
|
double minLot = GetMinLotSizeForSymbol(symbol);
|
|
strategyPerformances[newSize - 1].currentLotSize = minLot;
|
|
strategyPerformances[newSize - 1].quarterProfit = 0.0;
|
|
strategyPerformances[newSize - 1].quarterTrades = 0;
|
|
strategyPerformances[newSize - 1].quarterWins = 0;
|
|
strategyPerformances[newSize - 1].quarterLosses = 0;
|
|
strategyPerformances[newSize - 1].maxDrawdown = 0.0;
|
|
strategyPerformances[newSize - 1].winRate = 0.0;
|
|
strategyPerformances[newSize - 1].quarterStart = currentMonthStart;
|
|
strategyPerformances[newSize - 1].quarterEnd = currentMonthEnd;
|
|
strategyPerformances[newSize - 1].isActive = true;
|
|
strategyPerformances[newSize - 1].inPenaltyMode = false;
|
|
strategyPerformances[newSize - 1].lotSizeBeforePenalty = initialLotSize;
|
|
strategyPerformances[newSize - 1].penaltyStartTime = 0;
|
|
|
|
totalStrategies = newSize;
|
|
|
|
if(PE_EnableLogging)
|
|
Print("Performance Evaluator: Registered strategy '", strategyName,
|
|
"' (Magic: ", magicNumber, ", Initial Lot: ", initialLotSize, ")");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Update Strategy Performance Metrics |
|
|
//+------------------------------------------------------------------+
|
|
void UpdateStrategyPerformance(string strategyName, int magicNumber)
|
|
{
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].strategyName == strategyName &&
|
|
strategyPerformances[i].magicNumber == magicNumber &&
|
|
strategyPerformances[i].isActive)
|
|
{
|
|
// Calculate performance for current quarter
|
|
double totalProfit = 0.0;
|
|
int totalTrades = 0;
|
|
int wins = 0;
|
|
int losses = 0;
|
|
double maxDD = 0.0;
|
|
double peakBalance = 0.0;
|
|
|
|
// Scan all closed deals in current quarter
|
|
datetime quarterStart = strategyPerformances[i].quarterStart;
|
|
datetime quarterEnd = strategyPerformances[i].quarterEnd;
|
|
|
|
// Select history for the quarter
|
|
if(HistorySelect(quarterStart, quarterEnd))
|
|
{
|
|
int totalDeals = HistoryDealsTotal();
|
|
for(int j = 0; j < totalDeals; j++)
|
|
{
|
|
ulong ticket = HistoryDealGetTicket(j);
|
|
if(ticket > 0)
|
|
{
|
|
long dealMagic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
|
|
if(dealMagic == magicNumber)
|
|
{
|
|
double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
|
|
double swap = HistoryDealGetDouble(ticket, DEAL_SWAP);
|
|
double commission = HistoryDealGetDouble(ticket, DEAL_COMMISSION);
|
|
double totalDealProfit = profit + swap + commission;
|
|
|
|
totalProfit += totalDealProfit;
|
|
totalTrades++;
|
|
|
|
if(totalDealProfit > 0)
|
|
wins++;
|
|
else if(totalDealProfit < 0)
|
|
losses++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate win rate
|
|
double winRate = 0.0;
|
|
if(totalTrades > 0)
|
|
winRate = (double)wins / (double)totalTrades * 100.0;
|
|
|
|
// Update metrics
|
|
strategyPerformances[i].quarterProfit = totalProfit;
|
|
strategyPerformances[i].quarterTrades = totalTrades;
|
|
strategyPerformances[i].quarterWins = wins;
|
|
strategyPerformances[i].quarterLosses = losses;
|
|
strategyPerformances[i].winRate = winRate;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Strategy Ranking Structure |
|
|
//+------------------------------------------------------------------+
|
|
struct StrategyRank {
|
|
int index;
|
|
double score;
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate Strategy Score for Ranking |
|
|
//+------------------------------------------------------------------+
|
|
double CalculateStrategyScore(int strategyIndex)
|
|
{
|
|
double profit = strategyPerformances[strategyIndex].quarterProfit;
|
|
double winRate = strategyPerformances[strategyIndex].winRate;
|
|
double trades = strategyPerformances[strategyIndex].quarterTrades;
|
|
|
|
// Normalize profit (scale to 0-100 range, assuming max profit of $1000)
|
|
double normalizedProfit = MathMin(profit / 10.0, 100.0);
|
|
if(profit < 0) normalizedProfit = profit / 5.0; // Penalize losses more
|
|
|
|
// Calculate score
|
|
double score = 0.0;
|
|
if(PE_UseWinRateWeight)
|
|
{
|
|
// 50% profit, 50% win rate (if enough trades)
|
|
if(trades >= 5)
|
|
score = (normalizedProfit * 0.5) + (winRate * 0.5);
|
|
else
|
|
score = normalizedProfit; // Not enough trades, use profit only
|
|
}
|
|
else
|
|
{
|
|
// Profit only
|
|
score = normalizedProfit;
|
|
}
|
|
|
|
return score;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if Month Ended and Evaluate Performance |
|
|
//+------------------------------------------------------------------+
|
|
void CheckMonthEnd()
|
|
{
|
|
datetime now = TimeCurrent();
|
|
|
|
// Check if we've entered a new month
|
|
if(now >= currentMonthEnd)
|
|
{
|
|
if(PE_EnableLogging)
|
|
Print("Performance Evaluator: Month ended. Evaluating and ranking strategies...");
|
|
|
|
// Update performance metrics for all strategies
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].isActive)
|
|
{
|
|
UpdateStrategyPerformance(strategyPerformances[i].strategyName,
|
|
strategyPerformances[i].magicNumber);
|
|
}
|
|
}
|
|
|
|
// Rank strategies
|
|
int activeCount = 0;
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].isActive)
|
|
activeCount++;
|
|
}
|
|
|
|
if(activeCount > 0)
|
|
{
|
|
// Create ranking array
|
|
StrategyRank ranks[];
|
|
ArrayResize(ranks, activeCount);
|
|
int rankIndex = 0;
|
|
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].isActive)
|
|
{
|
|
ranks[rankIndex].index = i;
|
|
ranks[rankIndex].score = CalculateStrategyScore(i);
|
|
rankIndex++;
|
|
}
|
|
}
|
|
|
|
// Sort by score (descending - highest score first)
|
|
for(int i = 0; i < activeCount - 1; i++)
|
|
{
|
|
for(int j = i + 1; j < activeCount; j++)
|
|
{
|
|
if(ranks[j].score > ranks[i].score)
|
|
{
|
|
StrategyRank temp = ranks[i];
|
|
ranks[i] = ranks[j];
|
|
ranks[j] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Adjust lot sizes based on ranking
|
|
if(PE_EnableAutoAdjustment)
|
|
{
|
|
// Increase top performers (skip if in penalty mode)
|
|
int topCount = MathMin(PE_TopPerformersCount, activeCount);
|
|
for(int i = 0; i < topCount; i++)
|
|
{
|
|
int strategyIdx = ranks[i].index;
|
|
|
|
// Skip if strategy is in penalty mode
|
|
if(strategyPerformances[strategyIdx].inPenaltyMode)
|
|
continue;
|
|
|
|
double oldLotSize = strategyPerformances[strategyIdx].currentLotSize;
|
|
double newLotSize = oldLotSize * (1.0 + PE_LotSizeIncreasePercent / 100.0);
|
|
|
|
if(newLotSize > PE_MaxLotSize)
|
|
newLotSize = PE_MaxLotSize;
|
|
|
|
strategyPerformances[strategyIdx].currentLotSize = newLotSize;
|
|
|
|
if(PE_EnableLogging)
|
|
Print("Performance Evaluator: Rank #", (i+1), " - Increasing '",
|
|
strategyPerformances[strategyIdx].strategyName,
|
|
"' lot size from ", oldLotSize, " to ", newLotSize,
|
|
" (Score: ", DoubleToString(ranks[i].score, 2),
|
|
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
|
|
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%)");
|
|
}
|
|
|
|
// Decrease bottom performers (skip worst one if blitz play is enabled)
|
|
int bottomCount = MathMin(PE_BottomPerformersCount, activeCount);
|
|
int startIdx = activeCount - bottomCount;
|
|
|
|
// If blitz play is enabled, skip the worst performer (it will get minimum penalty)
|
|
if(PE_EnableBlitzPlay && activeCount > 0)
|
|
startIdx = activeCount - bottomCount + 1;
|
|
|
|
for(int i = startIdx; i < activeCount; i++)
|
|
{
|
|
int strategyIdx = ranks[i].index;
|
|
|
|
// Skip if strategy is in penalty mode
|
|
if(strategyPerformances[strategyIdx].inPenaltyMode)
|
|
continue;
|
|
|
|
double oldLotSize = strategyPerformances[strategyIdx].currentLotSize;
|
|
double newLotSize = oldLotSize * (1.0 - PE_LotSizeDecreasePercent / 100.0);
|
|
|
|
// Use symbol-specific minimum lot size
|
|
double minLot = GetMinLotSizeForSymbol(strategyPerformances[strategyIdx].symbol);
|
|
if(newLotSize < minLot)
|
|
newLotSize = minLot;
|
|
|
|
strategyPerformances[strategyIdx].currentLotSize = newLotSize;
|
|
|
|
if(PE_EnableLogging)
|
|
Print("Performance Evaluator: Rank #", (i+1), " - Decreasing '",
|
|
strategyPerformances[strategyIdx].strategyName,
|
|
"' lot size from ", oldLotSize, " to ", newLotSize,
|
|
" (Score: ", DoubleToString(ranks[i].score, 2),
|
|
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
|
|
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%)");
|
|
}
|
|
}
|
|
|
|
// Blitz Play: Apply penalty to worst performer
|
|
if(PE_EnableBlitzPlay && activeCount > 0)
|
|
{
|
|
// Find worst performer (last in ranking)
|
|
int worstIdx = ranks[activeCount - 1].index;
|
|
|
|
// Remove penalty from previous worst performer (if any)
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].isActive && strategyPerformances[i].inPenaltyMode)
|
|
{
|
|
// Check if penalty period has passed (one month)
|
|
if(now - strategyPerformances[i].penaltyStartTime >= 2592000) // ~30 days
|
|
{
|
|
// Restore lot size to before penalty
|
|
strategyPerformances[i].currentLotSize = strategyPerformances[i].lotSizeBeforePenalty;
|
|
strategyPerformances[i].inPenaltyMode = false;
|
|
strategyPerformances[i].penaltyStartTime = 0;
|
|
|
|
if(PE_EnableLogging)
|
|
Print("Blitz Play: Penalty removed from '", strategyPerformances[i].strategyName,
|
|
"'. Lot size restored to ", strategyPerformances[i].currentLotSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply penalty to new worst performer
|
|
if(!strategyPerformances[worstIdx].inPenaltyMode)
|
|
{
|
|
strategyPerformances[worstIdx].lotSizeBeforePenalty = strategyPerformances[worstIdx].currentLotSize;
|
|
// Use symbol-specific minimum lot size
|
|
double minLot = GetMinLotSizeForSymbol(strategyPerformances[worstIdx].symbol);
|
|
strategyPerformances[worstIdx].currentLotSize = minLot;
|
|
strategyPerformances[worstIdx].inPenaltyMode = true;
|
|
strategyPerformances[worstIdx].penaltyStartTime = now;
|
|
|
|
if(PE_EnableLogging)
|
|
Print("Blitz Play: WORST PERFORMER - '", strategyPerformances[worstIdx].strategyName,
|
|
"' penalized! Lot size reduced from ", strategyPerformances[worstIdx].lotSizeBeforePenalty,
|
|
" to minimum ", minLot, " (Score: ", DoubleToString(ranks[activeCount - 1].score, 2),
|
|
", Profit: $", DoubleToString(strategyPerformances[worstIdx].quarterProfit, 2), ")");
|
|
}
|
|
}
|
|
|
|
// Log performance report
|
|
if(PE_EnableLogging)
|
|
{
|
|
Print("=== Monthly Performance Ranking ===");
|
|
for(int i = 0; i < activeCount; i++)
|
|
{
|
|
int strategyIdx = ranks[i].index;
|
|
Print("Rank #", (i+1), ": ", strategyPerformances[strategyIdx].strategyName,
|
|
" - Score: ", DoubleToString(ranks[i].score, 2),
|
|
", Profit: $", DoubleToString(strategyPerformances[strategyIdx].quarterProfit, 2),
|
|
", Win Rate: ", DoubleToString(strategyPerformances[strategyIdx].winRate, 2), "%",
|
|
", Trades: ", (int)strategyPerformances[strategyIdx].quarterTrades,
|
|
", Lot Size: ", DoubleToString(strategyPerformances[strategyIdx].currentLotSize, 2));
|
|
}
|
|
Print("===================================");
|
|
}
|
|
}
|
|
|
|
// Reset month metrics for all strategies
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].isActive)
|
|
{
|
|
strategyPerformances[i].quarterProfit = 0.0;
|
|
strategyPerformances[i].quarterTrades = 0;
|
|
strategyPerformances[i].quarterWins = 0;
|
|
strategyPerformances[i].quarterLosses = 0;
|
|
strategyPerformances[i].maxDrawdown = 0.0;
|
|
strategyPerformances[i].winRate = 0.0;
|
|
}
|
|
}
|
|
|
|
// Update month dates
|
|
MqlDateTime dt;
|
|
TimeToStruct(now, dt);
|
|
|
|
// First day of current month
|
|
dt.day = 1;
|
|
dt.hour = 0;
|
|
dt.min = 0;
|
|
dt.sec = 0;
|
|
currentMonthStart = StructToTime(dt);
|
|
|
|
// First day of next month - 1 second
|
|
dt.mon += 1;
|
|
if(dt.mon > 12)
|
|
{
|
|
dt.mon = 1;
|
|
dt.year++;
|
|
}
|
|
currentMonthEnd = StructToTime(dt) - 1;
|
|
|
|
// Update month dates for all strategies
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
strategyPerformances[i].quarterStart = currentMonthStart;
|
|
strategyPerformances[i].quarterEnd = currentMonthEnd;
|
|
}
|
|
|
|
lastMonthCheck = now;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get Current Lot Size for Strategy |
|
|
//+------------------------------------------------------------------+
|
|
double GetStrategyLotSize(string strategyName, int magicNumber)
|
|
{
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].strategyName == strategyName &&
|
|
strategyPerformances[i].magicNumber == magicNumber &&
|
|
strategyPerformances[i].isActive)
|
|
{
|
|
return strategyPerformances[i].currentLotSize;
|
|
}
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Process Performance Evaluation (call from OnTick) |
|
|
//+------------------------------------------------------------------+
|
|
void ProcessPerformanceEvaluation()
|
|
{
|
|
// Check if month ended
|
|
CheckMonthEnd();
|
|
|
|
// Check for penalty expiration (blitz play)
|
|
if(PE_EnableBlitzPlay)
|
|
{
|
|
datetime now = TimeCurrent();
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].isActive && strategyPerformances[i].inPenaltyMode)
|
|
{
|
|
// Check if penalty period has passed (one month = ~30 days)
|
|
if(now - strategyPerformances[i].penaltyStartTime >= 2592000)
|
|
{
|
|
// Restore lot size to before penalty
|
|
strategyPerformances[i].currentLotSize = strategyPerformances[i].lotSizeBeforePenalty;
|
|
strategyPerformances[i].inPenaltyMode = false;
|
|
strategyPerformances[i].penaltyStartTime = 0;
|
|
|
|
if(PE_EnableLogging)
|
|
Print("Blitz Play: Penalty expired for '", strategyPerformances[i].strategyName,
|
|
"'. Lot size restored to ", strategyPerformances[i].currentLotSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update performance metrics periodically (every hour)
|
|
static datetime lastUpdate = 0;
|
|
if(TimeCurrent() - lastUpdate >= 3600)
|
|
{
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].isActive)
|
|
{
|
|
UpdateStrategyPerformance(strategyPerformances[i].strategyName,
|
|
strategyPerformances[i].magicNumber);
|
|
}
|
|
}
|
|
lastUpdate = TimeCurrent();
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get Performance Summary |
|
|
//+------------------------------------------------------------------+
|
|
string GetPerformanceSummary()
|
|
{
|
|
string summary = "\n=== Performance Summary ===\n";
|
|
summary += "Current Month: " + TimeToString(currentMonthStart) + " to " + TimeToString(currentMonthEnd) + "\n\n";
|
|
|
|
for(int i = 0; i < ArraySize(strategyPerformances); i++)
|
|
{
|
|
if(strategyPerformances[i].isActive)
|
|
{
|
|
summary += strategyPerformances[i].strategyName + ":\n";
|
|
summary += " Profit: $" + DoubleToString(strategyPerformances[i].quarterProfit, 2) + "\n";
|
|
summary += " Trades: " + IntegerToString((int)strategyPerformances[i].quarterTrades) + "\n";
|
|
summary += " Win Rate: " + DoubleToString(strategyPerformances[i].winRate, 2) + "%\n";
|
|
summary += " Lot Size: " + DoubleToString(strategyPerformances[i].currentLotSize, 2) + "\n\n";
|
|
}
|
|
}
|
|
|
|
return summary;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|