右侧追击(二)——券商行业
在上文中用东方财富的历史数据,回测了右侧出击策略的表现。资金占用时间不到一年半,对应40%的收益,表现基本让人满意。不过仅凭一只股票的收益表现,偶然性太高,很难说明策略的可靠度。
本次我们拿证券公司(399975)成分股中的所有证券公司,一共49家来代表券商行业、再选定2012-01-01至今作为时间区间来做数据回测,基本上就能评判出策略在券商行业中的综合实力。
1.股票代码预览
首先需要知道,一共是哪些券商公司,它们的代码分别是多少?写了个小爬虫程序,在某行情网站中抓取到了我们的目标券商公司股票代码,然后保存到本地csv文件(爬虫过程及源码这里不做展示)。
先将所有券商的代码及公司读取出来,如下:
# 导入相关库
import akshare as ak
import pandas as pd
import quan_trade # 交易类
import time# 从本地读取所有券商股票代码
dealer_df = pd.read_csv('dealer_data/symbols.csv',dtype = {'symbol':str})
# 展示前5条
dealer_df.head()
2. 函数封装
在右侧追击(一)——东方财富中我们对单只股票每次追击情况进行了统计。本次涉及到49支股票,还需要将每只股票的多次追击详情汇总。
定义一个函数,需传入的参数有order——订单记录,symbol——股票代码,begin_date——该股票具备行情数据的起始日期,capital——本金(每次买卖为本金的1/5,默认值为100000)。在函数内部计算出两个DataFrame数据返回,profit_df记录单只股票每次追击的情况,summary_df对该股票多次追击详情进行汇总,代码如下:
def get_profit(order,symbol,begin_date,capital = 100000):# 创建profit_df用来存放每次追击的情况, summary_df存放每只股票的追击汇总profit_df = pd.DataFrame(columns = ['symbol','start_time','end_time', 'capital', 'attack_times', 'profit', 'profit_rate','total_profit_rate'])summary_df = pd.DataFrame(columns = ['跟踪起始时间','代码','公司','本金','总追击次数','成功次数','追击成功率','单次最大亏损','单次最大盈利','总利润','资金占用时间/天','折合年化'])# 订单记录为空,直接返回if order.empty:print(symbol, ": 订单记录为空")return profit_df,summary_df# 初始化部分数据start_time = Noneattack_times = 0profit = 0amount = 0 # 记录每次追击累积交易的数量# 遍历订单记录,计算每次追击后的数据,存放到profit_dffor i in range(0,order.shape[0]):if order.iloc[i]['trade_type'] == 'B':# 记录每次追击开始的时间if attack_times == 0:start_time = order['date'].iloc[i]#计算累计追击次数以及累计买入数量attack_times += 1amount += capital / 5 / order.iloc[i]['trade_price']else :# 数据存放profit_df = profit_df.append({'symbol':symbol,'start_time':start_time,'end_time':order['date'].iloc[i], 'capital':capital,'attack_times':attack_times, 'profit': order.iloc[i]['trade_price'] * amount - capital / 5 * attack_times, 'profit_rate': (order.iloc[i]['trade_price'] * amount /(capital / 5 * attack_times) - 1) * 100,'total_profit_rate':(order.iloc[i]['trade_price'] * amount - capital / 5 * attack_times) / capital * 100},ignore_index=True)# 卖出意味着本次追击结束,清零attack_times = amount = 0 ### 追击详情汇总summary_df = pd.DataFrame({'跟踪起始时间':begin_date,'代码':symbol,'公司': dealer_df[dealer_df['symbol'] == symbol].company,'本金': capital,'总追击次数' : profit_df['attack_times'].count(),'成功次数': profit_df[profit_df['attack_times'] == 5].attack_times.count(),'追击成功率':profit_df[profit_df['attack_times'] == 5].attack_times.count() / profit_df['attack_times'].count() * 100,'单次最大亏损':profit_df['profit'].min(),'单次最大盈利':profit_df['profit'].max(),'总利润':profit_df['profit'].sum(),'资金占用时间/天':(profit_df['end_time'] - profit_df['start_time']).dt.days.sum() + profit_df.start_time.count(),'折合年化':((profit_df['profit'].sum() /capital + 1) ** (365 / ((profit_df['end_time'] - profit_df['start_time']).dt.days.sum() + profit_df.start_time.count())) -1) * 100})return profit_df, summary_df
函数get_profit的使用,拿东方财富为例:
df = ak.stock_zh_a_hist(symbol='300059', period="daily", start_date="20120101", end_date='20221231', adjust="qfq")
trade = quan_trade.Trade('300059', df, 'right_attack', 100 , 1000000, max_volatility = 10)
trade.main()
profit_df, summary_df = get_profit(trade.account.order, '300059', trade.data.iloc[0].日期)
# 查看每次追击情况
profit_df
# 查看汇总
summary_df
profit_df几乎就是我们在上一篇中所用到的数据,记录了对应股票每次追击情况;summary_df是对profit_df中数据的汇总,里边还计算出了对应股票右侧追击的折合年化回报。
3.计算每只股票的追击汇总
将49支股票的追击详情及汇总分别放到一个DateFrame中,基本上能看出来策略在券商行业中的整体表现了
# 记录程序运行开始时间
start = time.time()
# 定义变量存放所有股票的追击详情以及每只股票的追击汇总
dealer_profit_df = pd.DataFrame()
dealer_total_profit_df = pd.DataFrame()
# 遍历dealer_df取出所有股票代码
for symbol in dealer_df['symbol']:# 回测区间范围设定从2012年开始,截止到现在df = ak.stock_zh_a_hist(symbol= symbol, period="daily", start_date="20120101", end_date='20221231', adjust="qfq")trade = quan_trade.Trade(symbol, df, 'right_attack', 100 , 1000000, max_volatility = 10)trade.main()profit_df, summary_df = get_profit(trade.account.order, symbol, trade.data.iloc[0].日期)# 将每只股票追击详情及汇总分别添加到dealer_profit_df,dealer_total_profit_dfdealer_profit_df = dealer_profit_df.append(profit_df)dealer_total_profit_df = dealer_total_profit_df.append(summary_df)print("程序运行时间/min: ",(time.time() - start)/60 )
上面的代码涉及到一定数量的运算,会耗费一点时间。一共有6支股票没有订单记录,股票代码打印在了上面。也就是说在它们上面没有找到任何参与机会。经过简单的分析就可以知道,这些股票都是在最近两三年内上市,策略还需要有大半年的缓冲期(需跟踪180个交易日内的最高价),跟踪周期较短,没有参与机会也很正常。
# 查看所有股票的追击详情
dealer_profit_df
# 查看所有股票的追击汇总前5条
dealer_total_profit_df.head()
策略从2012-01-01开始追踪,其后的第一个交易日是2012-01-04,但dealer_total_profit_df中的起始跟踪时间有一些会晚于这个时间,那是因为它们直到这个起始跟踪时间点才上市挂牌交易。
根据追击汇总dealer_total_profit_df计算策略在券商行业中的平均收益
# 对所有股票追击汇总再汇总
avg_profit_df = pd.DataFrame({'参与股票数量':dealer_total_profit_df.shape[0],'盈利数量':dealer_total_profit_df[dealer_total_profit_df['总利润'] > 0].shape[0],'亏损数量':dealer_total_profit_df[dealer_total_profit_df['总利润'] <= 0].shape[0],'追击成功率':100 * dealer_total_profit_df['成功次数'].sum() / dealer_total_profit_df['总追击次数'].sum(),'单支股票最大利润':dealer_total_profit_df['总利润'].max(),'单支股票最大亏损':dealer_total_profit_df['总利润'].min(),'平均收益':dealer_total_profit_df['总利润'].mean(),'平均年化回报':dealer_total_profit_df['折合年化'].mean()
},index = [0])
avg_profit_df
4.结论分析
收益总览
一共参与了43支股票,追击成功率8.63%,平均收益2.8w,对应平均年化回报49.42%,整体还不错。
其中盈利的有25支,取得最大利润的是光大证券,18.8w。该股票追击了7次,就有2次追击成功。28%的成功率极大的拉高了收益,按171天的资金占用时间来计算,折合年化高达856.62%。
亏损数量为18支,踩坑的概率还是有一些的。创造最大亏损是国投资本,超过了1.8w。8次追击,0次成功。对应10w的本金,摩擦成本高达18%,风险也不容小觑。
利润周期
# 摘取追击成功的追击详情
dealer_profit_df[dealer_profit_df['attack_times'] == 5].sort_values(by='start_time')
看利润分布,绝大部分的收益都是2014第4季度创造的,追击成功的时间区间大部分也集中于此。
翻看证券公司(399975)的周线图,在我们跟踪的十年期间,2014年尾部也是其表现最为疯狂的时期,从10月份600点左右一路飙升,到12月下旬时涨到了第一个高点1500点左右,这一波所有股票加在一起吃到了接近120万的利润,极大的拉高了平均收益。随后在2015年2季度再创新高,同样也吃到了一小部分利润,相较而言其它时间段都没有特别突出的表现。
再往前看,2007年有类似行情,如果策略跟踪到2007年,收益应该还能再创辉煌。这里单纯意淫一下,2007年中到2014下旬花了7年多的时间,2014年底到现在也是7年多,券商行业是否又在酝酿一次大腾飞呢?
亏损分析
# 查看所有亏损股票的追击总览
dealer_total_profit_df[dealer_total_profit_df['总利润'] <= 0]
18支亏损的股票中,它们的追击成功次数全部为0。换句话说,只要有一次追击成功,没有一只股票是亏损的。
不过也并非所有成功次数为0的股票,都会亏损。比如国信、西南、哈投,它们都创造了正收益。其实只要策略在开启追击后,能连续买到第3、4次,就有很大机会创造正收益!
# 追击成功次数为0且利润为正的股票记录
dealer_total_profit_df[dealer_total_profit_df['总利润'] > 0 ][dealer_total_profit_df['成功次数'] == 0]
另外可以看到,所有亏损的股票中,除了国投和国海,其它的上市时间都晚于2014年,错过了券商行业最疯狂的黄金时段。乐观的看,如果未来再有这么一波强势行情,它们中的绝大部分是不是都能收回摩擦成本,并跑出可观的正收益?这个有待时间给我们答案!
# 国海证券的每次追击详情
dealer_profit_df[dealer_profit_df['symbol'] == '000750']
# 国投证券的每次追击详情
dealer_profit_df[dealer_profit_df['symbol'] == '600061']
国海和国投对于策略而言,彻底就是个坑了。即便在2014年底遍地捡黄金的时代,二者也没有完成一次完整的追击。
所以即便是券商这种反身性行业、严格遵守右侧追击制度、在整个行业疯狂上涨的3层buff加持下,追击个股也有踩坑的风险!国海和国投就是血淋淋的教训,把资金丢到它们身上,就等同于从枪林弹雨般的黄金中完美的躲了过去!
总结
-
平均年化回报达到49.42%,整体表现不错;
-
追击成功率8.63%,策略有待优化提高成功率;
-
绝大部分利润都贡献于2014年年底的行情,右侧追击博的就是这种机会;
-
虽然券商这种行业适合右侧追击,但追击个股依然有踩坑的风险。