宏观量化:高频数据驱动PMI预测模型改进

文摘   2024-07-11 22:14   广东  

本期代码预览

后台回复27代码获得本文参考文献及完整代码。

# 获取所有数据
dataframes = {
    "nationwide_blast_furnace_operating_rate_weekly": nationwide_blast_furnace_operating_rate_weekly,
    "tangshan_blast_furnace_operating_rate_weekly": tangshan_blast_furnace_operating_rate_weekly,
    "China_Capacity_Utilization_Electric_Furnace_weekly": China_Capacity_Utilization_Electric_Furnace_weekly,
    "key_enterprises_daily_crude_steel_production_ten_days": key_enterprises_daily_crude_steel_production_weekly,
    "nationwide_estimated_daily_crude_steel_production_ten_days": nationwide_estimated_daily_crude_steel_production_ten_days,
    "automotive_semi_steel_tire_operating_rate_weekly": automotive_semi_steel_tire_operating_rate_weekly,
    "automotive_full_steel_tire_operating_rate_weekly": automotive_full_steel_tire_operating_rate_weekly,
    "PTA_factory_industry_chain_load_rate_daily": PTA_factory_industry_chain_load_rate_daily,
    "jiangsu_zhejiang_weaving_machine_operating_rate_weekly": jiangsu_zhejiang_weaving_machine_operating_rate_weekly,
    "pmi_product_monthly": pmi_product_monthly
}

# 将所有高频数据转换为月度数据
monthly_data = {}
for key, df in dataframes.items():
    # 确保索引是DatetimeIndex
    if not isinstance(df.index, pd.DatetimeIndex):
    # 将索引转换为DatetimeIndex
        df.index = pd.to_datetime(df.index)
    if "weekly" in key:
        monthly_data[key] = df.resample('M').mean()
    elif "ten_days" in key:
        monthly_data[key] = df.resample('M').mean()
    elif "daily" in key:
        monthly_data[key] = df.resample('M').mean()
    else:
        monthly_data[key] = df
    # 根据频率进行重采样,并进行填充
    if "weekly" in key or "ten_days" in key or "daily" in key:
        monthly_data[key] = df.resample('M').mean().ffill().bfill()
    else:
        monthly_data[key] = df

写在前面

宏观研究中离不开PMI,比如依靠3月份制造业PMI的超预期景气度,铜相关行情起飞,相关投资者获得超额收益。

图1:2024年年初至5月沪铜行情

制造业PMI由五个关键分指数按权重汇总而成,公式如下:PMI=新订单指数x30%+生产指数x25%+从业人员指数x20%+(100-供应商配送时间指数)x15%+主要原材料库存x10%,其中每项得分来源于主观调研。

所以探索出一种能够高频跟踪并预测PMI的方法显得尤为重要,旨在数据官方披露前(如月中),提前得知PMI值以辅助决策制定。

因此,我们尝试进行了PMI高频数据预测模型的编制,最终结果详见如下,相比于参考文献效果 更好些,以及因子选取更具经济学逻辑。.

编写过程

有了idea,首先做的就是找文献,果然在慧博平台上找到了相关文献,这里我参考的是国君研报《宏观分析手册之一:PMI分析与预测》,后台回复27代码获得本文参考文献,其思路如下:

选取两个一级因子:新订单指数和生产指数,正如上文公式,PMI=新订单指数x30%+生产指数x25%+从业人员指数x20%+(100-供应商配送时间指数)x15%+主要原材料库存x10%,这两个一级因子可以解释55%。
选取二级因子进行分别拟合:对于新订单指数和生产指数进行高频数据预测,采用基础的OLS模型,采取逐步回归的方式
采用预测的一级因子进行再拟合:采取相同的OLS方法,进行模型重新训练与预测

接下来进行详细介绍。

   一、因子选择

广选因子,为捕捉经济活动的细微变化,广泛搜集各类高频经济指标作为潜在预测因子,包括但不限于“唐山钢厂高炉开工率”、“唐山钢厂高炉开工率”“30 大中城市商品房成交面积”等指标;同时注意因子的时效性,一定是至今持续披露的,否则效果好也用不上;最后注意对因子进行缩放,确保计算结果在PMI范围,相关代码如下:

# 新订单高频因子
china_30_city_housing_sales_area_code = "S2707380" # 中国:30大中城市:商品房成交面积(每日)
china_100_city_land_transactions_area_code = "S2726992" # 中国:100大中城市:成交土地占地面积(每周)
china_daily_passenger_car_sales_retail_code = "S6126413" # 中国:日均销量(厂家零售):乘用车(每日)
china_daily_passenger_car_sales_wholesale_code = "S6126411" # 中国:日均销量(厂家批发):乘用车(每日)
futures_settlement_price_coking_coal_code = "S0181379" # 期货结算价(活跃合约):焦煤(每日)
futures_settlement_price_coke_code = "S0181380" # 期货结算价(活跃合约):焦炭(每日)
futures_settlement_price_thermal_coal_code = "S0182141" # 期货结算价(活跃合约):动力煤(每日)
china_rebar_market_price_code = "S0033227" # 中国:市场价:螺纹钢(HRB400,Φ16-25mm)(每日)
china_cement_price_index_code = "S5914515" # 中国:水泥价格指数(每日)
china_concrete_price_index_code = "S5319531" # 中国:混凝土价格指数(每周)
china_export_price_index_cement_concrete_artificial_stone_code = "S5914490" # 中国出口价格指数:HS4:水泥、混凝土或人造石制品(每月)
baltic_dry_index_code = "S0031550" # 波罗的海干散货指数(每日)
china_export_containerized_freight_index_code = "S0000066" # 中国出口集装箱运价指数(每周)
shanghai_export_containerized_freight_index_code = "S0114089" # 上海出口集装箱运价指数(每周)
china_pmi_new_orders_code = "M0017128" # 中国:制造业PMI:新订单(每月)
# 生产高频因子
nationwide_blast_furnace_operating_rate_weekly = get_wind_data(nationwide_blast_furnace_code, start_date, end_date, "Fill=Previous;usedf=True")
tangshan_blast_furnace_operating_rate_weekly = get_wind_data(tangshan_blast_furnace_code, start_date, end_date, "Fill=Previous;usedf=True")
China_Capacity_Utilization_Electric_Furnace_weekly = get_wind_data(China_Capacity_Utilization_Electric_Furnace, start_date, end_date, "Fill=Previous;usedf=True")
key_enterprises_daily_crude_steel_production_weekly = get_wind_data(key_enterprises_crude_steel_code, start_date, end_date, "Fill=Previous;usedf=True")
nationwide_estimated_daily_crude_steel_production_ten_days = get_wind_data(nationwide_crude_steel_code, start_date, end_date, "Fill=Previous;usedf=True")
automotive_semi_steel_tire_operating_rate_weekly = get_wind_data(semi_steel_tire_code, start_date, end_date, "Fill=Previous;usedf=True")
automotive_full_steel_tire_operating_rate_weekly = get_wind_data(full_steel_tire_code, start_date, end_date, "Fill=Previous;usedf=True")
PTA_factory_industry_chain_load_rate_daily = get_wind_data(PTA_factory_load_code, start_date, end_date, "Fill=Previous;usedf=True")
jiangsu_zhejiang_weaving_machine_operating_rate_weekly = get_wind_data(weaving_machine_code, start_date, end_date, "Fill=Previous;usedf=True")
pmi_product_monthly = get_wind_data(pmi_product, start_date, end_date, "Fill=Previous;usedf=True")
import pandas as pd

# 读取 CSV 文件
df = pd.read_csv('pmi_product_new.csv', index_col=0, parse_dates=True)

# 提取 PMI 生产指数
pmi_product_monthly = df['pmi_product_monthly']

# 获取 PMI 生产指数的最小值和最大值
pmi_min = pmi_product_monthly.min()
pmi_max = pmi_product_monthly.max()

# 剔除 PMI 生产指数,其他变量进行范围调整
other_vars = df.drop(columns=['pmi_product_monthly'])

# 定义一个函数,将其他变量按 PMI 的范围进行缩放
def scale_to_pmi_range(series, pmi_min, pmi_max):
    series_min = series.min()
    series_max = series.max()
    scaled_series = (series - series_min) / (series_max - series_min) * (pmi_max - pmi_min) + pmi_min
    return scaled_series

