English Русский Español Deutsch 日本語 Português
preview
经济预测:探索 Python 的潜力

经济预测:探索 Python 的潜力

MetaTrader 5积分 |
41 17
Yevgeniy Koshtenko
Yevgeniy Koshtenko

概述

经济预测是一项相当复杂和耗费精力的任务。它允许我们使用过去的数据分析未来可能的走势。通过分析历史数据和当前经济指标,我们可以推测经济可能走向何方。这是一个非常有用的技能。在它的帮助下,我们可以在商业、投资和经济政策方面做出更明智的决定。

我们将使用 Python 和经济数据开发这个工具,从收集信息到创建预测模型。它将对未来进行分析和预测。

金融市场是经济的良好晴雨表。它们对最细微的变化都会做出反应。结果可能是可预测的,也可能是出乎意料的。让我们看看读数导致气压计波动的例子。

当 GDP 增长时,市场通常会做出积极反应。当通货膨胀上升时,通常会出现动荡。当失业率下降时,这通常被视为好消息。然而,也可能有例外。贸易平衡、利率 —— 每个指标都会影响市场情绪。

实践表明,市场往往不会对实际结果做出反应,而是对大多数参与者的期望做出反应。“买谣言,卖事实” —— 这句古老的股市智慧最准确地反映了正在发生的事情的本质。此外,缺乏重大变化可能会导致市场波动比意外消息更大。

经济情况是一个复杂的系统。这里的一切都是相互关联的,一个因素会影响另一个因素。一个参数的变化可能会引发连锁反应。我们的任务是理解这些联系并学会分析它们。我们将使用 Python 工具寻找解决方案。


设置环境:导入必要的库

那么,我们需要什么?首先要说的是 —— Python。如果您尚未安装,请访问 python.org。另外,在安装过程中不要忘记选中 “将 Python 添加到 PATH” 框。

下一步是库,库极大地扩展了我们工具的基本功能。我们需要:

  1. pandas —— 用于处理数据。
  2. wbdata —— 用于与世界银行互动。借助这个库,我们将能获得最新的经济数据。
  3. MetaTrader 5 —— 我们需要它直接与市场本身进行互动。
  4. 来自 catboost 的 CatBoostRegressor —— 一个小型手工制作的 AI。
  5. sklearn 中的 train_test_split 和 mean_squared_error —— 这些库将帮助我们评估模型的有效性。

要安装所需的一切,请打开命令提示符并输入:

pip install pandas wbdata MetaTrader5 catboost scikit-learn

一切都准备好了吗?非常好!现在让我们编写第一段代码文本:

import pandas as pd
import wbdata
import MetaTrader5 as mt5
from catboost import CatBoostRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import warnings

warnings.filterwarnings("ignore", category=UserWarning, module="wbdata")

我们已经准备好所有必要的工具,我们继续吧。


使用世界银行 API:加载经济指标

现在,让我们来弄清楚我们将如何从世界银行获得经济数据。

首先,我们创建一个包含指标代码的字典:

indicators = {
    'NY.GDP.MKTP.KD.ZG': 'GDP growth',  # GDP growth
    'FP.CPI.TOTL.ZG': 'Inflation',      # Inflation
    'FR.INR.RINR': 'Real interest rate', # Real interest rate
    # ... and a bunch of other smart parameters
}

每个代码都提供对特定类型数据的访问。

让我们继续吧,我们启动一个循环来遍历全部代码:

data_frames = []
for indicator in indicators.keys():
    try:
        data_frame = wbdata.get_dataframe({indicator: indicators[indicator]}, country='all')
        data_frames.append(data_frame)
    except Exception as e:
        print(f"Error fetching data for indicator '{indicator}': {e}")

在这里,我们尝试获取每个指标的数据。如果有效,我们就会将其放入列表。如果失败,则打印错误并继续。

之后,我们将所有数据收集到一个大数据帧中:

data = pd.concat(data_frames, axis=1)

在这个阶段,我们需要获取所有的经济数据。

下一步是将我们收到的所有内容保存到一个文件中,以便我们以后可以将其用于我们需要的目的:

data.to_csv('economic_data.csv', index=True)

我们刚刚从世界银行下载了一堆数据,就是这么简单。


主要经济指标分析概述

