//+------------------------------------------------------------------+ //| Market_Scanner_Pro.mq5 | //| QuantScan 5.1 - Multi-TF Global Sentiment | //| Copyright 2026, xxxxxxxx | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, xxxxxxxx" #property version "5.10" // Global Sentiment on H1, M15, M5 #property description "Exports 'QuantScan 5.0' for LLM Analysis." #property description "3-Layer Logic & Multi-TF Risk Sentiment." #property script_show_inputs //--- Include Custom Calculators #include #include #include #include #include #include #include #include #include #include #include #include //--- Input Parameters --- input group "Scanner Config" input bool InpUseMarketWatch = false; input string InpSymbolList = "EURUSD,USDJPY,GBPUSD,USDCHF,AUDUSD,XAUUSD,US500,DE40,XTIUSD,ETHUSD"; input string InpBenchmark = "US500"; input string InpForexBench = "DX"; input string InpBrokerTimeZone = "EET (UTC+2)"; input int InpScanHistory = 500; input group "Benchmark Settings" input int InpBetaLookback = 60; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input group "Timeframes (3-Layer Model)" input ENUM_TIMEFRAMES InpTFSlow = PERIOD_H1; // Layer 1: Context/Sentiment Base input ENUM_TIMEFRAMES InpTFMiddle = PERIOD_M15; // Layer 2: Flow/Session Sentiment input ENUM_TIMEFRAMES InpTFFast = PERIOD_M5; // Layer 3: Trigger/Shock Sentiment input group "Metric Settings" input int InpDSMAPeriod = 40; input double InpLaguerreGamma = 0.50; input int InpMurreyPeriod = 64; input int InpATRPeriod = 14; input int InpRSBars = 24; input int InpRVOLPeriod = 20; input int InpERPeriod = 10; input int InpZScorePeriod = 20; input group "TSI Settings" input int InpTSI_Slow = 25; input int InpTSI_Fast = 13; input int InpTSI_Signal = 13; input group "Squeeze Settings" input int InpSqueezeLength = 20; input double InpBBMult = 2.0; input double InpKCMult = 1.5; //--- Struct for QuantScan Data struct QuantData { string timestamp; string symbol; double price; // --- Layer 1: H1 Context --- double trend_score; double trend_qual; string zone; string rel_strength_str; string beta_str; string alpha_str; // --- Layer 2: M15 Flow --- double m15_momentum; double m15_vol_qual; string m15_squeeze; double m15_z_score; double m15_vola_regime; string m15_tsi_dir; // --- Layer 3: M5 Trigger --- double m5_momentum; double m5_vol_qual; string m5_tsi_dir; double m5_velocity; // --- Composites --- double rev_prob; string absorption; }; //+------------------------------------------------------------------+ //| Helper: Detect Asset Class | //+------------------------------------------------------------------+ bool IsForexPair(string sym) { if(sym == InpBenchmark || sym == InpForexBench) return false; if(StringFind(sym, "USD") != -1 || StringFind(sym, "EUR") != -1 || StringFind(sym, "JPY") != -1 || StringFind(sym, "CHF") != -1 || StringFind(sym, "AUD") != -1 || StringFind(sym, "CAD") != -1 || StringFind(sym, "NZD") != -1) { if(StringFind(sym, "XAU")!=-1 || StringFind(sym, "XTI")!=-1 || StringFind(sym, "WTI")!=-1 || StringFind(sym, "BTC")!=-1 || StringFind(sym, "ETH")!=-1) return false; return true; } return false; } //+------------------------------------------------------------------+ //| Helper: Get Sentiment String for TF | //+------------------------------------------------------------------+ string GetSentimentForTF(ENUM_TIMEFRAMES tf) { // Uses Last Closed Bar change vs Prev double u_close[2], d_close[2]; // Fetch 2 bars. Index 0=Oldest (Prev), Index 1=Newest (Last Closed) // Note: If using FetchData logic (ArraySetAsSeries false), copy from end. // But CopyClose(..., 0, 2) returns: [0]=Bar 1 ago, [1]=Bar 0 (Current) ? // Docs: CopyClose(..., start_pos, count, buffer) -> start_pos relative to current. // start_pos=0 is current bar. start_pos=1 is closed bar. // Let's create array of 2 elements from start_pos=1 (last closed two candles). // So [0] = Bar 2, [1] = Bar 1. if(CopyClose(InpBenchmark, tf, 1, 2, u_close) != 2) return "N/A"; if(CopyClose(InpForexBench, tf, 1, 2, d_close) != 2) return "N/A"; double us500_chg = (u_close[1] - u_close[0]); double dxy_chg = (d_close[1] - d_close[0]); double us500_pct = (u_close[0]!=0) ? (us500_chg / u_close[0])*100 : 0; double dxy_pct = (d_close[0]!=0) ? (dxy_chg / d_close[0])*100 : 0; string state = "MIXED"; if(dxy_chg < 0 && us500_chg > 0) state = "RISK-ON"; else if(dxy_chg > 0 && us500_chg < 0) state = "RISK-OFF"; else if(dxy_chg > 0 && us500_chg > 0) state = "STRESS"; else if(dxy_chg < 0 && us500_chg < 0) state = "DEFLATION"; // Format: "RISK-ON (S: +0.2% D: -0.1%)" string tf_name = EnumToString(tf); StringReplace(tf_name, "PERIOD_", ""); return StringFormat("%s: %s (US:%.2f%% DX:%.2f%%)", tf_name, state, us500_pct, dxy_pct); } //+------------------------------------------------------------------+ //| Script Start | //+------------------------------------------------------------------+ void OnStart() { string symbols[]; int total_symbols = 0; if(InpUseMarketWatch) { total_symbols = SymbolsTotal(true); ArrayResize(symbols, total_symbols); for(int i=0; i 0 && CopyOpen(InpBenchmark, InpTFSlow, InpRSBars, 1, b_open) > 0) if(b_open[0] != 0) bench_change_pct = ((b_close[0] - b_open[0]) / b_open[0]) * 100.0; } string filename = "QuantScan_" + TimeToString(TimeCurrent(), TIME_DATE|TIME_MINUTES) + ".csv"; StringReplace(filename, ":", ""); StringReplace(filename, " ", "_"); int file_handle = FileOpen(filename, FILE_CSV|FILE_WRITE|FILE_ANSI, ";"); if(file_handle == INVALID_HANDLE) return; // --- WRITE HEADER --- FileWrite(file_handle, sentiment_line); // --- DYNAMIC COLUMNS --- string str_slow = EnumToString(InpTFSlow); StringReplace(str_slow, "PERIOD_", ""); string str_mid = EnumToString(InpTFMiddle); StringReplace(str_mid, "PERIOD_", ""); string str_fast = EnumToString(InpTFFast); StringReplace(str_fast, "PERIOD_", ""); string header = ""; header += "TIME (" + InpBrokerTimeZone + ");"; header += "SYMBOL;"; header += "PRICE;"; // Layer 1 header += StringFormat("TREND_SCORE_%s;", str_slow); header += StringFormat("TREND_QUAL_%s;", str_slow); header += StringFormat("ZONE_%s;", str_slow); header += StringFormat("REL_STRENGTH_%s;", str_slow); header += StringFormat("BETA_%s;", str_slow); header += StringFormat("ALPHA_%s;", str_slow); // Layer 2 header += StringFormat("MOMENTUM_%s;", str_mid); header += StringFormat("VOL_QUAL_%s;", str_mid); header += StringFormat("SQUEEZE_%s;", str_mid); header += StringFormat("Z_SCORE_%s;", str_mid); header += StringFormat("VOL_REGIME_%s;", str_mid); header += StringFormat("TSI_DIR_%s;", str_mid); // Layer 3 header += StringFormat("MOMENTUM_%s;", str_fast); header += StringFormat("VOL_QUAL_%s;", str_fast); header += StringFormat("TSI_DIR_%s;", str_fast); header += StringFormat("VELOCITY_%s;", str_fast); // Composites header += "REVERSION_PROB;"; header += "ABSORPTION"; FileWrite(file_handle, header); PrintFormat("Scanning %d symbols...", total_symbols); for(int i=0; i InpBetaLookback) { CMathStatisticsCalculator stats; double asset_ret[], bench_ret[]; int size = ArraySize(slow_c); double asset_sub[], bench_sub[]; ArrayResize(asset_sub, InpBetaLookback); ArrayResize(bench_sub, InpBetaLookback); for(int k=0; k 3.0) score += 40; else if(MathAbs(data.m15_z_score) > 2.0) score += 20; if(StringFind(data.zone, "Extreme") >= 0) score += 30; if(data.m15_momentum > 0.90 || data.m15_momentum < 0.10) score += 30; data.rev_prob = score; // Absorption based on Flow (M15) or Trig (M5)? Standard is Flow due to volume significance. // Let's stick to M15 for Absorption to filter M5 noise. int idx_cl = ArraySize(mid_c) - 2; if(idx_cl >= 0 && mid_atr > 0) { double body = MathAbs(mid_c[idx_cl] - mid_o[idx_cl]); CRelativeVolumeCalculator rv; rv.Init(InpRVOLPeriod); double bar_rvol = rv.CalculateSingle(ArraySize(mid_v), mid_v, idx_cl); if(bar_rvol > 2.0 && body < (0.4 * mid_atr)) data.absorption = "YES"; else data.absorption = "NO"; } else data.absorption = "-"; return true; } //+------------------------------------------------------------------+ //| Velocity Calculation | //+------------------------------------------------------------------+ double Calc_Velocity(const double &close[], double atr, int period) { if(atr == 0) return 0; int total = ArraySize(close); if(total <= period+2) return 0; double sum_move = 0; for(int i=0; i k_lo[idx])) ? "ON" : "OFF"; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double Calc_LaguerreRSI(const double &o[], const double &h[], const double &l[], const double &c[]) { CLaguerreRSICalculator calc; calc.Init(InpLaguerreGamma, 3, SMA); double lrsi[], sig[]; int total=ArraySize(c); ArrayResize(lrsi, total); ArrayResize(sig, total); calc.Calculate(total, 0, PRICE_CLOSE, o, h, l, c, lrsi, sig); return lrsi[total-2] / 100.0; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Calc_TSI_Dir(const double &o[], const double &h[], const double &l[], const double &c[], string &dir) { CTSICalculator calc; calc.Init(InpTSI_Slow, EMA, InpTSI_Fast, EMA, InpTSI_Signal, EMA); double tsi[], sig[], osc[]; int total=ArraySize(c); ArrayResize(tsi, total); ArrayResize(sig, total); ArrayResize(osc, total); calc.Calculate(total, 0, PRICE_CLOSE, o, h, l, c, tsi, sig, osc); if(tsi[total-2] > sig[total-2]) dir = "BULL"; else dir = "BEAR"; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string Calc_MurreyZone(string symbol, ENUM_TIMEFRAMES tf) { CMurreyMathCalculator calc; calc.Init(symbol, tf, InpMurreyPeriod, 0); double levels[]; if(!calc.Calculate(levels)) return "N/A"; double price = iClose(symbol, tf, 1); if(price < levels[2]) return "Extreme Low"; if(price > levels[10]) return "Extreme High"; if(price >= levels[2] && price < levels[3]) return "0/8-1/8 (Bottom)"; if(price >= levels[3] && price < levels[4]) return "1/8-2/8 (Weak)"; if(price >= levels[4] && price < levels[6]) return "2/8-4/8 (Lower)"; if(price >= levels[6] && price < levels[8]) return "4/8-6/8 (Upper)"; if(price >= levels[8] && price < levels[9]) return "6/8-7/8 (Weak)"; return "7/8-8/8 (Top)"; } //+------------------------------------------------------------------+