Graphe des révisions Universal_Quant_EA/Include/UQ_Risk.mqh
Auteur SHA1 Message Date
c9f83f945a fix: stamp persisted risk state on one consistent broker-server clock
UQStateSave() overwrote updatedAt with TimeCurrent(), the last-quote time, while ApplyResetRequest() created the cycle on UQServerNow(), the broker-server time. On a quiet market the quote clock trails the server clock, so a legitimate owner reset produced updatedAt < createdAt and UQStateValidate() refused the staged state. The lock and baseline timestamps had the same split clock, and the mirrored case (a quote clock slightly ahead of the server clock, so staticLockTime > createdAt) also refused a valid reset.

All persisted state timestamps now come from UQStateStampResolve(), which resolves one stamp from UQServerNow() for the whole save: it never precedes the state's own creation, never rewinds the timestamp already proven on disk, refuses an unavailable clock, and refuses a value beyond the plausibility window UQStateValidate() enforces. Validation, the state schema and the reset authorization are unchanged.
2026-10-31 20:14:48 +07:00
5cb0c5b7cf Fence state-temp cleanup and stop destructive marker deletion
UQ-PERSISTENCE-CLEANUP-FENCING-001.

UQStateSave(): the shared '<primary>.tmp' staging name is replaced by an operation-scoped locator (V2 fingerprint + fixed 20 character operation tag: 12 hex instance seed and 8 hex monotonic counter). The tag keeps the whole name at 104 characters, well inside the MAX_PATH bound, and can never equal the '.cfg' primary or the '.bak' backup. The unconditional pre-write FileDelete() is removed because the name is private to the operation.

Every save failure path now calls UQStateDiscardTemp(), which re-verifies ownership at the moment of the removal and leaves the operation-owned temporary file as an orphan when ownership is lost. No shared, foreign or unidentifiable temporary file is deleted and no scope-wide sweep is added.

CUQRisk::ApplyResetRequest(): a failed initialization-marker update no longer calls GlobalVariableDel(). The marker keeps its previous value, the reset is reported as rejected and the evidence is preserved instead of being converted into marker absence. No marker-removal operation remains in the risk controller.

No change to the V2 fingerprint, primary/backup locators, legacy migration rules, guard or ballot protocol, risk limits or strategy.
2026-10-31 19:50:34 +07:00
UQ Review UQ Review
6408907b92 Fix duplicate-instance ownership integrity (V2 guard + persistence gate)
Defect A - shared guard deletion and first-creation race:

- The shared V2 guard is now a persistent coordination object. It is never
  deleted after creation; release and rollback are both a compare-and-swap
  back to the exact unclaimed sentinel (0.0), so a contender that acquires
  the guard between an owner's CAS and its cleanup keeps its claim.
- First creation is fenced by ballots: the contender announces its own
  ballot, scans for live foreign ballots, re-reads the guard AFTER the scan
  (a stale observation of absence is never used), verifies the sentinel it
  wrote, claims it by CAS and re-scans afterwards. A rival found at any
  point fails the claim closed and the guard is given back with a CAS to
  the sentinel - never deleted, never overwritten.
- The owner's ballot stays visible for as long as it owns the guard and is
  renewed by the heartbeat; only a contender's own ballot is ever removed,
  and only after it no longer owns the guard.
- UQ_GUARD_UNCLAIMED is a distinct protocol state, accepted for the V2
  guard namespace only. The legacy guard namespace keeps zero as corrupt
  evidence, and negative, fractional, non-finite and future values stay
  invalid everywhere.
- Ownership loss is latched for the lifetime of the guard object.

Defect B - ownership loss must stop persistence:

- CUQInstanceGuard implements CUQOwnershipVerifier: a read-only proof that
  the guard still carries this instance's value and is still inside the TTL.
- CUQRisk binds that verifier after the guard is acquired and before Init(),
  and refuses to initialize without one.