如果你是一个新手,理解大量的数据和数字可能会有点困难。让我们来看看使这一过程更容易的主要指标:

  1. GDP 增长对于一个国家来说是一种收入。增长的指标是积极的,而下降的指标对国家有负面影响。
  2. 通货膨胀是商品和服务价格的上涨。
  3. 实际利率 —— 如果上升,贷款成本就会更高。
  4. 进出口显示了一个国家的买入和卖出情况。更高的销售额被视为一个积极的发展。
  5. 经常账户余额 —— 其他国家欠某个国家的钱数额。数字越高,表明一个国家的财政状况越好。
  6. 政府债务代表一个国家的贷款。数字越小越好。
  7. 失业 —— 有多少人失业。少即是好。
  8. 人均 GDP 的增长表明普通人是否变得更加富裕。

代码如下所示:

# Loading data from the World Bank
indicators = {
    'NY.GDP.MKTP.KD.ZG': 'GDP growth',  # GDP growth
    'FP.CPI.TOTL.ZG': 'Inflation',       # Inflation
    'FR.INR.RINR': 'Real interest rate', # Real interest rate
    'NE.EXP.GNFS.ZS': 'Exports',         # Exports of goods and services (% of GDP)
    'NE.IMP.GNFS.ZS': 'Imports',         # Imports of goods and services (% of GDP)
    'BN.CAB.XOKA.GD.ZS': 'Current account balance', # Current account balance (% of GDP)
    'GC.DOD.TOTL.GD.ZS': 'Government debt', # Government debt (% of GDP)
    'SL.UEM.TOTL.ZS': 'Unemployment rate', # Unemployment rate (% of total labor force)
    'NY.GNP.PCAP.CD': 'GNI per capita',   # GNI per capita (current US$)
    'NY.GDP.PCAP.KD.ZG': 'GDP per capita growth', # GDP per capita growth (constant 2010 US$)
    'NE.RSB.GNFS.ZS': 'Reserves in months of imports', # Reserves in months of imports
    'NY.GDP.DEFL.KD.ZG': 'GDP deflator', # GDP deflator (constant 2010 US$)
    'NY.GDP.PCAP.KD': 'GDP per capita (constant 2015 US$)', # GDP per capita (constant 2015 US$)
    'NY.GDP.PCAP.PP.CD': 'GDP per capita, PPP (current international $)', # GDP per capita, PPP (current international $)
    'NY.GDP.PCAP.PP.KD': 'GDP per capita, PPP (constant 2017 international $)', # GDP per capita, PPP (constant 2017 international $)
    'NY.GDP.PCAP.CN': 'GDP per capita (current LCU)', # GDP per capita (current LCU)
    'NY.GDP.PCAP.KN': 'GDP per capita (constant LCU)', # GDP per capita (constant LCU)
    'NY.GDP.PCAP.CD': 'GDP per capita (current US$)', # GDP per capita (current US$)
    'NY.GDP.PCAP.KD': 'GDP per capita (constant 2010 US$)', # GDP per capita (constant 2010 US$)
    'NY.GDP.PCAP.KD.ZG': 'GDP per capita growth (annual %)', # GDP per capita growth (annual %)
    'NY.GDP.PCAP.KN.ZG': 'GDP per capita growth (constant LCU)', # GDP per capita growth (constant LCU)
}

每个指标都有其自身的重要性。单独来看,它们意义不大,但综合起来,它们却能提供更完整的画面。还应注意的是,这些指标相互影响。例如,低失业率通常是个好消息,但它可能会导致更高的通货膨胀。或者,如果以巨额债务为代价来实现 GDP 的高增长,可能就不会那么积极。

这就是为什么我们使用机器学习,因为它可以帮助我们考虑所有这些复杂的关系。它大大加快了信息处理和数据排序的过程。然而,你也需要付出一些努力来理解这个过程。


处理和构建世界银行数据

当然,乍一看,世界银行丰富的数据似乎是一项艰巨的任务。为了使工作和分析更容易,我们将在表格中收集数据。

data_frames = []
for indicator in indicators.keys():
    try:
        data_frame = wbdata.get_dataframe({indicator: indicators[indicator]}, country='all')
        data_frames.append(data_frame)
    except Exception as e:
        print(f"Error fetching data for indicator '{indicator}': {e}")

data = pd.concat(data_frames, axis=1)

接下来,我们获取每个指标并尝试获取其数据。个别指标可能存在问题,我们会写下来并继续前进。然后,我们将单个数据收集到一个大的数据帧(DataFrame)中。

但我们并不止步于此。现在,最有趣的部分开始了。

print("Available indicators and their data:")
print(data.columns)
print(data.head())

data.to_csv('economic_data.csv', index=True)

