Bài viết gần đây
-
-
Nhật ký vận hành Bot MT5: mẫu 7 ngày & khi nào phải dừng bot
Tháng 8 1, 2026 -
Cài MT5 trên VPS từ A→Z & giữ Terminal sống sau khi đóng RDP
Tháng 8 1, 2026
Trang chủ → Bài Viết → Global State trong Bot Python Hedging: Cấu trúc dữ liệu & Quản lý trạng thái toàn cục
| Global State trong Bot Python Hedging: Cấu trúc dữ liệu & Quản lý trạng thái toàn cục
Được viết bởi thanhdt vào ngày 01/08/2026 lúc 18:23 | 36 lượt xem
Global State trong Bot Python Hedging: Cấu trúc dữ liệu & Quản lý trạng thái toàn cục
Một bot Hedging bằng Python không phải bot 1 lệnh – 1 chỉ báo. Trong một vòng loop, bot phải trả lời nhanh hàng loạt câu hỏi: tài khoản đang có bao nhiêu BUY/SELL, cụm XAUUSD magic này đang Surplus hay Hedge, equity hiện tại so với lúc mở cụm ra sao, ticket nào thuộc Level 3… Nếu không có một Global State (kho trạng thái toàn cục) đúng chuẩn, bot sẽ loay hoay, gọi MT5 lặp lại, và dễ đưa quyết định sai.
Bài viết này giải thích cấu trúc dữ liệu và cách quản lý trạng thái toàn cục cho bot Python Hedging — nội dung Buổi 1 của khóa Lập trình Bot Python nâng cao Hedging & Tự động hóa tại Hướng Nghiệp Dữ Liệu.
🎓 Khai giảng & Lịch học
- Khai giảng: Thứ 7, 01/08/2026
- Lịch học: 15h00 – 17h00 các ngày Thứ 7 & Chủ nhật hàng tuần
- Hình thức: học trực tuyến (Zoom) + tài liệu, bài tập, hỗ trợ qua nhóm lớp
- Khóa học phù hợp: học viên đã có nền tảng Python & bot MT5 (hoặc đã học các khóa Bot Auto Trading tại HNDL)
📞 Đăng ký / tư vấn: Hotline/Zalo 0934 145 100 — hoặc xem chi tiết tại huongnghiepdulieu.com/lap-trinh-python-nang-cao-hedging-tu-dong-hoa
1. Vì sao Global State quan trọng với bot Hedging?
Bot Hedging cần trả lời nhanh các câu hỏi thực chiến:
| Câu hỏi thực chiến | Nếu không có Global State |
|---|---|
| Tài khoản đang có bao nhiêu BUY / SELL? | Gọi MT5 lặp lại → chậm, lệch số |
| Cụm XAUUSD magic 202601 đang Surplus hay Hedge? | Mỗi hàm tự tính → kết quả khác nhau |
| Equity hiện tại vs Equity lúc mở cụm? | Không nhớ → Guard sai |
| Ticket nào thuộc Level 3? | Quên comment/magic → đóng nhầm lệnh |
Nguyên tắc khóa học:
MT5 là nguồn sự thật của lệnh. Global State là nguồn sự thật của logic bot.
Mỗi tick/loop: sync từ MT5 → cập nhật State → quyết định Surplus/Hedge/Sweep → gửi lệnh → cập nhật lại State.
2. Bridge từ khóa MT5 — Global Variable vs Global State