- A persistence boundary inside CUQRisk gates the primary write, the backup
  write, the staging write, legacy migration, V2 marker creation, marker
  restoration, marker removal and reservation reconciliation. Any failure
  latches the loss: the controller is unhealthy, entries are blocked, and
  no retry or reacquisition is possible in that instance.
- UQStateSave() takes the verifier and fences its three file mutations
  individually, so a loss proven before the primary commit cannot write it.

EA event ordering:

- OnTick() and OnTimer() verify ownership before risk evaluation, lock
  servicing, trailing and symbol processing.
- OnTradeTransaction() does not invoke persistence-capable reservation
  reconciliation after the loss.
- OnDeinit() releases only a guard this instance provably still holds.
- Heartbeats are throttled; every event performs the read-only proof, so a
  two second timer is not the only discovery path.

Input semantics:

- InpPreventDuplicateInstances is deprecated and reserved. Exclusive
  persistence ownership is unconditional, and an ownership loss always
  blocks entries, so the input has no effect. It is kept so existing .set
  files keep loading, and a runtime note reports that it is ignored.

Preserved unchanged: V2 fingerprint and file locators, legacy identity hash
and key, legacy ambiguity refusal, risk limits and strategy logic.
2026-10-31 19:20:50 +07:00
UQ Review UQ Review
6eb215caac fix: acquire exclusive instance ownership before persistent risk access
- establish V2 guard ownership before CUQRisk::Init() can read, migrate, create or save a persistence scope; initialization fails closed when ownership is not proven

- replace check-then-publish with a compare-and-swap ownership protocol: atomic stale takeover, heartbeat that proves continued ownership, release that cannot touch a successor, and a private-ballot fence for the one non-atomic step (creating the variable)

- treat an existing guard or marker with an unusable value as an error instead of absence: zero, negative, non-finite, fractional and implausibly future values all refuse

- refuse legacy migration when the legacy identity serialisation is ambiguous (identical legacy key and identical legacy hash for a different tuple)
2026-10-31 13:47:47 +07:00
2ed89c6d3c fix: introduce collision-resistant identity locators with state migration 2026-10-31 13:28:11 +07:00
946d4b0e9d fix: enforce account scope and daily monitoring continuity
Schema 7 adds a persisted monitoring heartbeat, the last recorded gap and a gap hold, with deterministic migration from schema 6 that keeps every lock, cycle identity and reservation.

D0 is no longer invented on startup or on a rollover: a trading day is adopted only when its boundary was observed while the EA was executing, or when the equity path across it is provably constant (no position, no pending order, no account deal). Otherwise the previous D0 is preserved, a monitoring-gap hold blocks every new entry and the exact gap is reported; the hold survives restart and clears only at an observed day boundary or an explicit owner cycle. Static, target and daily thresholds keep their priority, so a breach is still locked and liquidated while a day is unverified.

Account scope is now counted and disclosed for owned and unmanaged positions and pending orders; unmanaged pending orders block entries under the external-exposure guard, and the EA states that the account-wide limits cannot be guaranteed while whole-account liquidation stays disabled.
2026-10-30 11:20:47 +07:00
Universal Quant EA Universal Quant EA
61485d6bf4 fix: recover the latest verified persistent state
The backup was built from the previous primary before the new primary was installed, so it could lag the newest successful update, and a missing primary was still treated as a candidate fresh installation. Neither an interrupted commit nor a stale mirror was detectable.

B1 state schema 6 (defines UQ_STATE_FORMAT=6, UQ_STATE_IDENTITY_FORMAT=5) adds a monotonic stateRevision plus recoveryHold/recoveryHoldTime. Formats 1..5 keep their own field block, so every published checksum still verifies over exactly the block that produced it, and a migration sets generation 1 without inventing a lock event.

B2 UQStateSave is write-ahead and generation-aware: the next generation is derived from what is proven on disk (primary and backup), the staged file is written and read back, the same verified bytes go to the backup and are read back, the primary is replaced and read back, and primary and backup are finally reconciled on generation and content. A newer verified state is never overwritten by an older instance; a failure at any step returns false, which blocks every new entry.

