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.

  • Cập nhậtUpdated 26/09/2026
  • Phiên bảnVersion 1.16
  • NguồnSource monitor/ea-account-status-updater/AccountStatusUpdater.mq5 · monitor/AccountStatusBroker/broker.js · monitor/README.html

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-status của fxconsole (Bearer). Đích TCP: AccountStatusBroker cổng 9602.
  • InpAccountSecretKey gắn tài khoản với user trên fxconsole (trùng mt5_secret_key của user).
  • Timer 1s (EventSetTimer(1)) chạy phần chậm (HTTP, HB, panel); OnTick chỉ đẩ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.
  • InpAccountSecretKey ties the account to a fxconsole user (equals the user's mt5_secret_key).
  • A 1s timer (EventSetTimer(1)) drives the slow paths (HTTP, HB, panel); OnTick only pushes TCP while streaming.

Lịch sử phiên bản gần đâyRecent version history

VersionNgàyDateThay đổiChange
1.162026-09-24Comment 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.142026-06-14Nhị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.132026-06-13Stream 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.122026-06-07Panel thêm dòng Socket: OK/DOWN | Last: <kind> Ns ago.Panel gains the Socket: OK/DOWN | Last: <kind> Ns ago line.
1.112026-06-06Nhậ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.102026-06-05On-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 ===

InputMặc địnhDefault.set vận hànhOperations .setÝ nghĩaMeaning
InpApiUrlhttp://127.0.0.1:8080/api/account-statushttps://fxconsole.gamelau.uk/api/account-statusEndpoint 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).
InpApiBearerplaceholder 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.
InpAccountSecretKeyplaceholderplaceholder<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.
InpPostIntervalSec55Chu kỳ POST ở chế độ HTTP (giây). < 1 → INIT_FAILED.POST cadence in HTTP mode (seconds). < 1 → INIT_FAILED.
InpPanelUpdateSec11Chu kỳ vẽ panel. < 1 → INIT_FAILED.Panel refresh cadence. < 1 → INIT_FAILED.
InpWebTimeoutMs50005000Timeout WebRequest (ms); WebRequest chặn đồng bộ tới mức này.WebRequest timeout (ms); WebRequest blocks synchronously up to this.
InpLogVerbosefalsefalseLog 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 ===

InputMặc địnhDefaultÝ nghĩaMeaning
InpStatusSTATUS_RUNNINGEnum 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.
InpNodetrốngemptyTê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 ===

InputMặc địnhDefault.set vận hànhOperations .setÝ nghĩaMeaning
InpTransportTRANSPORT_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.
InpBrokerHostfxconsole.gamelau.ukfxconsole.gamelau.ukHost 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.
InpBrokerPort96029602Cổng TLS-TCPTLS-TCP port
InpBrokerSecretplaceholderplaceholder<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").
InpBrokerNametrốngemptytrốngemptyTên broker trong HELLO. Trống → ACCOUNT_COMPANY; cũng trống → default.Broker name in HELLO. Empty → ACCOUNT_COMPANY; also empty → default.
InpHybridHttpIntervalSec3030Chỉ 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.
InpMinPushIntervalMs100500TCP/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).
InpHeartbeatSec1515TCP/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 ===

InputMặc địnhDefaultÝ nghĩaMeaning
InpPanelEnabletrueBật panelShow the panel
InpPanelX / InpPanelY550 / 25Toạ độ góc trên trái (px)Top-left position (px)
InpPanelWidth / InpPanelHeight460 / 252Kí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 / InpPanelTextColorclrBlack / clrWhiteNền / chữBackground / text
InpPanelFontSize10Cỡ chữ (font Consolas)Font size (Consolas)
InpPanelColorBuy / InpPanelColorSellclrDodgerBlue / clrOrangeRedDòng Buy / Sell; Sell cũng dùng cho lỗi và profit âmBuy / Sell lines; Sell also marks errors and negative profit
InpPanelColorNetLotclrYellowNet lot khi cân bằngNet lot when balanced
InpPanelColorProfitclrLimeProfit dương, API OK, Socket OKPositive profit, API OK, Socket OK
InpPanelColorBalance / InpPanelColorMarginclrWhite / clrSilverBalance, equity / marginBalance, equity / margin