Học viên đã học khóa MT5 biết Terminal Global Variables (GlobalVariableSet/Get) lưu vào gvariable.dat — sống sót khi tắt Terminal / mất điện (nếu ghi kịp xuống đĩa).
| Khóa MT5 | Khóa Python | |
|---|---|---|
| “Nhớ dài hạn” mặc định | GV Terminal trên ổ cứng | Không tự có — State nằm trong RAM process |
| Restart / crash | Đọc lại GV + quét lệnh | Process chết → State mất hết |
| Việc phải làm | GlobalVariableGet + rebuild cache |
sync_from_mt5() lại (bắt buộc); tùy chọn save/load JSON/SQLite ở buổi sau |
Hệ quả thực chiến: Bot Python restart giữa cụm lệnh không được tin State cũ trong biến. Luôn coi MT5 positions là chuẩn, rebuild Cluster/Surplus từ sync mới.
Buổi 1 chỉ xây State đúng trong phiên chạy. Persistence (ghi file/DB) sẽ bổ sung khi vào vận hành 24/7 — tinh thần giống Buổi 12 khóa MT5 (VPS), nhưng lớp lưu khác.
3. Các thực thể dữ liệu cốt lõi
3.1. Position (1 lệnh đang mở)
ticket, symbol, magic, comment
side (BUY/SELL), volume, price_open
sl, tp, profit, swap, time_open
level (X-Level suy ra từ comment hoặc index)
cluster_id (gom theo symbol+magic hoặc symbol+magic+side)
3.2. Cluster (1 “đám” lệnh cùng chiến lược)
cluster_id
symbol, magic
buy_volume, sell_volume
net_volume = buy - sell
avg_buy, avg_sell
surplus_side, surplus_volume
state: (FSM — Buổi 7; thống nhất với khóa MT5)
opened_equity, max_dd_pct
Tên trạng thái FSM: khóa MT5 dùng
WAITING | TRADING | LOCKDOWN | SWEEPING | PANIC. Khóa Python sẽ thống nhất cùng bộ tên ở Buổi 7 (FSM). Trong tài liệu nháp có thể gặpIDLE/ATTACK/HEDGE— coi là alias tạm, không dùng song song trong code production.
3.3. AccountSnapshot (ảnh chụp tài khoản)
balance, equity, margin, free_margin
margin_level, open_positions
ts (timestamp sync)
3.4. BotConfig (cấu hình bất biến trong phiên)
symbols, magics
step_points, x_levels
lot_base, lot_factor
max_dd_pct, max_spread
guard_enabled
4. Chọn cấu trúc dữ liệu nào?
![Tra cứu O(1) bằng dict[ticket] thay vì quét list mỗi lần](https://www.huongnghiepdulieu.com/img_datastructure.png)
| Nhu cầu | Cấu trúc đề xuất | Lý do |
|---|---|---|
| 1 position | @dataclass |
Rõ field, dễ đọc, type hint |
| Tra cứu nhanh theo ticket | dict[int, Position] |
O(1) lookup |
| Lọc theo symbol/magic | index phụ dict[tuple, set[int]] |
Tránh scan toàn bộ mỗi lần |
| Cấu hình | dataclass(frozen=True) hoặc TypedDict |
Ít bị sửa nhầm runtime |
| Snapshot phục vụ dashboard | dict / JSON-serializable |
Đẩy ra UI/Telegram dễ |
Anti-pattern cần tránh
- Biến global rải rác:
positions = [],equity = 0ở nhiều file. - List position rồi
fortìm ticket mỗi lần. - Nhét mọi thứ vào 1
dictkhổng lồ không schema. - Gọi MT5 trực tiếp trong mọi hàm nghiệp vụ (không qua State).
Anti-pattern: nhìn trước — sau
Anti-pattern 1: scan list mỗi lần cần 1 lệnh
# ❌ BAD
for p in positions: # positions là list
if p["ticket"] == 1250:
return p
# Mỗi tick mà làm thế này với vài lệnh thì vẫn chạy, nhưng khi 50 lệnh +
# nhiều module gọi, rõ ràng lãng phí và dễ sót ticket.
# ✅ GOOD
return gs.positions.get(1250)
Anti-pattern 2: gọi MT5 trong mọi hàm nghiệp vụ
# ❌ BAD — hàm quyết định tự hỏi MT5
def should_hedge():
positions = mt5.positions_get() # gọi broker mỗi lần
... # rồi tự đếm
# Rủi ro: nhiều hàm gọi → chậm, giới hạn API, kết quả "thời điểm khác nhau".
# ✅ GOOD — MT5 chỉ gọi ở 1 nơi (SyncEngine), mọi hàm đọc State
def should_hedge(gs: GlobalState):
cl = gs.get_cluster("XAUUSD#202601")
return cl.surplus_volume >= gs.config.lot_base
Anti-pattern 3: “god dict” không schema
# ❌ BAD
state = {"pos": [...], "eq": 0, "cfg": {...}}
state["eq"] = mt5.account_info().equity # ai cũng ghi, không kiểm soát
# ✅ GOOD — dataclass + method
gs.update_account(snapshot) # có lock, có chuẩn hoá
Anti-pattern 4: lọc theo comment bằng chuỗi trần rải rác
# ❌ BAD — magic "202601" nhập tay ở 5 chỗ, sửa 1 chỗ quên chỗ kia
# ✅ GOOD — magic nằm trong BotConfig, cluster_id sinh từ helper
def make_cluster_id(symbol: str, magic: int) -> str:
return f"{symbol}#{magic}"
5. Mô hình Global State (Singleton có kiểm soát)
┌─────────────────────────────────────────────┐
│ GlobalState │
│ config: BotConfig │
│ account: AccountSnapshot │
│ positions: dict[ticket -> Position] │
│ clusters: dict[cluster_id -> Cluster] │
│ flags: running, panic, last_error │
│ lock: threading.RLock() (chuẩn bị Buổi 6) │
└─────────────────────────────────────────────┘
▲ ▲
│ read │ write (qua method)
Strategy / Guard SyncEngine / OrderRouter
Quy tắc vàng
- Không gán trực tiếp field lung tung từ bên ngoài.
- Mọi thay đổi đi qua method:
upsert_position(),remove_position(),rebuild_clusters(). - Đọc nhiều, ghi ít, ghi có khóa (lock) khi vào đa luồng.
- Sau mỗi sync MT5: rebuild derived metrics (net, surplus, avg).
6. Derived State — tính toán từ dữ liệu gốc

Dữ liệu gốc (source): ticket, side, volume, price_open…
Dữ liệu suy ra (derived): không lưu tay nếu có thể tái tính ổn định.
net_volume = buy_volume - sell_volume
surplus_volume = abs(net_volume)
surplus_side = "BUY" if net_volume > 0 else ("SELL" if net_volume < 0 else "NONE")
Ví dụ thực chiến
- BUY 0.30 + SELL 0.10 → net = +0.20 → Surplus BUY 0.20 → cần cân nhắc Hedge SELL.
- BUY 0.20 + SELL 0.20 → net = 0 → có thể Sweep / chốt cụm.
Buổi 5 sẽ dùng các field này cho logic Tấn công/Phòng thủ. Buổi 1 chỉ cần tính đúng và lưu đúng chỗ.
7. Đồng bộ State với MT5 (luồng chuẩn)

1. positions_get() / orders_get() từ MT5
2. Lọc theo magic + symbol thuộc bot
3. upsert / remove trong GlobalState
4. rebuild_clusters()
5. refresh AccountSnapshot
6. Strategy đọc State → quyết định
7. Gửi lệnh → (tuỳ chọn) optimistic update + sync lại
Lưu ý: Optimistic update giúp UI mượt, nhưng luôn có 1 lần sync “cứng” sau lệnh để tránh State ảo.
7.1. Mở rộng State cho nhiều cặp & nhiều magic
Trong thực chiến, một tài khoản có thể chạy song song nhiều cụm: XAUUSD magic 202601 (chiến lược chính), XAUUSD magic 202602 (chiến lược phụ), XAGUSD magic 202601… Global State phải phân biệt rõ từng cụm.
Quy ước cluster_id = f"{symbol}#{magic}" giúp tránh nhầm lẫn giữa “cùng symbol khác magic” và “cùng magic khác symbol”.
def all_clusters_for_symbol(gs, symbol: str) -> list[Cluster]:
with gs._lock:
return [c for c in gs.clusters.values() if c.symbol == symbol]
def total_net_for_magic(gs, magic: int) -> float:
with gs._lock:
return round(sum(c.net_volume for c in gs.clusters.values()
if c.magic == magic), 8)
Khi nào nên tách cluster theo side?
- Nếu chiến lược “BUY-ladder” và “SELL-ladder” là 2 kế hoạch độc lập trên cùng symbol+magic, có thể đặt
cluster_id = f"{symbol}#{magic}#{side}". - Mặc định khóa học dùng
symbol#magic(gộp cả 2 chiều), rồi tính Surplus — đơn giản, đúng tinh thần Hedge. - Quyết định này nên được viết rõ trong BotConfig (thêm field
cluster_mode: str = "by_magic") thay vì rải rác trong code.
Đọc State theo cụm — bảng quyết định nhanh
| Tình huống State | Hành động gợi ý |
|---|---|
surplus_volume == 0 và đủ lệnh |
Chờ/cân nhắc Sweep chốt cụm |
| Surplus BUY lớn, giá giảm | Cân nhắc Hedge SELL đối trọng |
Drawdown ≥ max_dd_pct |
Chuyển cụm sang PANIC |
| Cụm không có lệnh | Xoá khỏi clusters để tránh rác |
8. Demo code thực tế
Model Position / Cluster bằng @dataclass
from dataclasses import dataclass
@dataclass
class Position:
ticket: int
symbol: str
magic: int
side: str # BUY / SELL
volume: float
price_open: float
comment: str = ""
profit: float = 0.0
level: int = 0
GlobalState singleton + method cập nhật
import threading
class GlobalState:
_instance = None
_init_lock = threading.Lock()
def __new__(cls):
with cls._init_lock:
if cls._instance is None:
obj = super().__new__(cls)
obj._ready = False
cls._instance = obj
return cls._instance
def __init__(self):
if getattr(self, "_ready", False):
return
self._lock = threading.RLock()
self.config = BotConfig()
self.account = AccountSnapshot()
self.positions: dict[int, Position] = {}
self.clusters: dict[str, Cluster] = {}
self.running = True
self.last_error = None
self._ready = True
# --- ghi dữ liệu có kiểm soát ---
def upsert_position(self, pos: Position):
with self._lock:
self.positions[pos.ticket] = pos
self._rebuild_clusters_unlocked()
def remove_position(self, ticket: int):
with self._lock:
self.positions.pop(ticket, None)
self._rebuild_clusters_unlocked()
def set_account(self, snap: AccountSnapshot):
with self._lock:
self.account = snap
# --- đọc tiện ích ---
def get_position(self, ticket: int) -> Position | None:
with self._lock:
return self.positions.get(ticket)
def get_cluster(self, cluster_id: str) -> Cluster | None:
with self._lock:
return self.clusters.get(cluster_id)
def get_positions_by_magic(self, magic: int) -> list[Position]:
with self._lock:
return [p for p in self.positions.values() if p.magic == magic]
def snapshot(self) -> dict:
"""Trả bản copy JSON-friendly cho UI / Telegram / dashboard."""
with self._lock:
return {
"account": {
"balance": self.account.balance,
"equity": self.account.equity,
"free_margin": self.account.free_margin,
"margin_level": self.account.margin_level,
},
"positions": [
{
"ticket": p.ticket, "symbol": p.symbol,
"magic": p.magic, "side": p.side,
"volume": p.volume, "profit": p.profit,
"level": p.level,
}
for p in self.positions.values()
],
"clusters": [
{
"cluster_id": c.cluster_id, "symbol": c.symbol,
"net_volume": c.net_volume,
"surplus_side": c.surplus_side,
"surplus_volume": c.surplus_volume,
"state": c.state,
}
for c in self.clusters.values()
],
"ts": time.time(),
}
# --- rebuild derived ---
def _rebuild_clusters_unlocked(self):
buckets = {}
for p in self.positions.values():
cid = make_cluster_id(p.symbol, p.magic)
buckets.setdefault(cid, []).append(p)
rebuilt = {}
for cid, lst in buckets.items():
buy = sum(p.volume for p in lst if p.side == "BUY")
sell = sum(p.volume for p in lst if p.side == "SELL")
net = round(buy - sell, 8)
avg_buy = (sum(p.volume * p.price_open for p in lst if p.side == "BUY")
/ buy) if buy else 0.0
avg_sell = (sum(p.volume * p.price_open for p in lst if p.side == "SELL")
/ sell) if sell else 0.0
rebuilt[cid] = Cluster(
cluster_id=cid,
symbol=lst[0].symbol,
magic=lst[0].magic,
buy_volume=buy, sell_volume=sell, net_volume=net,
surplus_side=("BUY" if net > 1e-8
else "SELL" if net < -1e-8 else "NONE"),
surplus_volume=abs(net),
avg_buy=avg_buy, avg_sell=avg_sell,
tickets=[p.ticket for p in lst],
)
self.clusters = rebuilt
# --- sync từ MT5 (nút sống còn sau restart) ---
def sync_from_mt5(self):
"""Gọi khi khởi động hoặc sau lệnh để chắc chắn State = MT5."""
with self._lock:
fresh = {} # quét positions thật từ MT5
for pos in read_positions_from_mt5(): # wrapper đọc broker
if pos.magic in self.config.magics:
fresh[pos.ticket] = pos
self.positions = fresh
self._rebuild_clusters_unlocked()
self.set_account(read_account_from_mt5())
Tại sao sync_from_mt5() là hàm “cứu mạng”?
Python process chết (crash, restart VPS, đổi code) → State trong RAM bay sạch. Nhưng MT5 vẫn giữ nguyên các lệnh thật. Gọi sync_from_mt5() ngay khi khởi động sẽ tái dựng toàn bộ positions + clusters + account từ broker — bot “tỉnh dậy” đúng tình trạng cụm lệnh, không đoán mò.
GlobalState hoàn chỉnh: set_account, get_cluster, snapshot, sync_from_mt5
class GlobalState:
def __init__(self):
self._lock = threading.RLock()
self.config = BotConfig()
self.account = AccountSnapshot()
self.positions: dict[int, Position] = {}
self.clusters: dict[str, Cluster] = {}
self.running = True
self.last_error = None
# --- ghi dữ liệu có kiểm soát ---
def upsert_position(self, pos: Position):
with self._lock:
self.positions[pos.ticket] = pos
self._rebuild_clusters_unlocked()
def remove_position(self, ticket: int):
with self._lock:
self.positions.pop(ticket, None)
self._rebuild_clusters_unlocked()
def set_account(self, snap: AccountSnapshot):
with self._lock:
self.account = snap
# --- đọc tiện ích ---
def get_position(self, ticket: int) -> Position | None:
with self._lock:
return self.positions.get(ticket)
def get_cluster(self, cluster_id: str) -> Cluster | None:
with self._lock:
return self.clusters.get(cluster_id)
def get_positions_by_magic(self, magic: int) -> list[Position]:
with self._lock:
return [p for p in self.positions.values() if p.magic == magic]
def snapshot(self) -> dict:
"""Trả bản copy JSON-friendly cho UI / Telegram / dashboard."""
with self._lock:
return {
"account": {
"balance": self.account.balance,
"equity": self.account.equity,
"free_margin": self.account.free_margin,
"margin_level": self.account.margin_level,
},
"positions": [
{
"ticket": p.ticket, "symbol": p.symbol,
"magic": p.magic, "side": p.side,
"volume": p.volume, "profit": p.profit,
"level": p.level,
}
for p in self.positions.values()
],
"clusters": [
{
"cluster_id": c.cluster_id, "symbol": c.symbol,
"net_volume": c.net_volume,
"surplus_side": c.surplus_side,
"surplus_volume": c.surplus_volume,
"state": c.state,
}
for c in self.clusters.values()
],
"ts": time.time(),
}
# --- rebuild derived ---
def _rebuild_clusters_unlocked(self):
buckets = {}
for p in self.positions.values():
cid = make_cluster_id(p.symbol, p.magic)
buckets.setdefault(cid, []).append(p)
rebuilt = {}
for cid, lst in buckets.items():
buy = sum(p.volume for p in lst if p.side == "BUY")
sell = sum(p.volume for p in lst if p.side == "SELL")
net = round(buy - sell, 8)
avg_buy = (sum(p.volume * p.price_open for p in lst if p.side == "BUY")
/ buy) if buy else 0.0
avg_sell = (sum(p.volume * p.price_open for p in lst if p.side == "SELL")
/ sell) if sell else 0.0
rebuilt[cid] = Cluster(
cluster_id=cid,
symbol=lst[0].symbol,
magic=lst[0].magic,
buy_volume=buy, sell_volume=sell, net_volume=net,
surplus_side=("BUY" if net > 1e-8
else "SELL" if net < -1e-8 else "NONE"),
surplus_volume=abs(net),
avg_buy=avg_buy, avg_sell=avg_sell,
tickets=[p.ticket for p in lst],
)
self.clusters = rebuilt
# --- sync từ MT5 (nút sống còn sau restart) ---
def sync_from_mt5(self):
"""Gọi khi khởi động hoặc sau lệnh để chắc chắn State = MT5."""
with self._lock:
fresh = {} # quét positions thật từ MT5
for pos in read_positions_from_mt5(): # wrapper đọc broker
if pos.magic in self.config.magics:
fresh[pos.ticket] = pos
self.positions = fresh
self._rebuild_clusters_unlocked()
self.set_account(read_account_from_mt5())
9. Xử lý lỗi & “sync cứng” sau lệnh
Trong vận hành, State có thể lệch MT5 vì nhiều lý do:
- Lệnh bị từ chối (requote, margin, spread cao) nhưng code “tưởng” đã vào.
- Lệnh được mở bằng tay trên MT5 (không qua bot).
- Bot đặt TP/SL, MT5 tự đóng lệnh khi chạm — bot chưa kịp cập nhật.
- Disconnect broker, sync bỏ lỡ 1 vài lệnh.
Quy trình phòng thủ:
- Sau mỗi lệnh gửi đi: chờ broker xác nhận (deal) → rồi gọi
sync_from_mt5()lại. - Định kỳ (ví dụ mỗi 30–60 giây hoặc mỗi tick thứ N): refresh nhẹ
positions. - Ghi
last_errorvào State để dashboard hiển thị trạng thái “bot đang bị lệch hay không”. - Khi phát hiện
len(gs.positions)≠ số lệnh MT5: dừng quyết định mới, sync lại, log cảnh báo.
Mẫu vòng lặp chính:
while gs.running:
try:
gs.sync_from_mt5() # 1) State = MT5
signal = strategy.on_tick(gs, price) # 2) đọc State
if signal == "HEDGE_NEEDED":
router.send_hedge(gs) # 3) gửi lệnh
gs.sync_from_mt5() # 4) sync cứng lại
except Exception as e:
gs.last_error = repr(e)
log.exception("loop error")
time.sleep(1)
time.sleep(0.05)
Mẹo: đừng để
sync_from_mt5()thất bại làm chết bot. Luôn có try/except và giữ State cũ để bot “đứng yên an toàn” thay vì hành động mù.
10. Kiểm thử Global State
Trước khi chạy live, hãy kiểm thử State bằng dữ liệu giả (không cần broker):
def test_net_surplus():
gs = GlobalState()
gs.upsert_position(Position(1, "XAUUSD", 202601, "BUY", 0.30, 2400.0))
gs.upsert_position(Position(2, "XAUUSD", 202601, "SELL", 0.10, 2405.0))
cl = gs.get_cluster("XAUUSD#202601")
assert cl.net_volume == 0.20
assert cl.surplus_side == "BUY"
assert cl.surplus_volume == 0.20
def test_remove_rebuild():
gs = GlobalState()
gs.upsert_position(Position(1, "XAUUSD", 202601, "BUY", 0.30, 2400.0))
gs.upsert_position(Position(2, "XAUUSD", 202601, "SELL", 0.30, 2405.0))
gs.remove_position(2)
cl = gs.get_cluster("XAUUSD#202601")
assert cl.net_volume == 0.30
assert cl.surplus_side == "BUY"
def test_snapshot_json():
gs = GlobalState()
gs.upsert_position(Position(1, "XAUUSD", 202601, "BUY", 0.30, 2400.0))
snap = gs.snapshot()
import json
json.dumps(snap) # không được lỗi
Kiểm thử giúp phát hiện sớm lỗi “tính Surplus sai” — lỗi mà khi live sẽ tốn tiền thật.
11. Sai lầm phổ biến của người mới
- Tin State cũ sau restart — không sync từ MT5 → cụm lệnh tính sai từ đầu.
- Nhét volume bằng tay thay vì rebuild từ positions → lệch số giữa các module.
- Gọi MT5 trong Strategy — chậm, và mỗi hàm đọc tại thời điểm khác nhau.
- Không có lock rồi sau này thêm thread → data race khó tìm.
- Lưu surplus_side như state độc lập mà không có nguồn gốc rõ → khi đảo chiều net, quên cập nhật.
- Sử dụng nhiều tên trạng thái (
IDLE,BUYING…) thay vì bộ FSM chuẩn → rối khi lên Buổi 7.
12. Checklist trước khi kết thúc buổi
- [ ] Phân biệt được Position / Cluster / Account / Config.
- [ ] Biết vì sao dùng
dict[ticket]thay vì chỉlist. - [ ] Hiểu Singleton GlobalState dùng method, không sửa field bừa.
- [ ] Tính được
net_volume,surplus_side,surplus_volume. - [ ] Nắm luồng sync MT5 → State → Strategy.
- [ ] Phân biệt GV MT5 (ổ cứng) vs State Python (RAM — mất khi restart).
- [ ] Biết tên FSM chuẩn sẽ thống nhất với khóa MT5 (
WAITING…) ở Buổi 7.
13. Bridge sang các buổi sau
| Buổi | Liên kết với Global State |
|---|---|
| 2 · Cache & Pandas/NumPy | Cache giá/OHLC cạnh State, không nhét history vào positions |
| 3 · Magic & Comment | cluster_id/level phụ thuộc magic + comment chuẩn |
| 5 · Surplus & Hedge | Đọc derived fields từ Cluster |
| 6 · Multi-thread | RLock trong GlobalState thành bắt buộc |
| 7 · FSM | cluster.state dùng bộ tên chuẩn MT5: WAITING/TRADING/LOCKDOWN/SWEEPING/PANIC |
| 9 · Guard | Đọc account + max_dd_pct để cắt rủi ro |
14. Thuật ngữ nhanh
| Thuật ngữ | Nghĩa ngắn |
|---|---|
| Global State | Kho trạng thái dùng chung toàn bot (RAM process Python) |
| Global Variable (MT5) | Biến Terminal lưu gvariable.dat — chống mất điện phía EA MQL5 |
| Source of truth | Nơi dữ liệu được tin là chuẩn |
| Derived metric | Chỉ số suy ra từ dữ liệu gốc |
15. Kết luận
Global State chính là “bộ não nhớ” của bot Hedging Python. Xây đúng cấu trúc dữ liệu (@dataclass + dict[ticket] + Singleton có kiểm soát) giúp mọi module — Strategy, Guard, SyncEngine, OrderRouter — đọc/ghi trạng thái thống nhất, tránh lệch cụm và quyết định sai. Đây là nền tảng cho toàn bộ khóa học.
👉 Khóa học Lập trình Bot Python nâng cao Hedging & Tự động hóa tại HNDL: https://www.huongnghiepdulieu.com/lap-trinh-python-nang-cao-hedging-tu-dong-hoa/
📞 Hotline/Zalo: 0934 145 100
FAQ
Q: Vì sao dùng dict[ticket] thay vì list?
A: Tra cứu theo ticket là O(1), trong khi list phải quét toàn bộ — với hàng chục lệnh mỗi giây, dict nhanh và sạch hơn nhiều.
Q: Global State mất khi restart Python có sao không?
A: Không — vì MT5 là nguồn sự thật. Sau restart, gọi sync_from_mt5() để rebuild State từ positions thật.
Q: Có cần lưu State ra file không?
A: Ở Buổi 1 chưa cần. Khi vào vận hành 24/7, bổ sung save/load JSON/SQLite (buổi sau).
Q: Surplus side tính thế nào?
A: net = buy - sell; net > 0 → BUY, net < 0 → SELL, net = 0 → NONE. Surplus volume = abs(net).
Q: Singleton có phải luôn tốt không?
A: Với bot đơn process, singleton có kiểm soát + lock là hợp lý. Khi scale nhiều process, dùng shared store (DB/queue) — nâng cao.
Q: Vì sao phải rebuild clusters sau mỗi upsert/remove mà không cộng dồn?
A: Cộng dồn dễ sai số float và quên cập nhật khi lệnh đổi magic/comment. Rebuild từ positions là nguồn duy nhất, đảm bảo mọi nơi cùng một kết quả.
Q: net_volume dùng số thập phân thế nào?
A: Volume có tối đa 2–3 số lẻ tùy broker. Nên round(..., 8) để tránh sai số float, nhưng luôn so sánh Surplus bằng > 1e-8 thay vì != 0.
Q: Khi nào cần snapshot cho dashboard?
A: Mỗi lần UI/Telegram hỏi, gọi gs.snapshot() để lấy bản copy JSON — không truyền thẳng các object đang bị thread khác sửa.
Q: Có nên tách GlobalState thành nhiều class nhỏ không?
A: Tùy độ lớn dự án. Nếu dự án nhỏ, 1 class đủ. Nếu lớn, tách PositionStore, ClusterStore, AccountStore nhưng vẫn đi qua 1 facade GlobalState để giữ API thống nhất.
Q: Học xong Buổi 1 cần tự viết lại toàn bộ không?
A: Không. Có sẵn demo_position_models.py và demo_global_state.py trong tài liệu buổi học. Hãy đọc hiểu, chạy thử, rồi tự viết lại cluster_id(), upsert/remove, sync_from_mt5, set_account, get_cluster, snapshot để nhớ sâu — đó chính là bài tập Buổi 1.
Kịch bản thực chiến: bot đang chạy mà “không biết mình đang có gì”
Hình dung một buổi tối thị trường XAUUSD biến động mạnh. Bot Hedging Python mở ra một cụm lệnh:
- Lệnh 1: BUY XAUUSD 0.30, magic 202601, ticket 1250.
- Lệnh 2: SELL XAUUSD 0.10, magic 202601, ticket 1251.
- Lệnh 3: SELL XAUUSD 0.20, magic 202601, ticket 1252.
- Lệnh 4: BUY XAUUSD 0.05, magic 202602 (một chiến lược phụ), ticket 1253.
Nếu mỗi hàm trong bot tự gọi positions_get() rồi tự đếm, rất dễ xảy ra:
- Hàm A đếm được 3 lệnh (lọc sai magic).
- Hàm B tính Surplus BUY 0.30, hàm C tính Surplus SELL 0.25.
- Guard thấy equity giảm 3% nhưng không biết lúc mở cụm equity là bao nhiêu.
- Khi cần đóng lệnh Level 3, bot không nhớ ticket nào thuộc Level 3.
Kết quả: bot ra lệnh Hedge trùng, hoặc không Hedge đúng lúc, hoặc đóng nhầm lệnh. Không phải bot dốt, mà vì không có một Global State chuẩn duy nhất.
Với Global State tốt, toàn bộ bot chỉ đọc một nguồn:
positions[1250]→ BUY 0.30 magic 202601.clusters["XAUUSD#202601"].surplus_side→ BUY, surplus 0.15.account.equityvsclusters["XAUUSD#202601"].opened_equity→ Guard quyết định chuẩn.positions[1250].level→ 3, có trong danh sách đóng lệnh.
Đây chính là lý do Buổi 1 bắt đầu bằng cấu trúc dữ liệu và quản lý trạng thái toàn cục, trước khi học Hedge, FSM hay Guard.
🎓 Khai giảng & Lịch học khóa Lập Trình Bot Python Nâng Cao Hedging & Tự Động Hóa:
Khai giảng Thứ 7 01/08/2026 · Lịch học 15h00 – 17h00 các ngày Thứ 7 & Chủ nhật hàng tuần (học online Zoom + tài liệu/bài tập).
Đăng ký/tư vấn: 📞 Hotline/Zalo 0934 145 100 — chi tiết: huongnghiepdulieu.com/lap-trinh-python-nang-cao-hedging-tu-dong-hoa
📌 Muốn làm chủ Bot Python Hedging thực chiến — từ cấu trúc dữ liệu, Global State, FSM đến Guard & vận hành tự động hóa?
Khóa học Lập Trình Bot Python Nâng Cao Hedging & Tự Động Hóa tại Hướng Nghiệp Dữ Liệu hướng dẫn chi tiết từng buổi: cấu trúc dữ liệu, sync MT5, Surplus/Hedge, FSM, multi-thread, Guard rủi ro và vận hành bot trên tài khoản thật.
Weekly Digest — Nhận Bản Tin Hàng Tuần
Nhận các bài viết phân tích kỹ thuật chuyên sâu, thuật toán giao dịch tự động (Trading Bot) và các giải pháp công nghệ mới nhất từ Hướng Nghiệp Dữ Liệu.
Đặng Trí Thanh
Giám đốc Công nghệ · DNT Digital · Giảng viên HNDLĐà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.