# EA-SPEC — ระบบแม่ปลาปากกาเขียว (XAUUSD)

> **สถานะ: spec + pseudo-code เท่านั้น — ยังไม่มีโค้ด MQL5 จริง**
> เขียน 30 ส.ค. 2026 · ฐาน: `GLOSSARY.md` + หนังสือ 4 เล่ม + `cases/CASE-001`
> เป้าหมายเอกสารนี้: ให้โปรแกรมเมอร์ (หรือ Codex) เปิดอ่านแล้วเขียน MQL5 ได้เลย โดยไม่ต้องเดาเอง

---

## วิธีอ่านป้ายกำกับ

| ป้าย | หมายถึง | ชั้นข้อมูล (กฎเหล็ก FIN ข้อ 1) |
|---|---|---|
| `[ปลา3 p.N]` `[แก้ว1 p.N]` `[โม1 p.N]` `[โม2 p.N]` | มาจากหนังสือตรงๆ | **A → B** (คำโค้ช แปลเป็นโค้ด) |
| `[CASE-001]` | ถอดจากภาพ TradingView จริง | **A'** |
| **`[SPEC-เดา]`** | **เราเดาเอง ไม่มีในเล่มไหน** ⚠️ | **C** |

> ⚠️ **กติกาบังคับ:** ทุกอย่างที่ติด `[SPEC-เดา]` **ต้องเป็น input parameter** ห้าม hardcode
> เหตุผล: ตอน backtest พัง จะได้แยกออกว่า *"ระบบโค้ชไม่เวิร์ค"* หรือ *"เราเดาผิดเอง"*

---

# §0 ข้อตกลงพื้นฐาน (ต้องตกลงก่อนเขียนบรรทัดแรก)

## 0.1 หน่วย "จุด"

```
1 จุด = 0.01 USD บน XAUUSD          // ยืนยันจากหนังสือ 3 ที่:
                                     //  โม: กรอบเช้า 1800 → ต้าน 1805 / รับ 1795 = ±500 จุด  [โม1 p.23]
                                     //  ปลา: พักครึ่ง (3474+3578)/2 → swing 104 USD = 10,400 จุด  [ปลา3 p.49]
                                     //  โม: พักสวิง (2892+2915)/2 → swing 23 USD = 2,300 จุด  [โม1 p.36]

const double PT = 0.01;
double PtsToPrice(int pts) { return pts * PT; }
```

⚠️ **ห้ามใช้ `_Point` ตรงๆ** — โบรกทศนิยม 3 ตำแหน่งจะได้ `_Point = 0.001` แล้วทุกระยะจะเพี้ยน 10 เท่า
ตอน `OnInit()` ให้ assert: `if(MathAbs(PT/_Point - 1) > 0.01 && MathAbs(PT/_Point - 10) > 0.01) return INIT_FAILED;`

## 0.2 กฎเหล็ก — ห้ามอ่านแท่งที่ยังไม่ปิด

```
index 0 = แท่งกำลังวิ่ง (ยังไม่ปิด)
index 1 = แท่งปิดล่าสุด   ← ทุกการ "ตัดสินใจ" เริ่มจากตรงนี้
```

**ห้ามใช้ index 0 ในทุกเงื่อนไข ยกเว้น 3 กรณีที่ระบุไว้ชัดเจนนี้เท่านั้น:**

| # | ใช้ได้ | เหตุผลที่ไม่ใช่ look-ahead |
|---|---|---|
| 1 | `iTime(sym, tf, 0)` เพื่อตรวจว่าแท่งใหม่เปิดหรือยัง | เวลาเปิดแท่งไม่ใช่ข้อมูลอนาคต |
| 2 | `Ask` / `Bid` ตอนส่งคำสั่ง | คือราคา ณ ปัจจุบันจริง |
| 3 | `iOpen(sym, PERIOD_H4, 0)` **เฉพาะตอนตีกรอบวัน 07:00** `[ปลา3 p.27]` | ราคาเปิดแท่ง fix ตั้งแต่วินาทีแรกและไม่เปลี่ยนอีก |

```
// assertion ที่ต้องมีในโค้ดจริง กันตัวเองพลาด
#define BAR(i)  (Assert(i >= 1 || InWhitelist()), i)
```

## 0.3 เวลาและโบรกเกอร์ — จุดที่พังเงียบที่สุด 🔴

```
ระบบนี้แกะจาก Exness · TradingView ต้องตั้ง GBE Brokers  [แก้ว1 p.72]
```

**ทำไมเรื่องนี้ใหญ่:** ทุกอย่างในระบบยึด "แท่ง H4 ที่เปิด 07:00 น."
ถ้า server offset ต่างไป 1 ชั่วโมง → ขอบแท่ง H4 ขยับ → **PA ทุกตัวบนกราฟเปลี่ยนหมด** และกรอบวันเปลี่ยนหมด

```
input int   ServerGMTOffset   = 3;     // GMT+3 (Exness summer) / GMT+2 (winter)
input int   DayFrameHourLocal = 7;     // 07:00 เวลาไทย = GMT+7
// ต้องแปลงให้แท่ง H4 ที่ "เปิด 07:00 ไทย" ตรงกับ bar boundary จริงของ feed
// ถ้าแปลงแล้วไม่ลงตัวพอดี → OnInit ต้อง FAIL พร้อม log บอกว่า feed นี้ใช้ไม่ได้
```

**กฎ backtest:** ต้องเทสบน data ของโบรกที่จะรันจริงเท่านั้น · ห้ามเอาผลจากโบรก A ไปอ้างกับโบรก B

## 0.4 TF ที่ระบบทำงาน

```
TF_SIG   = H4    // TF หลักที่หา Sig  [ปลา3 p.51 "H4 คือกุญแจสำคัญ"]
TF_BIAS  = D1    // ดูทิศทาง          [โม2 p.90]
TF_CONF  = H1    // ยืนยัน            [โม2 p.90]
TF_ENTRY = M5    // ดูเบรกอย่างเดียว   [ปลา3 p.51] — ใช้ที่ backtest ชั้น 2 เท่านั้น
```

`[แก้ว1 p.9]` **PA เกิดได้ตั้งแต่ M1 แต่เรียก SIG และนับรอบวิ่งได้ตั้งแต่ H1 ขึ้นไป** → EA จะไม่รับ Sig จาก TF < H1 เด็ดขาด

---

# §1 PSEUDO-CODE — เงื่อนไขทุกตัวที่โค้ดได้

## 1.0 โครงหลัก

```
OnTick():
    if (!IsNewBar(TF_SIG)) { ManageOpenPositions(); return; }

    // ---- ทุกอย่างต่อจากนี้ทำงานบนแท่งปิดแล้วเท่านั้น ----
    ctx = BuildContext()                 // §1.5  (เทรนด์ / วงจร / กรอบ / SW)
    UpdateActiveSigs(ctx)                // §1.5.4 (เช็ค TP1/TP2/จุดเช็คถูกทำลาย)

    if (!RiskGuardAllowsNewTrade()) return          // §1.9

    // จังหวะ 2 — ไส้หลังซิกปิดแล้ว (แท่ง 1 = ไส้หลังซิก, PA จบที่แท่ง 2)
    for each dir in {BUY, SELL}:
        pas = DetectAllPA(dir, anchor=2)            // §1.2
        pas = DedupeByPostWick(pas)                 // §1.2.7  ⚠️ สำคัญ
        for pa in pas:
            sig = BuildSig(pa, postWickIdx=1)       // §1.3
            if (sig.valid && PassAllGates(sig,ctx)) // §1.4 + §1.7
                OpenTrade(sig, ENTRY_MODE_2)        // §1.8

    // จังหวะ 1 — PA เพิ่งจบ ไส้หลังซิกกำลังก่อตัว (เข้าเลย ยังไม่คอนเฟิร์ม)
    if (EntryTiming1_Enabled):
        pas = DedupeByPostWick(DetectAllPA(dir, anchor=1))
        for pa in pas:
            if (PassAllGates_PAonly(pa, ctx))
                OpenTrade_PAonly(pa, ENTRY_MODE_1)  // SL = เหนือ/ใต้ชุด PA  [ปลา3 p.16]
```

---

## 1.1 Helper แท่งเทียน

```
struct Bar { double o,h,l,c; datetime t; }

Bar   B(tf,i)            = { iOpen(tf,i), iHigh(tf,i), iLow(tf,i), iClose(tf,i) }
double Body(b)           = MathAbs(b.c - b.o)
double Range(b)          = b.h - b.l
double BodyTop(b)        = MathMax(b.o, b.c)
double BodyBot(b)        = MathMin(b.o, b.c)
double UpWick(b)         = b.h - BodyTop(b)
double DnWick(b)         = BodyBot(b) - b.l
bool   IsBull(b)         = b.c > b.o
bool   IsBear(b)         = b.c < b.o

// กันแท่ง range 0 (วันหยุด / gap) — หารด้วยศูนย์เป็นสาเหตุ crash อันดับ 1
bool   IsTradeableBar(b) = Range(b) >= PtsToPrice(MinBarRangePts)     // [SPEC-เดา]
```

**"ชุด PA"** = แท่ง `i` ถึง `i+n-1` (i = แท่งใหม่สุดของชุด, n = จำนวนแท่ง 1/2/3)

```
double PA_High(i,n)     = max(h) ของแท่ง i..i+n-1
double PA_Low(i,n)      = min(l) ของแท่ง i..i+n-1
double PA_BodyTop(i,n)  = max(BodyTop) ของแท่ง i..i+n-1
double PA_BodyBot(i,n)  = min(BodyBot) ของแท่ง i..i+n-1
double PA_BodySpan(i,n) = PA_BodyTop - PA_BodyBot          // "เนื้อเทียนชุด Sig"  [ปลา3 p.9]
```

---

## 1.2 ตรวจจับ PA 5 รูปแบบ

> ทิศทางในโค้ดข้างล่างเขียนฝั่ง **BUY** (เกิดที่แนวรับ) · ฝั่ง SELL คือกลับด้านทุกบรรทัด
> `[ปลา3 p.4]` **PA Buy ใช้ที่แนวรับ · PA Sell ใช้ที่แนวต้าน** — เงื่อนไข "อยู่ฝั่งไหนของกรอบ" อยู่ที่ §1.4

### 1.2.0 กฎ 50% — ฟังก์ชันกลางที่ทุก PAT เรียกใช้

```
// [ปลา3 p.5] "แท่งสีแดงปิดเนื้อเกิน 50% ของแท่งเขียวก่อนหน้า นับว่าเป็น Sig"
// [โม1 p.37] "หากแท่งเทียนชนะแท่งก่อนหน้า มากกว่า 50% ของบอดี้ ถือว่าเป็น Pa"
// ⚠️ วัดกับ "บอดี้" ของแท่งก่อนหน้า ไม่ใช่ range — ทั้งสองเล่มพูดตรงกัน

double Penetration_BUY(Bar prev, double closePrice):
    // prev = แท่งแดง body จาก prev.o (บน) ลงไป prev.c (ล่าง)
    if (Body(prev) < PtsToPrice(MinBodyPts)) return 0     // [SPEC-เดา] กันหารด้วยแท่งจิ๋ว
    return (closePrice - prev.c) / Body(prev)

// pen >= 1.0   → กลืนกินเต็ม  = PAT2 ปกติ
// 0.5 < pen < 1 → PAT2 พิเศษ  [ปลา3 p.5]
// pen <= 0.5   → ไม่นับ PAT2 → ส่งต่อไปเช็ค PAT3 แบบ 3  [ปลา3 p.5]
```

### 1.2.1 PAT1 — Pin (1 แท่ง) 🔴 ต้องเดาตัวเลข

```
bool IsPAT1_BUY(i):                        // "ขาหยั่ง"  [แก้ว1 p.3]
    b = B(TF_SIG, i)
    if (!IsTradeableBar(b)) return false

    // [ปลา3 p.3] "Pin = ไส้ยาวด้านตรงข้ามทิศ เนื้อเล็ก — ไม่เกี่ยวกับสี"
    // ตัวเลขทั้ง 3 บรรทัดนี้ = [SPEC-เดา] ทั้งหมด (ดู §2 ช่อง G1)
    return DnWick(b) >= PinWickBodyRatio * Body(b)      // ไส้ล่าง >= 2.0 เท่าของเนื้อ
        && DnWick(b) >= PinWickRangePct  * Range(b)     // ไส้ล่าง >= 60% ของ range
        && UpWick(b) <= PinOppWickPct    * Range(b)     // ไส้บน <= 15% ของ range
```

