基于Python的CFTC数据挖掘与AI预测模型构建
外汇市场是全球规模最大的金融市场,但极高的波动率让行情预测充满挑战。COT/TFF报告能够揭示“聪明钱”的动向,帮助发现隐藏的市场趋势。
本文介绍的方法,会将COT/TFF持仓数据与市场行情整合到统一的Python模型中,并通过MetaTrader 5实现自动化交易。这样我们能够从分析直接转化为交易执行,无需延迟,也无需人工干预。
理论基础
什么是COT和TFF报告?
想象一下,您可以直接查看外汇市场顶级玩家的投资组合 —— 管理着数十亿资产的对冲基金、养老基金和投资银行。这正是美国商品期货交易委员会(CFTC)每周五发布的COT和TFF报告所能实现的功能。

这类报告诞生于20世纪70至80年代的市场危机之后,监管机构当时意识到,市场参与者需要了解大型机构的操作动向。如今,所有持仓规模超过特定阈值的交易者,都必须披露其持仓情况。美国商品期货交易委员会(CFTC)汇总这些信息并以整体形式发布,数据统计截止至每周二收盘。
市场参与者的分类及其交易动机
商业交易者 —— 实体产业机构,它们参与期货交易并非为了投机,而是用于对冲风险。航空公司买入原油期货,锁定燃油成本。出口商卖出外汇期货,对冲汇率下跌风险。农户提前卖出小麦期货,以提前锁定收成收益。
非商业交易者 —— 以价格波动获利为目的的投机者,主要包括对冲基金、投资银行、大型资产管理公司。这类资金常被称为“聪明钱”,因为它们拥有充足的资源进行深度市场研究与分析。
小型交易者 —— 持仓量未达到披露门槛的其他市场参与者,包括零售交易者、小型基金、个人投机者等。
COT报告揭示了什么?
COT报告呈现所有期货市场的整体持仓结构。针对每类参与者,分别展示多头持仓(看涨)、空头持仓(看跌)以及净持仓(多空差值)。未平仓量则反映市场中活跃合约的总规模。
例如,如果非商业交易者欧元净多头为+120,000 手,意味着投机者整体看涨欧元兑美元(EURUSD)。如果商业交易者净空头为-115,000手,则说明对冲机构要么预期欧元走弱,要么只是在对冲汇率风险。
TFF报告补充了哪些信息?
TFF报告仅聚焦金融期货,能更详细地描绘机构的持仓结构。在此,非商业交易者被进一步细分为:杠杆基金(激进型对冲基金,大量使用杠杆资金)和资产管理机构(偏稳健的养老基金、保险公司)。同时还包含交易商与中介机构 —— 即提供市场流动性的银行。
这种细分至关重要。如果杠杆基金大幅增持美元多头,说明短期投机性资金兴趣上升;如果是资产管理机构加仓,则意味着机构投资者的长期观点发生转向。
交易者为何需要这类数据?
这类报告的核心价值在于:在行情反映之前,提前看清“聪明钱”的流向。大型机构往往基于普通投资者无法获取的信息进行操作。
识别极端情绪:当投机资金持仓达到历史高位时,市场通常处于超买状态,随时可能回调; 反之,则可能出现反弹。其逻辑是逆向交易 —— 在极端情绪下与大众反向操作。
趋势确认:如果价格上涨,同时非商业交易者持续加仓多头,说明趋势强劲;如果价格上涨但他们却开始减仓,则趋势正在减弱。
提前捕捉反转:价格与持仓出现背离,往往是反转的前兆。价格可能还在上涨,但主力已在削减多头仓位,反转近在眼前。
实例说明
假设某份欧元兑美元报告显示:杠杆基金多头+85,000手,资产管理机构+25,000手,交易商-95,000手。这意味着激进投机者与保守机构均看涨欧元。交易商作为对手方,可能只是在执行客户订单或对冲自身风险。整体信号为欧元看涨。
重要细节
数据发布存在三天延迟,在快速变动的市场中影响显著。并非所有商业持仓都是纯粹对冲 —— 部分银行可能以商业名义进行投机。算法交易与机器学习正在改变市场参与者的传统行为模式。
现代算法甚至会直接根据COT/TFF报告做出决策,形成自我实现的预言,增加了数据解读的难度。
为何这套逻辑有效?
COT和TFF报告的价值在于展示真金白银的实际持仓,而非仅仅是观点或预测。当对冲基金以数十亿美元押注某个方向时,其信号意义远强于任何公开的市场评论。这些数据反映了最具信息与资金优势的市场参与者的集体判断。
将其与机器学习、自动化交易结合后,机构持仓分析便成为强力工具,可基于真正驱动市场的资金行为来构建策略。
基于COT/TFF数据的价格预测
价格预测基于一个核心假设 —— 主力持仓与未来价格走势之间存在相关性。例如,非商业交易者多头增加,往往预示看涨;空头增加则看跌。这些数据会作为机器学习模型的输入特征,并辅以来自MetaTrader 5的历史价格,以及波动率、移动平均线等技术指标。该方法能够捕捉市场情绪与价格趋势之间复杂的非线性关系。
数据准备
COT和TFF报告以Excel格式在CFTC官网发布。我们使用requests库下载数据,通过pandas进行处理。以下代码演示了带缓存机制的COT数据加载与预处理流程,可有效提升运行效率:
import requests import zipfile import pandas as pd import os import logging import MetaTrader5 as mt5 from datetime import datetime, timedelta import matplotlib.pyplot as plt import seaborn as sns from sklearn.ensemble import RandomForestRegressor from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split import importlib.util import glob import asyncio logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) plt.style.use('ggplot') plt.rcParams['figure.figsize'] = [10, 6] COT_URL = "https://www.cftc.gov/files/dea/history/dea_fut_xls_2024.zip" TFF_URL = "https://www.cftc.gov/files/dea/history/fut_fin_xls_2024.zip" OUTPUT_DIR = "data" os.makedirs(OUTPUT_DIR, exist_ok=True) def load_cot_reports() -> pd.DataFrame: cache_path = os.path.join(OUTPUT_DIR, "cot_report.csv") if os.path.exists(cache_path): logger.info(f"Loading COT data from cache: {cache_path}") return pd.read_csv(cache_path) try: response = requests.get(COT_URL) response.raise_for_status() zip_path = os.path.join(OUTPUT_DIR, "cot_data.zip") with open(zip_path, "wb") as f: f.write(response.content) with zipfile.ZipFile(zip_path, 'r') as zip_ref: zip_ref.extractall(OUTPUT_DIR) excel_file = [f for f in zip_ref.namelist() if f.endswith('.xls') or f.endswith('.xlsx')][0] cot_data = pd.read_excel(os.path.join(OUTPUT_DIR, excel_file)) relevant_columns = [ "Market_and_Exchange_Names", "NonComm_Positions_Long_All", "NonComm_Positions_Short_All", "Comm_Positions_Long_All", "Comm_Positions_Short_All", "Open_Interest_All" ] cot_data = cot_data[relevant_columns] cot_data["Net_NonComm"] = cot_data["NonComm_Positions_Long_All"] - cot_data["NonComm_Positions_Short_All"] cot_data["Net_Comm"] = cot_data["Comm_Positions_Long_All"] - cot_data["Comm_Positions_Short_All"] cot_data.to_csv(cache_path, index=False) logger.info(f"COT report saved in {cache_path}") return cot_data except Exception as e: logger.error(f"Error loading COT data: {e}") return pd.DataFrame()
TFF报告采用相同的处理流程:筛选货币期货数据,并分别计算杠杆基金和资产管理机构的净持仓。数据缓存能够最大限度减少网络请求,提升数据重复处理的效率。
集成MetaTrader 5
通过MetaTrader 5库下载历史价格数据,该库可直接对接市场行情数据源。以下代码用于获取最近30天的小时级K线价格:
def get_historical_prices(pair: str, days_history: int = 30) -> pd.DataFrame: if not mt5.initialize(): logger.error("MT5 initialization failed") return pd.DataFrame() timeframe = mt5.TIMEFRAME_H1 utc_from = datetime.now() - timedelta(days=days_history) rates = mt5.copy_rates_from(pair, timeframe, utc_from, 24 * days_history) if rates is None or len(rates) == 0: logger.warning(f"No historical data for {pair}") return pd.DataFrame() df = pd.DataFrame(rates) df['time'] = pd.to_datetime(df['time'], unit='s') df.set_index('time', inplace=True) df['price_change_24h'] = df['close'].shift(-24) / df['close'] - 1 df.dropna(inplace=True) return df[['open', 'high', 'low', 'close', 'tick_volume', 'price_change_24h']]
数据处理与合并
整合COT持仓数据、TFF持仓数据与历史价格数据,并构建衍生特征:波动率(最高价与最低价的差值,按收盘价归一化)、成交量与价格的24小时移动平均线,以及COT和TFF持仓数据的滞后项及其环比变化(百分比)。
特征构建代码:
def prepare_features(pair: str, cot_data: pd.DataFrame, tff_data: pd.DataFrame) -> pd.DataFrame: df_prices = get_historical_prices(pair) if df_prices.empty: logger.warning(f"No price data available for {pair}") df = pd.DataFrame(index=[datetime.now()], columns=['close', 'price_change_24h']) df['close'] = 1.0 df['price_change_24h'] = 0.0 else: df = df_prices.copy() df['volatility'] = (df['high'] - df['low']) / df['close'] df['volume_sma_24'] = df['tick_volume'].rolling(window=24).mean() df['price_sma_24'] = df['close'].rolling(window=24).mean() df['price_change_1h'] = df['close'].pct_change() market = map_pair_to_cot_tff(pair) if market and not cot_data.empty: cot_subset = cot_data[cot_data["Market_and_Exchange_Names"].str.contains(market, case=False, na=False)] if not cot_subset.empty: cot_features = cot_subset[['Net_NonComm', 'Net_Comm']].mean().to_frame().T for col in cot_features.columns: df[col] = cot_features[col].iloc[0] if market and not tff_data.empty: tff_subset = tff_data[tff_data["Market_and_Exchange_Names"].str.contains(market, case=False, na=False)] if not tff_subset.empty: tff_features = tff_subset[['Net_Lev_Money', 'Net_Asset_Mgr']].mean().to_frame().T for col in tff_features.columns: df[col] = tff_features[col].iloc[0] for col in ['Net_NonComm', 'Net_Comm', 'Net_Lev_Money', 'Net_Asset_Mgr']: if col in df.columns: df[f'{col}_lag1'] = df[col].shift(1) df[f'{col}_change'] = df[col].pct_change().fillna(0) df.dropna(inplace=True) return df def map_pair_to_cot_tff(pair: str) -> str: mapping = { 'EURUSD': 'EURO FX', 'GBPUSD': 'BRITISH POUND', 'USDJPY': 'JAPANESE YEN', 'AUDUSD': 'AUSTRALIAN DOLLAR', 'USDCAD': 'CANADIAN DOLLAR', 'USDCHF': 'SWISS FRANC', 'NZDUSD': 'NEW ZEALAND DOLLAR' } base_pair = pair.replace('.ecn', '')[:6] return mapping.get(base_pair, '')
为提升预测精度,可对决策树数量、最大深度等超参数进行优化,并通过交叉验证评估模型的稳健性。特征重要性将保存为CSV文件,用于分析各特征对预测结果的影响程度。
预测与可视化
价格预测
预测基于最新数据完成,模型输出未来24小时价格的涨跌幅(百分比变化),再结合当前市价计算预测价位。预测代码如下:
async def get_price_forecast(pair: str, model, scaler) -> dict: df = prepare_features(pair, cot_data, tff_data) if df.empty: logger.warning(f"No data to forecast for {pair}") return {'pair': pair, 'forecast_price': None, 'confidence': 0.0} X_latest = df.drop(columns=['price_change_24h']).iloc[-1:] X_scaled = scaler.transform(X_latest) price_change_pred = model.predict(X_scaled)[0] confidence = model.score(X_scaled, df['price_change_24h'].iloc[-1:]) if len(df) > 1 else 0.6 tick = mt5.symbol_info_tick(pair) if not tick: logger.warning(f"No current data for {pair}") return {'pair': pair, 'forecast_price': None, 'confidence': 0.0} current_price = (tick.bid + tick.ask) / 2 forecast_price = current_price * (1 + price_change_pred) forecast_df = pd.DataFrame([{ 'forecast_price': forecast_price, 'confidence': confidence, 'current_price': current_price, 'price_change_pred': price_change_pred, 'timestamp': datetime.now() }]) output_path = os.path.join(OUTPUT_DIR, f"forecast_{pair}.csv") forecast_df.to_csv(output_path, index=False) logger.info(f"Forecast for {pair} saved in {output_path}") visualize_forecast(pair, current_price, forecast_price, confidence) return { 'pair': pair, 'forecast_price': forecast_price, 'confidence': max(0.0, min(1.0, confidence)) }
可视化结果
为了便于分析,系统会绘制当前价格与预测价格走势图,以及 COT、TFF 净持仓变化图。可视化代码如下:
def visualize_cot_data(cot_data: pd.DataFrame): if cot_data.empty or "Net_NonComm" not in cot_data.columns: logger.warning("No COT data available for visualization") return plt.figure(figsize=(14, 8)) for market in cot_data["Market_and_Exchange_Names"].unique(): market_data = cot_data[cot_data["Market_and_Exchange_Names"] == market] plt.plot(range(len(market_data)), market_data["Net_NonComm"], label=market, alpha=0.7) plt.title("Net Non-Commercial positions for currency futures") plt.xlabel("Entry") plt.ylabel("Net positions") plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() output_path = os.path.join(OUTPUT_DIR, "cot_net_positions.png") plt.savefig(output_path, dpi=150) plt.close() logger.info(f"Chart of net COT positions saved in {output_path}") def visualize_tff_data(tff_data: pd.DataFrame): if tff_data.empty or "Net_Lev_Money" not in tff_data.columns: logger.warning("No TFF data available for visualization") return plt.figure(figsize=(14, 8)) for market in tff_data["Market_and_Exchange_Names"].unique(): market_data = tff_data[tff_data["Market_and_Exchange_Names"] == market] plt.plot(range(len(market_data)), market_data["Net_Lev_Money"], label=market, alpha=0.7) plt.title("Leveraged Funds net positions for currency futures (TFF)") plt.xlabel("Entry") plt.ylabel("Net positions") plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() output_path = os.path.join(OUTPUT_DIR, "tff_net_positions.png") plt.savefig(output_path, dpi=150) plt.close() logger.info(f"Chart of net TFF positions saved in {output_path}") def visualize_forecast(pair: str, current_price: float, forecast_price: float, confidence: float): plt.figure(figsize=(8, 5)) plt.bar(['Current Price', 'Forecast Price'], [current_price, forecast_price], color=['blue', 'green'], alpha=0.7) plt.title(f"Price forecast in 24 hours for {pair} (Confidence: {confidence:.2f})") plt.ylabel('Price') plt.tight_layout() output_path = os.path.join(OUTPUT_DIR, f"forecast_{pair}_plot.png") plt.savefig(output_path, dpi=150) plt.close() logger.info(f"Forecast graph saved to {output_path}")
图表能直观反映市场情绪与预测准确度,帮助交易者清晰理解数据背后的逻辑。
优化与异常处理
数据缓存
为减少网络请求,COT和TFF数据会以CSV文件形式缓存,便于复用最新数据并提升处理速度。程序在加载新数据前会先检查缓存,仅当缓存不存在或已过期时,才发起新的网络请求。
依赖检查
程序运行前会自动检查xlrd和openpyxl库是否已经安装好:
def check_dependencies(): dependencies = ['xlrd', 'openpyxl'] for dep in dependencies: if not importlib.util.find_spec(dep): logger.error(f"Missing dependency: {dep}. Install it using 'pip install {dep}'.") raise ImportError(f"Missing dependency: {dep}")
错误处理
代码针对所有关键操作均加入了异常处理:加载数据、连接MetaTrader 5以及训练模型。这样可以确保系统在遇到数据缺失、连接异常等问题时仍能稳定运行。
实际应用
模型测试
建议使用2024年历史数据对模型进行回测评估。R²决定系数、特征重要性等指标将被保存为CSV文件用于分析。通过测试可以评估预测准确率,并识别出影响最大的特征,例如非商业交易者净持仓、市场波动率等。
集成到交易策略
预测结果可通过MetaTrader 5库应用于自动化交易。例如,如果模型预测未来价格变动超过0.5%,且置信度较高(大于0.7),系统则可自动开仓做多或做空。自动化交易代码如下:
def execute_trade(pair: str, forecast_price: float, confidence: float): if not mt5.initialize(): logger.error("MT5 initialization failed") return symbol_info = mt5.symbol_info(pair) if not symbol_info: logger.warning(f"{pair} symbol not found") return current_price = (mt5.symbol_info_tick(pair).bid + mt5.symbol_info_tick(pair).ask) / 2 if confidence > 0.7 and forecast_price > current_price * 1.005: request = { "action": mt5.TRADE_ACTION_DEAL, "symbol": pair, "volume": 0.1, "type": mt5.ORDER_TYPE_BUY, "price": mt5.symbol_info_tick(pair).ask, "type_time": mt5.ORDER_TIME_GTC, "type_filling": mt5.ORDER_FILLING_IOC, } result = mt5.order_send(request) logger.info(f"Opened long position for {pair}: {result}") elif confidence > 0.7 and forecast_price < current_price * 0.995: request = { "action": mt5.TRADE_ACTION_DEAL, "symbol": pair, "volume": 0.1, "type": mt5.ORDER_TYPE_SELL, "price": mt5.symbol_info_tick(pair).bid, "type_time": mt5.ORDER_TIME_GTC, "type_filling": mt5.ORDER_FILLING_IOC, } result = mt5.order_send(request) logger.info(f"Opened short position for {pair}: {result}")
功能扩展
为优化模型效果,可添加如RSI、MACD等技术指标,纳入更多市场信号。使用梯度提升(XGBoost)或神经网络等替代算法,能进一步提升预测精度。可通过任务调度库(如schedule)实现COT、TFF数据的动态更新,在报告发布后自动抓取最新数据。还可扩展至商品、指数等其他品种的分析,只需对特征部分做相应适配即可。
完整实现:CurrencyForecastModule
为方便集成,将所有功能封装进CurrencyForecastModule类,包含数据加载、模型训练、预测及可视化模块:
class CurrencyForecastModule: def __init__(self, pairs: list, days_history: int = 30): self.pairs = pairs self.days_history = days_history self.models = {} self.scalers = {} self.forecasts = {} check_dependencies() if not mt5.initialize(): logger.error("MT5 initialization failed. Ensure MT5 terminal is running and connected.") raise RuntimeError("MT5 initialization failed") self._validate_symbols() self._initialize_data() def _validate_symbols(self): available_symbols = [s.name for s in mt5.symbols_get()] logger.info(f"Available symbols in MT5: {available_symbols}") self.symbol_mapping = {} for pair in self.pairs[:]: if pair in available_symbols: self.symbol_mapping[pair] = pair else: base_pair = pair.split('.')[0] if base_pair in available_symbols: self.symbol_mapping[pair] = base_pair logger.info(f"Matched: {pair} -> {base_pair}") else: logger.warning(f"{pair} symbol not found in MT5. Skipped.") self.pairs.remove(pair) def _initialize_data(self): logger.info("Initializing data for CurrencyForecastModule...") self.cot_data = load_cot_reports() self.tff_data = self._load_tff_reports() for pair in self.pairs: self._train_model(pair) def _load_tff_reports(self) -> pd.DataFrame: cache_path = os.path.join(OUTPUT_DIR, "tff_report.csv") if os.path.exists(cache_path): logger.info(f"Loading TFF data from cache: {cache_path}") return pd.read_csv(cache_path) try: response = requests.get(TFF_URL) response.raise_for_status() zip_path = os.path.join(OUTPUT_DIR, "tff_data.zip") with open(zip_path, "wb") as f: f.write(response.content) with zipfile.ZipFile(zip_path, 'r') as zip_ref: zip_ref.extractall(OUTPUT_DIR) logger.info(f"TFF archive contents: {zip_ref.namelist()}") excel_files = glob.glob(os.path.join(OUTPUT_DIR, "**", "*.xls*"), recursive=True) tff_files = [f for f in excel_files if 'FinFut' in f or 'fin' in f.lower()] if not tff_files: logger.error("TFF Excel file not found in extracted files") return pd.DataFrame() excel_file = tff_files[0] logger.info(f"Handling TFF file: {excel_file}") relevant_columns = [ "Market_and_Exchange_Names", "Lev_Money_Positions_Long_All", "Lev_Money_Positions_Short_All", "Asset_Mgr_Positions_Long_All", "Asset_Mgr_Positions_Short_All", "Open_Interest_All" ] tff_data = pd.read_excel(excel_file, engine='xlrd' if excel_file.endswith('.xls') else 'openpyxl') available_columns = [col for col in relevant_columns if col in tff_data.columns] if not available_columns: logger.error("Expected columns in TFF data not found") return pd.DataFrame() tff_data = tff_data[available_columns] forex_markets = ["EURO FX", "JAPANESE YEN", "BRITISH POUND", "AUSTRALIAN DOLLAR", "CANADIAN DOLLAR", "SWISS FRANC", "MEXICAN PESO", "NEW ZEALAND DOLLAR"] tff_data = tff_data[tff_data["Market_and_Exchange_Names"].str.contains('|'.join(forex_markets), case=False, na=False)] if "Lev_Money_Positions_Long_All" in tff_data.columns: tff_data["Net_Lev_Money"] = tff_data["Lev_Money_Positions_Long_All"] - tff_data["Lev_Money_Positions_Short_All"] if "Asset_Mgr_Positions_Long_All" in tff_data.columns: tff_data["Net_Asset_Mgr"] = tff_data["Asset_Mgr_Positions_Long_All"] - tff_data["Asset_Mgr_Positions_Short_All"] tff_data.to_csv(cache_path, index=False) logger.info(f"TFF report saved in {cache_path}") visualize_tff_data(tff_data) return tff_data except Exception as e: logger.error(f"Error loading TFF data: {e}") return pd.DataFrame() def _train_model(self, pair: str): df = prepare_features(pair, self.cot_data, self.tff_data) model, scaler = train_model(pair, df) if model and scaler: self.models[pair] = model self.scalers[pair] = scaler async def update_forecasts(self): logger.info("Updating price forecasts...") for pair in self.pairs: forecast = await get_price_forecast(pair, self.models.get(pair), self.scalers.get(pair)) logger.info(f"Forecast for {pair}: Price={forecast['forecast_price']}, Confidence={forecast['confidence']:.2f}") def __del__(self): mt5.shutdown()
该类采用模块化结构,实现了数据加载、模型训练与预测功能,简化了接入交易系统的流程。
最终,我们会得到预测结果与多张图表。以下是COT持仓与TFF持仓的对比示例图:

