EA Spec Protocol (ESP) 0.1
One JSON document per Expert Advisor build (keyed by the file's sha256) that says what the terminal exposes and what people know about every input. Every semantic field carries provenance and a trust level; the AlgoHand control server derives what an assistant may read, change and restart from trust, never from free text.
WhyDocument shapeTrustPermissionsThree ways inAPISchema (JSON)
Why a spec, not a document
A compiled EA shows the terminal its inputs, defaults and nothing else. Whether MaxSpread is safe to change to 50, whether LotSizingMethod=5 means "low risk", whether a restart with open positions loses the basket: that knowledge lives in manuals, in the author's source code and in customers' heads. ESP is the contract that carries it into the control server in a form a machine can check: the terminal is ground truth for structure, the spec only adds meaning, and each meaning knows who asserted it.
Document shape
{
"esp_version": "0.1",
"ea": { "name": "Gridkeeper", "file_name": "gridkeeper_v3.ex5", "sha256": "…", "ea_version": "3.2",
"author": "…", "platform": "MT5", "docs_url": null, "category": "grid", "description": null },
"generated_by": { "path": "author_source | client_manual | discovery_auto | catalog | test_run",
"tool": "algohand-prompt-v1 | onboarding-ui | agent-discovery", "timestamp": "2026-09-08T12:00:00Z" },
"restart_behavior": { "recovers_open_positions": true, "recovery_mechanism": "reads positions by magic number on init",
"safe_to_restart_with_open_positions": true, "notes": null,
"provenance": { "source": "author_source", "trust": "author" } },
"parameters": [
{ "key": "MaxSpread", "type": "double",
"terminal": { "default": 2.0, "min": null, "max": null, "current": null, "enum_labels": null },
"label": "Max spread", "meaning": "Maximum allowed spread in points before entries are skipped.", "unit": "points",
"risk_relevant": false, "safe_range": { "min": 0.5, "max": 5.0 }, "requires_restart": true, "hidden": false,
"provenance": { "terminal_block": "discovery",
"meaning": { "source": "author_source", "trust": "author" },
"safe_range": { "source": "client_manual", "trust": "client", "entered_by": "u_…", "timestamp": "…" } } }
],
"presets": [ { "name": "conservative", "label": "Cautious", "values": { "RiskPercent": 0.5 },
"provenance": { "source": "author_source", "trust": "author" } } ],
"signatures": [ { "who": "author@example.com", "role": "author", "asserted": ["parameters.MaxSpread.meaning"], "at": "…" } ]
}
terminal.* is always filled by AlgoHand's own discovery on a terminal, never taken from an upload. MT5 inputs carry no hard min/max, so those are usually null; safe_range is the human-asserted range. Types: bool, int, double, string, csv, enum. Uploads that fail the schema are refused with the failing paths.
Trust levels and provenance
| trust | source |
|---|---|
none | field empty or unknown (terminal block only) |
inferred | a guess from the input's name and default (AlgoHand's heuristics or AI); shown as a guess, never a permission |
client | entered by the customer in onboarding (chat, dashboard or API) and signed by them |
author | produced from the source code by the author's own AI through the published prompt, or written by the author in the studio |
verified | confirmed by a supervised run on a demo terminal: the reconciler observed the effect |
curated | reviewed and signed off by AlgoHand staff |
Trust only goes up: an upload never overwrites a field asserted by a stronger source. An uploader cannot claim more than their role (a client key asserts client at most). signatures[] records who asserted what and when; a customer who signs a safe range has taken that decision for their own account.
Permissions, derived at request time
| condition | effective permission |
|---|---|
| no spec, or an input with a terminal block only | read-only: status, reporting, pause/stop, remove from chart |
meaning at client+ and not risk_relevant | writable within the known range |
risk_relevant: true | writable only with a safe_range at author+, or a range the client signed for themselves; writes are clamped to it |
| enum without option labels | read-only until the labels are supplied |
requires_restart with open positions | allowed only when safe_to_restart_with_open_positions is asserted at author+ |
| bool switches, comment strings, lot size of a non-risk input | writable at inferred trust (whitelist v1, server-side, versioned) |
Presets are offered only at client trust or better: the customer's own or the author's. AlgoHand never generates or recommends risk presets.
Three ways in, one file
- Author with source code. Paste the published prompt into your own AI together with the source; the source never leaves your machine. The output is an ESP document at
authortrust, uploaded in the author studio. - Closed EA, customer fills the table. Discovery creates the skeleton (terminal blocks); the customer describes inputs row by row, each saved row is signed at
clienttrust. Progressive and never blocking: the EA runs in limited mode at once and inputs unlock as they are described. - Nothing available. Terminal blocks plus
inferredhints, clearly marked as guesses. A supervised run on a demo terminal can promote fields toverified.
Every upload is cross-checked row by row against what the terminal showed: an input the terminal does not have, a wrong type or a wrong default is rejected per row, the rest is accepted; inputs the upload does not mention keep their terminal block. The build's sha256 must match a file the agent found on the customer's VPS or in the studio. A new build gets a new hash: unchanged inputs keep their semantics, only new or changed ones are asked again.
API
| call | what |
|---|---|
GET /v1/esp/schema | the JSON Schema (draft 2020-12), public; also esp.schema.json |
GET /v1/esp/prompt | algohand-prompt-v1: what an author pastes into their own AI together with the source |
POST /v1/esp/validate {"spec": …} | public validation → {valid, errors[{path, message}]} |
GET /v1/eas/{ea_id}/spec | ESP view of a catalog EA with the derived permissions |
GET /v1/clients/{id}/eas/{custom_id}/spec | the customer's spec (stored, else derived from onboarding), permissions, progress |
POST /v1/clients/{id}/eas/{custom_id}/spec {"spec": …, "note": …} | upload or update: schema → sha256 → discovery cross-check → trust merge → signature → policy |
Errors: 422 SPEC_INVALID (schema, with paths), 409 SPEC_SHA_MISMATCH (another build), 422 EA_NOT_DISCOVERED. Full reference: API docs, section 10g of the README.
