| Global State Dataclass Python | Schema Vị Thế PyNhiQuaiBot

Được viết bởi thanhdt vào ngày 28/07/2026 lúc 13:49 | 32 lượt xem

Tham chiếu: bot_nhi_quai_v6_pro.py · Snapshot 1-pass · KG 01/08/2026


Sau khi đã snapshot positions 1-pass (bài Infrastructure trước), bước tiếp theo trong module ① DATA & STATE trên ảnh kiến trúc PyNhiQuaiBot là biến snapshot thành Global State có schema — không phải dictionary toàn cục cho mọi hàm sửa tùy tiện.

Bài viết dạy cách dùng dataclass trong Python để gom Config, snapshot vị thế, metric cụm 6111/6112 và metadata FSM — nền trước Grid Logic và Brain. Nội dung kỹ thuật Python · không hướng dẫn viết EA MQL5 · không cam kết lợi nhuận.

1. Global State ≠ biến global rải rác

Trong Bot_nhi_quai_v6_pro.py, lớp Config đã là @dataclass — map 1-1 input MQ5 port sang Python:

@dataclass
class Config:
    buy_magic: int = 6111
    sell_magic: int = 6112
    max_allowed_bias: float = 0.3
    buy_step: float = 50.0
    buy_x: float = 60.0
    # ...

Pain phổ biến: dev copy thêm buy_vol = 0, sell_vol = 0, last_bias = 0 rải rác trong class Bot — process_cluster cập nhật một nơi, draw_dashboard đọc nơi khác, reconnect xong số liệu lệch.

Global State đúng: một object (hoặc store có version) mà mọi module đọc snapshot bất biến và chỉ Reconciler được ghi sau khi đối soát MT5.

2. Schema tối thiểu cho bot hedging đa vị thế

Nhóm Trường gợi ý Vai trò
Identity symbol, buy_magic, sell_magic Tách cụm 6111 / 6112
Volume buy_vol, sell_vol, bias MaxAllowedBias · Guard
Positions buy_cluster, sell_cluster, all_positions Grid · Sweep
Account equity, balance, margin_free Lockdown · MinEqui2
FSM step_state, busy_until_ts STEP_BUSY 5s
Meta version, as_of_ts Phát hiện stale data

bias = buy_vol - sell_vol phải tính từ cùng snapshot với list lệnh mà process_cluster nhìn thấy — nếu không, Guard pass trong khi Grid đang nhìn half-old data.

3. Dataclass snapshot bất biến (frozen)

from dataclasses import dataclass, field
from typing import Any, List
import time

@dataclass(frozen=True)
class ClusterMetrics:
    buy_vol: float
    sell_vol: float
    buy_float: float
    sell_float: float

    @property
    def bias(self) -> float:
        return self.buy_vol - self.sell_vol


@dataclass(frozen=True)
class BotSnapshot:
    version: int
    as_of_ts: float
    symbol: str
    buy_magic: int
    sell_magic: int
    all_positions: tuple  # immutable tuple of MT5 position objects
    metrics: ClusterMetrics
    equity: float

    def buy_cluster(self) -> List[Any]:
        return [p for p in self.all_positions if int(p.magic) == self.buy_magic]

    def sell_cluster(self) -> List[Any]:
        return [p for p in self.all_positions if int(p.magic) == self.sell_magic]

frozen=True: strategy/Grid chỉ đọc — không vô tình snapshot.metrics.buy_vol += 0.01 giữa hai hàm.

4. BotState — store có version

@dataclass
class BotStateStore:
    _version: int = 0
    _current: BotSnapshot | None = None

    def publish(self, snap: BotSnapshot) -> None:
        self._version += 1
        object.__setattr__(snap, "version", self._version)  # nếu cần gán post-init
        self._current = snap

    def get(self) -> BotSnapshot:
        if self._current is None:
            raise RuntimeError("State chưa publish — gọi refresh trước on_tick")
        return self._current

Luồng mỗi tick:

  1. refresh_from_mt5() — một lần positions_get + account_info
  2. Tính ClusterMetrics từ list đã cache
  3. store.publish(BotSnapshot(...))
  4. process_cluster / tp_logic / Guard — chỉ store.get()
graph TD
    A(["Tick Python"]) --> B["positions_get 1 lần"]
    B --> C["build ClusterMetrics"]
    C --> D["store.publish snapshot"]
    D --> E["Grid · FSM · Guard đọc get"]
    E --> F{order_send?}
    F -->|Có| B
    F -->|Không| G(["gv.save · sleep"])

5. GVStore JSON — state qua restart

