| Khóa Lập trình Python Cơ bản Buổi 6/15: Dictionary, Date & Time — Module cấu trúc dữ liệu cho trader và quant

Được viết bởi Đặng Trí Thanh vào ngày 07/08/2026 lúc 11:48 | 25 lượt xem

Khóa Lập trình Python Cơ bản Buổi 6/15: Dictionary, Date & Time trong Module 4

DNT Digital – Hướng Nghiệp Dữ Liệu (HNDL) chào anh chị và các bạn đang theo dõi lộ trình Lập trình Python Cơ bản — khóa học 15 buổi dành cho người mới bước vào thế giới Algo Trading, Bot Auto Trading và phân tích dữ liệu tài chính. Buổi 6 nằm trong Module 4: Cấu trúc dữ liệu nâng cao, tiếp nối list, tuple và set ở các buổi trước. Hôm nay chúng ta tập trung vào dictionary (dict) và module datetime — hai công cụ xuất hiện liên tục khi bạn viết script đọc cấu hình EA, ghi log lệnh, hoặc đồng bộ thời gian giữa Python và MetaTrader 5 (MT5).

Nếu bạn chưa đăng ký khóa, có thể xem chương trình và lịch khai giảng tại: Lập trình Python Cơ bản.


Mục tiêu học tập Buổi 6

Sau buổi học và bài đọc này, học viên có thể:

  1. Giải thích khi nào nên dùng dictionary thay vì list hoặc tuple trong bài toán trading.
  2. Thao tác CRUD trên dict: tạo, đọc, cập nhật, xóa; duyệt key/value an toàn.
  3. Kết hợp dict lồng nhau để mô hình hóa cấu hình symbol, khung thời gian, tham số risk.
  4. Dùng datetime, date, time, timedelta và timezone cơ bản cho log và backtest.
  5. Parse chuỗi thời gian từ file CSV/JSON export từ MT5 hoặc broker.
  6. Tránh lỗi thường gặp: mutable default, KeyError, naive vs aware datetime.

Buổi 6 không yêu cầu bạn đã biết pandas — chúng ta làm việc với thư viện chuẩn để nền tảng vững trước khi sang Module 5 (file I/O, JSON, API).


Vì sao trader cần Dictionary?

So sánh List, Tuple, Set và Dictionary trong bài toán trading

Trong giao dịch thuật toán, dữ liệu thường mang tính đặt tên hơn là đánh số thứ tự. Ví dụ: symbol XAUUSD có spread tối đa 35 point, magic number 20260807, khung H1 bật trend filter — bạn không muốn nhớ rằng phần tử index 3 trong list là spread. Dictionary ánh xạ khóa (key) sang giá trị (value), giúp code tự mô tả và dễ bảo trì khi scale từ một tài khoản demo sang nhiều symbol.

So sánh nhanh:

Cấu trúc Đặc điểm Ví dụ trading
List Thứ tự, index số Chuỗi giá close 100 nến
Tuple Bất biến, cố định (symbol, timeframe, magic)
Set Không trùng Tập symbol đang mở lệnh
Dict Key → value Cấu hình bot theo symbol

Khi bạn học tiếp khóa Xây dựng IB bằng Bot Auto Trading, dict còn dùng để cache trạng thái snapshot MT5 trước khi đẩy lên dashboard — Buổi 6 là bước chuẩn bị quan trọng.


Dictionary trong Python: khái niệm cốt lõi

Tạo dictionary

config_eurusd = {
    "symbol": "EURUSD",
    "timeframe": "H1",
    "magic": 6006,
    "max_spread_points": 20,
    "risk_percent": 1.0,
    "enabled": True,
}

Key thường là chuỗi; value có thể là số, bool, list, hoặc dict khác (lồng nhau). Python 3.7+ giữ thứ tự chèn key — hữu ích khi in log cấu hình theo thứ tự ổn định.

Dict comprehension tương tự list comprehension:

symbols = ["EURUSD", "GBPUSD", "XAUUSD"]
default_magic = {s: 6000 + i for i, s in enumerate(symbols)}
# {'EURUSD': 6000, 'GBPUSD': 6001, 'XAUUSD': 6002}

Truy cập và cập nhật

print(config_eurusd["symbol"])          # EURUSD
config_eurusd["risk_percent"] = 0.5     # cập nhật
config_eurusd["comment"] = "Py-Buoi6"   # thêm key mới

Dùng .get() khi key có thể không tồn tại — tránh KeyError trên server chạy bot 24/7:

slippage = config_eurusd.get("max_slippage", 3)

.setdefault() vừa đọc vừa khởi tạo nếu thiếu — pattern hay khi gom lệnh theo symbol:

