2025-07-14 20:23:29 +02:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| 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 <Trade\SymbolInfo.mqh> // For CSymbolInfo class
|
|
|
|
|
|
|
|
|
|
// --- Input Parameters for Filtering ---
|
2025-07-15 15:52:51 +02:00
|
|
|
input string Symbols_To_Scan = ""; // Default to empty, meaning scan all symbols on server
|
|
|
|
|
input string Symbol_Separator = ","; // Separator for symbols in the list
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2025-07-14 20:23:29 +02:00
|
|
|
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:");
|
2025-07-15 15:52:51 +02:00
|
|
|
PrintFormat(" Symbols to Scan: '%s'", Symbols_To_Scan);
|
2025-07-14 20:23:29 +02:00
|
|
|
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("---------------------------------");
|
|
|
|
|
|
2025-07-15 15:52:51 +02:00
|
|
|
string symbols_array[];
|
|
|
|
|
int num_parsed_symbols = StringSplit(Symbols_To_Scan, StringGetCharacter(Symbol_Separator, 0), symbols_array);
|
|
|
|
|
|
2025-07-16 23:31:50 +02:00
|
|
|
// --- Logic: If Symbols_To_Scan is empty, scan all symbols on server ---
|
2025-07-15 15:52:51 +02:00
|
|
|
bool scan_all_server_symbols = false;
|
|
|
|
|
|
|
|
|
|
if(num_parsed_symbols == 1)
|
2025-07-14 20:23:29 +02:00
|
|
|
{
|
2025-07-15 15:52:51 +02:00
|
|
|
StringTrimRight(symbols_array[0]);
|
|
|
|
|
StringTrimLeft(symbols_array[0]);
|
2025-07-14 20:23:29 +02:00
|
|
|
}
|
2025-07-15 15:52:51 +02:00
|
|
|
|
|
|
|
|
if(num_parsed_symbols == 0 || (num_parsed_symbols == 1 && StringLen(symbols_array[0]) == 0))
|
2025-07-14 20:23:29 +02:00
|
|
|
{
|
2025-07-15 15:52:51 +02:00
|
|
|
scan_all_server_symbols = true;
|
|
|
|
|
|
|
|
|
|
int total_server_symbols = SymbolsTotal(false);
|
2025-07-16 23:31:50 +02:00
|
|
|
ArrayResize(symbols_array, total_server_symbols);
|
2025-07-15 15:52:51 +02:00
|
|
|
for(int j = 0; j < total_server_symbols; j++)
|
|
|
|
|
{
|
|
|
|
|
symbols_array[j] = SymbolName(j, false);
|
|
|
|
|
}
|
2025-07-16 23:31:50 +02:00
|
|
|
num_parsed_symbols = total_server_symbols;
|
2025-07-15 15:52:51 +02:00
|
|
|
|
|
|
|
|
Print("No specific symbols provided. Scanning all available symbols on the server.");
|
2025-07-14 20:23:29 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-16 23:31:50 +02:00
|
|
|
if(num_parsed_symbols == 0)
|
2025-07-14 20:23:29 +02:00
|
|
|
{
|
2025-07-15 15:52:51 +02:00
|
|
|
Print("No symbols to scan after processing input.");
|
2025-07-14 20:23:29 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int found_count = 0;
|
|
|
|
|
|
|
|
|
|
// Print header for the results table
|
2025-07-16 23:31:50 +02:00
|
|
|
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("--------------------------------------------------------------------------------------------------------------------------------------------------------");
|
2025-07-14 20:23:29 +02:00
|
|
|
|
2025-07-15 15:52:51 +02:00
|
|
|
// Iterate through the user-provided/generated list of symbols
|
|
|
|
|
for(int i = 0; i < num_parsed_symbols; i++)
|
2025-07-14 20:23:29 +02:00
|
|
|
{
|
2025-07-15 15:52:51 +02:00
|
|
|
string symbol_name = symbols_array[i];
|
2025-07-16 23:31:50 +02:00
|
|
|
StringTrimLeft(symbol_name);
|
|
|
|
|
StringTrimRight(symbol_name);
|
2025-07-15 15:52:51 +02:00
|
|
|
|
|
|
|
|
if(StringLen(symbol_name) == 0)
|
2025-07-16 23:31:50 +02:00
|
|
|
continue;
|
2025-07-15 15:52:51 +02:00
|
|
|
|
2025-07-14 20:23:29 +02:00
|
|
|
if(Filter_Only_Selected_In_MarketWatch)
|
|
|
|
|
{
|
2025-07-15 15:52:51 +02:00
|
|
|
if(!SymbolInfoInteger(symbol_name, SYMBOL_SELECT))
|
|
|
|
|
{
|
2025-07-16 23:31:50 +02:00
|
|
|
continue;
|
2025-07-15 15:52:51 +02:00
|
|
|
}
|
2025-07-14 20:23:29 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-16 23:31:50 +02:00
|
|
|
CSymbolInfo m_symbol_info;
|
2025-07-14 20:23:29 +02:00
|
|
|
|
|
|
|
|
if(!m_symbol_info.Name(symbol_name))
|
|
|
|
|
{
|
|
|
|
|
PrintFormat("Error: Failed to initialize CSymbolInfo for symbol '%s'. Error: %d", symbol_name, GetLastError());
|
2025-07-16 23:31:50 +02:00
|
|
|
continue;
|
2025-07-14 20:23:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!m_symbol_info.Refresh())
|
|
|
|
|
{
|
|
|
|
|
PrintFormat("Error: Failed to refresh symbol data for '%s'. Error: %d", symbol_name, GetLastError());
|
2025-07-16 23:31:50 +02:00
|
|
|
continue;
|
2025-07-14 20:23:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- 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;
|
2025-07-16 23:31:50 +02:00
|
|
|
int spread = -1;
|
2025-07-14 20:23:29 +02:00
|
|
|
string industry_name = "", description = "", path = "";
|
|
|
|
|
|
2025-07-16 23:31:50 +02:00
|
|
|
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;
|
2025-07-14 20:23:29 +02:00
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
2025-07-15 15:52:51 +02:00
|
|
|
// 1. Path Contains filter
|
|
|
|
|
if(StringLen(Filter_Path_Contains) > 0 && StringFind(path, Filter_Path_Contains, 0) == -1)
|
2025-07-14 20:23:29 +02:00
|
|
|
{
|
|
|
|
|
passed_filter = false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-15 15:52:51 +02:00
|
|
|
// 2. Min Volume filter
|
2025-07-14 20:23:29 +02:00
|
|
|
if(passed_filter && (volume_min == EMPTY_VALUE || volume_min < Filter_Min_Volume_Min || volume_min > Filter_Max_Volume_Min))
|
|
|
|
|
{
|
|
|
|
|
passed_filter = false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-15 15:52:51 +02:00
|
|
|
// 3. Volume Step filter
|
2025-07-14 20:23:29 +02:00
|
|
|
if(passed_filter && (volume_step == EMPTY_VALUE || volume_step < Filter_Min_Volume_Step || volume_step > Filter_Max_Volume_Step))
|
|
|
|
|
{
|
|
|
|
|
passed_filter = false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-15 15:52:51 +02:00
|
|
|
// 4. ETF filter
|
2025-07-14 20:23:29 +02:00
|
|
|
if(passed_filter && Filter_Only_ETFs)
|
|
|
|
|
{
|
|
|
|
|
if(industry_name != "Exchange Traded Fund") // Case-sensitive match
|
|
|
|
|
{
|
|
|
|
|
passed_filter = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-15 15:52:51 +02:00
|
|
|
// 5. Extended Hours filter
|
2025-07-14 20:23:29 +02:00
|
|
|
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
|
2025-07-16 23:31:50 +02:00
|
|
|
string spread_str = (spread == -1) ? "N/A" : IntegerToString(spread);
|
2025-07-14 20:23:29 +02:00
|
|
|
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;
|
|
|
|
|
|
2025-07-16 23:31:50 +02:00
|
|
|
// Print the data row, NOT the header again.
|
|
|
|
|
PrintFormat("%-15s | %-8s | %-8s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-20s",
|
2025-07-14 20:23:29 +02:00
|
|
|
symbol_name,
|
2025-07-16 23:31:50 +02:00
|
|
|
spread_str,
|
2025-07-14 20:23:29 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-16 23:31:50 +02:00
|
|
|
Print("--------------------------------------------------------------------------------------------------------------------------------------------------------");
|
2025-07-14 20:23:29 +02:00
|
|
|
PrintFormat("Scanner Completed. Found symbols: %d", found_count);
|
|
|
|
|
Print("---------------------------------");
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
�
|