Experts: Prop-Firm Equity Guard - daily and max drawdown protection (FTMO-style)

Prop-Firm Equity Guard - daily and max drawdown protection (FTMO-style):

Account-level protection utility for prop-firm challenges: daily and max drawdown limits with a safety buffer. Closes all positions and raises a global halt flag other EAs can check.

Prop-Firm Equity Guard - daily and max drawdown protection (FTMO-style)

Author: Cristian Ciunae

How can I get it? 
Given M #:
How can I get it? 
Click the link in the first post.

Nice concept, but there's a state-loss bug that undermines the "permanent" MAX-breach guarantee: OnInit() unconditionally calls GlobalVariableSet(InpHaltFlagName, 0.0) (line 81), and g_maxBreached/g_dailyBreached are plain in-memory bools with no persistence — nothing reads them back in OnInit. So the very first EA restart after a breach (VPS reboot, terminal update, recompile, manual re-attach — routine on a prop-firm VPS) clears the halt flag and re-snapshots the day-start baseline from whatever equity is left, even after a MAX drawdown breach the doc describes as blocking trading "permanently." The account gets unblocked exactly where it was mid-blown, with a fresh (lower) starting point.

Simple fix: persist g_maxBreached (and ideally g_dailyBreached) to a GlobalVariable or file, and have OnInit() check it before ever writing 0.0 to the halt flag — treat "was this account already halted" as state to restore, not state to re-derive from zero on every restart.

Valerii Sakara #:

Nice concept, but there's a state-loss bug that undermines the "permanent" MAX-breach guarantee: OnInit() unconditionally calls GlobalVariableSet(InpHaltFlagName, 0.0) (line 81), and g_maxBreached/g_dailyBreached are plain in-memory bools with no persistence — nothing reads them back in OnInit. So the very first EA restart after a breach (VPS reboot, terminal update, recompile, manual re-attach — routine on a prop-firm VPS) clears the halt flag and re-snapshots the day-start baseline from whatever equity is left, even after a MAX drawdown breach the doc describes as blocking trading "permanently." The account gets unblocked exactly where it was mid-blown, with a fresh (lower) starting point.

Simple fix: persist g_maxBreached (and ideally g_dailyBreached) to a GlobalVariable or file, and have OnInit() check it before ever writing 0.0 to the halt flag — treat "was this account already halted" as state to restore, not state to re-derive from zero on every restart.

Thanks Valerii, that's a valid catch and a serious one.

You're right: OnInit() resets the halt flag unconditionally, and both breach flags are memory-only, so any restart (VPS reboot, terminal update, re-attach) clears a permanent halt and re-snapshots the day-start baseline from the reduced equity. That defeats the whole point of the MAX rule.

I'm fixing it by treating the halt as persisted state: OnInit() reads the flag first and restores g_maxBreached / g_dailyBreached from it, and only initialises when nothing is stored. I'll also persist the day-start equity together with its date, otherwise the daily limit recomputes from whatever is left after a mid-day restart.

One more thing I'll handle: global variables are dropped after 4 weeks of inactivity, so for a "permanent" halt I'll mirror the state to a small file in Common\Files and read that as the source of truth.

Updated version coming shortly. Appreciate you taking the time to read the code that carefully.
Valerii Sakara #:

Nice concept, but there's a state-loss bug that undermines the "permanent" MAX-breach guarantee: OnInit() unconditionally calls GlobalVariableSet(InpHaltFlagName, 0.0) (line 81), and g_maxBreached/g_dailyBreached are plain in-memory bools with no persistence — nothing reads them back in OnInit. So the very first EA restart after a breach (VPS reboot, terminal update, recompile, manual re-attach — routine on a prop-firm VPS) clears the halt flag and re-snapshots the day-start baseline from whatever equity is left, even after a MAX drawdown breach the doc describes as blocking trading "permanently." The account gets unblocked exactly where it was mid-blown, with a fresh (lower) starting point.

Simple fix: persist g_maxBreached (and ideally g_dailyBreached) to a GlobalVariable or file, and have OnInit() check it before ever writing 0.0 to the halt flag — treat "was this account already halted" as state to restore, not state to re-derive from zero on every restart.

Follow-up: the fix is done.

You were right: OnInit() cleared the halt flag unconditionally, and both breach flags were memory-only, so any restart unblocked an account that should stay halted and re-anchored the daily limit to the reduced equity.

Fixed in 1.10: the breach state and the day-start snapshot are now persisted to a per-account file in Terminal\Common\Files. OnInit() restores them instead of writing 0.0, and OnDeinit() no longer clears an active breach. A new input, InpResetState, clears a stored halt when starting a new challenge. Tested: halt triggers, EA removed and re-attached, halt survives.

Update submitted. Appreciate the careful read.