orders_by_symbol = {}
ticket = 100001
sym = "XAUUSD"
orders_by_symbol.setdefault(sym, []).append(ticket)

Xóa và sao chép

del config_eurusd["comment"]
disabled = config_eurusd.pop("enabled", False)
import copy
backup = copy.deepcopy(config_eurusd)  # dict lồng nhau cần deepcopy

Shallow copy (config.copy()) đủ khi value là immutable; nếu value là list/dict con, chỉnh bản copy nông có thể ảnh hưởng bản gốc.

Duyệt dictionary

for key in config_eurusd:
    print(key, config_eurusd[key])

for k, v in config_eurusd.items():
    print(f"{k} = {v}")

for v in config_eurusd.values():
    print(v)

Trong pipeline log, pattern items() giúp format một dòng audit: symbol=EURUSD | magic=6006 | risk=0.5.


Dict lồng nhau: mô hình cấu hình đa symbol

Sơ đồ dict lồng nhau — cấu hình bot theo từng symbol

Cấu trúc gần với file JSON bạn sẽ gặp khi tích hợp Python ↔ MT5:

bot_profile = {
    "account": {"login": 12345678, "server": "Broker-Demo"},
    "symbols": {
        "EURUSD": {
            "timeframe": "M15",
            "magic": 6015,
            "filters": {"max_spread": 18, "session": "London"},
        },
        "XAUUSD": {
            "timeframe": "H1",
            "magic": 6101,
            "filters": {"max_spread": 35, "session": "NY"},
        },
    },
    "risk": {"mode": "percent", "value": 1.0, "max_daily_loss": 3.0},
}

Truy cập an toàn nhiều tầng:

spread_xau = bot_profile["symbols"]["XAUUSD"]["filters"]["max_spread"]
# Hoặc dùng get lồng nhau thủ công / helper
filters = bot_profile.get("symbols", {}).get("XAUUSD", {}).get("filters", {})
spread_xau = filters.get("max_spread", 999)

Bài tập nhỏ: Viết hàm get_symbol_config(profile, symbol) trả về dict filters hoặc {} nếu symbol không có trong profile.


Các phương thức và hàm hữu ích

keys = list(config_eurusd.keys())
values = list(config_eurusd.values())
config_eurusd.update({"max_spread_points": 25, "version": 1})

Gộp dict (Python 3.9+):

defaults = {"risk_percent": 1.0, "enabled": True}
user = {"symbol": "GBPUSD", "risk_percent": 0.75}
merged = defaults | user

collections.defaultdict tiện khi đếm sự kiện theo loại lệnh:

from collections import defaultdict
stats = defaultdict(int)
for action in ["BUY", "SELL", "BUY", "BUY"]:
    stats[action] += 1
# defaultdict(<class 'int'>, {'BUY': 3, 'SELL': 1})

Counter (cũng trong collections) mở rộng cho phân tích tần suất tag trong journal trading.


Module datetime: thời gian trong backtest và log

Naive vs aware datetime — đồng bộ thời gian với log MT5

Giao dịch là bài toán theo thời gian. Python chuẩn hóa qua module datetime:

  • date — ngày (không giờ).
  • time — giờ trong ngày.
  • datetime — ngày + giờ.
  • timedelta — khoảng cách thời gian.
  • timezone — múi giờ (UTC, v.v.).

Tạo và hiển thị

from datetime import datetime, date, time, timedelta, timezone

today = date.today()
now_local = datetime.now()  # naive — phụ thuộc OS
now_utc = datetime.now(timezone.utc)  # aware — khuyến nghị cho server

print(now_utc.isoformat())
# 2026-08-07T04:30:00+00:00

Format cho log file thống nhất trên VPS Việt Nam và broker UTC:

stamp = now_utc.strftime("%Y-%m-%d %H:%M:%S %Z")

timedelta: session, cooldown, expiry signal

candle_close = datetime(2026, 8, 7, 14, 0, tzinfo=timezone.utc)
next_check = candle_close + timedelta(minutes=15)
cooldown = timedelta(seconds=30)
session_end = datetime(2026, 8, 7, 21, 0, tzinfo=timezone.utc)
if next_check < session_end:
    print("Còn trong phiên")

Pattern cooldown sau lệnh tránh over-trading:

last_order_at = datetime.now(timezone.utc)
# ... sau khi đặt lệnh ...
if datetime.now(timezone.utc) - last_order_at < timedelta(minutes=5):
    print("Chưa hết cooldown")

Parse chuỗi từ CSV MT5

Export lịch sử MT5 thường có cột thời gian dạng 2026.08.07 13:45:02. Chuẩn hóa:

raw = "2026.08.07 13:45:02"
dt = datetime.strptime(raw.replace(".", "-", 2), "%Y-%m-%d %H:%M:%S")
# Hoặc giữ format gốc:
dt2 = datetime.strptime(raw, "%Y.%m.%d %H:%M:%S")

Luôn ghi chú timezone của broker trong README bot — lệch vài giờ có thể làm sai filter phiên Á/Âu/Mỹ.

So sánh và sắp xếp

events = [
    {"ticket": 1, "open_time": datetime(2026, 8, 7, 10, 0, tzinfo=timezone.utc)},
    {"ticket": 2, "open_time": datetime(2026, 8, 7, 9, 30, tzinfo=timezone.utc)},
]
events.sort(key=lambda e: e["open_time"])

Kết hợp dict + datetime: mỗi event là dict, sort theo key open_time — tiền đề cho danh sách lệnh trước khi học pandas DataFrame.


Timezone: naive vs aware — lỗi im lặng nguy hiểm

Naive datetime không gắn múi giờ; awaretzinfo. Trộn hai loại gây lỗi hoặc so sánh sai.

Quy ước HNDL trên VPS bot:

  1. Lưu trữ và so sánh nội bộ bằng UTC aware.
  2. Chỉ convert sang giờ Việt Nam (Asia/Ho_Chi_Minh) khi hiển thị dashboard cho con người.
  3. Document rõ broker server time vs UTC.
from zoneinfo import ZoneInfo  # Python 3.9+

utc = datetime(2026, 8, 7, 12, 0, tzinfo=timezone.utc)
vn = utc.astimezone(ZoneInfo("Asia/Ho_Chi_Minh"))
print(vn)  # 2026-08-07 19:00:00+07:00

Trên Windows cũ thiếu tzdata, cài gói tzdata qua pip theo hướng dẫn giảng viên.


Ví dụ tích hợp: journal lệnh bằng dict + datetime

Luồng ghi journal lệnh kết hợp dict và datetime

from datetime import datetime, timezone

journal = []

def log_trade(symbol, side, volume, price, magic):
    entry = {
        "ts": datetime.now(timezone.utc).isoformat(),
        "symbol": symbol,
        "side": side,
        "volume": volume,
        "price": price,
        "magic": magic,
    }
    journal.append(entry)
    return entry

log_trade("EURUSD", "BUY", 0.1, 1.0850, 6006)
log_trade("XAUUSD", "SELL", 0.01, 2388.5, 6101)

# Thống kê nhanh theo symbol
by_symbol = {}
for row in journal:
    by_symbol.setdefault(row["symbol"], []).append(row)

for sym, rows in by_symbol.items():
    print(sym, len(rows), "lệnh")

Buổi sau (Module 5) bạn sẽ ghi journal ra JSON thay vì chỉ giữ trong RAM.


Bài tập thực hành Buổi 6

Bài 1 — Cấu hình: Tạo dict RISK_RULES với key max_daily_loss_pct, max_open_trades, allowed_symbols (list). Viết hàm validate_order(rules, symbol, open_count) trả về True/False.

Bài 2 — Merge cấu hình: Cho BASE_CONFIGOVERRIDE_PER_SYMBOL["XAUUSD"]. Hàm build_config(symbol) trả về dict đã merge (override ưu tiên).

Bài 3 — Parse thời gian: Đọc list 5 chuỗi thời gian MT5, parse thành datetime aware UTC (giả sử input là giờ server UTC).

Bài 4 — Cooldown: Dict last_signal_at keyed by symbol. Hàm can_signal(symbol, minutes=5) kiểm tra timedelta.

Bài 5 — Thống kê: Từ list dict giao dịch giả lập, dùng defaultdict đếm số lệnh BUY/SELL theo từng symbol.

Bài 6 — Mini project: Script config_loader.py đọc dict Python hard-code, in bảng markdown các symbol enabled và magic tương ứng.

Gửi bài qua kênh nộp bài khóa học; giảng viên phản hồi trong 48 giờ làm việc.


Lỗi thường gặp và cách sửa

KeyError khi key chưa tồn tại — dùng .get() hoặc kiểm tra if key in d.

Mutable default argument trong hàm Python (dict/list làm default) — dùng None rồi khởi tạo trong thân hàm.

So sánh datetime naive và aware — thống nhất một kiểu trên toàn project.

Sửa dict trong lúc duyệt — duyệt bản copy list(d.items()) nếu cần xóa key theo điều kiện.

Nhầm strftime / strptimestrftime = format ra chuỗi; strptime = parse từ chuỗi.


FAQ

Dictionary có giữ thứ tự key không? Có, từ Python 3.7 (implementation) và 3.8+ (language spec).

Khi nào dùng tuple làm key? Khi key là tổ hợp bất biến, ví dụ (symbol, timeframe) làm key cache indicator — tuple phải toàn phần tử hashable.

Dict vs JSON? JSON là định dạng trao đổi; json.loads / json.dumps chuyển qua lại với dict Python. Không serialize trực tiếp datetime — chuyển sang chuỗi ISO trước.

pandas có thay dict không? Với bảng lớn (OHLCV), pandas phù hợp hơn; với cấu hình và metadata, dict vẫn gọn và rõ.

datetime.now() trên VPS có tin được không? Cần NTP đồng bộ giờ hệ thống; bot nên log cả UTC và server name.

Làm sao tính số nến giữa hai thời điểm? Chia timedelta cho độ dài nến (ví dụ 15 phút) — lưu ý nghỉ cuối tuần thị trường forex.

Module time vs datetime? time.sleep() trong vòng lặp bot; lịch và log dùng datetime.

Buổi 6 liên quan MT5 thế nào? Chuẩn bị parse export, cấu hình symbol, timestamp log — nền trước MetaTrader5 package ở module sau.

Có cần pytz không? Python 3.9+ ưu tiên zoneinfo; pytz vẫn gặp trên legacy code.

Học xong Buổi 6 làm gì tiếp? Ôn bài tập, xem trước JSON/file I/O Buổi 7, tham gia Q&A trên group học viên HNDL.


Tóm tắt và lộ trình Module 4

Buổi 6 khép lại tam giác cấu trúc dữ liệu thiết yếu: list (chuỗi), set (tập duy nhất), dict (ánh xạ có tên). Kết hợp datetime, bạn đủ công cụ để viết script quản lý cấu hình và nhật ký có thời gian chuẩn — bước không thể thiếu trước API, database và dashboard ở các khóa nâng cao Bot Auto Trading.

Tiếp theo trong khóa Lập trình Python Cơ bản: Buổi 7–8 (file, JSON, exception), Buổi 9–10 (requests, intro API), Buổi 11–15 (mini project bot paper trading).


Ôn tập nhanh: List, Tuple, Set trước khi đi sâu Dict

Ở Buổi 4–5, bạn đã làm việc với list để lưu chuỗi giá hoặc kết quả backtest đơn giản, tuple để đóng gói bộ ba (symbol, timeframe, magic) không thay đổi trong suốt vòng đời process, và set để loại trùng symbol khi quét nhiều nguồn tín hiệu. Dictionary không thay thế các cấu trúc đó mà bổ sung ở tầng semantic: mỗi tham số có tên rõ ràng. Một anti-pattern phổ biến trong code beginner là dùng list song song keys = ["symbol", "magic"]values = ["EURUSD", 6006] — dễ lệch index khi thêm field. Refactor sang dict một lần, sau này merge cấu hình từ file JSON sẽ tự nhiên hơn.

Hãy thử bài refactor nhỏ: cho LEGACY = ["EURUSD", 6006, 20, True] (symbol, magic, max_spread, enabled). Viết hàm legacy_to_dict(row) trả về dict với key có nghĩa. Đây là kỹ năng đọc code cũ khi bạn tham gia team quant hoặc maintain script copy-trade nội bộ.


Hiệu năng và bộ nhớ: dict có nhanh không?

Dictionary Python triển khai hash table. Trung bình, tra cứu, chèn, xóa theo key là O(1) nếu hàm băm phân bố tốt. Với bot chạy trên VPS 100–500 MB RAM, vài nghìn key (ticket, symbol, session) không đáng kể. Vấn đề thường không nằm ở dict mà ở logic bạn gắn vào vòng lặp: gọi API MT5 mỗi tick, ghi disk mỗi giây, hoặc deepcopy cấu hình lớn không cần thiết. Buổi 6 nhắc bạn profile trước khi tối ưu: đo thời gian bằng time.perf_counter() quanh khối xử lý dict nếu nghi ngờ bottleneck.

Key phải hashable: list làm key sẽ lỗi; tuple các số/chuỗi thì được. Trong cache indicator, pattern (symbol, timeframe, period) làm key tuple rất phổ biến trước khi chuyển sang LRU cache chuyên dụng ở khóa nâng cao.


Case study: cấu hình Grid đơn giản (minh họa giáo dục)

Giả sử bạn mô phỏng grid trên demo (không phải chiến lược khuyến nghị). Mỗi symbol có tham số riêng:

grid_profiles = {
    "EURUSD": {
        "base_lot": 0.01,
        "step_points": 200,
        "max_layers": 5,
        "magic": 7001,
    },
    "XAUUSD": {
        "base_lot": 0.01,
        "step_points": 500,
        "max_layers": 4,
        "magic": 7002,
    },
}

def layer_price(entry, step_points, layer_index, point=0.00001):
    return entry - layer_index * step_points * point

