Bài viết gần đây
-
Huỳnh Thị Ngọc Loan — Giảng Viên Yoga & Spa | HNDL
Tháng 8 6, 2026
| Market Cache, Pandas & NumPy cho Bot Hedging Python (Buổi 2) | HNDL
Được viết bởi Đặng Trí Thanh vào ngày 04/08/2026 lúc 13:01 | 55 lượt xem
Đặng Trí Thanh
Giám đốc Công nghệ · DNT Digital · Hướng Nghiệp Dữ Liệu
Buổi 2 khóa Python Hedging: Market Cache, Pandas & NumPy cho bot thực chiến.
Market Cache, Pandas & NumPy: Bot Hedging Python không được gọi copy_rates mỗi tick
Bot hedging Python thường không chết vì thiếu EMA. Nó chết vì mỗi vòng loop đều kéo lại 500 nến M1 từ MetaTrader 5, tính lại ATR từ đầu, rồi mới nhìn Surplus của cụm lệnh. Trong lúc đó giá đã chạy, spread đã giãn, và lệnh Hedge — nếu còn kịp — cũng vào với slippage lớn hơn kế hoạch.
Ở Buổi 1, khóa Lập trình Python nâng cao Hedging tự động hóa xây Global State: nguồn sự thật về vị thế, cluster, equity và các cờ vận hành. Buổi 2 trả lời câu hỏi kế tiếp: giá và OHLC để đâu? Nếu nhét lịch sử nến vào cùng chỗ với danh sách ticket, State phình ra, khó sync, khó khóa đa luồng. Nếu không có cache, Strategy gọi thẳng mt5.copy_rates_from_pos / copy_ticks_from mỗi lần cần một con số close — bot biến thành client API kém hiệu quả.
Bài viết này trình bày lớp Market Cache cho bot hedging Python: TTL cho tick, invalidate OHLC theo bar, dùng NumPy trên hot path, dùng Pandas khi cần bảng và phân tích. Nội dung mang tính giáo dục kỹ thuật trong lộ trình PyNhiQuaiBot / QuantTrade tại Hướng Nghiệp Dữ Liệu; không cam kết lợi nhuận và không thay thế đánh giá rủi ro trước khi chạy vốn thật.
1. Vì sao Cache là bài Infrastructure, không phải “tối ưu muộn”?
Nhiều học viên nghĩ: “Logic Surplus/Hedge chạy đúng đã, sau này tối ưu sau.” Với bot 1 chỉ báo trên 1 symbol, đôi khi tạm chấp nhận được. Với bot hedging đa level, đa cụm, loop dày, tối ưu muộn nghĩa là:
- CPU cao trên VPS → terminal/API chậm theo
- Quyết định dựa trên rates cũ vì lần gọi trước chưa xong
- Không đo được hit/miss → không biết hệ thống đang “đoán” hay đang “biết”
- Khi vào Buổi 6 (multi-thread), mọi chỗ gọi MT5 trực tiếp trở thành race condition tiềm ẩn
Hãy hình dung một vòng loop tối giản:
- Sync positions → Global State
- Lấy giá / ATR → quyết định có mở thêm Surplus hoặc Hedge không
- Guard kiểm tra spread, equity, stale data
- Gửi lệnh nếu được phép
Bước 2 nếu mỗi lần đều copy_rates 500 cây, bạn đang trả chi phí I/O cho một câu hỏi lẽ ra chỉ cần đọc buffer trong RAM — trừ khi bar đã đổi hoặc TTL tick đã hết.
Nguyên tắc Buổi 2:
State nhớ bot đang thế nào. Cache nhớ thị trường vừa thế nào. Config nhớ bot được phép thế nào.
Ba lớp này không trộn. Trộn là nguồn của stale decision và bug “hôm qua chạy đúng, hôm nay sai mà không biết vì sao”.
2. State khác Cache khác Config — bảng ranh giới thực chiến
| Loại dữ liệu | Ví dụ | Thuộc lớp | Hết hạn thế nào |
|---|---|---|---|
| Ticket, side, volume, magic | lệnh đang mở | Global State | Sync/reconcile từ MT5 |
| Surplus, net volume, cluster | metric suy ra | Global State (derived) | Rebuild sau sync |
| Bid/ask/last | giá hiện tại | Market Cache | TTL ngắn (ms) |
| OHLC M1 cửa sổ 500 | nến gần đây | Market Cache | Khi bar time đổi |
| ATR/EMA buffer | chỉ báo | Cache hoặc buffer cạnh cache | Theo OHLC / tick policy |
| Lot, step, max_dd, magic list | tham số | BotConfig | Đổi config / restart phiên |
| Lịch sử lệnh 90 ngày | audit/backtest | DB/file | Retention policy |
Cache không phải chỗ lưu “campaign đang HEDGE_BUILD”. Cái đó là State/FSM. Cache cũng không thay Config: không hard-code lot vào TickCache.
Một điểm dễ nhầm: có dữ liệu vừa cache vừa ảnh hưởng quyết định (ví dụ spread). Khi đó bắt buộc có metadata tuổi dữ liệu: as_of, cached_at, hoặc cờ is_stale. Guard Buổi 9 sẽ từ chối mở lệnh mới nếu tick quá cũ — không phải vì key biến mất, mà vì còn key nhưng hết hạn tin cậy.
3. Anti-pattern kinh điển: gọi rates trong mọi hàm con

