Python量化交易学习笔记(48)——选股规则准确率计算

上篇文章记录了因子扩展的过程,这篇文章利用这些因子,来演示如何实现特定选股规则准确率的判断。本文代码主要实现以下功能:

  • 通过遍历历史数据,实现某个选股规则下,N日内收益M%的准确率判断(例如10日内收益超过6%)。
  • 记录符合选股规则的个股及日期,并分为正负样本分别保存为文件,便于后续分析。正样本是指,在当前规则下,N日内能收益M%的某次选股;负样本是指,在当前规则下,N日内未能收益M%的某次选股。
  • 对所有选股的最大收益进行图形化显示。

预期收益

持股2周(10个交易日),收益6%

# 持股天数
g_days = 10
# 预期盈利
g_profit = 0.06

选股规则

  • 2日前倍量暴涨9%以上。
  • 随后两日缩量调整。
  • 收盘价在20均线上方。
  • 20、30、60、120、250均线多头排列。
  • 以下一日开盘价买进
            # 2日前倍量暴涨,连续缩量调整两天
            condition = df['value_boom_2a'].iloc[i] and \
                        df['volume_2a'].iloc[i] >= 2 * df['volume_3a'].iloc[i] and \
                        df['volume_2a'].iloc[i] > df['volume_1a'].iloc[i] and \
                        df['volume_2a'].iloc[i] > df['volume'].iloc[i] and \
                        df['close'].iloc[i] < df['close_2a'].iloc[i] and \
                        df['close_1a'].iloc[i] < df['close_2a'].iloc[i] and \
                        df['close'].iloc[i] > df['ma_20'].iloc[i] and df['ma_30'].iloc[i] > df['ma_60'].iloc[i] \
                        > df['ma_120'].iloc[i] > df['ma_250'].iloc[i]

这里用到了上一篇文章的扩展指标,close_2a表示2日前的收盘价,依次类推。

输出结果

准确率

实验对2018年1月1日以来的历史数据进行测试,所用选股规则的准确率为57.5%,即有57.5%的买入在两周内能至少盈利6%。

正负样本

程序会将正负样本输出到csv文件中,以pos结尾的为正样本,以neg结尾的为负样本,如下图所示。
在这里插入图片描述

正样本示例

正样本以sh600057_pos.csv为例,打开文件显示如下。
在这里插入图片描述

表格中只有一行日期(如果有多次符合选股规则,会显示多行日期),即表示在2020年8月3日买入600057,两周内会有至少6%的收益。来看下2020年8月3日附近的K线图。
在这里插入图片描述
2020年7月30日倍量暴涨,然后缩量调整两天到8月3日,均线多头排列,那么在8月4日开盘价买入(红色向上箭头),后面有一波涨幅。

负样本示例

负样本以sh600063_pos.csv为例,打开文件显示如下。
在这里插入图片描述
表格中只有一行日期(如果有多次符合选股规则,会显示多行日期),即表示在2019年10月14日买入600063,两周内没有实现6%的收益。来看下2019年10月14日附近的K线图。
在这里插入图片描述
2019年10月10日倍量暴涨,然后缩量调整两天到10月14日,均线多头排列,那么在10月15日开盘价买入(红色向上箭头),在后面有一个多月的调整,没有实现6%的收益。

通过正负样本K线的观察,可以叠加选股规则,实现准确率的进一步提升。

最大收益直方图

程序输出的收益直方图如下。
在这里插入图片描述
通过直方图可以看到,最高柱处在0的位置,也就是有很多买入即为高点的情况。也可以看到在0.8以上的位置有一根短柱,即有一部分选股可以实现两周接近翻倍的行情。
直方图可以直观地显示最大收益的分布,为后续指定预期收益的标准提供参考。

小结

  • 通过本文程序可以实现选股规则准确率的判断。
  • 通过输出正负样本,可以通过K线图观察选股准确与错误的情况,以便进一步优化选股规则。
  • 通过直方图可以直观显示最大收益分布情况,便于合理指定收益目标。

完整代码:

import os
import sys
import pandas as pd
import datetime
from matplotlib import pyplot as plt
import matplotlib as mpl

mpl.rcParams['font.sans-serif'] = ['SimHei']  # 设置简黑字体
mpl.rcParams['axes.unicode_minus'] = False  # 解决‘-’bug
# 获取当前目录
proj_path = os.path.dirname(os.path.abspath(sys.argv[0])) + '/../'