### 1.2.2 PAT2 — กลืนกิน (2 แท่ง)

```
bool IsPAT2_BUY(i):                        // i = แท่งเขียว (แท่งที่สอง), i+1 = แท่งแดง
    p = B(TF_SIG, i+1); c = B(TF_SIG, i)
    if (!IsBear(p) || !IsBull(c)) return false
    if (Body(p) < PtsToPrice(MinBodyPts)) return false          // [SPEC-เดา] ห้ามกลืน doji

    pen = Penetration_BUY(p, c.c)
    if (pen < 1.0) return false                                  // ไม่ถึงกลืนเต็ม
    if (Pat2_RequireFullBodyEngulf && c.o > p.c) return false     // [SPEC-เดา] เข้มขึ้น: ต้องเปิดต่ำกว่าปิดแท่งแดงด้วย
    return true

bool IsPAT2_SPECIAL_BUY(i):                // "PAT 2 พิเศษ"  [ปลา3 p.5]
    p = B(TF_SIG, i+1); c = B(TF_SIG, i)
    if (!IsBear(p) || !IsBull(c)) return false
    pen = Penetration_BUY(p, c.c)
    return pen > Engulf50Min && pen < 1.0                         // Engulf50Min default 0.50 [ปลา3 p.5]
```

### 1.2.3 PAT3 แบบ 1 / แบบ 2 — Doji star / Small star (3 แท่ง) 🔴 ต้องเดาตัวเลข

```
// i+2 = แท่งแรก (แดงใหญ่) · i+1 = แท่งกลาง · i = แท่งสาม (เขียว กลับทิศ)

bool IsDoji(b):                             // [SPEC-เดา] ทั้งหมด — ช่อง G2
    return Body(b) <= DojiBodyRangePct * Range(b)                 // เนื้อ <= 10% ของ range
        && Range(b) >  PtsToPrice(MinBarRangePts)

bool IsPAT3_1_BUY(i):                       // แท่งกลาง = Doji  [ปลา3 p.3]
    b1=B(i+2); b2=B(i+1); b3=B(i)
    if (!IsBear(b1) || !IsBull(b3)) return false
    if (!IsDoji(b2)) return false
    if (Body(b2) > DojiVsFirstPct * Body(b1)) return false         // [SPEC-เดา] เนื้อกลาง <= 30% ของแท่งแรก
    if (Apply50RuleToPat3_1_2 && Penetration_BUY(b1, b3.c) <= 0.5) return false   // [SPEC-เดา] ดู §2 ช่อง G14
    return true

bool IsPAT3_2_BUY(i):                       // แท่งกลาง "เล็ก แต่ไม่ใช่ Doji"  [ปลา3 p.3]
    b1=B(i+2); b2=B(i+1); b3=B(i)
    if (!IsBear(b1) || !IsBull(b3)) return false
    if (IsDoji(b2)) return false                                   // ⭐ ตัวแยกแบบ1 ออกจากแบบ2 — แก้ช่องว่าง §5 ข้อ 3
    if (Body(b2) > SmallBodyVsFirstPct * Body(b1)) return false     // [SPEC-เดา] เนื้อกลาง <= 50% ของแท่งแรก
    if (Apply50RuleToPat3_1_2 && Penetration_BUY(b1, b3.c) <= 0.5) return false
    return true
```

> ⭐ **แบบ 1 กับ แบบ 2 เป็น mutually exclusive โดยโครงสร้าง** (แบบ1 = แท่งกลางเป็น Doji, แบบ2 = แท่งกลางเล็กแต่ไม่ใช่ Doji)
> นี่คือคำตอบของช่องว่าง `GLOSSARY §5 ข้อ 3` — แต่เป็น **การตีความของเรา** `[SPEC-เดา]` โค้ชไม่ได้พูดแบบนี้

### 1.2.4 PAT3 แบบ 3 — Three Inside Up/Down (3 แท่ง)

```
bool IsPAT3_3_BUY(i):
    b1=B(i+2); b2=B(i+1); b3=B(i)
    if (!IsBear(b1)) return false
    if (Body(b1) < PtsToPrice(MinBodyPts)) return false             // "แท่งแรกใหญ่"  [ปลา3 p.6]

    // [ปลา3 p.6] "แท่ง 2 และ 3 ปิดรวมกันเกิน 50% ของแท่งแรก"
    // ⚠️ ประโยคนี้ตีความได้ 2 ทาง → ทำเป็น switch (ดู §2 ช่อง G3b)
    if (Pat33_Mode == CLOSE_LEVEL):
        ok = Penetration_BUY(b1, b3.c) > Pat33_MinPen              // ปิดแท่ง 3 เหนือ 50% ของบอดี้แท่ง 1
    else if (Pat33_Mode == BODY_SUM):
        bull = (IsBull(b2) ? Body(b2) : 0) + (IsBull(b3) ? Body(b3) : 0)
        ok = bull / Body(b1) > Pat33_MinPen                          // ผลรวมบอดี้เขียว > 50% ของแท่ง 1
    if (!ok) return false

    if (Pat33_NoNewExtreme && (b2.l < b1.l || b3.l < b1.l)) return false   // [SPEC-เดา] ห้ามทำโลว์ใหม่
    if (Pat33_RequireHarami && !(BodyTop(b2)<=BodyTop(b1) && BodyBot(b2)>=BodyBot(b1))) return false  // [SPEC-เดา] off by default
    return true
```

### 1.2.5 ⭐ ลำดับการตรวจ (สำคัญ — ระบบสั่งไว้)

```
// [ปลา3 p.5] "ไม่ถึง 50% → ไม่นับ PAT2 → ให้ใช้เป็น PAT3 แบบ 3"
DetectAllPA(dir, i):
    out = []
    if (IsPAT1(dir,i))         out += {PAT1,   n=1}
    if (IsPAT2(dir,i))         out += {PAT2,   n=2}
    else if (IsPAT2_SPECIAL(dir,i)) out += {PAT2S, n=2}
    else:                                                  // pen <= 0.5 → ตกไป PAT3-3
        if (IsPAT3_3(dir,i))   out += {PAT3_3, n=3}
    if (IsPAT3_1(dir,i))       out += {PAT3_1, n=3}
    if (IsPAT3_2(dir,i))       out += {PAT3_2, n=3}
    return out
```

### 1.2.6 Fake Sig — PIN เกิดผิดที่ `[แก้ว1 p.9]`

```
// "PIN Buy ที่แนวต้าน รวมร่างกลายเป็น PA Sell Pat 3 ทันที" → ห้าม BUY
bool IsFakeSig(pa, ctx):
    f = NearestQualifiedFrame(pa, ctx)
    if (f == NONE) return false
    if (pa.dir == BUY  && FrameRoleFor(f, pa) == RESISTANCE) return true    // หัวโหม่งแนวต้าน
    if (pa.dir == SELL && FrameRoleFor(f, pa) == SUPPORT)    return true    // หัวโหม่งแนวรับ
    return false

FrameRoleFor(f, pa):
    if (Close(pa.lastBar) >= f.price - PtsToPrice(OverlapPts)) return SUPPORT
    else                                                       return RESISTANCE
```

### 1.2.7 ⭐⭐ Dedupe — ห้ามยิงซ้ำ (บทเรียนจาก CASE-001)

```
// [CASE-001] ตาราง indicator แสดง PA12/PA1 และ PA3-2/PA2 คู่กัน — PostWick + TP เท่ากันเป๊ะ
// = แท่งเดียวกัน setup เดียวกัน ไม่ใช่ 2 สัญญาณ
// ถ้าไม่ dedupe: N ใน backtest จะพองขึ้น 1.5-2 เท่า → t-stat ปลอม → สรุปผิดทั้งงาน

DedupeByPostWick(pas):
    key = (dir, postWickBarTime, roundTo(postWickPrice, 1จุด))
    เก็บตัวเดียวต่อ key · ลำดับความสำคัญ: PAT3_3 > PAT2 > PAT2S > PAT3_1 > PAT3_2 > PAT1  [SPEC-เดา]
    เก็บ label รวมไว้ใน log (เช่น "PA12" = เข้าเงื่อนไขทั้ง PAT1 และ PAT2)  [CASE-001]
```

---

## 1.3 ไส้หลัง Sig (PostWick) — หัวใจของระบบ

### 1.3.1 อยู่แท่งไหน

```
// [แก้ว1 p.8] "หลังจากเกิด PA จะวัดไส้หลัง Sig ในแท่งถัดไปของชุด PA นั้น"
//   Pat1 → แท่งที่ 2 · Pat2 → แท่งที่ 3 · Pat3 → แท่งที่ 4
// = แท่งแรกถัดจากชุด PA จบพอดี → index = (i - 1) เสมอ เมื่อ i = แท่งใหม่สุดของชุด PA
postWickIdx = pa.i - 1
require(postWickIdx >= 1)         // ⭐ กฎเหล็ก §0.2 — จังหวะ 2 ต้องรอแท่งนี้ปิดก่อน

// ราคาไส้หลังซิก
postWickPrice = (dir==BUY) ? Low(postWickIdx) : High(postWickIdx)
```

### 1.3.2 แท่งไส้หลังซิกต้องหน้าตายังไง — 3 ข้อ `[โม1 p.16]`

```
ValidatePostWick_BUY(pa, p = postWickIdx):
    w = B(TF_SIG, p);  last = B(TF_SIG, pa.i)

    // W1 ปิดแท่งกลับทาง
    if (PostWickCloseRule == OPPOSITE  && !IsBear(w)) return INVALID     // [โม1 p.16]
    if (PostWickCloseRule == SAME_DIR  && !IsBull(w)) return INVALID     // [แก้ว1 p.3] ⚠️ ดู §2 ช่อง G13
    // PostWickCloseRule == ANY → ข้ามข้อนี้

    // W2 เนื้อเทียนไม่เกินครึ่งหนึ่งของแท่งก่อนหน้า
    if (Body(w) > PostWickBodyMaxPct * Body(last)) return DESTROYED      // [โม1 p.16] default 0.50

    // W3 ไส้เทียนไม่เกินไส้เทียนแท่งก่อนหน้า
    if (DnWick(w) > DnWick(last) * PostWickWickMaxMult) return DESTROYED // [โม1 p.16] default 1.00

    // W4 ไส้หลังซิกไม่ควรเกินครึ่งหนึ่งของเนื้อเทียนชุด Sig  [ปลา3 p.9 ข้อ 1]
    retrace = PA_BodyTop(pa) - w.l
    if (retrace > 0.5 * PA_BodySpan(pa)):
        slAnchor = PA_Low(pa)        // [ปลา3 p.9 ข้อ 2] ใช้ไส้ยาวสุดของชุด Sig วาง SL แทน
    else:
        slAnchor = w.l

    // W5 ห้ามทำลายชุด PA
    if (w.l < PA_Low(pa)) return DESTROYED                               // [ปลา3 p.9 ข้อ 3] "บอดี้เกินหรือไส้เกิน = PA ถูกทำลาย"

    return {VALID, postWick = w.l, slAnchor}
```

> ⚠️ **`DESTROYED` ≠ `INVALID`** — DESTROYED แปลว่า **ชุด PA นี้ตายถาวร ห้ามกลับมามอง** `[ปลา3 p.9]` `[โม1 p.16]`
> ต้อง blacklist `(pa.i, pa.n, dir)` ไว้ ไม่ให้แท่งถัดไปมาหยิบชุดเดิมไปใช้ใหม่

### 1.3.3 หน้าที่ 3 อย่างของไส้หลังซิก `[ปลา3 p.2, p.8, p.11]`

```
1. slAnchor       → จุดวาง SL
2. tpAnchor       → จุดเริ่มนับรอบวิ่ง (ใช้ postWick เสมอ ไม่ใช่ slAnchor)  [ปลา3 p.8]
3. checkpoint     → จุดเช็ค เมื่อราคาถึง TP1 แล้ว                          [ปลา3 p.11]
```

---

## 1.4 กรอบ + "เกิดที่กรอบ" 🔴 ตัวกรองสำคัญที่สุด และเดามากที่สุด

### 1.4.1 สร้างชุดกรอบ

