Русский Español Português
preview
Downloading International Monetary Fund Data Using Python

Downloading International Monetary Fund Data Using Python

MetaTrader 5Integration |
206 1
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Imagine this: while most traders are analyzing Japanese candlesticks and plotting support lines, you already know that the Eurozone economy is showing signs of slowing down, and the British pound is bracing for a series of shocks due to the deteriorating trade balance. Sounds like science fiction? Not at all. The International Monetary Fund publishes terabytes of macroeconomic data daily, which, if processed correctly, can become a powerful tool for creating trading strategies.

Over the decades of its existence, the IMF has accumulated a unique database covering economic indicators of 190 countries. This data includes not only traditional metrics like GDP and inflation, but also complex structural indicators that allow us to see the real state of economies long before markets begin to react. The problem is that this data is presented in raw form and requires significant processing to become of practical value.


Anatomy of IMF economic data

Every time the central bank publishes statistics or the government reports fiscal indicators, this information is entered into the IMF databases. But simply reading Excel spreadsheets will give little benefit to a trader. The real magic begins when you understand how different economic indicators interact with each other and affect exchange rates.

Take, for example, the current account to GDP ratio. To the untrained observer, this is just a boring number in a report. But an experienced analyst knows that a persistent current account deficit above 5% of GDP often precedes a currency crisis. Turkey in 2018, Argentina in 2019, and even the UK during the GBP crisis demonstrated exactly this dynamic.

IMF data are particularly valuable because they are standardized and comparable across countries. Unlike national statistics, which may differ in methodology, the IMF uses uniform accounting principles. This allows for relevant comparisons to be made and the relative strengths or weaknesses of economies to be identified.


Architectural solution for data mining

Building a system to work with IMF data requires understanding the specifics of their API. The International Monetary Fund uses the SDMX-JSON standard, which, although an industry standard, is somewhat complex to implement. The interface is built on the REST principle, but the structure of the responses can vary significantly depending on the requested data.

The main technical challenge is that the interface returns data in a multi-level JSON structure, where each time series can contain various metadata, observation statuses, and additional attributes. Moreover, some indicators are published at different intervals, which requires additional logic to synchronize the data.

class IMFDataCollector:
    def __init__(self):
        self.base_url = "http://dataservices.imf.org/REST/SDMX_JSON.svc"
        self.session = requests.Session()
        self.rate_limiter = RateLimitManager(max_requests_per_minute=60)
        
    def fetch_economic_data(self, dataset_id: str, countries: List[str], 
                           indicators: List[str], start_year: int, end_year: int) -> pd.DataFrame:
        """
        Universal method for fetching IMF data with error handling and optimization
        """
        self.rate_limiter.wait_if_needed()
        
        # Build complex URL according to SDMX specification
        countries_string = '+'.join(countries)
        indicators_string = '+'.join(indicators)
        
        url = f"{self.base_url}/CompactData/{dataset_id}/A.{countries_string}.{indicators_string}"
        
        params = {
            'startPeriod': str(start_year),
            'endPeriod': str(end_year)
        }
        
        try:
            response = self.session.get(url, params=params, timeout=30)
            response.raise_for_status()
            
            raw_data = response.json()
            return self._parse_complex_response(raw_data)
            
        except requests.exceptions.RequestException as e:
            logger.error(f"Error fetching data: {e}")
            return pd.DataFrame()

A critical element of the architecture is the caching system. The IMF API has query limits, and macroeconomic data is updated relatively infrequently. The implementation of an intelligent cache allows for significant acceleration of the system and reduction of the load on IMF servers.


The alchemy of economic indicators

Simply getting GDP or inflation data will not give you a competitive advantage. Real value comes from creating composite indicators that combine various economic indicators into a single metric. This is where the art of economic analysis comes into play.

Let's consider the creation of a country's economic strength index. The traditional approach involves simply weighting the indicators, but more sophisticated solution takes into account non-linear dependencies and dynamic correlations between variables.

