Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .agents/skills/mt5-httpapi/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,56 @@ curl -H "Authorization: Bearer $MT5_API_TOKEN" "$MT5_API_URL/history/deals?from=

Deal fields: `type` (0=buy, 1=sell), `entry` (0=opening, 1=closing), `profit` (0 for entries, realized P&L for exits).

### Chart Deployments

Deploy Expert Advisors to charts over HTTP — no RDP, no terminal restart.
Stage `.ex5` + `.set` files, declare deployments, and a resident loader EA
inside the terminal reconciles charts to match. The API holds desired state;
the loader reports observed truth. A deployment only flips to `running` once
the loader confirms the expert is live on a chart.

**Setup:** automatic via `[StartUp] Expert=` on boot. No manual attach needed.
Disable per terminal with `chartctl: false`.

Endpoint reference:

| Method | Endpoint | Description |
| ------ | -------- | ----------- |
| `POST` / `GET` / `DELETE` | `/experts` `/experts/<hash>` | Stage, list, remove EA `.ex5` |
| `POST` / `GET` | `/sets` `/sets/<name>` | Stage, list, inspect `.set` (returns parsed inputs) |
| `POST` / `GET` | `/deployments` | Create or list deployments |
| `GET` / `PATCH` / `DELETE` | `/deployments/<id>` | Inspect, pause/resume, change set, delete |
| `POST` | `/deployments/reconcile` | Force immediate reconcile |
| `GET` | `/charts` | Live chart/EA inventory |
| `GET` | `/loader` | Loader EA status and version |
| `POST` | `/charts/<id>/screenshot` | Capture chart as PNG |
| `POST` | `/charts/<id>/close` | Close a chart by id |

```bash
# Stage artifacts
curl -F "expert=@HappyGoldScalp.ex5" "$MT5_API_URL/experts"
curl -F "set=@gold-m5.set" "$MT5_API_URL/sets"

# Deploy
curl -X POST "$MT5_API_URL/deployments" \
-H "Authorization: Bearer $MT5_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"expert":"HappyGoldScalp.ex5","set":"gold-m5.set","symbol":"XAUUSD","timeframe":"M5"}'

# Check status
curl -H "Authorization: Bearer $MT5_API_TOKEN" "$MT5_API_URL/deployments"

# Pause / resume / delete
curl -X PATCH "$MT5_API_URL/deployments/dep_a1b2c3" \
-H "Authorization: Bearer $MT5_API_TOKEN" \
-d '{"enabled":false}'
curl -X DELETE "$MT5_API_URL/deployments/dep_a1b2c3" \
-H "Authorization: Bearer $MT5_API_TOKEN"
```

Full protocol: [`docs/chart-control-protocol.md`](docs/chart-control-protocol.md).
WebRequest allowlist provisioning: `GET/PUT /webrequest`, `POST /webrequest/apply`.

### Backtest

