热点复现|用Dash做A股量化策略可视

热点复现|用Dash做A股量化策略可视

近几个月来,用Dash做各种数据展示的案例频繁出现在公众号上,不禁想把业余做的A股量化策略研究也做个展示。

1. 数据获取

A股数据可以通过tushare获取。

1.1. 安装tushare包:

!pip install tushare

1.2. 导入tushare包,并初始化

import tushare as ts
pro = ts.pro_api('your token')

token可以通过注册tushare账号获取。

1.3. 获取行情数据

日线行情数据可以通过daily接口获取。

daily = pro.daily()
daily

由于这两句代码在ModelArts平台上未能通过调试,实现将在jupyter上进行。调试失败信息是“JSONDecodeError: Expecting ',' delimiter: line 1 column 86204 (char 86203)”,还请ModelArts的专家能关注一下。

1.4. 获取财务数据

利润表数据可以通过income接口获取。

daily = pro.income()
daily

1.5. 获取其他数据

其他数据可以根据策略需要通过不同接口获取,具体接口与参数可以到tushare管网查询。

2. 量化策略

量化策略的关键是决定在什么情况下买,什么情况下卖?是拍脑袋决定,还是通过数据和模型来决定。
简单起见,我们拍脑袋决定一个简单的量化策略,当然这不太可能是一个成功的策略。

2.1. 基本设定

假定其实金额100万元人民币,每个交易日以开盘价买入上一交易日成交额最大的股票,三天后卖出。买入数量为不超过且尽可能逼近当前总资产的1/4,总资产的计算不考虑浮动盈亏。回测期间为2020年1月1日-2020年12月31日。

start_amount = 1000000

2.2. 获取交易日历

trade_date = pro.trade_cal(start_date=20200101, end_date=20201231, is_open=1)
trade_date

2.3. 初始化

回测第一天不发生交易,为了方便,我们把它计为第0天。交易日期如下:

current_date = trade_date.loc[0, 'cal_date']
current_date

总资产为初始总资产,货币资金为总资产

amount = {}
currency_amount = {}
amount[current_date] = currency_amount[current_date] = start_amount

初始化交易数据

import pandas as pd
trade_data = pd.DataFrame(columns=['ts_code', 'buy_date', 'buy_price', 'buy_num', 'hold_days', 'sale_date', 'sale_price'])
trade_data

2.4. 第一天买卖

记录上一交易日

pre_date = current_date
pre_date

获得当前交易日

current_date = trade_date.loc[1, 'cal_date']
current_date

获得上一交易日的成交量信息

pre_daily = pro.daily(trade_date = pre_date, fields='ts_code, amount')
pre_daily

获取上一交易日成交量最大的股票。

ts_code = pre_daily[pre_daily['amount']==pre_daily.amount.max()].iloc[0, 0]
ts_code

获取本日开盘价

buy_price = pro.daily(trade_date = current_date, ts_code = ts_code).iloc[0, 2]
buy_price

计算买入数量

buy_amount_limit = min(amount[pre_date] / 4, currency_amount[pre_date])
buy_num = buy_amount_limit // (buy_price * 100)
buy_num

写入交易数据

trade_data.loc[len(trade_data),['ts_code', 'buy_date', 'buy_price', 'buy_num', 'hold_days', 'sale_price']] = [ts_code, current_date, buy_price, buy_num, 0, 0]
trade_data

改写货币资金和总资产

currency_amount[current_date] = currency_amount[pre_date] - buy_price * buy_num * 100
amount[current_date] = amount[pre_date]
currency_amount

2.5. 第二天买卖

记录上一交易日,获得当前交易日

pre_date = current_date
current_date = trade_date.loc[2, 'cal_date']

第一天买入的股票持有天数加1

trade_data.loc[trade_data['hold_days']<3, 'hold_days'] += 1
trade_data

重复第一天的买入过程

pre_daily = pro.daily(trade_date = pre_date, fields='ts_code, amount')
ts_code = pre_daily[pre_daily['amount']==pre_daily.amount.max()].iloc[0, 0]
buy_price = pro.daily(trade_date = current_date, ts_code = ts_code).iloc[0, 2]
buy_amount_limit = min(amount[pre_date] / 4, currency_amount[pre_date])
print(buy_amount_limit)
buy_num = buy_amount_limit // (buy_price * 100)
trade_data.loc[len(trade_data),['ts_code', 'buy_date', 'buy_price', 'buy_num', 'hold_days', 'sale_price']] = [ts_code, current_date, buy_price, buy_num, 0, 0]
trade_data

