//+------------------------------------------------------------------+ //| SymbolScannerPanel.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs // Show input parameters dialog on start #include // For CSymbolInfo class // --- Input Parameters for Filtering --- input string Symbols_To_Scan = ""; // Default to empty, meaning scan all symbols on server input string Symbol_Separator = ","; // Separator for symbols in the list //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input string Filter_Path_Contains = ""; // Filter: Symbol path contains (empty = no filter) input bool Filter_Only_Selected_In_MarketWatch = true; // Filter: Only symbols selected in Market Watch input double Filter_Min_Volume_Min = 0.0; // Filter: Minimum allowed minimum volume input double Filter_Max_Volume_Min = 1000000.0; // Filter: Maximum allowed minimum volume (e.g., 1,000,000 lots) input double Filter_Min_Volume_Step = 0.0; // Filter: Minimum allowed volume step input double Filter_Max_Volume_Step = 1000000.0; // Filter: Maximum allowed volume step input bool Filter_Only_ETFs = false; // Filter: Only Exchange Traded Funds input bool Filter_Only_Extended_Hours = false; // Filter: Only symbols with "Extended Hours" in description input string Extended_Hours_Keyword = "(Extended Hours)"; // Keyword to identify extended hours symbols //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { Print("--- Symbol Scanner Panel Started ---"); PrintFormat("Filter Criteria:"); PrintFormat(" Symbols to Scan: '%s'", Symbols_To_Scan); PrintFormat(" Path Contains: '%s'", Filter_Path_Contains); PrintFormat(" Only Market Watch Selected: %s", Filter_Only_Selected_In_MarketWatch ? "Yes" : "No"); PrintFormat(" Min Volume (Min): %.2f - %.2f", Filter_Min_Volume_Min, Filter_Max_Volume_Min); PrintFormat(" Volume Step: %.2f - %.2f", Filter_Min_Volume_Step, Filter_Max_Volume_Step); PrintFormat(" Only ETFs: %s", Filter_Only_ETFs ? "Yes" : "No"); PrintFormat(" Only Extended Hours: %s (Keyword: '%s')", Filter_Only_Extended_Hours ? "Yes" : "No", Extended_Hours_Keyword); Print("---------------------------------"); string symbols_array[]; int num_parsed_symbols = StringSplit(Symbols_To_Scan, StringGetCharacter(Symbol_Separator, 0), symbols_array); // --- Logic: If Symbols_To_Scan is empty, scan all symbols on server --- bool scan_all_server_symbols = false; if(num_parsed_symbols == 1) { StringTrimRight(symbols_array[0]); StringTrimLeft(symbols_array[0]); } if(num_parsed_symbols == 0 || (num_parsed_symbols == 1 && StringLen(symbols_array[0]) == 0)) { scan_all_server_symbols = true; int total_server_symbols = SymbolsTotal(false); ArrayResize(symbols_array, total_server_symbols); for(int j = 0; j < total_server_symbols; j++) { symbols_array[j] = SymbolName(j, false); } num_parsed_symbols = total_server_symbols; Print("No specific symbols provided. Scanning all available symbols on the server."); } if(num_parsed_symbols == 0) { Print("No symbols to scan after processing input."); return; } int found_count = 0; // Print header for the results table PrintFormat("%-15s | %-8s | %-8s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-20s", "Symbol", "Spread", "Point", "TickValue", "TV_Profit", "TV_Loss", "TickSize", "Vol_Min", "Vol_Step", "Swap_Long", "Swap_Short", "Path"); Print("--------------------------------------------------------------------------------------------------------------------------------------------------------"); // Iterate through the user-provided/generated list of symbols for(int i = 0; i < num_parsed_symbols; i++) { string symbol_name = symbols_array[i]; StringTrimLeft(symbol_name); StringTrimRight(symbol_name); if(StringLen(symbol_name) == 0) continue; if(Filter_Only_Selected_In_MarketWatch) { if(!SymbolInfoInteger(symbol_name, SYMBOL_SELECT)) { continue; } } CSymbolInfo m_symbol_info; if(!m_symbol_info.Name(symbol_name)) { PrintFormat("Error: Failed to initialize CSymbolInfo for symbol '%s'. Error: %d", symbol_name, GetLastError()); continue; } if(!m_symbol_info.Refresh()) { PrintFormat("Error: Failed to refresh symbol data for '%s'. Error: %d", symbol_name, GetLastError()); continue; } // --- Retrieve all necessary properties for display and filtering --- double point = EMPTY_VALUE, tick_value = EMPTY_VALUE, tick_value_profit = EMPTY_VALUE, tick_value_loss = EMPTY_VALUE, tick_size = EMPTY_VALUE; double volume_min = EMPTY_VALUE, volume_step = EMPTY_VALUE, swap_long = EMPTY_VALUE, swap_short = EMPTY_VALUE; int spread = -1; string industry_name = "", description = "", path = ""; double temp_double_val; string temp_string_val; long temp_long_val; // Get SYMBOL_SPREAD ResetLastError(); if(m_symbol_info.InfoInteger(SYMBOL_SPREAD, temp_long_val)) spread = (int)temp_long_val; // Get SYMBOL_POINT ResetLastError(); if(m_symbol_info.InfoDouble(SYMBOL_POINT, temp_double_val)) point = temp_double_val; // Get SYMBOL_TRADE_TICK_VALUE ResetLastError(); if(m_symbol_info.InfoDouble(SYMBOL_TRADE_TICK_VALUE, temp_double_val)) tick_value = temp_double_val; // Get SYMBOL_TRADE_TICK_VALUE_PROFIT ResetLastError(); if(m_symbol_info.InfoDouble(SYMBOL_TRADE_TICK_VALUE_PROFIT, temp_double_val)) tick_value_profit = temp_double_val; // Get SYMBOL_TRADE_TICK_VALUE_LOSS ResetLastError(); if(m_symbol_info.InfoDouble(SYMBOL_TRADE_TICK_VALUE_LOSS, temp_double_val)) tick_value_loss = temp_double_val; // Get SYMBOL_TRADE_TICK_SIZE ResetLastError(); if(m_symbol_info.InfoDouble(SYMBOL_TRADE_TICK_SIZE, temp_double_val)) tick_size = temp_double_val; // Get SYMBOL_VOLUME_MIN ResetLastError(); if(m_symbol_info.InfoDouble(SYMBOL_VOLUME_MIN, temp_double_val)) volume_min = temp_double_val; // Get SYMBOL_VOLUME_STEP ResetLastError(); if(m_symbol_info.InfoDouble(SYMBOL_VOLUME_STEP, temp_double_val)) volume_step = temp_double_val; // Get SYMBOL_SWAP_LONG ResetLastError(); if(m_symbol_info.InfoDouble(SYMBOL_SWAP_LONG, temp_double_val)) swap_long = temp_double_val; // Get SYMBOL_SWAP_SHORT ResetLastError(); if(m_symbol_info.InfoDouble(SYMBOL_SWAP_SHORT, temp_double_val)) swap_short = temp_double_val; // Get SYMBOL_INDUSTRY_NAME (for ETF filter) ResetLastError(); if(m_symbol_info.InfoString(SYMBOL_INDUSTRY_NAME, temp_string_val)) industry_name = temp_string_val; // Get SYMBOL_DESCRIPTION (for Extended Hours filter) ResetLastError(); if(m_symbol_info.InfoString(SYMBOL_DESCRIPTION, temp_string_val)) description = temp_string_val; // Get SYMBOL_PATH (for Path filter) ResetLastError(); if(m_symbol_info.InfoString(SYMBOL_PATH, temp_string_val)) path = temp_string_val; // --- Filtering Logic --- bool passed_filter = true; // 1. Path Contains filter if(StringLen(Filter_Path_Contains) > 0 && StringFind(path, Filter_Path_Contains, 0) == -1) { passed_filter = false; } // 2. Min Volume filter if(passed_filter && (volume_min == EMPTY_VALUE || volume_min < Filter_Min_Volume_Min || volume_min > Filter_Max_Volume_Min)) { passed_filter = false; } // 3. Volume Step filter if(passed_filter && (volume_step == EMPTY_VALUE || volume_step < Filter_Min_Volume_Step || volume_step > Filter_Max_Volume_Step)) { passed_filter = false; } // 4. ETF filter if(passed_filter && Filter_Only_ETFs) { if(industry_name != "Exchange Traded Fund") // Case-sensitive match { passed_filter = false; } } // 5. Extended Hours filter if(passed_filter && Filter_Only_Extended_Hours) { if(StringFind(description, Extended_Hours_Keyword, 0) == -1) // Case-sensitive search { passed_filter = false; } } // --- Display Result if all filters passed --- if(passed_filter) { found_count++; // Format values for display, handling EMPTY_VALUE and empty strings string spread_str = (spread == -1) ? "N/A" : IntegerToString(spread); string point_str = (point == EMPTY_VALUE) ? "N/A" : DoubleToString(point, 5); string tick_value_str = (tick_value == EMPTY_VALUE) ? "N/A" : DoubleToString(tick_value, 2); string tick_value_profit_str = (tick_value_profit == EMPTY_VALUE) ? "N/A" : DoubleToString(tick_value_profit, 2); string tick_value_loss_str = (tick_value_loss == EMPTY_VALUE) ? "N/A" : DoubleToString(tick_value_loss, 2); string tick_size_str = (tick_size == EMPTY_VALUE) ? "N/A" : DoubleToString(tick_size, 5); string volume_min_str = (volume_min == EMPTY_VALUE) ? "N/A" : DoubleToString(volume_min, 2); string volume_step_str = (volume_step == EMPTY_VALUE) ? "N/A" : DoubleToString(volume_step, 2); string swap_long_str = (swap_long == EMPTY_VALUE) ? "N/A" : DoubleToString(swap_long, 2); string swap_short_str = (swap_short == EMPTY_VALUE) ? "N/A" : DoubleToString(swap_short, 2); string path_str = (path == "") ? "N/A" : path; // Print the data row, NOT the header again. PrintFormat("%-15s | %-8s | %-8s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-20s", symbol_name, spread_str, point_str, tick_value_str, tick_value_profit_str, tick_value_loss_str, tick_size_str, volume_min_str, volume_step_str, swap_long_str, swap_short_str, path_str); } } Print("--------------------------------------------------------------------------------------------------------------------------------------------------------"); PrintFormat("Scanner Completed. Found symbols: %d", found_count); Print("---------------------------------"); } //+------------------------------------------------------------------+