参考资料:
2.python==python38
3. 查询dqnapi接口域名:http://www.dqnapi.com/
pro.stock_basic dqnapi号:100.3568/2023.1_v1
pro.daily dqnapi号:100.3568/2023.11_v1
分析:
这段代码实现的是基于股票池,利用行业联动异动因子和相关系数等指标筛选股票并等权持仓的策略,并进行了回测。
首先,在初始化函数中设置了一些参数,包括股票池、最大止损比例、最大涨幅比例、上一个交易月份、最大相关系数数量、最大V值数量、最大买入股票数量等。在交易函数 handle_bar 中,通过获取当前持仓情况,判断是否需要卖出股票。其次,如果进入了新的交易月份,会根据行业联动异动因子和行业相关系数等指标筛选股票,并根据等权买入方式对股票进行买入。具体而言,先筛选出符合要求的股票池,然后根据行业联动异动因子将不同行业的股票进行排序,选取前几个行业内的前几只股票作为备选股票池,再根据股票池内的股票数量等权买入股票。最后,每分钟访问 API 次数不超过 800 次,每次回测之间暂停 75 秒,如果因为访问次数限制无法成功访问则暂停一分钟后重试。
根据这段代码,我们可以得到一个基于行业联动异动因子和相关系数等指标的股票策略,该策略利用了多种指标来进行股票池筛选和买卖判断,同时也考虑了访问次数限制和回测时间范围等实际问题。但需要注意的是,该策略依然可能存在风险,具体效果还需根据实际情况进行评估。
python 代码
import tushare as ts
import pandas as pd
import time
from datetime import datetime, timedelta
# 初始化函数
def init(context):
# 初始化参数
context.sl = '000001.SH'
data = pro.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date')
context.data_list = data[['ts_code', 'industry']]
context.month = context.now.month
context.not_sell = []
context.max_loss = 0.05
context.max_rise = 0.3
context.last_month = 3
context.max_corr = 3
context.max_v = 5
context.max_buy = 3
print("Run Info: {}".format(context.run_info))
# 交易函数
def handle_bar(context, row):
positions = context.portfolio.positions
sell_stocks = []
for stock in positions:
if stock not in context.not_sell:
order_target_value(stock, 0)
else:
now_price = row[stock] # 获取当日股票价格
if now_price < positions[stock].avg_cost * (1 - context.max_loss):
order_target_value(stock, 0)
sell_stocks.append(stock)
if context.now.month != context.month:
sel_stock = []
for code in context.data_list['ts_code']:
# 剔除ST和停牌股票
if not ts.is_st(code) and not ts.get_realtime_quotes(code)['paused'][0]:
# 上市时间大于1年且当日成交额大于1000万
df = pro.stock_basic(exchange='', list_status='L', fields='ts_code,list_date') # 获取股票基础信息
list_date = df[df['ts_code'] == code]['list_date']
if len(list_date) > 0:
if (datetime.now() - datetime.strptime(list_date.values[0], '%Y%m%d')).days > 365 and ts.pro_api().daily(ts_code=code, trade_date=datetime.now().strftime('%Y%m%d'))['amount'][0] > 10000000:
sel_stock.append(code)
# 获取前4个月股票收盘价并求出收益率
pct_stock = pd.DataFrame(index=pd.date_range(start=(datetime.now() - pd.DateOffset(months=context.last_month+1)).strftime('%Y-%m-%d'), end=datetime.now().strftime('%Y-%m-%d')), columns=sel_stock)
for code in sel_stock:
while True:
try:
data = pro.daily(ts_code=code, start_date=(datetime.now() - pd.DateOffset(months=context.last_month+1)).strftime('%Y%m%d'), end_date=datetime.now().strftime('%Y%m%d'))[::-1]
break
except Exception as e:
time.sleep(61) # 暂停 61 秒后重试
pct_stock[code].loc[data['trade_date']] = data['close'].pct_change()
# 计算股票之间的相关系数
corr_stock = pct_stock.corr().unstack().reset_index(name='corr')
corr_stock = corr_stock[corr_stock['corr'] != 1]
corr_stock['codes'] = corr_stock.apply(lambda x: sorted([x['level_0'], x['level_1']]), axis=1)
corr_stock = corr_stock[['codes', 'corr']]
corr_stock = corr_stock.drop_duplicates(subset='codes', keep='last')
# 取当月行业股票对前m个相关系数求和,对前h个月行业股票对前m个相关系数求和并对月求平均
corr_stock = corr_stock.sort_values('corr', ascending=False).head(context.max_corr)
the_f = corr_stock['corr'].sum()
sum_stock = []
for i in range(context.last_month):
last_pct_stock = pct_stock.iloc[-20*(i+1):-20*i,:].corr().unstack().reset_index(name='corr')
last_pct_stock = last_pct_stock[last_pct_stock['corr'] != 1]
last_pct_stock['codes'] = last_pct_stock.apply(lambda x: sorted([x['level_0'], x['level_1']]), axis=1)
last_pct_stock = last_pct_stock[['codes', 'corr']]
last_pct_stock = last_pct_stock.drop_duplicates(subset='codes', keep='last')
sum_stock.append(last_pct_stock['corr'].sort_values(ascending=False).head(context.max_corr).sum())
avg = sum(sum_stock) / context.last_month
# 求出行业联动异动因子
v = (the_f - avg) / avg
# 以行业联动异动因子,行业代码,行业前m个股票对代码三个数据进行列表存储
v_list = [[v, code, corr_stock[corr_stock['codes'].apply(lambda x: code in x)].sort_values('corr', ascending=False)['codes']] for code in context.data_list['industry'].unique() if code != '银行']
# 排序行业联动异动指标
v_list.sort(key=lambda x: x[0], reverse=True)
buy_list = []
# 取前n个行业的前k个股票对中的股票作为备选股票池
for i in range(context.max_v):
res = v_list[i][2]
for j in range(min(context.max_buy, len(res))):
buy_list += res[j]
buy_list = list(set(buy_list))
# 卖出上月买入的所有股票
for stock in positions:
order_target_value(stock, 0)
# 根据备选股票池的买入股票名单等权买入股票
buy_len = len(buy_list)
if buy_len > 0:
for i in buy_list:
order_target_percent(i, 1/buy_len)
context.not_sell = buy_list
context.month = context.now.month
def run(context, start_date, end_date):
# 获取股票行情数据
ts.set_token("")
pro = ts.pro_api()
df = pro.query('stock_basic', exchange='', list_status='L', fields='ts_code')
codes = df['ts_code'].tolist()
# 回测时间范围
dates = pd.date_range(start=start_date, end=end_date, freq='B')
# 以日期为索引,创建 DataFrame 存储股票每日交易信息
stock_data = pd.DataFrame(index=dates, columns=codes)
for code in codes:
stock_data[code] = pro.daily(ts_code=code, start_date=start_date, end_date=end_date)['close']
time.sleep(1) # 暂停 1 秒避免超过最大访问次数限制
# 开始回测
for date, row in stock_data.iterrows():
handle_bar(context, row)
if context.now.minute % 2 == 0 and context.now.second < 30: # 每分钟最多访问800次
while True:
try:
pro.daily(ts_code=context.sl, start_date=date.strftime('%Y%m%d'), end_date=date.strftime('%Y%m%d'))
break
except Exception as e:
time.sleep(61) # 暂停 61 秒后重试
time.sleep(75) # 暂停 75 秒避免超过最大访问次数限制
if __name__ == '__main__':
start_date = '2023-01-01'
end_date = '2023-05-31'
# 初始化context
context = type('', (), {})()
context.sl = '000001.SH'
run(context, start_date, end_date)