Chiến lược & backtestStrategy & backtests
Chuỗi multiplier DCA — Persistent vs LegacyDCA multiplier chain — Persistent vs Legacy
Hiểu hai cách EA tính số mũ lot cho lệnh DCA sau khi một phần round bị Tia hoặc partial close đóng, và khi nào nên bật InpDcaChainPersistent.
Understand the two ways the EA computes the lot exponent for DCA adds after part of a round is closed by Tia or a partial close, and when to turn on InpDcaChainPersistent.
Tính năng ra đời ở v1.35 và vẫn còn nguyên trong EA production 1.42 b7 (cùng default false). Config ổn định v1.42.b7-pass58.set để InpDcaChainPersistent=false (legacy) và tắt DCANeg (InpDCANegEnable=false), nên thực tế chỉ nhánh DCAPos dùng công thức legacy bên dưới. Số dòng code trong trang này thuộc bản v1.35 và đã lệch. Tham số: Chain persistent · Config ổn định.
The feature arrived in v1.35 and is unchanged in the production EA 1.42 b7 (same default false). The stable config v1.42.b7-pass58.set sets InpDcaChainPersistent=false (legacy) and disables DCANeg (InpDCANegEnable=false), so in practice only the DCAPos branch uses the legacy formula below. Code line numbers on this page belong to v1.35 and are stale. Parameters: Chain persistent · Stable config.
1. Bối cảnh — vấn đề người dùng phát hiện1. Context — the issue the user spotted
Ảnh chụp backtest:
InpDCAPosStartLot = 0.10InpDCAPosMultiplier = 1.5- Init R68: Buy 0.10 lot
- DCAPos R68 B#1 (add đầu tiên): 0.10 lot
Người dùng kỳ vọng: 0.10 × 1.5 = 0.15 lot cho B#1.
Nguyên nhân gốc: lệnh Init Buy của R68 đã bị Tia đóng theo cụm trước khi B#1 khớp → nBuy = 0 tại thời điểm khớp → số mũ = 0 → lot reset về StartLot.
Backtest screenshot:
InpDCAPosStartLot = 0.10InpDCAPosMultiplier = 1.5- Init R68: Buy 0.10 lot
- DCAPos R68 B#1 (first add): 0.10 lot
The user expected 0.10 × 1.5 = 0.15 lot for B#1.
Root cause: R68's Init Buy had already been closed by a Tia cluster close before B#1 fired → nBuy = 0 at fire time → exponent = 0 → the lot reset to StartLot.
2. Hai chế độ — Legacy và Persistent2. Two modes — Legacy and Persistent
Legacy (InpDcaChainPersistent = false, mặc định)Legacy (InpDcaChainPersistent = false, default)
Công thức lot:Lot formula:
lot = InpDCAPosStartLot × InpDCAPosMultiplier^nBuy
Trong đó nBuy = số lệnh Buy ĐANG MỞ trong round (đếm tại thời điểm add khớp).
Hành vi: khi Tia/partial close làm nBuy giảm → số mũ giảm → lot của add kế tiếp RESET về mức nhỏ hơn.
Here nBuy = the number of Buy positions CURRENTLY OPEN in the round (counted when the add fires).
Behavior: when Tia or a partial close lowers nBuy → the exponent drops → the next add's lot RESETS to a smaller size.
Ưu điểmPros
- Thân thiện với phục hồi: rủi ro giảm sau khi dọn cụm
- Tự bảo vệ khỏi chuỗi DCA "chạy mất kiểm soát" khi lệnh bị đóng
- Recovery-friendly: risk drops after a cluster is cleared
- Self-protects against a runaway DCA chain as positions close
Nhược điểmCons
- Nhãn comment "B#1" không nhất quán với lot
- Khi Init bị đóng, chuỗi bị "đứt" — add tiếp theo quay về lot tối thiểu
- Lot không đi theo tiến trình DCA mà người dùng mong đợi
- The "B#1" comment label does not consistently match the lot
- When Init closes the chain "breaks" — the next add is back to the minimum lot
- Lots do not follow the DCA progression the user expects
Persistent (InpDcaChainPersistent = true, v1.35+)Persistent (InpDcaChainPersistent = true, v1.35+)
Công thức lot:Lot formula:
nextBuyNum = max(scanMaxBuyNum, RoundChainGetMaxBuy(roundId)) + 1
lot = InpDCAPosStartLot × InpDCAPosMultiplier^nextBuyNum
Trong đó bộ đếm RoundChainEntry.maxBuyNum được lưu BỀN (persistent) theo từng round trong mảng toàn cục g_roundChain[]. Bộ đếm chỉ RESET khi cả round chết (không còn lệnh nào của round).
Hành vi: chuỗi tiếp tục qua mọi lần partial/Tia close. Lot khớp chính xác với số thứ tự B#N.
Here the counter RoundChainEntry.maxBuyNum is stored PERSISTENTLY per round in the global array g_roundChain[]. The counter RESETS only when the entire round dies (no positions of that round remain).
Behavior: the chain continues through every partial/Tia close. The lot matches the B#N numbering exactly.
Ưu điểmPros
- Lot và nhãn luôn nhất quán: B#1 = 0.15, B#2 = 0.225, B#3 = 0.3375...
- Tiến trình chuỗi DCA đúng ý đồ người dùng
- Khớp logic Martingale nghiêm ngặt
- Lot and label always agree: B#1 = 0.15, B#2 = 0.225, B#3 = 0.3375...
- The DCA chain progresses as the user intended
- Matches strict Martingale logic
Nhược điểmCons
- Rủi ro cao hơn khi DD kéo dài: lot không reset xuống nhỏ
- Có thể leo nhanh tới cap
InpDCAPosMaxLotnếu chuỗi dài - Kịch bản tài khoản bị kẹt: Tia đóng lệnh thắng + chuỗi tiếp tục lớn dần → exposure tích luỹ
- Higher risk in a long drawdown: lots do not reset to small sizes
- Can climb quickly to the
InpDCAPosMaxLotcap if the chain is long - Trapped-account scenario: Tia closes the winners + the chain keeps growing → exposure accumulates
3. So sánh song song — cùng một kịch bản3. Side-by-side comparison — same scenario
Thiết lập: Round R, InpDCAPosStartLot=0.10, InpDCAPosMultiplier=1.5Setup: Round R, InpDCAPosStartLot=0.10, InpDCAPosMultiplier=1.5
| Sự kiệnEvent | Legacy (false) |
Persistent (true) |
|---|---|---|
| Init Buy khớpInit Buy fires | nBuy=1 (sau khi mở), lot 0.10nBuy=1 (after), lot 0.10 | maxBuyNum=0 (Init không có B#), lot 0.10maxBuyNum=0 (Init has no B#), lot 0.10 |
| Giá lên → Buy add #1 khớpPrice up → Buy add #1 fires | nBuy=1 (trước khi khớp), exp=1 → lot 0.15nBuy=1 (before fire), exp=1 → lot 0.15 | nextBuyNum=1, exp=1 → lot 0.15 |
| Buy add #2 khớpBuy add #2 fires | nBuy=2 → lot 0.225 | nextBuyNum=2, exp=2 → lot 0.225 |
| Tia đóng Init BuyTia closes Init Buy | nBuy=2 (B#1+B#2 còn mở)nBuy=2 (B#1+B#2 alive) | maxBuyNum=2 (lưu bền)maxBuyNum=2 persistent |
| Buy add #3 khớpBuy add #3 fires | nBuy=2 → lot 0.3375 | nextBuyNum=3, exp=3 → lot 0.3375 giống nhausame |
| Tia đóng B#1, B#2Tia closes B#1, B#2 (đã đóng hết Buy)(all Buys closed) | nBuy=0 | maxBuyNum=2 (vẫn lưu — round còn sống nhờ phía bên kia?)maxBuyNum=2 (still persistent — round alive due to the other side?) |
| Round vẫn sống (còn Sell)Round still alive (Sells exist) | — | maxBuyNum=2 |
| Buy add #4 khớpBuy add #4 fires | nBuy=0 → lot 0.10 RESET | nextBuyNum=3, exp=3 → lot 0.3375 tiếp tụccontinues |
| Mọi lệnh của round đã đóng (round chết)All round members closed (round dies) | — | RoundChainCleanup xoá entryRoundChainCleanup removes the entry |
| Round mới R' initNew round R' init | nBuy=1, lot 0.10 | maxBuyNum=0 (mới), lot 0.10maxBuyNum=0 fresh, lot 0.10 |
4. Chi tiết triển khai4. Implementation details
Cấu trúc dữ liệuData structure
struct RoundChainEntry {
int roundId;
int maxBuyNum; // highest B#N ever assigned in round
int maxSellNum; // highest S#N ever assigned in round
};
RoundChainEntry g_roundChain[];
int g_roundChainCount = 0;
Hàm hỗ trợHelper functions
| Function | Mục đíchPurpose |
|---|---|
RoundChainGetMaxBuy(roundId) | Tra maxBuyNum đã lưu của round (trả 0 nếu chưa tồn tại)Look up the persistent maxBuyNum for the round (returns 0 if absent) |
RoundChainGetMaxSell(roundId) | Tương tự cho phía SellSame for the Sell side |
RoundChainSetMaxBuy(roundId, n) | Cập nhật bộ đếm (chỉ tăng, không bao giờ giảm)Update the counter (only increases, never decreases) |
RoundChainSetMaxSell(roundId, n) | Tương tự cho SellSame for Sell |
RoundChainCleanup() | Xoá entry của các round không còn lệnh nào (round chết)Remove entries of rounds with no positions left (round dies) |
Logic dọn dẹpCleanup logic
RoundChainCleanup() được gắn vào chu kỳ dọn dẹp 30 giây cùng với TicketMapCleanup():RoundChainCleanup() is hooked into the 30-second cleanup cycle alongside TicketMapCleanup():
if (TimeCurrent() - lastMapClean >= 30) {
TicketMapCleanup();
RoundChainCleanup(); // v1.35
...
}
Mỗi entry trong g_roundChain[] được kiểm tra: còn lệnh nào cùng roundId đang mở không? Nếu không → xoá entry.Each entry in g_roundChain[] is checked: is any position with the same roundId still open? If not → the entry is removed.
4 nhánh đã sửaFour modified paths
| Path | Vị trí (v1.35)Location (v1.35) |
|---|---|
| DCAPos Buy add | GoldenRobber.mq5:3464 |
| DCAPos Sell add | GoldenRobber.mq5:3496 |
| DCANeg Buy add | GoldenRobber.mq5:3502 |
| DCANeg Sell add | GoldenRobber.mq5:3533 |
Mỗi nhánh có đoạn chuyển chế độ tương tự:Each path has a similar mode switch:
int nextBuyNum = (InpDcaChainPersistent
? MathMax(maxBuyNum, RoundChainGetMaxBuy(roundId)) + 1
: maxBuyNum + 1);
int lotExp = (InpDcaChainPersistent ? nextBuyNum : nBuy);
double rawLot = MathMin(InpDCAPosStartLot * MathPow(InpDCAPosMultiplier, lotExp), InpDCAPosMaxLot);
MathMax(maxBuyNum, RoundChainGetMaxBuy(roundId)) là chốt an toàn dự phòng: nếu g_roundChain mất trạng thái (vd. EA khởi động lại), dùng maxBuyNum quét từ lệnh đang mở làm mức tối thiểu.MathMax(maxBuyNum, RoundChainGetMaxBuy(roundId)) is a fallback safety: if g_roundChain loses its state (e.g. an EA restart), the scan-based maxBuyNum is used as the minimum.
Riêng DCANegDCANeg specifics
Nhánh DCANeg dùng lvl = nBuy - 1 (logic gốc, số mũ bắt đầu từ 0 cho add đầu tiên). v1.35 giữ nguyên ngữ nghĩa này:The DCANeg path uses lvl = nBuy - 1 (original logic, the exponent starts at 0 for the first add). v1.35 preserves those semantics:
if (InpDcaChainPersistent) {
lotExp = MathMax(0, nextBuyNum - 1); // first add exp = 0
} else {
int lvl = nBuy - 1; if (lvl < 0) lvl = 0;
lotExp = lvl;
}
5. Khi nào dùng chế độ nào?5. Which mode when?
Dùng Legacy (false) khiUse Legacy (false) when
- Cần tương thích ngược với config cũ (Pass 10, ScalpingTrending, A baseline)
- Chiến lược ưu tiên phục hồi (sau Tia close, bắt đầu lại từ lot nhỏ)
- Khẩu vị rủi ro thận trọng
- E ngại tail risk khi chuỗi leo cao
- Mặc định — không cần thay đổi gì
- You need backward compatibility with old configs (Pass 10, ScalpingTrending, A baseline)
- The strategy is recovery-friendly (after a Tia close, restart small)
- Your risk profile is conservative
- You worry about tail risk as the chain climbs
- Default — nothing to change
Dùng Persistent (true) khiUse Persistent (true) when
- Muốn tiến trình chuỗi DCA nghiêm ngặt (Martingale truyền thống)
- Equity cao, chấp nhận rủi ro lớn hơn để tận dụng chuỗi
- Chiến lược được thiết kế quanh ý "chuỗi bền" như spec gốc
- Lot và nhãn B#N phải nhất quán cho việc phân tích
- Đã backtest và xác nhận hành vi tốt hơn legacy
- You want a strict DCA chain progression (classic Martingale)
- Equity is high and you accept more risk to exploit the chain
- The strategy is built around "chain persistence" as in the original spec
- Lot and B#N label must agree for analysis
- You have backtested and confirmed it behaves better than legacy
6. Cảnh báo khi bật Persistent6. Warnings when enabling Persistent
- Phình rủi ro: chuỗi không reset → lot leo cao → exposure tích luỹ. Cap bằng
InpDCAPosMaxLot/InpDCANegMaxLotlà lưới an toàn SỐNG CÒN. - Kết hợp với Tia close: Tia đóng lệnh thắng không reset chuỗi → add kế tiếp có thể leo nhanh tới max lot → rủi ro tăng đột biến nếu DD đổi chiều ngay sau đó.
- Round dài = lot cao: round sống lâu (qua nhiều lần partial close) → chuỗi leo cao → các add cuối ở max lot. Nếu round không đóng được → tail risk cực lớn.
- Backtest xác nhận TRƯỚC khi chạy live: với
InpDcaChainPersistent=true, động lực hành vi khác hẳn. CẦN chạy lại backtest cùng config và so sánh: tỷ lệ Profit/DD, Sharpe, sự kiện chuỗi thua dài nhất, Margin Level thấp nhất.
- Risk inflation: the chain does not reset → lots climb → exposure accumulates. The
InpDCAPosMaxLot/InpDCANegMaxLotcaps are a CRITICAL safety net. - Combined with Tia close: Tia closing winners does not reset the chain → the next add can climb fast to max lot → risk spikes if the drawdown reverses right after.
- Long round = high lot: a long-lived round (through many partial closes) → the chain climbs high → final adds sit at max lot. If the round never closes → extreme tail risk.
- Backtest BEFORE going live: with
InpDcaChainPersistent=truethe dynamics are completely different. You MUST re-run the backtest with the same config and compare: Profit/DD ratio, Sharpe, max consecutive-loss event, minimum Margin Level.
7. Checklist chuyển đổi7. Migration checklist
Khi nạp config cũ vào EA v1.35Loading an old config into EA v1.35
- Input
InpDcaChainPersistentmặc định false → hành vi giống hệt v1.34 và trước đó. - File
.setcũ không cóInpDcaChainPersistent→ EA dùng mặc địnhfalse→ an toàn. - Các round đang chạy:
g_roundChain[]ban đầu rỗng → khi dựng lại qua chu kỳ cleanup, chỉ đếm các round đang có lệnh.
InpDcaChainPersistentdefaults to false → behavior identical to v1.34 and earlier.- Old
.setfiles lackInpDcaChainPersistent→ the EA defaults tofalse→ safe. - Rounds already running:
g_roundChain[]starts empty → when rebuilt via the cleanup cycle, only rounds with current positions are counted.
Khi muốn chuyển sang PersistentSwitching to Persistent mode
- Backtest trước: nạp cùng config + giai đoạn với
InpDcaChainPersistent=true→ so sánh chỉ số. - Kiểm tra vốn: chuỗi leo cao hơn → vốn cần có thể tăng (xem Hướng dẫn vốn).
- Theo dõi sát: tail risk tăng → giảm
InpDCAPosMaxLot/InpDCANegMaxLottương ứng nếu cần. - Forward test: chạy vốn nhỏ trước khi dồn toàn bộ.
- Backtest first: load the same config + period with
InpDcaChainPersistent=true→ compare metrics. - Capital check: the chain climbs higher → required capital may rise (see Capital sizing).
- Monitor closely: tail risk rises → reduce
InpDCAPosMaxLot/InpDCANegMaxLotproportionally if needed. - Forward test: deploy small capital before committing fully.
8. Tra cứu nhanh8. Quick reference
| Câu hỏiQuestion | Trả lờiAnswer |
|---|---|
| Chế độ mặc định là gì?What is the default mode? | InpDcaChainPersistent = false (legacy) |
| Logic CŨ tương ứng giá trị nào?Which value is the OLD logic? | false |
| Logic MỚI v1.35 tương ứng giá trị nào?Which value is the NEW v1.35 logic? | true |
| Tương thích ngược khi nạp config cũ?Backward compatible with old configs? | CóYes Mặc định false → giống v1.34Default false → same as v1.34 |
Cần backtest lại khi bật true?Re-backtest when enabling true? | CÓYES động lực hành vi khácthe dynamics differ |
| Dọn dẹp ở đâu?Where is cleanup? | RoundChainCleanup() gắn vào chu kỳ dọn TicketMap 30sRoundChainCleanup() hooked into the 30s TicketMap cleanup |
Chuỗi reset khi nào (chế độ true)?When does the chain reset (true mode)? | Chỉ khi cả round chết (không còn lệnh)Only when the entire round dies (no positions) |
9. Tham chiếu mã nguồn (v1.35)9. Source code references (v1.35)
Các số dòng dưới đây thuộc GoldenRobber.mq5 bản v1.35, không còn khớp với bản hiện tại.The line numbers below belong to GoldenRobber.mq5 v1.35 and no longer match current builds.
| MụcItem | Vị tríLocation |
|---|---|
| Khai báo inputInput declaration | GoldenRobber.mq5:210 |
| Định nghĩa structStruct definition | GoldenRobber.mq5:431-441 |
| Hàm hỗ trợHelper functions | GoldenRobber.mq5:2322-2380 |
| DCAPos Buy đã sửaDCAPos Buy modified | GoldenRobber.mq5:3464-3496 |
| DCAPos Sell đã sửaDCAPos Sell modified | GoldenRobber.mq5:3496-3525 |
| DCANeg Buy đã sửaDCANeg Buy modified | GoldenRobber.mq5:3502-3540 |
| DCANeg Sell đã sửaDCANeg Sell modified | GoldenRobber.mq5:3543-3580 |
| Điểm gắn cleanupCleanup hook | GoldenRobber.mq5:5816 |
| Ghi chú lịch sửHistory note | GoldenRobber.mq5:7 |