Services
EA AccountStatusUpdaterAccountStatusUpdater EA
EA MQL5 gắn trên một chart của mỗi terminal MT5: gửi trạng thái tài khoản (balance, equity, lệnh mở, lịch sử, EA đang chạy) lên fxconsole qua HTTP hoặc TLS-TCP, stream lịch sử deal khi có yêu cầu báo cáo và vẽ panel thông tin. Trang này giúp bạn cấu hình input, chọn transport và đọc panel. An MQL5 EA attached to one chart per MT5 terminal: it sends account status (balance, equity, open positions, history, running EAs) to fxconsole over HTTP or TLS-TCP, streams deal history when a report is requested, and draws an info panel. This page helps you configure inputs, pick a transport and read the panel.
Tổng quanOverview
- Mỗi terminal chạy một AccountStatusUpdater; nó đọc toàn bộ tài khoản đang đăng nhập (không lọc theo magic) và liệt kê EA trên mọi chart của terminal.
- Đích HTTP:
POST /api/account-statuscủa fxconsole (Bearer). Đích TCP: AccountStatusBroker cổng 9602. InpAccountSecretKeygắn tài khoản với user trên fxconsole (trùngmt5_secret_keycủa user).- Timer 1s (
EventSetTimer(1)) chạy phần chậm (HTTP, HB, panel);OnTickchỉ đẩy TCP khi stream bật.
- Each terminal runs one AccountStatusUpdater; it reads the whole logged-in account (no magic filter) and lists the EAs on every chart of the terminal.
- HTTP target: fxconsole
POST /api/account-status(Bearer). TCP target: AccountStatusBroker on port 9602. InpAccountSecretKeyties the account to a fxconsole user (equals the user'smt5_secret_key).- A 1s timer (
EventSetTimer(1)) drives the slow paths (HTTP, HB, panel);OnTickonly pushes TCP while streaming.
Lịch sử phiên bản gần đâyRecent version history
| Version | NgàyDate | Thay đổiChange |
|---|---|---|
1.16 | 2026-09-24 | Comment của lệnh đã đóng trong today_history giờ lấy từ deal mở lệnh (vd Init R44 P |init_manual), giống tab History của MT5. Trước đây EA đọc comment của deal đóng: trống khi EA tự đóng bằng PositionClose(), hoặc [tp …] / [sl …] / [so …] khi sàn đóng, nên trang tài khoản trên fxconsole hiện "—". Comment của deal đóng chuyển sang field mới close_comment, và được dùng thay khi comment mở lệnh trống. Lệnh mở từ trước hôm nay được tra bằng HistorySelectByPosition(). Số liệu không đổi: mỗi dòng vẫn lấy profit / swap / commission / volume / giá / giờ từ cùng deal đóng, cùng số dòng, cùng tổng.The comment of closed trades in today_history now comes from the opening deal (e.g. Init R44 P |init_manual), as in MT5's History tab. The EA used to read the closing deal's comment: empty when an EA closes via PositionClose(), or [tp …] / [sl …] / [so …] when the broker closes, so the fxconsole account page showed "—". The closing comment moves to a new close_comment field and is used when the opening comment is empty. Positions opened before today are looked up with HistorySelectByPosition(). Numbers unchanged: every row still takes profit / swap / commission / volume / price / time from the same closing deal — same rows, same totals. |
1.14 | 2026-06-14 | Nhịp HTTP đo bằng GetTickCount64() thay TimeCurrent(). TimeCurrent() là giờ quote cuối, đứng yên khi đóng thị trường, nên bản cũ vẫn gửi TCP heartbeat nhưng không gửi HTTP cuối tuần → báo cáo không được xử lý. Áp dụng cho HTTP và HYBRID.HTTP cadence measured with GetTickCount64() instead of TimeCurrent(). TimeCurrent() is the last quote time and freezes while the market is closed, so older builds kept TCP heartbeats but sent no HTTP on weekends → reports never ingested. Applies to HTTP and HYBRID. |
1.13 | 2026-06-13 | Stream báo cáo không còn theo nhịp heartbeat: sau mỗi chunk thành công (và khi phát hiện yêu cầu mới qua HTTP) bật g_forceHttpNow để chunk kế tiếp đi ở tick timer 1s kế tiếp. Báo cáo 3 tháng (~13 chunk tuần) xong trong ~13s thay vì ~6.5 phút.Report streaming no longer heartbeat-paced: after each successful chunk (and on discovering a new request over HTTP) g_forceHttpNow is re-armed so the next chunk goes out on the next 1s timer tick. A 3-month report (~13 weekly chunks) finishes in ~13s instead of ~6.5 min. |
1.12 | 2026-06-07 | Panel thêm dòng Socket: OK/DOWN | Last: <kind> Ns ago.Panel gains the Socket: OK/DOWN | Last: <kind> Ns ago line. |
1.11 | 2026-06-06 | Nhận hb_interval_secs từ frame stream_off (clamp 5–120s) để admin chỉnh nhịp HB từ /admin/settings.Takes hb_interval_secs from the stream_off frame (clamped 5–120s) so admins can retune HB cadence from /admin/settings. |
1.10 | 2026-06-05 | On-demand stream: composite_id, HELLO v2, stream_on/stream_off, frame hb_min, handler report_request. Broker cũ từ chối HELLO này — phải deploy broker và EA cùng lúc.On-demand stream: composite_id, HELLO v2, stream_on/stream_off, hb_min frame, report_request handler. Older brokers reject this HELLO — deploy brokers and EAs together. |
Không có bản 1.15 đang dùng: bản đó (dropdown enum cho InpStatus) đã bị revert (commit f413166) nhưng file .ex5 của nó vẫn còn trên đĩa, nên bản kế tiếp nhảy thẳng lên 1.16. InpStatus vẫn là enum ENUM_NODE_STATUS như dưới đây.
There is no 1.15 in use: that build (enum dropdown for InpStatus) was reverted (commit f413166) but its .ex5 is still on disk, so the next release jumps straight to 1.16. InpStatus is still the ENUM_NODE_STATUS enum described below.
InputInputs
Giá trị mặc định lấy từ source 1.16 (không đổi so với 1.14). Cột ".set vận hành" là giá trị trong AccountStatusUpdater-running.set / -suspend.set (chỉ các input không bí mật).
Defaults come from the 1.16 source (unchanged since 1.14). The "operations .set" column shows the values in AccountStatusUpdater-running.set / -suspend.set (non-secret inputs only).
=== API ===
| Input | Mặc địnhDefault | .set vận hànhOperations .set | Ý nghĩaMeaning |
|---|---|---|---|
InpApiUrl | http://127.0.0.1:8080/api/account-status | https://fxconsole.gamelau.uk/api/account-status | Endpoint HTTP. CR/LF/TAB, khoảng trắng đầu cuối và / cuối được tự bỏ (có cảnh báo trong log).HTTP endpoint. CR/LF/TAB, surrounding whitespace and a trailing / are stripped automatically (with a log warning). |
InpApiBearer | placeholder change_me…placeholder change_me… | <secret> | Header Authorization: Bearer; phải bằng setting ea_api_bearer trên fxconsole.Authorization: Bearer header; must equal the fxconsole ea_api_bearer setting. |
InpAccountSecretKey | placeholderplaceholder | <secret> | Field secret_key trong payload; trùng mt5_secret_key của user sở hữu tài khoản.secret_key field in the payload; equals the owning user's mt5_secret_key. |
InpPostIntervalSec | 5 | 5 | Chu kỳ POST ở chế độ HTTP (giây). < 1 → INIT_FAILED.POST cadence in HTTP mode (seconds). < 1 → INIT_FAILED. |
InpPanelUpdateSec | 1 | 1 | Chu kỳ vẽ panel. < 1 → INIT_FAILED.Panel refresh cadence. < 1 → INIT_FAILED. |
InpWebTimeoutMs | 5000 | 5000 | Timeout WebRequest (ms); WebRequest chặn đồng bộ tới mức này.WebRequest timeout (ms); WebRequest blocks synchronously up to this. |
InpLogVerbose | false | false | Log chi tiết (mỗi lần cập nhật panel, push TCP, frame broker).Verbose log (every panel update, TCP push, broker frame). |
=== Node identity ===
| Input | Mặc địnhDefault | Ý nghĩaMeaning |
|---|---|---|
InpStatus | STATUS_RUNNING | Enum ENUM_NODE_STATUS: STATUS_RUNNING = 0 → "running", STATUS_SUSPEND = 1 → "suspend". Gửi trong field status của cả payload HTTP/TCP lẫn frame hb. Xem running / suspend.Enum ENUM_NODE_STATUS: STATUS_RUNNING = 0 → "running", STATUS_SUSPEND = 1 → "suspend". Sent in the status field of the HTTP/TCP payload and the hb frame. See running / suspend. |
InpNode | trốngempty | Tên node (server chấp nhận ≤ 64 ký tự). Trống → tên máy Windows qua kernel32.dll GetComputerNameW (cần bật Allow DLL imports; bị chặn thì gửi rỗng). Cột Node (VPS) của fxconsole không hiện nguyên chuỗi này: server tìm VPS theo login (login do agent VPS báo, rồi bản ghi deploy, cuối cùng mới so tên) và hiện tên VPS hiện tại, nên đổi tên VPS trên web vẫn hiện đúng dù tên máy Windows giữ tên cũ.Node name (server accepts ≤ 64 chars). Empty → Windows computer name via kernel32.dll GetComputerNameW (needs Allow DLL imports; if denied an empty string is sent). fxconsole's Node (VPS) column does not show this string as-is: the server finds the VPS by login (logins reported by the VPS agent, then the deployment row, and only then by name) and shows the VPS's current name, so renaming a VPS on the web shows up even though the Windows machine keeps its old name. |
=== Transport ===
| Input | Mặc địnhDefault | .set vận hànhOperations .set | Ý nghĩaMeaning |
|---|---|---|---|
InpTransport | TRANSPORT_HTTP (0) | 2 (HYBRID) | TRANSPORT_HTTP = 0, TRANSPORT_TCP_SOCKET = 1, TRANSPORT_HYBRID = 2. Xem transport.TRANSPORT_HTTP = 0, TRANSPORT_TCP_SOCKET = 1, TRANSPORT_HYBRID = 2. See transports. |
InpBrokerHost | fxconsole.gamelau.uk | fxconsole.gamelau.uk | Host AccountStatusBroker; gửi làm SNI, phải trùng CERT_HOST của broker; phải có trong Allow WebRequest.AccountStatusBroker host; sent as SNI, must match the broker's CERT_HOST; must be in Allow WebRequest. |
InpBrokerPort | 9602 | 9602 | Cổng TLS-TCPTLS-TCP port |
InpBrokerSecret | placeholderplaceholder | <secret> | Phải bằng BROKER_SECRET của broker (fxconsole hiển thị read-only ở "Broker stream secret").Must equal the broker's BROKER_SECRET (fxconsole shows it read-only as "Broker stream secret"). |
InpBrokerName | trốngempty | trốngempty | Tên broker trong HELLO. Trống → ACCOUNT_COMPANY; cũng trống → default.Broker name in HELLO. Empty → ACCOUNT_COMPANY; also empty → default. |
InpHybridHttpIntervalSec | 30 | 30 | Chỉ HYBRID: nhịp HTTP control plane. < 5 → cảnh báo spam; khuyến nghị 15–60.HYBRID only: HTTP control-plane cadence. < 5 → spam warning; 15–60 recommended. |
InpMinPushIntervalMs | 100 | 500 | TCP/HYBRID: khoảng tối thiểu giữa 2 lần push socket (0 = mọi tick có thay đổi).TCP/HYBRID: minimum gap between socket pushes (0 = every changed tick). |
InpHeartbeatSec | 15 | 15 | TCP/HYBRID khi stream bật: ép gửi frame đầy đủ mỗi N giây kể cả khi payload không đổi, để dashboard không báo "lost" lúc thị trường yên (0 = tắt).TCP/HYBRID while streaming: forces a full frame every N seconds even if unchanged, so dashboards don't flip to "lost" in quiet markets (0 = off). |
=== Info Panel ===
| Input | Mặc địnhDefault | Ý nghĩaMeaning |
|---|---|---|
InpPanelEnable | true | Bật panelShow the panel |
InpPanelX / InpPanelY | 550 / 25 | Toạ độ góc trên trái (px)Top-left position (px) |
InpPanelWidth / InpPanelHeight | 460 / 252 | Kích thước; độ rộng cũng quyết định độ dài cắt dòng APISize; the width also sets where the API line is truncated |
InpPanelBgColor / InpPanelTextColor | clrBlack / clrWhite | Nền / chữBackground / text |
InpPanelFontSize | 10 | Cỡ chữ (font Consolas)Font size (Consolas) |
InpPanelColorBuy / InpPanelColorSell | clrDodgerBlue / clrOrangeRed | Dòng Buy / Sell; Sell cũng dùng cho lỗi và profit âmBuy / Sell lines; Sell also marks errors and negative profit |
InpPanelColorNetLot | clrYellow | Net lot khi cân bằngNet lot when balanced |
InpPanelColorProfit | clrLime | Profit dương, API OK, Socket OKPositive profit, API OK, Socket OK |
InpPanelColorBalance / InpPanelColorMargin | clrWhite / clrSilver | Balance, equity / marginBalance, equity / margin |
Transport: HTTP / TCP_SOCKET / HYBRID
TRANSPORT_HTTP | TRANSPORT_TCP_SOCKET | TRANSPORT_HYBRID |
|
|---|---|---|---|
| Đường dữ liệuData path | POST mỗi InpPostIntervalSecPOST every InpPostIntervalSec | TLS tới broker: frame đầy đủ khi stream bật, hb khi tắtTLS to the broker: full frames while streaming, hb otherwise | Như TCP_SOCKET + POST HTTP mỗi InpHybridHttpIntervalSecLike TCP_SOCKET + an HTTP POST every InpHybridHttpIntervalSec |
| Độ trễ dashboardDashboard latency | Theo chu kỳ POSTPOST cadence | Gần tức thời khi có người xemNear-instant while someone watches | Gần tức thời khi có người xemNear-instant while someone watches |
| Cảnh báo sụt equityEquity-drop alerts | ✓ | ✗ | ✓ (qua HTTP)(via HTTP) |
lot_history | ✓ | ✗ | ✓ (qua HTTP)(via HTTP) |
| Generate reportReport generation | ✓ (phát hiện qua response, tối đa 1 chu kỳ POST)✓ (found via the response, up to one POST cycle) | ✗ | ✓ (report_request đẩy ngay)✓ (report_request pushed instantly) |
| Ghi chúNotes | Đầy đủ tính năng, mặc địnhFull features, the default | Nhanh nhất; OnInit in cảnh báo mất tính năngFastest; OnInit warns about the missing features | Khuyến nghị cho vận hành (.set đang dùng)Recommended for operations (used by the .set files) |
- OnTick (chỉ TCP/HYBRID, chỉ khi stream bật): dựng payload, bỏ qua nếu giống hệt lần gửi thành công trước, bỏ qua nếu chưa đủ
InpMinPushIntervalMs, rồi push socket. - OnTimer mỗi 1s (
TryUpdate): đọc frame broker → nếu stream bật thì push (ép theoInpHeartbeatSec), nếu tắt thì gửihbtheog_hbIntervalMs→ xét nhịp HTTP bằngGetTickCount64()(HTTP:InpPostIntervalSec; HYBRID:InpHybridHttpIntervalSechoặc cờg_forceHttpNow) → cập nhật panel. - OnInit: kiểm tra input, tính
composite_id(in ra log), bật timer 1s, gọiTryUpdate(true)(ép gửi ngay để broker thấy EA).
- OnTick (TCP/HYBRID only, only while streaming): builds the payload, skips it if identical to the last successful send, skips if
InpMinPushIntervalMshasn't elapsed, then pushes to the socket. - OnTimer every 1s (
TryUpdate): drains broker frames → if streaming, pushes (forced byInpHeartbeatSec); otherwise sendshbeveryg_hbIntervalMs→ checks the HTTP cadence withGetTickCount64()(HTTP:InpPostIntervalSec; HYBRID:InpHybridHttpIntervalSecor theg_forceHttpNowflag) → refreshes the panel. - OnInit: validates inputs, computes
composite_id(printed to the log), starts the 1s timer, callsTryUpdate(true)(forced send so the broker sees the EA right away).
Payload HTTPHTTP payload
POST InpApiUrl, header Content-Type: application/json; charset=utf-8, Accept: application/json, Authorization: Bearer <secret>. Cùng JSON này cũng là frame "đầy đủ" gửi qua socket. Chuỗi được escape \ " và mọi ký tự < 0x20 bị thay bằng dấu cách (một số broker trả tên có ký tự điều khiển, proxy nghiêm ngặt trả 400).
POST InpApiUrl with headers Content-Type: application/json; charset=utf-8, Accept: application/json, Authorization: Bearer <secret>. The same JSON is the "full" frame sent over the socket. Strings are escaped for \ and ", and every character < 0x20 becomes a space (some brokers return names with control characters, which strict proxies reject with 400).
| Field | NguồnSource |
|---|---|
account_id, account_name, account_server, account_company | ACCOUNT_LOGIN, ACCOUNT_NAME, ACCOUNT_SERVER, ACCOUNT_COMPANY |
secret_key, status, node | InpAccountSecretKey, InpStatus, InpNode / tên máy/ computer name |
balance, equity, margin, profit | ACCOUNT_*; profit là floating (ACCOUNT_PROFIT)ACCOUNT_*; profit is floating (ACCOUNT_PROFIT) |
profit_today, profit_week, profit_month, profit_last_month | Tổng profit + swap + commission của deal đóng (DEAL_ENTRY_OUT/OUT_BY, có symbol) theo giờ broker: từ 00:00, từ thứ Hai 00:00 (ISO), từ ngày 1, cả tháng trướcSum of profit + swap + commission of closing deals (DEAL_ENTRY_OUT/OUT_BY, with a symbol) on broker time: since 00:00, since Monday 00:00 (ISO), since the 1st, the whole previous month |
win_profit_* / loss_profit_* (today, month, last_month)(today, month, last_month) | Tổng net của lệnh thắng / trị tuyệt đối của lệnh thua; web tính Win % = win / (win + loss)Net sum of winning trades / absolute sum of losing trades; the web shows Win % = win / (win + loss) |
buy_orders, buy_lot_total, sell_orders, sell_lot_total, lot_diff | Đếm position mở; lot_diff = buy − sellOpen position counts; lot_diff = buy − sell |
broker_time | TimeCurrent() (giờ broker mã hoá unix, không phải UTC)TimeCurrent() (broker wall clock encoded as unix, not UTC) |
positions[] | ticket, symbol, type, volume, price_open, price_current, sl, tp, profit, swap, commission (luôn 0)(always 0), open_time, comment, magic |
experts[] | Mỗi chart có EA trong terminal này: chart_id, symbol, period, expert, ask, bidEvery chart with an EA in this terminal: chart_id, symbol, period, expert, ask, bid |
today_history[] | Deal đóng hôm nay: ticket, symbol, type (hướng position gốc), volume, price, profit, swap, commission, time, comment (comment của deal mở lệnh, từ 1.16), close_comment (comment của deal đóng, từ 1.16). Cache theo (ngày, số deal) để không duyệt lịch sử mỗi tick.Deals closed today: ticket, symbol, type (original position side), volume, price, profit, swap, commission, time, comment (the opening deal's comment, since 1.16), close_comment (the closing deal's comment, since 1.16). Cached by (day, deal count) so history isn't walked every tick. |
report_deals[], report_balance_now, report_chunk_is_first, report_chunk_is_last | Chỉ khi đang có yêu cầu báo cáo (xem dưới)Only while a report request is pending (see below) |
Kết quả HTTP hiện ở dòng API của panel: -1 → {"error":"web_request","get_last_error":N} và log nhắc thêm URL vào Allow WebRequest; response HTML → {"http":N,"error":"non_json_response"}; non-2xx → log mã + body. Server trả 401 (sai Bearer), 422 (sai dữ liệu).
The HTTP result appears on the panel's API line: -1 → {"error":"web_request","get_last_error":N} plus a log hint to add the URL to Allow WebRequest; an HTML response → {"http":N,"error":"non_json_response"}; non-2xx → code + body logged. The server returns 401 (wrong Bearer), 422 (invalid data).
Stream báo cáo qua pending_reportReport streaming via pending_report
- User bấm Generate trên
/dashboard/accounts/{id}. Laravel lưu yêu cầu; responsePOST /api/account-statuskế tiếp chứa"pending_report":{…,"from_ts":N,…,"to_ts":N}. Ở HYBRID, broker còn đẩyreport_requestngay.A user clicks Generate on/dashboard/accounts/{id}. Laravel stores the request; the nextPOST /api/account-statusresponse carries"pending_report":{…,"from_ts":N,…,"to_ts":N}. In HYBRID the broker also pushesreport_requestimmediately. - EA thấy khoảng
from/tomới → con trỏ =from, bậtg_forceHttpNow. Cùng khoảng (đang stream dở) → giữ con trỏ. Response không cònpending_report→ xoá trạng thái.The EA sees a newfrom/torange → cursor =from, setsg_forceHttpNow. Same range (mid-stream) → cursor kept. Nopending_reportin the response → state cleared. - Mỗi POST mang 1 chunk 7 ngày
[cursor, min(cursor + 7d, to)):report_dealsgồm deal đóng lệnh (typeđảo từ loại deal thành hướng position gốc) và thao tác balance (type = "balance", symbol rỗng, comment của broker như "Deposit").is_firstkhi chunk bắt đầu tạifrom;is_lastkhi chạmto.Each POST carries one 7-day chunk[cursor, min(cursor + 7d, to)):report_dealsholds closing deals (typeflipped from the deal type to the original position side) and balance operations (type = "balance", empty symbol, broker comment such as "Deposit").is_firstwhen the chunk starts atfrom;is_lastwhen it reachesto. - POST thành công: chunk cuối → xoá trạng thái; chưa cuối → con trỏ = cuối chunk và bật lại
g_forceHttpNowđể chunk sau đi ở tick 1s kế tiếp (1.13).Successful POST: last chunk → state cleared; otherwise cursor = chunk end andg_forceHttpNowis re-armed so the next chunk leaves on the next 1s tick (1.13). - Server:
is_firstxoá deal cũ của tài khoản,is_lasttính tổng kết và xoá yêu cầu. Thiếu hai cờ → coi như true/true (EA cũ gửi 1 lần). Giới hạn:report_deals≤ 20000,today_history≤ 2000 phần tử. Lỗi ingest không làm hỏng lần push trạng thái, được log vào kênhdeployment.Server:is_firsttruncates the account's old deals,is_lastcomputes the summary and clears the request. Missing flags → treated as true/true (older single-shot EAs). Limits:report_deals≤ 20000,today_history≤ 2000 items. Ingest errors don't fail the status push and are logged to thedeploymentchannel.
Broker không trả lời theo từng frame nên socket không nhận được pending_report; chunk luôn đi qua HTTP. Vì vậy TRANSPORT_TCP_SOCKET không tạo được báo cáo dù có nhận report_request.
The broker sends no per-frame reply, so the socket never receives pending_report; chunks always go over HTTP. That is why TRANSPORT_TCP_SOCKET cannot produce reports even though it receives report_request.
Luồng TLS với AccountStatusBrokerTLS flow with AccountStatusBroker
Kết nối và HELLOConnect and HELLO
SocketCreate()→SocketConnect(InpBrokerHost, InpBrokerPort, 5000)→SocketTlsHandshake(host). Lỗi ở bước nào thì đóng handle, in lỗi và trả false.SocketCreate()→SocketConnect(InpBrokerHost, InpBrokerPort, 5000)→SocketTlsHandshake(host). Any failure closes the handle, prints the error and returns false.- Gửi
HELLO|statuspub|v2|<composite_id>|<InpBrokerSecret>|<broker_name>.composite_id=slug(ACCOUNT_COMPANY)::slug(ACCOUNT_SERVER)::login(chữ thường, ký tự khác[a-z0-9]gộp thành một-, bỏ-đầu cuối; rỗng →unknown-company/unknown-server) — cùng quy tắc với regex của broker.SendHELLO|statuspub|v2|<composite_id>|<InpBrokerSecret>|<broker_name>.composite_id=slug(ACCOUNT_COMPANY)::slug(ACCOUNT_SERVER)::login(lowercase, any run of non-[a-z0-9]collapsed to one-, leading/trailing-trimmed; empty →unknown-company/unknown-server) — the same rule as the broker's regex. - Socket mới = trạng thái mới:
g_streamOn = false, xoá buffer nhận. Broker trảstream_offhoặcstream_onngay sau HELLO.Fresh socket = fresh state:g_streamOn = false, receive buffer cleared. The broker answers withstream_offorstream_onright after HELLO.
Kết nối lại là kiểu lười: không có vòng reconnect riêng. Handle = INVALID_HANDLE thì lần gửi kế tiếp (hb trong timer, hoặc push trong OnTick khi stream bật) tự gọi BrokerConnect(). Gửi thiếu byte (SocketTlsSend trả khác độ dài) → đóng handle để lần sau kết nối lại.
Reconnect is lazy: there is no dedicated reconnect loop. When the handle is INVALID_HANDLE, the next send (hb from the timer, or a push from OnTick while streaming) calls BrokerConnect() itself. A short send (SocketTlsSend returns a different length) closes the handle so the next attempt reconnects.
Frame nhận từ brokerFrames received from the broker
Mỗi giây (BrokerDrainFrames): nếu SocketIsReadable thì SocketTlsReadAvailable tối đa 4096 byte, tách theo \n, đọc field t bằng tìm chuỗi đơn giản.
Every second (BrokerDrainFrames): if SocketIsReadable, SocketTlsReadAvailable reads up to 4096 bytes, splits on \n and reads the t field with a simple string search.
| Frame | EA làm gìWhat the EA does |
|---|---|
{"t":"stream_on","cadence_ms":N} | g_streamOn = true, xoá payload đã gửi để lần sau gửi frame đầy đủ. cadence_ms hợp lệ 0–60000 (khác → 900) nhưng chỉ để tham khảo: EA vẫn giới hạn bằng InpMinPushIntervalMs.g_streamOn = true, clears the last sent payload so a full frame goes next. cadence_ms accepted 0–60000 (else 900) but informational only: the EA still rate-limits with InpMinPushIntervalMs. |
{"t":"stream_off","hb_interval_secs":N} | g_streamOn = false; N trong [5, 120] → g_hbIntervalMs = N × 1000 (ngoài miền thì giữ nguyên; mặc định lúc khởi động 15s).g_streamOn = false; N within [5, 120] → g_hbIntervalMs = N × 1000 (out of range is ignored; cold-start default 15s). |
{"t":"report_request","from_ts":N,"to_ts":M} | Khoảng mới → đặt from/to, con trỏ = from; luôn bật g_forceHttpNow để POST HTTP ở tick kế tiếp (chỉ HYBRID thực sự gửi).New range → sets from/to, cursor = from; always sets g_forceHttpNow for an HTTP POST on the next tick (only HYBRID actually sends it). |
{"t":"hello_err","reason":…} | Log lý do và đóng socket — nhưng AccountStatusBroker không bao giờ gửi frame này (xem hạn chế).Logs the reason and closes the socket — but AccountStatusBroker never sends this frame (see limitations). |
{"t":"hello_ok"} | Không làm gì (broker hiện cũng không gửi).No-op (the broker doesn't send it today either). |
Frame hb gửi khi stream tắt: {"t":"hb","composite_id","secret_key","status","equity","balance","buy_orders","buy_lot_total","sell_orders","sell_lot_total","ts"} (ts = TimeCurrent()). Không có positions / experts / today_history — đó là phần nặng mà gate on-demand tiết kiệm. Khi stream tắt, OnTick thoát ngay.
The hb frame sent while the stream is off: {"t":"hb","composite_id","secret_key","status","equity","balance","buy_orders","buy_lot_total","sell_orders","sell_lot_total","ts"} (ts = TimeCurrent()). No positions / experts / today_history — those are the heavy parts the on-demand gate saves. While the stream is off, OnTick returns immediately.
sequenceDiagram
participant E as AccountStatusUpdater (HYBRID)
participant B as AccountStatusBroker 9602
participant L as fxconsole /api/account-status
E->>B: SocketConnect 5s + TLS + HELLO statuspub v2
B-->>E: stream_off hb_interval_secs=15
loop Mỗi 15s (stream tắt)
E->>B: t=hb
end
loop Mỗi 30s (InpHybridHttpIntervalSec)
E->>L: POST payload đầy đủ
L-->>E: ok (+ pending_report nếu có)
end
B-->>E: stream_on cadence_ms (có người mở dashboard)
loop Mỗi tick, cách nhau ≥ InpMinPushIntervalMs
E->>B: payload đầy đủ nếu có thay đổi
end
B-->>E: report_request from_ts to_ts
E->>L: POST + report_deals chunk 7 ngày
L-->>E: pending_report
E->>L: chunk tiếp theo ở tick 1s kế tiếp ... is_last
B-->>E: stream_off (người xem cuối rời đi)sequenceDiagram
participant E as AccountStatusUpdater (HYBRID)
participant B as AccountStatusBroker 9602
participant L as fxconsole /api/account-status
E->>B: SocketConnect 5s + TLS + HELLO statuspub v2
B-->>E: stream_off hb_interval_secs=15
loop Every 15s (stream off)
E->>B: t=hb
end
loop Every 30s (InpHybridHttpIntervalSec)
E->>L: POST full payload
L-->>E: ok (+ pending_report if any)
end
B-->>E: stream_on cadence_ms (someone opened a dashboard)
loop Every tick, at least InpMinPushIntervalMs apart
E->>B: full payload if changed
end
B-->>E: report_request from_ts to_ts
E->>L: POST + report_deals 7-day chunk
L-->>E: pending_report
E->>L: next chunk on the next 1s tick ... is_last
B-->>E: stream_off (last viewer left)Info panelInfo panel
| DòngRow | Nội dungContents |
|---|---|
| 0 | ACCOUNT STATUS |
| 1 | ID (trái) · Name (giữa) · Server (phải)ID (left) · Name (centre) · Server (right) |
| 2 | Balance · Equity | Margin · Profit today | Profit (màu profit nếu ≥ 0, màu sell nếu âm)Balance · Equity | Margin · Profit today | Profit (profit colour if ≥ 0, sell colour if negative) |
| 3 | Buy N / x lot · Sell N / x lot · Net x lot (màu buy nếu > 0.001, màu sell nếu < −0.001, vàng nếu cân bằng)Buy N / x lot · Sell N / x lot · Net x lot (buy colour if > 0.001, sell colour if < −0.001, yellow when balanced) |
| 4 | Kết quả gửi gần nhất (body JSON một dòng, bị cắt theo độ rộng; {"transport":"tcp","ok":true} sau push socket; {"info":"starting"} lúc khởi động). Xanh lime nếu OK, đỏ nếu lỗi. Frame hb không cập nhật dòng này.Last send result (one-line JSON body, truncated to the width; {"transport":"tcp","ok":true} after a socket push; {"info":"starting"} at startup). Lime when OK, red on error. hb frames don't update this row. |
| 5 | Socket: OK | Last: <kind> Ns ago, Socket: OK | Last: -- (chưa gửi được lần nào) hoặc Socket: DOWN. Có từ 1.12 (monitor/README.html chỉ mô tả dòng 0–4).Socket: OK | Last: <kind> Ns ago, Socket: OK | Last: -- (nothing sent yet) or Socket: DOWN. Added in 1.12 (monitor/README.html only describes rows 0–4). |
Last: | NghĩaMeaning |
|---|---|
tcp:stream | Payload đầy đủ qua socket TLS (stream bật — đang có người xem)Full payload over the TLS socket (stream on — someone is watching) |
tcp:hb_min | Frame keep-alive nhỏ qua socket (stream tắt)Small keep-alive frame over the socket (stream off) |
http:status | Payload đầy đủ qua HTTPS (HTTP, hoặc control plane của HYBRID)Full payload over HTTPS (HTTP mode, or the HYBRID control plane) |
Socket: OK không có nghĩa là broker đã chấp nhậnSocket: OK does not mean the broker accepted you
OK chỉ nghĩa là EA đang giữ một handle hợp lệ (g_brokerSock != INVALID_HANDLE). HELLO bị từ chối hoặc đường truyền rớt im lặng vẫn có thể hiện OK cho tới khi một lần gửi thất bại. Ở chế độ TRANSPORT_HTTP dòng này luôn là Socket: DOWN hoặc Last: http:status.
OK only means the EA holds a valid handle (g_brokerSock != INVALID_HANDLE). A rejected HELLO or a silently dropped path can still show OK until a send fails. In TRANSPORT_HTTP mode this row always reads Socket: DOWN or Last: http:status.
Hạn chế đã biếtKnown limitations
Phát hiện khi review code 1.14 và AccountStatusBroker/broker.js. Chưa sửa, vẫn còn ở 1.16.
Found while reviewing the 1.14 code and AccountStatusBroker/broker.js. Not fixed yet; still present in 1.16.
| # | Hạn chếLimitation | Hậu quảEffect |
|---|---|---|
| 1 | Không có backoff khi kết nối lại, và SocketConnect chặn tới 5s. Lần thử thất bại không cập nhật g_lastHbMs / g_lastTcpPushMs, nên EA thử lại ở mỗi tick timer 1s (stream tắt) hoặc mỗi tick giá qua được giới hạn InpMinPushIntervalMs (stream đang bật lúc broker chết).No reconnect backoff, and SocketConnect blocks for up to 5s. A failed attempt doesn't update g_lastHbMs / g_lastTcpPushMs, so the EA retries on every 1s timer tick (stream off) or on every price tick past InpMinPushIntervalMs (stream was on when the broker died). | Broker không tới được → thread của EA gần như luôn kẹt trong connect: panel đứng, POST HTTP của HYBRID bị trễ.Broker unreachable → the EA's thread sits in connect almost continuously: the panel freezes and HYBRID HTTP POSTs are delayed. |
| 2 | Broker trả dòng thô ERR|… (không phải JSON) khi HELLO sai, nên handler hello_err của EA không bao giờ chạy; dòng này bị coi là frame lạ (chỉ log khi InpLogVerbose).The broker replies with a raw ERR|… line (not JSON) on a bad HELLO, so the EA's hello_err handler never fires; the line is treated as an unknown frame (logged only with InpLogVerbose). | Sai InpBrokerSecret hoặc EA quá cũ không hiện lý do trên panel; kết hợp với #1, EA có thể lặp connect → HELLO → bị đóng liên tục.A wrong InpBrokerSecret or an outdated EA shows no reason on the panel; combined with #1 the EA can loop connect → HELLO → closed indefinitely. |
| 3 | Không có ping tầng ứng dụng: không hello_ok, không ping/pong; broker chỉ bật TCP keep-alive 15s và EA không đọc phản hồi cho frame nó gửi.No application-level ping: no hello_ok, no ping/pong; the broker only enables 15s TCP keep-alive and the EA reads no reply to its frames. | Socket nửa mở chỉ bị phát hiện khi một lần gửi thất bại.Half-open sockets are only detected when a send fails. |
| 4 | Một subscriber bật stream_on cho mọi EA: gate của broker là toàn cục (tổng subscriber per-key + admin).One subscriber turns stream_on for every EA: the broker's gate is global (total per-key + admin subscribers). | Một user mở dashboard tài khoản của mình là cả fleet chuyển sang push đầy đủ mỗi tick; chi phí CPU/băng thông tăng theo số EA.One user opening their own dashboard switches the whole fleet to full per-tick pushes; CPU/bandwidth cost grows with the number of EAs. |
| 5 | OnDeinit không xoá label ASU_WS (dòng Socket): danh sách ObjectDelete thiếu g_objWs.OnDeinit leaves the ASU_WS label (Socket row): the ObjectDelete list omits g_objWs. | Gỡ EA vẫn còn chữ Socket: … trên chart; xoá tay trong Object List.After removing the EA a Socket: … text stays on the chart; delete it from the Object List. |
- Comment 1.10 ghi
composite_idđược gửi trong "HYBRID HTTP HB payload", nhưngToJson()không có fieldcomposite_id. Đoạn code đồng bộmt5_accounts.composite_idtrongAccountStatusApiControllervì thế không được EA này kích hoạt; tài khoản không cócomposite_idchỉ nhận yêu cầu báo cáo quapending_reportcủa HTTP. - HTTP non-2xx: EA
Printnguyên payload (cósecret_key) vào log Experts. Đừng chia sẻ log thô.
- The 1.10 comment says
composite_idships in the "HYBRID HTTP HB payload", butToJson()has nocomposite_idfield. Themt5_accounts.composite_idsync inAccountStatusApiControlleris therefore never triggered by this EA; accounts without acomposite_idlearn about report requests only through HTTPpending_report. - HTTP non-2xx: the EA
Prints the whole payload (includingsecret_key) to the Experts log. Don't share raw logs.
InpStatus running / suspend và file .setInpStatus running / suspend and the .set files
InpStatus chỉ là nhãn EA báo lên: payload và frame hb mang "status":"running" hoặc "status":"suspend", server chỉ chấp nhận hai giá trị này. EA không đổi hành vi gửi theo nhãn và không can thiệp giao dịch trên terminal.
Trong monitor/ea-account-status-updater/ có hai preset:
InpStatus is just a label the EA reports: the payload and the hb frame carry "status":"running" or "status":"suspend", and the server accepts only those two values. The EA doesn't change how it sends based on the label and doesn't touch trading on the terminal.
monitor/ea-account-status-updater/ holds two presets:
| FileFile | InpStatus | Dùng khiUse when |
|---|---|---|
AccountStatusUpdater-running.set | 0 (running) | Terminal đang vận hành tài khoảnThe terminal is actively running the account |
AccountStatusUpdater-suspend.set | 1 (suspend) | Terminal tạm dừng / dự phòngThe terminal is paused / standby |
AccountStatusUpdater.set | 1 trong working tree hiện tại (đang sửa cục bộ)1 in the current working tree (locally modified) | Preset chung cũOlder general preset |
Ngoài InpStatus, hai file giống nhau ở mọi input không bí mật: InpApiUrl=https://fxconsole.gamelau.uk/api/account-status, InpPostIntervalSec=5, InpPanelUpdateSec=1, InpWebTimeoutMs=5000, InpTransport=2 (HYBRID), InpBrokerHost=fxconsole.gamelau.uk, InpBrokerPort=9602, InpBrokerName trống, InpHybridHttpIntervalSec=30, InpMinPushIntervalMs=500, InpHeartbeatSec=15, InpPanelEnable=true. InpNode bị đặt cứng theo một máy — sửa cho từng terminal hoặc để trống để tự lấy tên máy.
Apart from InpStatus, both files are identical on every non-secret input: InpApiUrl=https://fxconsole.gamelau.uk/api/account-status, InpPostIntervalSec=5, InpPanelUpdateSec=1, InpWebTimeoutMs=5000, InpTransport=2 (HYBRID), InpBrokerHost=fxconsole.gamelau.uk, InpBrokerPort=9602, empty InpBrokerName, InpHybridHttpIntervalSec=30, InpMinPushIntervalMs=500, InpHeartbeatSec=15, InpPanelEnable=true. InpNode is hard-set to one machine — change it per terminal or leave it empty for the computer name.
InpApiBearer, InpAccountSecretKey và InpBrokerSecret trong ba file .set là giá trị thật. Hai file running/suspend đang untracked: không commit, không gửi qua chat; khi chia sẻ preset hãy xoá trắng ba input này.
InpApiBearer, InpAccountSecretKey and InpBrokerSecret in the three .set files are real values. The running/suspend files are untracked: don't commit them or paste them in chat; blank those three inputs before sharing a preset.
Đổi trạng thái: mở hộp thoại EA (F7) → Inputs → Load file .set tương ứng → OK. EA khởi động lại với nhãn mới; lần push kế tiếp cập nhật dashboard.
Switching status: open the EA dialog (F7) → Inputs → Load the matching .set → OK. The EA restarts with the new label; the next push updates the dashboard.
Cài đặt trong MT5MT5 setup
- Tools → Options → Expert Advisors → Allow WebRequest for listed URL: thêm host của
InpApiUrlvàInpBrokerHost(SocketConnectdùng chung danh sách này).Tools → Options → Expert Advisors → Allow WebRequest for listed URL: add the host ofInpApiUrlandInpBrokerHost(SocketConnectuses the same list). - Bật Allow DLL imports nếu muốn
InpNodetự lấy tên máy.Enable Allow DLL imports ifInpNodeshould default to the computer name. - Điền
InpApiUrl,InpApiBearer,InpAccountSecretKey, chu kỳ POST/panel; với HYBRID thêmInpBrokerSecret. Quan hệ các loại key: fxconsole — Bearer token, secret key.FillInpApiUrl,InpApiBearer,InpAccountSecretKey, POST/panel cadence; for HYBRID alsoInpBrokerSecret. How the keys relate: fxconsole — Bearer token, secret key. - Gắn EA vào một chart mỗi terminal. Kiểm tra log
AccountStatusUpdater composite_id=…vàAccountStatusUpdater OK 200, panel dòng API xanh.Attach the EA to one chart per terminal. Check the log forAccountStatusUpdater composite_id=…andAccountStatusUpdater OK 200, and a green API row on the panel.
- HTTPS cần cert hợp lệ: cert self-signed làm
WebRequestlỗiGetLastError = 4014. - Cập nhật
.ex5: gỡ EA khỏi chart rồi kéo lại (F7 khi EA đang gắn không nạp binary mới). Hộp thoại MT5 LiveUpdate → chọn Later (Restart sẽ tắt các EA đang chạy). - Nâng cấp từ bản < 1.10 phải đi cùng broker v2 (HELLO cũ bị từ chối). Mọi lần sửa code phải bump
#property version. - Nút "Download EA" trên topbar fxconsole trả
/ea-account-status-updater.ziptrongpublic/, hoặc chuyển tớiEA_DOWNLOAD_URLnếu đặt.
- HTTPS needs a valid certificate: a self-signed cert makes
WebRequestfail withGetLastError = 4014. - Updating the
.ex5: remove the EA from the chart and attach it again (F7 on an attached EA doesn't load the new binary). MT5 LiveUpdate dialog → choose Later (Restart kills running EAs). - Upgrading from < 1.10 must go together with the v2 broker (the old HELLO is rejected). Every code change bumps
#property version. - The "Download EA" button in the fxconsole top bar serves
/ea-account-status-updater.zipfrompublic/, or redirects toEA_DOWNLOAD_URLwhen set.