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散点图我们也可以看到,两者之间的关系并不是很明显。

相关推荐
try2find1 小时前
安装llama-cpp-python踩坑记
开发语言·python·llama
博观而约取2 小时前
Django ORM 1. 创建模型(Model)
数据库·python·django
ytttr8733 小时前
matlab通过Q学习算法解决房间路径规划问题
学习·算法·matlab
精灵vector3 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
Zonda要好好学习4 小时前
Python入门Day2
开发语言·python
Vertira4 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉4 小时前
Python之 sorted() 函数的基本语法
python
项目題供诗4 小时前
黑马python(二十四)
开发语言·python
晓13135 小时前
OpenCV篇——项目(二)OCR文档扫描
人工智能·python·opencv·pycharm·ocr
是小王同学啊~5 小时前
(LangChain)RAG系统链路向量检索器之Retrievers(五)
python·算法·langchain