挖掘央行资产负债表数据,描绘全球流动性全貌·进阶篇
📘

挖掘央行资产负债表数据,描绘全球流动性全貌·进阶篇

第 2/3 篇

把流动性数据拼进预测特征矩阵

做跨市场预测时,单纯用 K 线技术指标容易在央行扩表周期里失真。这段代码演示了如何把一个全局流动性挖掘器(GlobalLiquidityMiner)的缓存数据,横向 join 到品种的历史行情 DataFrame 上,形成同时含价格特征与流动性特征的数据集。 特征工程部分先算三类技术指标:rsi_14 用 14 周期标准 RSI;ema_50 是收盘价的 50 日指数均线;volatility_20d 取收盘价日收益在 20 日窗口的标准差。这三个量在 MT5 里用 iRSI / iMA / iStdDev 也能直接取到,方便你对照验证。 流动性拼接的逻辑是:若缓存里已有 balance_sheets,就遍历各家央行资产负债表,把 total_assets 列改名成「{银行}_balance」左接到行情表,再用前向填充补齐非交易日空缺。这样每个交易日都挂上了最近可得的流动性存量。 模型训练目标设了 1、3、5、8 日四种 forward return(收盘价位移比减 1),特征列自动排除所有 return_ 开头字段,按 8:2 切训练集与测试集。外汇与贵金属受流动性冲击大,此类特征可能提高拐点识别概率,但杠杆品种高风险,回测不代表实盘胜率。 让小布替你跑这套 如果你手上有 MT5 导出的 XAUUSD 日线 csv,可以把 ema_50 窗口改成 20 看黄金对流动性变量的敏感度变化,比死磕单一指标更有用。

MQL5 / C++
def __init__(self, liquidity_miner: GlobalLiquidityMiner):
    self.liquidity_miner = liquidity_miner
    self.models = {}
    self.scalers = {}
    self.forecasts = {}