print("Economic Data Statistics:")
print(data.describe())

我们看看我们取得了什么成就。有哪些指标?第一行数据是什么样的?这就像第一次看到一个完整的谜题:一切都准备好了吗?然后我们将所有这些内容保存到 CSV 文件中。 

最后,是一些统计数据。平均值、最高值、最低值。这就像一次快速检查 —— 我们的数据一切正常吗?这就是我们如何将一堆不同的数字转换为一个连贯的数据系统。我们现在拥有进行严肃经济分析的所有工具。 


MetaTrader 5 简介:建立连接并接收数据

现在我们来讨论一下 MetaTrader 5。首先我们需要建立连接。它看起来是这样的:

if not mt5.initialize():
    print("initialize() failed")
    mt5.shutdown()

下一个重要步骤是获取数据。首先,我们需要检查可用的货币对:

symbols = mt5.symbols_get()
symbol_names = [symbol.name for symbol in symbols]

执行上述代码后,我们将获得所有可用货币对的列表。接下来,我们需要下载每个可用货币对的历史报价数据:

historical_data = {}
for symbol in symbol_names:
    rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_D1, 0, 1000)
    df = pd.DataFrame(rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    df.set_index('time', inplace=True)
    historical_data[symbol] = df

我们输入的代码中发生了什么?我们指示 MetaTrader 下载每种交易工具过去 1000天 的数据。在此之后,数据被加载到表中。

下载的数据包含了过去三年货币市场发生的一切,非常详细。现在,可以分析收到的报价并找到模式。这里的可能性几乎是无限的。


数据准备:结合经济指标和市场数据

在这个阶段,我们将直接进行数据处理。我们有两个独立的部分:经济指标世界和汇率世界。我们的任务是将这些部分整合在一起。

让我们从数据准备函数开始。在我们的一般任务中,此代码将如下所示:

def prepare_data(symbol_data, economic_data):
    data = symbol_data.copy()
    data['close_diff'] = data['close'].diff()
    data['close_corr'] = data['close'].rolling(window=30).corr(data['close'].shift(1))

    for indicator in indicators.keys():
        if indicator in economic_data.columns:
            data[indicator] = economic_data[indicator]
        else:
            print(f"Warning: Data for indicator '{indicator}' is not available.")

    data.dropna(inplace=True)
    return data

现在让我们一步一步来。首先,我们创建货币对数据的副本。为什么呢?使用数据副本总是比使用原始数据更好。如果出现错误,我们将不必再次创建原始文件。

现在,最有趣的部分来了。我们添加了两个新列:“close_diff” 和 “close_corr”。第一列显示收盘价与前一天相比变化了多少。通过这种方式,我们将知道价格是正向还是负向变化。第二列是收盘价与自身的相关性,但有一天的偏移。这是为了什么呢?事实上,这只是了解今天的价格与昨天的价格有多相似的最方便的方法。

现在最难的部分来了:我们试图将经济指标添加到我们的货币数据中。这就是我们开始将数据整合到一个结构中的方式。我们浏览了所有的经济指标,并试图在世界银行的数据中找到它们。如果我们找到它,很好,我们会将其添加到我们的货币数据中。如果没有,那也无所谓。我们只是写一个警告,然后继续前进。 

经过这一切,我们可能会留下大量缺失的数据。我们只是删除它们。

现在让我们看看如何应用此函数:

prepared_data = {}
for symbol, df in historical_data.items():
    prepared_data[symbol] = prepare_data(df, data)

我们选取每对货币并将我们编写的函数应用于它。在输出时,我们为每一个货币对获得一个现成的数据集。每对都有单独的一套,但它们都是根据相同的原理组装的。

你知道这个过程中最重要的是什么吗?我们正在创造一些新的东西。我们采用不同的经济数据和实时汇率数据,并从中创造出连贯的东西。单独来看,它们可能看起来很混乱,但当放在一起时,我们可以识别出模式。

现在我们有了一个现成的数据集用于分析。我们可以在其中寻找序列,做出预测并得出结论。然而,我们需要确定真正值得关注的迹象。在数据的世界里,没有不重要的细节。数据准备的每一步都对最终结果至关重要。


我们模型中的机器学习

机器学习是一个相当复杂且耗费精力的过程。CatBoost Regressor —— 该函数稍后将发挥重要作用。以下是我们如何使用它:

from catboost import CatBoostRegressor

model = CatBoostRegressor(iterations=1000, learning_rate=0.1, depth=8, loss_function='RMSE', verbose=100)
model.fit(X_train, y_train, verbose=False)

这里的每个参数都很重要。1000 次迭代是模型遍历数据的次数。学习率(learning_rate) 0.1 —— 不需要立即设置很高的速度,我们应该逐步学习。深度(depth) 8 —— 寻找复杂的联系。RMSE —— 这是我们评估错误的方式。训练一个模型需要一定的时间。我们展示示例并评估正确答案。CatBoost 尤其适用于不同类型的数据。它并不局限于狭窄的功能范围。

为了预测货币,我们采取以下措施:

def forecast(symbol_data):
    X = symbol_data.drop(columns=['close'])
    y = symbol_data['close']

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, shuffle=False)

    model = CatBoostRegressor(iterations=1000, learning_rate=0.1, depth=8, loss_function='RMSE', verbose=100)
    model.fit(X_train, y_train, verbose=False)

    predictions = model.predict(X_test)
    mse = mean_squared_error(y_test, predictions)
    print(f"Mean Squared Error for {symbol}: {mse}")


