//+------------------------------------------------------------------+ //| MaePla_L0.mq5 | //| ระบบแม่ปลาปากกาเขียว — BACKTEST ระดับ 0 (baseline เปลือย) | //| | //| ตอบคำถามเดียว: "ตัวสัญญาณ (PA 5 รูปแบบ + ไส้หลังซิก) มีเปรียบ | //| เชิงสถิติจริงไหม เมื่ออยู่เดี่ยวๆ ไม่มีตัวกรองใดๆ" | //| | //| ⚠️ EA ตัวนี้ "ไม่เทรด" — เป็น census/measurement EA | //| เหตุผล: ระดับ 0 ต้องวัดจาก "เส้นทางราคา" ไม่ใช่ผลไม้ | //| (EA-SPEC §4.1 — RR 1:5 ต้องใช้ ~2,000 ไม้ถึงพิสูจน์ได้) | //| การเทรดจริงจะทำให้เสียสัญญาณที่ซ้อนกัน (1 position ต่อครั้ง) | //| → บันทึกทุกสัญญาณลง CSV แล้ววิเคราะห์ offline แทน | //| | //| กฎเหล็ก §0.2 : ห้ามอ่านแท่งที่ยังไม่ปิด | //| - ทุกเงื่อนไข PA/ไส้หลังซิก อ่าน index >= 1 เท่านั้น (มี assert) | //| - index 0 ใช้ได้แค่ iTime(0) (ตรวจแท่งใหม่) และ iOpen(0) ณ | //| วินาทีที่แท่งเปิด = ราคาปัจจุบันจริง (whitelist #2/#3) | //| - MFE/MAE ประเมินแบบ deferred: รอ 20 แท่งผ่านไปแล้วค่อยย้อนอ่าน | //| M1 ที่เป็นอดีตทั้งหมด → เป็นไปไม่ได้ที่จะ look-ahead | //+------------------------------------------------------------------+ #property copyright "Hon / FIN[psi]" #property version "1.00" #property description "MaePla Level-0 census: PA5 + post-wick, NO filters. Writes CSV (MFE/MAE/TP-vs-SL race). Does NOT trade." //=== หน่วยจุด (EA-SPEC §0.1) — ห้ามใช้ _Point ตรงๆ ==================== #define PTV 0.01 //=== เอาต์พุต ========================================================= input group "Output" input string InpCsvName = "maepla_l0.csv"; // ชื่อไฟล์ CSV (เขียนลง Terminal/Common/Files) input bool InpRecordControl = true; // บันทึก "ทุกแท่ง" เป็น baseline สุ่ม (ต้องมี — เส้นที่ต้องชนะ) input bool InpRecordRejected = true; // บันทึก PA ที่ตกด่านไส้หลังซิกด้วย (เป็นข้อมูล funnel) input int InpEvalBars = 20; // วัด MFE/MAE กี่แท่ง H4 หลังเข้า //=== ทิศทาง (ระดับ 0 ไม่มี interaction ระหว่างฝั่ง — แยกได้ใน analysis) = input group "Direction" input int InpDirFilter = 0; // 0=ทั้งสองฝั่ง 1=BUY เท่านั้น 2=SELL เท่านั้น //=== PA — ค่า [SPEC-เดา] ทั้งหมดล็อกที่ default ตลอดระดับ 0 (EA-SPEC §5 ข้อ 10) input group "PA — PAT1 Pin [SPEC-DAO G1]" input double InpPinWickBodyRatio = 2.0; // ไส้ >= 2.0 เท่าของเนื้อ input double InpPinWickRangePct = 0.60; // ไส้ >= 60% ของ range input double InpPinOppWickPct = 0.15; // ไส้ตรงข้าม <= 15% ของ range input group "PA — PAT2 / PAT2 พิเศษ" input double InpEngulf50Min = 0.50; // [ปลา3 p.5] ล็อก input bool InpPat2FullEngulf = false; // [SPEC-DAO] input group "PA — PAT3 [SPEC-DAO G2/G3/G3b]" input double InpDojiBodyRangePct = 0.10; // Doji: เนื้อ <= 10% ของ range input double InpDojiVsFirstPct = 0.30; // เนื้อกลาง <= 30% ของแท่งแรก input double InpSmallVsFirstPct = 0.50; // แบบ2: เนื้อกลาง <= 50% ของแท่งแรก input bool InpApply50ToPat31_2 = true; // [SPEC-DAO G14] input int InpPat33Mode = 0; // 0=CLOSE_LEVEL 1=BODY_SUM [G3b] input double InpPat33MinPen = 0.50; // [ปลา3 p.6] ล็อก input bool InpPat33NoNewExtrm = true; // [SPEC-DAO] input bool InpPat33Harami = false; // [SPEC-DAO] input group "PA — กันแท่งจิ๋ว [SPEC-DAO]" input int InpMinBarRangePts = 80; // range ขั้นต่ำ (จุด) input int InpMinBodyPts = 30; // body ขั้นต่ำ (จุด) //=== ไส้หลังซิก (§1.3.2) ============================================== input group "PostWick [Mo1 p.16] [Pla3 p.9]" input int InpPostWickCloseRule = 0; // 0=OPPOSITE(โมจา) 1=SAME(ลูกแก้ว) 2=ANY §4.1b input double InpPostWickBodyMax = 0.50; // [โม1 p.16] ล็อก input double InpPostWickWickMult = 1.00; // [โม1 p.16] ล็อก //=== SL / TP (§1.6) =================================================== input group "SL / TP — ท่า A2: SL = ไส้ของ Sig [ปลา3 p.16]" input int InpTP1Points = 1500; // รอบวิ่ง H4 นับจากไส้หลังซิก [ปลา3 p.8] input int InpTP2Points = 3000; input int InpSLBufferPts = 0; // [SPEC-DAO] buffer เพิ่มจากไส้ (0 = ไส้เป๊ะตามโค้ช) //--- §4.1 ไส้หลังซิกถูกทำลายเมื่อไหร่ — ไม่ใช่พารามิเตอร์ตัวเลขตัวเดียว // เป็น comparator + buffer พร้อมกัน (GLOSSARY §4.1) // แม่ปลา : price > postwick (GT, buffer 0) // ลูกแก้ว : price >= postwick (GTE, buffer 0) — สัมผัสก็ตาย // โมจา : price > postwick + 300*0.01 (GT, buffer 300) input int InpWickBreakOp = 0; // 0=GT(แม่ปลา/โมจา) 1=GTE(ลูกแก้ว) input int InpWickBreakBufferPts = 0; // 0=แม่ปลา/ลูกแก้ว · 300=โมจา input int InpMojaBufferPts = 300; // [โม1 p.39] ล็อก //--- ⚠️ สองตัวล่างนี้ไม่ใช่ SL ของระบบ — เป็น "หัววัด" ของเกณฑ์ B ใน EA-SPEC §4.1 // ("P(MFE ถึง 1500 ก่อน MAE ถึง 300)") ซึ่งเป็นเรขาคณิตคงที่ไว้เทียบ baseline เท่านั้น // SL จริงของท่า A2 = ไส้ของ Sig (คอลัมน์ sl_price / sys_*) input int InpRaceSLPts = 300; // หัววัดเกณฑ์ B — ไม่ใช่ SL ระบบ input int InpRaceSL2Pts = 500; // หัววัดทางเลือก (ตลาดเมกา) //=== State ============================================================ datetime gLastBarTime = 0; int gAtr = INVALID_HANDLE; int gFH = INVALID_HANDLE; // สถิติ funnel int gCntBars=0, gCntCandidate[2]={0,0}, gCntValid[2]={0,0}; int gCntStatus[5]={0,0,0,0,0}; // 0=VALID 1=W1 2=W2 3=W3 4=W5 int gCntPatWin[6]={0,0,0,0,0,0}; // ต่อ pattern ที่ชนะ priority (เฉพาะ VALID) int gCntRaceM1=0, gCntRaceH4=0, gCntDropped=0; //=== Pending (deferred MFE/MAE) ====================================== #define MAXPEND 512 struct PendItem { int kind; // 0=CTRL 1=SIG int dir; // +1 BUY -1 SELL datetime entryTime; double entry; double postwick; double slAnchor; double sysSL; double sysTP1; double sysTP2; int patWin; // 0..5 , -1 = CTRL int patMask; int paN; int status; // 0=VALID 1..4 = reject reason double atr; int barsElapsed; }; PendItem gPend[MAXPEND]; int gPendN = 0; //===================================================================== // Helper แท่งเทียน — บังคับ index >= 1 (กฎเหล็ก §0.2) //===================================================================== bool gViolation=false; void GuardIdx(const int i) { if(i < 1 && !gViolation) { gViolation=true; Print("!!! LOOK-AHEAD VIOLATION: read bar index ",i); } } double Op(const int i){ GuardIdx(i); return iOpen (_Symbol,PERIOD_CURRENT,i); } double Hi(const int i){ GuardIdx(i); return iHigh (_Symbol,PERIOD_CURRENT,i); } double Lo(const int i){ GuardIdx(i); return iLow (_Symbol,PERIOD_CURRENT,i); } double Cl(const int i){ GuardIdx(i); return iClose(_Symbol,PERIOD_CURRENT,i); } double Bd (const int i){ return MathAbs(Cl(i)-Op(i)); } double Rg (const int i){ return Hi(i)-Lo(i); } double BTop(const int i){ return MathMax(Op(i),Cl(i)); } double BBot(const int i){ return MathMin(Op(i),Cl(i)); } double UW (const int i){ return Hi(i)-BTop(i); } double DW (const int i){ return BBot(i)-Lo(i); } bool Bull(const int i){ return Cl(i)>Op(i); } bool Bear(const int i){ return Cl(i)0 ? Bull(i) : Bear(i); } bool AgainstDir(const int i,const int dir){ return dir>0 ? Bear(i) : Bull(i); } double SideWick (const int i,const int dir){ return dir>0 ? DW(i) : UW(i); } double OppWick (const int i,const int dir){ return dir>0 ? UW(i) : DW(i); } double SideExtr (const int i,const int dir){ return dir>0 ? Lo(i) : Hi(i); } double P(const int pts){ return pts*PTV; } // กฎ 50% (§1.2.0) — วัดกับ "บอดี้" ของแท่งก่อนหน้า double Penetration(const int prevIdx,const double closePrice,const int dir) { double b = Bd(prevIdx); if(b < P(InpMinBodyPts)) return 0.0; return dir*(closePrice - Cl(prevIdx)) / b; } bool IsTradeableBar(const int i){ return Rg(i) >= P(InpMinBarRangePts); } bool IsDoji(const int i) { return (Bd(i) <= InpDojiBodyRangePct*Rg(i)) && (Rg(i) > P(InpMinBarRangePts)); } //===================================================================== // PA 5 รูปแบบ (§1.2) — i = แท่งใหม่สุดของชุด PA //===================================================================== bool IsPAT1(const int i,const int dir) // Pin [ปลา3 p.3] { if(!IsTradeableBar(i)) return false; if(SideWick(i,dir) < InpPinWickBodyRatio*Bd(i)) return false; if(SideWick(i,dir) < InpPinWickRangePct *Rg(i)) return false; if(OppWick (i,dir) > InpPinOppWickPct *Rg(i)) return false; return true; } bool IsPAT2(const int i,const int dir) // กลืนกิน [ปลา3 p.5] { if(!AgainstDir(i+1,dir)) return false; if(!WithDir (i ,dir)) return false; if(Bd(i+1) < P(InpMinBodyPts)) return false; // ห้ามกลืน doji if(Penetration(i+1,Cl(i),dir) < 1.0) return false; if(InpPat2FullEngulf && dir*(Op(i)-Cl(i+1)) > 0) return false; return true; } bool IsPAT2S(const int i,const int dir) // PAT2 พิเศษ [ปลา3 p.5] { if(!AgainstDir(i+1,dir)) return false; if(!WithDir (i ,dir)) return false; double pen = Penetration(i+1,Cl(i),dir); return (pen > InpEngulf50Min && pen < 1.0); } bool IsPAT3_1(const int i,const int dir) // แท่งกลาง = Doji [ปลา3 p.3] { if(!AgainstDir(i+2,dir)) return false; if(!WithDir (i ,dir)) return false; if(!IsDoji(i+1)) return false; if(Bd(i+1) > InpDojiVsFirstPct*Bd(i+2)) return false; if(InpApply50ToPat31_2 && Penetration(i+2,Cl(i),dir) <= 0.5) return false; return true; } bool IsPAT3_2(const int i,const int dir) // แท่งกลางเล็ก ไม่ใช่ Doji { if(!AgainstDir(i+2,dir)) return false; if(!WithDir (i ,dir)) return false; if(IsDoji(i+1)) return false; // ตัวแยกแบบ1/แบบ2 if(Bd(i+1) > InpSmallVsFirstPct*Bd(i+2)) return false; if(InpApply50ToPat31_2 && Penetration(i+2,Cl(i),dir) <= 0.5) return false; return true; } bool IsPAT3_3(const int i,const int dir) // Three Inside [ปลา3 p.6] { if(!AgainstDir(i+2,dir)) return false; if(Bd(i+2) < P(InpMinBodyPts)) return false; // "แท่งแรกใหญ่" bool ok=false; if(InpPat33Mode==0) ok = (Penetration(i+2,Cl(i),dir) > InpPat33MinPen); else { double w = (WithDir(i+1,dir)?Bd(i+1):0.0) + (WithDir(i,dir)?Bd(i):0.0); double b = Bd(i+2); ok = (b>0 && w/b > InpPat33MinPen); } if(!ok) return false; if(InpPat33NoNewExtrm) { if(dir>0 && (Lo(i+1)Hi(i+2) || Hi(i)>Hi(i+2))) return false; } if(InpPat33Harami) if(!(BTop(i+1)<=BTop(i+2) && BBot(i+1)>=BBot(i+2))) return false; return true; } // ลำดับการตรวจ §1.2.5 → bitmask // bit0 PAT1 | bit1 PAT2 | bit2 PAT2S | bit3 PAT3_1 | bit4 PAT3_2 | bit5 PAT3_3 int DetectAllPA(const int i,const int dir) { int m = 0; if(IsPAT1(i,dir)) m |= 1; if(IsPAT2(i,dir)) m |= 2; else if(IsPAT2S(i,dir)) m |= 4; else if(IsPAT3_3(i,dir)) m |= 32; // "ไม่ถึง 50% → ไม่นับ PAT2 → ใช้เป็น PAT3 แบบ3" if(IsPAT3_1(i,dir)) m |= 8; if(IsPAT3_2(i,dir)) m |= 16; return m; } // Dedupe §1.2.7 — ทุก PA ที่ anchor เดียวกัน + dir เดียวกัน = ไส้หลังซิกแท่งเดียวกัน // → เหลือตัวเดียว ตามลำดับ PAT3_3 > PAT2 > PAT2S > PAT3_1 > PAT3_2 > PAT1 int PatWinner(const int mask) { if(mask & 32) return 5; // PAT3_3 if(mask & 2 ) return 1; // PAT2 if(mask & 4 ) return 2; // PAT2S if(mask & 8 ) return 3; // PAT3_1 if(mask & 16) return 4; // PAT3_2 if(mask & 1 ) return 0; // PAT1 return -1; } int PatBars(const int w) { if(w==0) return 1; if(w==1 || w==2) return 2; return 3; } string PatName(const int w) { switch(w) { case 0: return "PAT1"; case 1: return "PAT2"; case 2: return "PAT2S"; case 3: return "PAT3_1"; case 4: return "PAT3_2"; case 5: return "PAT3_3"; } return "CTRL"; } //=== ค่าของ "ชุด PA" (§1.1) ========================================== double PA_SideExtreme(const int i,const int n,const int dir) { double v = SideExtr(i,dir); for(int k=i+1;k0){ if(xv) v=x; } } return v; } double PA_BodyTop(const int i,const int n) { double v=BTop(i); for(int k=i+1;kv) v=BTop(k); return v; } double PA_BodyBot(const int i,const int n) { double v=BBot(i); for(int k=i+1;k InpPostWickBodyMax*Bd(i)) return 2; // W3 ไส้เทียนไม่เกินไส้เทียนแท่งก่อนหน้า if(SideWick(p,dir) > SideWick(i,dir)*InpPostWickWickMult) return 3; // W4 ไส้หลังซิกเกินครึ่งเนื้อชุด Sig → ใช้ไส้ยาวสุดของชุด Sig วาง SL แทน double bt = PA_BodyTop(i,n), bb = PA_BodyBot(i,n); double span = bt-bb; double retrace = (dir>0) ? (bt - Lo(p)) : (Hi(p) - bb); if(span>0 && retrace > 0.5*span) slAnchor = PA_SideExtreme(i,n,dir); // W5 ห้ามทำลายชุด PA double paX = PA_SideExtreme(i,n,dir); if(dir>0 && Lo(p) < paX) return 4; if(dir<0 && Hi(p) > paX) return 4; return 0; } //===================================================================== // OnInit / OnDeinit //===================================================================== int OnInit() { // §0.1 assert หน่วยจุด double pt=_Point; if(pt<=0 || (MathAbs(PTV/pt-1.0)>0.01 && MathAbs(PTV/pt-10.0)>0.01)) { PrintFormat("INIT_FAILED: _Point=%.5f ไม่เข้ากับ PT=0.01 (ทศนิยม 2 หรือ 3 เท่านั้น)",pt); return INIT_FAILED; } gAtr = iATR(_Symbol,PERIOD_CURRENT,14); if(gAtr==INVALID_HANDLE) return INIT_FAILED; gFH = FileOpen(InpCsvName, FILE_WRITE|FILE_TXT|FILE_ANSI|FILE_COMMON); if(gFH==INVALID_HANDLE) { PrintFormat("INIT_FAILED: เปิดไฟล์ %s ไม่ได้ err=%d",InpCsvName,GetLastError()); return INIT_FAILED; } FileWriteString(gFH, "kind,datetime,year,direction,pattern_type,pattern_mask,pa_bars,status," "entry_price,postwick_price,sl_price,tp1_price,atr14," "mfe_points_20bars,mae_points_20bars,bars_to_max,bars_to_mae," "min_to_tp1,min_to_sl300,min_to_sl500,min_to_tp2," "min_to_systp1,min_to_sl_pla,min_to_sl_kaew,min_to_sl_moja,min_to_syssl," "hit_tp1,hit_sl,tp1_before_sl,sys_tp1_before_sl," "sys_tp1_before_sl_pla,sys_tp1_before_sl_kaew,sys_tp1_before_sl_moja," "fwd6_points,fwd20_points,race_src\r\n"); PrintFormat("MaePla_L0 census started. TP1=%d SL(race)=%d evalBars=%d dirFilter=%d csv=%s", InpTP1Points,InpRaceSLPts,InpEvalBars,InpDirFilter,InpCsvName); return INIT_SUCCEEDED; } void OnDeinit(const int reason) { if(gFH!=INVALID_HANDLE){ FileFlush(gFH); FileClose(gFH); gFH=INVALID_HANDLE; } if(gAtr!=INVALID_HANDLE) IndicatorRelease(gAtr); gCntDropped += gPendN; PrintFormat("=== L0 CENSUS SUMMARY ==="); PrintFormat("bars processed : %d", gCntBars); PrintFormat("PA candidates BUY : %d | SELL : %d", gCntCandidate[0], gCntCandidate[1]); PrintFormat("VALID sigs BUY : %d | SELL : %d", gCntValid[0], gCntValid[1]); PrintFormat("status VALID/W1/W2/W3/W5 : %d / %d / %d / %d / %d", gCntStatus[0],gCntStatus[1],gCntStatus[2],gCntStatus[3],gCntStatus[4]); PrintFormat("valid by pattern PAT1/PAT2/PAT2S/PAT3_1/PAT3_2/PAT3_3 : %d / %d / %d / %d / %d / %d", gCntPatWin[0],gCntPatWin[1],gCntPatWin[2],gCntPatWin[3],gCntPatWin[4],gCntPatWin[5]); PrintFormat("race resolved by M1 : %d | fallback H4 : %d", gCntRaceM1, gCntRaceH4); PrintFormat("dropped (window incomplete at end) : %d", gCntDropped); if(gViolation) Print("!!! LOOK-AHEAD VIOLATION DETECTED — ผลรอบนี้ใช้ไม่ได้"); } //===================================================================== // OnTick //===================================================================== void OnTick() { datetime t = iTime(_Symbol,PERIOD_CURRENT,0); // whitelist #1 if(t==0 || t==gLastBarTime) return; gLastBarTime = t; OnBarClose(); } void OnBarClose() { if(Bars(_Symbol,PERIOD_CURRENT) < InpEvalBars+40) return; gCntBars++; // --- A) เดินเวลาให้ pending ทุกตัว for(int k=0;k0) continue; // baseline สุ่ม: เข้าที่ทุกแท่ง ด้วย SL/TP เดียวกัน if(InpRecordControl) AddPending(0,dir,entryTime,entry,0,0,0,0,0,-1,0,0,0,atr); // ---- สัญญาณจริง: ชุด PA จบที่แท่ง 2, ไส้หลังซิก = แท่ง 1 ---- int mask = DetectAllPA(2,dir); if(mask==0) continue; gCntCandidate[s]++; int win = PatWinner(mask); int n = PatBars(win); double postWick=0, slAnchor=0; int st = ValidatePostWick(2,n,dir,postWick,slAnchor); gCntStatus[(st==0?0:st)]++; if(st!=0 && !InpRecordRejected) continue; if(st==0){ gCntValid[s]++; gCntPatWin[win]++; } double sysSL = slAnchor - dir*P(InpSLBufferPts); double sysTP1 = postWick + dir*P(InpTP1Points); // [ปลา3 p.8] วัดจากไส้หลังซิกเสมอ double sysTP2 = postWick + dir*P(InpTP2Points); AddPending(1,dir,entryTime,entry,postWick,slAnchor,sysSL,sysTP1,sysTP2, win,mask,n,st,atr); } } void AddPending(const int kind,const int dir,const datetime et,const double entry, const double pw,const double sla,const double ssl,const double stp1,const double stp2, const int win,const int mask,const int n,const int st,const double atr) { if(gPendN>=MAXPEND){ Print("WARN: pending overflow"); return; } gPend[gPendN].kind=kind; gPend[gPendN].dir=dir; gPend[gPendN].entryTime=et; gPend[gPendN].entry=entry; gPend[gPendN].postwick=pw; gPend[gPendN].slAnchor=sla; gPend[gPendN].sysSL=ssl; gPend[gPendN].sysTP1=stp1; gPend[gPendN].sysTP2=stp2; gPend[gPendN].patWin=win; gPend[gPendN].patMask=mask; gPend[gPendN].paN=n; gPend[gPendN].status=st; gPend[gPendN].atr=atr; gPend[gPendN].barsElapsed=0; gPendN++; } //===================================================================== // ประเมิน MFE/MAE + การแข่ง TP/SL จาก M1 ที่เป็นอดีตทั้งหมด //===================================================================== void FlushMature() { // นับว่ามีตัวครบกำหนดไหม int nMature=0; for(int k=0;k=InpEvalBars) nMature++; if(nMature==0) return; const int E = InpEvalBars; // แท่ง entry อยู่ที่ index E ตอนนี้ // เวลาเปิดของ 20 แท่งในหน้าต่าง (เก่า→ใหม่) = index E .. 1 datetime h4t[]; ArrayResize(h4t,E); for(int b=0;b E*30); // ต้องได้ M1 มากพอถึงจะเชื่อ // fwd6 / fwd20 (นับแท่ง entry เป็นแท่งที่ 1) double c6 = Cl(E-5); double c20 = Cl(1); for(int k=0;k=h4t[slot+1]) slot++; double fav = (dir>0) ? (m1[j].high-en) : (en-m1[j].low); double adv = (dir>0) ? (en-m1[j].low) : (m1[j].high-en); if(fav>mfe){ mfe=fav; barMfe=slot+1; } if(adv>mae){ mae=adv; barMae=slot+1; } int mins = (int)((t - it.entryTime)/60); // SL_FIRST: ถ้าชนทั้งคู่ในนาทีเดียวกัน SL ชนะ (ค่า minute เท่ากัน → ไม่นับว่า TP มาก่อน) if(mTp1 <0 && fav>=dTp1) mTp1 =mins; if(mTp2 <0 && fav>=dTp2) mTp2 =mins; if(mSl300 <0 && adv>=dSl3) mSl300 =mins; if(mSl500 <0 && adv>=dSl5) mSl500 =mins; if(mSysTp1<0 && fav>=dSysTp1) mSysTp1=mins; if(mSlPla <0 && adv> dSlPla) mSlPla =mins; // แม่ปลา GT if(mSlKaew<0 && adv>=dSlKaew) mSlKaew=mins; // ลูกแก้ว GTE (สัมผัสก็ตาย) if(mSlMoja<0 && adv> dSlMoja) mSlMoja=mins; // โมจา GT + 300 if(mSysSL <0 && (InpWickBreakOp==1 ? (adv>=dSlCfg) : (adv>dSlCfg))) mSysSL=mins; } } else { gCntRaceH4++; for(int b=0;b0) ? (hh-en) : (en-ll); double adv = (dir>0) ? (en-ll) : (hh-en); if(fav>mfe){ mfe=fav; barMfe=b+1; } if(adv>mae){ mae=adv; barMae=b+1; } int mins = b*240; if(mTp1 <0 && fav>=dTp1) mTp1 =mins; if(mTp2 <0 && fav>=dTp2) mTp2 =mins; if(mSl300 <0 && adv>=dSl3) mSl300 =mins; if(mSl500 <0 && adv>=dSl5) mSl500 =mins; if(mSysTp1<0 && fav>=dSysTp1) mSysTp1=mins; if(mSlPla <0 && adv> dSlPla) mSlPla =mins; if(mSlKaew<0 && adv>=dSlKaew) mSlKaew=mins; if(mSlMoja<0 && adv> dSlMoja) mSlMoja=mins; if(mSysSL <0 && (InpWickBreakOp==1 ? (adv>=dSlCfg) : (adv>dSlCfg))) mSysSL=mins; } } if(mfe<0) mfe=0; if(mae<0) mae=0; bool hitTp1 = (mTp1>=0); bool hitSl = (mSl300>=0); // TP1 ก่อน SL: ต้องมาถึงก่อนจริงๆ (เท่ากัน = SL ชนะ, IntrabarOrder=SL_FIRST §2 G20) bool tp1First = hitTp1 && (!hitSl || mTp1 < mSl300); bool sysPla = (mSysTp1>=0) && (mSlPla <0 || mSysTp1 < mSlPla ); bool sysKaew = (mSysTp1>=0) && (mSlKaew<0 || mSysTp1 < mSlKaew); bool sysMoja = (mSysTp1>=0) && (mSlMoja<0 || mSysTp1 < mSlMoja); bool sysTp1First = (mSysTp1>=0) && (mSysSL<0 || mSysTp1 < mSysSL); double fwd6 = dir*(c6 -en)/PTV; double fwd20 = dir*(c20-en)/PTV; MqlDateTime dt; TimeToStruct(it.entryTime,dt); string row = StringFormat( "%s,%s,%d,%s,%s,%d,%d,%d," "%.2f,%.2f,%.2f,%.2f,%.2f," "%.0f,%.0f,%d,%d," "%d,%d,%d,%d," "%d,%d,%d,%d,%d," "%d,%d,%d,%d," "%d,%d,%d," "%.0f,%.0f,%s", (it.kind==0?"CTRL":"SIG"), TimeToString(it.entryTime,TIME_DATE|TIME_MINUTES), dt.year, (dir>0?"BUY":"SELL"), (it.kind==0?"CTRL":PatName(it.patWin)), it.patMask, it.paN, it.status, it.entry, it.postwick, it.sysSL, it.sysTP1, it.atr, mfe/PTV, mae/PTV, barMfe, barMae, mTp1, mSl300, mSl500, mTp2, mSysTp1, mSlPla, mSlKaew, mSlMoja, mSysSL, (hitTp1?1:0), (hitSl?1:0), (tp1First?1:0), (sysTp1First?1:0), (sysPla?1:0), (sysKaew?1:0), (sysMoja?1:0), fwd6, fwd20, (useM1?"M1":"H4")); FileWriteString(gFH,row+"\r\n"); if((gCntRaceM1+gCntRaceH4)%500==0) FileFlush(gFH); } //+------------------------------------------------------------------+