def calculate_dynamic_economic_strength(country_data: pd.DataFrame) -> pd.Series:
    """
    Calculate dynamic economic strength index accounting for volatility patterns
    """
    # Extract key economic indicators
    gdp_growth = country_data['NGDP_RPCH'].rolling(4).mean()
    inflation = country_data['PCPIPCH']
    unemployment = country_data['LUR']
    current_account = country_data['BCA_NGDPD']
    
    # Normalize with historical volatility consideration
    gdp_normalized = normalize_with_volatility(gdp_growth, target_vol=0.15)
    inflation_normalized = proximity_to_target(inflation, target=2.0, tolerance=1.0)
    unemployment_normalized = inverse_normalize(unemployment)
    current_account_normalized = sigmoid_normalize(current_account, inflection=-3.0)
    
    # Dynamic weights based on currency correlation
    weights = calculate_dynamic_weights(country_data, lookback_quarters=8)
    
    strength_index = (gdp_normalized * weights['gdp'] + 
                     inflation_normalized * weights['inflation'] +
                     unemployment_normalized * weights['unemployment'] +
                     current_account_normalized * weights['current_account'])
    
    return strength_index.rolling(2).mean()  # Smoothing to reduce noise

Of particular interest is the creation of a currency attractiveness indicator that takes into account not only current economic indicators, but also their trends, as well as the country's relative position compared to trading partners.


Translating macroeconomics into trading signals

Having good economic data alone does not guarantee profitable trading. The key challenge is to transform fundamental information into concrete trading decisions. It is important to understand here that macroeconomic factors operate on different time horizons and with different lags.

One of the most effective strategies is based on the concept of economic momentum. The idea is that changes in the relative economic strength of two countries should eventually be reflected in the exchange rate of their currencies. However, markets often react with a delay, which creates trading opportunities for those who can interpret macro data correctly.

class EconomicMomentumStrategy:
    def __init__(self, imf_data_source: IMFDataCollector):
        self.data_source = imf_data_source
        self.momentum_threshold = 0.3
        self.confirmation_period = 3  # quarters
        
    def generate_trading_signals(self, currency_pairs: List[str]) -> List[Dict]:
        """
        Generate signals based on economic momentum changes
        """
        signals = []
        
        for pair in currency_pairs:
            base_country = self._extract_country_from_currency(pair[:3])
            quote_country = self._extract_country_from_currency(pair[3:])
            
            # Get economic data for the last 5 years
            base_data = self._fetch_country_indicators(base_country, years=5)
            quote_data = self._fetch_country_indicators(quote_country, years=5)
            
            # Calculate economic strength
            base_strength = self._calculate_economic_strength(base_data)
            quote_strength = self._calculate_economic_strength(quote_data)
            
            # Determine trend and momentum
            momentum = self._calculate_momentum_differential(base_strength, quote_strength)
            trend_confirmation = self._verify_trend_confirmation(momentum)
            
            # Generate signal if sufficient momentum and confirmation exist
            if abs(momentum.iloc[-1]) > self.momentum_threshold and trend_confirmation:
                direction = 'BUY' if momentum.iloc[-1] > 0 else 'SELL'
                confidence = min(abs(momentum.iloc[-1]) / self.momentum_threshold, 2.0)
                
                signals.append({
                    'pair': pair,
                    'direction': direction,
                    'confidence': confidence,
                    'economic_basis': self._get_fundamental_reasoning(base_data, quote_data),
                    'expected_duration': 'medium_term'  # 3-12 months
                })
        
        return self._rank_signals_by_quality(signals)

Another promising strategy is to look for divergences in interest rates and economic activity. Central banks often adjust monetary policy with a lag relative to changes in the economy, creating windows of opportunity for profitable trading.


Integration with trading infrastructure