一部分数据用于训练,另一部分用于测试。这就像上学:首先你学习,然后你参加考试

我们把数据分成两部分。为什么呢?一部分用于训练,另一部分用于测试。毕竟,我们需要在尚未使用的数据上测试模型。

训练后,模型试图进行预测。均方根误差显示了它的工作效果。误差越小,预测越好。CatBoost 以其不断改进而闻名。它从错误中学习。

当然,CatBoost 不是一个自动程序,它需要良好的数据。否则,我们在输入时会得到无效的数据,在输出时也会得到无效的数据。但有了正确的数据,结果就会是积极的。现在我们来谈谈数据划分。我提到过我们需要报价来进行验证。代码是这样的:

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, shuffle=False)

50% 的数据用于测试。不要混合它们 —— 保持金融数据的时间顺序很重要。

创建和训练模型是最有趣的部分。CatBoost 在这里充分展示了其功能:

model = CatBoostRegressor(iterations=1000, learning_rate=0.1, depth=8, loss_function='RMSE', verbose=100)
model.fit(X_train, y_train, verbose=False)

该模型贪婪地吸收数据,寻找模式。每次迭代都是朝着更好地了解市场迈出的一步。

现在是关键时刻。准确度评估:

predictions = model.predict(X_test)
mse = mean_squared_error(y_test, predictions)

均方根误差也是我们工作中的一个重要问题。这表明该模型错误的程度。少即是好。这使我们能够评估该程序的质量。请记住,交易中没有最终的保证。但有了 CatBoost,这个过程就更加高效了。它看到了我们可能会错过的东西。随着每次预测的进行,结果都有所改善。


预测货币对的未来价值

预测货币对是基于概率的。有时我们会得到积极的结果,有时我们会遭受损失。最重要的是,最终结果符合我们的期望。

在我们的代码中,“预测”函数处理概率。它执行计算的方式如下:

def forecast(symbol_data):
    X = symbol_data.drop(columns=['close'])
    y = symbol_data['close']

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

    model = CatBoostRegressor(iterations=1000, learning_rate=0.1, depth=8, loss_function='RMSE', verbose=100)
    model.fit(X_train, y_train, verbose=False)

    predictions = model.predict(X_test)
    mse = mean_squared_error(y_test, predictions)
    print(f"Mean Squared Error for {symbol}: {mse}")

    future_data = symbol_data.tail(30).copy()
    if len(predictions) >= 30:
        future_data['close'] = predictions[-30:]
    else:
        future_data['close'] = predictions

    future_predictions = model.predict(future_data.drop(columns=['close']))

    return future_predictions

首先,我们将现有数据与预测数据分开。然后我们将数据分为两部分:用于训练和测试。模型从一组数据中学习,然后我们在另一组数据上进行测试。训练后,模型进行预测。我们看看使用均方根误差的错误程度。数字越低,预测越好。

但最有趣的是对未来可能价格走势的报价分析。我们使用过去 30 天的数据,让模型预测接下来会发生什么。这看起来像是我们求助于经验丰富的分析师的预测。至于可视化...不幸的是,该代码还没有提供任何显式的结果可视化。但是让我们添加它并看看它是什么样子:

import matplotlib.pyplot as plt

for symbol, forecast in forecasts.items():
    plt.figure(figsize=(12, 6))
    plt.plot(range(len(forecast)), forecast, label='Forecast')
    plt.title(f'Forecast for {symbol}')
    plt.xlabel('Days')
    plt.ylabel('Price')
    plt.legend()
    plt.savefig(f'{symbol}_forecast.png')
    plt.close()