B3 LoadPersistentState orders primary and backup by generation: newer primary wins, equal generations must agree on content (otherwise fail closed), and a backup exactly one generation ahead is the interrupted-commit window and is adopted. Anything else is refused.

B4 a backup is evidence of a previous initialization, so a missing primary is never a fresh installation and never creates a replacement B0; a verified backup is restored only under explicit owner authorization.

B5 a recovered, reordered or superseded state always starts behind a persisted recovery hold (UQ_BLOCK_RECOVERY) that survives restart and only an explicit owner-authorized recovery risk cycle (UQ_RESET_RECOVERY, fresh nonce, explicit B0, flat scope) can clear. A recovery never clears a permanent lock the recovered state carries, and a cycle without a prior permanent lock records no prior-cycle reference instead of an inconsistent one.

B6 the initialization marker is written and then verified; a marker that cannot be persisted or verified fails the load closed.

B7 the status panel reports the persistence generation and an active recovery hold.
2026-10-30 11:09:43 +07:00
be53bc0c07 fix: correlate outstanding entry risk reservations
A reservation could be released merely because an unrelated position or pending order existed under the same Magic Number, and an order ticket was treated as a position ticket. An entry whose submission result was lost also released its reserved risk.

- UQ_Defs: state schema 5 adds the reservation identity; ENUM_UQ_SUBMIT_RESULT and a retcode classifier that only accepts proven non-execution.

- UQ_State: reservation id, magic, scope, direction, volume, entry, stop, order/deal/position tickets are persisted, checksummed and strictly validated. A previous-schema reservation migrates to UNRESOLVED with its money preserved, never to NONE.

- UQ_Risk: UQReserveCorrelate links a reservation to the broker only through its own order ticket, a deal whose DEAL_ORDER is that ticket, or the traced POSITION_IDENTIFIER. Ambiguity, a disconnected terminal and a missing record all keep the money reserved as UNRESOLVED and block entries.

- UQ_Exec: UQSendMarket reports the submission outcome; the caller withdraws reserved risk only on a local preflight failure or a proven broker rejection.

- Universal_Quant_EA.mq5: deals and order transactions reconcile the reservation immediately; status and startup diagnostics expose the reservation identity.
2026-10-30 10:43:15 +07:00
c6bdac5fbf fix: fail closed on invalid aggregate exposure
UQExposureFromPools() no longer converts an unusable OrderCalcProfit() result into a plausible risk with MathAbs().

Every owned position and pending order is now validated: direction, instrument pricing, volume, entry/reference price, protective stop, stop side, OrderCalcProfit success, finiteness and a strictly adverse monetary result. A zero, positive, non-finite or inconsistent result marks the aggregate exposure unbounded, records the symbol, ticket and reason, and refuses new entries instead of counting the item as zero risk.

The conservative worse-of opening/current reference rule is unchanged, the aggregate formula (1.0% of live equity) is unchanged, and unquantifiable exposure is never represented as zero. The volume sizing and the reserved entry risk in UQ_Exec.mqh require the same adverse result, so a fabricated amount can never enter the reservation or the ceiling.
2026-10-30 10:33:44 +07:00
Universal_Quant_EA Universal_Quant_EA
0bfd26c9a9 fix: validate dual permanent-lock state
The owner-reset writer could record priorLockKind=3 (both permanent locks armed), but the state validator accepted only 0, 1 and 2, so a persisted state written by a partial reset was rejected on the next load and the surviving permanent lock could not be reloaded at all.