Run MT5 Strategy Tester via the API. Two-stage workflow: build the INI from a
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ mt5installers/*
assets/experts/*
!assets/experts/.gitkeep
!assets/experts/MT5SystemWarmup.mq5
!assets/experts/MT5ChartLoader.mq5
!assets/experts/include
!assets/experts/include/ChartControl.mqh
assets/sets/*
!assets/sets/.gitkeep
config/config.yaml
Expand Down
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,40 @@ The project follows [Semantic Versioning](https://semver.org/): patch = bug fixe

## [Unreleased]

Remote EA deployment: attach Expert Advisors to charts with set files over the
HTTP API, no RDP and no terminal restart.

### Added

- **Chart Deployments feature** (`mt5api/chartctl/`, `mt5api/handlers/chartctl.py`). Stage `.ex5`/`.set` artifacts, declare deployments (expert + set + symbol + timeframe) as desired state, and a resident loader EA reconciles the terminal's charts to it. New endpoints: `POST/GET/DELETE /experts`, `POST/GET /sets` + `GET /sets/<name>`, `POST/GET /deployments` + `GET/PATCH/DELETE /deployments/<id>`, `POST /deployments/reconcile`, `GET /charts`, `GET /loader`, `POST /charts/<chart_id>/screenshot`.
- **Chart Control Protocol v1** — a file-based contract in `MQL5\Files\chartctl\` (`desired.json` / `observed.json` / command channel). Documented in `docs/chart-control-protocol.md`. Deployments only report `running` once the loader confirms the expert is live on a chart; drift and failures surface in `observed.json`.
- **Reference loader EA** `assets/experts/MT5ChartLoader.mq5` plus the portable include `assets/experts/include/ChartControl.mqh`, so an existing resident EA (e.g. an account tracker) can adopt the protocol with three calls instead of running a second EA. Single-loader mutex via a terminal GlobalVariable makes co-existence safe.
- Config block `chartctl:` in `config.yaml` (enable flag, reconcile hint, staleness window, command timeout, upload cap). Live-mode terminals only; per-terminal `chartctl: false` override.
- **Zero-touch loader bootstrap** — `scripts/compile-chartctl-loader.bat` auto-compiles the loader in every broker base on boot and propagates the `.ex5` to existing terminal instances; `config_helper.py write_ini` adds a `[StartUp] Expert=Advisors\MT5ChartLoader` section (honoring `symbol_suffix`) to live chartctl-enabled terminals, so the loader attaches itself at terminal launch. No RDP or manual attach anywhere in the deploy path. Duplicate loaders from re-fired `[StartUp]` lines self-close via the mutex.
- Tests: `tests/test_chartctl_units.py`, `tests/test_chartctl_endpoints.py`, and a Python `tests/chartctl_fake_loader.py` that plays the EA side of the protocol so the full endpoint suite runs on Linux with no MT5.
- **WebRequest allowlist provisioning** (`GET`/`PUT /webrequest`, `POST /webrequest/apply`; `mt5api/chartctl/webrequest.py`, `mt5api/chartctl/autoit_webrequest.py`, `mt5api/handlers/webrequest.py`). Set the terminal's `WebRequest()` allowed-URL list over the API instead of the Options dialog. A dedicated call (not a deployment field) since it's rarely needed. Two apply paths, chosen at runtime:
- **Windows VM (default here):** the allowlist is *not* stored in `common.ini` on this terminal build — it lives in the machine-bound `MQL5\experts.dat` and MT5 drops it on every restart. So it's set the way a user would: a bundled portable AutoIt interpreter (`assets/autoit/AutoIt3_x64.exe` + `set_webrequest.au3`) drives Tools → Options → Expert Advisors and types the URLs in. Takes effect immediately in-session (verified: a probe EA's `WebRequest()` returns HTTP 200 right after). Since MT5 forgets it on restart, `main.py` re-applies the persisted list automatically ~25 s after each terminal (re)start, surviving the periodic auto-reboot.
- **Bare metal:** where `common.ini` *is* the store, falls back to encoding the list into `common.ini` (`scripts/webrequest_allowlist_codec.py`, format reverse-engineered from `terminal64.exe`, verified byte-identical against 18 real broker blobs) + a terminal restart.

Desired list persisted per terminal (`Config/webrequest.json`); first use migrates from the terminal's existing list, preserving manually-configured URLs. GUI applies are serialized host-wide by a named Windows kernel mutex (`Global\mt5_httpapi_webrequest_autoit`; crash-safe via `WAIT_ABANDONED`) since desktop focus is shared across terminals, and each terminal's boot re-apply is staggered by its port — so many terminals per VM can provision URLs without keystroke collisions. `inspect_options.au3`/`selftest.au3` ship as GUI-automation diagnostics (`POST /webrequest/apply?script=`). Tests: `tests/test_webrequest.py`. Live-mode chartctl terminals only.

- `POST /charts/<chart_id>/close` + a `close_chart` loader command — close any chart by id, including charts the loader cannot attribute to a deployment. The loader refuses to close its own chart.

### Fixed

- **WebRequest AutoIt apply drove the wrong terminal window (`options_not_found` / silent wrong-terminal writes).** The scripts matched the MT5 main window by title substring (the login), but `WinList()` also returns hidden windows and cloned terminals of the same account have byte-identical titles — so the apply could target the wrong terminal or fail to activate one at all. The API now resolves its terminal64.exe PID by executable path (WMI) and passes it to the scripts, which match only visible windows owned by that PID and verify the Options dialog belongs to it too, with activation retries. Legacy 3-arg script invocation still works (title fallback).

- **Loader v1.0.2 — duplicate-chart leak across terminal restarts.** The `chartctl:<id>` chart-comment stamp does not survive MT5's profile save/restore cycle, so every terminal restart (including the periodic auto-reboot) left the loader unable to recognize its own chart and it opened a fresh duplicate — accumulating until the terminal's chart cap. Reconcile now first **adopts** an unowned chart already running the deployment's exact expert + symbol + timeframe before opening a new one; comment stamps are verified by read-back (`ChartSetString` is async); a failed attach closes the chart it opened (previously an expert-less chart leaked per attempt) and backs off for 60 s; and errors are tracked per deployment instead of in a single shared slot (one deployment's failure no longer masks another's).

### Notes

- All chartctl handlers are **lock-free** — pure file I/O against the terminal data dir — so they never queue behind the process-wide MT5 SDK lock.
- Additive and **opt-in**: `chartctl.enabled` defaults to `false`. With the block
absent the API behaves exactly as before and no `[StartUp]` section is written.
Enabling it auto-attaches the loader EA to every live terminal, which is a
fleet-wide behaviour change and must never arrive by upgrade.


## [v4.12.2] — 2026-08-20

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.test
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ COPY tests ./tests
# docker-compose.yml.j2 is here because the compose-generation test renders the
# REAL template — a stub would assert nothing about what actually ships.
COPY requirements-api.txt requirements-mcpunifier.txt docker-compose.yml.example docker-compose.yml.j2 run.sh ./
COPY scripts/config_helper.py scripts/start.bat scripts/check_health.py scripts/healthcheck.sh scripts/verify_binaries.py scripts/wickworks-healthcheck.py scripts/recreate-vm.sh ./scripts/
COPY scripts/config_helper.py scripts/start.bat scripts/check_health.py scripts/healthcheck.sh scripts/verify_binaries.py scripts/wickworks-healthcheck.py scripts/recreate-vm.sh scripts/webrequest_allowlist_codec.py ./scripts/
COPY assets/binaries.lock.json ./assets/

ENV PYTHONPATH=/app
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ The old README became a massive wall of API shit, so the details now live in sep
| Copy working curl and Go examples instead of guessing | [Clients and examples](docs/clients-and-examples.md) |
| Operate the bastard: Make targets, ports, remote access, concurrency, and logs | [Operations](docs/operations.md) |
| Split terminals across several Windows VMs or NUMA nodes | [Multi-VM setup](docs/multi-vm-setup.md) |
| Deploy EAs onto charts over HTTP instead of clicking through the Navigator | [Chart Deployments](docs/chart-deployments.md) |

## API at a glance

Expand Down
Binary file added assets/autoit/AutoIt3_x64.exe
Binary file not shown.
49 changes: 49 additions & 0 deletions assets/autoit/EULA.htm
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<title>License</title>
<meta charset="utf-8">
<link href="css/default.css" rel="stylesheet">
</head>
<body>
<h1>Software License</h1>
<p><strong>AutoIt</strong></p>
<p><strong>Author</strong> : Jonathan Bennett and the AutoIt Team<br />
<strong>WWW</strong> : https://www.autoitscript.com/site/autoit/<br />
<strong>Email</strong> : support at autoitscript dot com<br />
________________________________________________________</p>
<p><strong>END-USER LICENSE AGREEMENT FOR THIS SOFTWARE</strong><br /></p>
<p>This End-User License Agreement ("EULA") is a legal agreement between you (either an individual or a single entity) and the mentioned author of this Software for the software product identified above, which includes computer software and may include
associated media, printed materials, and "online" or electronic documentation ("SOFTWARE PRODUCT"). By installing, copying, or otherwise using the SOFTWARE PRODUCT, you agree to be bound by the terms of this EULA. If you do not agree to the terms of
this EULA, do not install or use the SOFTWARE PRODUCT.</p>
<p>&nbsp;</p>
<p><strong>SOFTWARE PRODUCT LICENSE</strong></p>
<p>The SOFTWARE PRODUCT is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The SOFTWARE PRODUCT is licensed, not sold.</p>
<p>The definition of SOFTWARE PRODUCT does not includes any files generated by the SOFTWARE PRODUCT, such as compiled script files in the form of standalone executables.<br /></p>
<p><strong>1. GRANT OF LICENSE</strong></p>
<p>This EULA grants you the following rights:</p>
<p>Installation and Use. You may install and use an unlimited number of copies of the SOFTWARE PRODUCT.</p>
<p>Reproduction and Distribution. You may reproduce and distribute an unlimited number of copies of the SOFTWARE PRODUCT either in whole or in part; each copy should include all copyright and trademark notices, and shall be accompanied by a copy of
this EULA. Copies of the SOFTWARE PRODUCT may be distributed as a standalone product or included with your own product.</p>
<p>Commercial Use. You may use the SOFTWARE PRODUCT for commercial purposes. You may sell for profit and freely distribute scripts and/or compiled scripts that were created with the SOFTWARE PRODUCT.</p>
<p>Reverse engineering. You may not reverse engineer or disassemble the SOFTWARE PRODUCT.</p>
<p><strong>2. COPYRIGHT</strong></p>
<p>All title and copyrights in and to the SOFTWARE PRODUCT (including but not limited to any images, photographs, animations, video, audio, music, text, and "applets" incorporated into the SOFTWARE PRODUCT), the accompanying printed materials, and any
copies of the SOFTWARE PRODUCT are owned by the Author of this Software. The SOFTWARE PRODUCT is protected by copyright laws and international treaty provisions. Therefore, you must treat the SOFTWARE PRODUCT like any other copyrighted material.</p>
<p>&nbsp;</p>
<p><strong>MISCELLANEOUS</strong></p>
<p>If you acquired this product in the United Kingdom, this EULA is governed by the laws of the United Kingdom. If this product was acquired outside the United Kingdom, then local law may apply.</p>
<p>Should you have any questions concerning this EULA, or if you desire to contact the author of this Software for any reason, please contact him/her at the email address mentioned at the top of this EULA.</p>
<p>&nbsp;</p>
<p><strong>LIMITED WARRANTY</strong></p>
<p><strong>1. NO WARRANTIES</strong></p>
<p>The Author of this Software expressly disclaims any warranty for the SOFTWARE PRODUCT. The SOFTWARE PRODUCT and any related documentation is provided "as is" without warranty of any kind, either express or implied, including, without limitation, the
implied warranties or merchantability, fitness for a particular purpose, or non-infringement. The entire risk arising out of use or performance of the SOFTWARE PRODUCT remains with you.</p>
<p><strong>2. NO LIABILITY FOR DAMAGES</strong></p>
<p>In no event shall the author of this Software be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or any other pecuniary loss) arising out of
the use of or inability to use this product, even if the Author of this Software has been advised of the possibility of such damages. Because some states/jurisdictions do not allow the exclusion or limitation of liability for consequential or
incidental damages, the above limitation may not apply to you.</p>
<p>&nbsp;</p>
<p>[END OF LICENSE]</p>
</body>
</html>
37 changes: 37 additions & 0 deletions assets/autoit/NOTICE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
AutoIt — bundled copy, copyright and trademark notices
======================================================

This directory redistributes one unmodified component of AutoIt v3:

AutoIt3_x64.exe version 3.3.18.0
sha256: 5d69a932a077fee044b193c28e84564143f5c7e51079ab48e88fef74ab0b77b7
byte-identical to the copy in AutoIt's official portable archive
(https://www.autoitscript.com/files/autoit3/autoit-v3.zip)

Copyright notice (from the binary's own version resource):

© 1999-2025 Jonathan Bennett & AutoIt Team

Trademark notice (from https://www.autoitscript.com/site/):

AutoIt is a trademark of AutoIt Consulting Ltd.

Author : Jonathan Bennett and the AutoIt Team
WWW : https://www.autoitscript.com/site/autoit/
Email : support at autoitscript dot com

License
-------

AutoIt is redistributed here under its End-User License Agreement, which
permits reproduction and distribution of copies "either in whole or in part"
provided that "each copy should include all copyright and trademark notices,
and shall be accompanied by a copy of this EULA".

The unmodified official EULA accompanies this copy as EULA.htm in this
directory, as published by the AutoIt Team in the AutoIt v3 documentation
(https://www.autoitscript.com/autoit3/docs/license.htm, retrieved 2026-08-27,
matching the release the bundled binary ships in).

The .au3 scripts in this directory are NOT part of AutoIt. They are this
repository's own automation, covered by this repository's license.
Loading