代码最终会输出一组交易品种的24小时价格预测:

当然,更合理的做法是以周五收盘价格作为预测基准,这样能与COT和TFF报告的发布频率保持一致。
结论
通过MetaTrader 5的Python库,将COT和TFF数据与历史行情结合,能够构建高效的价格预测模型,并直接集成到交易策略中。本方案实现了数据自动获取、模型训练与交易执行的全流程自动化,为交易者在金融市场中提供了稳定的分析与决策基础。此代码支持灵活扩展,可新增技术指标与算法模型,同时实现数据自动更新,确保预测结果始终保持最新。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/18303
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
| Net_NonComm,0.0,0.0。 | |||
| Net_Comm,0.0,0.0. | |||
| Net_Lev_Money,0.0,0.0. | |||
| Net_Asset_Mgr,0.0,0.0 | |||
| Net_NonComm_lag1,0.0,0.0 | |||
| Net_NonComm_change,0.0,0.0 | |||
| Net_Comm_lag1,0.0,0.0 | |||
| Net_Comm_change,0.0,0.0 | |||
| Net_Lev_Money_lag1,0.0,0.0 | |||
| Net_Lev_Money_change,0.0,0.0 | |||
| Net_Asset_Mgr_lag1,0.0,0.0 | |||
| Net_Asset_Mgr_change,0.0,0.0 | |||
神经网络在交易中的应用:基于频域的异常检测 (CATCH)
新手在交易中的10个基本错误
挖掘央行资产负债表数据,描绘全球流动性全貌