Python量化交易学习——Part12:回归模型的典型应用

回归模型在很多的时候被应用于对股票的基本面数据进行分析,例如经典的CAPM模型、Fama-French三因子模型以及最新的PB_ROE模型等。这些都是已经应用于现实中的金融市场并获得较好收益的经典模型。本章将通过介绍PB_ROE模型,进一步讲解回归分析在实战过程中的使用。

PB-ROE 回归模型的使用

选股的核心思想在于寻找价格低于内在价值的股票,从而获取未来价格修复的收益。具体来看,符合价值投资理念的标的首先要有良好的基本面,其次要有合理的价格。PB-ROE选股模型便是价值投资中的一种经典方法。该模型中,ROE作为基本面指标,用于衡量公司质地是否优良,PE作为估值指标,用于衡量当前价格是否低估。

市净率(Price-to-Book Ratio,简称P/B PBR)指的是每股股价与每股净资产的比率。 市净率可用于股票投资分析,一般来说市净率较低的股票,投资价值较高,相反,则投资价值较低;但在判断投资价值时还要考虑当时的市场环境以及公司经营情况、盈利能力等因素。

股东权益报酬率=可供普通股东分配的净利润/平均普通股东权益×100%,这个比率通常也被称为净资产收益率,英文缩写ROE

因此PB-ROE选股策略可以概况为以下几点:

(1)获取股票的PB\ROE数据,建立PB-ROE数据之间的回归模型;

(2)一般来说,净资产收益率高的股票,市净率也应该高一些,因此可以根据回归模型计算单个股票拟合后的PB值,与真实PB值进行比较,并对差值进行排序,当(真实PB-预测PB)的值越大,说明股票越被高估,当值越小,说明越被低估。

(3)根据排序后的结果进行股票买卖

(4)在导入股票的时候,我们只选择了沪深主板的股票,原因很简单,因为我没有创业板和科创板买卖权限...

# coding=utf-8
from __future__ import print_function, absolute_import
import statsmodels.api as sm
import numpy as np
import matplotlib.pyplot as plt
import random
import pandas as pd
from gm.api import *
import datetime
import os
import statsmodels.api as sm
# from MSCI_tools import mcsi_tools as tools


set_token('a7f3404da7e8c9a3aea631b5ae0c893b1d57161d')
now, hour_and_mins = str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')).split(" ")


# 获取A股除了流动性不足的股票以及ST股票外,入选中证组合的几乎全部的股票清单(沪深主板)
def get_symbol_list_all():

    A_share_list = {"SHSE.000010": "SHANG_ZHENG180", "SHSE.000016": "SHANG_ZHENG50", "SHSE.000300": "HU_SHEN300",
                    "SHSE.000903": "ZHONG_ZHENG100",
                    "SHSE.000904": "ZHONG_ZHENG200", "SHSE.000905": "ZHONG_ZHENG500", "SHSE.000906": "ZHONG_ZHENG800",
                    "SHSE.000907": "ZHONG_ZHENG700", "SHSE.000852": "ZHONG_ZHENG1000"}
    A_share_main = {"600", "601", "602", "603", "000", "001", "002", "003"} #沪深主板股票代码的开头
    symbol_list_all = pd.DataFrame([])
    for key in A_share_list.keys():
        symbol_list = stk_get_index_constituents(key)  # 获取指数成分股数据
        symbol_list_all = pd.concat([symbol_list_all, symbol_list], ignore_index=True)
        # symbol_list_all =symbol_list_all.append(symbol_list)  #新语句中append已经弃用
    symbol_set = symbol_list_all["symbol"].values
    target_symbol_list= []
    for symbol in symbol_set:#从股票池中寻找主板股票
        if symbol[5:8]  in A_share_main and symbol not in target_symbol_list:
            target_symbol_list.append(symbol)
    print("沪深主板股票资源池数量",len(target_symbol_list))
    return target_symbol_list

def PB_ROE_strategy(symbol_list,now):
    last_day = get_previous_trading_date("SHSE",now)
    ROE = stk_get_finance_deriv_pt(symbols=symbol_list,fields="roe",date=last_day,df=True)
    ROE = ROE.sort_values(["symbol"],ascending=False)
    ROE = ROE.reset_index(drop=True)
    PB = stk_get_daily_valuation_pt(symbols=symbol_list, fields="pb_mrq", trade_date=last_day, df=True)
    PB = PB.sort_values(["symbol"],ascending=False)
    PB = PB.reset_index(drop=True)
    ROE_PB_list = pd.merge(ROE, PB)
    drop_num =[]
    for num in range(len(ROE_PB_list)-1):
        if ROE_PB_list["roe"].values[num] < 0 or ROE_PB_list["pb_mrq"].values[num] > 6:
            drop_num.append(num)
    ROE_PB_list = ROE_PB_list.drop(drop_num)
    ROE_PB_list.dropna()
    symbol_roe = ROE_PB_list["roe"].values #自变量
    symbol_pb = ROE_PB_list["pb_mrq"].values #因变量
    symbol_roe[np.isnan(symbol_roe)] = 0
    symbol_roe[np.isinf(symbol_roe)] = 0
    symbol_pb[np.isnan(symbol_pb)] = 0
    symbol_pb[np.isinf(symbol_pb)] = 0
    #进行回归分析
    symbol_roe = sm.add_constant(symbol_roe)
    model = sm.OLS(symbol_pb, symbol_roe)  # 前面为因变量,后面为自变量
    result = model.fit()
    bias, weight = (result.params)  # 前面为截距,后面为斜率
    ROE_PB_list["PB_new"] = bias + ROE_PB_list["roe"]*weight
    # print(ROE_PB_list)
    ROE_PB_list["value"] = ROE_PB_list["PB_new"] -ROE_PB_list["pb_mrq"] #大于0说明是低估
    ROE_PB_list = ROE_PB_list.sort_values(["value"],ascending=False)
    ROE_PB_list = ROE_PB_list.reset_index(drop=True)
    # print(ROE_PB_list)

    #绘制ROE-PB关系曲线
    # fig = plt.figure()
    # #将画图窗口分为1行1列,选择第一块区域做子图
    # ax = fig.add_subplot(111)
    # ax.set_title("ROE-PB model")
    # ax.set_xlabel("ROE")
    # ax.set_ylabel("PB")
    # ax.scatter(ROE_PB_list["roe"].values,ROE_PB_list["pb_mrq"].values,c="k",marker=".")
    # ax.plot(ROE_PB_list["roe"].values,ROE_PB_list["PB_new"].values,c="b")
    # plt.show()

    return ROE_PB_list["symbol"].values

