基于tushare股票数据进行股票因子回测得到优势权重股

注:数据来源:tushare
       署名:406940

使用框架数据需要提前从从tushare数据源获取数据,思路是这样的:

  1. 爬取某一天所有股票的因子-换手率,通过pro_bar接口获取,从中取出靠前的股票
def handle_mvo_data():
    """
    爬取某一天所有股票的因子-换手率
    :return:
    """
    all_ts_data = pd.DataFrame(columns=['trade_date', 'ts_code', 'close', 'pre_close', 'pct_chg', 'turnover_rate'])
    stock_data = pd.read_csv(current_app.config['STOCK_DATA'] + '\\stock.csv')
    stock_list = stock_data['ts_code'].to_list()
    # 获取时间
    ts_date = handle_ts_date_today()

    tasks = []
    for code in stock_list:
        task = asyncio.ensure_future(handle_data(code, ts_date[1], ts_date[0]))
        tasks.append(task)
    result = loop.run_until_complete(asyncio.wait(tasks))
    all_data = [data.result() for data in result[0] if len(data.result())]
    all_ts_data = pd.concat([all_ts_data, *all_data])
    all_ts_data.to_csv(current_app.config['STOCK_DATA'] + f'\\stock_{datetime.now().strftime("%Y%m")}.csv')
    handle_recommend_stock()
  1. 爬取这些靠前的股票的近一个月时间的数据
def handle_recommend_stock():
    """
    获取到换手率排名前十五的股票代号,然后爬取这些股票的上个月的数据
    :return: dataFrame
    """
    stock_data = pd.read_csv(current_app.config['STOCK_DATA'] + f'\\stock_{datetime.now().strftime("%Y%m")}.csv')
    stock_data = stock_data.sort_values(by='turnover_rate', ascending=False)
    stock_list = stock_data[0:50]['ts_code'].values.tolist()
    ts_date = handle_ts_date()

    all_ts_data = pd.DataFrame(columns=['trade_date', 'ts_code', 'close', 'pre_close', 'pct_chg'])
    for code in stock_list:
        df = pro.daily(ts_code=code, start_date=ts_date[0], end_date=ts_date[1])
        if len(df) >= 22:
            take_df = df[0:22][['trade_date', 'ts_code', 'close', 'pre_close', 'pct_chg']]
            take_df['trade_date'] = take_df['trade_date'].apply(str_date_type)
            all_ts_data = pd.concat([all_ts_data, take_df])
        if len(all_ts_data) >= 330:
            break
    all_ts_data.columns = ['datetime', 'ticker', 'adjlastclose', 'adjclose', 'ret']
    # all_ts_data = all_ts_data.set_index(['datetime', 'ticker'])
    all_ts_data.to_csv(current_app.config['STOCK_DATA'] + f'\\stock_r{datetime.now().strftime("%Y%m")}.csv')
  1. 将数据传入框架进行回测
import numpy as np
import pandas as pd
from datetime import datetime, date
import scipy.optimize as sco
from dateutil.relativedelta import relativedelta

from app.config.setting import STOCK_DATA, FUND_DATA


class Context:
    def __init__(self, noa, count, cate='s'):
        try:
            if cate == 's':
                data = pd.read_csv(STOCK_DATA + f'\\stock_r{datetime.now().strftime("%Y%m")}.csv')
            else:
                data = pd.read_csv(FUND_DATA + f'\\fund_r{datetime.now().strftime("%Y%m")}.csv')
        except:
            if cate == 's':
                data = pd.read_csv(STOCK_DATA + f'\\stock_r{(date.today() - relativedelta(months=+1)).strftime("%Y%m")}.csv')
            else:
                data = pd.read_csv(FUND_DATA + f'\\fund_r{(date.today() - relativedelta(months=+1)).strftime("%Y%m")}.csv')
        self.stock_data = data.set_index(['datetime', 'ticker'])
        self.date_range = data[['datetime']]
        # self.date_range = data[['datetime']].drop_duplicates()[0:23]
        self.noa = noa
        self.frequency = count

    @staticmethod
    def to_datetime(date_range):
        # 将str格式的时间转换为datetime
        date_range = date_range['datetime'].values.tolist()
        # 将date_range修改成datetime格式
        for i in range(len(date_range)):
            date_range[i] = datetime.strptime(date_range[i], "%Y-%m-%d")
        return date_range

    @staticmethod
    def query_buy_stocks_data(buy_stocks):
        # 查询要购买的股票的所有数据
        sql = ""
        isFirst = True  # 判断是否第一次操作
        for buy_stock in buy_stocks:
            if isFirst:
                sql += 'ticker==' + '"' + buy_stock + '"'
                isFirst = False
            else:
                sql += ' or ticker==' + '"' + buy_stock + '"'
        return sql

    def order(self):
        date_range = self.to_datetime(self.date_range)
        for i in range(len(date_range)):
            # date等于date_range的第i天
            date = date_range[i]
            if i > 0:
                # ------------------------------------------------------------
                # 每个交易频率交易一次
                if i % self.frequency == 0:
                    # 获取上一个频率交易日的交易日期
                    last_date = date_range[i - self.frequency]
                    # 获取当日的所有股票数据数据\
                    stock = self.stock_data[self.stock_data.index.get_level_values(0) == str(date)[0:10]]

                    # 获取到的股票数据根据tf因子排序,获取最优秀的noa个股票
                    buy_stocks = stock.index.get_level_values(1).values
                    # print(buy_stocks)
                    sql = self.query_buy_stocks_data(buy_stocks)
                    last_stocks_data = self.stock_data.query(sql)
                    # 获得宽表
                    last_stocks_data_returns = last_stocks_data['ret'].unstack()
                    last_stocks_data_returns_corr = last_stocks_data_returns.cov() * self.frequency

                    ''' 均值方差模型的操作 '''
                    cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
                            {'type': 'ineq', 'fun': lambda x: 0.3 - x},)
                    bnds = tuple((0, 1) for x in range(self.noa))

                    def statistics(weights):
                        weights = np.array(weights)
                        port_returns = np.sum(last_stocks_data_returns.mean() * weights) * self.frequency
                        port_variance = np.sqrt(np.dot(weights.T, np.dot(last_stocks_data_returns_corr, weights)))
                        return np.array([port_returns, port_variance, port_returns / port_variance])

                    # 最小化方差
                    def min_variance(weights):
                        return statistics(weights)[1]

                    # 最大化夏普指数的负值
                    def min_sharpe(weights):
                        return -statistics(weights)[2]

                    # shrape最大优化
                    opts = sco.minimize(min_sharpe, self.noa * [1. / self.noa, ], method='SLSQP', bounds=bnds,
                                        constraints=cons)
                    s_weights = opts['x'].round(3)
                    if len(buy_stocks) > 0 and len(s_weights) > 0:
                        s_w = map(lambda x: round(float(x * 100), 1), list(s_weights))
                        return [list(buy_stocks), list(s_w)]
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九月镇灵将

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值