- accept the documented bit field 0..3 in the validator and keep the structural checks (prior cycle id, baseline, lock event timestamp); - introduce shared helpers so writer, validator and display cannot diverge again; - record the lock history with those helpers and reject a recorded permanent lock that has no consumed reset nonce; - log and display the recorded prior lock kind, and state explicitly that a partial reset leaves the other permanent lock blocking entries.
2026-10-30 10:20:54 +07:00
3ced6419e6 fix: persist the first risk-cycle identity on a fresh initialization
A fresh owner initialization established B0 and D0 but never set the risk-cycle sequence or cycle identifier, so the state schema rejected the staged file with 'risk-cycle sequence is not positive'. The state was then reported as unwritable, new entries were blocked and OnInit failed closed, which made a brand-new deployment impossible to start. The initial cycle now records cycle 1, its identifier and an empty prior-cycle reference, so the authorized baseline can be verified and persisted.
2026-10-30 09:03:13 +07:00
4edc78650f fix: account for pending-order and reserved entry risk in the aggregate ceiling
One authoritative exposure calculation (UQExposureFromPools) replaces the positions-only aggregate: owned positions, owned pending orders and money reserved by an accepted but unreconciled entry all count, and a stop-less or non-convertible exposure is reported as unbounded so entries are refused.

An entry now reserves its monetary risk and persists the reservation before the request leaves the terminal (state schema 4). The reservation keeps blocking entries until the broker state proves the position or the pending order it produced; a verified rejection or a connected re-query that finds no exposure releases it. An unreconciled reservation survives a restart.

Position risk is measured from the worse of the entry and current price down to the stop, so the bid/ask spread cannot release aggregate budget right after an entry.

Sizing and risk now use the tick-size normalised stop that is actually submitted, the final volume is re-checked against the budget and the remaining aggregate budget, and OrderCheck validates the exact request. Adds the optional InpRiskBufferPct withholding for costs and slippage (default 0).
2026-10-30 08:52:18 +07:00
7173abfabb fix: treat an owner reset as an explicit new risk cycle
A permanent static-drawdown or equity-target lock is no longer cleared in place. Every accepted reset now closes the current cycle and opens a new one with an owner-declared positive baseline, a new daily baseline, a new cycle identifier, a new creation timestamp and a consumed nonce, and it is persisted and re-verified before entries may resume.

State schema moves to format 3 (cycle identity, consumed nonce history, prior-cycle reference). Formats 1 and 2 remain loadable: the checksum is verified over the field set that produced it and the state is then migrated deterministically.

A rejected reset fails closed - the lock stays active and entries stay blocked - instead of aborting risk management, and a nonce consumed by an earlier cycle is acknowledged but never re-applied after a restart.

Also fixes an unreachable state: the two permanent locks are independent and may both be active, so the loader no longer refuses such a state (which previously made it unwritable).
2026-10-30 08:37:46 +07:00
062c53e81c fix: validate persistent risk-state integrity and fail closed on missing state
* UQ_State: checksummed schema v2 with strict typed parsing, duplicate/unknown-field rejection and semantic validation of every mandatory field; legacy v1 files are validated and migrated on load

* UQ_State: verified backup retention, staged-file verification before commit and post-commit re-verification

* UQ_State: redundant identity-scoped initialization marker reconciled against the persisted state

* UQ_Risk: a new risk cycle requires an explicit owner authorization and a deliberately declared positive B0; the current account balance is never used as a substitute

* UQ_Risk: missing, empty, corrupt, foreign, version-unsupported or schema-invalid state fails closed; backup recovery only under explicit owner authorization

* UQ_Risk: a failed state write degrades the controller to unhealthy, blocks all new entries, preserves the in-memory lock and retries at a bounded interval

* UQ_Risk: daily rollover keeps permanent locks and reports unmonitored intervals explicitly

* UQ_Defs: state schema constants, marker global-variable prefix and the STATE PERSISTENCE FAILED block reason

* Universal_Quant_EA: first-initialization and backup-restore authorization inputs, input validation and state-health status line
2026-10-30 08:07:18 +07:00
fe73380ead feat: add Universal_Quant_EA with channel breakout signals and centralized equity risk controls
Volatility-normalized channel breakout on completed candles with EMA trend filter, ATR initial stop and ATR trailing stop. Centralized risk controller enforcing 5% daily loss, 8% static drawdown and 10% equity target with persistent, identity-scoped locks. Deterministic broker symbol resolution, risk-based sizing via OrderCalcProfit, bounded liquidation and duplicate-instance guard.
2026-10-30 07:19:59 +07:00