```
BuildFrames(atBar):
    F = []

    // (a) กรอบสถิติ — เส้นทุก 500 จุด ลงท้าย 0/5   [ปลา3 p.20]
    for k = floor((px - Span)/5.00) .. ceil((px + Span)/5.00):
        F += { price: k*5.00, src: GRID, w: 1 }

    // (b) กรอบวัน 1,000 จุด   [ปลา3 p.27] [แก้ว1 p.14] [โม1 p.23]
    h4_0700 = แท่ง H4 ที่ opentime == 07:00 ของวันนี้    // ⚠️ ใช้ iOpen(...,0) ได้ ดู §0.2 whitelist #3
    anchor  = RoundToNearest(Open(h4_0700), 5.00)
    F += { anchor,        src: DAY_MID, w: 2 }
    F += { anchor + 5.00, src: DAY_UP,  w: 2 }
    F += { anchor - 5.00, src: DAY_DN,  w: 2 }

    // (c) กรอบ Sig ชน Sig (ขอบเป็นไส้ ไม่ใช่เลขกลม)   [โม1 p.31]
    if (ctx.cycle == SW && lastBuySig != NULL && lastSellSig != NULL):
        F += { lastSellSig.postWick, src: SIGVSSIG_HI, w: 3 }
        F += { lastBuySig.postWick,  src: SIGVSSIG_LO, w: 3 }

    // (d) กรอบครบรอบ TP   [แก้ว1 p.17]
    for s in activeSigs where s.reachedTP1:
        F += { s.tp1, src: TPDONE, w: 3 }

    // (e) กรอบ ATH   [โม1 p.30]
    if (px >= HighestHigh(TF_SIG, ATHLookback)):
        athGridWaived = true            // [ปลา3 p.27] โซนนิวไฮ ปลายไส้สัมผัสอาจไม่ถึง 7-14 จุด

    // (f) คัดกรองด้วยจำนวนจุดสัมผัส 7-14   [ปลา3 p.2] [แก้ว1 p.14] [โม1 p.12]
    for f in F:
        f.touches   = CountTouches(f.price)
        f.qualified = (f.src != GRID)                       // กรอบวัน/SW/TPDONE ผ่านอัตโนมัติ
                   || athGridWaived
                   || (f.touches >= MinTouches && f.touches <= MaxTouches)
    return F
```

### 1.4.2 นับ "ปลายไส้สัมผัส 7-14 จุด" ยังไง 🔴 `[SPEC-เดา]` ทั้งบล็อก (ช่อง G5)

```
// หนังสือบอกแต่ตัวเลข 7-14 ไม่บอกว่านับจากอะไรถึงอะไร ต้องสัมผัสกี่ครั้ง
int CountTouches(double f):
    cnt = 0; lastTouchBar = -999
    for i = 1 .. TouchLookbackBars:                       // นับย้อนหลังจากแท่ง 1 เท่านั้น (กัน look-ahead)
        b = B(PERIOD_H4, i)
        touched = (MathAbs(b.h - f) <= PtsToPrice(TouchBandPts))
               || (MathAbs(b.l - f) <= PtsToPrice(TouchBandPts))
        if (touched && (lastTouchBar - i) >= TouchSeparationBars):
            cnt++; lastTouchBar = i
    return cnt
```

> **`TouchSeparationBars` คือหัวใจ** — ถ้าไม่มี การแกว่งในกรอบเดียว 10 แท่งติดจะถูกนับเป็น 10 จุดสัมผัส
> ทำให้ทุกเส้นผ่านเกณฑ์ 7 หมด แล้ว "กรอบ" กลายเป็นตัวกรองที่กรองอะไรไม่ได้เลย

### 1.4.3 ⭐⭐ "เกิดที่กรอบ" — นิยามที่เลือกใช้ (ช่อง G4)

**ปัญหาที่ต้องแก้ก่อน:** เส้นสถิติห่างกัน 500 จุด ถ้านิยาม "ใกล้กรอบ" = ±300 จุด (ตามเลข OVL ในระบบ)
→ หน้าต่าง 600 จุด > ระยะห่าง 500 จุด → **ทุกราคาบนโลกอยู่ "ที่กรอบ" หมด ตัวกรองตายทันที**

**ทางแก้ที่เลือก:** ไม่ใช้ "ใกล้" แต่ใช้ **"แตะแล้วหยุด"** — ตรงกับนิยาม *เบรก = หยุด* `[ปลา3 p.2]`

```
bool AtFrame_BUY(pa, ctx):
    lo   = PA_Low(pa)
    last = B(TF_SIG, pa.i)
    for f in ctx.frames where f.qualified:
        if (lo <= f.price + PtsToPrice(TouchInPts)        // ① ไส้ต้องลงมา "ถึง" กรอบจริง
         && lo >= f.price - PtsToPrice(OverlapPts)        // ② ทะลุลงได้ไม่เกิน OVL 300 จุด  [ปลา3 p.2]
         && last.c > f.price - PtsToPrice(OverlapPts)     // ③ ปิดกลับมาฝั่งบนของกรอบ
         && FrameRoleFor(f, pa) == SUPPORT):              // ④ PA Buy ต้องอยู่ที่แนวรับ  [ปลา3 p.4]
            pa.frame = f; return true
    return false
```

**ระดับความเข้มของกรอบ** `[แก้ว1 p.24-26]` — ใช้เป็นตัวคัด ไม่ใช่ตัวห้าม

```
FrameStrength(f, ctx):
    s = 0
    if (f.touches >= MinTouches)                s += 1     // "แข็งแรง"
    if (CountOverlappingTF(f.price) >= 2)       s += 1     // "สำคัญ" = ซ้อน >= 2 TF  [แก้ว1 p.24]
    if (f.src in {DAY_MID,DAY_UP,DAY_DN})       s += 1
    if (f.src in {SIGVSSIG_HI,SIGVSSIG_LO,TPDONE}) s += 1
    return s                                                // ใช้เป็น filter: require s >= MinFrameStrength
```

---

## 1.5 บริบท — เทรนด์ / วงจร / SW

### 1.5.1 เทรนด์ (ลำดับ 1 — ชนะทุกอย่าง `[ปลา3 p.63]`)

```
int BiasOf(tf):
    switch (BiasMode):
      case DAY_CANDLE:                                     // [ปลา3 p.52] "ทุก 7 โมงเช้า ดู TF Day ปิดแท่งสีอะไร"
          b = B(tf, 1); return IsBull(b) ? +1 : (IsBear(b) ? -1 : 0)
      case EMA14_SLOPE:                                    // [โม2 p.77]
          d = EMA14(tf,1) - EMA14(tf, 1+EmaSlopeBars)
          if (MathAbs(d) < EmaFlatATR * ATR(tf,14,1)) return 0        // แบน = SW
          return d > 0 ? +1 : -1
      case SWING_STRUCTURE:                                // [SPEC-เดา] HH/HL vs LH/LL
          ...
```

```
// [โม2 p.90] "เข้าเฉพาะทิศทางเดียวกันกับ H4 / Day เท่านั้น"
bool TrendGate(dir, ctx):
    if (RequireDayAlign  && BiasOf(D1) != dir) return false
    if (RequireWeekAlign && BiasOf(W1) != dir) return false
    // [แก้ว1 p.16] "กราฟไปทิศทางเดียวกันอย่างน้อย 3 TF → จะส่งให้เกิด SIG"
    if (Require3TFAlign):
        n = count(dir == BiasOf(tf) for tf in {W1, D1, H4, H1})
        if (n < 3) return false
    return true
```

### 1.5.2 ยืนกรอบ / หลุดกรอบ (§4.2 โค้ชขัดกัน)

```
bool HoldsFrame(dir, f, n = HoldFrameBars):
    // [ปลา3 p.21] ต้องปิดเหนือกรอบอย่างน้อย 3 แท่ง  |  [แก้ว1 p.3][โม1 p.12] แค่เนื้อเทียนยืน
    for i = 1 .. n:
        b = B(TF_SIG, i)
        if (dir == BUY  && BodyBot(b) <= f.price) return false
        if (dir == SELL && BodyTop(b) >= f.price) return false
    return true
// HoldFrameBars = 1 (แก้ว/โม) หรือ 3 (แม่ปลา) → พารามิเตอร์เดียวครอบทั้งสองสำนัก
```

### 1.5.3 วงจรกราฟ — state machine `[ปลา3 p.37-43]`

```
enum Cycle { SW, SIG_ACTIVE, RUN_TP, REST }

UpdateCycle(ctx):
    // Sig ค้ำ Sig — ทิศเดียวกันซ้อน = เทรนด์  [ปลา3 p.44] [โม1 p.54]
    if (newSig.dir == lastSig.dir && !lastSig.destroyed && !lastSig.reachedTP2):
        ctx.cycle = SIG_ACTIVE; ctx.stacked++; return

    // Sig ชน Sig — สลับทิศ = Sideway  [ปลา3 p.45] [โม1 p.31]
    if (newSig.dir != lastSig.dir && !lastSig.destroyed):
        ctx.cycle = SW
        ctx.swHi = (SELL side).postWick;  ctx.swLo = (BUY side).postWick
        return

    if (activeSig.reachedTP1) ctx.cycle = RUN_TP
    if (activeSig.reachedTP2 || activeSig.destroyed):
        ctx.cycle = REST; ctx.restStart = CurrentPrice

    // ออกจาก SW: หลุดกรอบ SW + ยืนกรอบได้  [ปลา3 p.46-47]
    if (ctx.cycle == SW && (HoldsFrame(BUY, ctx.swHi) || HoldsFrame(SELL, ctx.swLo))):
        ctx.cycle = SIG_ACTIVE
```

### 1.5.4 อัปเดต Sig ที่ยัง active — TP / จุดเช็ค

```
UpdateActiveSigs(ctx):
  for s in activeSigs:
    hi = High(1); lo = Low(1)

    // §4.1 ไส้หลังซิกถูกทำลายเมื่อไหร่ — 3 สำนัก รวมเป็น 1 พารามิเตอร์
    //   แก้ว  = สัมผัสก็ตาย        → WickBreakTolPts = 0     [แก้ว1 p.3]
    //   แม่ปลา = ต้องเกินกว่า      → WickBreakTolPts = 1     [ปลา3 p.10]
    //   โม    = เกิน 300 จุดถึงตาย → WickBreakTolPts = 300   [โม1 p.39]
    brk = (s.dir==BUY) ? (lo <= s.postWick - PtsToPrice(WickBreakTolPts))
                       : (hi >= s.postWick + PtsToPrice(WickBreakTolPts))
    if (brk):
        // [โม1 p.39] ข้อยกเว้น: ถ้าตึงรอบแล้ว การทำลายไส้ตัวเอง "ไม่ผิดโครงสร้าง"
        if (!(AllowSelfBreakAfterTP2 && s.reachedTP2)) { s.destroyed = true; continue }

    if ((s.dir==BUY && hi >= s.tp1) || (s.dir==SELL && lo <= s.tp1)) s.reachedTP1 = true
    if ((s.dir==BUY && hi >= s.tp2) || (s.dir==SELL && lo <= s.tp2)) s.reachedTP2 = true
```

### 1.5.5 SIG ค้าง `[แก้ว1 p.3]`

```
// "ไล่ดูว่า TF ไหนยังวิ่งไม่ครบรอบ / ยังทำงานอยู่"
list PendingSigs(): return [s for s in allSigs if !s.destroyed && !s.reachedTP2]
// ใช้ที่เงื่อนไข [แก้ว1 p.14] "ไม่มี SIG ค้าง → ห้ามเล่นที่รับ/ต้านย่อย"
```

### 1.5.6 MTF — 2 แท่ง H4 = 8 แท่ง H1 `[โม1 p.37-38]`

```
bool MTF_Confirm(pa):
    if (!RequireH1Confirm) return true
    // "Pa Buy P2 ใน H4 ต้องมี Sig Buy ใน H1 เกิดภายใน 8 แท่งนั้น"
    barsH1 = pa.n * 4 + PostWickH1Slack                  // n แท่ง H4 = n*4 แท่ง H1
    for j = 1 .. barsH1:
        if (AnyPA(PERIOD_H1, pa.dir, j)) return true
    return false
```

---

## 1.6 SL / TP

### 1.6.1 SL

