2025-08-23 15:01:31 +02:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| SymbolScannerPanel.mq5 |
|
|
|
|
|
//| Copyright 2025, xxxxxxxx |
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "Copyright 2025, xxxxxxxx"
|
|
|
|
|
#property link ""
|
|
|
|
|
#property version "2.00" // Refactored into a class-based structure
|
|
|
|
|
#property script_show_inputs
|
|
|
|
|
#property description "Scans and filters symbols based on various criteria."
|
|
|
|
|
|
|
|
|
|
#include <Trade\SymbolInfo.mqh>
|
|
|
|
|
|
|
|
|
|
//--- Input Parameters for Filtering ---
|
|
|
|
|
input string InpSymbolsToScan = "";
|
|
|
|
|
input string InpSymbolSeparator = ",";
|
|
|
|
|
input string InpFilterPathContains = "";
|
|
|
|
|
input bool InpFilterMarketWatch = true;
|
|
|
|
|
input double InpFilterMinVolMin = 0.0;
|
|
|
|
|
input double InpFilterMaxVolMin = 1000000.0;
|
|
|
|
|
input double InpFilterMinVolStep = 0.0;
|
|
|
|
|
input double InpFilterMaxVolStep = 1000000.0;
|
|
|
|
|
input bool InpFilterOnlyETFs = false;
|
|
|
|
|
input bool InpFilterExtendedHours= false;
|
|
|
|
|
input string InpKeywordExtHours = "(Extended Hours)";
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| CSymbolScanner Class |
|
|
|
|
|
//| Encapsulates all logic for scanning and filtering symbols. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CSymbolScanner
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
//--- A structure to hold all necessary data for a symbol
|
|
|
|
|
struct SymbolData
|
|
|
|
|
{
|
|
|
|
|
string name;
|
|
|
|
|
string path;
|
|
|
|
|
string description;
|
|
|
|
|
string industry_name;
|
|
|
|
|
int spread;
|
|
|
|
|
double point;
|
|
|
|
|
double tick_value;
|
|
|
|
|
double tick_value_profit;
|
|
|
|
|
double tick_value_loss;
|
|
|
|
|
double tick_size;
|
|
|
|
|
double volume_min;
|
|
|
|
|
double volume_step;
|
|
|
|
|
double swap_long;
|
|
|
|
|
double swap_short;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//--- Filter criteria
|
|
|
|
|
string m_symbols_to_scan;
|
|
|
|
|
string m_symbol_separator;
|
|
|
|
|
string m_filter_path_contains;
|
|
|
|
|
bool m_filter_market_watch;
|
|
|
|
|
double m_filter_min_vol_min;
|
|
|
|
|
double m_filter_max_vol_min;
|
|
|
|
|
double m_filter_min_vol_step;
|
|
|
|
|
double m_filter_max_vol_step;
|
|
|
|
|
bool m_filter_only_etfs;
|
|
|
|
|
bool m_filter_extended_hours;
|
|
|
|
|
string m_keyword_ext_hours;
|
|
|
|
|
|
|
|
|
|
//--- Internal objects
|
|
|
|
|
CSymbolInfo m_symbol_info;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
//--- Constructor
|
|
|
|
|
CSymbolScanner(string symbols, string separator, string path, bool market_watch,
|
|
|
|
|
double min_vol_min, double max_vol_min, double min_vol_step, double max_vol_step,
|
|
|
|
|
bool only_etfs, bool ext_hours, string keyword);
|
|
|
|
|
//--- Main execution method
|
|
|
|
|
void Run(void);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
//--- Helper methods
|
|
|
|
|
bool GetSymbolsToScan(string &symbols_array[]);
|
|
|
|
|
bool SymbolPassesFilters(const SymbolData &data);
|
|
|
|
|
void GatherSymbolData(const string symbol_name, SymbolData &data);
|
|
|
|
|
void PrintSymbolData(const SymbolData &data);
|
|
|
|
|
void PrintHeader(void);
|
|
|
|
|
void PrintFilterCriteria(void);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Constructor: Initializes the scanner with filter criteria |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
CSymbolScanner::CSymbolScanner(string symbols, string separator, string path, bool market_watch,
|
|
|
|
|
double min_vol_min, double max_vol_min, double min_vol_step, double max_vol_step,
|
|
|
|
|
bool only_etfs, bool ext_hours, string keyword)
|
|
|
|
|
{
|
|
|
|
|
m_symbols_to_scan = symbols;
|
|
|
|
|
m_symbol_separator = separator;
|
|
|
|
|
m_filter_path_contains = path;
|
|
|
|
|
m_filter_market_watch = market_watch;
|
|
|
|
|
m_filter_min_vol_min = min_vol_min;
|
|
|
|
|
m_filter_max_vol_min = max_vol_min;
|
|
|
|
|
m_filter_min_vol_step = min_vol_step;
|
|
|
|
|
m_filter_max_vol_step = max_vol_step;
|
|
|
|
|
m_filter_only_etfs = only_etfs;
|
|
|
|
|
m_filter_extended_hours = ext_hours;
|
|
|
|
|
m_keyword_ext_hours = keyword;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Main execution method |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CSymbolScanner::Run(void)
|
|
|
|
|
{
|
|
|
|
|
Print("--- Symbol Scanner Panel Started ---");
|
|
|
|
|
PrintFilterCriteria();
|
|
|
|
|
Print("---------------------------------");
|
|
|
|
|
|
|
|
|
|
string symbols_to_scan[];
|
|
|
|
|
if(!GetSymbolsToScan(symbols_to_scan))
|
|
|
|
|
{
|
|
|
|
|
Print("No symbols to scan. Exiting.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int found_count = 0;
|
|
|
|
|
PrintHeader();
|
|
|
|
|
|
|
|
|
|
SymbolData current_symbol_data;
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < ArraySize(symbols_to_scan); i++)
|
|
|
|
|
{
|
|
|
|
|
string symbol_name = symbols_to_scan[i];
|
|
|
|
|
StringTrimLeft(symbol_name);
|
|
|
|
|
StringTrimRight(symbol_name);
|
|
|
|
|
|
|
|
|
|
if(StringLen(symbol_name) == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
GatherSymbolData(symbol_name, current_symbol_data);
|
|
|
|
|
|
|
|
|
|
if(SymbolPassesFilters(current_symbol_data))
|
|
|
|
|
{
|
|
|
|
|
found_count++;
|
|
|
|
|
PrintSymbolData(current_symbol_data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Print("--------------------------------------------------------------------------------------------------------------------------------------------------------");
|
|
|
|
|
PrintFormat("Scanner Completed. Found %d matching symbols.", found_count);
|
|
|
|
|
Print("---------------------------------");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Gathers all required data for a single symbol |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CSymbolScanner::GatherSymbolData(const string symbol_name, SymbolData &data)
|
|
|
|
|
{
|
|
|
|
|
if(!m_symbol_info.Name(symbol_name))
|
|
|
|
|
return;
|
|
|
|
|
if(!m_symbol_info.RefreshRates())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
data.name = m_symbol_info.Name();
|
|
|
|
|
data.path = m_symbol_info.Path();
|
|
|
|
|
data.description = m_symbol_info.Description();
|
|
|
|
|
m_symbol_info.InfoString(SYMBOL_INDUSTRY_NAME, data.industry_name);
|
|
|
|
|
data.spread = m_symbol_info.Spread();
|
|
|
|
|
data.point = m_symbol_info.Point();
|
|
|
|
|
data.tick_value = m_symbol_info.TickValue();
|
|
|
|
|
data.tick_value_profit = m_symbol_info.TickValueProfit();
|
|
|
|
|
data.tick_value_loss = m_symbol_info.TickValueLoss();
|
|
|
|
|
data.tick_size = m_symbol_info.TickSize();
|
|
|
|
|
data.volume_min = m_symbol_info.LotsMin();
|
|
|
|
|
data.volume_step = m_symbol_info.LotsStep();
|
|
|
|
|
data.swap_long = m_symbol_info.SwapLong();
|
|
|
|
|
data.swap_short = m_symbol_info.SwapShort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Checks if a symbol's data meets all the filter criteria |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CSymbolScanner::SymbolPassesFilters(const SymbolData &data)
|
|
|
|
|
{
|
|
|
|
|
if(m_filter_market_watch && !SymbolInfoInteger(data.name, SYMBOL_SELECT))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(StringLen(m_filter_path_contains) > 0 && StringFind(data.path, m_filter_path_contains) == -1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(data.volume_min < m_filter_min_vol_min || data.volume_min > m_filter_max_vol_min)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(data.volume_step < m_filter_min_vol_step || data.volume_step > m_filter_max_vol_step)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(m_filter_only_etfs && data.industry_name != "Exchange Traded Fund")
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if(m_filter_extended_hours && StringFind(data.description, m_keyword_ext_hours) == -1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Gets the list of symbols to scan from inputs or the whole market |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CSymbolScanner::GetSymbolsToScan(string &symbols_array[])
|
|
|
|
|
{
|
|
|
|
|
string symbols_input_copy = m_symbols_to_scan;
|
|
|
|
|
StringTrimLeft(symbols_input_copy);
|
|
|
|
|
StringTrimRight(symbols_input_copy);
|
|
|
|
|
|
|
|
|
|
if(StringLen(symbols_input_copy) > 0)
|
|
|
|
|
{
|
|
|
|
|
StringSplit(symbols_input_copy, StringGetCharacter(m_symbol_separator, 0), symbols_array);
|
|
|
|
|
PrintFormat("%d symbols provided by user.", ArraySize(symbols_array));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int total_server_symbols = SymbolsTotal(false);
|
|
|
|
|
if(total_server_symbols == 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
ArrayResize(symbols_array, total_server_symbols);
|
|
|
|
|
for(int j = 0; j < total_server_symbols; j++)
|
|
|
|
|
{
|
|
|
|
|
symbols_array[j] = SymbolName(j, false);
|
|
|
|
|
}
|
|
|
|
|
Print("No specific symbols provided. Scanning all available server symbols.");
|
|
|
|
|
}
|
|
|
|
|
return(ArraySize(symbols_array) > 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Prints a formatted row of data for a single symbol |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CSymbolScanner::PrintSymbolData(const SymbolData &data)
|
|
|
|
|
{
|
|
|
|
|
PrintFormat("%-15s | %-8s | %-8s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-20s",
|
|
|
|
|
data.name,
|
|
|
|
|
(string)data.spread,
|
|
|
|
|
DoubleToString(data.point, -1),
|
|
|
|
|
DoubleToString(data.tick_value, 2),
|
|
|
|
|
DoubleToString(data.tick_value_profit, 2),
|
|
|
|
|
DoubleToString(data.tick_value_loss, 2),
|
|
|
|
|
DoubleToString(data.tick_size, -1),
|
|
|
|
|
DoubleToString(data.volume_min, -1),
|
|
|
|
|
DoubleToString(data.volume_step, -1),
|
|
|
|
|
DoubleToString(data.swap_long, 2),
|
|
|
|
|
DoubleToString(data.swap_short, 2),
|
|
|
|
|
data.path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Prints the header for the output table |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CSymbolScanner::PrintHeader()
|
|
|
|
|
{
|
|
|
|
|
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("--------------------------------------------------------------------------------------------------------------------------------------------------------");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Prints the filter criteria that the scanner is using |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CSymbolScanner::PrintFilterCriteria()
|
|
|
|
|
{
|
|
|
|
|
PrintFormat("Filter Criteria:");
|
|
|
|
|
PrintFormat(" Symbols to Scan: '%s'", m_symbols_to_scan);
|
|
|
|
|
PrintFormat(" Path Contains: '%s'", m_filter_path_contains);
|
|
|
|
|
PrintFormat(" Only Market Watch Selected: %s", (string)m_filter_market_watch);
|
|
|
|
|
PrintFormat(" Min Volume (Min): %g - %g", m_filter_min_vol_min, m_filter_max_vol_min);
|
|
|
|
|
PrintFormat(" Volume Step: %g - %g", m_filter_min_vol_step, m_filter_max_vol_step);
|
|
|
|
|
PrintFormat(" Only ETFs: %s", (string)m_filter_only_etfs);
|
|
|
|
|
PrintFormat(" Only Extended Hours: %s (Keyword: '%s')", (string)m_filter_extended_hours, m_keyword_ext_hours);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Script program start function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void OnStart()
|
|
|
|
|
{
|
|
|
|
|
//--- Create an instance of the scanner with the input parameters
|
|
|
|
|
CSymbolScanner scanner(InpSymbolsToScan, InpSymbolSeparator, InpFilterPathContains, InpFilterMarketWatch,
|
|
|
|
|
InpFilterMinVolMin, InpFilterMaxVolMin, InpFilterMinVolStep, InpFilterMaxVolStep,
|
|
|
|
|
InpFilterOnlyETFs, InpFilterExtendedHours, InpKeywordExtHours);
|
|
|
|
|
//--- Run the scanner
|
|
|
|
|
scanner.Run();
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//+------------------------------------------------------------------+
|