改写货币资金和总资产

currency_amount[current_date] = currency_amount[pre_date] - buy_price * buy_num * 100
amount[current_date] = amount[pre_date]
currency_amount

2.6. 第三天买卖

重复第二天的操作

pre_date = current_date
current_date = trade_date.loc[3, 'cal_date']
trade_data.loc[trade_data['hold_days']<3, 'hold_days'] += 1
pre_daily = pro.daily(trade_date = pre_date, fields='ts_code, amount')
ts_code = pre_daily[pre_daily['amount']==pre_daily.amount.max()].iloc[0, 0]
buy_price = pro.daily(trade_date = current_date, ts_code = ts_code).iloc[0, 2]
buy_amount_limit = min(amount[pre_date] / 4, currency_amount[pre_date])
buy_num = buy_amount_limit // (buy_price * 100)
trade_data.loc[len(trade_data),['ts_code', 'buy_date', 'buy_price', 'buy_num', 'hold_days', 'sale_price']] = [ts_code, current_date, buy_price, buy_num, 0, 0]
currency_amount[current_date] = currency_amount[pre_date] - buy_price * buy_num * 100
amount[current_date] = amount[pre_date]
trade_data

2.7. 第四天买卖

重复第三天的操作

pre_date = current_date
current_date = trade_date.loc[4, 'cal_date']
trade_data.loc[trade_data['hold_days']<3, 'hold_days'] += 1
pre_daily = pro.daily(trade_date = pre_date, fields='ts_code, amount')
ts_code = pre_daily[pre_daily['amount']==pre_daily.amount.max()].iloc[0, 0]
buy_price = pro.daily(trade_date = current_date, ts_code = ts_code).iloc[0, 2]
buy_amount_limit = min(amount[pre_date] / 4, currency_amount[pre_date])
buy_num = buy_amount_limit // (buy_price * 100)
trade_data.loc[len(trade_data),['ts_code', 'buy_date', 'buy_price', 'buy_num', 'hold_days', 'sale_price']] = [ts_code, current_date, buy_price, buy_num, 0, 0]
currency_amount[current_date] = currency_amount[pre_date] - buy_price * buy_num * 100
amount[current_date] = amount[pre_date]
trade_data

2.8. 第五天买卖

记录上一个交易日并获取当前交易日

pre_date = current_date
current_date = trade_date.loc[5, 'cal_date']

找到持有达到3天的股票以及持有数量

trade_index = ((trade_data['hold_days']==3) & (trade_data['sale_price']==0))
ts_code = trade_data.loc[trade_index, 'ts_code'][0]
sale_num = trade_data.loc[trade_index, 'buy_num'][0]
orignal_buy_price = trade_data.loc[trade_index, 'buy_price'][0]
ts_code, sale_num

获取当天收盘价

sale_price = pro.daily(trade_date = current_date, ts_code = ts_code).loc[0, 'close']
sale_price

写入交易数据

trade_data.loc[trade_index, ['sale_date', 'sale_price']] = [current_date, sale_price]
trade_data

重复上一天的操作

trade_data.loc[trade_data['hold_days']<3, 'hold_days'] += 1
pre_daily = pro.daily(trade_date = pre_date, fields='ts_code, amount')
ts_code = pre_daily[pre_daily['amount']==pre_daily.amount.max()].iloc[0, 0]
buy_price = pro.daily(trade_date = current_date, ts_code = ts_code).iloc[0, 2]
buy_amount_limit = min(amount[pre_date] / 4, currency_amount[pre_date])
buy_num = buy_amount_limit // (buy_price * 100)
trade_data.loc[len(trade_data),['ts_code', 'buy_date', 'buy_price', 'buy_num', 'hold_days', 'sale_price']] = [ts_code, current_date, buy_price, buy_num, 0, 0]
trade_data

改写货币资金和总资产

currency_amount[current_date] = currency_amount[pre_date] - buy_price * buy_num * 100 + sale_num * sale_price * 100
amount[current_date] = amount[pre_date] + (sale_price - orignal_buy_price) * sale_num * 100
currency_amount, amount

2.9. 完整代码

易见,第1天-第4天是第5天的特殊情况。本策略的完整代码如下:

