Vizion-Trading-EA/Shared Projects/VizionAI-Trading/VizionAI-Trading-Repo/VizionAI-EA/AIGateway.mqh
2026-02-19 23:19:25 -06:00

669 lines
18 KiB
MQL5

#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