Skip to content

MtApi5: custom symbols support (CustomSymbol*, CustomRates*, CustomTicks*)#307

Open
biohazardxxx wants to merge 7 commits into
vdemydiuk:devfrom
biohazardxxx:feat/mql5-custom-symbols
Open

MtApi5: custom symbols support (CustomSymbol*, CustomRates*, CustomTicks*)#307
biohazardxxx wants to merge 7 commits into
vdemydiuk:devfrom
biohazardxxx:feat/mql5-custom-symbols

Conversation

@biohazardxxx

Copy link
Copy Markdown

Summary

Adds MT5 custom symbols support to the MtApi5 connector. MQL5 can create synthetic instruments and feed them history/ticks natively (CustomSymbolCreate, CustomRatesUpdate, CustomTicksAdd, …) but none of it was reachable from the .NET client. This adds 11 CustomSymbol* / CustomRates* / CustomTicks* functions end-to-end.

C# client (MtApi5) — 11 methods on MtApi5Client:

Command id Method
340 bool CustomSymbolCreate(string symbolName, string symbolPath = "", string symbolOrigin = "")
341 bool CustomSymbolDelete(string symbolName)
342 bool CustomSymbolSetDouble(string symbolName, ENUM_SYMBOL_INFO_DOUBLE propertyId, double value)
343 bool CustomSymbolSetInteger(string symbolName, ENUM_SYMBOL_INFO_INTEGER propertyId, long value)
344 bool CustomSymbolSetString(string symbolName, ENUM_SYMBOL_INFO_STRING propertyId, string value)
345 int CustomRatesDelete(string symbolName, DateTime from, DateTime to)
346 int CustomRatesReplace(string symbolName, DateTime from, DateTime to, MqlRates[] rates)
347 int CustomRatesUpdate(string symbolName, MqlRates[] rates)
348 int CustomTicksAdd(string symbolName, MqlTick[] ticks)
349 int CustomTicksDelete(string symbolName, long fromMsc, long toMsc)
350 int CustomTicksReplace(string symbolName, long fromMsc, long toMsc, MqlTick[] ticks)

MQL5 EA (mq5/MtApi5.mq5) — 11 Execute_Custom* handlers + ADD_EXECUTOR registrations (ids 340–350) + new JsonToMqlRates / JsonToMqlTick / JsonToMqlRatesArray / JsonToMqlTickArray parse helpers. mq5/MtApi5.ex5 recompiled and included.

TestClient — a CustomSymbolTest button that runs the full lifecycle manually (create → set int/double/string properties → CustomRatesUpdate 3 bars → SymbolSelectCustomTicksAdd 2 ticks → CustomTicksDeleteCustomRatesDelete → deselect → CustomSymbolDelete, each result logged).

Implementation notes

  • Command ids 340–350 match exactly between MtApi5/MtProtocol/Mt5CommandType.cs and the mq5 ADD_EXECUTOR registrations.
  • Struct-array wire format mirrors the existing EA→client serializers in reverse: MqlRates[] uses the MqlRatesToJson keys (mt_time, open, high, low, close, tick_volume, spread, real_volume; datetimes as unix seconds), MqlTick[] uses the MqlTickToJson keys plus TimeMsc (Time, TimeMsc, Bid, Ask, Last, Volume, VolumeReal, Flags); flags sent as uint, volume_real as double. The mq5 parse helpers use json.mqh with ZeroMemory on struct elements and follow the house pattern of JsonToMqlTradeRequest (null-check getValue(key) then read via the one-arg getters — the type-checked out-parameter getters in json.mqh don't work for numbers because the parser never sets JSON_NUMBER).
  • MqlTick (C#) gained an additive public long time_msc property (the class previously only had second-resolution MtTime); serialization falls back to MtTime * 1000 when time_msc is 0, and the EA reconciles time/time_msc in both directions.
  • Error semantics: bool commands propagate the raw native bool, int commands propagate the raw native return including -1 (success responses, no CreateErrorResponse mapping) — matching the calendar-PR philosophy (post-d8c459fd).
  • Known limitation (pre-existing transport, not touched here): MT5Connector.cpp convertSystemString truncates inbound command payloads at 1000 wide chars (memcpy bounded by wcsnlen(..., 1000)), even though the EA-side buffer is 5000. Payloads with more than roughly 6 rates or 5–6 ticks per call get truncated at runtime and the EA answers "Failed to get payload". Callers must chunk large arrays: CustomRatesUpdate / CustomTicksAdd are incremental so chunking is semantically safe; a chunked CustomRatesReplace / CustomTicksReplace is not equivalent to one atomic call. The TestClient action keeps its arrays within this cap.
  • Diff is purely additive; branch is based on dev and contains custom-symbols changes only.

Testing

  • MtApi5 builds clean (Release, 0 warnings / 0 errors); MtApi5TestClient builds with 0 errors (only pre-existing nullability-pattern warnings).
  • mq5/MtApi5.mq5 compiles via MetaEditor with "Result: 0 errors"; the recompiled MtApi5.ex5 is included.
  • Not runtime-tested against a live MT5 terminal — verification was compile-level plus a manual wire-format review (payload/response JSON keys, datetime and flags/volume marshaling checked against both sides). The TestClient CustomSymbolTest button is provided for a live round-trip, which I'd recommend before merge.

🤖 Generated with Claude Code

biohazardxxx and others added 6 commits July 4, 2026 00:04
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MqlRates[]/MqlTick[] are serialized into the command payload as JSON
arrays using the same field names the EA emits for the reverse
direction. MqlTick gains a time_msc field (falls back to MtTime*1000
when unset). Int-returning functions propagate the native -1 failure
value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…340-350)

Adds JsonToMqlRates/JsonToMqlTick (and array variants) to parse struct
arrays from the command payload - the reverse of MqlRatesToJson /
MqlTickToJson. Tick time is reconciled between seconds and time_msc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fix JsonToMqlRates/JsonToMqlTick always failing at runtime: they used
json.mqh's checked out-parameter getters (getLong/getDouble/getInt with
out params), which gate on isNumber() -- but JSONNumber constructors
never call setType(JSON_NUMBER), so _type stays JSON_NULL and every
numeric read returned false. All four struct-array commands
(CustomRatesReplace/CustomRatesUpdate/CustomTicksAdd/CustomTicksReplace)
therefore answered 'Failed to parse parameter Rates/Ticks'.

Follow the house pattern of JsonToMqlTradeRequest instead: null-check
jo.getValue(key), then read via the one-arg unchecked getters.
Recompiled MtApi5.ex5 (0 errors, 0 warnings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MqlTickToJson never emitted time_msc and the MtTick protocol DTO had no
field for it, so the C# MqlTick.time_msc introduced with the custom
symbols feature was always 0 on the receive path. Verified live:
millisecond timestamps now populate for forex ticks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@biohazardxxx

Copy link
Copy Markdown
Author

Pushed a completing fix: MqlTickToJson never emitted time_msc and the MtTick protocol DTO had no field for it, so the C# MqlTick.time_msc added in this PR was always 0 on the receive path (CopyTicks/SymbolInfoTick). Now round-trips in both directions — verified live: millisecond timestamps populate for forex ticks (e.g. 1783588818147/…360/…372 within one second). Recompiled ex5 included.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant