#ifndef AI_GATEWAY_MQH #define AI_GATEWAY_MQH string AI_Trim(const string s) { string x = s; StringTrimLeft(x); StringTrimRight(x); return x; } string AI_Upper(const string s) { string x = s; StringToUpper(x); return x; } string AI_JSONEscape(const string s) { string x = s; StringReplace(x, "\\", "\\\\"); StringReplace(x, "\"", "\\\""); StringReplace(x, "\n", " "); StringReplace(x, "\r", " "); return x; } bool AI_ModeMandatory() { return (AI_Upper(AI_Mode) == "MANDATORY"); } bool AI_ModeAdvisory() { return (AI_Upper(AI_Mode) == "ADVISORY"); } bool AI_ModeOff() { return (AI_Upper(AI_Mode) == "OFF"); } bool AI_ExtractNumber(const string &json, const string &key, double &out) { int k = StringFind(json, "\"" + key + "\""); if(k < 0) return false; int colon = StringFind(json, ":", k); if(colon < 0) return false; int endComma = StringFind(json, ",", colon); int endBrace = StringFind(json, "}", colon); int end = endComma; if(end < 0 || (endBrace >= 0 && endBrace < end)) end = endBrace; if(end < 0) return false; string val = StringSubstr(json, colon + 1, end - (colon + 1)); val = AI_Trim(val); out = StringToDouble(val); return true; } bool AI_ExtractInt(const string &json, const string &key, int &out) { double tmp = 0.0; if(!AI_ExtractNumber(json, key, tmp)) return false; out = (int)MathRound(tmp); return true; } bool AI_ExtractString(const string &json, const string &key, string &out) { int k = StringFind(json, "\"" + key + "\""); if(k < 0) return false; int colon = StringFind(json, ":", k); if(colon < 0) return false; int q1 = StringFind(json, "\"", colon + 1); if(q1 < 0) return false; int q2 = StringFind(json, "\"", q1 + 1); if(q2 < 0) return false; out = StringSubstr(json, q1 + 1, q2 - q1 - 1); StringReplace(out, "\\n", " "); StringReplace(out, "\\\"", "\""); return true; } bool AI_Post(const string route, const string payload, string &response) { if(AI_ModeOff()) return false; string base = AI_Trim(AI_Gateway_URL); if(StringLen(base) < 8) return false; string url = base; if(StringLen(route) > 0) { if(StringSubstr(base, StringLen(base) - 1, 1) == "/") url = base + route; else url = base + "/" + route; } string headers = "Content-Type: application/json\r\n"; string token = AI_Trim(AI_Gateway_Token); if(StringLen(token) > 0) headers += "Authorization: Bearer " + token + "\r\n"; char post_data[]; StringToCharArray(payload, post_data, 0, WHOLE_ARRAY, CP_UTF8); char result[]; string result_headers = ""; ResetLastError(); int code = WebRequest("POST", url, headers, MathMax(1000, AI_TimeoutMs), post_data, result, result_headers); if(code <= 0) { AI_Last_Error = "AI gateway request failed err=" + IntegerToString(GetLastError()) + " route=" + route; return false; } response = CharArrayToString(result, 0, -1, CP_UTF8); return true; } bool InitializeAIGateway() { if(AI_ModeOff()) { AI_Initialized = false; AI_Last_Error = "AI mode is off"; return false; } string ping = "{\"symbol\":\"" + _Symbol + "\",\"tf\":\"" + EnumToString((ENUM_TIMEFRAMES)Period()) + "\"}"; string resp = ""; // Prefer a health endpoint; if not present, initialize optimistically for advisory mode. if(AI_Post("health", ping, resp)) { AI_Initialized = true; AI_Last_Error = ""; return true; } AI_Initialized = AI_ModeAdvisory(); if(AI_Initialized) AI_Last_Error = "Gateway health check unavailable; running advisory fallback."; return AI_Initialized; } bool IsAIEnabled() { return AI_Initialized && !AI_ModeOff(); } bool AIValidateTrade(const string setup_name, const bool is_buy, double &size_multiplier, string &reasoning, string &decision_out) { size_multiplier = 1.0; reasoning = ""; decision_out = "NO"; if(!AI_Validate_Trades || AI_ModeOff()) { decision_out = "YES"; reasoning = "AI validation disabled"; return true; } if(!AI_Initialized) { if(AI_ModeMandatory()) { reasoning = "AI gateway unavailable (mandatory mode)"; decision_out = "NO"; return false; } reasoning = "AI gateway unavailable (advisory fallback)"; decision_out = "YES"; return true; } string payload = "{"; payload += "\"symbol\":\"" + _Symbol + "\","; payload += "\"tf\":\"" + EnumToString((ENUM_TIMEFRAMES)Period()) + "\","; payload += "\"setup\":\"" + AI_JSONEscape(setup_name) + "\","; payload += "\"side\":\"" + (is_buy ? "BUY" : "SELL") + "\","; payload += "\"mode\":\"" + ModeToString(Current_Mode) + "\","; payload += "\"state\":\"" + StateToString(Current_State) + "\","; payload += "\"warnings\":" + IntegerToString(Active_Warnings) + ","; payload += "\"praise\":" + IntegerToString(Active_Praise_Signals) + ","; payload += "\"nn_bias\":" + IntegerToString(NN_Bias) + ","; payload += "\"nn_confidence\":" + DoubleToString(NN_Confidence, 2) + ","; payload += "\"nn_risk\":" + DoubleToString(NN_RiskScore, 2) + ","; payload += "\"cluster_strength\":" + DoubleToString(Coordinator_Cluster_Strength, 2) + ","; payload += "\"cluster_conflict\":" + DoubleToString(Coordinator_Conflict_Score, 2) + ","; payload += "\"news_bias\":" + IntegerToString(News_Bias_Direction) + ","; payload += "\"news_strength\":" + DoubleToString(News_Bias_Strength, 2); payload += "}"; string resp = ""; if(!AI_Post("validate-trade", payload, resp)) { if(AI_ModeMandatory()) { reasoning = "AI gateway request failed"; decision_out = "NO"; return false; } reasoning = "AI gateway request failed (advisory fallback)"; decision_out = "YES"; return true; } string decision = ""; AI_ExtractString(resp, "decision", decision); if(decision == "") decision = "NO"; decision = AI_Upper(decision); double mult = 1.0; if(AI_ExtractNumber(resp, "size_multiplier", mult)) size_multiplier = MathMax(0.1, MathMin(3.0, mult)); if(!AI_ExtractString(resp, "reason", reasoning)) reasoning = "gateway"; decision_out = decision; if(decision == "YES") return true; if(decision == "MAYBE") { if(size_multiplier > 1.0) size_multiplier = 1.0; return true; } return false; } string ValidateTradeWithAI(const string setup_name, const bool is_buy) { double mult = 1.0; string reason = ""; string decision = "NO"; bool ok = AIValidateTrade(setup_name, is_buy, mult, reason, decision); if(!ok && decision == "YES") decision = "NO"; return decision + " | " + reason; } bool AIFetchBriefing(const string tf, const string context, string &text, int &bias, double &confidence) { text = ""; bias = 0; confidence = 0.0; if(!AI_Initialized || AI_ModeOff()) return false; string payload = "{"; payload += "\"symbol\":\"" + _Symbol + "\","; payload += "\"tf\":\"" + AI_JSONEscape(tf) + "\","; payload += "\"context\":\"" + AI_JSONEscape(context) + "\","; payload += "\"mode\":\"" + ModeToString(Current_Mode) + "\","; payload += "\"state\":\"" + StateToString(Current_State) + "\""; payload += "}"; string resp = ""; if(!AI_Post("briefing", payload, resp)) return false; string out_text = ""; int out_bias = 0; double out_conf = 0.0; if(!AI_ExtractString(resp, "summary", out_text)) out_text = "Briefing unavailable"; AI_ExtractInt(resp, "bias", out_bias); AI_ExtractNumber(resp, "confidence", out_conf); if(out_bias > 1) out_bias = 1; if(out_bias < -1) out_bias = -1; text = out_text; bias = out_bias; confidence = MathMax(0.0, MathMin(100.0, out_conf)); return true; } void DisplayAIBriefing(const string briefing) { Last_AI_Analysis = briefing; Print("[AI BRIEFING] ", briefing); } string GetAIDailyBriefing() { string text = ""; int bias = 0; double conf = 0.0; if(!AIFetchBriefing("D1", AI_Brief_4H_Text, text, bias, conf)) return "AI briefing unavailable (gateway failed)."; AI_Brief_D_Text = text; AI_Brief_D_Bias = bias; AI_Brief_D_Confidence = conf; AI_Brief_D_Time = TimeCurrent(); return text; } bool AIFetchNewsBias(int &bias, double &strength, string &reason) { bias = 0; strength = 0.0; reason = ""; if(!AI_Initialized || AI_ModeOff() || !AI_Enable_News_Bias) return false; string payload = "{"; payload += "\"symbol\":\"" + _Symbol + "\","; payload += "\"tf\":\"" + EnumToString((ENUM_TIMEFRAMES)Period()) + "\","; payload += "\"local_news_report\":\"" + AI_JSONEscape(News_Bias_Report) + "\","; payload += "\"last_headline\":\"" + AI_JSONEscape(News_Last_Headline) + "\""; payload += "}"; string resp = ""; if(!AI_Post("news-bias", payload, resp)) return false; int b = 0; double s = 0.0; string r = ""; bool okb = AI_ExtractInt(resp, "bias", b); bool oks = AI_ExtractNumber(resp, "strength", s); AI_ExtractString(resp, "reason", r); if(!okb || !oks) return false; if(b > 1) b = 1; if(b < -1) b = -1; bias = b; strength = MathMax(0.0, MathMin(100.0, s)); reason = r; return true; } void AI_GetSessionWindow(datetime now, datetime &sess_start, datetime &sess_end, string &sess_name) { MqlDateTime dt; TimeToStruct(now, dt); // UTC-style session buckets: // Asia: 00-08, London: 08-13, New York: 13-21, Rollover: 21-24 int h = dt.hour; MqlDateTime sdt = dt; MqlDateTime edt = dt; if(h >= 0 && h < 8) { sess_name = "ASIA"; sdt.hour = 0; sdt.min = 0; sdt.sec = 0; edt.hour = 8; edt.min = 0; edt.sec = 0; } else if(h >= 8 && h < 13) { sess_name = "LONDON"; sdt.hour = 8; sdt.min = 0; sdt.sec = 0; edt.hour = 13; edt.min = 0; edt.sec = 0; } else if(h >= 13 && h < 21) { sess_name = "NEWYORK"; sdt.hour = 13; sdt.min = 0; sdt.sec = 0; edt.hour = 21; edt.min = 0; edt.sec = 0; } else { sess_name = "ROLLOVER"; sdt.hour = 21; sdt.min = 0; sdt.sec = 0; edt.hour = 23; edt.min = 59; edt.sec = 59; } sess_start = StructToTime(sdt); sess_end = StructToTime(edt); } void AI_DrawSessionOverlay(const string phase, const datetime sess_start, const datetime sess_end, const string sess_name, const double confidence, const string next_phase) { if(!AI_Session_Draw_Rectangles) return; int i_start = iBarShift(_Symbol, PERIOD_CURRENT, sess_start, false); int i_end = iBarShift(_Symbol, PERIOD_CURRENT, sess_end, false); if(i_start < 0 || i_end < 0) return; int lo = MathMin(i_start, i_end); int hi = MathMax(i_start, i_end); double p_hi = -DBL_MAX; double p_lo = DBL_MAX; for(int i = lo; i <= hi; ++i) { double h = iHigh(_Symbol, PERIOD_CURRENT, i); double l = iLow(_Symbol, PERIOD_CURRENT, i); if(h > p_hi) p_hi = h; if(l < p_lo) p_lo = l; } if(p_hi <= p_lo) return; string key = IntegerToString((int)sess_start); string rect = "AI_SESS_RECT_" + key; string lbl = "AI_SESS_LBL_" + key; if(ObjectFind(0, rect) < 0) ObjectCreate(0, rect, OBJ_RECTANGLE, 0, sess_start, p_hi, sess_end, p_lo); ObjectSetInteger(0, rect, OBJPROP_TIME, 0, sess_start); ObjectSetDouble(0, rect, OBJPROP_PRICE, 0, p_hi); ObjectSetInteger(0, rect, OBJPROP_TIME, 1, sess_end); ObjectSetDouble(0, rect, OBJPROP_PRICE, 1, p_lo); color c = clrSilver; string u = AI_Upper(phase); if(u == "ACCUMULATION") c = clrDodgerBlue; if(u == "MANIPULATION") c = clrOrangeRed; if(u == "DISTRIBUTION") c = clrGold; ObjectSetInteger(0, rect, OBJPROP_COLOR, c); ObjectSetInteger(0, rect, OBJPROP_BACK, true); ObjectSetInteger(0, rect, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, rect, OBJPROP_WIDTH, 1); if(ObjectFind(0, lbl) < 0) ObjectCreate(0, lbl, OBJ_TEXT, 0, sess_start, p_hi); string txt = sess_name + " " + phase + " (" + DoubleToString(confidence, 1) + "%)"; if(AI_Session_Predict_Next && StringLen(next_phase) > 0) txt += " -> next: " + next_phase; ObjectSetString(0, lbl, OBJPROP_TEXT, txt); ObjectSetInteger(0, lbl, OBJPROP_COLOR, c); ObjectSetInteger(0, lbl, OBJPROP_FONTSIZE, 8); ObjectSetInteger(0, lbl, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); ObjectSetInteger(0, lbl, OBJPROP_TIME, sess_start); ObjectSetDouble(0, lbl, OBJPROP_PRICE, p_hi); } bool AIFetchSessionBriefing(const string sess_name, const datetime sess_start, const datetime sess_end, string &phase, double &confidence, string &next_phase, string &reason) { phase = ""; confidence = 0.0; next_phase = ""; reason = ""; if(!AI_Initialized || !AI_Session_Briefing || AI_ModeOff()) return false; string payload = "{"; payload += "\"symbol\":\"" + _Symbol + "\","; payload += "\"session\":\"" + sess_name + "\","; payload += "\"session_start\":" + IntegerToString((int)sess_start) + ","; payload += "\"session_end\":" + IntegerToString((int)sess_end) + ","; payload += "\"tf\":\"" + EnumToString((ENUM_TIMEFRAMES)Period()) + "\","; payload += "\"frameworks\":\"Wyckoff,ICT\""; payload += "}"; string resp = ""; if(!AI_Post("session-briefing", payload, resp)) return false; string p = ""; string n = ""; string r = ""; double c = 0.0; bool okp = AI_ExtractString(resp, "phase", p); bool okc = AI_ExtractNumber(resp, "confidence", c); AI_ExtractString(resp, "next_phase", n); AI_ExtractString(resp, "reason", r); if(!okp || !okc) return false; phase = p; confidence = MathMax(0.0, MathMin(100.0, c)); next_phase = n; reason = r; return true; } void AIUpdateBriefingsTimer() { if(!AI_Initialized || AI_ModeOff()) return; static datetime last_h1_bar = 0; static datetime last_h4_bar = 0; static datetime last_d1_bar = 0; static datetime last_w1_bar = 0; static datetime last_mn1_bar = 0; static string h4_day_compound = ""; datetime h1 = iTime(_Symbol, PERIOD_H1, 0); datetime h4 = iTime(_Symbol, PERIOD_H4, 0); datetime d1 = iTime(_Symbol, PERIOD_D1, 0); datetime w1 = iTime(_Symbol, PERIOD_W1, 0); datetime mn1 = iTime(_Symbol, PERIOD_MN1, 0); if(AI_Briefing_1H && h1 > 0 && h1 != last_h1_bar) { string txt = ""; int b = 0; double c = 0.0; if(AIFetchBriefing("H1", AI_Brief_4H_Text, txt, b, c)) { AI_Brief_1H_Text = txt; AI_Brief_1H_Bias = b; AI_Brief_1H_Confidence = c; AI_Brief_1H_Time = TimeCurrent(); } last_h1_bar = h1; } if(AI_Briefing_4H && h4 > 0 && h4 != last_h4_bar) { string txt = ""; int b = 0; double c = 0.0; if(AIFetchBriefing("H4", AI_Brief_1H_Text, txt, b, c)) { AI_Brief_4H_Text = txt; AI_Brief_4H_Bias = b; AI_Brief_4H_Confidence = c; AI_Brief_4H_Time = TimeCurrent(); string stamp = TimeToString(h4, TIME_DATE|TIME_MINUTES); if(StringLen(h4_day_compound) > 0) h4_day_compound += " || "; h4_day_compound += "[" + stamp + "] " + txt; } last_h4_bar = h4; } if(AI_Briefing_Daily && d1 > 0 && d1 != last_d1_bar) { string ctx = h4_day_compound; if(StringLen(ctx) == 0) ctx = AI_Brief_4H_Text; string txt = ""; int b = 0; double c = 0.0; if(AIFetchBriefing("D1", ctx, txt, b, c)) { AI_Brief_D_Text = txt; AI_Brief_D_Bias = b; AI_Brief_D_Confidence = c; AI_Brief_D_Time = TimeCurrent(); Last_AI_Analysis = txt; } h4_day_compound = ""; last_d1_bar = d1; } if(AI_Briefing_Weekly && w1 > 0 && w1 != last_w1_bar) { string txt = ""; int b = 0; double c = 0.0; if(AIFetchBriefing("W1", AI_Brief_D_Text, txt, b, c)) { AI_Brief_W_Text = txt; AI_Brief_W_Bias = b; AI_Brief_W_Confidence = c; AI_Brief_W_Time = TimeCurrent(); } last_w1_bar = w1; } if(AI_Briefing_Monthly && mn1 > 0 && mn1 != last_mn1_bar) { string txt = ""; int b = 0; double c = 0.0; if(AIFetchBriefing("MN1", AI_Brief_W_Text, txt, b, c)) { AI_Brief_M_Text = txt; AI_Brief_M_Bias = b; AI_Brief_M_Confidence = c; AI_Brief_M_Time = TimeCurrent(); } last_mn1_bar = mn1; } } void AIUpdateSessionTimer() { if(!AI_Initialized || !AI_Session_Briefing || AI_ModeOff()) return; static datetime last_session_start = 0; datetime now = TimeCurrent(); datetime sess_start = 0; datetime sess_end = 0; string sess_name = ""; AI_GetSessionWindow(now, sess_start, sess_end, sess_name); if(sess_start <= 0 || sess_end <= 0) return; if(sess_start == last_session_start) return; string phase = ""; double conf = 0.0; string next_phase = ""; string reason = ""; if(AIFetchSessionBriefing(sess_name, sess_start, sess_end, phase, conf, next_phase, reason)) { AI_Session_Name = sess_name; AI_Session_Phase = phase; AI_Session_Next = next_phase; AI_Session_Confidence = conf; AI_Session_Reason = reason; AI_Session_Start = sess_start; AI_Session_End = sess_end; AI_DrawSessionOverlay(phase, sess_start, sess_end, sess_name, conf, next_phase); } last_session_start = sess_start; } #endif