def describe_grid(symbol, entry_price):
    p = grid_profiles.get(symbol)
    if not p or not p.get("enabled", True):
        return f"{symbol}: profile không khả dụng"
    lines = [f"Grid {symbol} | magic={p['magic']}"]
    for i in range(p["max_layers"]):
        px = layer_price(entry_price, p["step_points"], i)
        lines.append(f"  L{i}: lot={p['base_lot']} @ {px:.5f}")
    return "n".join(lines)

print(describe_grid("EURUSD", 1.0900))

Hàm describe_grid minh họa tách data (dict) và logic (hàm) — nguyên tắc sau này map sang class OOP trong MQL5 ở khóa IB HP2. Bạn có thể mở rộng dict bằng "enabled": False cho symbol tạm dừng mà không xóa cấu hình.


Pattern match với dict (Python 3.10+)

Khi phân loại trạng thái tài khoản từ snapshot (dict), structural pattern matching giúp code dễ đọc:

def account_mode(snapshot: dict) -> str:
    match snapshot:
        case {"trade_allowed": False, "reason": reason}:
            return f"Không giao dịch: {reason}"
        case {"trade_allowed": True, "margin_level": ml} if ml < 150:
            return "Cảnh báo margin thấp"
        case {"trade_allowed": True}:
            return "Bình thường"
        case _:
            return "Thiếu dữ liệu snapshot"

print(account_mode({"trade_allowed": True, "margin_level": 320}))

Cú pháp này không bắt buộc trong bài tập Buổi 6 nhưng hữu ích nếu bạn dùng Python 3.11+ trên VPS — đồng bộ với môi trường HNDL khuyến nghị.


Chuẩn bị serialize: dict và datetime sang JSON

Module json không biết datetime mặc định. Pattern chuẩn:

import json
from datetime import datetime, timezone

def dt_hook(obj):
    if isinstance(obj, datetime):
        return {"__datetime__": obj.isoformat()}
    raise TypeError

def dt_rev_hook(d):
    if "__datetime__" in d:
        return datetime.fromisoformat(d["__datetime__"])
    return d

state = {
    "updated_at": datetime.now(timezone.utc),
    "symbols": {"EURUSD": {"magic": 6006}},
}
raw = json.dumps(state, default=dt_hook)
restored = json.loads(raw, object_hook=dt_rev_hook)

Buổi 7 sẽ làm quen file .json cấu hình bot trên disk; Buổi 6 giúp bạn hiểu vì sao cần hook hoặc chuyển datetime sang chuỗi ISO trước khi dump.


Workshop từng bước trên máy học viên

Bước 1: Cài Python 3.11+, tạo thư mục buoi6_lab. Bước 2: Tạo config.py chứa dict SYMBOLS ít nhất ba symbol. Bước 3: Viết print_config_table() in ASCII table magic/spread. Bước 4: Tạo journal.py append dict lệnh giả lập với timestamp UTC ISO. Bước 5: Viết stats.py dùng defaultdict đếm theo symbol và side. Bước 6: Parse file times.txt với năm dòng thời gian MT5, sort tăng dần, in chênh lệch phút giữa liên tiếp bằng timedelta. Bước 7: Commit lên repo cá nhân (private) hoặc nộp zip theo hướng dẫn LMS. Mỗi bước chụp màn hình terminal — giảng viên dùng để feedback nhanh trên group Zalo/Telegram học viên.

Nếu gặp lỗi encoding trên Windows, chạy $env:PYTHONIOENCODING='utf-8' trong PowerShell trước khi python script.py để log tiếng Việt không vỡ font.


Phụ lục A — Tra cứu nhanh phương thức dict

Phương thức / toán tử Mô tả ngắn
d[key] Lấy value; KeyError nếu thiếu
d.get(k, default) An toàn khi key có thể không có
d.keys(), d.values(), d.items() View để duyệt
d.update(other) Gộp từ dict hoặc iterable cặp
`d other` Merge tạo dict mới (3.9+)
d.setdefault(k, default) Lấy hoặc gán nếu thiếu
d.pop(k[, default]) Xóa và trả value
del d[k] Xóa key
in Kiểm tra membership key
copy(), deepcopy() Sao chép nông / sâu

Phụ lục B — Tra cứu nhanh datetime

API Mục đích
date.today() Ngày hôm nay local
datetime.now(tz=None) Now; thêm timezone.utc cho aware
datetime.strptime(s, fmt) Parse chuỗi
dt.strftime(fmt) Format ra chuỗi
timedelta(days=, hours=, ...) Khoảng thời gian
dt + delta, dt - delta Cộng trừ
dt1 - dt2 Khoảng cách (timedelta)
ZoneInfo("Area/City") Múi giờ IANA
dt.astimezone(tz) Đổi múi giờ
dt.isoformat() Chuỗi chuẩn ISO 8601

Giải mẫu một phần Bài tập 1 (validate_order)

RISK_RULES = {
    "max_daily_loss_pct": 3.0,
    "max_open_trades": 5,
    "allowed_symbols": ["EURUSD", "GBPUSD", "XAUUSD"],
}

def validate_order(rules, symbol, open_count, daily_loss_pct=0.0):
    if symbol not in rules.get("allowed_symbols", []):
        return False
    if open_count >= rules.get("max_open_trades", 999):
        return False
    if daily_loss_pct >= rules.get("max_daily_loss_pct", 100.0):
        return False
    return True

assert validate_order(RISK_RULES, "EURUSD", 2) is True
assert validate_order(RISK_RULES, "USDJPY", 1) is False

Phần daily_loss_pct thực tế lấy từ equity curve hoặc báo cáo broker — ở Buổi 6 ta truyền tham số giả lập để tập trung vào cấu trúc điều kiện và đọc dict.


Kết nối với MetaTrader 5 và khóa IB (preview có trách nhiệm)

Sau khi vững dict và datetime, học viên sang module kết nối MT5 qua package MetaTrader5 sẽ gặp dict-like named tuples và object account info. Việc quen key có tên từ Buổi 6 giúp bạn map account_info._asdict() hoặc build dict snapshot thủ công trước khi gửi lên FastAPI dashboard ở khóa Xây dựng IB bằng Bot Auto Trading. Không có shortcut: dashboard đẹp mà dữ liệu thời gian lộn xộn vẫn gây quyết định sai trên môi trường live.

HNDL khuyến khích lộ trình Python Cơ bản → HP2 IB Bot thay vì nhảy thẳng vào copy EA — bạn sẽ đọc được log Python, chỉnh được cấu hình JSON, và trao đổi kỹ thuật với team support rõ ràng hơn.


Câu hỏi mở rộng FAQ (Buổi 6+)

Làm sao version hóa cấu hình dict? Thêm key "schema_version": 1 và hàm migrate khi đổi tên field — tránh bot cũ đọc file mới lỗi im lặng.

Dict có thread-safe không? Trong CPython, một thao tác dict đơn lẻ atomic; vẫn cần lock nếu nhiều thread cùng đọc/ghi phức tạp (ít gặp trên bot single-process MT5).

OrderedDict còn cần không? Thường không; dict thường đủ thứ tự chèn; OrderedDict còn dùng khi cần move_to_end cho LRU thủ công.

Làm calendar nghỉ lễ forex? Dùng set ngày nghỉ + date so sánh; thư viện chuyên biệt để sau — Buổi 6 nắm datetimedelta là đủ cho lab.

Epoch timestamp từ broker? datetime.fromtimestamp(ts, tz=timezone.utc) — kiểm tra broker trả giây hay millisecond.

Ghi log rotation theo ngày? Tên file journal_2026-08-07.jsonl tạo bằng date.today().isoformat() — kết hợp Buổi 7 file I/O.

So sánh dict với dataclass? dataclass cho schema cố định, gợi ý IDE; dict linh hoạt cho config động — cả hai xuất hiện ở codebase HNDL.

Type hint cho dict? dict[str, float | int] (3.10+) giúp team đọc code; không bắt buộc buổi 6.

Học online tương tác Buổi 6? Slide + live coding dict journal; Q&A cuối buổi 20 phút; recording trên LMS trong 24 giờ.


Checklist tự đánh giá trước Buổi 7

  • [ ] Viết dict cấu hình ít nhất 3 symbol không copy-paste sai key.
  • [ ] Dùng .get() thay vì [] ở ít nhất một chỗ tra cứu optional.
  • [ ] Parse được chuỗi thời gian MT5 mẫu trong tài liệu.
  • [ ] In được thời điểm UTC ISO và quy đổi sang giờ Việt Nam.
  • [ ] Hoàn thành ít nhất 4/6 bài tập cuối bài.
  • [ ] Đọc disclaimer và hiểu nội dung không phải tư vấn đầu tư.

Hoàn thành checklist, bạn sẵn sàng cho file text, JSON, và xử lý ngoại lệ — những thứ giúp bot không dừng giữa phiên vì thiếu file config hoặc lỗi mạng thoáng qua.


Thực chiến đọc cấu hình nhiều lớp: nguyên tắc “một nguồn sự thật”

Khi bạn chạy nhiều script Python trên cùng một VPS — một process đọc tín hiệu TradingView, một process ghi log, một process đồng bộ với EA MT5 — việc mỗi script hard-code magic number khác nhau là nguồn lỗi kinh điển. Buổi 6 giới thiệu tư duy single source of truth: một dict (sau này là một file JSON) tại config/master.json được mọi process đọc khi khởi động. Hàm loader đơn giản:

def load_master_config(path: str) -> dict:
    import json
    with open(path, encoding="utf-8") as f:
        data = json.load(f)
    required = ["schema_version", "symbols", "risk"]
    for key in required:
        if key not in data:
            raise ValueError(f"Thiếu key bắt buộc: {key}")
    return data

Validation tối thiểu như trên đã cứu nhiều buổi demo trên lớp HNDL: học viên quên thêm symbol mới nhưng quên cập nhật magic, bot mở lệnh nhầm cluster. Dict giúp bạn in toàn bộ symbols ra log startup để đối chiếu mắt trước khi bật auto trade.


Bảng ánh xạ khái niệm MT5 ↔ Python (tham khảo)

Khái niệm MT5 / EA Gợi ý biểu diễn Python Buổi 6
Magic number int trong dict symbol
Symbol name str key hoặc field
Timeframe (PERIOD_H1) str "H1" hoặc enum sau này
Thời điểm mở lệnh datetime aware UTC
Comment lệnh str trong dict journal
Volume float
Spread tối đa int points trong filters
Session filter str hoặc dict giờ bắt đầu/kết thúc

Bảng không thay thế tài liệu MetaQuotes; nó giúp bạn dịch yêu cầu nghiệp vụ sang cấu trúc code trước khi viết MQL5 hoặc gọi API.


Bài đọc thêm và thói quen hàng ngày (15 phút)

Mỗi ngày trong tuần học Buổi 6, dành 15 phút: (1) mở REPL Python, tạo dict cấu hình một symbol bất kỳ; (2) thêm một key mới bằng .update(); (3) parse một dòng thời gian từ lịch sử MT5 demo của chính bạn; (4) ghi ra file text một dòng journal ISO. Thói quen nhỏ này khiến Buổi 7 về file I/O không còn xa lạ. Tài liệu chính thức: docs.python.org — dict, datetime — đọc song ngữ nếu cần, slide tiếng Việt trên LMS là lớp đầu tiên.


Mini quiz tự luyện (đáp án suy luận, không nộp)

  1. Dict a = {"x": 1}b = a; b["y"] = 2. a có key y không? (Có — cùng tham chiếu.)
  2. datetime.now()datetime.utcnow() khác nhau thế nào trên VPS Việt Nam? (Local vs UTC naive; khuyến nghị now(timezone.utc).)
  3. Key tuple ("EURUSD", "H1") có hợp lệ không? (Có.)
  4. config.get("risk", {}).get("value") trả gì nếu thiếu risk? (None.)
  5. Làm sao sort list dict theo field ts dạng chuỗi ISO? (ISO sort được theo thứ tự thời gian nếu format chuẩn.)

Kịch bản lab mở rộng: đồng hồ phiên giao dịch bằng datetime

Forex không phải lúc nào cũng thanh khoản đồng đều; nhiều bot chỉ cho phép signal trong “phiên” nhất định. Buổi 6 không cài thư viện lịch phức tạp, nhưng bạn có thể mô phỏng bằng dict mô tả phiên và datetime UTC:

from datetime import datetime, time, timezone

SESSIONS = {
    "London": {"start": time(7, 0), "end": time(16, 0)},
    "NewYork": {"start": time(12, 0), "end": time(21, 0)},
}

def in_session(now_utc: datetime, session_name: str) -> bool:
    s = SESSIONS[session_name]
    t = now_utc.time()
    if s["start"] <= s["end"]:
        return s["start"] <= t <= s["end"]
    return t >= s["start"] or t <= s["end"]

def allowed_sessions_for_symbol(config: dict, symbol: str) -> list:
    return config.get("symbols", {}).get(symbol, {}).get("sessions", [])

Hàm allowed_sessions_for_symbol đọc dict cấu hình đa symbol — kết hợp đúng tinh thần Buổi 6. Trên production, bạn sẽ bổ sung ngày nghỉ, tin tức mạnh (news filter) ở khóa nâng cao; phần datetime ở đây là xương sống để so sánh “bây giờ có nằm trong khoảng cho phép không”. Hãy viết unit test giả lập now_utc cố định thay vì phụ thuộc giờ thực khi chấm bài.