GVStore trong V6 Pro lưu P0, Step, FSM key vào file JSON — bền vững qua restart, khác snapshot sống trong một tick:

class GVStore:
    def __init__(self, path: Path):
        self.path = path
        self.data: Dict[str, float] = {}
        self._load()

    def get(self, name: str, default: float = 0.0) -> float:
        return float(self.data.get(name, default))

    def set(self, name: str, value: float) -> None:
        self.data[name] = float(value)

    def save(self) -> None:
        self.path.write_text(json.dumps(self.data, ensure_ascii=False, indent=2), encoding="utf-8")

Phân tách rõ:

Layer Sống bao lâu Ví dụ
Snapshot 1 tick positions, floating, bias
GVStore Qua restart P0, Step index, FSM busy
Config Deploy magic, step, x-level

Grid không nên ghi P0 vào biến global — dùng gv.set("BUY_P0", price).

6. Tích hợp vào on_tick PyNhiQuaiBot

def refresh_snapshot(self) -> BotSnapshot:
    raw = list(mt5.positions_get(symbol=self.symbol) or [])
    buy_vol = sell_vol = buy_f = sell_f = 0.0
    for p in raw:
        pr = float(p.profit + p.swap + p.commission)
        vol = float(p.volume)
        if int(p.magic) == self.cfg.buy_magic:
            buy_vol += vol
            buy_f += pr
        elif int(p.magic) == self.cfg.sell_magic:
            sell_vol += vol
            sell_f += pr
    acc = mt5.account_info()
    equity = float(acc.equity) if acc else 0.0
    metrics = ClusterMetrics(buy_vol, sell_vol, buy_f, sell_f)
    snap = BotSnapshot(
        version=0,
        as_of_ts=time.time(),
        symbol=self.symbol,
        buy_magic=self.cfg.buy_magic,
        sell_magic=self.cfg.sell_magic,
        all_positions=tuple(raw),
        metrics=metrics,
        equity=equity,
    )
    self.state.publish(snap)
    return snap

def on_tick(self) -> None:
    bid, ask, spread = self.rates()
    if bid <= 0 or spread > self.cfg.max_spread:
        return
    self.refresh_snapshot()
    snap = self.state.get()
    # process_cluster đọc snap.buy_cluster() — không gọi positions_get lại

7. Pandas — khi nào thêm?

Khóa PyNhiQuaiBot dùng list + dataclass trước Buổi 2–3. Pandas hữu ích khi:

  • Dashboard gom history nhiều tick
  • Backtest replay snapshot CSV
  • Báo cáo exposure theo campaign

Không bắt buộc cho vòng on_tick production — tránh mọi module sửa chung một DataFrame.

Đọc thêm: Global State & Pandas hub · Kiến trúc 6111/6112

8. FAQ

Dataclass thay Pydantic được không?
Được cho bot single-process. Pydantic tốt hơn khi validate JSON API / FastAPI dashboard.

Snapshot frozen có chậm không?
Copy tuple reference — nhẹ hơn gọi positions_get lặp. Refresh sau mỗi lệnh thành công.

Port từ MQ5 GlobalVariable?
Đúng hướng: GVStore Python thay GlobalVariableGet/Set. Logic Grid/Brain vẫn viết Python.

KG 01/08 còn mấy ngày?
Tính 29/07/2026 — còn 3 ngày · T7+CN 15:00.

9. Tóm tắt

  • Global State = schema + version, không phải biến rải rác
  • @dataclass(frozen=True) cho snapshot đọc-only mỗi tick
  • GVStore JSON cho P0/FSM qua restart · tách khỏi snapshot
  • Một publish() sau refresh — Grid/Brain/Guard dùng get() chung
  • Nền module ① trước Step/X và FSM STEP_BUSY

Giao dịch tự động có rủi ro drawdown — bài mang tính giáo dục kỹ thuật.

Liên hệ & đăng ký: Python Nâng cao Hedging · PyNhiQuaiBot · Hotline 0934 145 100 · Zalo 039 791 9841

Khóa PyNhiQuaiBot: https://www.huongnghiepdulieu.com/lap-trinh-python-nang-cao-hedging-tu-dong-hoa/ · Hotline 0934 145 100

Đặng Trí Thanh

Đặng Trí Thanh

Giám đốc Công nghệ · DNT Digital · Giảng viên HNDL
813 Bài viết
15.4k Người theo dõi
120k+ Lượt đọc

Đào tạo và triển khai thực chiến Python, MT5 và hệ thống bot auto trading / IB cho học viên và doanh nghiệp.