Creating beautiful charts and calculating indicators is only half the task. For practical application, it is necessary to integrate the macrodata analysis system with the MetaTrader 5 trading platform. This integration should provide real-time data updates, reliable signal broadcast and the ability to backtest strategies.

The most elegant solution is to create an intermediate layer that converts Python analysis into a format understandable by MQL5. This layer can use various communication methods: from simple CSV files to more advanced solutions via named pipes or TCP connections.

class MT5DataBridge:
    def __init__(self, output_directory: str = "Files"):
        self.output_dir = Path(output_directory)
        self.output_dir.mkdir(exist_ok=True)
        
    def export_economic_signals(self, signals: List[Dict], filename: str):
        """
        Export signals in format optimized for MQL5 reading
        """
        mt5_signals = []
        
        for signal in signals:
            mt5_signals.append({
                'timestamp': datetime.now().strftime('%Y.%m.%d %H:%M'),
                'symbol': signal['pair'],
                'signal_type': signal['direction'],
                'strength': signal['confidence'],
                'timeframe': 'H4',  # Recommended timeframe for macro strategies
                'fundamental_score': signal.get('economic_basis', 0),
                'expected_duration_days': self._convert_duration(signal['expected_duration'])
            })
        
        df = pd.DataFrame(mt5_signals)
        df.to_csv(self.output_dir / f"{filename}.csv", index=False)
        
        # Create binary file for fast reading as well
        self._create_binary_export(df, filename)


Practical cases and proven strategies

Theoretical arguments about the benefits of macroeconomic analysis become convincing only when there are concrete examples of successful application. Let's look at several cases where correct interpretation of IMF data could yield significant profits.

In 2018, IMF data showed a steady deterioration in Turkey's external economic indicators. The current account deficit reached 6% of GDP, external debt exceeded 50% of GDP, and inflation began to spiral out of control. Traders using our monitoring system would have noticed these warning signs months before the Turkish lira's August collapse, when USDTRY rose from 4.5 to 7.0 in just a few weeks.

Even more illustrative is the example of GBP during Brexit. IMF data on the UK's trade balance, direct investment and export structure clearly demonstrated the economy's vulnerability to a breakdown in trade ties with the EU. An early warning system based on this data could have warned of increased GBP volatility well before the referendum.

Of particular interest is the application of the carry trade model, supplemented by macroeconomic analysis. Traditional carry trading relies on interest rate differentials, but adding fundamental factors significantly improves the risk-reward ratio.

class EnhancedCarryTradeStrategy:
    def __init__(self, imf_data: IMFDataCollector):
        self.data_source = imf_data
        self.min_rate_differential = 2.0  # Minimum rate difference in %
        self.stability_threshold = 0.7    # Economic stability threshold
        
    def find_carry_opportunities(self) -> List[Dict]:
        """
        Search for carry trade opportunities considering macroeconomic stability
        """
        major_currencies = ['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD', 'CHF', 'NZD']
        opportunities = []
        
        # Get interest rates and economic indicators data
        rates_data = self._fetch_interest_rates_data(major_currencies)
        economic_data = self._fetch_economic_stability_data(major_currencies)
        
        # Analyze all possible pairs
        for high_yield_currency in major_currencies:
            for low_yield_currency in major_currencies:
                if high_yield_currency == low_yield_currency:
                    continue
                
                rate_differential = rates_data[high_yield_currency] - rates_data[low_yield_currency]
                
                if rate_differential >= self.min_rate_differential:
                    # Check economic stability of high-yield currency
                    stability_score = self._calculate_stability_score(
                        economic_data[high_yield_currency]
                    )
                    
                    if stability_score >= self.stability_threshold:
                        risk_premium = self._calculate_risk_premium(
                            economic_data[high_yield_currency],
                            economic_data[low_yield_currency]
                        )
                        
                        expected_return = rate_differential - risk_premium
                        
                        opportunities.append({
                            'pair': f"{high_yield_currency}{low_yield_currency}",
                            'rate_differential': rate_differential,
                            'stability_score': stability_score,
                            'risk_premium': risk_premium,
                            'expected_annual_return': expected_return,
                            'recommended_allocation': self._calculate_optimal_size(
                                expected_return, stability_score
                            )
                        })
        
        return sorted(opportunities, key=lambda x: x['expected_annual_return'], reverse=True)


