注:数据来源:tushare
署名:406940
使用框架数据需要提前从从tushare数据源获取数据,思路是这样的:
- 爬取某一天所有股票的因子-换手率,通过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()
- 爬取这些靠前的股票的近一个月时间的数据
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')
- 将数据传入框架进行回测
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)]