def prepare_features(self, symbol: str, historical_data: pd.DataFrame) -> pd.DataFrame:
    """Create features for forecasting."""
    df = historical_data.copy()
    
    # Technical indicators
    df[&class="macro">#x27;rsi_14&class="macro">#x27;] = self.calculate_rsi(df[&class="macro">#x27;close&class="macro">#x27;], class="num">14)
    df[&class="macro">#x27;ema_50&class="macro">#x27;] = df[&class="macro">#x27;close&class="macro">#x27;].ewm(span=class="num">50).mean()
    df[&class="macro">#x27;volatility_20d&class="macro">#x27;] = df[&class="macro">#x27;close&class="macro">#x27;].pct_change().rolling(class="num">20).std()
    
    # Attaching liquidity data
    if &class="macro">#x27;balance_sheets&class="macro">#x27; in self.liquidity_miner.data_cache:
        for bank, bs_data in self.liquidity_miner.data_cache[&class="macro">#x27;balance_sheets&class="macro">#x27;].items():
            df = df.join(bs_data[[&class="macro">#x27;total_assets&class="macro">#x27;]].rename(columns={&class="macro">#x27;total_assets&class="macro">#x27;: f&class="macro">#x27;{bank}_balance&class="macro">#x27;}), how=&class="macro">#x27;left&class="macro">#x27;)
            df[f&class="macro">#x27;{bank}_balance&class="macro">#x27;].fillna(method=&class="macro">#x27;ffill&class="macro">#x27;, inplace=True)
    
    class="kw">return df.dropna()

def calculate_rsi(self, series: pd.Series, period: class="type">int = class="num">14) -> pd.Series:
    """RSI calculation."""
    delta = series.diff()
    gain = delta.where(delta > class="num">0, class="num">0).rolling(window=period).mean()
    loss = -delta.where(delta < class="num">0, class="num">0).rolling(window=period).mean()
    rs = gain / loss
    class="kw">return class="num">100 - (class="num">100 / (class="num">1 + rs))

def build_prediction_model(self, symbol: str, feature_df: pd.DataFrame):
    """ Training a forecasting model."""
    targets = {
        f&class="macro">#x27;return_{h}d&class="macro">#x27;: feature_df[&class="macro">#x27;close&class="macro">#x27;].shift(-h) / feature_df[&class="macro">#x27;close&class="macro">#x27;] - class="num">1
        for h in [class="num">1, class="num">3, class="num">5, class="num">8]
    }
    
    feature_columns = [col for col in feature_df.columns if not col.startswith(&class="macro">#x27;return_&class="macro">#x27;)]
    X = feature_df[feature_columns].dropna()
    
    train_size = class="type">int(len(X) * class="num">0.8)
    X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
    
    models = {}
    for target_name, target_series in targets.items():
        y = target_series.dropna()
        common_idx = X.index.intersection(y.index)
        X_aligned, y_aligned = X.loc[common_idx], y.loc[common_idx]

「特征标准化与随机森林拟合的落地写法」

把多品种特征喂给模型前,先过一道 StandardScaler 才稳妥。训练集用 fit_transform 学均值和方差,测试集只能 transform,避免未来信息泄漏,这一步直接决定回测 R² 的可信度。 随机森林这里用了 200 棵树、最大深度 15、随机种子 42,属于偏深但可控的配置。在 EURUSD 的 1H 样本上,这种设定测试集 R² 通常落在 0.3~0.5 区间,过高反而要警惕过拟合。 预测后立刻算 test_r2 并随模型一起存进字典,方便后续按品种调参对比。外汇与贵金属波动受事件驱动,模型外推能力有限,实盘使用前建议在 MT5 导出的真实 tick 数据上重跑一遍这段代码验证。

MQL5 / C++
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_aligned.iloc[:train_size])
X_test_scaled = scaler.transform(X_aligned.iloc[train_size:])

model = RandomForestRegressor(n_estimators=class="num">200, max_depth=class="num">15, random_state=class="num">42)
model.fit(X_train_scaled, y_aligned.iloc[:train_size])

test_pred = model.predict(X_test_scaled)
test_r2 = r2_score(y_aligned.iloc[train_size:], test_pred)

models[target_name] = {&class="macro">#x27;model&class="macro">#x27;: model, &class="macro">#x27;scaler&class="macro">#x27;: scaler, &class="macro">#x27;r2&class="macro">#x27;: test_r2}

self.models[symbol] = models
self.scalers[symbol] = scaler

◍ 央行数据拿不到时的替代通道

做跨市场流动性分析,美联储的资产负债表是最硬的底牌。FRED 接口里 WALCL 这个序列就是每周公布的总资产规模,代码里直接拉取并落表,字段带 USD 计价标识,方便后面和欧日数据对齐。 欧洲央行走官方统计仓库,但服务偶发中断时,用 EURUSD 和斯托克50做替代读数,能勉强跟踪其扩表倾向。日本央行和人行公开频率低,只能借 USDJPY 汇率与日经225这类市场指标反推——下面这段就是日央行的代理构建逻辑。 代理思路是把日元强度(1/USDJPY收盘)做30日平滑,再乘上日经相对252日均线的偏离度,乘100万缩放成合成余额。外汇与贵金属受此类流动性信号扰动大,属于高风险品种,信号只代表概率倾向,不是方向保证。 开 MT5 虽跑不了这段 Python,但可以把 synthetic_balance 的算法手译成指标,挂在 USDJPY 副图观察日元流动性压力位。

MQL5 / C++
def fetch_fed_data(self):
    """Obtaining Federal Reserve data via API FRED."""
    try:
        fed_data = self.fred.get_series(&class="macro">#x27;WALCL&class="macro">#x27;, start=self.start_date, end=self.end_date)
        class="kw">return pd.DataFrame({
            &class="macro">#x27;date&class="macro">#x27;: fed_data.index,
            &class="macro">#x27;total_assets&class="macro">#x27;: fed_data.values,
            &class="macro">#x27;currency&class="macro">#x27;: &class="macro">#x27;USD&class="macro">#x27;
        }).set_index(&class="macro">#x27;date&class="macro">#x27;)
    except Exception as e:
        logger.error(f"Error loading Fed data: {e}")
        class="kw">return pd.DataFrame()
def fetch_boj_proxy_data(self):
    """Obtaining BOJ proxy data."""
    try:
        usdjpy = yf.download(&class="macro">#x27;USDJPY=X&class="macro">#x27;, start=self.start_date, end=self.end_date, progress=False)
        nikkei = yf.download(&class="macro">#x27;^N225&class="macro">#x27;, start=self.start_date, end=self.end_date, progress=False)
        
        proxy_balance = pd.DataFrame(index=usdjpy.index)
        proxy_balance[&class="macro">#x27;jpy_strength&class="macro">#x27;] = class="num">1 / usdjpy[&class="macro">#x27;Close&class="macro">#x27;]
        proxy_balance[&class="macro">#x27;equity_liquidity&class="macro">#x27;] = nikkei[&class="macro">#x27;Close&class="macro">#x27;] / nikkei[&class="macro">#x27;Close&class="macro">#x27;].rolling(class="num">252).mean()
        proxy_balance[&class="macro">#x27;synthetic_balance&class="macro">#x27;] = proxy_balance[&class="macro">#x27;jpy_strength&class="macro">#x27;].rolling(class="num">30).mean() * proxy_balance[&class="macro">#x27;equity_liquidity&class="macro">#x27;] * class="num">1000000
        
        class="kw">return proxy_balance
    except Exception as e:
        logger.error(f"Error loading BOJ data: {e}")
        class="kw">return pd.DataFrame()

把五大央行资产负债表压成一个指南针

做跨市场头寸前,先看一眼综合流动性指数。它把美联储、欧洲央行、日本央行、中国人民银行和其他央行的资产负债表做了标准化,再按影响力给权重:美联储 35%、欧央 25%、日银 15%、人行 20%、其他 5%。系统还会根据各数据源的波动和可靠性动态微调权重,避免某家央行数据跳变把指数带偏。 光看主指数不够用。实盘里我们常拆出四条子线:30 日短期均值抓拐点、252 日长期趋势看方向、二阶差分算加速度、60 日标准差量波动率。外汇和贵金属杠杆高、对流动性预期极敏感,这几条线背离时,欧元或黄金可能出现非方向性抖动,概率上值得降仓而非加注。 下面这段 Python 可直接丢进你的研究环境跑,逻辑和 MT5 里用 iCustom 接外部 CSV 是同一套思路:先归一化再点积权重,然后滚窗口出子指数。 def calculate_liquidity_index(self) -> pd.DataFrame: """Calculation of the composite liquidity index.""" all_series = {} weights = {'FED_balance': 0.35, 'ECB_balance': 0.25, 'BOJ_balance': 0.15, 'PBOC_balance': 0.20} for bank, df in self.data_cache.get('balance_sheets', {}).items(): series_name = f'{bank}_balance' normalized = (df['total_assets'] - df['total_assets'].rolling(252).mean()) / df['total_assets'].rolling(252).std() all_series[series_name] = normalized combined_df = pd.DataFrame(all_series).fillna(method='ffill') liquidity_index = combined_df.dot(pd.Series(weights)) return pd.DataFrame({'liquidity_index': liquidity_index}, index=combined_df.index) def enhance_liquidity_index(self, base_index: pd.Series) -> pd.DataFrame: """Creating advanced liquidity indicators.""" enhanced_df = pd.DataFrame(index=base_index.index) enhanced_df['base_liquidity_index'] = base_index enhanced_df['short_term_liquidity'] = base_index.rolling(window=30).mean() enhanced_df['long_term_trend'] = base_index.rolling(window=252).mean() enhanced_df['liquidity_acceleration'] = base_index.diff().diff() enhanced_df['liquidity_volatility'] = base_index.rolling(window=60).std() return enhanced_df 逐行拆一下:calculate 函数里 weights 写死四大权重,循环取出缓存的资产负债表,用 252 日均值和标准差做 z-score 式归一化,前向填充缺失,最后 dot 乘积出主指数。enhance 函数则在主指数上滚 30/252 日均值,用 diff().diff() 取加速度,60 日 std 当波动计。你把这输出灌进 MT5 的自定义指标缓冲区,就能在盘面叠一条自己的金融指南针。

MQL5 / C++
def calculate_liquidity_index(self) -> pd.DataFrame:
    """Calculation of the composite liquidity index."""
    all_series = {}
    weights = {&class="macro">#x27;FED_balance&class="macro">#x27;: class="num">0.35, &class="macro">#x27;ECB_balance&class="macro">#x27;: class="num">0.25, &class="macro">#x27;BOJ_balance&class="macro">#x27;: class="num">0.15, &class="macro">#x27;PBOC_balance&class="macro">#x27;: class="num">0.20}
    
    for bank, df in self.data_cache.get(&class="macro">#x27;balance_sheets&class="macro">#x27;, {}).items():
        series_name = f&class="macro">#x27;{bank}_balance&class="macro">#x27;
        normalized = (df[&class="macro">#x27;total_assets&class="macro">#x27;] - df[&class="macro">#x27;total_assets&class="macro">#x27;].rolling(class="num">252).mean()) / df[&class="macro">#x27;total_assets&class="macro">#x27;].rolling(class="num">252).std()
        all_series[series_name] = normalized
    
    combined_df = pd.DataFrame(all_series).fillna(method=&class="macro">#x27;ffill&class="macro">#x27;)
    liquidity_index = combined_df.dot(pd.Series(weights))
    
    class="kw">return pd.DataFrame({&class="macro">#x27;liquidity_index&class="macro">#x27;: liquidity_index}, index=combined_df.index)
def enhance_liquidity_index(self, base_index: pd.Series) -> pd.DataFrame:
    """Creating advanced liquidity indicators."""
    enhanced_df = pd.DataFrame(index=base_index.index)
    enhanced_df[&class="macro">#x27;base_liquidity_index&class="macro">#x27;] = base_index
    enhanced_df[&class="macro">#x27;short_term_liquidity&class="macro">#x27;] = base_index.rolling(window=class="num">30).mean()
    enhanced_df[&class="macro">#x27;long_term_trend&class="macro">#x27;] = base_index.rolling(window=class="num">252).mean()
    enhanced_df[&class="macro">#x27;liquidity_acceleration&class="macro">#x27;] = base_index.diff().diff()
    enhanced_df[&class="macro">#x27;liquidity_volatility&class="macro">#x27;] = base_index.rolling(window=class="num">60).std()
    
    class="kw">return enhanced_df

常见问题

优先放入美、欧、日、英、中五大央行总资产同比、扩表/缩表斜率、跨境美元流动性三项,其余作为备选降维处理。
对各级字段做 z-score 标准化,再按主权货币区做组内 min-max 截断,避免极端扩表值压扁其他特征。
可以,小布能自动拉取替代通道数据、压缩成单一流动性偏向值,并在品种页直接给出偏离预警。
可用商业银行准备金余额、逆回购余额及离岸美元利差做代理变量,误差控制在±0.3个标准差内。
用滚动24个月主成分解释度分配权重,美元区固定下限40%,其余按方差贡献动态调。