Transport: HTTP / TCP_SOCKET / HYBRID

TRANSPORT_HTTPTRANSPORT_TCP_SOCKETTRANSPORT_HYBRID
Đường dữ liệuData pathPOST mỗi InpPostIntervalSecPOST every InpPostIntervalSecTLS tới broker: frame đầy đủ khi stream bật, hb khi tắtTLS to the broker: full frames while streaming, hb otherwiseNhư TCP_SOCKET + POST HTTP mỗi InpHybridHttpIntervalSecLike TCP_SOCKET + an HTTP POST every InpHybridHttpIntervalSec
Độ trễ dashboardDashboard latencyTheo chu kỳ POSTPOST cadenceGần tức thời khi có người xemNear-instant while someone watchesGầ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 defaultNhanh nhất; OnInit in cảnh báo mất tính năngFastest; OnInit warns about the missing featuresKhuyế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 theo InpHeartbeatSec), nếu tắt thì gửi hb theo g_hbIntervalMs → xét nhịp HTTP bằng GetTickCount64() (HTTP: InpPostIntervalSec; HYBRID: InpHybridHttpIntervalSec hoặ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ọi TryUpdate(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 InpMinPushIntervalMs hasn't elapsed, then pushes to the socket.
  • OnTimer every 1s (TryUpdate): drains broker frames → if streaming, pushes (forced by InpHeartbeatSec); otherwise sends hb every g_hbIntervalMs → checks the HTTP cadence with GetTickCount64() (HTTP: InpPostIntervalSec; HYBRID: InpHybridHttpIntervalSec or the g_forceHttpNow flag) → refreshes the panel.
  • OnInit: validates inputs, computes composite_id (printed to the log), starts the 1s timer, calls TryUpdate(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).

FieldNguồnSource
account_id, account_name, account_server, account_companyACCOUNT_LOGIN, ACCOUNT_NAME, ACCOUNT_SERVER, ACCOUNT_COMPANY
secret_key, status, nodeInpAccountSecretKey, InpStatus, InpNode / tên máy/ computer name
balance, equity, margin, profitACCOUNT_*; profit là floating (ACCOUNT_PROFIT)ACCOUNT_*; profit is floating (ACCOUNT_PROFIT)
profit_today, profit_week, profit_month, profit_last_monthTổ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_timeTimeCurrent() (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_lastChỉ 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

  1. User bấm Generate trên /dashboard/accounts/{id}. Laravel lưu yêu cầu; response POST /api/account-status kế tiếp chứa "pending_report":{…,"from_ts":N,…,"to_ts":N}. Ở HYBRID, broker còn đẩy report_request ngay.A user clicks Generate on /dashboard/accounts/{id}. Laravel stores the request; the next POST /api/account-status response carries "pending_report":{…,"from_ts":N,…,"to_ts":N}. In HYBRID the broker also pushes report_request immediately.
  2. EA thấy khoảng from/to mới → con trỏ = from, bật g_forceHttpNow. Cùng khoảng (đang stream dở) → giữ con trỏ. Response không còn pending_report → xoá trạng thái.The EA sees a new from/to range → cursor = from, sets g_forceHttpNow. Same range (mid-stream) → cursor kept. No pending_report in the response → state cleared.
  3. Mỗi POST mang 1 chunk 7 ngày [cursor, min(cursor + 7d, to)): report_deals gồ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_first khi chunk bắt đầu tại from; is_last khi chạm to.Each POST carries one 7-day chunk [cursor, min(cursor + 7d, to)): report_deals holds closing deals (type flipped from the deal type to the original position side) and balance operations (type = "balance", empty symbol, broker comment such as "Deposit"). is_first when the chunk starts at from; is_last when it reaches to.
  4. 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 and g_forceHttpNow is re-armed so the next chunk leaves on the next 1s tick (1.13).
  5. Server: is_first xoá deal cũ của tài khoản, is_last tí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ênh deployment.Server: is_first truncates the account's old deals, is_last computes 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 the deployment channel.
Chỉ HTTP mang chunk báo cáoOnly HTTP carries report chunks

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

  1. 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.
  2. 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.Send HELLO|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.
  3. Socket mới = trạng thái mới: g_streamOn = false, xoá buffer nhận. Broker trả stream_off hoặc stream_on ngay sau HELLO.Fresh socket = fresh state: g_streamOn = false, receive buffer cleared. The broker answers with stream_off or stream_on right 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.

FrameEA 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

ACCOUNT STATUS ID: 25938032 Name: DEMO USER Server: Broker-Live Balance: 100000.00 Equity: 99850.20 | Margin: 1200.00 Profit today: 350.00 | Profit: -149.80 Buy 3 / 0.30 lot Sell 1 / 0.10 lot Net 0.20 lot {"ok":true,"account_id":"25938032","updated_at":"…"} Socket: OK | Last: tcp:stream 0.4s ago
DòngRowNội dungContents
0ACCOUNT STATUS
1ID (trái) · Name (giữa) · Server (phải)ID (left) · Name (centre) · Server (right)
2Balance · 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)
3Buy 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)
4Kế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.
5Socket: 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:streamPayload đầ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_minFrame keep-alive nhỏ qua socket (stream tắt)Small keep-alive frame over the socket (stream off)
http:statusPayload đầ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ếLimitationHậu quảEffect
1Khô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.
2Broker 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.
3Khô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.
4Mộ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.
5OnDeinit 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.
Thêm, phát hiện khi viết trang này (chưa kiểm chứng tác động)Also found while writing this page (impact not verified)
  • Comment 1.10 ghi composite_id được gửi trong "HYBRID HTTP HB payload", nhưng ToJson() không có field composite_id. Đoạn code đồng bộ mt5_accounts.composite_id trong AccountStatusApiController vì thế không được EA này kích hoạt; tài khoản không có composite_id chỉ nhận yêu cầu báo cáo qua pending_report của HTTP.
  • HTTP non-2xx: EA Print nguyên payload (có secret_key) vào log Experts. Đừng chia sẻ log thô.
  • The 1.10 comment says composite_id ships in the "HYBRID HTTP HB payload", but ToJson() has no composite_id field. The mt5_accounts.composite_id sync in AccountStatusApiController is therefore never triggered by this EA; accounts without a composite_id learn about report requests only through HTTP pending_report.
  • HTTP non-2xx: the EA Prints the whole payload (including secret_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:

FileFileInpStatusDùng khiUse when
AccountStatusUpdater-running.set0 (running)Terminal đang vận hành tài khoảnThe terminal is actively running the account
AccountStatusUpdater-suspend.set1 (suspend)Terminal tạm dừng / dự phòngThe terminal is paused / standby
AccountStatusUpdater.set1 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.

Các file này chứa secret thậtThese files contain real secrets

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

  1. Tools → Options → Expert Advisors → Allow WebRequest for listed URL: thêm host của InpApiUrl và InpBrokerHost (SocketConnect dùng chung danh sách này).Tools → Options → Expert Advisors → Allow WebRequest for listed URL: add the host of InpApiUrl and InpBrokerHost (SocketConnect uses the same list).
  2. Bật Allow DLL imports nếu muốn InpNode tự lấy tên máy.Enable Allow DLL imports if InpNode should default to the computer name.
  3. Điền InpApiUrl, InpApiBearer, InpAccountSecretKey, chu kỳ POST/panel; với HYBRID thêm InpBrokerSecret. Quan hệ các loại key: fxconsole — Bearer token, secret key.Fill InpApiUrl, InpApiBearer, InpAccountSecretKey, POST/panel cadence; for HYBRID also InpBrokerSecret. How the keys relate: fxconsole — Bearer token, secret key.
  4. 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 for AccountStatusUpdater composite_id=… and AccountStatusUpdater OK 200, and a green API row on the panel.
  • HTTPS cần cert hợp lệ: cert self-signed làm WebRequest lỗi GetLastError = 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.zip trong public/, hoặc chuyển tới EA_DOWNLOAD_URL nếu đặt.
  • HTTPS needs a valid certificate: a self-signed cert makes WebRequest fail with GetLastError = 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.zip from public/, or redirects to EA_DOWNLOAD_URL when set.