Từ điển thuật ngữ (song ngữ ngắn)

  • Dictionary / dict — ánh xạ khóa–giá trị, cấu trúc băm.
  • Key / Value — khóa tra cứu và dữ liệu gắn với khóa.
  • Hashable — đối tượng có thể làm key dict (immutable thường hashable).
  • Naive datetime — datetime không gắn timezone.
  • Aware datetime — datetime có tzinfo, khuyến nghị trên server.
  • UTC — giờ phối hợp quốc tế, chuẩn lưu log.
  • ISO 8601 — định dạng chuỗi thời gian ví dụ 2026-08-07T12:00:00+00:00.
  • Timedelta — khoảng thời gian, dùng cooldown và session.
  • Snapshot — ảnh chụp trạng thái tại một thời điểm (preview dashboard IB).
  • Schema version — số phiên bản cấu hình dict/JSON để migrate an toàn.

Thuật ngữ xuất hiện lại ở Buổi 15 HP2 (FastAPI dashboard); học viên Python Cơ bản nắm sớm sẽ ít choáng khi gặp payload JSON từ MT5.


Hỏi đáp nhanh trên lớp (micro-FAQ bổ sung)

Em có nên dùng eval() để parse string thành dict không? Không — rủi ro bảo mật; dùng json.loads() từ Buổi 7 hoặc literal an toàn. Copy dict bằng b = a có an toàn không? Chỉ nếu bạn không mutate; khi cập nhật cấu hình runtime hãy copy() hoặc deepcopy(). Lưu dict vào file .py hay JSON? JSON cho cấu hình động; .py cho default có comment. Khi nào list of dict thay vì dict of dict? Khi thứ tự quan trọng (journal theo thời gian) hoặc trùng key logic (nhiều lệnh cùng symbol). Python trên Mac/Linux khác Windows về datetime? zoneinfo và locale; test trên OS gần VPS production.

Em muốn in dict đẹp trong log terminal? Dùng import pprint; pprint.pp(config, width=100) hoặc json.dumps(d, indent=2, ensure_ascii=False) sau Buổi 7 — tránh print(dict) một dòng khó đọc khi support khách lúc 2 giờ sáng. Có nên dùng Pydantic cho config dict chưa? Tuỳ chọn ở HP2; Buổi 6 giữ dict thuần để hiểu mapping trước khi thêm validation schema. Dict có thể nested bao sâu? Python không giới hạn cứng, nhưng cấu hình bot nên giữ tối đa 3–4 tầng để con người đọc được.


Ghi chú cho mentor và học viên tự học bù

Nếu bạn xem recording Buổi 6 bù buổi live, hãy pause tại từng demo code và gõ lại tay thay vì copy-paste — muscle memory với dict và datetime giảm thời gian debug sau này đáng kể. Khi hỏi trên group, gửi kèm dict đã che login và traceback đầy đủ; mentor HNDL ưu tiên trả lời ca có context rõ. Buổi 6 là buổi “nhiều chữ” trong module cấu trúc dữ liệu: kiên nhẫn hoàn thành bài tập 5–6 sẽ thấy Buổi 8–9 (API) nhẹ hơn vì nền đã chắc. Cuối tuần, thử đọc một file log bot cũ của bạn (nếu có) và viết lại một dòng journal theo chuẩn dict + ISO UTC — đó là bài kiểm tra thực tế tốt nhất trước khi sang Module 5.


Lời kết từ đội ngũ giảng dạy DNT Digital

Dictionary và datetime là “ngôn ngữ” mà các hệ thống Bot Auto Trading, báo cáo IB và pipeline dữ liệu dùng mỗi ngày. Buổi 6 cố ý giữ ví dụ gần thực tế nhưng vẫn trong phạm vi lập trình cơ bản — không đi sâu chiến lược giao dịch hay cam kết hiệu suất. Hãy dành thêm 2–3 giờ tuần này refactor một script cũ từ list sang dict và chuẩn hóa timestamp log; khoản đầu tư thời gian nhỏ này trả lãi suốt các buổi còn lại của khóa và cả hành trình quant sau này. Gặp mentor trên LMS nếu bài tập 5–6 bị kẹt quá 30 phút — đừng im lặng rồi bỏ qua dict lồng nhau. Chúc bạn học tốt Buổi 6/15 và hẹn gặp trên Lập trình Python Cơ bản.


Disclaimer

Nội dung thuộc chương trình đào tạo lập trình và kỹ thuật của HNDL DNT Digital, không phải tư vấn đầu tư, không phải khuyến nghị mua/bán công cụ tài chính. Giao dịch forex, vàng, CFD có rủi ro cao. Ví dụ symbol, giá và tham số risk trong bài chỉ minh họa. Kết quả học tập phụ thuộc nỗ lực cá nhân.


Bài biên soạn đồng bộ với slide Buổi 6/15 — Module 4. Cập nhật: 07/08/2026.

Đặng Trí Thanh

Đặng Trí Thanh

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

Đặng Trí Thanh — Founder & CTO · Hướng Nghiệp Dữ Liệu - DNT Digital. Chuyên đà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.