forked from Princeec13/mql5
325 lines
11 KiB
MQL5
325 lines
11 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| EnhancedSecurity.mqh |
|
|
//| Copyright 2023, MetaQuotes Software Corp. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2023, MetaQuotes Software Corp."
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
#property strict
|
|
|
|
#include <Trade\AccountInfo.mqh>
|
|
#include <Trade\SymbolInfo.mqh>
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Security Enhancement Class |
|
|
//+------------------------------------------------------------------+
|
|
class CEnhancedSecurity
|
|
{
|
|
private:
|
|
// Security parameters
|
|
bool m_enabled; // Security features enabled
|
|
bool m_allowLiveTrading; // Allow live trading
|
|
bool m_allowModifyOrders; // Allow order modifications
|
|
bool m_allowDeleteOrders; // Allow order deletions
|
|
|
|
// Rate limiting
|
|
int m_maxTradesPerMinute; // Maximum trades per minute
|
|
int m_maxOrdersPerMinute; // Maximum orders per minute
|
|
datetime m_lastTradeTime; // Time of last trade
|
|
int m_tradeCount; // Trade counter for rate limiting
|
|
|
|
// Authorization
|
|
string m_allowedAccountNumbers[]; // Allowed account numbers
|
|
|
|
// Objects
|
|
CAccountInfo *m_account; // Pointer to account info
|
|
|
|
// Private methods
|
|
bool IsAccountAuthorized();
|
|
bool CheckRateLimits();
|
|
|
|
public:
|
|
CEnhancedSecurity();
|
|
~CEnhancedSecurity();
|
|
|
|
// Initialization
|
|
bool Initialize(CAccountInfo *account);
|
|
|
|
// Security checks
|
|
bool ValidateTradeRequest(double lots, double price,
|
|
double sl, double tp,
|
|
ENUM_ORDER_TYPE type);
|
|
|
|
// Getters
|
|
bool IsEnabled() const { return m_enabled; }
|
|
bool IsLiveTradingAllowed() const { return m_allowLiveTrading; }
|
|
|
|
// Setters
|
|
void SetEnabled(bool enabled) { m_enabled = enabled; }
|
|
void SetLiveTrading(bool allow) { m_allowLiveTrading = allow; }
|
|
|
|
// Security utilities
|
|
static string GetAccountFingerprint();
|
|
static bool IsValidSymbol(const string symbol);
|
|
static bool IsValidPrice(double price);
|
|
static bool IsValidLotSize(double lots, const string symbol);
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CEnhancedSecurity::CEnhancedSecurity() : m_enabled(true),
|
|
m_allowLiveTrading(false),
|
|
m_allowModifyOrders(true),
|
|
m_allowDeleteOrders(true),
|
|
m_maxTradesPerMinute(10),
|
|
m_maxOrdersPerMinute(30),
|
|
m_lastTradeTime(0),
|
|
m_tradeCount(0),
|
|
m_account(NULL)
|
|
{
|
|
// Initialize allowed accounts array
|
|
ArrayResize(m_allowedAccountNumbers, 0);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CEnhancedSecurity::~CEnhancedSecurity()
|
|
{
|
|
m_account = NULL;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize security module |
|
|
//+------------------------------------------------------------------+
|
|
bool CEnhancedSecurity::Initialize(CAccountInfo *account)
|
|
{
|
|
if(account == NULL)
|
|
{
|
|
Print("Error: Invalid account info in security initialization");
|
|
return false;
|
|
}
|
|
|
|
m_account = account;
|
|
|
|
// Add current account to allowed accounts if empty
|
|
if(ArraySize(m_allowedAccountNumbers) == 0)
|
|
{
|
|
ArrayResize(m_allowedAccountNumbers, 1);
|
|
m_allowedAccountNumbers[0] = ""; // Empty means current account is allowed
|
|
}
|
|
|
|
// Reset rate limiting
|
|
m_lastTradeTime = 0;
|
|
m_tradeCount = 0;
|
|
|
|
// Check if we're in the strategy tester
|
|
if(MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_VISUAL_MODE) || MQLInfoInteger(MQL_OPTIMIZATION))
|
|
{
|
|
// Allow everything in tester/optimization
|
|
m_allowLiveTrading = true;
|
|
m_allowModifyOrders = true;
|
|
m_allowDeleteOrders = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Validate trade request |
|
|
//+------------------------------------------------------------------+
|
|
bool CEnhancedSecurity::ValidateTradeRequest(double lots, double price,
|
|
double sl, double tp,
|
|
ENUM_ORDER_TYPE type)
|
|
{
|
|
if(!m_enabled)
|
|
return true;
|
|
|
|
// Check account authorization
|
|
if(!IsAccountAuthorized())
|
|
{
|
|
Print("Security: Account not authorized");
|
|
return false;
|
|
}
|
|
|
|
// Check rate limits
|
|
if(!CheckRateLimits())
|
|
{
|
|
Print("Security: Rate limit exceeded");
|
|
return false;
|
|
}
|
|
|
|
// Validate price
|
|
if(!IsValidPrice(price))
|
|
{
|
|
Print("Security: Invalid price: ", price);
|
|
return false;
|
|
}
|
|
|
|
// Validate lot size
|
|
CSymbolInfo symbol;
|
|
symbol.Name(_Symbol);
|
|
if(!IsValidLotSize(lots, symbol.Name()))
|
|
{
|
|
Print("Security: Invalid lot size: ", lots);
|
|
return false;
|
|
}
|
|
|
|
// Validate stop loss and take profit if provided
|
|
if((sl > 0 && !IsValidPrice(sl)) || (tp > 0 && !IsValidPrice(tp)))
|
|
{
|
|
Print("Security: Invalid stop loss or take profit");
|
|
return false;
|
|
}
|
|
|
|
// For market orders, check if live trading is allowed
|
|
if((type == ORDER_TYPE_BUY || type == ORDER_TYPE_SELL) && !m_allowLiveTrading)
|
|
{
|
|
Print("Security: Live trading is disabled");
|
|
return false;
|
|
}
|
|
|
|
// Update trade count for rate limiting
|
|
m_tradeCount++;
|
|
m_lastTradeTime = TimeCurrent();
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if account is authorized |
|
|
//+------------------------------------------------------------------+
|
|
bool CEnhancedSecurity::IsAccountAuthorized()
|
|
{
|
|
if(m_account == NULL)
|
|
return false;
|
|
|
|
// If no accounts are specified, allow all (for backward compatibility)
|
|
if(ArraySize(m_allowedAccountNumbers) == 0)
|
|
return true;
|
|
|
|
// Check if current account is in the allowed list
|
|
string currentAccount = IntegerToString(m_account.Login());
|
|
|
|
for(int i = 0; i < ArraySize(m_allowedAccountNumbers); i++)
|
|
{
|
|
if(m_allowedAccountNumbers[i] == "" || m_allowedAccountNumbers[i] == currentAccount)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check rate limits |
|
|
//+------------------------------------------------------------------+
|
|
bool CEnhancedSecurity::CheckRateLimits()
|
|
{
|
|
// Reset counter if more than a minute has passed
|
|
if(TimeCurrent() - m_lastTradeTime > 60)
|
|
{
|
|
m_tradeCount = 0;
|
|
return true;
|
|
}
|
|
|
|
// Check if we've exceeded the rate limits
|
|
if(m_tradeCount >= m_maxTradesPerMinute)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get account fingerprint |
|
|
//+------------------------------------------------------------------+
|
|
string CEnhancedSecurity::GetAccountFingerprint()
|
|
{
|
|
CAccountInfo account;
|
|
string fingerprint = "";
|
|
|
|
// Create a fingerprint based on account and machine info
|
|
fingerprint += IntegerToString(account.Login());
|
|
fingerprint += "|" + account.Company();
|
|
fingerprint += "|" + account.Server();
|
|
fingerprint += "|" + TerminalInfoString(TERMINAL_COMMONPATH);
|
|
|
|
// Hash the fingerprint for security
|
|
uchar hash[];
|
|
StringToCharArray(fingerprint, hash, 0, -1, CP_UTF8);
|
|
|
|
// Use a simple hash (in a real application, use a proper cryptographic hash)
|
|
uint h = 0;
|
|
for(int i = 0; i < ArraySize(hash); i++)
|
|
h = 31 * h + hash[i];
|
|
|
|
return StringFormat("%X", h);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if symbol is valid |
|
|
//+------------------------------------------------------------------+
|
|
bool CEnhancedSecurity::IsValidSymbol(const string symbol)
|
|
{
|
|
if(symbol == NULL || StringLen(symbol) == 0)
|
|
return false;
|
|
|
|
// Check if symbol is selected in MarketWatch
|
|
if(!SymbolSelect(symbol, true))
|
|
{
|
|
Print("Security: Symbol ", symbol, " is not available");
|
|
return false;
|
|
}
|
|
|
|
// Check if symbol is visible and tradable
|
|
if(!SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_DISABLED)
|
|
{
|
|
Print("Security: Trading is disabled for symbol ", symbol);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if price is valid |
|
|
//+------------------------------------------------------------------+
|
|
bool CEnhancedSecurity::IsValidPrice(double price)
|
|
{
|
|
// Check for NaN and infinity
|
|
if(!MathIsValidNumber(price))
|
|
return false;
|
|
|
|
// Check if price is within reasonable bounds
|
|
if(price <= 0 || price > 1000000) // Adjust these values based on your needs
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if lot size is valid |
|
|
//+------------------------------------------------------------------+
|
|
bool CEnhancedSecurity::IsValidLotSize(double lots, const string symbol)
|
|
{
|
|
if(lots <= 0)
|
|
return false;
|
|
|
|
// Get symbol parameters
|
|
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
|
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
|
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
|
|
|
// Check against symbol limits
|
|
if(lots < minLot || lots > maxLot)
|
|
return false;
|
|
|
|
// Check if lot size is a multiple of lot step
|
|
double remainder = MathMod(lots, lotStep);
|
|
if(remainder > 0.0000001) // Account for floating point precision
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|