处理持仓中的"雷"股
由iquant创建,最终由iquant 被浏览 61 用户
导语
通过数据过滤我们可以在预测的时候避开ST股和退市股,但如果很不幸我们的买入持仓中有股票变成了ST股或者退市股时,我们应该如何快速卖出逃脱呢?本节我们就聊聊如何处理持仓中的“雷”股。
我们知道,模板的策略逻辑是卖出每日预测排序靠后的股票。那么尝试思考这样一个场景:某个持仓的股票突然发布公告启动ST或者退市流程,好股变成了“”雷“”股。但是很可能我们的排序预测模型始终意识不到这个雷,而导致此股的打分排序始终不是靠后的。这会导致这些烫手的山芋无法脱手,自爆仓中。不仅导致策略无法卖出此股,还会因其占用了资金而无法买入新的股票。
因此,我们在每天的交易逻辑前加入“雷股判断”,一旦发现持仓的股票中有雷就赶快卖出处理掉。
处理“”雷“的步骤
股票名称获取与数据合并
第一步: 我们通过增加一个输入特征和数据抽取模块获取每日股票的名称字段name,然后传入回测模块。
在初始化函数中读取传入的股票名称数据
context.options_data = context.options['data'].read()
在K线处理函数中将股票名称与预测结果进行合并
import pandas as pd
# 按日期过滤得到今日的预测数据
ranker_prediction = context.data[
context.data.date == data.current_dt.strftime("%Y-%m-%d")
]
# 获得股票名称数据
stock_name = context.options_data[context.options_data["date"] == data.current_dt.strftime("%Y-%m-%d")]
ranker_prediction = pd.merge(ranker_prediction, stock_name, on=['date','instrument'], how='left')
匹配字段”ST“或者“退”
第二步:我们在主函数中增加一段代码来判断所有持仓的股票名称中是否含有”ST“或者“退”字,如果含有则立即卖出,并记录在stock_sold列表中。
点击查看卖出持仓雷股逻辑代码
# 2. ST股和退市股的卖出
stock_sold = [] # 记录卖出的股票,防止多次卖出出现空单
#-------------------------- START: ST和退市股卖出 ---------------------
st_stock_list = []
for instrument in positions.keys():
try:
instrument_name = ranker_prediction[ranker_prediction.instrument==instrument].name.values[0]
# 如果股票状态变为了st或者退市 则卖出
if 'ST' in instrument_name or '退' in instrument_name:
if instrument in stock_sold:
continue
if data.can_trade(context.symbol(instrument)):
context.order_target(context.symbol(instrument), 0)
st_stock_list.append(instrument)
cash_for_sell -= positions[instrument]
except:
continue
if st_stock_list!=[]:
print(today,'持仓出现st股/退市股',st_stock_list,'进行卖出处理')
stock_sold += st_stock_list
#-------------------------- END: ST和退市股卖出 ---------------------
轮仓卖出逻辑的修改
第三步:为了避免在轮仓再次卖出ST股票导致空单,在轮仓卖出逻辑中判断如果已经卖过的股票就跳过,同时我们对卖出的股票可以同样记录到stock_sold列表中防止再次买入。
轮仓卖出逻辑的修改
# 3. 生成轮仓卖出订单:hold_days天之后才开始卖出;对持仓的股票,按机器学习算法预测的排序末位淘汰
if not is_staging and cash_for_sell > 0:
equities = {e: e for e, p in context.portfolio.positions.items()}
instruments = list(
reversed(
list(
ranker_prediction.instrument[
ranker_prediction.instrument.apply(lambda x: x in equities)
]
)
)
)
for instrument in instruments:
# 如果资金够了就不卖出了
if cash_for_sell <= 0:
break
# 防止多个止损条件同时满足,出现多次卖出产生空单
if instrument in stock_sold:
continue
context.order_target(instrument, 0)
cash_for_sell -= positions[instrument]
stock_sold.append(instrument)
轮仓买入逻辑的修改
第四步:在轮仓买入的时候,我们在排序股票列表中的结果中过滤掉名称中含有“ST”或者“退”的股票。我们也可以根据需要,同时过滤掉已经卖出的股票列表。
轮仓买入逻辑的修改
# 4. 生成买入订单:按机器学习算法预测的排序, 买入前面的stock_count只股票
# 计算今日ST/退市的股票
st_list = list(ranker_prediction[ranker_prediction.name.str.contains('ST')|ranker_prediction.name.str.contains('退')].instrument)
# 计算所有禁止买入的股票池
banned_list = stock_sold+st_list
buy_cash_weights = context.stock_weights
buy_instruments=[k for k in list(ranker_prediction.instrument) if k not in banned_list][:len(buy_cash_weights)]
max_cash_per_instrument = (
context.portfolio.portfolio_value * context.max_cash_per_instrument
)
for i, instrument in enumerate(buy_instruments):
cash = cash_for_buy * buy_cash_weights[i]
if cash > max_cash_per_instrument - positions.get(instrument, 0):
# 确保股票持仓量不会超过每次股票最大的占用资金量
cash = max_cash_per_instrument - positions.get(instrument, 0)
if cash > 0:
context.order_value(instrument, cash)
案例策略
https://bigquant.com/codesharev2/f4ed5041-597d-4ac4-8782-cf596f8ca2f9
\