symbol_list = get_symbol_list_all()
PB_ROE_strategy(symbol_list,now)
# symbol_list1 = stk_get_index_constituents("SHSE.000903")
# symbol_list2 = stk_get_index_constituents("SHSE.000016")
# print(symbol_list1)
# print(symbol_list2)
# print(symbol_list1.append(symbol_list2))

# coding=utf-8


def init(context):
    # 每天14:50 定时执行algo任务,
    # algo执行定时任务函数,只能传context参数
    # date_rule执行频率,目前暂时支持1d、1w、1m,其中1w、1m仅用于回测,实时模式1d以上的频率,需要在algo判断日期
    # time_rule执行时间, 注意多个定时任务设置同一个时间点,前面的定时任务会被后面的覆盖
    schedule(schedule_func=algo, date_rule='1m', time_rule='09:31:00')
    context.count = 1
    context.num = 5
    context.fields = "open,high,low,close"

def algo(context):
    now = context.now
    last_day = get_previous_trading_date("SHSE",now)
    target_symbol_list = get_symbol_list_all()
    ROE_PB_list = PB_ROE_strategy(target_symbol_list,now)
    target_list = ROE_PB_list[0:context.num]
    positions = context.account().positions()

    for position in positions:
        symbol = position["symbol"]
        if symbol not in target_list:
            order_target_percent(symbol=symbol,percent=0,order_type=OrderType_Market,position_side=PositionSide_Long)
    positions = context.account().positions()
    holded_symbol = []

    for position in positions:
        symbol = position["symbol"]
        holded_symbol.append(symbol)
    print(holded_symbol)

    for symbol in target_list:
        if symbol not in holded_symbol:
            print(symbol)
            data = history_n(symbol=symbol,frequency='1d',count=2,end_time=now,fields=context.fields,adjust=ADJUST_PREV, df=True)
            open =data["open"].values
            close = data["close"].values
            if open[-1] < close[0]*1.08: #没有涨停,涨停就无法买入了
                order_target_percent(symbol=symbol,percent=(1/context.num)*0.95,order_type=OrderType_Market,position_side=PositionSide_Long)
            else:
                pass


# 查看最终的回测结果
def on_backtest_finished(context, indicator):
    print(indicator)


if __name__ == '__main__':
    '''
        strategy_id策略ID, 由系统生成
        filename文件名, 请与本文件名保持一致
        mode运行模式, 实时模式:MODE_LIVE回测模式:MODE_BACKTEST
        token绑定计算机的ID, 可在系统设置-密钥管理中生成
        backtest_start_time回测开始时间
        backtest_end_time回测结束时间
        backtest_adjust股票复权方式, 不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
        backtest_initial_cash回测初始资金
        backtest_commission_ratio回测佣金比例
        backtest_slippage_ratio回测滑点比例
        backtest_match_mode市价撮合模式,以下一tick/bar开盘价撮合:0,以当前tick/bar收盘价撮合:1
    '''
    run(strategy_id='8e30d6a2-3b4c-11ef-94ca-e073e7f1ba05',
        filename='pb_roe_strategy.py',
        mode=MODE_BACKTEST,
        token='自己的token码',
        backtest_start_time='2022-11-01 09:30:00',
        backtest_end_time='2024-06-20 15:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001,
        backtest_match_mode=1)

结果如下:从结果看,获得了一定的超额收益,但收益并不明显,通过绘制PB-ROE散点图我们也可以看到,两者之间的关系并不是很明显。

相关推荐
PandaCave1 分钟前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
yuwinter6 分钟前
鸿蒙HarmonyOS学习笔记(2)
笔记·学习·harmonyos
LZXCyrus20 分钟前
【杂记】vLLM如何指定GPU单卡/多卡离线推理
人工智能·经验分享·python·深度学习·语言模型·llm·vllm
Enougme23 分钟前
Appium常用的使用方法(一)
python·appium
red_redemption28 分钟前
自由学习记录(23)
学习·unity·lua·ab包
懷淰メ29 分钟前
PyQt飞机大战游戏(附下载地址)
开发语言·python·qt·游戏·pyqt·游戏开发·pyqt5
hummhumm43 分钟前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
幽兰的天空1 小时前
默语博主的推荐:探索技术世界的旅程
学习·程序人生·生活·美食·交友·美女·帅哥
hummhumm1 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
每天吃饭的羊1 小时前
python里的数据结构
开发语言·python