# 持股天数
g_days = 10
# 预期盈利
g_profit = 0.06
# 最小周期数,需要根据选股因子的进行不同设置
g_min_period = 280

if __name__ == '__main__':
    # 读入股票代码
    stk_code_file = proj_path + 'data/tdx/all_stock_codes.csv'
    stk_codes = pd.read_csv(stk_code_file, encoding='unicode_escape')
    # 记录正确样本次数
    pos_count = 0
    # 记录错误样本次数
    neg_count = 0
    # 记录最大收益的列表
    profit_list = []
    # 输出路径,用于保存正负样本的代码及日期
    output_path = proj_path + 'data/temp/' + datetime.datetime.strftime(
        datetime.datetime.now(), '%Y-%m-%d-%H-%M-%S') + '/'
    if not os.path.exists(output_path):
        os.makedirs(output_path)
    # 循环处理每只股票
    for code in stk_codes['code']:
        print('processing {} ...'.format(code))
        # 读入股票因子
        input_file = proj_path + 'data/extension/d/hard_rules/' + code + '.csv'
        df = pd.read_csv(input_file)
        # 如果日线数据不足,则跳过
        if df.shape[0] < g_min_period:
            continue
        # 只分析2018年以后的行情
        df = df[df['date'] > '2018-01-01']
        # 存储正、负样本的列表
        out_pos_list = []
        out_neg_list = []

        for i in range(g_min_period, df.shape[0] - g_days):
            # 2日前倍量暴涨,连续缩量调整两天
            condition = df['value_boom_2a'].iloc[i] and \
                        df['volume_2a'].iloc[i] >= 2 * df['volume_3a'].iloc[i] and \
                        df['volume_2a'].iloc[i] > df['volume_1a'].iloc[i] and \
                        df['volume_2a'].iloc[i] > df['volume'].iloc[i] and \
                        df['close'].iloc[i] < df['close_2a'].iloc[i] and \
                        df['close_1a'].iloc[i] < df['close_2a'].iloc[i] and \
                        df['close'].iloc[i] > df['ma_20'].iloc[i] and df['ma_30'].iloc[i] > df['ma_60'].iloc[i] \
                        > df['ma_120'].iloc[i] > df['ma_250'].iloc[i]

            if condition:
                # 记录满足条件时的收盘价
                open_price = df['open'].iloc[i + 1]
                max_price = 0
                # 计算g_days日内的最大收益
                for j in range(g_days):
                    max_price = max(max_price, df['high'].iloc[i + j + 1])
                # 将g_days日内的最大收益保存在列表中
                profit_list.append((max_price - open_price) / open_price)
                # 如果最大收益大于g_profit则为正样本,否则为负样本
                if (max_price - open_price) / open_price > g_profit:
                    pos_count += 1
                    out_pos_list.append(df['date'].iloc[i])
                else:
                    neg_count += 1
                    out_neg_list.append(df['date'].iloc[i])
            if pos_count + neg_count:
                print('pos_count:{}\tneg_count:{}\taccuracy:{}'.format(
                    pos_count, neg_count, pos_count / (pos_count + neg_count)))
        # 将正、负样本股票及日期保存在文件中,便于后续分析
        if len(out_pos_list):
            pd.DataFrame(columns=['date'], data=out_pos_list).to_csv(
                '{}{}_pos.csv'.format(output_path, code), index=False)
        if len(out_neg_list):
            pd.DataFrame(columns=['date'], data=out_neg_list).to_csv(
                '{}{}_neg.csv'.format(output_path, code), index=False)

    print('strategy accuracy:{}'.format(pos_count / (pos_count + neg_count)))
    # 绘制最大收益直方图
    plt.hist(profit_list, 100)
    plt.xlabel('收益')
    plt.ylabel('分布')
    plt.title('收益直方图')
    plt.show()

欢迎大家关注、点赞、转发、留言,感谢支持!
微信群用于学习交流,感兴趣的读者请扫码加微信!
QQ群(676186743)用于资料共享,欢迎加入!

在这里插入图片描述
在这里插入图片描述

おすすめ

転載: blog.csdn.net/m0_46603114/article/details/112850701