//+------------------------------------------------------------------+ //| AbangGuard EA v1.10 — XAUUSD starter EA + federasi (pilihan) | //| Untuk: pembelajaran MT5. DEMO ONLY — jangan live tanpa berbulan | //| pra-jalan demo. | //| | //| HUKUM DESIGN (non-negotiable, hardcoded): | //| 1. Setiap trade ADA Stop Loss. EA tolak entry tanpa SL. | //| 2. Lot saiz ikut equity — risiko max 1% per trade. | //| 3. Max 1 posisi terbuka pada satu masa. | //| 4. Daily loss cap 3% — berhenti trading bila kena, esok baru | //| sambung. | //| 5. Tiada martingale, tiada grid, tiada recovery-lot. | //| | //| Strategi: MA cross (fast 20 / slow 50) + RSI filter (RSI>50 buy, | //| RSI<50 sell). Sederhana & boleh dibaca — ini alat belajar, bukan | //| mesin untung. | //| | //| v1.10 — LAPISAN FEDERASI (arifOS WEALTH :18082, lalai TUTUP): | //| InpUseFederationGate=true memanggil WEALTH organ sebelum entry | //| (5 hukum disemak semula di pelayan + tetingkap hujung minggu + | //| blackout berita + revenge-lock 2 kerugian berturut). | //| Gagal capaian → EA terus dengan 5 hukum LOKAL (fail-open) | //| melainkan InpFedStrict=true (maka tiada entry tanpa kelulusan).| //| SEBELUM guna: MT5 → Tools → Options → Expert Advisors → | //| "Allow WebRequest for listed URL" → tambah: | //| http://127.0.0.1:18082 (atau IP mesh 100.64.0.2:18082) | //+------------------------------------------------------------------+ #property copyright "Web Of Projects Sdn Bhd — tools lane" #property version "1.10" #property strict #include CTrade trade; input group "=== Strategi ===" input int InpFastMA = 20; // Fast EMA period input int InpSlowMA = 50; // Slow EMA period input int InpRSIPeriod = 14; // RSI period input double InpRSIBuyMin = 50.0; // RSI min untuk BUY input double InpRSISellMax = 50.0; // RSI max untuk SELL input group "=== Risiko (locked) ===" input double InpRiskPercent = 1.0; // Risiko % equity per trade (max 1.0 disahkan) input double InpDailyLossCap = 3.0; // Daily loss cap % (max 3.0 disahkan) input int InpSL_ATR_Mult = 20; // SL = ATR(14) x multiplier /10 input group "=== Am ===" input long InpMagic = 20261110; // Magic number input string InpComment = "AbangGuard"; input group "=== Federasi (pilihan, lalai TUTUP) ===" input bool InpUseFederationGate = false; // Semak verdict WEALTH sebelum entry input string InpFedURL = "http://127.0.0.1:18082/abangguard/verdict"; // Endpoint verdict federasi input int InpFedTimeoutMs = 2000; // Timeout WebRequest (ms) input bool InpFedStrict = false; // true = tiada entry jika federasi tak capai input int InpFedMaxConsecLosses= 2; // Revenge-lock: kerugian berturut hari ini int hFastMA=0, hSlowMA=0, hRSI=0, hATR=0; double dayStartEquity = 0.0; datetime dayStamp = 0; bool dayLocked = false; int OnInit() { if(InpRiskPercent > 1.0) { Print("RISIKO>1% ditolak — reset ke 1.0"); } if(InpDailyLossCap > 3.0) { Print("DAILY CAP>3% ditolak — reset ke 3.0"); } hFastMA = iMA(_Symbol, PERIOD_CURRENT, InpFastMA, 0, MODE_EMA, PRICE_CLOSE); hSlowMA = iMA(_Symbol, PERIOD_CURRENT, InpSlowMA, 0, MODE_EMA, PRICE_CLOSE); hRSI = iRSI(_Symbol, PERIOD_CURRENT, InpRSIPeriod, PRICE_CLOSE); hATR = iATR(_Symbol, PERIOD_CURRENT, 14); if(hFastMA==INVALID_HANDLE || hSlowMA==INVALID_HANDLE || hRSI==INVALID_HANDLE || hATR==INVALID_HANDLE) return INIT_FAILED; trade.SetExpertMagicNumber(InpMagic); if(InpUseFederationGate) Print("FEDERASI ON: ", InpFedURL, " — pastikan URL dibenarkan di Tools>Options>Expert Advisors>WebRequest."); return INIT_SUCCEEDED; } void OnDeinit(const int reason) {} double RiskLot(double entry, double sl) { double riskPct = MathMin(InpRiskPercent, 1.0); double riskMoney = AccountInfoDouble(ACCOUNT_EQUITY) * riskPct / 100.0; double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double distPts = MathAbs(entry - sl) / tickSize; double lossPerLot = distPts * tickValue; if(lossPerLot <= 0) return 0.0; double lot = riskMoney / lossPerLot; double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); lot = MathFloor(lot/step)*step; lot = MathMax(minLot, MathMin(maxLot, lot)); return lot; } bool DailyLockCheck() { MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); datetime today = StringToTime(StringFormat("%04d.%02d.%02d 00:00", dt.year, dt.mon, dt.day)); if(today != dayStamp) { dayStamp = today; dayStartEquity = AccountInfoDouble(ACCOUNT_EQUITY); dayLocked = false; } double capPct = MathMin(InpDailyLossCap, 3.0); if(!dayLocked && dayStartEquity > 0) { double nowEq = AccountInfoDouble(ACCOUNT_EQUITY); if((dayStartEquity - nowEq) / dayStartEquity * 100.0 >= capPct) { dayLocked = true; Print("DAILY LOSS CAP kena (", DoubleToString(capPct,1), "%) — trading berhenti sampai esok."); } } return dayLocked; } bool HaveOpenPosition() { for(int i=PositionsTotal()-1; i>=0; i--) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket) && PositionGetInteger(POSITION_MAGIC)==InpMagic && PositionGetString(POSITION_SYMBOL)==_Symbol) return true; } return false; } //────────────────────────────────────────────────────────────────── // LAPISAN FEDERASI (v1.10) — advisory overlay, 5 hukum lokal kekal //────────────────────────────────────────────────────────────────── // Kira kerugian berturut-turut hari ini (deal OUT milik EA ini). int ConsecLossesToday() { datetime dayStart = iTime(_Symbol, PERIOD_D1, 0); if(!HistorySelect(dayStart, TimeCurrent())) return 0; int total = HistoryDealsTotal(); int consec = 0; for(int i=total-1; i>=0; i--) // dari terkini ke lampau { ulong ticket = HistoryDealGetTicket(i); if(ticket == 0) continue; if(HistoryDealGetInteger(ticket, DEAL_MAGIC) != InpMagic) continue; if(HistoryDealGetString(ticket, DEAL_SYMBOL) != _Symbol) continue; if((ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket, DEAL_ENTRY) != DEAL_ENTRY_OUT) continue; double profit = HistoryDealGetDouble(ticket, DEAL_PROFIT) + HistoryDealGetDouble(ticket, DEAL_SWAP) + HistoryDealGetDouble(ticket, DEAL_COMMISSION); if(profit < 0) consec++; else break; // untung/breakeven memutuskan rentetan } return consec; } // Tanya organ WEALTH. Return true = dibenarkan masuk. bool FederationAllowsEntry() { if(!InpUseFederationGate) return true; // lalai: 5 hukum lokal sahaja int consec = ConsecLossesToday(); string url = StringFormat("%s?symbol=%s&lot=%.2f&risk_pct=%.2f&daily_cap_pct=%.2f" "&open_positions=%d&consec_losses=%d&max_consec_losses=%d" "&operator=abang_sado", InpFedURL, _Symbol, MathMin(InpRiskPercent,1.0), MathMin(InpDailyLossCap,3.0), HaveOpenPosition() ? 1 : 0, consec, InpFedMaxConsecLosses); char post[]; string headers; char result[]; string resHeaders; ResetLastError(); int status = WebRequest("GET", url, "", InpFedTimeoutMs, post, result, resHeaders); if(status == -1) { int err = GetLastError(); if(InpFedStrict) { Print("FED STRICT: WebRequest gagal (err ", err, ") — entry DITOLAK. ", "Tambah URL di Tools>Options>Expert Advisors>WebRequest."); return false; } Print("FED fail-open: WebRequest gagal (err ", err, ") — terus dengan 5 hukum lokal."); return true; } string body = CharArrayToString(result); if(status != 200) { if(InpFedStrict) { Print("FED STRICT: HTTP ", status, " — entry ditolak."); return false; } Print("FED fail-open: HTTP ", status, " — terus dengan 5 hukum lokal."); return true; } bool permit = (StringFind(body, "\"permit\":true") >= 0); if(!permit) { int vpos = StringFind(body, "\"reason\":\""); string reason = (vpos >= 0) ? StringSubstr(body, vpos+11, 60) : "RESTRAINT_HOLD"; Print("FED RESTRAINT_HOLD: ", reason); } return permit; } //────────────────────────────────────────────────────────────────── void OnTick() { if(DailyLockCheck()) return; // hukum #4 if(HaveOpenPosition()) return; // hukum #3 // baru entry pada candle baharu sahaja static datetime lastBar = 0; datetime curBar = iTime(_Symbol, PERIOD_CURRENT, 0); if(curBar == lastBar) return; lastBar = curBar; if(!FederationAllowsEntry()) return; // federasi (pilihan) — selepas undang lokal double fast[2], slow[2], rsi[2], atr[1]; if(CopyBuffer(hFastMA,0,1,2,fast)<2) return; if(CopyBuffer(hSlowMA,0,1,2,slow)<2) return; if(CopyBuffer(hRSI,0,1,2,rsi)<2) return; if(CopyBuffer(hATR,0,1,1,atr)<1) return; double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double slDist = atr[0] * InpSL_ATR_Mult / 10.0; if(slDist <= 0) return; bool crossUp = (fast[1] <= slow[1]) && (fast[0] > slow[0]); bool crossDown = (fast[1] >= slow[1]) && (fast[0] < slow[0]); if(crossUp && rsi[0] >= InpRSIBuyMin) { double sl = NormalizeDouble(ask - slDist, _Digits); double lot = RiskLot(ask, sl); if(lot > 0 && sl > 0) if(!trade.Buy(lot, _Symbol, 0.0, sl, 0.0, InpComment)) Print("BUY gagal: ", trade.ResultRetcodeDescription()); } else if(crossDown && rsi[0] <= InpRSISellMax) { double sl = NormalizeDouble(bid + slDist, _Digits); double lot = RiskLot(bid, sl); if(lot > 0 && sl > 0) if(!trade.Sell(lot, _Symbol, 0.0, sl, 0.0, InpComment)) Print("SELL gagal: ", trade.ResultRetcodeDescription()); } } //+------------------------------------------------------------------+