此代码将为每个货币对创建一个图表。从视觉上看,它是线性构建的。每个点都是特定日期的预测价格。这些图表旨在显示可能的趋势,已经使用了大量数据,对普通人来说往往太复杂了。如果这条线朝上,货币将变得更加昂贵。下降呢?那就为汇率下降做好准备吧。

请记住,预测不是保证。市场可能会做出自己的改变。但有了良好的可视化,你至少会知道会发生什么。毕竟,在这种情况下,我们有一个高质量的分析触手可及。

我还编写了一段代码,通过打开一个文件并将预测输出到注释,在 MQL5 中可视化预测结果:

//+------------------------------------------------------------------+
//|                                                 Economic Forecast|
//|                                Copyright 2024, Evgeniy Koshtenko |
//|                          https://www.mql5.com/en/users/koshtenko |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Evgeniy Koshtenko"
#property link      "https://www.mql5.com/en/users/koshtenko"
#property version   "4.00"

#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_label1  "Forecast"
#property indicator_type1   DRAW_SECTION
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

double ForecastBuffer[];
input string FileName = "EURUSD_forecast.csv"; // Forecast file name

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, ForecastBuffer, INDICATOR_DATA);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Draw forecast                                                    |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   static bool first=true;
   string comment = "";

   if(first)
     {
      ArrayInitialize(ForecastBuffer, EMPTY_VALUE);
      ArraySetAsSeries(ForecastBuffer, true);
      int file_handle = FileOpen(FileName, FILE_READ|FILE_CSV|FILE_ANSI);
      if(file_handle != INVALID_HANDLE)
        {
         // Skip the header
         string header = FileReadString(file_handle);
         comment += header + "\n";

         // Read data from file
         while(!FileIsEnding(file_handle))
           {
            string line = FileReadString(file_handle);
            string str_array[];
            StringSplit(line, ',', str_array);
            datetime time=StringToTime(str_array[0]);
            double   price=StringToDouble(str_array[1]);
            PrintFormat("%s  %G", TimeToString(time), price);
            comment += str_array[0] + ",     " + str_array[1] + "\n";

            // Find the corresponding bar on the chart and set the forecast value
            int bar_index = iBarShift(_Symbol, PERIOD_CURRENT, time);
            if(bar_index >= 0 && bar_index < rates_total)
              {
               ForecastBuffer[bar_index] = price;
               PrintFormat("%d  %s   %G", bar_index, TimeToString(time), price);
              }
           }

         FileClose(file_handle);
         first=false;
        }
      else
        {
         comment = "Failed to open file: " + FileName;
        }
      Comment(comment);
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Indicator deinitialization function                              |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Comment("");
  }
//+------------------------------------------------------------------+
//| Create the arrow                                                 |
//+------------------------------------------------------------------+
bool ArrowCreate(const long              chart_ID=0,           // chart ID
                 const string            name="Arrow",         // arrow name
                 const int               sub_window=0,         // subwindow number
                 datetime                time=0,               // anchor point time
                 double                  price=0,              // anchor point price
                 const uchar             arrow_code=252,       // arrow code
                 const ENUM_ARROW_ANCHOR anchor=ANCHOR_BOTTOM, // anchor point position
                 const color             clr=clrRed,           // arrow color
                 const ENUM_LINE_STYLE   style=STYLE_SOLID,    // border line style
                 const int               width=3,              // arrow size
                 const bool              back=false,           // in the background
                 const bool              selection=true,       // allocate for moving
                 const bool              hidden=true,          // hidden in the list of objects
                 const long              z_order=0)            // mouse click priority
  {
//--- set anchor point coordinates if absent
   ChangeArrowEmptyPoint(time, price);
//--- reset the error value
   ResetLastError();
//--- create an arrow
   if(!ObjectCreate(chart_ID, name, OBJ_ARROW, sub_window, time, price))
     {
      Print(__FUNCTION__,
            ": failed to create an arrow! Error code = ", GetLastError());
      return(false);
     }
//--- set the arrow code
   ObjectSetInteger(chart_ID, name, OBJPROP_ARROWCODE, arrow_code);
//--- set anchor type
   ObjectSetInteger(chart_ID, name, OBJPROP_ANCHOR, anchor);
//--- set the arrow color
   ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);
//--- set the border line style
   ObjectSetInteger(chart_ID, name, OBJPROP_STYLE, style);