import pandas as pd
import tushare as ts
pro = ts.pro_api('your token')

trade_date = pro.trade_cal(start_date=20200101, end_date=20201231, is_open=1)
amount = {}
currency_amount = {}
trade_data = pd.DataFrame(columns=['ts_code', 'buy_date', 'buy_price', 'buy_num', 'hold_days', 'sale_date', 'sale_price'])
pre_date = trade_date.loc[0, 'cal_date']
amount[pre_date] = currency_amount[pre_date] = 1000000
for current_date in trade_date.loc[1:, 'cal_date']:
    trade_index = ((trade_data['hold_days']==3) & (trade_data['sale_price']==0))
    sale_num = sale_price = orignal_buy_price = 0
    if len(trade_index) and max(trade_index):
        ts_code = list(trade_data.loc[trade_index, 'ts_code'])[0]
        sale_num = list(trade_data.loc[trade_index, 'buy_num'])[0]
        orignal_buy_price = list(trade_data.loc[trade_index, 'buy_price'])[0]
        sale_price = pro.daily(trade_date = current_date, ts_code = ts_code).loc[0, 'close']
        trade_data.loc[trade_index, ['sale_date', 'sale_price']] = [current_date, sale_price]
    trade_data.loc[trade_data['hold_days']<3, 'hold_days'] += 1
    pre_daily = pro.daily(trade_date = pre_date, fields='ts_code, amount')
    ts_code = pre_daily[pre_daily['amount']==pre_daily.amount.max()].iloc[0, 0]
    buy_price = pro.daily(trade_date = current_date, ts_code = ts_code).iloc[0, 2]
    buy_amount_limit = min(amount[pre_date] / 4, currency_amount[pre_date])
    buy_num = buy_amount_limit // (buy_price * 100)
    trade_data.loc[len(trade_data),['ts_code', 'buy_date', 'buy_price', 'buy_num', 'hold_days', 'sale_price']] = [ts_code, current_date, buy_price, buy_num, 0, 0]
    currency_amount[current_date] = currency_amount[pre_date] - buy_price * buy_num * 100 + sale_num * sale_price * 100
    amount[current_date] = amount[pre_date] + (sale_price - orignal_buy_price) * sale_num * 100
trade_data

3. Dash可视

安装Dash和Dash的html组件和核心组件

!pip install dash
!pip install dash_html_components
!pip install dash_core_components

先来显示个标题

import dash
import dash_html_components as html
app = dash.Dash()
app.layout = html.Div([
    html.H2(children='一个量化策略',
            style = dict(backgroundColor = '#111111', textAlign = 'center', color = '#7FDBFF')),
    ])
if __name__ == '__main__':
    app.run_server(host="0.0.0.0")

运行后打开浏览器,访问http://localhost:8050/,可以看到 显示前面量化策略的效果

import dash, datetime
import dash_html_components as html
import dash_core_components as dcc

def numtodate(n):
    return datetime.datetime.strptime(str(n), '%Y%m%d')

app = dash.Dash()
app.layout = html.Div([
    html.H2(children='一个量化策略',
            style = dict(backgroundColor = '#111111', textAlign = 'center', color = '#7FDBFF')),
    dcc.Graph(figure={
                'data':[
                    {
                    'x': [numtodate(k) for k in amount.keys()],
                    'y': [int(v) for k, v in amount.items()]
                    },
                ]
            }),
    ])
if __name__ == '__main__':
    app.run_server(host="0.0.0.0")

可以看到,随便拍个策略果然赚不到钱。

4.总结

我们演示的量化策略并不成功。即使这样,它还有很多未考虑全面的地方,包括买卖手续费怎么计算?如果买入当天开盘涨停怎么办?如果卖出当天跌停无法成交怎么办?
如果我们要对策略进行优化和更有效的测试,那么还需要考虑我们的回测周期是不是应该穿越牛熊?还是说我们的策略就是为了捕捉短期因子?
在越来越多机构进入量化领域的今天,散户玩A股量化基本上不存在暴利的机会,但寻找一些短期盈利的策略还是有可能的。我在http://www.gchatst.club:8050/展示了一些看着貌似能盈利的策略,有兴趣的小伙伴可以看一看。当然,我只回测了最近一年,我也只能是尽可能的消除未来函数。投资有风险。。。。哈哈

(完)