```
double ComputeSL(sig, mode):
    switch (mode):
      case SL_POSTWICK:                                     // จังหวะ 2  [ปลา3 p.16]
          raw = sig.slAnchor                                // = postWick หรือ PA_Low ตามกฎ W4  [ปลา3 p.9]
      case SL_PA:                                           // จังหวะ 1  [ปลา3 p.16]
          raw = (dir==BUY) ? PA_Low(sig.pa) : PA_High(sig.pa)
      case SL_FRAME:                                        // ท่า A1/A3  [ปลา3 p.53,62]
          raw = (dir==BUY) ? sig.frame.price - PtsToPrice(300)
                           : sig.frame.price + PtsToPrice(300)

    sl = raw ∓ PtsToPrice(SLBufferPts)                      // [SPEC-เดา] กัน spread + noise

    // เพดาน SL ตามช่วงตลาด  [แก้ว1 p.37] [โม2 p.90]
    cap = InUSSession() ? SLMaxPtsUS : SLMaxPtsAsia         // 500 / 300
    if (DistancePts(entry, sl) > cap):
        if (SLCapMode == CLAMP) sl = entry ∓ PtsToPrice(cap)
        else                    return REJECT               // SLCapMode == SKIP  [SPEC-เดา] default SKIP
    if (DistancePts(entry, sl) < SLMinPts) return REJECT     // [SPEC-เดา] กัน SL จิ๋วโดน spread กิน
    return sl
```

### 1.6.2 TP — 3 โหมด (⭐ อ่าน §5.1 ก่อนเลือก)

```
double RunPts(tf):                                           // [ปลา3 p.8,43] ตรงกันทั้ง 3 โค้ช
    H1 → 1000  |  H4 → 1500  |  D1 → 5000  |  W1 → 15000
double RunPts2(tf):
    H1 → 0     |  H4 → 3000  |  D1 → 10000 |  W1 → 30000

ComputeTP(sig):
    base = sig.postWick                                       // [ปลา3 p.8] "วัดจากไส้หลัง Sig เสมอ"
    switch (TPMode):

      case FIXED:                                             // ตามหนังสือเป๊ะ
          d1 = PtsToPrice(RunPts(TF_SIG))
          d2 = PtsToPrice(RunPts2(TF_SIG))

      case ATR_SCALED:                                        // [CASE-001] + [SPEC-เดา]
          scale = ATR(TF_SIG, ATRPeriod, 1) / PtsToPrice(ATRRefPts[TF_SIG])
          scale = clamp(scale, ScaleMin, ScaleMax)             // กัน ATR ระเบิดตอนข่าว
          d1 = PtsToPrice(RunPts(TF_SIG))  * scale
          d2 = PtsToPrice(RunPts2(TF_SIG)) * scale

      case INDICATOR_RATIO:                                   // เลียนแบบ indicator ที่เห็นในจอ
          d1 = TP1Mult * ATR(TF_SIG, ATRPeriod, 1)
          d2 = d1 * TP2Ratio                                   // TP2Ratio = 1.7947  [CASE-001]

    sig.tp1 = base ± d1;  sig.tp2 = base ± d2
```

### 1.6.3 พักครึ่ง / พักสวิง (§4.5 โค้ชขัดกัน — 3 สูตร)

```
double RestPrice(sig, formula):
    far = ราคาไส้ที่กราฟไปไกลสุดนับจาก sig
    switch (formula):
      PLA:   return (sig.postWick + far) / 2                   // [ปลา3 p.49]
      KAEW:  return (LowestWick(window) + HighestWick(window)) / 2   // [แก้ว1 p.41]
      MOJA:  return (PA_Extreme(sig.pa) + far) / 2              // [โม1 p.36]
// [ปลา3 p.49][โม1 p.36] "จุดพักใช้แล้วใช้ซ้ำไม่ได้" → ต้องมี flag consumed
```

---

## 1.7 เงื่อนไขห้ามเข้า 9 ข้อ `[GLOSSARY §2.5]`

```
bool PassAllGates(sig, ctx):

  // 1. ชุด PA ถูกทำลาย  [ปลา3 p.18] [โม1 p.16]
  if (sig.state == DESTROYED) return false

  // 2. Sig ครบรอบวิ่ง TP2 แล้ว  [ปลา3 p.18]   (§4.3 โค้ชขัดกัน → พารามิเตอร์)
  if (sig.reachedTP2 && !AllowTradeAfterTP2) return false

  // 3. จุดเช็ค (ไส้หลัง Sig) ถูกทำลาย  [ปลา3 p.18]
  if (sig.destroyed) return false

  // 4. ราคาทำลายจุดเช็คเกิน 300 จุด → ห้ามฝืนเข้า  [โม1 p.38]
  if (BreachPts(sig.checkpoint) > NoForceEntryPts) return false     // default 300

  // 5. PIN เกิดผิดที่ = Fake Sig  [แก้ว1 p.9]
  if (IsFakeSig(sig.pa, ctx)) return false

  // 6. อยู่ใน SW → ห้ามหา SIG  [แก้ว1 p.15]
  if (ctx.cycle == SW && !AllowSigInSW) return false

  // 7. วอลุ่มเยอะ (H4 ปิดสีเดียวกันเกิน 3 แท่ง) → ห้ามเล่นรอบพักตัว  [โม1 p.34]
  if (sig.setup == REST_SETUP && ConsecSameColor(TF_SIG,1) > MaxConsecColor) return false

  // 8. ห้ามเก็บ BODY ใน SW  [แก้ว1 p.36]
  if (sig.setup == BODY_SETUP && ctx.cycle == SW) return false

  // 9. EMA14 แบน/ขนานพื้น = SW → ไม่ควรเข้า  [โม2 p.77]
  if (UseEmaFlatFilter && IsEmaFlat(TF_SIG)) return false

  // --- นอกเหนือ 9 ข้อ: ประตูบริบทที่ต้องผ่านด้วย ---
  if (!TrendGate(sig.dir, ctx))   return false      // [ปลา3 p.63] เทรนด์ชนะทุกอย่าง
  if (!AtFrame(sig.pa, ctx))      return false      // [ปลา3 p.42] "SIG = PA ที่เกิดที่กรอบ"
  if (FrameStrength(sig.pa.frame, ctx) < MinFrameStrength) return false
  if (!MTF_Confirm(sig.pa))       return false      // [โม1 p.37-38]
  // [แก้ว1 p.14] ไม่มี SIG ค้าง → ห้ามเล่นที่รับ/ต้านย่อย
  if (sig.pa.frame.src == DAY_MID && PendingSigs().size == 0) return false
  return true
```

---

## 1.8 การเข้าและออก

```
enum EntryMode { MODE1_PA_ONLY, MODE2_CONFIRMED, MODE_SNIPER, MODE_RETEST, MODE_M5BREAK }
```

| Mode | เข้าตอนไหน | SL | ที่มา | เทสได้ที่ชั้น |
|---|---|---|---|---|
| `MODE1_PA_ONLY` | market ระหว่างแท่งไส้หลังซิกกำลังก่อตัว | เหนือ/ใต้ชุด PA | `[ปลา3 p.16]` จังหวะ 1 | 1 |
| `MODE2_CONFIRMED` | market ที่ open แท่งถัดจากไส้หลังซิกปิด | เหนือ/ใต้ไส้หลังซิก | `[ปลา3 p.17]` จังหวะ 2 | 1 |
| `MODE_SNIPER` | limit วางที่ราคาไส้หลังซิกพอดี | ใต้ไส้หลังซิก | `[โม2 p.78-80]` | 1 |
| `MODE_RETEST` | limit + ต้องไม่ทำลายไส้เกิน 300 จุด | ใต้ไส้หลังซิก | `[โม1 p.39]` | 1 |
| `MODE_M5BREAK` | รอ PA M1/M5 + เบรกที่กรอบ | ตามกรอบ | `[แก้ว1 p.29-33]` `[ปลา3 p.53]` | **2 เท่านั้น** |

```
OpenTrade(sig, mode):
    if (mode in {MODE_SNIPER, MODE_RETEST}):
        PlaceLimit(price = sig.postWick ± PtsToPrice(RetestBufferPts),
                   expiry = now + RetestExpiryBars * PeriodSeconds(TF_SIG))     // [SPEC-เดา]
    else:
        Market(entry = (dir==BUY ? Ask : Bid))

    sl  = ComputeSL(sig, mode);  if (sl == REJECT) return
    lots = ComputeLots(entry, sl)                                                // §1.9
    Send(dir, lots, sl, tp = sig.tp2)      // TP order วางที่ TP2 · TP1 จัดการเองใน ManagePositions
```

```
ManageOpenPositions():
    for pos in positions:
        // ปิดบางส่วนที่ TP1  [ปลา3 p.11 "พิจารณาเก็บออเดอร์ TP1"] — % ไม่มีในเล่ม  [SPEC-เดา]
        if (!pos.tp1Taken && Reached(pos.tp1)):
            ClosePartial(pos, TP1ClosePercent)
            pos.tp1Taken = true

        // ⚠️ กฎ 4 ของโมจา: ห้ามย้าย SL  [โม2 p.84]
        if (AllowBreakEvenAfterTP1):                 // default = FALSE
            MoveSL(pos, pos.entry)                   // ⚠️ เปิดข้อนี้ = ละเมิดกฎ 4 โดยตรง ต้อง log ไว้

        // ออกเมื่อจุดเช็คถูกทำลาย (สำหรับ MODE1 ที่ SL อยู่ไกลกว่าจุดเช็ค)
        if (ExitOnCheckpointBreak && sig.destroyed) Close(pos)
```

---

## 1.9 Risk Guard — กฎ 15 ข้อของโมจา `[โม2 p.84-89]` + ลูกแก้ว `[แก้ว1 p.63]`

```
// กฎ 5: 1 ไม้ = 1 Risk ที่กำหนดไว้ล่วงหน้า  [โม2 p.84]
double ComputeLots(entry, sl):
    riskMoney = (SizingMode == PERCENT)
              ? AccountEquity() * RiskPercent/100
              : AccountEquity() / PlansPerDay                    // [ปลา3 p.67] ทุน ÷ จำนวนแผน
    slPts = DistancePts(entry, sl)
    lots  = riskMoney / (slPts * PT * TickValuePerLot)

    // กฎ 15: ไม้ถัดไปต้องเล็กลง หรือไม่เข้าเลย  [โม2 p.89]
    if (lastTradeResult == LOSS):
        lots = MathMin(lots, lastLots * LossReduceFactor)        // default 0.5
    return NormalizeLot(lots)                                     // floor ลง lotStep เสมอ ห้ามปัดขึ้น

bool RiskGuardAllowsNewTrade():
    // กฎ 11: แพ้ติดกัน 2 ไม้ = หยุดเทรดทันที  [โม2 p.87]
    if (consecutiveLosses >= MaxConsecLosses) return false        // default 2, reset ตอนขึ้นวันใหม่

    // [แก้ว1 p.63] ไม่เทรดเกิน 3 ครั้ง/วัน · ได้ TP 1 ครั้ง → ออกตลาด · SL 1-2 ครั้ง → ออกตลาด
    if (tradesToday >= MaxTradesPerDay)  return false             // default 3
    if (StopAfterFirstTP && winsToday >= 1) return false
    if (lossesToday >= MaxLossesPerDay)  return false             // default 2

    if (openPositions >= MaxOpenPositions) return false           // [SPEC-เดา] default 1
    if (dailyLossPct >= MaxDailyLossPct)  return false            // [SPEC-เดา] default 3%
    if (equityDDPct  >= MaxTotalDDPct)    return false            // [SPEC-เดา] default 15% → หยุดถาวรรอคน
    return true
```

**กฎ 4 — ห้ามย้าย SL (No Exception)** `[โม2 p.84]` แปลงเป็น invariant ที่ตรวจได้:

```
// ตรวจทุก tick — ถ้าละเมิดให้ EA หยุดตัวเองและ log
assert(pos.sl == pos.slAtOpen || AllowBreakEvenAfterTP1 == true)
```

---

# §2 ช่องที่โค้ดไม่ได้ — ค่าที่เราเดา (`[SPEC-เดา]` ทั้งหมด)

> ⚠️ **ทุกช่องในตารางนี้คือ "เราเดา" ไม่ใช่คำโค้ช** — ถ้า backtest ออกมาแย่ ต้องสงสัยตารางนี้ก่อนสงสัยระบบ

| # | ช่องว่าง | ค่าที่เสนอ | เหตุผล / ที่มาของค่า |
|---|---|---|---|
| **G1** | นิยาม Pin เป็นตัวเลข (`§5 ข้อ1`) | `PinWickBodyRatio=2.0`<br>`PinWickRangePct=0.60`<br>`PinOppWickPct=0.15` | Pin bar สากลนิยาม **ไส้ ≥ 2× เนื้อ** (Martin Pring / Steve Nison hammer) · Hammer มาตรฐาน = ไส้ ≥ 2/3 ของ range → ปัดเป็น 0.60 ให้หลวมนิดหน่อยกับทองที่ไส้สองข้าง · ไส้ตรงข้าม ≤ 15% กันไม่ให้ Doji-ไส้ยาวสองข้างมานับเป็น Pin |
| **G2** | นิยาม Doji เป็นตัวเลข (`§5 ข้อ2`) | `DojiBodyRangePct=0.10`<br>`DojiVsFirstPct=0.30` | Doji สากล = เนื้อ ≤ 5-10% ของ range · เลือก 10% (หลวมกว่า) เพราะทองแทบไม่มีแท่งเนื้อ 0 จริง · เพิ่มเงื่อนไขเทียบแท่งแรกด้วย เพราะ "Doji" ในบริบท star ต้องเล็กเมื่อเทียบแท่งก่อนหน้า ไม่ใช่เล็กลอยๆ |
| **G3** | PAT3 แบบ2 ต่างจากแบบ1 ยังไง (`§5 ข้อ3`) | `แบบ2 := เล็ก(≤50% ของแท่งแรก) AND ไม่ใช่ Doji` | ทำให้สองแบบ **แยกจากกันสนิท** ไม่ทับซ้อน · ไม่มีในเล่ม แต่เป็นการอ่านที่สอดคล้องที่สุดกับตาราง `[ปลา3 p.3]` ที่เขียนว่า "แท่งกลางเล็ก (ไม่ใช่ Doji)" |
| **G3b** | "แท่ง 2+3 ปิดรวมกันเกิน 50%" หมายถึงอะไร | `Pat33_Mode = CLOSE_LEVEL` | อ่านได้ 2 ทาง: (ก) ราคาปิดแท่ง 3 อยู่เหนือครึ่งบอดี้แท่ง 1 (ข) ผลรวมบอดี้เขียว 2 แท่ง > ครึ่งบอดี้แท่ง 1 · เลือก (ก) เป็น default เพราะตรงกับรูป Three Inside Up `[โม1 p.6]` และวัดง่ายกว่า — **แต่ต้อง A/B เทส** |
| **G4** | **"เกิดที่กรอบ" คือใกล้กี่จุด** (`§5 ข้อ4`) 🔴 | `TouchInPts=50`<br>`OverlapPts=300` (จากเล่ม) | ⚠️ **ห้ามใช้ ±300 เป็น "ใกล้"** — เส้นสถิติห่าง 500 หน้าต่าง 600 จะคลุมทั้งกราฟ ตัวกรองตายทันที · เลือกใช้นิยาม **"ไส้แตะกรอบแล้วเนื้อปิดกลับ"** แทน ซึ่งตรงกับ *เบรก = หยุด* `[ปลา3 p.2]` และใช้ OVL 300 เป็น "ทะลุได้แค่ไหน" ตามเล่ม |
| **G5** | นับปลายไส้สัมผัส 7-14 จุดยังไง (`§5 ข้อ5`) | `TouchLookbackBars=500`<br>`TouchBandPts=30`<br>`TouchSeparationBars=3`<br>`MinTouches=7 / MaxTouches=14` | 500 แท่ง H4 ≈ 3.5 เดือน = ความจำที่กรอบยังมีความหมาย · band 30 จุด = 0.6% ของ ATR H4 ปัจจุบัน · **separation 3 แท่ง คือตัวสำคัญ** ไม่มีตัวนี้ การแกว่งกองเดียวจะถูกนับเป็น 10 สัมผัส ทำให้ทุกเส้นผ่านเกณฑ์ |
| **G6** | SW วัดเป็นตัวเลขยังไง | ใช้ **Sig ชน Sig** เป็นหลัก `[โม1 p.31]` + `EMA14 flat` เป็นตัวเสริม `[โม2 p.77]` | อันนี้ **เกือบไม่ต้องเดา** — โค้ชให้นิยามเชิงเหตุการณ์มาแล้ว แค่แปลงเป็น state machine · ที่เดาคือค่า `EmaFlatATR=0.5`, `EmaSlopeBars=10` |
| **G7** | "เทรนด์" วัดยังไง | `BiasMode=DAY_CANDLE` | `[ปลา3 p.52]` บอกตรงๆ ว่า "ทุก 7 โมงเช้า ดู TF Day ปิดแท่งสีอะไร" → ใช้สีแท่ง D1 ที่ปิดล่าสุดเป็น default (มีที่มาจากเล่ม) · ทางเลือกอื่นเป็นของเรา |
| **G8** | "วอลุ่มเยอะ" (`§5 ข้อ8`) | `MaxConsecColor=3` (จากเล่ม) + `TickVolZ > 1.5` เป็นทางเลือก | `[โม1 p.34]` ให้เกณฑ์เดียวคือ "H4 ปิดเขียวเกิน 3 แท่ง" ใช้ตรงๆ ได้ · z-score ของ tick volume เป็นของเราเพิ่ม เพราะ tick volume ของ MT5 ≠ วอลุ่มจริง ใช้ได้แค่หยาบๆ |
| **G9** | ปิดบางส่วนที่ TP1 กี่ % (`§5 ข้อ6`) | `TP1ClosePercent=50` | ไม่มีในเล่มไหน · 50% เป็นค่ากลางที่ทำให้ผลลัพธ์อยู่กึ่งกลางระหว่าง "ปิดหมดที่ TP1" กับ "ถือไป TP2" → เป็นจุดตั้งต้นที่ยุติธรรมกับทั้งสองฝั่งตอน sweep |
| **G10** | ห้ามเทรดช่วงข่าวไหม (`§5 ข้อ7`) | `NewsBlackoutMin=0` (ปิดไว้ก่อน) | ✅ ตรวจแล้ว `[แก้ว1 p.73-74]` **ไม่มีกฎเวลาห้ามเทรดในเล่มเลย** → **ห้ามใส่เอง** ในระดับ 0/1 เพราะจะเป็นการเพิ่มเปรียบที่ระบบไม่ได้มี · ทำเป็น option ไว้ทดสอบที่ระดับ 2 เท่านั้น |
| **G11** | ขนาดขั้นต่ำของแท่ง/บอดี้ | `MinBarRangePts=80`<br>`MinBodyPts=30` | กัน PA จิ๋วช่วงตลาดเงียบ (03:00-06:00) ที่รูปทรงถูกแต่ไม่มีความหมาย · 80 จุด ≈ 30% ของ ATR H4 ยุคทอง 2,500 |
| **G12** | ไส้ไหนคือ "ไส้ของ Sig" ตอนวาง SL | ตามกฎ W4 `[ปลา3 p.9]` | เล่มให้กฎมาแล้ว (ไส้หลังเกินครึ่งเนื้อชุด → ใช้ไส้ยาวสุดของชุด PA) แค่ต้องนิยาม "เนื้อเทียนชุด Sig" = `PA_BodySpan` ← **ตรงนี้เราเดา** |
| **G13** | แท่งไส้หลังซิกปิดทางไหน 🔴 **ขัดกันที่เพิ่งเจอ** | `PostWickCloseRule = OPPOSITE` | `[โม1 p.16]` "ต้องปิดแท่งลง" (สำหรับ Sig Buy) VS `[แก้ว1 p.3]` "ไส้หลัง SIG จะวิ่งไปทิศทางเดียวกับ SIG" · **ยังไม่มีใน GLOSSARY §4 — ควรเพิ่ม** · เลือก OPPOSITE เป็น default เพราะโมจาเขียนเป็นเงื่อนไขชัด ส่วนลูกแก้วเขียนเป็นคำบรรยาย |
| **G14** | กฎ 50% ใช้กับ PAT3 แบบ1/แบบ2 ด้วยไหม | `Apply50RuleToPat3_1_2 = true` | `[ปลา3 p.5]` ขึ้นต้นว่า "ชุด PA **ทั้ง 5 รูปแบบ**..." แล้วค่อยยกตัวอย่างเฉพาะ PAT2/PAT3-3 · เลือกตีความแบบเข้ม (ใช้กับทุกแบบ) เป็น default |
| **G15** | เวลา 07:00 ของใคร | `ServerGMTOffset` เป็น input + assert | `[แก้ว1 p.72]` บอกว่าต้องเป็น Exness/GBE · เลื่อน 1 ชม. = PA เปลี่ยนทั้งกระดาน → ต้องบังคับตรวจตอน OnInit |
| **G16** | ต้นทุนการเทรด | `SpreadPts=25`, `SlippagePts=15`<br>`SlippageAtFramePts=30` | สเปรดทองบัญชี standard ≈ 20-35 จุด · **ที่กรอบเลขกลมต้องคิด slippage หนักกว่าปกติ** เพราะเป็นที่ที่ stop กองกันอยู่ (order clustering) |
| **G17** | หลาย PA ยิงพร้อมกัน | dedupe ตาม `(dir, postWickBar)` | `[CASE-001]` แสดงชัดว่า indicator เองก็แสดงซ้ำ (PA12/PA1 คู่กัน) · **ไม่ dedupe = N พองเท่าตัว = สถิติปลอม** |
| **G18** | TP2 = 2× หรือ 1.795× TP1 | `TP2Ratio` เป็น input (2.0 / 1.7947) | หนังสือ = 2.0 `[ปลา3 p.8]` · ของจริงบนจอ = 1.7947 `[CASE-001]` — ดู §5.1 |
| **G19** | เลขจุดตายตัวควรสเกลตาม ATR ไหม | `TPMode` + `ScaleAllPointConstants` | ดู §5.1 — **เรื่องใหญ่ที่สุดในเอกสารนี้** |
| **G20** | ลำดับ TP/SL ในแท่งเดียวกัน | `IntrabarOrder = SL_FIRST` | H4 หนึ่งแท่งกิน 300 จุด และ 1,500 จุดได้พร้อมกัน · ต้องสมมติแบบมองโลกร้าย ไม่งั้น backtest สวยหลอกทันที |

**สรุป: 20 ช่อง · เป็นการเดาล้วน 13 ช่อง · มีฐานจากเล่มบางส่วน 7 ช่อง (G6, G7, G8, G10, G12, G15, G18)**

---

# §3 ตารางพารามิเตอร์ทั้งหมด

## 3.1 โครงสร้าง / ระบบ

| ชื่อ | Default | ช่วง sweep | ที่มา |
|---|---|---|---|
| `TF_SIG` | H4 | H1, H4 | `[ปลา3 p.51]` |
| `ServerGMTOffset` | 3 | 2, 3 | `[แก้ว1 p.72]` |
| `DayFrameHourLocal` | 7 | — | `[ปลา3 p.27]` |
| `IntrabarOrder` | SL_FIRST | — | `[SPEC-เดา]` |

## 3.2 รูปแบบ PA

| ชื่อ | Default | ช่วง sweep | ที่มา |
|---|---|---|---|
| `PinWickBodyRatio` | 2.0 | 1.5 – 3.0 | `[SPEC-เดา]` |
| `PinWickRangePct` | 0.60 | 0.50 – 0.75 | `[SPEC-เดา]` |
| `PinOppWickPct` | 0.15 | 0.10 – 0.30 | `[SPEC-เดา]` |
| `DojiBodyRangePct` | 0.10 | 0.05 – 0.15 | `[SPEC-เดา]` |
| `DojiVsFirstPct` | 0.30 | 0.20 – 0.40 | `[SPEC-เดา]` |
| `SmallBodyVsFirstPct` | 0.50 | 0.35 – 0.70 | `[SPEC-เดา]` |
| `Engulf50Min` | 0.50 | **ล็อก** | `[ปลา3 p.5]` — เลขจากเล่ม ห้าม sweep ที่ระดับ 0/1 |
| `Pat33_MinPen` | 0.50 | **ล็อก** | `[ปลา3 p.6]` |
| `Pat33_Mode` | CLOSE_LEVEL | CLOSE_LEVEL / BODY_SUM | `[SPEC-เดา]` G3b |
| `Pat33_NoNewExtreme` | true | true / false | `[SPEC-เดา]` |
| `Pat33_RequireHarami` | false | true / false | `[SPEC-เดา]` |
| `Pat2_RequireFullBodyEngulf` | false | true / false | `[SPEC-เดา]` |
| `Apply50RuleToPat3_1_2` | true | true / false | `[SPEC-เดา]` G14 |
| `MinBarRangePts` | 80 | 0 – 150 | `[SPEC-เดา]` |
| `MinBodyPts` | 30 | 0 – 80 | `[SPEC-เดา]` |

## 3.3 ไส้หลังซิก

| ชื่อ | Default | ช่วง sweep | ที่มา |
|---|---|---|---|
| `PostWickCloseRule` | OPPOSITE | OPPOSITE / SAME / ANY | `[โม1 p.16]` vs `[แก้ว1 p.3]` **โค้ชขัดกัน** |
| `PostWickBodyMaxPct` | 0.50 | **ล็อก** | `[โม1 p.16]` |
| `PostWickWickMaxMult` | 1.00 | **ล็อก** | `[โม1 p.16]` |
| `WickBreakTolPts` | 300 | **0 / 1 / 300** | **§4.1 โค้ชขัดกัน 3 เสียง** `[แก้ว1 p.3]` `[ปลา3 p.10]` `[โม1 p.39]` |
| `NoForceEntryPts` | 300 | **ล็อก** | `[โม1 p.38]` |
| `AllowSelfBreakAfterTP2` | false | true / false | `[โม1 p.39]` |

## 3.4 กรอบ

| ชื่อ | Default | ช่วง sweep | ที่มา |
|---|---|---|---|
| `TouchInPts` | 50 | 20 – 150 | `[SPEC-เดา]` G4 |
| `OverlapPts` | 300 | **ล็อก** | `[ปลา3 p.2]` |
| `TouchLookbackBars` | 500 | 250 – 1000 | `[SPEC-เดา]` G5 |
| `TouchBandPts` | 30 | 15 – 60 | `[SPEC-เดา]` G5 |
| `TouchSeparationBars` | 3 | 1 – 6 | `[SPEC-เดา]` G5 |
| `MinTouches` / `MaxTouches` | 7 / 14 | **ล็อก** | `[ปลา3 p.2]` |
| `MinFrameStrength` | 1 | 0 – 3 | `[SPEC-เดา]` |
| `HoldFrameBars` | 3 | **1 / 3** | **§4.2 โค้ชขัดกัน** `[ปลา3 p.21]` vs `[แก้ว1 p.3]` |

## 3.5 เทรนด์ / บริบท

| ชื่อ | Default | ช่วง sweep | ที่มา |
|---|---|---|---|
| `BiasMode` | DAY_CANDLE | DAY_CANDLE / EMA14 / SWING | `[ปลา3 p.52]` / `[โม2 p.77]` |
| `RequireDayAlign` | true | true / false | `[โม2 p.90]` |
| `RequireWeekAlign` | false | true / false | `[โม2 p.90]` |
| `Require3TFAlign` | false | true / false | `[แก้ว1 p.16]` |
| `RequireH1Confirm` | false | true / false | `[โม1 p.37-38]` |
| `PostWickH1Slack` | 2 | 0 – 4 | `[SPEC-เดา]` |
| `AllowSigInSW` | false | **ล็อก false** | `[แก้ว1 p.15]` |
| `UseEmaFlatFilter` | true | true / false | `[โม2 p.77]` |
| `EmaFlatATR` | 0.5 | 0.3 – 0.8 | `[SPEC-เดา]` |
| `EmaSlopeBars` | 10 | 5 – 20 | `[SPEC-เดา]` |
| `MaxConsecColor` | 3 | **ล็อก** | `[โม1 p.34]` |

## 3.6 TP / SL

| ชื่อ | Default | ช่วง sweep | ที่มา |
|---|---|---|---|
| `TPMode` | FIXED | FIXED / ATR_SCALED / INDICATOR_RATIO | `[ปลา3 p.8]` vs `[CASE-001]` |
| `TP2Ratio` | 2.0 | 1.7947 / 2.0 | `[ปลา3 p.8]` vs `[CASE-001]` §4.x |
| `ATRPeriod` | 14 | 10 – 20 | `[SPEC-เดา]` |
| `ATRRefPts[H4]` | 1500 | ต้องวัดจากข้อมูลจริง | `[SPEC-เดา]` — ดู §5.1 |
| `TP1Mult` | 1.0 | 0.8 – 1.5 | `[SPEC-เดา]` |
| `ScaleMin` / `ScaleMax` | 0.5 / 2.5 | — | `[SPEC-เดา]` กัน ATR ระเบิด |
| `AllowTradeAfterTP2` | false | true / false | **§4.3 โค้ชขัดกัน 3 เสียง** |
| `TP1ClosePercent` | 50 | 0 / 25 / 50 / 100 | `[SPEC-เดา]` G9 |
| `AllowBreakEvenAfterTP1` | **false** | true / false | ⚠️ true = ละเมิดกฎ 4 `[โม2 p.84]` |
| `ExitOnCheckpointBreak` | false | true / false | `[SPEC-เดา]` |
| `SLBufferPts` | 20 | 10 – 50 | `[SPEC-เดา]` |
| `SLMinPts` | 100 | 80 – 150 | `[แก้ว1 p.64]` "รีด SL 100-300" |
| `SLMaxPtsAsia` / `SLMaxPtsUS` | 300 / 500 | **ล็อก** | `[แก้ว1 p.37]` `[โม2 p.90]` |
| `SLCapMode` | SKIP | SKIP / CLAMP | `[SPEC-เดา]` |
| `RestFormula` | PLA | PLA / KAEW / MOJA | **§4.5 โค้ชขัดกัน 3 สูตร** |

## 3.7 Entry / Risk

| ชื่อ | Default | ช่วง sweep | ที่มา |
|---|---|---|---|
| `EntryMode` | MODE2_CONFIRMED | MODE1 / MODE2 / SNIPER / RETEST | `[ปลา3 p.16-17]` `[โม2 p.78]` `[โม1 p.39]` |
| `RetestBufferPts` | 0 | 0 – 100 | `[SPEC-เดา]` |
| `RetestExpiryBars` | 8 | 4 – 16 | `[SPEC-เดา]` |
| `SizingMode` | PERCENT | PERCENT / PLANS | `[ปลา3 p.67]` `[แก้ว1 p.60]` |
| `RiskPercent` | 2.0 | 1 – 5 | `[แก้ว1 p.60]` "2-5% ต่อไม้" |
| `PlansPerDay` | 3 | 3 | `[ปลา3 p.67]` |
| `MaxConsecLosses` | 2 | **ล็อก** | `[โม2 p.87]` กฎ 11 |
| `LossReduceFactor` | 0.5 | 0.3 – 1.0 | `[โม2 p.89]` กฎ 15 (ค่าตัวเลข = `[SPEC-เดา]`) |
| `MaxTradesPerDay` | 3 | **ล็อก** | `[แก้ว1 p.63]` |
| `StopAfterFirstTP` | true | true / false | `[แก้ว1 p.63]` |
| `MaxLossesPerDay` | 2 | **ล็อก** | `[แก้ว1 p.63]` |
| `MaxOpenPositions` | 1 | 1 – 3 | `[SPEC-เดา]` |
| `MaxDailyLossPct` | 3.0 | — | `[SPEC-เดา]` |
| `MaxTotalDDPct` | 15.0 | — | `[SPEC-เดา]` |
| `SpreadPts` / `SlippagePts` | 25 / 15 | — | `[SPEC-เดา]` G16 |

## 3.8 Preset ต่อโค้ช (§3 ของ GLOSSARY)

| Parameter | **แม่ปลา** | **ลูกแก้ว** | **โมจา** |
|---|---|---|---|
| `WickBreakTolPts` | 1 | 0 | 300 |
| `HoldFrameBars` | 3 | 1 | 1 |
| `AllowTradeAfterTP2` | false | true | true |
| `RestFormula` | PLA | KAEW | MOJA |
| `SLMaxPtsAsia/US` | 300 / 300 | 300 / 500 | 300 / 500 |
| `EntryMode` | MODE2 | MODE1 (เก็บ BODY) | MODE_RETEST |
| `MaxTradesPerDay` | ∞ | 3 | ∞ |
| `StopAfterFirstTP` | false | **true** | false |
| `TPMode` | FIXED | FIXED | FIXED (ล.1) / 1000จุด (ล.2) |
| `RequireDayAlign` | true | true | **true (เข้มสุด)** |
| `UseEmaFlatFilter` | false | false | **true** |
| `RequireH1Confirm` | false | false | **true** |
| ถอนกำไร | สินทรัพย์ | ไม่ถอน | ทุกวัน |

---

# §4 แผน backtest 3 ระดับ

## 4.0 ชุดข้อมูลและกติกาที่ใช้ร่วมทุกระดับ

```
Symbol   : XAUUSD (โบรกเดียวกับที่จะรันจริง)
Period   : 2019-01-01 → ปัจจุบัน  (≥ 6 ปี, ครอบทั้งทองขาขึ้นและช่วงย่อ)
Model    : "1 minute OHLC" สำหรับ logic H4 ล้วน · "Every tick based on real ticks" ถ้าแตะ M5
Costs    : spread 25 จุด + slippage 15 จุด (30 จุดที่กรอบ) + swap จริง + commission จริง
Intrabar : SL ก่อน TP เสมอ
Holdout  : กัน 2 ปีสุดท้ายไว้ ห้ามแตะจนถึงระดับ 2 ⚠️
```

**สิ่งที่ต้องทำก่อนกดปุ่ม backtest ครั้งแรก:**
1. รัน **signal census** ก่อน — นับว่าในช่วงข้อมูลนี้ระบบยิง Sig กี่ตัว (หลัง dedupe), ตัวละกี่แท่ง, กระจายตัวยังไงต่อปี
2. ถ้า N < 300 → **หยุด** เปลี่ยน TF หรือขยายช่วงข้อมูล ก่อนจะเสียเวลาตีความผลที่ไม่มีนัยสำคัญ

---

## 4.1 ระดับ 0 — baseline เปลือย

**ทำอะไร:** `Sig H4 (PA 5 รูปแบบ + ไส้หลังซิก 3 ข้อ)` → `SL ไส้` → `TP ตายตัว`
**ปิดทุกอย่าง:** ไม่ดูกรอบ ไม่ดูเทรนด์ ไม่ดู SW ไม่มีเงื่อนไขห้ามเข้า ไม่มี risk guard (คิดขนาดไม้คงที่)
**ตอบคำถามเดียว:** *ตัวสัญญาณเองมีเปรียบไหม*

### ⚠️ ปัญหาที่ต้องรู้ก่อน — RR 1:5 พิสูจน์ยากมาก

```
TP 1500 / SL 300 → R = 5 → breakeven winrate = 1/6 = 16.7%
sd ของผลต่อไม้ ณ จุด breakeven ≈ 2.24R

จำนวนไม้ที่ต้องใช้ให้ t = 2.0:
   เปรียบ 0.10R → N ≈ 2,000 ไม้    ← เป็นไปไม่ได้ (H4 ให้ ~100-200 ไม้/ปี)
   เปรียบ 0.25R → N ≈   320 ไม้    ← พอไหว
   เปรียบ 0.50R → N ≈    80 ไม้
```

→ **ถ้าวัดจากกำไร/ขาดทุนต่อไม้อย่างเดียว จะไม่มีวันพิสูจน์เปรียบเล็กๆ ได้เลยด้วยข้อมูลที่มี**
→ **ระดับ 0 ต้องวัดจาก "เส้นทางราคา" ไม่ใช่ผลไม้** เพราะทุกแท่งให้ข้อมูล ไม่ใช่แค่ชนะ/แพ้

### เกณฑ์ผ่านระดับ 0 (ต้องผ่าน A และ B)

| # | ตัววัด | เกณฑ์ผ่าน | ทำไมเลือกเลขนี้ |
|---|---|---|---|
| **A** | **Forward excursion** — ค่าเฉลี่ยของ `dir × (Close[t+6] − entry) / ATR` หักด้วยค่าเฉลี่ยเดียวกันของ "ทุกแท่ง" (คุมอคติทองขาขึ้น) | **≥ +0.20 ATR · t ≥ 2.5** | 6 แท่ง H4 = 1 วัน · หัก drift ออกเพื่อกันสรุปว่าเก่งทั้งที่แค่ถือทอง · sd ≈ 2.4 → N 300 ให้ t พอ |
| **B** | **การแข่ง TP/SL** — `P(MFE ถึง 1500 ก่อน MAE ถึง 300)` | **≥ 22%** และ **bootstrap 95% CI ขอบล่าง > 18%** | breakeven 16.7% · +ต้นทุน 40 จุด ≈ 18.5% · ต้องเผื่อขอบเหนือต้นทุนจริงถึงจะเรียกว่าเปรียบ |
| **C** | Expectancy จริง (มีต้นทุน) | ≥ +0.05R · PF ≥ 1.10 | ตัววัดยืนยัน ไม่ใช่ตัวตัดสิน (power ต่ำเกินไป) |
| **D** | ความสม่ำเสมอ | บวกใน ≥ 4 จาก 6 ปี | กันปีเดียวแบกทั้งงาน |
| **E** | **แยกฝั่ง** | BUY-only และ SELL-only ต้อง ≥ breakeven − 0.05R **ทั้งคู่** | 🔴 **ข้อสำคัญที่สุด** — ทองขึ้นจาก 1,270 → 4,500 ใน 6 ปี ระบบที่เอียงซื้ออย่างเดียวจะสวยโดยอัตโนมัติ |
| **F** | N หลัง dedupe | ≥ 300 | ต่ำกว่านี้ตีความไม่ได้ |

**ผลลัพธ์ 3 แบบ และต้องทำอะไรต่อ:**

| ผล | แปลว่า | ทำต่อ |
|---|---|---|
| A ✅ B ✅ | สัญญาณมีเปรียบ และเรขาคณิต TP/SL รับเปรียบนั้นได้ | → ระดับ 1 |
| A ✅ B ❌ | มีเปรียบเชิงทิศทาง แต่ TP/SL ตั้งผิด | **แก้ TP/SL ก่อน** (ลอง ATR_SCALED) แล้ววนกลับ B — ห้ามข้ามไปเติมตัวกรอง |
| A ❌ | สัญญาณเปล่าไม่มีเปรียบ | 🛑 **หยุด** · **ห้ามหวังว่าตัวกรองจะช่วย** — การเอาตัวกรองไปใส่สัญญาณที่เปรียบเป็นศูนย์ จะได้ subset ที่ดูดีเสมอ นั่นคือ overfitting ล้วนๆ ไม่ใช่การค้นพบ |

---

## 4.2 ระดับ 1 — เติมตัวกรอง

**เติม:** เกิดที่กรอบ (§1.4) · เทรนด์ Day/Week (§1.5.1) · เงื่อนไขห้ามเข้า 9 ข้อ (§1.7) · SW state

### วิธีทดสอบ — ทีละตัว ไม่ใช่เหมาโหล

```
สำหรับตัวกรองแต่ละตัว f ทำ 2 การทดสอบ:
   leave-one-in  : baseline + f เท่านั้น    → f มีค่าเดี่ยวๆ ไหม
   leave-one-out : ชุดเต็ม − f             → f มีค่าเมื่ออยู่กับตัวอื่นไหม
```

### เกณฑ์เก็บตัวกรองไว้ (ต้องผ่านครบ 4 ข้อ)

| # | เกณฑ์ | เหตุผล |
|---|---|---|
| 1 | Δexpectancy ≥ **+0.05R** | ต่ำกว่านี้กลืนหายในต้นทุน |
| 2 | เหลือไม้ ≥ **35%** ของระดับ 0 | ตัวกรองที่เหลือไม้ 5% = เลือก sample จนพอดี ไม่ใช่กรอง |
| 3 | t ของ Δ ≥ **1.5** | ไม่บังคับ 2.0 เพราะเป็นการทดสอบรอง แต่ต้องไม่ใช่ noise ล้วน |
| 4 | ช่วยใน ≥ **4 จาก 6 ปี** | ตัวกรองที่ช่วยปีเดียว = จำ regime |

### เกณฑ์ผ่านระดับ 1 (ชุดเต็ม)

```
expectancy ≥ +0.20R
N ≥ 150
Profit Factor ≥ 1.30
Max DD ≤ 25R
เทสบนช่วง 2019-2023 → ผลบนช่วงที่ไม่เคยแตะ 2024-2025 ต้อง ≥ 60% ของ expectancy ในช่วงแรก
```

> 💡 **ข้อสังเกตที่ต้องจับตา:** โค้ชบอกเองว่า **กรอบ 80% : SIG 20%** `[ปลา3 p.63]`
> ถ้าระดับ 0 ผ่านสบายแต่ตัวกรองกรอบเพิ่มค่าไม่ได้เลย → แปลว่า **เราตีความ "เกิดที่กรอบ" ผิด** (ช่อง G4/G5)
> ไม่ใช่ว่าโค้ชผิด — ให้กลับไปแก้ §1.4 ก่อนสรุปอะไร

---

## 4.3 ระดับ 2 — preset ต่อโค้ช + ลง M5

**ทำอะไร:** รัน 3 preset (§3.8) บนข้อมูลชุดเดียวกัน ช่วงเดียวกัน ต้นทุนเดียวกัน + เปิด holdout 2 ปี + ลง M5 ตาม `[FIN CLAUDE.md ชั้น 2]`

### เกณฑ์ผ่าน

| # | เกณฑ์ | เหตุผล |
|---|---|---|
| 1 | ผู้ชนะต้องชนะที่สอง โดย **90% bootstrap CI ไม่ทับกัน** | ต่างกัน 5% บนตัวเลขเดียวไม่ใช่การชนะ |
| 2 | Walk-forward 6 fold (IS 12 เดือน / OOS 6 เดือน): **OOS บวก ≥ 4 จาก 6 fold** และ **OOS/IS ≥ 0.5** | มาตรฐานกันการจูนย้อนหลัง |
| 3 | **Parameter plateau** — ค่าที่เลือกต้องอยู่ในบริเวณที่ขยับ ±1 ขั้นทุกทิศแล้วยังได้ ≥ 70% ของยอด | ยอดแหลมโดดเดี่ยว = fitting noise 100% |
| 4 | จำนวนพารามิเตอร์ที่ optimize ตลอดทั้งงาน **≤ 8 ตัว** (ระดับ1 ≤5 + ระดับ2 ≤3) | 40 พารามิเตอร์ × 2,000 ไม้ = overfit แน่นอน |
| 5 | ลง M5 แล้ว expectancy ต้อง **เพิ่ม** ไม่ใช่แค่ไม่ลด | ถ้า M5 ไม่ช่วย = อย่าเพิ่มความซับซ้อน |
| 6 | ผ่านแล้ว → **demo อย่างเดียว** | กฎเหล็ก FIN ข้อ 5 |

### เทสความแกร่งเพิ่ม (ทำที่ระดับ 2)

```
1. เทสบน XAGUSD ด้วยพารามิเตอร์เดียวกัน (สเกลตาม ATR)
   → ถ้าพังสนิท = กฎถูก fit กับทองยุคนี้ ไม่ใช่หลักการราคา
2. Monte Carlo สลับลำดับไม้ 1,000 รอบ → ดู DD ที่ percentile 95
3. เลื่อนเวลาเปิดแท่ง H4 ±1 ชม. → ถ้าผลพลิกจากบวกเป็นลบ
   = ระบบเปราะกับ bar alignment เกินกว่าจะเทรดจริง 🔴
```

---

# §5 ประเมินความเสี่ยงเชิงสถิติ (ตรงไปตรงมา)

## 5.1 ⭐ เลข hardcode 1500/3000 จะพังตอนไหน — และ TP 27.04/48.53 คืออะไร

### สิ่งที่ตัวเลขบนจอบอก

```
ratio TP2/TP1 = 1.79473 ± 0.00013   (7 แถว — คงที่ระดับทศนิยม 4 ตำแหน่ง)
ระยะ TP1      = 26.81 – 27.21       (แกว่ง 1.5%)
TP1/ราคา      = 0.582% – 0.601%     (แกว่ง 3.2%)
```

| สมมติฐาน | ผลตรวจ |
|---|---|
| ค่าคงที่ตายตัว (1,500 จุด) | ❌ ระยะไม่เท่ากันทุกแถว |
| % คงที่ของราคา | ❌ ถ้าเป็น % ระยะ/ราคาต้องเท่ากันเป๊ะ แต่แกว่ง 3.2% (สูงกว่า error การปัดเศษ 100 เท่า) |
| % ของ "ราคาเข้า" ที่ต่างจาก PostWick | ❌ ถ้าเป็นแบบนั้น `TP2/TP1` ต้องคงที่ **ในหน่วยราคา** แต่วัดได้ 1.0046143 / 1.0046272 / 1.0046730 — ต่างกัน 30 เท่าของ error การปัดเศษ |
| **`TP1 = c₁ × V` และ `TP2 = c₂ × V`** โดย V = ตัวแปรความผันผวนของแท่งนั้น | ✅ **เหลือทางเดียว** — อธิบายได้ทั้งการที่ ratio นิ่งและระยะแกว่ง |

**หลักฐานยืนยันเพิ่ม:** สองแถวที่ใช้ PostWick เดียวกัน (`4591.41` → PA3-2 กับ PA2) ได้ V เท่ากันเป๊ะ ทั้งที่**รูปแบบ PA คนละแบบ คนละจำนวนแท่ง**
→ **V ไม่ได้มาจากขนาดของชุด PA แต่มาจากแท่ง/เวลา** = เป็นค่าความผันผวน (ATR หรือญาติ)

### 🎯 ข้อสรุปที่ใช้ออกแบบได้

```
เลข 1,500 จุด ในหนังสือ ≈ 0.588% ของราคาทอง
→ ย้อนกลับได้ว่าเลขนี้ถูกตั้งตอนทองอยู่ราว $2,550
→ วันนี้ทองอยู่ 4,570 → ระยะเดียวกันในเชิงความผันผวน = ~2,700 จุด
→ ตรงกับที่ indicator ใช้จริง (2,681–2,721 จุด) พอดี
```

**แปลว่า: "1,500 จุด" ไม่ใช่ค่าคงที่ของธรรมชาติ — มันคือ *ATR ของ H4 ในยุคที่หนังสือถูกเขียน* ที่ถูกแช่แข็งเป็นตัวเลข**
เช็คสอดคล้องทั้งตาราง `[ปลา3 p.8]`: 1,000 (H1) / 1,500 (H4) / 5,000 (D) / 15,000 (W) ≈ **1.0–1.5 เท่าของ ATR ของ TF นั้นๆ ในยุคทอง $2,500** ทั้งแถว

### สูตรที่เสนอ

```
regime_scale = ATR(TF, 14)[1] / ATR_ref[TF]
               โดย ATR_ref[H4] = 15.00 USD (= 1,500 จุด)   ← ต้องวัดยืนยันจากข้อมูลจริงก่อนใช้

TP1 = postWick ± 1500จุด × regime_scale
TP2 = postWick ± 3000จุด × regime_scale
```

⚠️ **แต่เรื่องไม่จบแค่ TP — เลข 300 จุด อันตรายกว่า**

| ค่าคงที่ | ที่ทอง $2,550 | ที่ทอง $4,570 | ผล |
|---|---|---|---|
| SL 300 จุด | 0.20 × ATR(H4) | **0.11 × ATR(H4)** | โดนกินด้วย noise เฉยๆ |
| OVL 300 จุด | 0.20 ATR | 0.11 ATR | "ทะลุกรอบ" เกิดบ่อยขึ้นเท่าตัว |
| ทำลายจุดเช็ค 300 จุด | 0.20 ATR | 0.11 ATR | Sig ตายเร็วกว่าที่โค้ชออกแบบ 2 เท่า |
| TP 1500 จุด | 1.00 ATR | 0.55 ATR | ถึงง่ายขึ้น → **winrate ดูดีขึ้น** |

> 🔴 **นี่คือกับดักหลัก:** ถ้า hardcode ทั้ง SL และ TP ตามเล่ม แล้วเทสบนทองยุคนี้
> จะได้ **winrate สูงขึ้น แต่ expectancy ต่ำลง** และรู้สึกว่า "ระบบใช้ได้" ทั้งที่ RR จริงถูกบีบไปแล้ว
> **ต้องมี `ScaleAllPointConstants` ที่คูณ regime_scale เข้าทุกค่าคงที่ ไม่ใช่แค่ TP**

### TP2 = 2× หรือ 1.795× ?

**สมมติฐาน P (เราเสนอ):** `TP1 = c₁·ATR`, `TP2 = c₂·ATR`, `c₂/c₁ = 1.7947` คงที่ตลอดกาล
**สมมติฐาน Q (`[CASE-001]` เสนอ):** `TP1 = 1500จุด·k`, `TP2 = 1500จุด·k²` โดย k = สเกลความผันผวน (ตอนนี้ 1.7947)

Q สวยมากและตัวเลขลงตัวน่าตกใจ (`TP1²/TP2 = 15.03 ≈ $15.00` = 1,500 จุดพอดี) **แต่มีจุดตายเชิงตรรกะ:**

```
ถ้า k = 1  (ทองกลับไป $2,550) → TP1 = TP2 = 1,500 จุด   ← TP2 ไร้ความหมาย
ถ้า k < 1  (ความผันผวนต่ำ)     → TP2 < TP1               ← เป็นไปไม่ได้
```

→ ไม่มีคนออกแบบ indicator คนไหนเขียนบันไดที่พังตอนความผันผวนกลับสู่ปกติ
→ **ให้น้ำหนัก P มากกว่า Q** แต่ยังไม่ปิดคดี

**การทดลองที่ตัดสินได้ในภาพเดียว:**
```
ขอให้พี่ฮอนแคปตารางเดิมอีกครั้ง ตอนความผันผวนต่างจากเดิมชัดๆ แล้วดูช่อง TP2/TP1:
   ยังเป็น 1.7947 เท่าเดิม  → สมมติฐาน P ถูก (คู่ตัวคูณคงที่ ฐานคือ ATR)
   เปลี่ยนไปเป็นเลขอื่น     → สมมติฐาน Q ถูก (บันไดเรขาคณิต ฐาน $15 คงที่)
```
พร้อมกันนั้น ถ้ารู้ ATR ณ แท่งนั้น จะแก้สมการหา `c₁` ได้ทันที (`c₁ = 27.0 / ATR`) แล้วรู้ว่า period/TF ไหน

**ระหว่างรอ:** ให้ `TPMode` เป็น input แล้วเทสทั้ง FIXED / ATR_SCALED / INDICATOR_RATIO ที่ระดับ 0 ข้อ B

---

## 5.2 ระบบนี้มีเปรียบเชิงทฤษฎีจากอะไรบ้าง

| # | แหล่งเปรียบ | หลักฐานทางวิชาการ | ความเห็น |
|---|---|---|---|
| 1 | **Round number effect** (กรอบ 0/5 ทุก 500 จุด) | มีจริง — order clustering ที่เลขกลม (Osler, งาน FX microstructure) **แต่** เปรียบเล็กและถูกอาร์บออกไปมากแล้ว | ⚠️ เกรด **C** · ยิ่งกริดถี่ (ทุก $5) ยิ่งเจือจาง · และเป็นที่ที่ slippage แย่ที่สุดเพราะ stop กองอยู่ |
| 2 | **Candlestick reversal** (PA 5 รูปแบบ) | งานวิจัยส่วนใหญ่: แพตเทิร์นแท่งเทียนเดี่ยวๆ **ไม่มีเปรียบ**ที่มีนัยสำคัญหลังหักต้นทุน | ⚠️ เกรด **D** · จุดอ่อนที่สุดของระบบ — และเป็นส่วนที่ระบบใช้ชื่อเยอะที่สุด |
| 3 | **RR ไม่สมมาตร + SL ที่มีความหมายเชิงโครงสร้าง** | จุดแข็งจริง — SL ใต้ไส้หลังซิกคือ **จุดที่สมมติฐานตาย** ไม่ใช่ระยะสุ่ม | ✅ เกรด **B+** · ทำให้ต้องการ winrate แค่ ~20% ก็อยู่ได้ |
| 4 | **Time-series momentum แฝง** ("เข้าเฉพาะทางเดียวกับ H4/Day") | เปรียบที่มีหลักฐานแน่นที่สุดในสินค้าโภคภัณฑ์ (Moskowitz-Ooi-Pedersen) และทองก็อยู่ในกลุ่มนั้น | ✅ เกรด **A−** · **น่าจะเป็นที่มาของเปรียบจริงส่วนใหญ่** |
| 5 | **วงจรความผันผวน** (SW → Sig → รอบวิ่ง → พักตัว) | = volatility clustering + breakout after compression — มีหลักฐานรองรับดี | ✅ เกรด **B** |

**สรุปตรงๆ:** เปรียบที่น่าจะมีจริงอยู่ที่ **(4) + (5) + (3)** ซึ่งคือ *เทรนด์ + จังหวะความผันผวน + สต็อปที่มีเหตุผล*
ส่วน **(2) การอ่านรูปทรงแท่งเทียน — ซึ่งเป็นชื่อยี่ห้อของระบบ — น่าจะให้เปรียบใกล้ศูนย์เมื่ออยู่เดี่ยวๆ**

นี่ตรงกับที่โค้ชพูดเองเป๊ะ: **"เทรนด์ ชนะ กรอบ · กรอบ ชนะ Sig · กรอบ 80% : Sig 20%"** `[ปลา3 p.63]`
→ ระบบรู้ตัวว่าค่าอยู่ที่บริบท ไม่ใช่ที่รูปแท่ง · **ปัญหาคือส่วนบริบทคือส่วนที่โค้ดได้ยากที่สุด**

---

## 5.3 จุดที่จะทำให้ backtest ออกมาสวยหลอก

| # | กับดัก | ทำไมอันตราย | วิธีกัน |
|---|---|---|---|
| 1 | **อ่านแท่ง index 0** | เห็นอนาคตครึ่งแท่ง H4 = 2 ชม. → ผลสวยจนหลอกตัวเอง | assert ใน `BAR()` + whitelist 3 ข้อ §0.2 · code review หา `,0)` ทุกจุด |
| 2 | **ลำดับ TP/SL ในแท่งเดียว** | H4 หนึ่งแท่งกิน 300 และ 1500 พร้อมกันได้ · ถ้า tester สมมติ TP ก่อน = ปลอมล้วน | `IntrabarOrder = SL_FIRST` เสมอ · ถ้าทำได้ให้ใช้ real tick |
| 3 | **สร้างกรอบด้วยข้อมูลอนาคต** | `CountTouches` ที่นับแท่งหลังจุดเข้า = look-ahead ที่มองไม่เห็น | นับจาก index 1 ย้อนหลังเท่านั้น · กรอบ SW/TPDONE ต้องมี timestamp กำกับว่าเกิดตอนไหน |
| 4 | **ไม่ dedupe PA** | `[CASE-001]` ยืนยันว่า 1 setup โผล่ได้ 2-3 บรรทัด → N พอง 1.5-2 เท่า → t-stat พอง √2 เท่า → "มีนัยสำคัญ" ปลอมๆ | §1.2.7 บังคับ |
| 5 | **ทองขาขึ้น 6 ปี (1,270 → 4,500)** | ระบบที่เอียงซื้อจะสวยโดยไม่ต้องมีสกิลอะไรเลย | 🔴 **เกณฑ์ E ที่ระดับ 0** — BUY-only และ SELL-only ต้องผ่านทั้งคู่ · หัก drift ที่ตัววัด A |
| 6 | **กฎถูก fit มากับยุคนี้อยู่แล้ว** | โค้ชเขียนหนังสือปี 2025-26 จากกราฟทอง 2020-2025 → เทสบนช่วงเดียวกัน = in-sample โดยปริยาย ไม่ว่าเราจะ holdout ยังไง | holdout 2 ปี + เทสข้ามสินค้า (XAGUSD) + ยอมรับตรงๆ ว่าไม่มี OOS ที่แท้จริง |
| 7 | **optimize เลข 300/1500/3000** | จะได้ค่าที่แค่ "เท่ากับ ATR เฉลี่ยของช่วงข้อมูล" ไม่ใช่ความรู้ใหม่ | ห้าม sweep เลขจุดดิบ · sweep ได้เฉพาะ **ตัวคูณ ATR** |
| 8 | **ทดสอบหลายทางพร้อมกัน** | 3 preset × 6 รูปแบบ × 4 entry mode × 3 wick-tol = 216 ชุด · ที่ p=0.05 จะมี ~11 ชุด "ชนะ" ด้วยความบังเอิญ | ประกาศ primary test ล่วงหน้า 1 ชุด · ที่เหลือเป็น exploratory เท่านั้น · หรือใช้ Bonferroni |
| 9 | **เวลาเปิดแท่ง H4 คนละแบบ** | เลื่อน 1 ชม. → PA ทุกตัวเปลี่ยน → ผลเทสไม่ตรงกับของจริงเลย | §0.3 · เทสความไวโดยเลื่อน ±1 ชม. (ระดับ 2 ข้อ 3) |
| 10 | **`[SPEC-เดา]` 13 ตัวถูกจูนพร้อมกัน** | นั่นไม่ใช่การเทสระบบโค้ช นั่นคือการสร้างระบบใหม่แล้วติดป้ายชื่อโค้ช | **ล็อกทุก `[SPEC-เดา]` ที่ค่า default ตลอดระดับ 0** · เปิดให้ขยับได้ไม่เกิน 5 ตัวที่ระดับ 1 |
| 11 | **ต้นทุนที่กรอบต่ำเกินจริง** | เข้าที่เลขกลม = เข้าที่ที่ stop กองอยู่ = slippage แย่กว่าค่าเฉลี่ย | `SlippageAtFramePts = 2× ปกติ` |
| 12 | **winrate 90% ที่โค้ชอ้าง** `[แก้ว1 p.35]` | ไม่มีวิธีวัดกำกับ ไม่มี sample ไม่มีช่วงเวลา | ห้ามใช้เป็น prior · ห้ามใช้เป็นเกณฑ์ผ่าน · ถ้าเทสแล้วได้ใกล้ 90% ให้ **สงสัยว่าเราทำผิด** ก่อนดีใจ |

---

# §6 สิ่งที่ต้องไปหาต่อ (เรียงตามผลกระทบ)

| ลำดับ | ต้องได้อะไร | ปลดล็อกอะไร |
|---|---|---|
| 1 | **ภาพตาราง indicator อีก 2-3 ครั้ง ต่างวัน ต่างความผันผวน** | ตัดสิน P vs Q (§5.1) และหาสูตร TP ที่แท้จริง — ผลกระทบสูงสุด ต้นทุนต่ำสุด |
| 2 | **หน้า Settings ของ indicator** | เห็นชื่อ input → รู้เลยว่าใช้ ATR period เท่าไหร่ โดยไม่ต้องแก้สมการ |
| 3 | **ถามโค้ชตรงๆ: "เกิดที่กรอบ" คือใกล้กี่จุด** | ช่อง G4 — ตัวกรองที่โค้ชบอกเองว่ามีน้ำหนัก 80% |
| 4 | **ถามโค้ช: Pin/Doji มีเกณฑ์ตัวเลขไหม** | ช่อง G1/G2 — ถ้าโค้ชตอบว่า "ดูเอา" นั่นคือคำตอบเชิงระบบที่สำคัญมาก (แปลว่าระบบเป็น discretionary จริง โค้ดได้แค่โครง) |
| 5 | **ถามโค้ช: แท่งไส้หลังซิกปิดขึ้นหรือลง** | ช่อง G13 — โมจากับลูกแก้วเขียนไม่ตรงกัน และมันเปลี่ยนชุดไม้ทั้งหมด |
| 6 | คลิปหนังสือเสียง `[โม2 p.92]` (ต้องขอรหัสในไลน์กลุ่ม) | ตัวอย่างจุดเข้าจริง 1-5 นาที = หลักฐานชั้น A' เพิ่ม |
| 7 | `[โม2 p.20-72]` 52 หน้าตัวอย่างจุดเข้าจริง 6 ท่า | ยังไม่ได้อ่าน — น่าจะมีคำตอบของ G4 ซ่อนอยู่ในภาพ |

---

## ⚠️ คำเตือนปิดท้าย

เอกสารนี้ทดสอบ **"ระบบแม่ปลาตามที่เราตีความ"** ไม่ใช่ **"ระบบแม่ปลา"**

จุดที่โค้ชใช้สายตาตัดสิน (Pin หน้าตายังไง · ใกล้กรอบแค่ไหน · ตอนนี้ SW หรือยัง) คือจุดที่เราเดาแทนถึง 13 จุด
ถ้าผลออกมาแย่ **ห้ามสรุปว่า "ระบบแม่ปลาไม่เวิร์ค"** — ต้องไล่ §2 ก่อนว่าเราเดาผิดตรงไหน
และถ้าผลออกมาดีเกินคาด ให้สงสัย §5.3 ทุกข้อก่อนดีใจ

> กฎเหล็ก FIN ข้อ 2 — **ประตูตัด:** เขียนกฎเป็น if-then ไม่ได้ = ระบบยังไม่ชัด จบตรงนั้น อย่าเดาต่อ
> เอกสารนี้เดาไปแล้ว 13 จุดโดยเจตนา **แต่ติดป้ายไว้ครบทุกจุด** — นั่นคือเงื่อนไขเดียวที่ทำให้การเดายอมรับได้