//--- set the arrow size
   ObjectSetInteger(chart_ID, name, OBJPROP_WIDTH, width);
//--- display in the foreground (false) or background (true)
   ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);
//--- enable (true) or disable (false) the mode of moving the arrow by mouse
//--- when creating a graphical object using ObjectCreate function, the object cannot be
//--- highlighted and moved by default. Selection parameter inside this method
//--- is true by default making it possible to highlight and move the object
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, selection);
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, selection);
//--- hide (true) or display (false) graphical object name in the object list
   ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, hidden);
//--- set the priority for receiving the event of a mouse click on the chart
   ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, z_order);
//--- successful execution
   return(true);
  }

//+------------------------------------------------------------------+
//| Check anchor point values and set default values                 |
//| for empty ones                                                   |
//+------------------------------------------------------------------+
void ChangeArrowEmptyPoint(datetime &time, double &price)
  {
//--- if the point time is not set, it will be on the current bar
   if(!time)
      time=TimeCurrent();
//--- if the point price is not set, it will have Bid value
   if(!price)
      price=SymbolInfoDouble(Symbol(), SYMBOL_BID);
  }
//+------------------------------------------------------------------+

终端中的预测结果如下:



结果的解释:经济因素对汇率的影响分析

现在,让我们仔细看看如何根据代码解释结果。我们已经将数千个不同的事实收集到有组织的数据中,这些数据也需要进行分析。

首先,我们拥有一系列经济指标 —— 从 GDP 增长到失业率。每个因素对市场背景都有自己的影响。个别指标有其自身的影响,但这些数据共同影响最终汇率。

以 GDP 为例。在代码中,它由几个指标表示:

'NY.GDP.MKTP.KD.ZG': 'GDP growth',
'NY.GDP.PCAP.KD.ZG': 'GDP per capita growth',

GDP 增长通常会使货币走强。为什么呢?因为积极的消息吸引了寻求投资机会以进一步增长的投资者。投资者被不断增长的经济体所吸引,对其货币的需求也在增加。

相反,通货膨胀('FP.CPI.TOTL.ZG':'Inflation' )对交易员来说是一个令人担忧的信号。通货膨胀率越高,货币价值下降得越快。高通胀通常会削弱货币,仅仅是因为服务和商品在相关国家开始变得更加昂贵。

看看贸易平衡很有趣:

'NE.EXP.GNFS.ZS': 'Exports',
'NE.IMP.GNFS.ZS': 'Imports',
'BN.CAB.XOKA.GD.ZS': 'Current account balance',

这些指标就像天平。如果出口超过进口,该国将获得更多的外汇,这通常会使本国货币走强。

现在让我们看看如何在代码中分析它。CatBoost Regressor 是我们的主要工具。就像一位经验丰富的指挥家,它能同时听到所有乐器的声音,并理解它们是如何相互影响的。

以下是您可以添加到预测函数中的内容,以更好地了解因素的影响:

def forecast(symbol_data):
    # ......

    feature_importance = model.feature_importances_
    feature_names = X.columns
    importance_df = pd.DataFrame({'feature': feature_names, 'importance': feature_importance})
    importance_df = importance_df.sort_values('importance', ascending=False)
    print(f"Feature Importance for {symbol}:")
    print(importance_df.head(10))  # Top 10 important factors

    return future_predictions

这将向我们展示哪些因素对每种货币对的预测最重要。事实可能会证明,对欧元而言,关键因素是欧洲央行的利率,而对日元而言,则是日本的贸易平衡。数据输出示例:

EURUSD 的解释:

    1.价格趋势:预测显示未来 30 天呈上升趋势。

    2.波动性:预测的价格走势显示波动性较低。

    3.关键影响因素:此次预测最重要的特征是“低”。

    4.经济影响:

       - 如果 GDP 增长是首要因素,则表明强劲的经济表现正在影响货币。

       - 通货膨胀率的高度重要性可能表明货币政策的变化正在影响货币。

       - 如果贸易平衡因素至关重要,国际贸易动态可能会推动货币走势。

    5.交易影响:

       - 上升趋势表明有可能建立多头头寸。

       - 较低的波动性可能允许更大范围的止损。

    6.风险评估:

       - 始终考虑模型的局限性和意外市场事件的可能性。

       - 过去的表现并不能保证未来的结果。

但请记住,经济学中没有简单的答案。有时一种货币会不顾一切地走强,有时则会无缘无故地下跌。市场往往依赖于预期,而不是当前的现实。

