Memory plus an expiry policy: what a restarted guard is allowed to assume

VigilDesk engineering notes

Persisting state is the easy half. Every interesting bug I have hit in restart-safe guards was in the second half: deciding what the resumed process is allowed to assume about a world that kept moving while it was gone.

The resumed process is not the same process resumed. It is a new process that happens to share a file.

1. A counter without a date is not state

Restore the value and forget which day it belongs to, and you get one of two failures, both of which look like success:

Neither throws. Both are the persistence "working".

2. Write ordering is a real decision

Persist before acting and a crash in between lets the action through twice. Persist after acting and the crash window is genuinely there - an action that happened and was not recorded. Only the second ordering cannot double-act, and choosing it means accepting an unbounded-by-default window instead of pretending the window does not exist. Bound it deliberately (the action is idempotent, the state is reconciled from the broker on start) rather than hoping.

3. Staleness must be a first-class state

The resumed guard inherits a world model that was true four minutes ago. If it cannot distinguish "my memory is old" from "my memory is current", it will act on a stale premise with complete confidence - which is exactly the shape of every silent failure in this series.

So the question worth designing against is not "did the memory survive?" but:

"Does the system know how old its own memory is?"

If the answer is no, then everything downstream is unfalsifiable.

4. The unit is a record, not a value

FieldWhy it must be persisted together with the value
the valueobviously
its day / period stampotherwise the record cannot be scoped to a period, only applied or discarded
when it was writtenso the next process can measure its own staleness instead of guessing
which clock it was written againstthe broker's day is not the host's day (see the trading-day note)
the decision it encodesstate that records inputs without the verdict can be re-derived wrongly

What this looks like in practice

{
  "day":        20261004,          // broker trading day this record scopes to
  "anchor":     10000.00,          // equity at the start of that day
  "used":       412.50,            // measured drawdown/usage inside the day
  "blocked":    true,              // the verdict, persisted, not recomputed
  "written_at": 1759555200,        // so a later process can compute its own age
  "clock":      "trade_server"
}

On start: compare day with the current broker day. Equal -> restore. Different -> reset, and say so in the log. Measure now - written_at: if it exceeds the policy, treat the record as untrusted and say so. Two comparisons, and the whole class of "it restored, therefore it is protecting me" disappears.

The uncomfortable summary

Persistence answers "did the memory survive?". It does not answer "is the memory still true?", "is it still mine?", or "is it still today?". Those are policy questions, and they have to be written down next to the data - because the process reading them later cannot reconstruct the context it was written in.


Related notes

Get the guard

Free MT5 daily-loss guard, source included: https://xuks124.github.io/vigildesk/free.html

Risk disclosure

Algorithmic trading carries both technical and market risk. No tool eliminates the possibility of loss. Nothing on this page is investment advice, and no performance is implied or promised.