给自己搭个量化投资系统之六——基于传染病模型的新高买入策略探索

举报
darkpard 发表于 2022/05/23 19:12:42 2022/05/23
【摘要】 2020年11月28日,周教授情报中心原创发布了《关注这一类标的!》,提出了一个简单的量化思路。具体如下:周教授指出这个策略从2020年第一个交易日到2020年10月30日的回测收益率为64.28%。今天,我们来探索一下,这一类新高策略在当前这种变态的环境下是否仍有一定的超额收益。1. 明确几个问题1.1. 关于买入条件周教授的买入条件实际上是上市120交易日以上,创出历史新高的股票。这里有...

2020年11月28日,周教授情报中心原创发布了《关注这一类标的!》,提出了一个简单的量化思路。具体如下:

图片

周教授指出这个策略从2020年第一个交易日到2020年10月30日的回测收益率为64.28%。

今天,我们来探索一下,这一类新高策略在当前这种变态的环境下是否仍有一定的超额收益。

1. 明确几个问题

1.1. 关于买入条件

周教授的买入条件实际上是上市120交易日以上,创出历史新高的股票。这里有一个拍脑袋的参数,120;还有一个隐藏的参数,历史新高,它也可以是252天新高或60天新高等等。

1.2. 关于仓位分布

周教授假设仓位是每天滚动在所有符合条件的股票之间平均分布。这一条实际上是做不到的。为了便于后续将新高策略纳入我的量化投资系统,我这里假设每个股票的买入都有一个限额。比如,可以假设每次买入总额不少于10000元、不超过20000元。

1.3. 回测时间

同样遵循我目前用的时间框架,只回测最近一年。

1.4. 初始金额

假设初始金额为150000。

2. 股票池及传递规则

这个量化不同于现在很火的机器学习模型,这里其实有点类似于传染病模型,所以我们需要建立几个股票池,类似传染病模型里的不同人群:正常人群、易感人群、感染人群、康复人群等。同时,我们需要定义不同池子之间的传导规则,当然这里的传导规则跟传染病模型几乎是完全不一样的。

2.1. 未持有股票池(正常人群)

这里,初始的未持有股票池就是所有A股。它的流出是满足买入条件的股票;它的流入是新股的发行,以及符合卖出条件的股票。

2.2. 应该持有股票池(感染人群)

应该要持有的股票的集合作为应该持有股票池。它的流出是满足卖出条件的股票,它们将流入到未持有股票池;流入是满足买入条件的未持有股票,它们将从未持有股票池流入到本池。

2.3. 可用资金池

即模拟可用资金,这里假设初始资金是150000元。它的流出是买入股票,它的流入是卖出股票。同时,买入和卖出需要支付一定的手续费。

2.4. 实际持有股票池(治疗人群)

实际持有的股票的集合作为实际持有股票池,它是应该持有股票池的一部分。这是因为实际投资时,资金是有限的,并不是所有应该持有的股票都有资金买入。它的流出跟应该持有股票池是一样的,由于它的流向也跟应该持有股票池重合,所以不用重复计量。它的流入是满足买入条件和可用资金需求的未持有股票,当资金不足以买入所有股票时,这里按股票代码顺序排列。

3. 代码实际

3.1. 明确交易日历

这里我们以最近一年为回测周期。

import data_operate as do

trade_dates = do.sql2pd("select format(trade_date, '%Y-%m-%d') from SSEC where trade_date>20210508 order by trade_date")
print(trade_dates)

图片

显然,这个日期数据还需要一点小处理

trade_dates[0] = trade_dates[0].apply(lambda x: x.replace(',', '')[:8])

3.2. 未持有股票池的实现

nonhold_stocks = do.sql2pd("select ts_code from daily where trade_date=%d" % trade_dates[0][0])

3.3. 应该持有股票池的实现

应该持有股票池的初始化

should_hold_stocks = pd.DataFrame(columns=[0, 1, 2])

应该持有股票池的流入

    temp_stocks = do.sql2pd("select a.ts_code, a.high, a.trade_date from daily as a inner join (select ts_code, max(high) as mh from daily where trade_date<%d group by  ts_code having count(high)>120) as b on a.ts_code=b.ts_code where a.high>b.mh and a.trade_date=%d" % (int(trade_date), int(trade_date)))
    temp_stocks = temp_stocks[~ temp_stocks[0].isin(should_hold_stocks[0].to_list())]
    should_hold_stocks = should_hold_stocks.append(temp_stocks).reset_index()

应该持有股票池的流出

    i = 0
    while i in should_hold_stocks.index:
        low = do.sql2pd("select low from daily where trade_date=%d and ts_code='%s'" % (int(trade_date), should_hold_stocks[0][i]))
        if len(low):
            if low[0][0] / should_hold_stocks[1][i] < 0.9:
                should_hold_stocks.drop(i, inplace=True)
                continue
        last5th = do.sql2pd("select trade_date, high from daily where ts_code='%s' and trade_date<%d order by trade_date desc limit 4, 1" % (int(trade_date), should_hold_stocks[0][i]))
            if should_hold_stocks[2][i] <= last5th[0][0]:
                if last5th[1][0] >= do.sql2pd("select max(high) from (select high from daily where trade_date<=%d and ts_code='%s' order by trade_date desc limit 0, 5) as a" % (int(trade_date), should_hold_stocks[0][i]))[0][0]:
                    should_hold_stocks.drop(i, inplace=True)
                    continue
        i += 1

3.4. 资金池与实际持有股票池的实现

先是资金池和实际持有股票池的初始化

hold_stocks = pd.DataFrame(columns=[0, 1, 2, 3])
currency = 150000

判断资金是否充足,并尝试买入

def buy_stocks(currency, should_buy_stocks, hold_stocks):
    for i in should_buy_stocks.index:
        if should_buy_stocks[1][i] < price_limit:
            buy_num = buy_limit // should_buy_stocks[1][i] + 1
            buy_amount = buy_num * 100 * should_buy_stocks[1][i] * 1.0001
            if currency >= buy_amount:
                currency -= buy_amount
                hold_stocks.loc[len(hold_stocks), ] = should_buy_stocks.loc[i, ] + [buy_num, ]
    return currency, hold_stocks
                
currency, hold_stocks = buy_stocks(currency, should_hold_stocks, hold_stocks)

currency, hold_stocks = buy_stocks(currency, temp_stocks, hold_stocks)

卖出股票

def sale_stocks(currency, ts_code, price, hold_stocks):
    hold_i = hold_stocks[hold_stocks[0] == ts_code].index[0]
    currency += hold_stocks[3][hold_i] * price * 100 * 0.9999
    hold_stocks.drop(hold_i, inplace=True)
    return currency, hold_stocks
    
currency, hold_stocks = sale_stocks(currency, should_hold_stocks[0][i], low[0][0], hold_stocks)

currency, hold_stocks = sale_stocks(currency, should_hold_stocks[0][i], current_price, hold_stocks)

4. 总结

经过一年的回测,发现如果是最近一年的话效果非常差,150000元初始资金,一年后仅留下了39602元,收益率是-74%。

我们可以看一些案例,有些买入就开始亏(K线图画法可参见给自己搭个量化投资系统之三——用pthon画K线图

图片图片

有些是先赚,最后又亏了

图片图片图片

我们也可以尝试一些不同的参数

比如把最低买入量改成5万元,结果一年后只剩下36817元,跟前面的结果差不多。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。