另一个关键点是时间延迟。经济的变化不会立即反映在汇率上。这就像驾驶一艘大船 —— 你转动方向盘,但船不会立刻改变航向。在代码中,我们使用每日数据,但一些经济指标的更新频率较低。这可能会给预测带来一些误差。最终,解释结果既是一门科学,也是一门艺术。模型是一个强大的工具,但决策总是由人做出的。明智地使用这些数据,希望你的预测准确!


在经济数据中寻找不明显的模式

外汇市场是一个巨大的交易平台。它的特点不是可预测的价格变动,但除此之外,还有一些特殊事件会增加当前的波动性和流动性。这些都是全球性事件。

在我们的代码中,我们依赖于经济指标:

indicators = {
    'NY.GDP.MKTP.KD.ZG': 'GDP growth',
    'FP.CPI.TOTL.ZG': 'Inflation',
    # ...
}

但是,当意外发生时该怎么办呢?例如,疫情或是政治危机?

某种 “惊异指数” 在这里会很有用。想象一下,我们在代码中添加这样的内容:

def add_global_event_impact(data, event_date, event_magnitude):
    data['global_event'] = 0
    data.loc[event_date:, 'global_event'] = event_magnitude
    data['global_event_decay'] = data['global_event'].ewm(halflife=30).mean()
    return data

# ---
def prepare_data(symbol_data, economic_data):
    # ... ...
    data = add_global_event_impact(data, '2020-03-11', 0.5)  # 
    return data

这将使我们能够考虑到突发的全球事件及其逐渐减弱。

但这里最有趣的问题是这如何影响预测。有时,全球事件可能会完全颠覆我们的预期。例如,在危机期间,美元或瑞士法郎等“安全”货币可能会违背经济逻辑而走强。

在这种情况下,我们模型的生产率会下降。在这里,重要的是不要恐慌,而是要适应。也许值得暂时缩短预测范围或为最近的数据增加更多权重?

recent_weight = 2 if data['global_event'].iloc[-1] > 0 else 1
model.fit(X_train, y_train, sample_weight=np.linspace(1, recent_weight, len(X_train)))

记住:在货币的世界里,就像在跳舞一样,最重要的是能够适应节奏。即使这种节奏有时会以最意想不到的方式变化!


寻找异常:如何在经济数据中发现不明显的模式

现在让我们来谈谈最有趣的部分 —— 在我们的数据中寻找隐藏的宝藏。这就像当侦探,只是我们没有证据,只有数字和图表。

我们的代码中已经使用了相当多的经济指标。但是,如果它们之间存在一些不明显的联系呢?让我们尝试找到它们吧!

首先,我们可以看看不同指标之间的相关性:

correlation_matrix = data[list(indicators.keys())].corr()
print(correlation_matrix)

然而,这仅仅是开始。当我们开始寻找非线性关系时,真正的魔力就开始了。例如,GDP 的变化可能不会立即影响汇率,而是需要几个月的时间。

让我们在数据准备函数中添加一些“平移”指标:

def prepare_data(symbol_data, economic_data):
    # ......
    for indicator in indicators.keys():
        if indicator in economic_data.columns:
            data[indicator] = economic_data[indicator]
            data[f"{indicator}_lag_3"] = economic_data[indicator].shift(3)
            data[f"{indicator}_lag_6"] = economic_data[indicator].shift(6)
    # ...

现在我们的模型将能够捕获延迟 3 个月和 6 个月的依赖关系。

但最有趣的是寻找完全不明显的模式。例如,欧元汇率可能与美国冰淇淋销量存在奇怪的相关性(这是一个笑话,但你明白我的意思)。

为了达到这样的目的,可以使用特征提取方法,例如 PCA(主成分分析):

from sklearn.decomposition import PCA

def find_hidden_patterns(data):
    pca = PCA(n_components=5)
    pca_result = pca.fit_transform(data[list(indicators.keys())])
    print("Explained variance ratio:", pca.explained_variance_ratio_)
    return pca_result

pca_features = find_hidden_patterns(data)
data['hidden_pattern_1'] = pca_features[:, 0]
data['hidden_pattern_2'] = pca_features[:, 1]

这些“隐藏模式”可能是更准确预测的关键。

也不要忘记季节性。一些货币的表现可能因一年中的不同时间而异。将月份和星期几的信息添加到您的数据中 —— 您可能会发现一些有趣的东西!

data['month'] = data.index.month
data['day_of_week'] = data.index.dayofweek

