Bài viết gần đây
Trang chủ → Bài Viết → Trailing Plow 3 mốc Act/Ret trong PyNhiQuaiBot: thu quân theo Step và mở lại có buffer
| Trailing Plow 3 mốc Act/Ret trong PyNhiQuaiBot: thu quân theo Step và mở lại có buffer
Được viết bởi thanhdt vào ngày 12/07/2026 lúc 17:49 | 8 lượt xem
Trailing stop thường bám theo giá bằng một khoảng cố định. Trailing plow Python Bot trong Nhi Quái V6 dùng một cách nhìn khác: mỗi lệnh thuộc một Step có ba mốc kích hoạt Act1, Act2, Act3; khi giá đã đi thuận tới mốc cao nhất rồi hồi về ngưỡng Ret tương ứng, bot cân nhắc “thu quân” position đó nếu lợi nhuận ròng vẫn đạt MinProfit. Sau khi đóng, state được ghi theo Step để tránh bot mở lại ngay; nếu EnableReopen bật, state chỉ được xóa khi giá đã rời Step một khoảng ReopenBuffer.
Cơ chế này kết hợp ba bài toán: đo hành trình giá tương đối với step_price, lưu mốc tiến triển bền vững và kiểm soát vòng đời mở lại. Nếu chỉ dịch nó thành vài câu if, người phát triển dễ bỏ qua race condition, state stale, tính bất đối xứng Bid/Ask hoặc trường hợp giá nhảy qua nhiều mốc trong một tick. Bài viết sẽ phân tích đúng logic đang có trong PyNhiQuaiBot, đồng thời chỉ ra các cải tiến cần thiết khi phát triển Python Bot Auto Trading.
Đây là tài liệu kỹ thuật. Trailing không bảo đảm chốt được lợi nhuận tại một con số cụ thể, không loại bỏ slippage và không biến chiến lược grid/hedge thành hệ thống chắc thắng.
1. “Plow” và “thu quân” nên được hiểu thế nào?
Trong ngữ cảnh Nhi Quái V6, mỗi tầng giá giống một vị trí đã rải quân. Khi position đi thuận đủ xa, bot không đóng ngay tại mốc đầu tiên mà ghi nhận rằng lệnh đã đạt một stage. Nếu giá tiếp tục đi thuận, stage được nâng. Khi giá hồi về mức bảo vệ của stage cao nhất, bot đóng position nếu net P/L đủ.
Mục tiêu của cơ chế:
- cho position một khoảng chạy thuận;
- bảo vệ một phần kết quả khi giá hồi;
- tăng mức bảo vệ theo hành trình;
- tránh chốt một ticket đang dương rất nhỏ nhưng chưa bù phí;
- đánh dấu tầng đã thu để không tái nhập tức thì;
- cho phép tái sử dụng Step sau khi giá rời vùng đủ xa.
Tên “plow” chỉ là cách mô tả chiến thuật, không phải thuật ngữ chuẩn của thị trường. Về kỹ thuật, đây là một finite state machine nhỏ theo từng Step.
2. Sáu tham số Act/Ret và ý nghĩa phần trăm Step
Cấu hình mặc định của dự án:
Act1 = 50% Ret1 = 10%
Act2 = 70% Ret2 = 20%
Act3 = 90% Ret3 = 30%
Các tỷ lệ không tính theo giá tuyệt đối hay lợi nhuận phần trăm. Chúng được nhân với khoảng cách Step.
Với Buy:
act_price_n = step_price + ActN/100 * BuyStep
ret_price_n = step_price + RetN/100 * BuyStep
Với Sell:
act_price_n = step_price - ActN/100 * SellStep
ret_price_n = step_price - RetN/100 * SellStep
Ví dụ minh họa cụm Buy có step_price = 100.000 và BuyStep = 50:
- Act1 tại 100.025; Ret1 tại 100.005;
- Act2 tại 100.035; Ret2 tại 100.010;
- Act3 tại 100.045; Ret3 tại 100.015.
Nếu Bid tăng tới 100.038, stage lên 2. Khi Bid sau đó giảm về hoặc dưới 100.010, điều kiện exit stage 2 xuất hiện. Bot vẫn kiểm tra net >= MinProfit trước khi gửi close.
Các con số chỉ minh họa phép tính. Symbol, digits, tick size, spread và đặc điểm contract quyết định mức giá hợp lệ thực tế.
Vì sao Ret tăng theo stage?
Stage cao hơn nghĩa là giá từng đi thuận xa hơn. Ret3 lớn hơn Ret2, và Ret2 lớn hơn Ret1, nên mức thoát nằm xa step_price hơn, theo hướng có lợi hơn. Cơ chế cố gắng giữ lại nhiều tiến triển hơn khi position từng đạt mốc cao.
Điều kiện cấu hình hợp lý thường là:
0 <= Ret1 < Act1
0 <= Ret2 < Act2
0 <= Ret3 < Act3
Act1 < Act2 < Act3
Ret1 <= Ret2 <= Ret3
Mã cần validate. Nếu Ret2 > Act2, position có thể kích hoạt stage rồi gần như thỏa exit ngay. Nếu Act không tăng, stage semantics trở nên khó giải thích.
3. Step price được tính từ P0
Mỗi cụm có giá gốc P0 và khoảng Step riêng. Với chỉ số tầng s:
step_price = P0 + s * step_size
Nhi Quái V6 dùng cùng công thức để xác định vùng của cả Buy và Sell trong một Magic. Comment position chứa hậu tố Step, ví dụ _B_S12 hoặc _S_S12, để bot khôi phục lệnh thuộc tầng nào.
Trailing plow không bám theo giá entry thực tế của ticket mà bám theo step_price lý thuyết. Hai giá thường gần nhau nhưng có thể khác do spread, slippage hoặc order khớp muộn. Vì vậy MinProfit là guard thứ hai: dù giá đã chạm Ret theo lưới, bot chỉ đóng nếu position net đủ.
Đây là lựa chọn thiết kế có ưu và nhược:
- ưu điểm: quy tắc nhất quán với grid và dễ tái dựng từ P0/Step;
- nhược điểm: mức Act/Ret có thể lệch đáng kể so với entry thật trong thị trường trượt mạnh.
Một phiên bản nâng cao có thể lưu cả step_price và fill_price, rồi chọn reference theo đặc tả. Quan trọng là không trộn hai reference mà không ghi rõ.
4. State PLOW_STAGE theo từng tầng
Mỗi Step có key:
<prefix>_PLOW_STAGE_<s>
Giá trị 0 hoặc không tồn tại nghĩa chưa kích hoạt. 1, 2, 3 tương ứng stage cao nhất đã đạt. Prefix bao gồm bot, symbol, login và Magic, giúp cô lập state giữa cụm.
Trong vòng xử lý Buy:
if stage < 1 and bid >= Act1Price: stage = 1
if stage < 2 and bid >= Act2Price: stage = 2
if stage < 3 and bid >= Act3Price: stage = 3
Ba câu if độc lập, không dùng elif. Vì vậy nếu giá gap từ dưới Act1 lên trên Act3 trong một tick, stage được nâng lần lượt tới 3 ngay trong cùng vòng. Đây là hành vi đúng nếu mong muốn ghi nhận mốc cao nhất đã đi qua theo snapshot.
Sau đó exit được chọn theo stage cuối:
stage 1 -> hồi về Ret1
stage 2 -> hồi về Ret2
stage 3 -> hồi về Ret3
State không bị hạ từ 3 xuống 2 khi giá giảm. Nó lưu “high-water mark” rời rạc. Chỉ khi position đóng thành công, key stage được xóa.
Buy dùng Bid, Sell dùng Ask
Buy position đóng bằng Sell ở Bid, nên đánh giá trailing Buy trên Bid phù hợp với giá có thể thoát. Sell position đóng bằng Buy ở Ask, nên đánh giá trailing Sell trên Ask. Nếu dùng Mid hoặc chart Last, trigger có thể xuất hiện dù giá executable chưa chạm.
Đây là chi tiết nhỏ nhưng quan trọng của Python Bot Auto Trading: tín hiệu exit nên dựa trên phía quote dùng để đóng.
5. Luồng trailing plow cho Buy
Giả sử tại Step s có ít nhất một Buy:
- đọc stage hiện tại từ GVStore;
- so Bid với ba ngưỡng Act;
- lưu stage cao nhất;
- so Bid với Ret của stage;
- nếu hồi đủ, tìm ticket Buy tại Step;
- đọc position theo ticket;
- tính
profit + swap + commission; - nếu net đạt
min_profit, gửi close; - khi close thành công, đặt
_PLOW_CLOSED_s = 1; - xóa
_PLOW_STAGE_s; - cập nhật count/cache và dừng vòng nếu state thay đổi.
Điểm số 8 ngăn bot đóng ticket vẫn âm do spread hoặc entry lệch. Nhưng nếu giá đã hồi qua Ret và net chưa đạt MinProfit, bot không đóng. Stage vẫn còn. Ở tick sau, nếu net tăng đủ trong khi điều kiện Ret vẫn đúng, bot mới đóng.
Điều này có thể tạo position “kẹt stage”: giá đã hồi sâu, net không bao giờ đạt MinProfit, state tiếp tục chờ. Chính sách cần xác định đây là hành vi mong muốn hay phải có timeout/stop khác. Trailing profit không nên được kỳ vọng thay cho risk stop.
6. Luồng trailing plow cho Sell
Sell là ảnh gương:
- giá đi thuận khi Ask giảm;
- stage 1 khi Ask nhỏ hơn hoặc bằng
step_price - Act1 * SellStep; - stage tăng khi giá giảm sâu hơn;
- exit khi Ask hồi tăng lên ngưỡng
step_price - RetN * SellStep; - net phải đạt MinProfit;
- đóng thành công thì ghi PLOW_CLOSED và xóa stage.
Sai dấu là lỗi rất phổ biến khi port:
Buy activation: >= plus Act
Buy return: <= plus Ret
Sell activation: <= minus Act
Sell return: >= minus Ret
Nên viết unit test đối xứng cho hai side thay vì copy rồi thay dấu bằng mắt. Có thể tạo hàm chuẩn hóa “favorable distance”:
favorable_distance =
bid - step_price for Buy
step_price - ask for Sell
Khi đó activation là distance >= ActDistance; return cần state cùng peak/history, nhưng biểu diễn thống nhất giúp giảm lỗi.
7. PLOW_CLOSED ngăn mở lại ngay
Sau khi trailing đóng position ở Step s, bot ghi:
<prefix>_PLOW_CLOSED_<s> = 1
Khi quyết định có mở lệnh tại tầng, mã tính:
can_open =
not X_CLOSED_s
and not PLOW_CLOSED_s
Nếu Step đã được chốt bằng X-Level hoặc trailing plow, bot không tái tạo ngay position chỉ vì s == cur_s. Đây là cơ chế idempotency nghiệp vụ: hành động đóng phải để lại dấu, nếu không core grid thấy tầng thiếu lệnh và mở bù ngay tick kế tiếp.
State PLOW_STAGE trả lời “position đang ở mốc trailing nào”. State PLOW_CLOSED trả lời “tầng đã được thu và chưa được phép tái dùng”. Hai key có vòng đời khác nhau.
8. EnableReopen và ReopenBuffer
Nếu enable_reopen bật, trước khi đếm lệnh ở Step, bot kiểm tra:
abs(current_price - step_price)
> ReopenBuffer/100 * step_size
Khi đúng, bot xóa cả X_CLOSED_s và PLOW_CLOSED_s. Tầng lại có thể mở khi các điều kiện direction, current Step, bias và FSM cho phép.
Với ReopenBuffer = 50 và Step = 100, khoảng yêu cầu là 50 đơn vị giá. Giá phải cách step_price hơn 50 theo bất kỳ hướng nào.
Ý tưởng là tạo “vùng giải phóng”: sau khi chốt, giá phải rời khỏi vùng tầng đủ xa trước khi bot quên dấu đã chốt. Nếu không có buffer, một dao động nhỏ quanh Step có thể gây chuỗi đóng-mở-đóng, tiêu tốn spread và commission.
Dùng trị tuyệt đối có hệ quả gì?
abs() cho phép clear state khi giá đi xa theo hướng thuận hoặc nghịch. Ví dụ Buy vừa chốt; giá giảm sâu hơn nửa Step cũng xóa dấu. Sau này khi quay lại đúng Step, tầng có thể được sử dụng lại.
Đây có thể là thiết kế mong muốn vì mục tiêu chỉ là yêu cầu giá “rời vùng”. Nhưng nếu muốn bắt buộc một hành trình cụ thể, cần state direction:
- sau Buy close, chỉ rearm khi giá đi xuống dưới vùng rồi quay lại;
- hoặc chỉ rearm khi current Step thay đổi;
- hoặc yêu cầu crossing event thay vì snapshot distance.
Snapshot abs(distance) > buffer không chứng minh giá đã đi ra rồi quay lại; nó chỉ nói tại thời điểm hiện tại đang xa.
9. Tương tác giữa Act/Ret và MinProfit
Act/Ret là điều kiện giá, còn MinProfit là điều kiện kinh tế. Cả hai phải đúng mới close. Điều này bảo vệ khỏi trường hợp:
- spread lớn làm position chưa net dương;
- commission ăn hết biên;
- fill price lệch step_price;
- swap âm;
- tick value khác giả định.
Nhưng MinProfit cố định cũng có giới hạn. Một ticket 0,01 lot và một ticket 1 lot cùng yêu cầu 10 USD tạo tỷ lệ khác nhau. Có thể dùng:
- profit tối thiểu theo lot;
- profit theo risk unit;
- net return theo margin;
- hoặc max giữa floor cố định và chi phí ước lượng.
Không nên tối ưu MinProfit chỉ để backtest đẹp. Nó thay đổi tần suất exit và thời gian giữ lệnh, có thể làm exposure tích tụ.
Khi trigger Ret nhưng close thất bại
Nếu close_ticket bị reject, không nên đặt PLOW_CLOSED. Stage phải giữ để retry có kiểm soát. Mã hiện tại chỉ ghi closed sau khi close trả true, đây là nguyên tắc đúng.
Tuy nhiên, cần phân loại lỗi. Retry mỗi 250 ms trong market closed hoặc invalid filling sẽ spam. Nên có cooldown, max retries và trạng thái CLOSE_PENDING.
10. Stage dùng chung cho Buy và Sell tại cùng Step
Trong mã hiện tại, key PLOW_STAGE_s không chứa side. Nếu cùng Step có cả Buy và Sell, hai nhánh có thể đọc/ghi chung stage. Tại S0, hệ thống có thể tạo 1 Buy + 1 Sell. Đây là điểm kiến trúc cần chú ý.
Ví dụ Buy đi thuận và đặt stage 2. Sau đó nhánh Sell đọc stage 2 dù Sell chưa từng đi thuận đến Act2. Nếu Ask hồi theo điều kiện Sell Ret2 và net Sell đủ, logic có thể đánh giá sai mốc.
Giải pháp là namespace theo side:
_PLOW_STAGE_<s>_B
_PLOW_STAGE_<s>_S
Tương tự, cân nhắc PLOW_CLOSED theo side nếu một Step có thể giữ hai phía độc lập. Nếu đặc tả cố ý coi Step là một đơn vị duy nhất, phải bảo đảm không xử lý trailing cả Buy lẫn Sell bằng cùng stage. Bất biến này cần được viết thành test.
Đây là ví dụ cho lợi ích của review khi port: code chạy được chưa đồng nghĩa state key có đủ chiều định danh.
11. Cache position và FSM chống lệnh trùng
PyNhiQuaiBot tạo cache theo Step với b_cnt, s_cnt, ticket Buy/Sell. count_at_step() còn cộng một count ảo nếu FSM của side đang BUSY, nhằm ngăn vòng lặp gửi thêm lệnh trong khi request trước chưa hoàn tất.
Sau trailing close:
- count side giảm;
changed = True;- vòng Step dừng để snapshot được dựng lại ở cycle kế tiếp.
Đây là cách tránh tiếp tục hành động trên cache vừa lỗi thời. Một nguyên tắc chung: sau khi gửi lệnh làm thay đổi positions, không nên dùng phần còn lại của snapshot như thể chưa có gì xảy ra.
Dù vậy, close và open cần correlation với broker. FSM timeout 5 giây không đủ nếu order response mất nhưng broker đã khớp. Reconcile positions phải diễn ra trước retry.
12. Trường hợp giá gap qua Act và Ret
Polling không quan sát mọi giá trung gian. Giả sử tick trước dưới Act1, tick sau giá đã vượt Act3; stage lên 3. Tick kế tiếp gap xuống dưới Ret1. Vì stage 3 và điều kiện Buy exit là bid <= Ret3, nó sẽ trigger. Hệ thống không biết đỉnh thật hoặc đường đi trong khoảng.
Nếu cùng một snapshot vừa vượt Act3 thì không thể đồng thời nhỏ hơn Ret3 với cấu hình chuẩn, nên không activate và exit trong chính một tick. Nhưng feed out-of-order hoặc cấu hình Ret > Act có thể phá giả định.
Backtest OHLC càng khó: một cây nến có High vượt Act3 và Low dưới Ret3 nhưng không biết thứ tự. Mô phỏng phải chọn quy tắc bảo thủ hoặc dùng tick data. Nếu backtest giả định High xảy ra trước Low, kết quả có thể quá lạc quan.
13. Kiểm thử unit cho ba mốc
Buy stage progression
Với Step = 100, step_price = 1.000:
- Bid 1.049 → stage 0;
- Bid 1.050 → stage 1;
- Bid 1.069 → stage 1;
- Bid 1.070 → stage 2;
- Bid 1.090 → stage 3.
Sau mỗi bước, xác minh key persistence.
Buy return
- stage 1, Bid 1.011 → chưa exit; Bid 1.010 → exit;
- stage 2, Bid 1.021 → chưa exit; Bid 1.020 → exit;
- stage 3, Bid 1.031 → chưa exit; Bid 1.030 → exit.
Điều kiện dùng <=, nên đúng biên phải trigger.
Sell đối xứng
Với step_price = 1.000:
- Ask 0.950 → stage 1;
- Ask 0.930 → stage 2;
- Ask 0.910 → stage 3;
- stage 3, Ask 0.970 → exit theo Ret3.
Kiểm tra dấu và đúng quote Ask.
Nhảy mốc
Từ stage 0 trực tiếp Bid 1.095 phải thành stage 3, không dừng ở 1.
MinProfit
Exit price đúng nhưng net 9,99 với MinProfit 10 → không close. Net đúng 10 → close. Sau thành công có PLOW_CLOSED và không còn stage.
Close reject
Mock broker trả lỗi. Stage còn nguyên, PLOW_CLOSED không được tạo, count không giảm.
14. Kiểm thử EnableReopen
Với ReopenBuffer = 50%, Step = 100:
- distance 49,99 → giữ closed;
- distance 50,00 → vẫn giữ vì code dùng
>; - distance 50,01 → xóa closed;
enable_reopen = False→ không xóa ở mọi distance.
Sau rearm, xác minh bot chưa mở ngay nếu Step đó không phải cur_s. Khi giá quay lại, còn phải qua:
- direction logic;
- bias filter;
- position count;
- FSM ready;
- max spread;
- cluster không frozen;
- time window và các guard khác.
Reopen chỉ gỡ một rào cản, không phải lệnh mở.
Test vòng lặp chống churn
Mô phỏng giá dao động quanh step_price mà không vượt buffer. Số lần open phải không tăng sau close. Sau khi vượt buffer và quay lại, chỉ một open được phép. Đây là bài test hành vi quan trọng hơn test từng hàm.
15. Validate cấu hình trước khi chạy
on_init() nên từ chối hoặc cảnh báo cấu hình:
- Step <= 0;
- Act ngoài 0–100 nếu đặc tả chỉ cho trong một Step;
- Ret âm;
- Ret >= Act;
- Act không tăng;
- Ret không tăng;
- ReopenBuffer <= 0 hoặc quá nhỏ so với spread;
- MinProfit không đủ chi phí dự kiến;
- Buy/Sell dùng state key không tách side;
- tick size làm các mốc rơi vào giá không hợp lệ.
Act có thể lớn hơn 100 nếu muốn mốc vượt một Step, nhưng phải là lựa chọn có chủ đích. Không nên áp giới hạn chỉ vì tên %; validate theo đặc tả.
Mức giá Act/Ret phải normalize theo tick size:
normalized = round_to_tick(raw_price, tick_size)
So sánh float trực tiếp có thể sai ở biên do biểu diễn nhị phân. Có thể so với epsilon theo tick size hoặc chuyển về integer ticks.
16. Persistence và restart giữa một trailing
Nếu process restart sau khi position đã đạt Act3 nhưng trước khi hồi, JSON PLOW_STAGE_s = 3 giúp bot tiếp tục bảo vệ theo Ret3. Nếu không lưu, stage trở về 0 và toàn bộ high-water mark bị mất.
Nhưng JSON có thể stale hoặc hỏng. Khi startup:
- đọc positions thật;
- xác định Step và side;
- loại bỏ stage cho position không còn;
- cảnh báo stage có nhưng không tìm thấy ticket;
- giữ stage hợp lệ;
- không tự suy ra stage từ giá hiện tại nếu không biết lịch sử đỉnh.
Không thể khôi phục chính xác high-water mark chỉ từ snapshot giá hiện tại. Nếu persistence mất, chính sách an toàn có thể chuyển position sang quản lý thủ công hoặc một trailing fallback bảo thủ.
State nên ghi atomic. Nếu mỗi tick ghi toàn bộ JSON, cần cân nhắc hiệu năng và wear; có thể journal event rồi snapshot định kỳ, nhưng mọi tối ưu phải giữ durability cho chuyển stage quan trọng.
17. Observability cho trailing plow
Dashboard nên cho biết:
- Step và side;
- ticket, fill price, step_price;
- stage hiện tại;
- giá Act kế tiếp;
- Ret đang bảo vệ;
- net P/L và MinProfit;
- trạng thái OPEN/TRAILING/CLOSE_PENDING/CLOSED_COOLDOWN/REARMED;
- khoảng cách còn lại tới ReopenBuffer;
- thời gian ở stage;
- số lần close retry;
- cache/state age.
Log chuyển stage chỉ phát khi state thay đổi:
event=PLOW_STAGE_CHANGED
magic=6111
step=4
side=BUY
from=1
to=2
bid=...
act_price=...
Khi close, log trigger price, request price, fill price và actual net. Nhờ vậy người vận hành đo slippage thay vì tin mức Ret là giá khớp.
Khi rearm, log lý do và distance. Nếu hàng trăm Step cùng rearm do một lỗi P0, metrics sẽ phát hiện spike.
18. Tối ưu tham số mà không overfit
Act/Ret tạo nhiều tổ hợp. Tối ưu trên một giai đoạn dễ chọn bộ tham số chỉ phù hợp đường giá đã biết. Quy trình tốt hơn:
- chia train, validation và out-of-sample theo thời gian;
- dùng walk-forward;
- mô phỏng spread, commission, swap, slippage;
- kiểm tra nhiều regime: trend, range, gap, volatility cao;
- đánh giá không chỉ lợi nhuận mà còn turnover, max exposure, thời gian giữ, số reopen;
- stress test tham số lân cận.
Nếu chỉ một điểm Act/Ret cho kết quả đẹp còn giá trị gần đó tệ mạnh, hệ thống có thể overfit. Một vùng tham số ổn định đáng tin hơn cực đại sắc nhọn, dù vẫn không bảo đảm tương lai.
ReopenBuffer cũng cần đánh giá bằng chi phí churn. Buffer nhỏ tăng tần suất tái nhập; buffer lớn giảm tái sử dụng Step và có thể bỏ cơ hội theo đặc tả. Không có giá trị mặc định tốt cho mọi symbol.
19. Trailing plow không phải stop loss
Cơ chế chỉ close khi:
- position đã đạt một Act;
- giá hồi về Ret;
- net vẫn >= MinProfit.
Nếu giá đi ngược ngay từ đầu và không chạm Act1, trailing không làm gì. Nếu đã chạm Act nhưng gap khiến net âm tại Ret, MinProfit chặn close. Vì vậy module này không giới hạn thua lỗ phía bất lợi.
Hệ thống vẫn cần:
- max loss/cycle;
- max positions và gross exposure;
- equity lockdown;
- spread guard;
- free margin guard;
- time stop;
- emergency kill switch;
- quy trình xử lý mất kết nối.
Gọi trailing là “bảo vệ lợi nhuận có điều kiện” chính xác hơn gọi nó là bảo vệ tài khoản toàn diện.
20. Đề xuất refactor state theo side
Một model dữ liệu rõ ràng:
@dataclass
class PlowState:
cycle_id: str
magic: int
step: int
side: str
ticket: int
stage: int
status: str
activated_at: float | None
closed_at: float | None
rearm_reference: float | None
Key identity là (cycle_id, magic, step, side). Ticket được lưu để không vô tình áp stage cũ cho position mới cùng Step. Status diễn tả workflow tốt hơn nhiều boolean.
Khi ticket đổi, stage cũ không được kế thừa trừ khi có migration có chủ đích. Khi reset cycle, toàn bộ state cycle cũ được archive thay vì chỉ xóa, giúp audit.
GVStore JSON có thể vẫn dùng, nhưng schema nên versioned và serialize object. Nếu quy mô lớn, SQLite với transaction hoặc event journal sẽ an toàn hơn.
21. Checklist triển khai trailing plow
Trước khi chạy demo:
- xác nhận Act/Ret tính theo Step, không phải phần trăm giá;
- validate thứ tự và quan hệ Ret < Act;
- dùng Bid cho Buy, Ask cho Sell;
- normalize theo tick size;
- Step parser đọc đúng comment;
- P0 và cycle ID nhất quán;
- stage tách theo side nếu cùng Step có hai phía;
- close chỉ đánh dấu thành công sau broker confirmation;
- net gồm profit, swap, commission;
- MinProfit phù hợp volume và chi phí;
- PLOW_CLOSED chặn mở bù tức thì;
- ReopenBuffer dùng đúng điều kiện biên;
- rearm không tự động đồng nghĩa mở;
- restart giữ stage đúng ticket;
- lỗi JSON dẫn tới fail-safe;
- backtest dùng tick hoặc giả định intrabar bảo thủ;
- có guard rủi ro độc lập với trailing.
22. Bài học cho Python Bot Auto Trading
Trailing plow cho thấy chiến thuật theo giá luôn cần state lịch sử. Snapshot hiện tại không nói position từng đạt Act3 hay chưa. Do đó persistence là một phần của thuật toán, không chỉ tiện ích.
Bài học thứ hai là state phải có đủ chiều định danh. Step không đủ nếu có Buy và Sell; cần side, ticket và cycle. Thiếu một chiều có thể tạo lỗi logic rất khó thấy trong backtest đơn giản.
Bài học thứ ba là hành động close phải để lại dấu nghiệp vụ. Nếu không có PLOW_CLOSED, core grid sẽ “sửa” sự thiếu position bằng cách mở lại. Các module cần chia sẻ semantics thay vì hoạt động cô lập.
Bài học thứ tư là reopen cần hysteresis. Buffer tạo khoảng cách giữa close và khả năng tái nhập, giảm rung lắc state. Nhưng hysteresis phải được kiểm thử theo hành trình, không chỉ snapshot.
Kết luận
Trailing plow ba mốc của PyNhiQuaiBot nâng stage khi giá đi thuận tới Act1/Act2/Act3, rồi đóng position khi giá hồi về Ret1/Ret2/Ret3 tương ứng và net P/L đạt MinProfit. Sau close, PLOW_CLOSED ngăn grid mở bù. EnableReopen cùng ReopenBuffer cho phép tầng được tái sử dụng sau khi giá rời vùng đủ xa.
Thiết kế có giá trị học tập lớn vì kết nối Step, Bid/Ask, state persistence, execution và idempotency. Đồng thời, người phát triển cần xử lý state theo side, close failure, restart, float precision và cấu hình sai. Trailing chỉ quản lý thoát có điều kiện; nó không thay thế stop loss, equity guard hay giới hạn exposure.
Để thực hành xây FSM theo Step, trailing plow, Basket TP và persistence cho PyNhiQuaiBot, bạn có thể xem Lập trình Python nâng cao Hedging tự động hóa của Hướng Nghiệp Dữ Liệu. Khóa học tập trung vào năng lực thiết kế và kiểm thử Python Bot Auto Trading, không đưa ra cam kết lợi nhuận hoặc lời khuyên đầu tư cá nhân.
📌 Khóa Lập trình Python nâng cao Hedging (PyNhiQuaiBot)
Xem lộ trình & đăng ký · Zalo 0934.145.100 · Hướng Nghiệp Dữ Liệu
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.
admin
Biên tập viên, Hướng Nghiệp Dữ LiệuBiên tập viên nội dung tại Hướng Nghiệp Dữ Liệu, phụ trách tổng hợp và biên soạn các bài viết về lập trình Python, dữ liệu và công nghệ.