# 对其他变量进行范围调整
scaled_other_vars = other_vars.apply(scale_to_pmi_range, args=(pmi_min, pmi_max))

# 合并 PMI 生产指数和调整范围后的其他变量
scaled_df = pd.concat([pmi_product_monthly, scaled_other_vars], axis=1)

# 查看结果
print(scaled_df.head())

# 保存调整范围后的数据
scaled_df.to_csv('pmi_product_standardized.csv')

   二、逐步回归

(一)计算VIF

计算VIF,在下面的代码处理中如果大于10予以剔除。

import pandas as pd
import statsmodels.api as sm
from statsmodels.stats.outliers_influence import variance_inflation_factor

def calculate_vif(X):
    """计算方差膨胀因子(VIF)"""
    vif = pd.DataFrame()
    vif["variables"] = X.columns
    vif["VIF"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
    return vif

(二)逐步回归,保留显著的变量

def stepwise_regression(X, y, p_threshold=0.1):
    """逐步回归,保留显著的变量"""
    selected_vars = X.columns.tolist()
    while True:
        X_temp = X[selected_vars]
        model = sm.OLS(y, X_temp).fit()
        p_values = model.pvalues.drop('const')
        if p_values.max() > p_threshold:  # 剔除 p 值大于阈值的变量
            excluded_var = p_values.idxmax()
            selected_vars.remove(excluded_var)
        else:
            break
    return selected_vars

(三)输出结果及拟合方程

检测并剔除多重共线性严重的变量,并且显示显著的变量,输出拟合结果和拟合方程

def remove_multicollinearity(X, selected_vars, vif_threshold=10):
    """检测并剔除多重共线性严重的变量"""
    while True:
        X_temp = X[selected_vars]
        vif = calculate_vif(X_temp)
        max_vif = vif['VIF'].max()
        if max_vif > vif_threshold:
            excluded_var = vif.loc[vif['VIF'].idxmax(), 'variables']
            if excluded_var == 'const':  # 不剔除常数项
                break
            selected_vars.remove(excluded_var)
        else:
            break
    return selected_vars

def display_model_equation(model):
    """显示拟合的模型方程"""
    params = model.params
    equation = "PMI 生产指数 = "
    for param, value in params.items():
        if param == 'const':
            equation += f"{value:.4f} "
        else:
            equation += f"{'+' if value >= 0 else '-'} {abs(value):.4f} * {param} "
    return equation

# 读取标准化后的 CSV 文件
df = pd.read_csv('pmi_product_standardized.csv', index_col=0, parse_dates=True)

# 提取 PMI 生产指数和其他变量
pmi_product_monthly = df['pmi_product_monthly']
other_vars = df.drop(columns=['pmi_product_monthly'])

# 自变量(高频指标)和因变量(PMI 生产指数)
X = sm.add_constant(other_vars)
y = pmi_product_monthly

# 逐步回归,保留显著的变量
selected_vars = stepwise_regression(X, y)

# 检测并剔除多重共线性严重的变量
selected_vars = remove_multicollinearity(X, selected_vars)

# 最终选择的变量
X_selected = X[selected_vars]

# 最终模型拟合
final_model = sm.OLS(y, X_selected).fit()
print(final_model.summary())

# 输出模型方程
model_equation = display_model_equation(final_model)
print("\n拟合的模型方程:")
print(model_equation)


另一个因子重复相同步骤,最终两个二级因子重复相同步骤,结果如下,展示了较好的预测性,并且后续可以选择更多因子&模型进行优化。

   四、代码优点

(一)程序自动抓取数据,无需手工调整

只要任意一个因子数据有更新,即会自动抓取并计算,无需手工调整。

# 定义获取数据的函数
def get_wind_data(codes, begin_time, end_time, options):
    data = w.edb(codes, begin_time, end_time, options)
    if data.ErrorCode != 0:
        print(f"Error in fetching data for {codes}: {data.ErrorCode}")
        return None
    df = pd.DataFrame(data.Data[0], index=data.Times, columns=[codes] if isinstance(codes, str) else codes)
    return df

# 变量代码字典
variable_codes = {
    "nationwide_blast_furnace_operating_rate_weekly""C1925068",
    "tangshan_blast_furnace_operating_rate_weekly""S5711217",
    "China_Capacity_Utilization_Electric_Furnace_weekly""S5715660",
    "key_enterprises_daily_crude_steel_production_weekly""S5704502",
    "nationwide_estimated_daily_crude_steel_production_ten_days""S5708247",
    "automotive_semi_steel_tire_operating_rate_weekly""S6124651",
    "automotive_full_steel_tire_operating_rate_weekly""S6124650",
    "PTA_factory_industry_chain_load_rate_daily""S5446173",
    "jiangsu_zhejiang_weaving_machine_operating_rate_weekly""S5417019"
}


(二)可以任意增删因子

一级因子放在一起了,可以任意增删,后面的模型训练是既定的,省时省力。

# 最新指标和Wind代码
variable_codes = {
    "china_30_city_housing_sales_area_daily""S2707380",  # 中国:30大中城市:商品房成交面积(每日)
    "china_100_city_land_transactions_area_weekly""S2726992",  # 中国:100大中城市:成交土地占地面积(每周)
    "china_daily_passenger_car_sales_retail_daily""S6126413",  # 中国:日均销量(厂家零售):乘用车(每日)
    "china_daily_passenger_car_sales_wholesale_daily""S6126411",  # 中国:日均销量(厂家批发):乘用车(每日)
    "futures_settlement_price_coking_coal_daily""S0181379",  # 期货结算价(活跃合约):焦煤(每日)
    "futures_settlement_price_coke_daily""S0181380",  # 期货结算价(活跃合约):焦炭(每日)
    "futures_settlement_price_thermal_coal_daily""S0182141",  # 期货结算价(活跃合约):动力煤(每日)
    "china_rebar_market_price_daily""S0033227",  # 中国:市场价:螺纹钢(HRB400,Φ16-25mm)(每日)
    "china_cement_price_index_daily""S5914515",  # 中国:水泥价格指数(每日)
    "china_concrete_price_index_weekly""S5319531",  # 中国:混凝土价格指数(每周)
    "china_export_price_index_cement_concrete_artificial_stone_monthly""S5914490",  # 中国出口价格指数:HS4:水泥、混凝土或人造石制品(每月)
    "baltic_dry_index_daily""S0031550",  # 波罗的海干散货指数(每日)
    "china_export_containerized_freight_index_weekly""S0000066",  # 中国出口集装箱运价指数(每周)
    "shanghai_export_containerized_freight_index_weekly""S0114089",  # 上海出口集装箱运价指数(每周)
    "china_pmi_new_orders_monthly""M0017128"  # 中国:制造业PMI:新订单(每月)
}


# 读取 CSV 文件,并根据 end_date 过滤数据
df = pd.read_csv('pmi_product_new1.csv', index_col=0, parse_dates=True)
df = df.loc[start_date:end_date]  # 过滤数据,确保只使用 start_date 到 end_date 之间的数据

关注我们

   资源获取

后台回复【27代码】即可得到【详细代码及参考文献】。

   知识星球

知识星球全面升级,本代码及文献早于普通公众号发布,

近期星球推出了每日价值信息汇总,将最新的一级二级信息进行每日汇总,相关说明如下:

(1)转融通利好名单,今日已得到结果兑现
(2)萝卜快跑拟商业化运营,成功推票百度,上涨8%
(3)成功预测半导体本轮行情
...

<<< 左右滑动见更多 >>>

并且对于暑期和校招生来说,星球内定期发布从数千人群聊中沉淀的一手校招价值信息。

(1)证券今年有无hc,及往年留用比例
(2)证券进度-信采
(3)银行第一批offer
(4)基金拒了是不是会拉黑
...

<<< 左右滑动见更多 >>>

需要的朋友扫码获取或者微店购买(2选1即可)



 感谢观看,本篇相关资源在关注后可领取 
 欢迎点赞与在看,感谢支持!


文商科学码自救小组
专注于结合编程与文商科知识,消灭工作中的Dirty Work,内容涵盖Python/RPA等。
 最新文章