记住,在数据的世界里,总是有发现的空间。保持好奇心,进行实验,谁知道呢,也许你会发现这种模式会改变交易世界。


结论:算法交易中的经济预测前景

我们从一个简单的想法开始 —— 我们能否根据经济数据预测汇率走势?我们发现了什么?事实证明,这个想法有一些优点。但这并不像乍一看那么简单。

我们的代码大大简化了经济数据的分析。我们学会了从世界各地收集信息,处理信息,甚至让计算机做出预测。但请记住,即使是最先进的机器学习模型也只是一种工具。它是一个非常强大的工具,但仍然只是一个工具。

我们了解了 CatBoost Regressor 如何发现经济指标和汇率之间的复杂关系。这使我们能够超越人类的能力,大大减少处理和分析数据所花费的时间。但即使是这样一个伟大的工具也无法 100% 准确地预测未来。

为什么呢?因为经济是一个取决于多种因素的过程。今天每个人都在关注油价,而明天整个世界可能会被一些意想不到的事件颠覆。我们在讲“惊异指数”的时候提到过这种效应。这正是它如此重要的原因。 

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/15998

附加的文件 |
economic_data.csv (3388.32 KB)
最近评论 | 前往讨论 (17)
Yevgeniy Koshtenko
Yevgeniy Koshtenko | 16 10月 2024 在 17:33
Aleksey Nikolayev #:

从这里开始。关于这个问题的一篇旧文章

到目前为止,我能从苍蝇身上得到的最多的信息就是今天的数据((((

Yevgeniy Koshtenko
Yevgeniy Koshtenko | 16 10月 2024 在 17:47
lynxntech #:

我不明白的是,MQ 是做什么的?


这就是上文作者发出的信号。

这个信号纯粹是为了测试 Sber 上的一个模型。但我从未测试过它,它只是货币市场基金中的货币而已。基本上,我自己不对我的模型进行交易,我无法摆脱改进和发展的想法)))),不断有新的改进想法)而在证券交易所,我主要投资股票,长期而言,我在MOEX上购买股票作为非REZ,在KASE上购买Kazbirji指数公司的股票。

Aleksey Nikolayev
Aleksey Nikolayev | 16 10月 2024 在 17:54
Yevgeniy Koshtenko #:

到目前为止,我们能从苍蝇身上得到的最好信息就是今天的数据((((

据我所知,他们收集的是与监控相连的账户数据?即使一切都是诚实的,那也只是沧海一粟。

在我看来,更值得信赖的是 CFTC 的数据,即使它不是现货,而是带有期权的期货。那里有自 2005 年以来的历史数据,虽然形式不是很方便,但可能有一些 Python 应用程序接口。

当然,这取决于您,我只是谈谈我的看法。

lynxntech
lynxntech | 16 10月 2024 在 18:00
Yevgeniy Koshtenko #:

这个信号纯粹是为了测试 Sber 上的一个模型。但我从未对其进行过测试,它只是货币市场基金中的货币而已。基本上,我自己不对我的模型进行交易,因为我无法摆脱改进和发展的想法)))),不断有新的改进想法)而在证券交易所,我主要投资股票,长期而言,我在 MOEX 上购买非雷茨股票,在 KASE 上购买 Kazbirji 指数公司的股票。

信息有出入,不对您构成任何索赔

Mihail Aleksandrovich
Mihail Aleksandrovich | 9 2月 2025 在 06:48
如何在今天通过 pip 安装catboost
IDLE Python 3.13.1,cmd 崩溃,无法安装。
创建 MQL5-Telegram 集成 EA 交易(第 7 部分):图表指标自动化的命令分析 创建 MQL5-Telegram 集成 EA 交易(第 7 部分):图表指标自动化的命令分析
在本文中,我们将探讨如何将 Telegram 命令与 MQL5 集成,以自动在交易图表上添加指标。我们涵盖了解析用户命令、在MQL5中执行命令以及测试系统以确保基于指标的交易顺利进行的过程
您应当知道的 MQL5 向导技术(第 45 部分):蒙特卡洛强化学习 您应当知道的 MQL5 向导技术(第 45 部分):蒙特卡洛强化学习
蒙特卡洛是我们正在研究的第四种不同的强化学习算法,目的是探索它在向导汇编智能交易系统中的实现。尽管它锚定在随机抽样,但它提供了我们可以利用的多种模拟方法。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
周期与交易 周期与交易
本文将探讨如何在交易中运用周期理论。我们将考虑基于周期模型构建交易策略。