Risk management in macroeconomic strategies

Macroeconomic strategies require a special approach to risk management. Unlike short-term technical strategies, here we are dealing with fundamental imbalances that may persist longer than you have the means to wait them out, as Keynes said.

The key element is diversification not only across currency pairs, but also across economic regions and factors. A strategy based solely on interest rate divergence may fail during a global crisis when correlations between currencies increase sharply.

Equally important is proper position sizing management. Macroeconomic signals often have high accuracy but low frequency, which requires an appropriate approach to position sizing.

class MacroRiskManager:
    def __init__(self, max_portfolio_risk: float = 0.15):
        self.max_risk = max_portfolio_risk
        self.correlation_matrix = None
        self.last_correlation_update = None
        
    def calculate_position_size(self, signal: Dict, portfolio_context: Dict) -> float:
        """
        Calculate position size considering correlations and macroeconomic risks
        """
        # Base size based on signal strength
        base_size = signal['confidence'] * 0.1  # Maximum 10% per signal
        
        # Adjustment for correlation with existing positions
        correlation_adjustment = self._calculate_correlation_adjustment(
            signal['pair'], portfolio_context
        )
        
        # Adjustment for macroeconomic risk environment
        macro_risk_adjustment = self._assess_macro_risk_environment()
        
        # Adjustment for historical volatility of the pair
        volatility_adjustment = self._get_volatility_adjustment(signal['pair'])
        
        final_size = (base_size * 
                     correlation_adjustment * 
                     macro_risk_adjustment * 
                     volatility_adjustment)
        
        return min(final_size, self.max_risk / 3)  # No more than 1/3 of maximum risk


Technical problems and their solutions

Handling IMF data presents a number of technical challenges that are important to understand and process correctly. The interface sometimes returns incomplete data or data with different quality statuses. Some figures are published with revisions, which may affect historical consistency.

The handling of missing values and outliers is particularly important. Economic data from developing countries often contain gaps or extreme values that can distort the analysis. The use of advanced interpolation techniques and robust statistical methods becomes critical.

class DataQualityManager:
    def __init__(self):
        self.outlier_threshold = 3.0  # Z-score threshold for outliers
        self.max_missing_ratio = 0.3   # Maximum proportion of missing values
        
    def clean_economic_series(self, series: pd.Series, country: str, indicator: str) -> pd.Series:
        """
        Comprehensive cleaning of economic time series
        """
        # Log initial data quality
        missing_ratio = series.isnull().sum() / len(series)
        
        if missing_ratio > self.max_missing_ratio:
            logger.warning(f"High missing data ratio for {country}.{indicator}: {missing_ratio:.2%}")
        
        # Handle outliers considering economic context
        cleaned_series = self._handle_outliers(series, country, indicator)
        
        # Intelligent interpolation
        cleaned_series = self._smart_interpolation(cleaned_series, indicator)
        
        # Check for structural breaks
        cleaned_series = self._detect_structural_breaks(cleaned_series, country)
        
        return cleaned_series
    
    def _handle_outliers(self, series: pd.Series, country: str, indicator: str) -> pd.Series:
        """
        Handle outliers considering economic specifics
        """
        if indicator in ['PCPIPCH']:  # Inflation can have legitimate extremes
            threshold = 5.0  # Softer threshold for inflation
        elif country in ['AR', 'TR', 'VE']:  # High volatility countries
            threshold = 4.0
        else:
            threshold = self.outlier_threshold
        
        z_scores = np.abs(stats.zscore(series.dropna()))
        outliers = z_scores > threshold
        
        # Replace outliers with median filter values
        series_cleaned = series.copy()
        series_cleaned[outliers] = series.rolling(5, center=True).median()[outliers]
        
        return series_cleaned