# SAI — hot path gọi MT5 trực tiếp
def calc_atr(symbol):
rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M1, 0, 500)
# ... tính ATR
def should_hedge(symbol, cluster):
atr = calc_atr(symbol) # gọi rates
tick = mt5.symbol_info_tick(symbol) # gọi tick
...
Mỗi lần Strategy hỏi một câu, MT5 bị hỏi lại. Trên demo có thể “chạy được”. Trên VPS + tin tức + nhiều symbol, latency cộng dồn. Đúng hơn:
# ĐÚNG — một lần lấy qua cache mỗi loop
tick = cache.get_tick(symbol)
close = cache.close_array(symbol, "M1", n=500)
atr_val = atr(high, low, close)[-1] # hoặc buffer ATR đã cache
Fetcher (MT5) chỉ nằm sau cache. Strategy không biết — và không cần biết — lần này là hit hay miss, miễn là API cache ổn định và có thể quan sát hits/misses khi debug.
4. Kiến trúc MarketCache tối thiểu

Strategy / Guard
│ get_tick / get_ohlc / close_array
▼
┌─────────────────────────────────────┐
│ MarketCache │
│ ticks[(symbol)] + TTL │
│ ohlc[(symbol, tf)] + bar_time │
│ bar_time_fn(symbol, tf) │
│ RLock (chuẩn bị multi-thread) │
│ hits / misses │
└─────────────────────────────────────┘
│ miss / stale / bar đổi
▼
tick_fetcher / ohlc_fetcher → MT5 hoặc fake (bài tập)
Vì sao cần bar_time_fn?
Nếu mỗi lần get_ohlc đều gọi copy_rates rồi mới so sánh bar cuối, bạn vẫn trả phí fetch dù “logic” bảo là hit. Muốn hit thật sự (không gọi fetcher), cache phải biết bar hiện tại bằng một hàm rẻ hơn full window — ví dụ đọc time của nến đang chạy, hoặc đồng hồ bar giả lập trong bài tập lớp.
Luồng quyết định OHLC:
current_bar = bar_time_fn(symbol, tf)- Nếu đã có entry cùng
current_barvà cùngn→ hit, trả copy DataFrame/array - Ngược lại → miss, gọi
ohlc_fetcher, lưubar_time, trả dữ liệu mới
Tick thì đơn giản hơn: so now - cached_at với TTL (ví dụ 50–200ms, hoặc “một lần mỗi loop” nếu loop tự quản).
TickQuote gợi ý
@dataclass
class TickQuote:
symbol: str
bid: float
ask: float
time_msc: int
@property
def mid(self) -> float:
return (self.bid + self.ask) / 2.0
@property
def spread(self) -> float:
return self.ask - self.bid
Spread lấy từ đây cho Guard; không cần một biến global current_spread rời rạc.
5. TTL, invalidate và “stale nhưng vẫn còn trong RAM”
Ba cơ chế đừng gộp thành một:
| Cơ chế | Ý nghĩa | Ví dụ |
|---|---|---|
| TTL | Bản ghi còn được phép dùng trong bao lâu | Tick 150ms |
| Invalidate theo bar | Cửa sổ OHLC đổi khi nến mới | M1 time đổi |
| Stale flag / age check | Dữ liệu còn đó nhưng chiến lược từ chối tin | Mất feed 3 giây |
Bot hedge tin tức thường cần Guard đọc age của tick. Cache có thể vẫn trả quote cuối cùng để dashboard hiển thị “giá cuối biết được”, nhưng execution path phải fail-closed: không mở level mới khi stale.
Đây là chỗ Bridge sang Buổi 9: Cache cung cấp dữ liệu + tuổi; Guard quyết định có được hành động không. Đừng để MarketCache tự “im lặng bịa” giá trung bình khi mất tick — trừ khi bạn cố ý thiết kế fallback và ghi log rất rõ.
6. NumPy hay Pandas? Đừng chọn theo thói quen notebook
| Nhu cầu | Công cụ | Lý do |
|---|---|---|
| EMA/ATR/rolling trên cửa sổ cố định trong loop | NumPy | Ít object, vectorized, ổn định kiểu float64 |
| Debug bảng nến, export CSV, join session | Pandas | Đọc được, linh hoạt |
| Backtest hàng triệu dòng (Buổi 11) | Pandas + NumPy | Throughput + API phân tích |
| Sửa chung một DataFrame từ nhiều thread | Không | Race + chậm + khó audit |
Pattern khóa này khuyến nghị:
- Cache giữ DataFrame OHLC (tiện quan sát) hoặc structured array
- Hot path lấy
close = df["close"].to_numpy(dtype=float) - Indicator chạy trên
ndarray - Dashboard có thể nhận snapshot DataFrame đã copy
Pandas không phải “Global State”. Đừng df_positions toàn cục để mọi module .loc[...] =. Buổi 1 đã chốt: positions là schema/dataclass + dict theo ticket.
Rolling window NumPy
Khi chỉ cần N close gần nhất, buffer cố định tránh list.append rồi cắt:
buf = np.empty(N, dtype=float)
# khi có close mới:
buf[:-1] = buf[1:]
buf[-1] = new_close
Cách này giữ allocation ổn định — hữu ích khi loop dày trên VPS nhỏ.
7. Chỉ báo trên cache: ATR & EMA đủ dùng cho Step động
Buổi 4 sẽ dùng biến động để tính Step/X-Level. Buổi 2 chỉ cần buffer đúng và tính đúng hướng.
True Range rút gọn:
def true_range(high, low, close):
prev_close = np.r_[close[0], close[:-1]]
return np.maximum(
high - low,
np.maximum(np.abs(high - prev_close), np.abs(low - prev_close)),
)
EMA cổ điển:
def ema(arr, period):
alpha = 2 / (period + 1)
out = np.empty_like(arr, dtype=float)
out[0] = arr[0]
for i in range(1, len(arr)):
out[i] = alpha * arr[i] + (1 - alpha) * out[i - 1]
return out
Trong production, bạn có thể cache sẵn mảng ATR và chỉ cập nhật điểm cuối khi bar đóng — đó là tối ưu bậc hai. Học viên Buổi 2 hãy làm đúng trước: OHLC không fetch thừa, indicator nhận ndarray sạch.
Ví dụ số mang tính minh họa: XAUUSD M1, cửa sổ 120 nến giả lập, ATR14 khoảng vài đơn vị giá tùy noise. Quan trọng không phải con số đẹp, mà pipeline OHLC → ndarray → ATR[-1] → StepEngine không gọi MT5 chen giữa.
8. Luồng loop chuẩn sau Buổi 1 + Buổi 2
1. GlobalState.sync_from_mt5() # lệnh / account
2. tick = cache.get_tick(symbol) # TTL
3. ohlc = cache.get_ohlc(symbol, tf, n)
4. Đọc cluster surplus từ State
5. Đọc ATR/close/spread từ Cache
6. Strategy đề xuất hành vi
7. Guard duyệt (spread, DD, stale...)
8. OrderRouter gửi lệnh
9. Sync State lại (reconcile)
Hai lệnh cấm trong Strategy thuần:
mt5.copy_rates_*trực tiếp- Sửa
positionsdict bỏ qua method State
Khi vào multi-thread, cả State và Cache nên dùng RLock hoặc mô hình “một writer / nhiều reader snapshot”. Buổi 6 sẽ siết kỷ luật này; Buổi 2 chỉ cần chừa chỗ cho khóa.
9. Quan sát hit/miss — metric nhỏ nhưng cứu vận hành
MarketCache nên đếm:
hits,misses- (nâng cao) thời gian fetch trung bình
- (nâng cao) số lần stale reject ở Guard
Nếu miss gần như bằng số lần gọi get_ohlc mỗi phút trên M1, có thể:
bar_time_fnbáo bar đổi quá thường (sai đồng hồ)nbị đổi liên tục giữa các chỗ gọi (500 vs 501) → phá hit- Ai đó gọi
invalidatequá tay
Nếu hit 100% kéo dài trong giờ biến động mạnh trong khi giá trên chart đã chạy xa, nghi TTL tick quá dài hoặc fetcher bị stub.
Dashboard Buổi 10 có thể treo hai số hit/miss cạnh equity — rẻ mà rất “ops”.
10. Bài tập lớp gắn với bài viết này
Học viên không cần MT5 để hoàn thành bài tập Buổi 2. Inject tick_fetcher, ohlc_fetcher, bar_time_fn giả lập:
- Hai lần
get_ticktrong TTL → chỉ fetch 1 lần - Hai lần
get_ohlccùng bar → fetch 1 lần advance_bar()rồiget_ohlc→ fetch lần 2atr/emachạy trên NumPy, phần tử cuối hữu hạn
Cách làm này buộc hiểu hit thật (không gọi fetcher), không phải hit danh nghĩa sau khi đã trả phí I/O.
11. Anti-pattern checklist trước khi mang ra tài khoản thật
- Gọi
copy_ratestrong mọi helper chỉ báo - Nhét 10.000 nến H1 vào Global State cùng positions
- Dùng một DataFrame global cho vừa giá vừa lệnh, nhiều thread cùng ghi
- TTL tick 5–10 giây rồi mang đi scalping/hedge dày
- Không có
as_of— Guard không biết dữ liệu già - Đổi
nlung tung (500/512/480) khiến cache luôn miss - Coi cache hit là “giá đúng sự thật tuyệt đối” dù feed đứt
- Tính Step bằng Pandas
applytừng dòng trong hot path
Mỗi dòng trên là một bug vận hành đội lốt feature.
12. Case study: cùng logic Surplus, khác lớp Cache
Giả sử cụm XAUUSD magic 202601 đang Surplus BUY 0.20 lot. Strategy quy ước: nếu giá đi ngược thêm khoảng k * ATR14 thì mở Hedge SELL để kéo net về gần 0 theo kế hoạch phòng thủ.
Kịch bản A — không cache Mỗi lần kiểm tra điều kiện, bot gọi copy_rates 500 nến + symbol_info_tick. Trên VPS trung bình, một vòng có thể tốn vài chục đến vài trăm milli-giây tùy tải Terminal. Trong phiên biến động, vòng lặp dày hơn — nghịch lý là lúc cần nhanh thì API càng chậm vì chính bot đang spam đọc lịch sử.
Kịch bản B — có MarketCache Tick lấy theo TTL 100–150ms. OHLC M1 chỉ fetch khi bar_time đổi. ATR tính trên ndarray đã có. Phần lớn vòng loop chỉ đọc RAM. Khi tin tức làm spread giãn, Guard đọc tick.spread và tick.time_msc để quyết định đứng ngoài — không cần kéo lại cả cửa sổ nến chỉ để biết ask−bid.
Hai kịch bản có thể dùng chung công thức Hedge. Khác nhau ở chỗ: B giữ được nhịp quyết định ổn định hơn và đo được sức khỏe dữ liệu qua hit/miss + age. Đó là khác biệt giữa “script demo” và “module infrastructure”.
Lưu ý: Cache không làm chiến lược lãi hơn theo nghĩa edge thị trường. Nó làm đường ống đáng tin hơn. Edge vẫn thuộc về Step, Surplus/Hedge, Guard và kỷ luật vốn — các buổi sau.
13. Adapter MT5 thực tế: đặt fetcher ở đâu?
Trong bài tập lớp, fetcher là hàm giả. Trên bot thật, nên bọc MT5 một lớp mỏng:
def mt5_tick_fetcher(symbol: str) -> TickQuote:
t = mt5.symbol_info_tick(symbol)
if t is None:
raise RuntimeError(f"no tick: {symbol}")
return TickQuote(symbol, t.bid, t.ask, t.time_msc)
def mt5_ohlc_fetcher(symbol: str, timeframe: str, n: int) -> pd.DataFrame:
tf = TF_MAP[timeframe] # "M1" -> mt5.TIMEFRAME_M1
rates = mt5.copy_rates_from_pos(symbol, tf, 0, n)
if rates is None or len(rates) == 0:
raise RuntimeError(f"no rates: {symbol} {timeframe}")
df = pd.DataFrame(rates)
df["time"] = pd.to_datetime(df["time"], unit="s", utc=True)
return df
bar_time_fn có thể lấy int(df_cached_time) kết hợp với đồng hồ phút, hoặc gọi một lần copy_rates_from_pos(..., 0, 1) chỉ để đọc time nến hiện tại — rẻ hơn kéo 500 cây. Một số team chấp nhận “mỗi vòng peek 1 nến”; vẫn tốt hơn kéo full window.
Quan trọng: Strategy không import MetaTrader5. Chỉ adapter/fetcher được phép nói chuyện với Terminal. Nhờ đó:
- Unit test dùng fake feed
- Đổi nguồn (MT5 → CSV backtest) không đụng logic Surplus
- Khi Terminal down, exception tập trung ở biên, Guard/FSM xử lý fail-closed
Đây cũng là tinh thần khóa Python Hedging: tách I/O khỏi quyết định.
14. Pandas trong Buổi 2 — dùng đúng liều
Có học viên hỏi: “Buổi 2 ghi Pandas/NumPy nhưng hot path bảo dùng NumPy, vậy Pandas để làm gì?”
Pandas đóng ba việc ngay trong module Cache:
- Chuẩn hóa rates MT5 thành bảng có tên cột rõ (
time/open/high/low/close) - Debug trên lớp / notebook:
df.tail(), lọc session, xuất CSV khi giảng viên soi vì sao ATR nhảy - Cầu nối Buổi 11: schema OHLC quen thuộc khi backtest scale lớn
Không dùng Pandas để:
- Lưu positions realtime
df.apply(axis=1)tính ATR từng dòng trong loop 10ms- Chia sẻ một DataFrame mutable cho nhiều thread ghi
Một mẹo giảng dạy hiệu quả: trên lớp demo, in df.tail(3) bằng Pandas cho học viên “thấy”, rồi ngay lập tức lấy close.to_numpy() trước khi tính chỉ báo. Học viên nhớ được ranh giới bằng mắt, không chỉ bằng lý thuyết.
15. Đồng bộ thời gian và look-ahead — lỗi ẩn khi có cache
Cache làm tăng nguy cơ look-ahead nếu bạn không kỷ luật timestamp:
- Dùng nến đang chạy (incomplete bar) để tính tín hiệu như thể bar đã đóng
- Backtest lấy
close[i]nhưng vô tình kèmhigh[i]tương lai trong cùng điều kiện vào lệnh - Cache trả cửa sổ có bar cuối chưa đóng, trong khi StepEngine giả định bar đã chốt
Quy ước gợi ý cho khóa:
- Tín hiệu Step/filter dựa trên bar đã đóng dùng
iloc[:-1]hoặcnsao cho phần tử cuối là closed bar - Tick realtime chỉ dùng cho giá vào lệnh / spread / Guard tức thời
- Ghi rõ trong config:
use_forming_bar: bool— mặc địnhFalsevới học viên mới
Cache không tạo look-ahead một mình; cách bạn chọn phần tử cuối mới tạo. Hãy viết điều đó thành comment trong code, không để thành truyền miệng.
16. Bridge sang các buổi sau
| Buổi | Cache/State đóng góp gì |
|---|---|
| 3 · Magic & Comment | State lọc đúng cụm; cache vẫn theo symbol |
| 4 · Step & X-Level | ATR/volatility từ cache → khoảng step |
| 5 · Surplus & Hedge | Cluster (State) + giá (Cache) cùng snapshot logic |
| 6 · Multi-thread | RLock / snapshot read |
| 9 · Guard | Từ chối khi tick/OHLC stale hoặc spread vượt ngưỡng |
| 11 · Backtest | Cùng schema OHLC, scale Pandas/NumPy |
Nếu bạn đọc bài Global State trước đó trên blog HNDL, hãy xem Buổi 2 là lớp kế bên — không thay thế schema vị thế, mà nuôi schema đó bằng thị trường đủ tươi và đủ rẻ.
Cũng cần lưu ý bản đồ buổi trên một số bài cũ có thể gọi “Buổi 2 = Global State + Pandas”. Với folder khóa 082026 đang soạn, Buổi 1 là Global State, Buổi 2 là Cache & Pandas/NumPy. Khi đăng web, title và đoạn mở nên khớp folder để học viên không lạc mục lục.
17. FAQ ngắn
Bot tôi chỉ 1 symbol, có cần MarketCache không? Vẫn nên. Không phải vì “đẹp kiến trúc”, mà vì tách fetcher giúp test Strategy bằng fake feed và đo hit/miss khi lên VPS. Một symbol nhưng loop dày vẫn đủ để copy_rates trở thành nút thắt.
Pandas chậm hơn NumPy — có nên cấm Pandas trong bot? Không cấm. Cấm nhầm vai trò: Pandas cho bảng/phân tích/backtest; NumPy cho đệm nóng. Đừng sửa DataFrame chung từ nhiều thread.
TTL bao nhiêu là đúng? Không có một số thần kỳ. Bắt đầu từ “một lần mỗi loop” hoặc 50–200ms cho tick hedging dày, đo latency và tần suất miss, rồi chỉnh. TTL phải khớp nhịp quyết định, không khớp cảm giác.
Mất kết nối MT5 thì cache có được dùng tiếp không? Được để hiển thị/quan sát; không được để mở risk mới nếu Guard yêu cầu dữ liệu tươi. Fail-closed an toàn hơn fail-open “còn giá cũ vẫn trade”.
Bài này có phải QuantTrade / Machine Learning không? Đây là nền quant engineering: dữ liệu, cache, kỷ luật vòng lặp. ML nằm ở lộ trình khác hoặc phần sau; không nhét model vào chỗ chưa ổn cache/state.
Vì sao bài tập không bắt buộc cài MetaTrader5? Vì mục tiêu Buổi 2 là hợp đồng Cache (hit/miss/TTL/bar), không phải cài Terminal. Inject fetcher giúp chấm bài deterministic. MT5 gắn ở adapter khi vào thực chiến / VPS.
OHLC timeframe nào nên cache trước? Với hedging grid kiểu khóa này, thường ưu tiên timeframe dùng tính Step/filter (hay gặp M1/M5). Đừng cache mười timeframe “cho chắc” ngay từ đầu — mỗi key thêm một nguồn stale và miss.
Copy DataFrame mỗi lần get có lãng phí không? Có chi phí, nhưng đổi lại caller không sửa nhầm buffer nội bộ cache. Khi profile cho thấy đây là hot spot, có thể trả ndarray read-only hoặc snapshot bất biến. Học viên Buổi 2 ưu tiên đúng đắn trước tốc độ micro.
18. Kết & CTA
Bot hedging Python bền không bắt đầu từ tín hiệu “mua/bán đẹp trên chart”. Nó bắt đầu từ việc mỗi vòng loop biết mình đang đọc State đúng và Cache đủ tươi với chi phí đúng. Buổi 2 khóa Python Hedging tại Hướng Nghiệp Dữ Liệu chốt đúng mảnh đó: MarketCache, TTL, invalidate theo bar, NumPy trên hot path, Pandas đúng chỗ.
Khi lớp Cache ổn, Step/X-Level và Surplus/Hedge mới có chỗ đứng sạch sẽ — không còn bị che bởi việc gọi copy_rates như một thói quen.
Tìm hiểu lộ trình 12 buổi và điều kiện đầu vào tại trang khóa Lập trình Python nâng cao Hedging tự động hóa trên huongnghiepdulieu.com. Học để vận hành có kiểm soát; mọi quyết định vốn là trách nhiệm của bạn.
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Đặ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.