2026-08-13 09:00:54 -07:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| PropFirmGuard.mq5 |
|
|
|
|
|
//| Copyright 2026, MetaQuotes Ltd. |
|
|
|
|
|
//| https://www.mql5.com |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-08-15 02:44:23 -07:00
|
|
|
#property copyright "Copyright 2026, MetaQuotes Ltd."
|
|
|
|
|
#property link "https://www.mql5.com"
|
|
|
|
|
#property version "1.00"
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Standard Library |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include <Trade\Trade.mqh>
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Enumerations |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Represents the general compliance condition of the account. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
enum ENUM_COMPLIANCE_STATUS
|
|
|
|
|
{
|
|
|
|
|
COMPLIANCE_SAFE = 0, // Account is operating within configured limits
|
|
|
|
|
COMPLIANCE_WARNING, // Account is approaching a compliance limit
|
|
|
|
|
COMPLIANCE_CRITICAL, // Account is very close to a compliance limit
|
|
|
|
|
COMPLIANCE_BREACHED // A configured compliance limit has been breached
|
|
|
|
|
};
|
2026-08-30 05:53:24 +03:00
|
|
|
|
2026-08-15 02:44:23 -07:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Profit target status |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Represents progress toward the configured account profit target. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
enum ENUM_TARGET_STATUS
|
|
|
|
|
{
|
2026-08-30 05:53:24 +03:00
|
|
|
TARGET_IN_PROGRESS = 0, // Target is still being pursued
|
|
|
|
|
TARGET_NEARING, // At least 90% of the target has been reached
|
|
|
|
|
TARGET_REACHED // Required target balance has been achieved
|
2026-08-15 02:44:23 -07:00
|
|
|
};
|
2026-08-30 05:53:24 +03:00
|
|
|
|
2026-08-15 02:44:23 -07:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Protection action |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Defines what Prop Firm Guard should do when a monitored rule |
|
|
|
|
|
//| requires protective action. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
enum ENUM_PROTECTION_ACTION
|
|
|
|
|
{
|
2026-08-30 05:53:24 +03:00
|
|
|
PROTECTION_WARN_ONLY = 0, // Report the condition without closing positions
|
|
|
|
|
PROTECTION_CLOSE_AFFECTED, // Close positions affected by the triggering rule
|
|
|
|
|
PROTECTION_CLOSE_ALL // Close every open account position
|
2026-08-28 18:38:10 +03:00
|
|
|
};
|
2026-08-30 06:30:53 +03:00
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Inputs |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
input group "General"
|
|
|
|
|
input int InpTimerSeconds = 1; // Dashboard update interval in seconds
|
|
|
|
|
input int InpDashboardX = 10; // Dashboard horizontal position
|
|
|
|
|
input int InpDashboardY = 105; // Dashboard vertical position
|
|
|
|
|
input bool InpEnableAlerts = true; // Enable terminal alerts
|
|
|
|
|
input bool InpEnablePushNotifications = false; // Enable mobile push notifications
|
|
|
|
|
|
|
|
|
|
input group "Prop Firm Rules"
|
|
|
|
|
input double InpInitialAccountBalance = 20000.0; // Initial prop-firm account balance
|
|
|
|
|
input double InpDailyDrawdownPercent = 5.0; // Maximum permitted daily drawdown
|
|
|
|
|
input double InpOverallDrawdownPercent = 10.0; // Maximum permitted overall drawdown
|
|
|
|
|
input double InpProfitTargetPercent = 8.0; // Required profit target
|
|
|
|
|
|
|
|
|
|
input group "Warning Levels"
|
|
|
|
|
input double InpWarningLevelPercent = 70.0; // Loss limit usage that triggers warning
|
|
|
|
|
input double InpCriticalLevelPercent = 90.0; // Loss limit usage that triggers critical state
|
|
|
|
|
|
|
|
|
|
input group "News Filter"
|
|
|
|
|
input bool InpEnableNewsFilter = true; // Enable economic news monitoring
|
|
|
|
|
input ENUM_CALENDAR_EVENT_IMPORTANCE InpNewsImportance = CALENDAR_IMPORTANCE_HIGH; // Minimum monitored importance
|
|
|
|
|
input int InpMinutesBeforeNews = 10; // Restricted minutes before news
|
|
|
|
|
input int InpMinutesAfterNews = 10; // Restricted minutes after news
|
|
|
|
|
input bool InpAllowNewsHolding = true; // Allow positions to remain open during news
|
|
|
|
|
|
|
|
|
|
input group "Protection Actions"
|
|
|
|
|
input ENUM_PROTECTION_ACTION InpDailyDrawdownAction = PROTECTION_WARN_ONLY; // Daily breach action
|
|
|
|
|
input ENUM_PROTECTION_ACTION InpOverallDrawdownAction = PROTECTION_WARN_ONLY; // Overall breach action
|
|
|
|
|
input ENUM_PROTECTION_ACTION InpNewsRestrictionAction = PROTECTION_WARN_ONLY; // News restriction action
|
|
|
|
|
|
2026-08-30 11:53:44 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Account state structure |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Holds the live account values required by the monitoring logic. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
struct SAccountState
|
|
|
|
|
{
|
|
|
|
|
long login; // Trading account login number
|
|
|
|
|
double balance; // Current account balance
|
|
|
|
|
double equity; // Current account equity
|
|
|
|
|
double floatingPL; // Current floating profit or loss
|
|
|
|
|
ENUM_COMPLIANCE_STATUS status; // Current overall compliance status
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-30 06:30:53 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Account settings structure |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Stores the main prop-firm rules associated with this account. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
struct SAccountSettings
|
|
|
|
|
{
|
|
|
|
|
long accountLogin; // Trading account login
|
|
|
|
|
string accountServer; // Broker trading server
|
|
|
|
|
double initialBalance; // Initial prop-firm account balance
|
|
|
|
|
double dailyDrawdownPercent; // Maximum daily drawdown
|
|
|
|
|
double overallDrawdownPercent; // Maximum overall drawdown
|
|
|
|
|
double profitTargetPercent; // Required profit target
|
|
|
|
|
datetime updatedAt; // Last settings update time
|
|
|
|
|
};
|
2026-08-30 11:53:44 +03:00
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Global variables |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
SAccountState g_accountState; // Latest monitored account state
|
|
|
|
|
SAccountSettings g_accountSettings; // Persisted prop-firm settings
|
2026-08-30 13:06:01 +03:00
|
|
|
string g_databaseName = "prop_firm_compliance.sqlite"; // SQLite database file
|
|
|
|
|
int g_database = INVALID_HANDLE; // Active SQLite database handle
|
2026-08-30 11:53:44 +03:00
|
|
|
|
2026-08-30 12:36:36 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Validate compliance inputs |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool ValidateComplianceInputs()
|
|
|
|
|
{
|
|
|
|
|
// Financial calculations require a valid initial challenge balance.
|
|
|
|
|
if(InpInitialAccountBalance <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Initial account balance must be greater than zero.");
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpDailyDrawdownPercent <= 0.0 ||
|
|
|
|
|
InpDailyDrawdownPercent > 100.0)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Daily drawdown percentage must be between 0 and 100.");
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpOverallDrawdownPercent <= 0.0 ||
|
|
|
|
|
InpOverallDrawdownPercent > 100.0)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Overall drawdown percentage must be between 0 and 100.");
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpProfitTargetPercent <= 0.0 ||
|
|
|
|
|
InpProfitTargetPercent > 100.0)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Profit target percentage must be between 0 and 100.");
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpWarningLevelPercent <= 0.0 ||
|
|
|
|
|
InpWarningLevelPercent >= InpCriticalLevelPercent)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Warning level must be greater than 0 and below the critical level.");
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(InpCriticalLevelPercent <= InpWarningLevelPercent ||
|
|
|
|
|
InpCriticalLevelPercent >= 100.0)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Critical level must be above the warning level and below 100.");
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// News restriction windows cannot use negative durations.
|
|
|
|
|
if(InpMinutesBeforeNews < 0 ||
|
|
|
|
|
InpMinutesAfterNews < 0)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: News restriction minutes cannot be negative.");
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(true);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-30 11:53:44 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Update account state |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void UpdateAccountState()
|
|
|
|
|
{
|
|
|
|
|
// Read the account identifier so persisted data can later be tied
|
|
|
|
|
// to the correct trading account.
|
|
|
|
|
g_accountState.login = AccountInfoInteger(ACCOUNT_LOGIN);
|
|
|
|
|
|
|
|
|
|
// Balance represents realized funds currently available on the account.
|
|
|
|
|
g_accountState.balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
|
|
|
|
|
|
|
|
// Equity includes the effect of all currently open positions.
|
|
|
|
|
g_accountState.equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
|
|
|
|
|
|
|
|
// The difference between equity and balance represents the current
|
|
|
|
|
// floating profit or loss of open positions.
|
|
|
|
|
g_accountState.floatingPL = g_accountState.equity - g_accountState.balance;
|
|
|
|
|
|
|
|
|
|
// Compliance calculations have not been introduced yet.
|
|
|
|
|
g_accountState.status = COMPLIANCE_SAFE;
|
|
|
|
|
}
|
2026-08-30 13:06:01 +03:00
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Open database |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool OpenDatabase()
|
|
|
|
|
{
|
|
|
|
|
// Open the database for reading and writing, creating the file
|
|
|
|
|
// automatically when Prop Firm Guard runs for the first time.
|
|
|
|
|
ResetLastError();
|
|
|
|
|
|
|
|
|
|
g_database = DatabaseOpen(g_databaseName,
|
|
|
|
|
DATABASE_OPEN_READWRITE |
|
|
|
|
|
DATABASE_OPEN_CREATE);
|
|
|
|
|
|
|
|
|
|
if(g_database == INVALID_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Could not open database ",
|
|
|
|
|
g_databaseName,
|
|
|
|
|
". Error: ",
|
|
|
|
|
GetLastError());
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Print("Prop Firm Guard: Database opened successfully: ",
|
|
|
|
|
g_databaseName);
|
|
|
|
|
|
|
|
|
|
return(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Close database |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CloseDatabase()
|
|
|
|
|
{
|
|
|
|
|
// Do nothing if no valid database connection currently exists.
|
|
|
|
|
if(g_database == INVALID_HANDLE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ResetLastError();
|
|
|
|
|
|
|
|
|
|
DatabaseClose(g_database);
|
|
|
|
|
|
|
|
|
|
int errorCode = GetLastError();
|
|
|
|
|
|
|
|
|
|
if(errorCode != 0)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Database close reported error: ",
|
|
|
|
|
errorCode);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Database closed successfully.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Never keep a handle after the database has been closed.
|
|
|
|
|
g_database = INVALID_HANDLE;
|
|
|
|
|
}
|
2026-08-30 14:24:21 +03:00
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Ensure database tables exist |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool EnsureDatabaseTables()
|
|
|
|
|
{
|
|
|
|
|
// Store one configuration for each trading account/server pair.
|
|
|
|
|
string settingsTable =
|
|
|
|
|
"CREATE TABLE IF NOT EXISTS settings ("
|
|
|
|
|
"account_login INTEGER NOT NULL,"
|
|
|
|
|
"account_server TEXT NOT NULL,"
|
|
|
|
|
"initial_balance REAL NOT NULL,"
|
|
|
|
|
"daily_drawdown_percent REAL NOT NULL,"
|
|
|
|
|
"overall_drawdown_percent REAL NOT NULL,"
|
|
|
|
|
"profit_target_percent REAL NOT NULL,"
|
|
|
|
|
"updated_at INTEGER NOT NULL,"
|
|
|
|
|
"PRIMARY KEY(account_login, account_server)"
|
|
|
|
|
");";
|
|
|
|
|
|
|
|
|
|
ResetLastError();
|
|
|
|
|
|
|
|
|
|
if(!DatabaseExecute(g_database,
|
|
|
|
|
settingsTable))
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Could not create settings table. Error: ",
|
|
|
|
|
GetLastError());
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Finalize database request |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void FinalizeDatabaseRequest(const int request)
|
|
|
|
|
{
|
|
|
|
|
if(request == INVALID_HANDLE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ResetLastError();
|
|
|
|
|
|
|
|
|
|
DatabaseFinalize(request);
|
|
|
|
|
|
|
|
|
|
int errorCode = GetLastError();
|
|
|
|
|
|
|
|
|
|
if(errorCode != 0)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Could not finalize database request. Error: ",
|
|
|
|
|
errorCode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Execute prepared database request |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool ExecuteDatabaseRequest(const int request,
|
|
|
|
|
const string operation)
|
|
|
|
|
{
|
|
|
|
|
ResetLastError();
|
|
|
|
|
|
|
|
|
|
bool result = DatabaseRead(request);
|
|
|
|
|
|
|
|
|
|
int errorCode = GetLastError();
|
|
|
|
|
|
|
|
|
|
// INSERT and UPDATE statements return no result rows. MQL5 therefore
|
|
|
|
|
// reports ERR_DATABASE_NO_MORE_DATA after successful execution.
|
|
|
|
|
bool success =
|
|
|
|
|
(result || errorCode == ERR_DATABASE_NO_MORE_DATA);
|
|
|
|
|
|
|
|
|
|
if(!success)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Database error while ",
|
|
|
|
|
operation,
|
|
|
|
|
". Error: ",
|
|
|
|
|
errorCode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FinalizeDatabaseRequest(request);
|
|
|
|
|
|
|
|
|
|
return(success);
|
|
|
|
|
}
|
2026-08-30 19:24:48 +03:00
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Save account settings |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool SaveAccountSettings()
|
|
|
|
|
{
|
|
|
|
|
string sql =
|
|
|
|
|
"INSERT OR REPLACE INTO settings ("
|
|
|
|
|
"account_login,"
|
|
|
|
|
"account_server,"
|
|
|
|
|
"initial_balance,"
|
|
|
|
|
"daily_drawdown_percent,"
|
|
|
|
|
"overall_drawdown_percent,"
|
|
|
|
|
"profit_target_percent,"
|
|
|
|
|
"updated_at"
|
|
|
|
|
") VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);";
|
|
|
|
|
|
|
|
|
|
int request = DatabasePrepare(g_database, sql);
|
|
|
|
|
|
|
|
|
|
if(request == INVALID_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Could not prepare settings save request. Error: ",
|
|
|
|
|
GetLastError());
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bind each value separately so text and numeric values remain data
|
|
|
|
|
// rather than being concatenated directly into the SQL statement.
|
|
|
|
|
if(!DatabaseBind(request, 0, g_accountSettings.accountLogin) ||
|
|
|
|
|
!DatabaseBind(request, 1, g_accountSettings.accountServer) ||
|
|
|
|
|
!DatabaseBind(request, 2, g_accountSettings.initialBalance) ||
|
|
|
|
|
!DatabaseBind(request, 3, g_accountSettings.dailyDrawdownPercent) ||
|
|
|
|
|
!DatabaseBind(request, 4, g_accountSettings.overallDrawdownPercent) ||
|
|
|
|
|
!DatabaseBind(request, 5, g_accountSettings.profitTargetPercent) ||
|
|
|
|
|
!DatabaseBind(request, 6, g_accountSettings.updatedAt))
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Could not bind settings values. Error: ",
|
|
|
|
|
GetLastError());
|
|
|
|
|
|
|
|
|
|
FinalizeDatabaseRequest(request);
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// INSERT/UPDATE statements do not return rows. In MQL5 a successful
|
|
|
|
|
// DatabaseRead() therefore finishes with ERR_DATABASE_NO_MORE_DATA.
|
|
|
|
|
if(!ExecuteDatabaseRequest(request, "saving account settings"))
|
|
|
|
|
return(false);
|
|
|
|
|
|
|
|
|
|
return(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Load account settings |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool LoadAccountSettings()
|
|
|
|
|
{
|
|
|
|
|
string accountServer = AccountInfoString(ACCOUNT_SERVER);
|
|
|
|
|
|
|
|
|
|
string sql =
|
|
|
|
|
"SELECT "
|
|
|
|
|
"account_login,"
|
|
|
|
|
"account_server,"
|
|
|
|
|
"initial_balance,"
|
|
|
|
|
"daily_drawdown_percent,"
|
|
|
|
|
"overall_drawdown_percent,"
|
|
|
|
|
"profit_target_percent,"
|
|
|
|
|
"updated_at "
|
|
|
|
|
"FROM settings "
|
|
|
|
|
"WHERE account_login = ?1 AND account_server = ?2;";
|
|
|
|
|
|
|
|
|
|
int request = DatabasePrepare(g_database, sql);
|
|
|
|
|
|
|
|
|
|
if(request == INVALID_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Could not prepare settings load request. Error: ",
|
|
|
|
|
GetLastError());
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!DatabaseBind(request, 0, g_accountState.login) ||
|
|
|
|
|
!DatabaseBind(request, 1, accountServer))
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Could not bind settings search values. Error: ",
|
|
|
|
|
GetLastError());
|
|
|
|
|
|
|
|
|
|
FinalizeDatabaseRequest(request);
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ResetLastError();
|
|
|
|
|
|
|
|
|
|
// A true result means the SELECT statement returned its first row.
|
|
|
|
|
if(!DatabaseRead(request))
|
|
|
|
|
{
|
|
|
|
|
int errorCode = GetLastError();
|
|
|
|
|
|
|
|
|
|
FinalizeDatabaseRequest(request);
|
|
|
|
|
|
|
|
|
|
// No row simply means this account has not been saved before.
|
|
|
|
|
if(errorCode == ERR_DATABASE_NO_MORE_DATA)
|
|
|
|
|
return(false);
|
|
|
|
|
|
|
|
|
|
Print("Prop Firm Guard: Could not read account settings. Error: ",
|
|
|
|
|
errorCode);
|
|
|
|
|
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long accountLogin;
|
|
|
|
|
string storedServer;
|
|
|
|
|
double initialBalance;
|
|
|
|
|
double dailyDrawdown;
|
|
|
|
|
double overallDrawdown;
|
|
|
|
|
double profitTarget;
|
|
|
|
|
long updatedAt;
|
|
|
|
|
|
|
|
|
|
// Read every selected column and verify every database conversion.
|
|
|
|
|
if(!DatabaseColumnLong(request, 0, accountLogin) ||
|
|
|
|
|
!DatabaseColumnText(request, 1, storedServer) ||
|
|
|
|
|
!DatabaseColumnDouble(request, 2, initialBalance) ||
|
|
|
|
|
!DatabaseColumnDouble(request, 3, dailyDrawdown) ||
|
|
|
|
|
!DatabaseColumnDouble(request, 4, overallDrawdown) ||
|
|
|
|
|
!DatabaseColumnDouble(request, 5, profitTarget) ||
|
|
|
|
|
!DatabaseColumnLong(request, 6, updatedAt))
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Could not read stored settings columns. Error: ",
|
|
|
|
|
GetLastError());
|
|
|
|
|
|
|
|
|
|
FinalizeDatabaseRequest(request);
|
|
|
|
|
return(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_accountSettings.accountLogin = accountLogin;
|
|
|
|
|
g_accountSettings.accountServer = storedServer;
|
|
|
|
|
g_accountSettings.initialBalance = initialBalance;
|
|
|
|
|
g_accountSettings.dailyDrawdownPercent = dailyDrawdown;
|
|
|
|
|
g_accountSettings.overallDrawdownPercent = overallDrawdown;
|
|
|
|
|
g_accountSettings.profitTargetPercent = profitTarget;
|
|
|
|
|
g_accountSettings.updatedAt = (datetime)updatedAt;
|
|
|
|
|
|
|
|
|
|
FinalizeDatabaseRequest(request);
|
|
|
|
|
|
|
|
|
|
return(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Initialize account settings |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool InitializeAccountSettings()
|
|
|
|
|
{
|
|
|
|
|
bool settingsFound = LoadAccountSettings();
|
|
|
|
|
|
|
|
|
|
// Associate these settings with the current trading account.
|
|
|
|
|
g_accountSettings.accountLogin = g_accountState.login;
|
|
|
|
|
g_accountSettings.accountServer = AccountInfoString(ACCOUNT_SERVER);
|
|
|
|
|
|
|
|
|
|
// Synchronize the current EA inputs with persistent account settings.
|
|
|
|
|
g_accountSettings.initialBalance = InpInitialAccountBalance;
|
|
|
|
|
g_accountSettings.dailyDrawdownPercent = InpDailyDrawdownPercent;
|
|
|
|
|
g_accountSettings.overallDrawdownPercent = InpOverallDrawdownPercent;
|
|
|
|
|
g_accountSettings.profitTargetPercent = InpProfitTargetPercent;
|
|
|
|
|
g_accountSettings.updatedAt = TimeCurrent();
|
|
|
|
|
|
|
|
|
|
// Store the current configuration so SQLite remains synchronized.
|
|
|
|
|
if(!SaveAccountSettings())
|
|
|
|
|
return(false);
|
|
|
|
|
|
|
|
|
|
if(settingsFound)
|
|
|
|
|
Print("Prop Firm Guard: Account settings restored and synchronized.");
|
|
|
|
|
else
|
|
|
|
|
Print("Prop Firm Guard: New account settings saved.");
|
|
|
|
|
|
|
|
|
|
return(true);
|
|
|
|
|
}
|
2026-08-15 02:44:23 -07:00
|
|
|
|
2026-08-13 09:00:54 -07:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert initialization function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int OnInit()
|
|
|
|
|
{
|
2026-08-30 21:30:12 +03:00
|
|
|
// The monitoring timer introduced later requires an interval of
|
|
|
|
|
// at least one second.
|
|
|
|
|
if(InpTimerSeconds < 1)
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Timer interval must be at least 1 second.");
|
|
|
|
|
|
|
|
|
|
return(INIT_PARAMETERS_INCORRECT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reject invalid prop-firm rules before any account or persistence
|
|
|
|
|
// component begins using the configured values.
|
2026-08-30 12:36:36 +03:00
|
|
|
if(!ValidateComplianceInputs())
|
|
|
|
|
return(INIT_PARAMETERS_INCORRECT);
|
|
|
|
|
|
2026-08-30 21:30:12 +03:00
|
|
|
// Collect the current account identity and live financial values.
|
2026-08-30 11:53:44 +03:00
|
|
|
UpdateAccountState();
|
|
|
|
|
|
2026-08-30 21:30:12 +03:00
|
|
|
// Open the SQLite database used for account-specific persistence.
|
2026-08-30 13:06:01 +03:00
|
|
|
if(!OpenDatabase())
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Database initialization failed.");
|
|
|
|
|
|
|
|
|
|
return(INIT_FAILED);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-30 21:30:12 +03:00
|
|
|
// Ensure the persistence schema exists before settings are accessed.
|
2026-08-30 14:24:21 +03:00
|
|
|
if(!EnsureDatabaseTables())
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Could not initialize database tables.");
|
|
|
|
|
|
|
|
|
|
CloseDatabase();
|
|
|
|
|
|
|
|
|
|
return(INIT_FAILED);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-30 21:30:12 +03:00
|
|
|
// Restore the current account/server record when available and
|
|
|
|
|
// synchronize it with the settings supplied through the Inputs tab.
|
2026-08-30 19:24:48 +03:00
|
|
|
if(!InitializeAccountSettings())
|
|
|
|
|
{
|
|
|
|
|
Print("Prop Firm Guard: Could not initialize account settings.");
|
|
|
|
|
|
|
|
|
|
CloseDatabase();
|
|
|
|
|
|
|
|
|
|
return(INIT_FAILED);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-30 21:30:12 +03:00
|
|
|
Print("Prop Firm Guard initialized successfully.");
|
|
|
|
|
|
2026-08-13 09:00:54 -07:00
|
|
|
return(INIT_SUCCEEDED);
|
|
|
|
|
}
|
2026-08-15 02:44:23 -07:00
|
|
|
|
2026-08-13 09:00:54 -07:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert deinitialization function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnDeinit(const int reason)
|
|
|
|
|
{
|
2026-08-30 21:30:12 +03:00
|
|
|
// Close the SQLite connection after all Part 1 persistence work
|
|
|
|
|
// has finished.
|
2026-08-30 13:06:01 +03:00
|
|
|
CloseDatabase();
|
|
|
|
|
|
2026-08-30 21:30:12 +03:00
|
|
|
// Record why MetaTrader removed or restarted the EA.
|
2026-08-30 13:06:01 +03:00
|
|
|
Print("Prop Firm Guard removed. Deinitialization reason: ", reason);
|
2026-08-13 09:00:54 -07:00
|
|
|
}
|
2026-08-15 02:44:23 -07:00
|
|
|
|
2026-08-13 09:00:54 -07:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Expert tick function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnTick()
|
|
|
|
|
{
|
2026-08-30 05:53:24 +03:00
|
|
|
}
|