```mark legal extremes
            threshold = 5.0  # A softer threshold for inflation
        elif country in ['AR', 'TR', 'VE']:  # Countries with high volatility
            threshold = 4.0
        else:
            threshold = self.outlier_threshold
        
        z_scores = np.abs(stats.zscore(series.dropna()))
        outliers = z_scores > threshold
        
        # Replace outliers with values calculated through a median filter
        series_cleaned = series.copy()
        series_cleaned[outliers] = series.rolling(5, center=True).median()[outliers]
        
        return series_cleaned


Development prospects and integration with machine learning

Modern machine learning capabilities open new horizons for the analysis of macroeconomic data. The use of deep learning techniques makes it possible to identify complex non-linear relationships between economic indicators of different countries.

Particularly promising is the use of recurrent neural networks for forecasting economic time series and transformer architectures for analyzing the relationships between various economic indicators.

class MacroeconomicLSTM:
    def __init__(self, sequence_length: int = 12, hidden_size: int = 64):
        self.sequence_length = sequence_length
        self.model = self._build_model(hidden_size)
        
    def _build_model(self, hidden_size: int):
        """
        Create LSTM model for macroeconomic indicators forecasting
        """
        model = tf.keras.Sequential([
            tf.keras.layers.LSTM(hidden_size, return_sequences=True),
            tf.keras.layers.Dropout(0.2),
            tf.keras.layers.LSTM(hidden_size // 2),
            tf.keras.layers.Dropout(0.2),
            tf.keras.layers.Dense(32, activation='relu'),
            tf.keras.layers.Dense(1)
        ])
        
        model.compile(
            optimizer='adam',
            loss='mse',
            metrics=['mae']
        )
        
        return model
    
    def train_on_multi_country_data(self, economic_data: Dict[str, pd.DataFrame]):
        """
        Train model on multiple countries data
        """
        X_train, y_train = self._prepare_training_data(economic_data)
        
        # Add regularization and early stopping
        callbacks = [
            tf.keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True),
            tf.keras.callbacks.ReduceLROnPlateau(factor=0.5, patience=5)
        ]
        
        history = self.model.fit(
            X_train, y_train,
            validation_split=0.2,
            epochs=100,
            batch_size=32,
            callbacks=callbacks,
            verbose=1
        )
        
        return history

Integration with modern machine learning practices in production enables the creation of a fully automated system that continuously learns from new data and adapts trading strategies to changing market conditions.


Key indicators for currency analysis

Indicator (code)
Description
Impact on currency
Example of interpretation
NGDP_XDC
Nominal GDP in national currency. Shows the overall economic activity of a country without taking inflation into account.
Positive in case of growth
Australia's 3% GDP growth points to economic expansion → AUD strengthening
TXG_FOB_USD
Export of goods at FOB prices (in USD). Shows the volume of goods shipped abroad.
Positive in case of growth
Canada's 15% export surge reflects strong external demand → CAD appreciation
TMG_CIF_USD
Import of goods at CIF prices (in USD). Shows the volume of imported goods including delivery.
Negative in case of growth
Switzerland's rising imports could lead to currency outflows → CHF weakening
LUR_PT
Unemployment rate (as a percentage of the working-age population). A key indicator of the labor market.
Negative in case of growth
The rise in unemployment in Canada from 5% to 7% indicates problems in the labor market → CAD weakening
PCPI_IX
Consumer Price Index (CPI). Measures the price level relative to a base year.
Negative in case of high growth
Sharp rise in Swiss CPI could signal inflation → possible weakening of CHF if the Central Bank does not raise the rate
ENDA_XDC_USD_RATE
Average exchange rate of the national currency against USD. Shows the relative strength of a currency.
Context-sensitive
The decline in the JPY to USD exchange rate from 110 to 130 indicates a weakening of the JPY → growth of pairs with JPY in the denominator
BCA_NGDPD
Current account balance as a percentage of GDP. Shows the balance of foreign trade and financial flows.
Positive in case of growth
Australia's current account surplus of +2% of GDP indicates a healthy external balance → support for AUD
GGXWDG_NGDP
Government debt as a percentage of GDP. Reflects the debt burden on the country's economy.
Negative in case of growth
Japan's government debt rising above 250% of GDP could be a concern for investors → putting pressure on JPY
GGXONLB_NGDP
Budget deficit/surplus as a percentage of GDP. Shows the state of public finances.
Positive in case of growth
Canada's budget deficit narrows from -3% to -1% of GDP, boosting confidence → CAD support

Some of these indicators have an extremely high correlation with the currency pair (1.00), while others have an extremely negative correlation (-1.00):


Conclusion: From data to profit

International Monetary Fund data opens the way for strategic trading decisions based on macroeconomic signals. Their successful application requires not so much complex algorithms as a deep understanding of economic relationships and the ability to transform information into actionable trading ideas.

It is important to understand that macroeconomic strategies require patience and discipline. This is not high-frequency trading, where profits are measured in milliseconds. Here we work with fundamental imbalances that unfold over quarterly and yearly horizons.

In the next article, we will take a detailed look at creating a forecasting algorithm based on this data.

Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/18451

Attached files |
IMF_Datemine.py (48.11 KB)
Last comments | Go to discussion (1)
Henrique Grilo Verza
Henrique Grilo Verza | 12 Feb 2026 at 17:29
Great content, I believe that if you cross this data with interest rates and 10Y, you'll get a great weekly indicator.
Position Management: Safe Pyramiding with a Unified Stop in MQL5 Position Management: Safe Pyramiding with a Unified Stop in MQL5
This article presents CPyramidEngine, a reusable MQL5 class that adds disciplined pyramiding to any Expert Advisor with about six lines of integration. The engine enforces three constraints: strictly decreasing lot sizes, a single unified stop that advances after each add-on, and broker-level validation of every modification. It explains common failure modes in naive implementations and shows how to keep total account risk quantifiable and controlled as positions are added.
MQL5 Trading Tools (Part 31): Creating an Interactive Tools Palette in MQL5 MQL5 Trading Tools (Part 31): Creating an Interactive Tools Palette in MQL5
We turn the Tools Palette sidebar from a static shell into an interactive MQL5 system. The article implements flyout menus per category, a chart event handler, a multi-click drawing engine (one-, two-, and three-click tools), and mouse interactions including drag, bottom-edge resize, scrolling, hover states, and live theme toggling. You will be able to select a tool and place chart objects directly from the palette for analysis
Beyond GARCH (Part I): Mandelbrot's MMAR versus Engle's GARCH Beyond GARCH (Part I): Mandelbrot's MMAR versus Engle's GARCH
This article starts the MMAR pipeline on EURUSD M5 data. We load market data via the MetaTrader5 Python API and run partition-function analysis with non-overlapping intervals to test for multifractal scaling. The result is an evidence-based decision on fractality, a prerequisite for building MMAR and for choosing whether to proceed beyond GARCH.
Adaptive Malaysian Engulfing Indicator (Part 2): Optimized Retest Bar Range Adaptive Malaysian Engulfing Indicator (Part 2): Optimized Retest Bar Range
The article adds a self-adaptive layer to the Malaysian Engulfing indicator by optimizing the retest bar range with a constrained brute-force search scored by MFE and MAE. It details the data model, helper routines, and an MQL5 implementation that gathers historical setups, computes excursions, and selects the best parameter. Readers learn how to remove manual tuning and run the indicator with context-appropriate settings across symbols and timeframes.