用户手册¶
本页面给用户提供了一个简洁清晰的入门指南,涵盖各个功能模块
数据API¶
本产品提供了金融数据api,方便用户调用接口获取各种数据,通过python的api调用接口,返回DataFrame格式的数据和消息,以下是用法
导入接口¶
在python程序里面导入module,然后用注册的用户帐号登录就可以使用行情和参考数据的接口来获取数据了
引入模块¶
from jaqs.data.dataapi import DataApi
登录数据服务器¶
api = DataApi(addr='tcp://data.quantos.org:8910')
api.login("phone", "token")
行情数据获取¶
获取实时行情¶
使用quote()函数查询最新市场行情。
输入参数:
字段 | 类型 | 说明 | 缺省值 |
---|---|---|---|
symbol | string | 标的代码,支持多标的查询 | 不可缺省 |
fields | string | 需要返回字段,多字段以’,’隔开;为”“时返回所有字段 | “” |
使用示例:
df,msg = api.quote("000001.SH, cu1709.SHF", fields="open,high,low,last,volume")
输出字段:
字段 | 类型 | 说明 |
---|---|---|
symbol | string | 标的代码 |
code | string | 交易所原始代码 |
date | int | 自然日,YYYYMMDD格式,如20170823 |
time | int | 时间,精确到毫秒,如14:21:05.330记为142105330 |
trade_date | int | YYYYMMDD格式,如20170823 |
open | double | 开盘价 |
high | double | 最高价 |
low | double | 最低价 |
last | double | 最新价 |
close | double | 收盘价 |
volume | double | 成交量(总) |
turnover | double | 成交金额(总) |
vwap | double | 截止到行情时间的日内成交均价 |
oi | double | 持仓总量 |
settle | double | 今结算价 |
iopv | double | 净值估值 |
limit_up | double | 涨停价 |
limit_down | double | 跌停价 |
preclose | double | 昨收盘价 |
presettle | double | 昨结算价 |
preoi | double | 昨持仓 |
askprice1 | double | 申卖价1 |
askprice2 | double | 申卖价2 |
askprice3 | double | 申卖价3 |
askprice4 | double | 申卖价4 |
askprice5 | double | 申卖价5 |
bidprice1 | double | 申买价1 |
bidprice2 | double | 申买价2 |
bidprice3 | double | 申买价3 |
bidprice4 | double | 申买价4 |
bidprice5 | double | 申买价5 |
askvolume1 | double | 申卖量1 |
askvolume2 | double | 申卖量2 |
askvolume3 | double | 申卖量3 |
askvolume4 | double | 申卖量4 |
askvolume5 | double | 申卖量5 |
bidvolume1 | double | 申买量1 |
bidvolume2 | double | 申买量2 |
bidvolume3 | double | 申买量3 |
bidvolume4 | double | 申买量4 |
bidvolume5 | double | 申买量5 |
获取日线行情¶
代码示例:
df, msg = api.daily(
symbol="600832.SH, 600030.SH",
start_date=20121026,
end_date=20121130,
fields="",
adjust_mode="post")
结果示例(前5条记录):
close | code | high | low | oi | open | settle | symbol | trade_date | trade_status | turnover | volume | vwap |
---|---|---|---|---|---|---|---|---|---|---|---|---|
5.09 | 600832 | 5.24 | 5.08 | NaN | 5.23 | NaN | 600832.SH | 20121026 | 交易 | 2.779057e+07 | 5381800 | 5.16 |
5.10 | 600832 | 5.15 | 5.08 | NaN | 5.11 | NaN | 600832.SH | 20121029 | 交易 | 1.320333e+07 | 2582557 | 5.11 |
5.11 | 600832 | 5.18 | 5.08 | NaN | 5.12 | NaN | 600832.SH | 20121030 | 交易 | 1.622705e+07 | 3170615 | 5.12 |
5.11 | 600832 | 5.14 | 5.09 | NaN | 5.12 | NaN | 600832.SH | 20121031 | 交易 | 1.072007e+07 | 2097770 | 5.11 |
5.18 | 600832 | 5.20 | 5.12 | NaN | 5.12 | NaN | 600832.SH | 20121101 | 交易 | 1.972100e+07 | 3814712 | 5.17 |
获取分钟线行情(不含ask,bid信息)¶
代码示例:
df,msg = api.bar(
symbol="600030.SH",
trade_date=20170928,
freq="5M",
start_time= 90000,
end_time= 160000,
fields="")
结果示例(前5条记录):
close | code | date | freq | high | low | oi | open | settle | symbol | time | trade_date | turnover | volume | vwap |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
18.05 | 600030 | 20170928 | 5M | 18.08 | 18.00 | NaN | 18.01 | NaN | 600030.SH | 93500 | 20170928 | 13576973.0 | 752900 | 18.032903 |
18.03 | 600030 | 20170928 | 5M | 18.06 | 18.01 | NaN | 18.04 | NaN | 600030.SH | 94000 | 20170928 | 16145566.0 | 895110 | 18.037522 |
18.04 | 600030 | 20170928 | 5M | 18.05 | 18.02 | NaN | 18.03 | NaN | 600030.SH | 94500 | 20170928 | 11024829.0 | 611400 | 18.032105 |
17.99 | 600030 | 20170928 | 5M | 18.05 | 17.97 | NaN | 18.04 | NaN | 600030.SH | 95000 | 20170928 | 30021003.0 | 1667190 | 18.006948 |
18.02 | 600030 | 20170928 | 5M | 18.03 | 17.97 | NaN | 17.98 | NaN | 600030.SH | 95500 | 20170928 | 13691203.0 | 761161 | 17.987263 |
获取分钟线行情(包含ask,bid信息)¶
代码示例:
df,msg = api.bar_quote(
symbol="000001.SH,cu1709.SHF",
start_time = 95600,
end_time=135600,
trade_date=20170823,
freq= "5M",
fields="open,high,low,last,volume")
结果示例(前5条记录):
high | low | symbol | time | trade_date | volume |
---|---|---|---|---|---|
3294.3371 | 3291.7666 | 000001.SH | 100000 | 20170823 | 493058300 |
3292.3162 | 3289.5202 | 000001.SH | 100500 | 20170823 | 492695100 |
3290.4118 | 3288.3906 | 000001.SH | 101000 | 20170823 | 458298100 |
3289.2133 | 3285.9129 | 000001.SH | 101500 | 20170823 | 535085000 |
3287.4892 | 3284.6076 | 000001.SH | 102000 | 20170823 | 426738700 |
基本数据获取¶
获取证券基础信息¶
代码示例:
df, msg = api.query(view="jz.instrumentInfo",
fields="list_date,delist_date,symbol,market",
filter="inst_type=1&trade_date=20171219")
结果示例(前5条记录):
delist_date | list_date | market | name | symbol |
---|---|---|---|---|
99999999 | 19991110 | SH | 浦发银行 | 600000.SH |
99999999 | 20001219 | SH | 民生银行 | 600016.SH |
99999999 | 20030106 | SH | 中信证券 | 600030.SH |
99999999 | 20021009 | SH | 中国联通 | 600050.SH |
99999999 | 19970807 | SH | 国金证券 | 600109.SH |
99999999 | 20000526 | SH | 广汇能源 | 600256.SH |
获取指数基本信息¶
代码示例:
df, msg = api.query(
view="lb.indexInfo",
fields="",
filter="",
data_format='pandas')
结果示例(前5条记录):
获取交易日历¶
代码示例:
df, msg = api.query(
view="jz.secTradeCal",
fields="date,istradeday,isweekday,isholiday",
filter="start_date=20170101&end_date=20170801")
结果示例(前5条记录):
isholiday | istradeday | isweekday | trade_date |
---|---|---|---|
F | T | T | 20170103 |
F | T | T | 20170104 |
F | T | T | 20170105 |
F | T | T | 20170106 |
F | T | T | 20170109 |
获取分配除权信息¶
代码示例:
df, msg = api.query(
view="lb.secDividend",
fields="",
filter="start_date=20170101&end_date=20170801",
data_format='pandas')
结果示例(前5条记录):
ann_date | bonus_list_date | cash | cash_tax | cashpay_date | end_date | exdiv_date | publish_date | record_date | share_ratio | share_trans_ratio | symbol |
---|---|---|---|---|---|---|---|---|---|---|---|
20161025 | 0.20000000 | 0.20000000 | 20170103 | 20160930 | 20170103 | 20161227 | 20161230 | 0.0 | 0.000000 | 002059.SZ | |
20170117 | 20170217 | 3.60000000 | 3.60000000 | 20170217 | 20161231 | 20170217 | 20170210 | 20170216 | 0.0 | 5.000000 | 300561.SZ |
20161105 | 0.50000000 | 0.50000000 | 20170216 | 20160630 | 20170216 | 20170210 | 20170215 | 0.0 | 0.000000 | 601900.SH | |
20170120 | 4.50000000 | 4.50000000 | 20170303 | 20161231 | 20170303 | 20170224 | 20170302 | 0.0 | 0.000000 | 603025.SH | |
20170125 | 20170307 | 6.00000000 | 6.00000000 | 20170306 | 20161231 | 20170306 | 20170227 | 20170303 | 0.0 | 12.000000 | 600816.SH |
获取复权因子¶
代码示例:
df, msg = api.query(
view="lb.secAdjFactor",
fields="",
filter="symbol=002059&start_date=20170101&end_date=20170801",
data_format='pandas')
结果示例(前5条记录):
adjust_factor | symbol | trade_date |
---|---|---|
2.077892 | 002059.SZ | 20170103 |
2.077892 | 002059.SZ | 20170104 |
2.077892 | 002059.SZ | 20170105 |
2.077892 | 002059.SZ | 20170106 |
2.077892 | 002059.SZ | 20170109 |
获取停牌信息¶
代码示例:
df, msg = api.query(
view="lb.secSusp",
fields="susp_time",
filter="symbol=002059",
data_format='pandas')
结果示例(前5条记录):
ann_date | resu_date | susp_date | susp_reason | susp_time | symbol |
---|---|---|---|---|---|
20080408 | 20080409 | 20080408 | 召开股东大会 | 9:30:00 | 002059.SZ |
20080612 | 20080613 | 20080612 | 召开股东大会 | 9:30:00 | 002059.SZ |
20080922 | 20080922 | 20080922 | 异常波动 | 9:30:00 | 002059.SZ |
20090220 | 20090223 | 20090220 | 召开股东大会 | 9:30:00 | 002059.SZ |
20090417 | 20090420 | 20090417 | 召开股东大会 | 9:30:00 | 002059.SZ |
获取行业分类¶
代码示例:
df, msg = api.query(
view="lb.secIndustry",
fields="",
filter="industry1_name=金融&industry2_name=金融&industry_src=中证",
data_format='pandas')
结果示例(前5条记录):
in_date | industry1_code | industry1_name | industry2_code | industry2_name | industry3_code | industry3_name | industry4_code | industry4_name | industry_src | is_new | out_date | symbol |
---|---|---|---|---|---|---|---|---|---|---|---|---|
20130219 | J | 金融业 | J66 | 货币金融服务 | 中证指数有限公司 | Y | 000001.SZ | |||||
20130219 | J | 金融业 | J69 | 其他金融业 | 中证指数有限公司 | Y | 000563.SZ | |||||
20130219 | J | 金融业 | J66 | 货币金融服务 | 中证指数有限公司 | Y | 600000.SH | |||||
20130219 | J | 金融业 | J66 | 货币金融服务 | 中证指数有限公司 | Y | 600015.SH | |||||
20130219 | J | 金融业 | J66 | 货币金融服务 | 中证指数有限公司 | Y | 600016.SH |
获取指数成份¶
代码示例:
df, msg = api.query(
view="lb.indexCons",
fields="",
filter="index_code=399001&is_new=Y",
data_format='pandas')
结果示例(前5条记录):
in_date | index_code | out_date | symbol |
---|---|---|---|
20140814 | 000001.SH | 603126.SH | |
20140815 | 000001.SH | 603111.SH | |
20090511 | 000001.SH | 600372.SH | |
20140819 | 000001.SH | 603100.SH | |
20140822 | 000001.SH | 603609.SH |
获取常量参数¶
代码示例:
df, msg = api.query(
view="jz.sysConstants",
fields="",
filter="code_type=symbol_type",
data_format='pandas')
结果示例(前5条记录):
code | code_type | value |
---|---|---|
1 | inst_type | 股票 |
10 | inst_type | 回购 |
100 | inst_type | 指数 |
101 | inst_type | 股指期货 |
102 | inst_type | 国债期货 |
获取日行情估值¶
代码示例:
df, msg = api.query(
view="lb.secDailyIndicator",
fields='pb,net_assets,ncf,price_level',
filter='symbol=000063.SZ&start_date=20170605&end_date=20170701')
结果示例(前5条记录):
close_price | float_market_value | high_52w | low_52w | net_assets | pb | pe | price_level | share_float_free | symbol | total_market_value | trade_date |
---|---|---|---|---|---|---|---|---|---|---|---|
19.62 | 6.726969e+06 | 20.05 | 13.07 | 3.659734e+10 | 2.2457 | 0.0 | 0 | 215879.8077 | 000063.SZ | 8.218724e+06 | 20170605 |
19.81 | 6.792113e+06 | 20.05 | 13.07 | 3.659734e+10 | 2.2675 | 0.0 | 0 | 215879.8077 | 000063.SZ | 8.298314e+06 | 20170606 |
20.59 | 7.059546e+06 | 20.80 | 13.07 | 3.659734e+10 | 2.3567 | 0.0 | 0 | 215879.8077 | 000063.SZ | 8.625052e+06 | 20170607 |
20.63 | 7.073260e+06 | 21.05 | 13.07 | 3.659734e+10 | 2.3613 | 0.0 | 0 | 215879.8077 | 000063.SZ | 8.641808e+06 | 20170608 |
20.98 | 7.193262e+06 | 21.09 | 13.07 | 3.659734e+10 | 2.4014 | 0.0 | 0 | 215879.8077 | 000063.SZ | 8.788421e+06 | 20170609 |
获取资产负债表¶
代码示例:
df, msg = api.query(
view="lb.balanceSheet",
fields="",
filter="symbol=002636.SZ",
data_format='pandas')
结果示例(前5条记录):
acct_rcv | ann_date | inventories | notes_rcv | report_date | report_type | symbol | tot_cur_assets |
---|---|---|---|---|---|---|---|
2.035835e+08 | 20130318 | 7.627147e+07 | 1.737082e+08 | 20121231 | 408006000 | 002636.SZ | 1.074759e+09 |
7.050691e+08 | 20130425 | 1.685824e+08 | 2.460369e+08 | 20130331 | 408001000 | 002636.SZ | 1.890115e+09 |
2.436788e+08 | 20120421 | 6.736024e+07 | 5.982293e+07 | 20101231 | 408009000 | 002636.SZ | 4.718200e+08 |
2.495033e+08 | 20120424 | 1.077278e+08 | 1.173246e+08 | 20120331 | 408006000 | 002636.SZ | 1.133775e+09 |
2.035835e+08 | 20140422 | 7.627147e+07 | 1.737082e+08 | 20121231 | 408009000 | 002636.SZ | 1.074759e+09 |
获取利润表¶
代码示例:
df, msg = api.query(
view="lb.income",
fields="",
filter="symbol=600030.SH,000063.SZ,000001.SZ&report_type=408002000&start_date=20160601&end_date=20170601",
data_format='pandas')
结果示例(前5条记录):
ann_date | int_income | less_handling_chrg_comm_exp | net_int_income | oper_exp | oper_profit | oper_rev | report_date | symbol | tot_oper_cost | tot_profit | total_oper_rev |
---|---|---|---|---|---|---|---|---|---|---|---|
20160812 | 3.120900e+10 | 857000000.0 | 1.779800e+10 | 1.909500e+10 | 8.142000e+09 | 2.723700e+10 | 20160630 | 000001.SZ | 1.909500e+10 | 8.125000e+09 | 2.723700e+10 |
20160825 | 0.000000e+00 | 0.0 | 6.524571e+08 | 5.588709e+09 | 4.970444e+09 | 1.055915e+10 | 20160630 | 600030.SH | 5.588709e+09 | 4.917090e+09 | 1.055915e+10 |
20160826 | 0.000000e+00 | 0.0 | 0.000000e+00 | 0.000000e+00 | 1.811750e+08 | 2.589879e+10 | 20160630 | 000063.SZ | 2.615474e+10 | 1.336791e+09 | 2.589879e+10 |
20161029 | 0.000000e+00 | 0.0 | 7.365511e+08 | 5.237163e+09 | 3.643600e+09 | 8.880763e+09 | 20160930 | 600030.SH | 5.237163e+09 | 3.659715e+09 | 8.880763e+09 |
20161021 | 3.200700e+10 | 863000000.0 | 1.836700e+10 | 1.881000e+10 | 8.389000e+09 | 2.719900e+10 | 20160930 | 000001.SZ | 1.881000e+10 | 8.406000e+09 | 2.719900e+10 |
获取现金流量表¶
代码示例:
df, msg = api.query(
view="lb.cashFlow",
fields="",
filter="symbol=002548.SZ",
data_format='pandas')
结果示例(前5条记录):
ann_date | cash_recp_prem_orig_inco | cash_recp_return_invest | cash_recp_sg_and_rs | incl_dvd_profit_paid_sc_ms | net_cash_flows_inv_act | net_cash_received_reinsu_bus | net_incr_dep_cob | net_incr_disp_tfa | net_incr_fund_borr_ofi | … | net_incr_int_handling_chrg | net_incr_loans_central_bank | other_cash_recp_ral_fnc_act | other_cash_recp_ral_oper_act | recp_tax_rends | report_date | report_type | stot_cash_inflows_oper_act | stot_cash_outflows_oper_act | symbol |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
20140815 | 0.0 | 1071150.68 | 4.747366e+08 | 0.0 | 3.387516e+05 | 0.0 | 0.0 | 0.0 | 0.0 | … | 0.0 | 0.0 | 0.000000e+00 | 2.372317e+07 | 0.00 | 20130630 | 408003000 | 4.984598e+08 | 4.938527e+08 | 002548.SZ |
20140815 | 0.0 | 492274.24 | 4.574233e+08 | 0.0 | -5.160987e+06 | 0.0 | 0.0 | 0.0 | 0.0 | … | 0.0 | 0.0 | 0.000000e+00 | -2.986875e+05 | 0.00 | 20140630 | 408002000 | 4.571247e+08 | 4.261462e+08 | 002548.SZ |
20140815 | 0.0 | 37071150.68 | 1.294270e+08 | 0.0 | 3.893878e+07 | 0.0 | 0.0 | 0.0 | 0.0 | … | 0.0 | 0.0 | 0.000000e+00 | -7.582736e+06 | 0.00 | 20130630 | 408008000 | 1.218442e+08 | 1.523719e+08 | 002548.SZ |
20140815 | 0.0 | 492274.24 | 1.098741e+08 | 0.0 | -4.613494e+07 | 0.0 | 0.0 | 0.0 | 0.0 | … | 0.0 | 0.0 | 0.000000e+00 | 2.659082e+08 | 0.00 | 20140630 | 408007000 | 3.757823e+08 | 3.125591e+08 | 002548.SZ |
20170429 | 0.0 | 19237803.19 | 2.754383e+09 | 2450000.0 | -7.779336e+08 | 0.0 | 0.0 | 0.0 | 0.0 | … | 0.0 | 0.0 | 1.547990e+08 | 4.374965e+07 | 0.00 | 20161231 | 408001000 | 2.798133e+09 | 2.846801e+09 | 002548.SZ |
获取业绩快报¶
代码示例:
df, msg = api.query(
view="lb.profitExpress",
fields="",
filter="start_anndate=20170101",
data_format='pandas')
结果示例(前5条记录):
ann_date | net_profit_int_inc | oper_profit | oper_rev | report_date | symbol | total_assets | total_profit |
---|---|---|---|---|---|---|---|
20170207 | 1.054700e+10 | 1.227300e+10 | 9.844400e+10 | 20161231 | 601633.SH | 9.214600e+10 | 1.248000e+10 |
20170713 | 1.493567e+08 | 1.902676e+08 | 1.218885e+09 | 20170630 | 002258.SZ | 3.658932e+09 | 1.890177e+08 |
20170228 | 1.177647e+08 | 1.142228e+08 | 1.023947e+09 | 20161231 | 002406.SZ | 2.538539e+09 | 1.389901e+08 |
20170228 | 2.148007e+08 | 1.276432e+08 | 4.011206e+09 | 20161231 | 002087.SZ | 7.744742e+09 | 2.504674e+08 |
20170228 | 1.621291e+08 | 1.944727e+08 | 1.480000e+09 | 20161231 | 002688.SZ | 2.713363e+09 | 2.025310e+08 |
获取限售股解禁表¶
代码示例:
df, msg = api.query(
view="lb.secRestricted",
fields="",
filter="list_date=20170925",
data_format='pandas')
结果示例(前5条记录):
lifted_shares | list_date | symbol |
---|---|---|
7.597341e+08 | 20171011 | 000536.SZ |
7.586547e+08 | 20171011 | 000813.SZ |
2.776560e+08 | 20171011 | 002701.SZ |
1.151995e+08 | 20171010 | 603010.SH |
5.385944e+06 | 20171010 | 300190.SZ |
数据视图(DataView)¶
数据视图(DataView)将各种行情数据和参考数据进行了封装,方便用户使用数据。
DataView做什么¶
将频繁使用的DataFrame
操作自动化,使用者操作数据时尽量只考虑业务需求而不是技术实现:
- 根据字段名,自动从不同的数据api获取数据
- 按时间、标的整理对齐(财务数据按发布日期对齐)
- 在已有数据基础上,添加字段、加入自定义数据或根据公式计算新数据
- 数据查询
- 本地存储
初始化¶
DataView初始化工作主要包括创建DataView和DataService、初始化配置、数据准备三步。
创建DataView和DataService¶
DataService提供原始的数据,目前jaqs已经提供远程数据服务类(RemoteDataService),可以通过互联网获取行情数据和参考数据。
from jaqs.data.dataservice import RemoteDataService
from jaqs.data.dataview import DataView
dv = DataView()
ds = RemoteDataService()
初始化配置¶
通过init_from_config函数进行初始化配置,配置参数如下表所示:
字段 | 类型 | 说明 | 缺省值 |
---|---|---|---|
symbol | string | universe标的代码,多标的以’,’隔开,如‘000001.SH, 600300.SH’,指数代码不会被展开成对应成员股票代码 | 不可缺失,symbol与universe二选一 |
universe | string | 指数代码,单标的,将该指数的成员作为universe | 不可缺省,symbol与universe二选一 |
start_date | int | 开始日期 | 不可缺省 |
end_date | int | 结束日期 | 不可缺省 |
fields | string | 数据字段,多字段以’,’隔开,如’open,close,high,low’ | 不可缺省 |
freq | int | 数据类型,目前只支持1,表示日线数据 | 1 |
示例代码:
dv = DataView()
ds = RemoteDataService()
secs = '600030.SH,000063.SZ,000001.SZ'
props = {'start_date': 20160601, 'end_date': 20170601, 'symbol': secs,
'fields': 'open,close,high,low,volume,pb,net_assets,eps_basic',
'freq': 1}
dv.init_from_config(props, data_api=ds)
获取数据¶
数据结构说明¶
DataView用一个三维的数据结构保存的所需数据,其三维数据轴分别为:
- 标的代码,如 600030.SH, 000002.SH
- 交易日期,如 20150202, 20150203
- 数据字段,如 open, high, low, close
根据日期获取数据:¶
使用get_snapshot()函数来获取某日的数据快照(在时间轴切片),输入参数见下表:
字段 | 类型 | 说明 | 缺省值 |
---|---|---|---|
snapshot_date | int | 交易日 | 不可缺省 |
symbol | string | 标的代码,多标的以’,’隔开,如‘000001.SH, 600300.SH’ | 不可缺省 |
fields | string | 数据字段,多字段以’,’隔开,如’open,close,high,low’ | 不可缺省 |
返回在结果格式为pandas DataFrame,标的代码作为DataFrame的index,数据字段作为DataFrame的column。
示例代码:
snap1 = dv.get_snapshot(20170504, symbol='600030.SH,000063.SZ', fields='close,pb')
返回结果示例:
根据数据字段获取数据¶
使用get_ts()函数获取某个数据字段的时间序列(在字段轴切片),输入参数见下表:
字段 | 类型 | 说明 | 缺省值 |
---|---|---|---|
field | string | 数据字段,多字段以’,’隔开 | 不可缺省 |
symbol | string | “标的代码,多标的以’,’隔开,如”“000001.SH, 600300.SH”“” | 不可缺省 |
start_date | int | 开始日期 | 不可缺省 |
end_date | int | 结束日期 | 不可缺省 |
返回结果格式为pandas DataFrame,交易日作为DataFrame的index,标的代码作为DataFrame的column
示例代码:
ts1 = dv.get_ts('close', symbol='600030.SH,000063.SZ',
start_date=20170101, end_date=20170302)
数据视图及保存¶
- 可以读取修改后继续存储
- 默认覆盖
保存DataView到文件¶
使用save_dataview()函数将当前数据视图保存到指定文件夹,保存格式为h5文件。函数输入参数如下:
字段 | 类型 | 说明 | 缺省值 |
---|---|---|---|
folder_path | string | 文件保存主目录 | 不可缺省 |
sub_folder | string | 文件保存子目录,缺省为’{start_date}*{end*date}freq={freq}D’,例如,若DataView初始参数为startdate=20120101,end_date=20120110,freq=1时,sub_folder为‘20120101_20120110_freq=1D’ | ‘{start_date}*{end*date}_freq={freq}D’| |
示例代码:
dv.save_dataview('prepared', 'demo')
Store data...
Dataview has been successfully saved to:
/home/user/prepared/demo
You can load it with load_dataview('/home/user/prepared/demo')
读取已经保存的DataView¶
利用load_dataview()函数,DataView可以不经初始化,直接读取已经保存的DataView数据。函数输入参数如下所示:
字段 | 类型 | 说明 | 缺省值 |
---|---|---|---|
folder | string | DataView文件保存目录 | 不可缺省 |
示例代码:
dv = DataView()
dv.load_dataview('/home/user/prepared/demo')
Dataview loaded successfully.
添加数据¶
- 从DataApi获取更多字段:
dv.add_field('roe')
- 加入自定义DataFrame:
dv.append_df(name, df)
- 根据公式计算衍生指标:
dv.add_formula(name, formula, is_quarterly=False)
添加字段¶
利用add_field()函数可以添加当前DataView没有包含的数据,输入参数如下:
字段 | 类型 | 说明 | 缺省值 |
---|---|---|---|
field_name | string | 需要添加的字段名称 | 不可缺省 |
data_api | BaseDataServer | 缺省时为None,即利用DataView初始化时传入的DataService添加数据;当DataView是从文件中读取得到时,该DataView没有DataService,需要外部传入一个DataService以添加数据。 | None |
示例代码:
.
添加自定义公式数据¶
利用add_formula()函数可以添加当前DataView添加自定义公式数据字段,输入参数如下所示:
字段 | 类型 | 说明 | 缺省 |
---|---|---|---|
field_name | string | 字段名称 | 不可缺省 |
formula | string | 公式表达式 | 不可缺省 |
is_quarterly | bool | 是否为季度数据,如财务季报数据 | 不可缺省 |
formula_func_name_style | string | 函数名大小写识别模式,’upper’:使用默认函数名,’lower’:formular里所有函数名都为应为小写。 | ‘upper’ |
data_api | BaseDataServer | 数据服务 | None |
示例代码:
## 日频0/1指标:是否接近涨跌停
dv.add_formula('limit_reached', 'Abs((open - Delay(close, 1)) / Delay(close, 1)) > 0.095', is_quarterly=False)
dv.get_ts('limit_reached').iloc[:, 100:].head(2)
## 日频指标:与52周高点的百分比
dv.add_formula('how_high_52w', 'close_adj / Ts_Max(close_adj, 252)', is_quarterly=False)
dv.get_ts('how_high_52w').tail().applymap(lambda x: round(100*x, 1))
## 日频指标:量价背离
dv.add_formula('price_volume_divert', 'Correlation(vwap_adj, volume, 10)', is_quarterly=False)
dv.get_snapshot(20171009, fields='price_volume_divert')
## 季频指标:eps增长率
dv.add_formula('eps_growth', 'Return(eps_basic, 4)', is_quarterly=True)
dv.get_ts('eps_growth', start_date=20160810).head()
ds = RemoteDataService()
dv.add_field('total_share', ds)
目前支持的公式如下表所示:
公式 | 说明 | 示例 |
---|---|---|
+ |
加法运算 | close + open |
- |
减法运算 | close - open |
* |
乘法运算 | vwap * volume |
/ |
除法运算 | close / open |
^ |
幂函数 | close ^ 2 |
% |
取余函数 | oi % 10 |
== |
判断是否相等 | close == open |
!= |
判断是否不等 | close != open |
> |
大于 | close > open |
< |
小于 | close < open |
>= |
大于等于 | close >= open |
<= |
小于等于 | close <= open |
&& |
逻辑与 | (close > open) && (close > vwap) |
|| |
逻辑或 | (close > open) ||(close > vwap) |
! |
逻辑非 | !(close>open) |
Sin(x) | 正弦函数 | Sin(close/open) |
Cos(x) | 余弦函数 | Cos(close/open) |
Tan(x) | 正切函数 | Tan(close/open) |
Sqrt(x) | 开平方函数 | Sqrt(close^2 + open^2) |
Abs(x) | 绝对值函数 | Abs(close-open) |
Log(x) | 自然对数 | Log(close/open) |
Ceil(x) | 向上取整 | Ceil(high) |
Floor(x) | 向下取整 | Floor(low) |
Round(x) | 四舍五入 | Round(close) |
Sign(x) | 取 x 正负号,返回以-1,0和1标志 | Sign(close-open) |
-x | 对x取负 | -close |
Max(x,y) | 取 x 和 y 同位置上的较大值组成新的DataFrame返回 | Max(close, open) |
Min(x,y) | 取 x 和 y 同位置上的较小值组成新的DataFrame返回 | Min(close,open) |
Delay(x,n) | 时间序列函数, n 天前 x 的值 | Delay(close,1) 表示前一天收盘价 |
Rank(x) | 各标的根据给出的指标x的值,在横截面方向排名 | Rank( close/Delay(close,1)-1 ) 表示按日收益率进行排名 |
GroupRank(x,g) | 各标的根据指标 x 的值,在横截面方向进行按分组 g 进行分组排名。分组 DataFrame g 以int数据标志分组,例如三个标的在某一天的截面上的分组值都为2,则表示这三个标的在同一组 | GroupRank(close/Delay(close,1)-1, g) 表示按分组g根据日收益率进行分组排名 |
ConditionRank(x,cond) | 各标的根据条件 DataFrame cond,按照给出的指标 x 的值,在横截面方向排名,只有 cond 中值为True的标的参与排名。 | GroupRank(close/Delay(close,1)-1, cond) 表示按条件cond根据日收益率进行分组排名 |
Quantile(x,n) | 各标的按根据指标 x 的值,在横截面方向上进行分档,每档标的数量相同 | Quantile( close/Delay(close,1)-1,5)表示按日收益率分为5档 |
GroupQuantile(x,g,n) | 各标的根据指标 x 的值,在横截面方向上按分组 g 进行分组分档,分组 DataFrame g 以int数据标志分组,例如三个标的在某一天的截面上的分组值都为2,则表示这三个标的在同一组 | GroupQuantile(close/Delay(close,1)-1,g,5) 表示按日收益率和分组g进行分档,每组分为5档 |
Standardize(x) | 标准化,x值在横截面上减去平均值后再除以标准差 | Standardize(close/Delay(close,1)-1) 表示日收益率的标准化 |
Cutoff(x,z_score) | x值在横截面上去极值,用MAD方法 | Cutoff(close,3) 表示去掉z_score大于3的极值 |
Sum(x,n) | 时间序列函数,x 指标在过去n天的和,类似于pandas的rolling_sum()函数 | Sum(volume,5) 表示一周成交量 |
Product(x,n) | 时间序列函数,计算 x 中的值在过去 n 天的积 | Product(close/Delay(close,1),5) - 1 表示过去5天累计收益 |
CountNans(x,n) | 时间序列函数,计算 x 中的值在过去 n 天中为 nan (非数字)的次数 | CountNans((close-open)^0.5, 10) 表示过去10天内有几天close小于open |
Ewma(x,halflife) | 指数移动平均,以halflife的衰减对x进行指数移动平均 | Ewma(x,3) |
StdDev(x,n) | 时间序列函数,计算 x 中的值在过去n天的标准差 | StdDev(close/Delay(close,1)-1, 10) |
Covariance(x,y,n) | 时间序列函数,计算 x 中的值在过去n天的协方差 | Covariance(close, open, 10) |
Correlation(x,y,n) | 时间序列函数,计算 x 中的值在过去n天的相关系数 | Correlation(close,open, 10) |
Delta(x,n) | 时间序列函数,计算 x 当前值与n天前的值的差 | Delta(close,5) |
Return(x,n,log) | 时间序列函数,计算x值n天的增长率,当log为False时,计算线性增长;当log为True时,计算对数增长 | Return(close,5,True)计算一周对数收益 |
Ts_Mean(x,n) | 时间序列函数,计算 x 中的值在过去n天的平均值 | Ts_Mean(close,5) |
Ts_Min(x,n) | 时间序列函数,计算 x 中的值在过去n天的最小值 | Ts_Min(close,5) |
Ts_Max(x,n) | 时间序列函数,计算 x 中的值在过去n天的最大值 | Ts_Max(close,5) |
Ts_Skewness(x,n) | 时间序列函数,计算 x 中的值在过去n天的偏度 | Ts_Skewness(close,20) |
Ts_Kurtosis(x,n) | 时间序列函数,计算 x 中的值在过去n天的峰度 | Ts_Kurtosis(close,20) |
Tail(x, lower, upper, newval) | 如果 x 的值介于 lower 和 upper,则将其设定为 newval | Tail(close/open, 0.99, 1.01, 1.0) |
Step(n) | Step(n) 为每个标的创建一个向量,向量中 n 代表最新日期,n-1 代表前一天,以此类推。 | Step(30) |
Decay_linear(x,n) | 时间序列函数,过去n天的线性衰减函数。Decay_linear(x, n) = (x[date] * n + x[date - 1] * (n - 1) + … + x[date – n - 1]) / (n + (n - 1) + … + 1) | Decay_linear(close,15) |
Decay_exp(x,f,n) | 时间序列函数, 过去 n 天的指数衰减函数,其中 f 是平滑因子。这里 f 是平滑因子,可以赋一个小于 1 的值。Decay_exp(x, f, n) = (x[date] + x[date - 1] * f + … +x[date – n - 1] * (f ^ (n – 1))) / (1 + f + … + f ^ (n - 1)) | Decay_exp(close,0.9,10) |
Pow(x,y) | 幂函数x^y | Pow(close,2) |
SignedPower(x,e) | 等价于Sign(x) * (Abs(x)^e) | SignedPower(close-open, 0.5) |
If(cond,x,y) | cond为True取x的值,反之取y的值 | If(close > open, close, open) 表示取open和close的较大值 |
研究¶
信号研究与回测: SignalDigger
模块
功能¶
- 收益分析:分组收益、加权组合收益等
- 相关性分析:每日IC、IC分布等
- 排除涨跌停、停牌、非指数成分等
测试量价背离因子¶
- 输入:两个
DataFrame
:因子值,标的价格/收益 - 设置:period,quantile个数
factor = -dv.get_ts('price_volume_divert').shift(1, axis=0) # avoid look-ahead bias
price = dv.get_ts('close_adj')
price_bench = dv.data_benchmark
my_period = 5
obj = SignalDigger(output_folder='.', output_format='plot')
obj.process_signal_before_analysis(factor, price=price,
mask=mask_all,
n_quantiles=5, period=my_period,
benchmark_price=price_bench,
)
res = obj.create_full_report()
利用输出数据做进一步分析¶
def performance(ret):
cum = ret.add(1.0).cumprod(axis=0)
std = np.std(ret)
start = pd.to_datetime(ser.index[0], format="%Y%m%d")
end = pd.to_datetime(ser.index[-1], format="%Y%m%d")
years = (end - start).days / 365.0
yearly_return = np.power(cum.values[-1], 1. / years) - 1
yearly_vol = std * np.sqrt(225.)
# beta = np.corrcoef(df_returns.loc[:, 'bench'], df_returns.loc[:, 'strat'])[0, 1]
sharpe = yearly_return / yearly_vol
print "ann. ret = {:.1f}%; ann. vol = {:.1f}%, sharpe = {:.2f}".format(yearly_return*100, yearly_vol*100, sharpe)
ser = res['quantile_active_ret_correct'][1]['mean']#.iloc[90:]
print ser.index[0], ser.index[-1]
plt.figure(figsize=(14, 5))
plt.plot(ser.add(1.0).cumprod().values)
performance(ser)
20160105 20171013
ann. ret = -17.2%; ann. vol = 3.7%, sharpe = -4.63
回测¶
JAQS支持Alpha选股策略和事件驱动择时策略,两种策略使用不同方法回测。
对于入门用户,推荐首先查看快速入门,关于JAQS策略系统的介绍,见这里。本文在以上教程的基础上,举出更多策略样例。
*注*:本文所用例子的完整代码见这里,安装JAQS后即可直接运行。请勿直接复制下方代码运行。
格雷厄姆选股策略¶
本策略完整实现代码见 这里。
主要介绍基于回测框架实现格雷厄姆模型。格雷厄姆模型分为两步,首先是条件选股,其次按照市值从小到大排序,选出排名前五的股票。
一. 数据准备¶
我们选择如下指标,对全市场的股票进行筛选,实现过程如下:
a. 首先在数据准备模块save_dataview()中通过props设置数据起止日期,股票版块,以及所需变量
props = {
'start_date': 20150101,
'end_date': 20170930,
'universe':'000905.SH',
'fields': ('tot_cur_assets,tot_cur_liab,inventories,pre_pay,deferred_exp, eps_basic,ebit,pe,pb,float_mv,sw1'),
'freq': 1
}
b. 接着创建0-1变量表示某只股票是否被选中,并通过add_formula将变量添加到dataview中
- 市盈率(pe ratio)低于 20
- 市净率(pb ratio)低于 2
- 同比每股收益增长率(inc_earning_per_share)大于 0
- 税前同比利润增长率(inc_profit_before_tax)大于 0
- 流动比率(current_ratio)大于 2
- 速动比率(quick_ratio)大于 1
factor_formula = 'pe < 20'
dv.add_formula('pe_condition', factor_formula, is_quarterly=False)
factor_formula = 'pb < 2'
dv.add_formula('pb_condition', factor_formula, is_quarterly=False)
factor_formula = 'Return(eps_basic, 4) > 0'
dv.add_formula('eps_condition', factor_formula, is_quarterly=True)
factor_formula = 'Return(ebit, 4) > 0'
dv.add_formula('ebit_condition', factor_formula, is_quarterly=True)
factor_formula = 'tot_cur_assets/tot_cur_liab > 2'
dv.add_formula('current_condition', factor_formula, is_quarterly=True)
factor_formula = '(tot_cur_assets - inventories - pre_pay - deferred_exp)/tot_cur_liab > 1'
dv.add_formula('quick_condition', factor_formula, is_quarterly=True)
需要注意的是,涉及到的财务数据若不在secDailyIndicator表中,需将is_quarterly设置为True,表示该变量为季度数据。
- 由于第二步中需要按流通市值排序,我们将这一变量也放入dataview中
dv.add_formula('mv_rank', 'Rank(float_mv)', is_quarterly=False)
二. 条件选股¶
条件选股在my_selector函数中完成:
- 首先我们将上一步计算出的0/1变量提取出来,格式为Series
- 接着我们对所有变量取交集,选中的股票设为1,未选中的设为0,并将结果通过DataFrame形式返回
def my_selector(context, user_options=None):
#
pb_selector = context.snapshot['pb_condition']
pe_selector = context.snapshot['pe_condition']
eps_selector = context.snapshot['eps_condition']
ebit_selector = context.snapshot['ebit_condition']
current_selector = context.snapshot['current_condition']
quick_selector = context.snapshot['quick_condition']
#
merge = pd.concat([pb_selector, pe_selector, eps_selector, ebit_selector, current_selector, quick_selector], axis=1)
result = np.all(merge, axis=1)
mask = np.all(merge.isnull().values, axis=1)
result[mask] = False
return pd.DataFrame(result, index=merge.index, columns=['selector'])
三、按市值排序¶
按市值排序功能在signal_size函数中完成。我们根据流通市值排序变量’mv_rank’对所有股票进行排序,并选出市值最小的5只股票。
def signal_size(context, user_options = None):
mv_rank = context.snapshot_sub['mv_rank']
s = np.sort(mv_rank.values)[::-1]
if len(s) > 0:
critical = s[-5] if len(s) > 5 else np.min(s)
mask = mv_rank < critical
mv_rank[mask] = 0.0
mv_rank[~mask] = 1.0
return mv_rank
四、回测¶
我们在test_alpha_strategy_dataview()模块中实现回测功能
1. 载入dataview,设置回测参数¶
该模块首先载入dataview并允许用户设置回测参数,比如基准指数,起止日期,换仓周期等。
dv = DataView()
fullpath = fileio.join_relative_path('../output/prepared', dv_subfolder_name)
dv.load_dataview(folder=fullpath)
props = {
"benchmark": "000905.SH",
"universe": ','.join(dv.symbol),
"start_date": dv.start_date,
"end_date": dv.end_date,
"period": "week",
"days_delay": 0,
"init_balance": 1e8,
"position_ratio": 1.0,
}
2. StockSelector选股模块¶
接着我们使用StockSelector选股模块,将之前定义的my_selector载入
stock_selector = model.StockSelector
stock_selector.add_filter(name='myselector', func=my_selector)
3. FactorSignalModel模块¶
在进行条件选股后,使用FactorSignalModel模块对所选股票进行排序
signal_model = model.FactorSignalModel(context)
signal_model.add_signal(name='signalsize', func = signal_size)
4. 策略回测模块¶
将上面定义的stockSelector和FactorSignalModel载入AlphaStrategy函数进行回测
strategy = AlphaStrategy(
stock_selector=stock_selector,
signal_model=signal_model,
pc_method='factor_value_weight')
5. 启动数据准备及回测模块¶
t_start = time.time()
test_save_dataview()
test_alpha_strategy_dataview()
test_backtest_analyze()
t3 = time.time() - t_start
print "\n\n\nTime lapsed in total: {:.1f}".format(t3)
五、回测结果¶
回测的参数如下:
指标 | 值 |
---|---|
Beta | 0.87 |
Annual Return | 0.08 |
Annual Volatility | 0.29 |
Sharpe Ratio | 0.28 |
回测的净值曲线图如下:
基于因子IC的多因子选股模型¶
本策略完整实现代码见 这里。
主要介绍基于回测框架实现基于因子IC的因子权重优化模型。
一. 因子IC定义及优化模型¶
1. 因子IC的定义方法¶
Coefficient)的定义。传统意义上,因子在某一期的IC为该期因子与股票下期收益率的秩相关系数,即: | $$IC_t = RankCorrelation(\vec{f_t}, \vec{r_{t+1}})$$ | 其中$\vec{f_t}$为所有股票在t期的因子值向量,$\vec{r_{t+1}}$为所有股票在t到t+1期的收益率向量。秩相关系数直接反映了因子的预测能力:IC越高,说明该因子对接下里一期股票收益的预测能力越强。
2. 因子的获取及计算方法¶
在本示例中我们简单选取了几个因子,更多的因子可以在股票因子数据中找到:
- Turnover, 换手率
- BP, Book-to-Market Ratio
- MOM20, 过去20天收益率
- LFMV, 对数流通市值
实现过程如下:
a. 首先在数据准备模块save_dataview()中通过props设置数据起止日期,股票版块,以及所需变量
props = {'start_date': 20150101, 'end_date': 20170930, 'universe':
'000905.SH', 'fields': ('turnover,float_mv,close_adj,pe'), 'freq': 1}
b. 接着计算因子,进行标准化和去极值处理后通过add_formula()将因子添加到变量列表中
factor_formula = 'Cutoff(Standardize(turnover / 10000 / float_mv), 2)'
dv.add_formula('TO', factor_formula, is_quarterly=False)
factor_formula = 'Cutoff(Standardize(1/pb), 2)'
dv.add_formula('BP', factor_formula, is_quarterly = False)
factor_formula = 'Cutoff(Standardize(Return(close_adj, 20)), 2)'
dv.add_formula('REVS20', factor_formula, is_quarterly=False)
factor_formula = 'Cutoff(Standardize(Log(float_mv)), 2)'
dv.add_formula('float_mv_factor', factor_formula, is_quarterly=False)
factorList = ['TO', 'BP', 'REVS20', 'float_mv_factor']
factorList_adj = [x + '_adj' for x in factorList]
from jaqs.util import fileio
fileio.save_json(factorList_adj, '.../myCustomData.json')
c. 由于多个因子间可能存在多重共线性,我们对因子进行施密特正交化处理,并将处理后的因子添加到变量列表中。
### add the orthogonalized factor to dataview
for trade_date in dv.dates:
snapshot = dv.get_snapshot(trade_date)
factorPanel = snapshot[factorList]
factorPanel = factorPanel.dropna()
if len(factorPanel) != 0:
orthfactorPanel = Schmidt(factorPanel)
orthfactorPanel.columns = [x + '_adj' for x in factorList]
snapshot = pd.merge(left = snapshot, right = orthfactorPanel,
left_index = True, right_index = True, how = 'left')
for factor in factorList:
orthFactor_dic[factor][trade_date] = snapshot[factor]
for factor in factorList:
dv.append_df(pd.DataFrame(orthFactor_dic[factor]).T, field_name = factor + '_adj', is_quarterly=False)
3. 计算因子IC¶
从dataview中提取所有交易日,在每个交易日计算每个因子的IC
def get_ic(dv):
"""
Calculate factor IC on all dates and save it in a DataFrame
:param dv:
:return: DataFrame recording factor IC on all dates
"""
factorList = fileio.read_json('.../myCustomData.json')
ICPanel = {}
for singleDate in dv.dates:
singleSnapshot = dv.get_snapshot(singleDate)
ICPanel[singleDate] = ic_calculation(singleSnapshot, factorList)
ICPanel = pd.DataFrame(ICPanel).T
return ICPanel
其中计算IC的函数为ic_calculation()
def ic_calculation(snapshot, factorList):
"""
Calculate factor IC on single date
:param snapshot:
:return: factor IC on single date
"""
ICresult = []
for factor in factorList:
# drop na
factorPanel = snapshot[[factor, 'NextRet']]
factorPanel = factorPanel.dropna()
ic, _ = stats.spearmanr(factorPanel[factor], factorPanel['NextRet'])
ICresult.append(ic)
return ICresult
4. 因子权重优化¶
\overline{IC_2}, \cdots, \overline{IC_k},)’$,相应协方差矩阵为$\Sigma$,因子的权重向量为$\vec{v}=(\overline{V_1}, \overline{V_2},\cdots, \overline{V_k})’$。则所有因子的复合IR值为 | $$IR = \frac{\vec{v}’\vec{IC}}{\sqrt{\vec{v}’ \Sigma \vec{v}}}$$ | 我们的目标是通过调整$\vec{v}$使IR最大化。经简单计算我们可以直接求出$\vec{v}$的解析解,则最优权重向量为: | $$\vec{v}^* = \Sigma^{-1}\vec{IC}$$ | 具体实现过程如下:
def store_ic_weight():
"""
Calculate IC weight and save it to file
"""
dv = DataView()
fullpath = fileio.join_relative_path('../output/prepared', dv_subfolder_name)
dv.load_dataview(folder=fullpath)
w = get_ic_weight(dv)
store = pd.HDFStore('/home/lli/ic_weight.hd5')
store['ic_weight'] = w
store.close()
其中使用到了get_ic_weight()函数,其作用是计算每个因子IC对应的weight
def get_ic_weight(dv):
"""
Calculate factor IC weight on all dates and save it in a DataFrame
:param dv: dataview
:return: DataFrame containing the factor IC weight, with trading date as index and factor name as columns
"""
ICPanel = get_ic(dv)
ICPanel = ICPanel.dropna()
N = 10
IC_weight_Panel = {}
for i in range(N, len(ICPanel)):
ICPanel_sub = ICPanel.iloc[i-N:i, :]
ic_weight = ic_weight_calculation(ICPanel_sub)
IC_weight_Panel[ICPanel.index[i]] = ic_weight
IC_weight_Panel = pd.DataFrame(IC_weight_Panel).T
return IC_weight_Panel
我们在计算weight时需要确定一个rolling window,这里选择N=10。
def ic_weight_calculation(icpanel):
"""
Calculate factor IC weight on single date
:param icpanel:
:return: a vector containing all factor IC weight
"""
mat = np.mat(icpanel.cov())
mat = nlg.inv(mat)
weight = mat * np.mat(icpanel.mean()).reshape(len(mat), 1)
weight = np.array(weight.reshape(len(weight), ))[0]
return weight
二. 基于因子IC及相应权重的选股模型¶
在介绍选股模型的具体实现之前,我们首先熟悉一下策略模块test_alpha_strategy_dataview()。该模块的功能是基于dataview对具体策略进行回测。
1. 载入dataview,设置回测参数¶
该模块首先载入dataview并允许用户设置回测参数,比如基准指数,起止日期,换仓周期等。
dv = DataView()
fullpath = fileio.join_relative_path('../output/prepared', dv_subfolder_name)
dv.load_dataview(folder=fullpath)
props = {
"benchmark": "000905.SH",
"universe": ','.join(dv.symbol),
"start_date": dv.start_date,
"end_date": dv.end_date,
"period": "week",
"days_delay": 0,
"init_balance": 1e8,
"position_ratio": 1.0,
}
2. 载入context¶
context是一个类用来保存一些中间结果,可在程序中任意位置调用,并将之前算出的ic_weight放入context中。
context = model.Context(dataview=dv, gateway=gateway)
store = pd.HDFStore('.../ic_weight.hd5')
context.ic_weight = store['ic_weight']
store.close()
3. StockSelector选股模块¶
接着我们使用StockSelector选股模块。基于因子IC及相应权重的选股过程在my_selector中实现。
stock_selector = model.StockSelector(context)
stock_selector.add_filter(name='myselector', func=my_selector)
a.首先载入因子ic的权重context.ic_weight,回测日期列表context.trade_date记忆因子名称列表factorList
ic_weight = context.ic_weight
t_date = context.trade_date
current_ic_weight = np.mat(ic_weight.loc[t_date,]).reshape(-1,1)
factorList = fileio.read_json('.../myCustomData.json')
factorPanel = {}
for factor in factorList:
factorPanel[factor] = context.snapshot[factor]
factorPanel = pd.DataFrame(factorPanel)
b.接着根据各因子IC的权重,对当天各股票的IC值进行加权求和,选出得分最高的前30只股票。最后返回一个列表,1代表选中,0代表未选中。
factorResult = pd.DataFrame(np.mat(factorPanel) * np.mat(current_ic_weight), index = factorPanel.index)
factorResult = factorResult.fillna(-9999)
s = factorResult.sort_values(0)[::-1]
critical = s.values[30]
mask = factorResult > critical
factorResult[mask] = 1.0
factorResult[~mask] = 0.0
4. 启动数据准备及回测模块¶
t_start = time.time()
test_save_dataview()
store_ic_weight()
test_alpha_strategy_dataview()
test_backtest_analyze()
t3 = time.time() - t_start
print "\n\n\nTime lapsed in total: {:.1f}".format(t3)
三、回测结果¶
回测的参数如下:
指标 | 值 |
---|---|
Beta | 0.92 |
Annual Return | 0.19 |
Annual Volatility | 0.16 |
Sharpe Ratio | 1.21 |
四、参考文献¶
- 基于因子IC的多因子模型
- 《安信证券-多因子系列报告之一:基于因子IC的多因子模型》
Calendar Spread交易策略¶
本策略完整实现代码见 这里。
本帖主要介绍了基于事件驱动回测框架实现calendar spread交易策略。
一. 策略介绍¶
二. 参数准备¶
我们在test_spread_commodity.py文件中的test_spread_trading()函数中设置策略所需参数,例如交易标的,策略开始日期,终止日期,换仓频率等。
props = {
"symbol" : "ru1801.SHF,ru1805.SHF",
"start_date" : 20170701,
"end_date" : 20171109,
"bar_type" : "DAILY",
"init_balance" : 2e4,
"bufferSize" : 20,
"future_commission_rate": 0.00002,
"stock_commission_rate" : 0.0001,
"stock_tax_rate" : 0.0000
}
三. 策略实现¶
策略实现全部在spread_commodity.py中完成,创建名为SpreadCommodity()的class继承EventDrivenStrategy,具体分为以下几个步骤:
1. 策略初始化¶
这里将后续步骤所需要的变量都创建好并初始化。
def __init__(self):
EventDrivenStrategy.__init__(self)
self.symbol = ''
self.s1 = ''
self.s2 = ''
self.quote1 = None
self.quote2 = None
self.bufferSize = 0
self.bufferCount = 0
self.spreadList = ''
2. 从props中得到变量值¶
这里将props中设置的参数传入。其中,self.spreadList记录了最近$n$天的spread值,$n$是由self.bufferSize确定的。
def init_from_config(self, props):
super(SpreadCommodity, self).init_from_config(props)
self.symbol = props.get('symbol')
self.init_balance = props.get('init_balance')
self.bufferSize = props.get('bufferSize')
self.s1, self.s2 = self.symbol.split(',')
self.spreadList = np.zeros(self.bufferSize)
3. 策略实现¶
q1 = quote_dic.get(self.s1)
q2 = quote_dic.get(self.s2)
self.quote1 = q1
self.quote2 = q2
spread = q1.close - q2.close
接着更新self.spreadList。因为self.spreadList为固定长度,更新方法为将第2个到最后1个元素向左平移1位,并将当前的spread放在队列末尾。
self.spreadList[0:self.bufferSize - 1] = self.spreadList[1:self.bufferSize]
self.spreadList[-1] = spread
self.bufferCount += 1
接着将self.spreadList中的数据对其对应的编号(例如从1到20)做regression,观察回归系数的pvalue是否显著,比如小于0.05。如果结果不显著,则不对仓位进行操作;如果结果显著,再判断系数符号,如果系数大于0则做多spread,反之做空spread。
X, y = np.array(range(self.bufferSize)), np.array(self.spreadList)
X = X.reshape(-1, 1)
y = y.reshape(-1, 1)
X = sm.add_constant(X)
est = sm.OLS(y, X)
est = est.fit()
if est.pvalues[1] < 0.05:
if est.params[1] < 0:
self.short_spread(q1, q2)
else:
self.long_spread(q1, q2)
四. 回测结果¶
商品期货的Dual Thrust日内交易策略¶
本策略完整实现代码见 这里。
本帖主要介绍了基于事件驱动回测框架实现Dual Thrust日内交易策略。
一. 策略介绍¶
Thrust是一个趋势跟踪策略,具有简单易用、适用度广的特点,其思路简单、参数较少,配合不同的参数、止盈止损和仓位管理,可以为投资者带来长期稳定的收益,被投资者广泛应用于股票、货币、贵金属、债券、能源及股指期货市场等。 | 在本文中,我们将Dual Thrust应用于商品期货市场中。 | 简而言之,该策略的逻辑原型是较为常见的开盘区间突破策略,以今日开盘价加减一定比例确定上下轨。日内突破上轨时平空做多,突破下轨时平多做空。 | 在Dual Thrust交易系统中,对于震荡区间的定义非常关键,这也是该交易系统的核心和精髓。Dual Thrust系统使用 | $$Range = Max(HH-LC,HC-LL)$$ | 来描述震荡区间的大小。其中HH是过去N日High的最大值,LC是N日Close的最小值,HC是N日Close的最大值,LL是N日Low的最小值。
二. 参数准备¶
我们在test_spread_commodity.py文件中的test_spread_trading()函数中设置策略所需参数,例如交易标的,策略开始日期,终止日期,换仓频率等,其中$k1,k2$为确定突破区间上下限的参数。
props = {
"symbol" : "rb1710.SHF",
"start_date" : 20170510,
"end_date" : 20170930,
"buffersize" : 2,
"k1" : 0.7,
"k2" : 0.7,
"bar_type" : "MIN",
"init_balance" : 1e5,
"future_commission_rate": 0.00002,
"stock_commission_rate" : 0.0001,
"stock_tax_rate" : 0.0000
}
三. 策略实现¶
策略实现全部在DualThrust.py中完成,创建名为DualThrustStrategy()的class继承EventDrivenStrategy,具体分为以下几个步骤:
1. 策略初始化¶
这里将后续步骤所需要的变量都创建好并初始化。其中self.bufferSize为窗口期长度,self.pos记录了实时仓位,self.Upper和self.Lower记录了突破区间上下限。
def __init__(self):
EventDrivenStrategy.__init__(self)
self.symbol = ''
self.quote = None
self.bufferCount = 0
self.bufferSize = ''
self.high_list = ''
self.close_list = ''
self.low_list = ''
self.open_list = ''
self.k1 = ''
self.k2 = ''
self.pos = 0
self.Upper = 0.0
self.Lower = 0.0
2. 从props中得到变量值¶
这里将props中设置的参数传入。其中,self.high_list为固定长度的list,保存了最近$N$天的日最高价,其他变量类似。
def init_from_config(self, props):
super(DualThrustStrategy, self).init_from_config(props)
self.symbol = props.get('symbol')
self.init_balance = props.get('init_balance')
self.bufferSize = props.get('buffersize')
self.k1 = props.get('k1')
self.k2 = props.get('k2')
self.high_list = np.zeros(self.bufferSize)
self.close_list = np.zeros(self.bufferSize)
self.low_list = np.zeros(self.bufferSize)
self.open_list = np.zeros(self.bufferSize)
3. 策略实现¶
在每天开始时,首先调用initialize()函数,得到当天的open,close,high和low的值,并对应放入list中。
def initialize(self):
self.bufferCount += 1
# get the trading date
td = self.ctx.trade_date
ds = self.ctx.data_api
# get the daily data
df, msg = ds.daily(symbol=self.symbol, start_date=td, end_date=td)
# put the daily value into the corresponding list
self.open_list[0:self.bufferSize - 1] =
self.open_list[1:self.bufferSize]
self.open_list[-1] = df.high
self.high_list[0:self.bufferSize - 1] =
self.high_list[1:self.bufferSize]
self.high_list[-1] = df.high
self.close_list[0:self.bufferSize - 1] =
self.close_list[1:self.bufferSize]
self.close_list[-1] = df.close
self.low_list[0:self.bufferSize - 1] =
self.low_list[1:self.bufferSize]
self.low_list[-1] = df.low
策略的主体部分在on_bar()函数中实现。因为我们选择分钟级回测,所以会在每分钟调用on_bar()函数。
首先取到当日的quote,并计算过去$N$天的HH,HC,LC和LL,并据此计算Range和上下限Upper,Lower
HH = max(self.high_list[:-1])
HC = max(self.close_list[:-1])
LC = min(self.close_list[:-1])
LL = min(self.low_list[:-1])
Range = max(HH - LC, HC - LL)
Upper = self.open_list[-1] + self.k1 * Range
Lower = self.open_list[-1] - self.k2 * Range
我们的交易时间段为早上9:01:00到下午14:28:00,交易的逻辑为:
当分钟Bar的open向上突破上轨时,如果当时持有空单,则先平仓,再开多单;如果没有仓位,则直接开多单;
当分钟Bar的open向下突破下轨时,如果当时持有多单,则先平仓,再开空单;如果没有仓位,则直接开空单;
if self.pos == 0: if self.quote.open > Upper: self.short(self.quote, self.quote.close, 1) elif self.quote.open < Lower: self.buy(self.quote, self.quote.close, 1) elif self.pos < 0: if self.quote.open < Lower: self.cover(self.quote, self.quote.close, 1) self.long(self.quote, self.quote.close, 1) else: if self.quote.open > Upper: self.sell(self.quote, self.quote.close, 1) self.short(self.quote, self.quote.close, 1)
由于我们限制该策略为日内策略,故当交易时间超过14:28:00时,进行强行平仓。
elif self.quote.time > 142800: if self.pos > 0: self.sell(self.quote, self.quote.close, 1) elif self.pos < 0: self.cover(self.quote, self.quote.close, 1)
我们在下单后,可能由于市场剧烈变动导致未成交,因此在on_trade_ind()函数中记录具体成交情况,当空单成交时,self.pos减一,当多单成交时,self.pos加一。
def on_trade_ind(self, ind): if ind.entrust_action == 'sell' or ind.entrust_action == 'short': self.pos -= 1 elif ind.entrust_action == 'buy' or ind.entrust_action == 'cover': self.pos += 1 print(ind)
四. 回测结果¶
五、参考文献¶
版块内股票轮动策略¶
本策略完整实现代码见 这里。
本帖主要介绍了基于事件驱动回测框架实现版块内股票轮动策略。
一. 策略介绍¶
计算各股在过去$m$天相对板块指数的收益率 | $$R^A_{i,t} = (lnP_{i,t}-lnP_{i,t-m})-(lnP_{B,t}-lnP_{B,t-m})$$ | 其中$P_{i,t}$为股票$i$在$t$天的收盘价,$P_{B,t}$为板块指数在$t$天的收盘价。每天检查持仓,若持仓股$R^A_{i,t}$超过过去$n$天均值加$k$倍标准差,则卖出;反之,若有未持仓股$R^A_{i,t}$小于过去$n$天均值减$k$倍标准差,则买入。
二. 参数准备¶
我们在test_roll_trading.py文件中的test_strategy()函数中设置策略所需参数。首先确定策略开始日期,终止日期以及板块指数。在本文中,我们选择券商指数399975.SZ,并听过data_service得到该指数中所有成份股。
start_date = 20150901
end_date = 20171030
index = '399975.SZ'
data_service = RemoteDataService()
symbol_list = data_service.get_index_comp(index, start_date, start_date)
接着在props中设置参数
symbol_list.append(index)
props = {"symbol": ','.join(symbol_list),
"start_date": start_date,
"end_date": end_date,
"bar_type": "DAILY",
"init_balance": 1e7,
"std multiplier": 1.5,
"m": 10,
"n": 60,
"future_commission_rate": 0.00002,
"stock_commission_rate": 0.0001,
"stock_tax_rate": 0.0000}
我们可以在bar_type中设置换仓周期,现在支持分钟和日换仓,本例中选择每日调仓。
三. 策略实现¶
策略实现全部在roll.py中完成,创建名为RollStrategy()的class继承EventDrivenStrategy,具体分为以下几个步骤:
1. 策略初始化¶
这里将后续步骤所需要的变量都创建好并初始化。
def __init__(self):
EventDrivenStrategy.__init__(self)
self.symbol = ''
self.benchmark_symbol = ''
self.quotelist = ''
self.startdate = ''
self.bufferSize = 0
self.rollingWindow = 0
self.bufferCount = 0
self.bufferCount2 = 0
self.closeArray = {}
self.activeReturnArray = {}
self.std = ''
self.balance = ''
self.multiplier = 1.0
self.std_multiplier = 0.0
2. 从props中得到变量值¶
这里将props中设置的参数传入。其中,self.closeArray和self.activeReturnArray数据类型为dict,key为股票代码,value分别为最近$m$天的收盘价和最近$n$天的active return。
def init_from_config(self, props):
super(RollStrategy, self).init_from_config(props)
self.symbol = props.get('symbol').split(',')
self.init_balance = props.get('init_balance')
self.startdate = props.get('start_date')
self.std_multiplier = props.get('std multiplier')
self.bufferSize = props.get('n')
self.rollingWindow = props.get('m')
self.benchmark_symbol = self.symbol[-1]
self.balance = self.init_balance
for s in self.symbol:
self.closeArray[s] = np.zeros(self.rollingWindow)
self.activeReturnArray[s] = np.zeros(self.bufferSize)
3. 策略实现¶
self.quotelist = []
for s in self.symbol:
self.quotelist.append(quote_dic.get(s))
接着对每只股票更新self.closeArray。因为self.closeArray为固定长度,更新方法为将第2个到最后1个元素向左平移1位,并将当前quote中最新的close放在末尾。
for stock in self.quotelist:
self.closeArray[stock.symbol][0:self.rollingWindow - 1] = self.closeArray[stock.symbol][1:self.rollingWindow]
self.closeArray[stock.symbol][-1] = stock.close
计算每只股票在过去$m$天的active return,存入self.activeReturnArray。
### calculate active return for each stock
benchmarkReturn = np.log(self.closeArray[self.benchmark_symbol][-1])
-np.log(self.closeArray[self.benchmark_symbol][0])
for stock in self.quotelist:
stockReturn = np.log(self.closeArray[stock.symbol][-1])
-np.log(self.closeArray[stock.symbol][0])
activeReturn = stockReturn - benchmarkReturn
self.activeReturnArray[stock.symbol][0:self.bufferSize - 1]
= self.activeReturnArray[stock.symbol][1:self.bufferSize]
self.activeReturnArray[stock.symbol][-1] = activeReturn
在策略首次执行时,默认等价值持有版块中所有的股票。
### On the first day of strategy, buy in equal value stock in the universe
stockvalue = self.balance/len(self.symbol)
for stock in self.quotelist:
if stock.symbol != self.benchmark_symbol:
self.buy(stock, stock.close,
np.floor(stockvalue/stock.close/self.multiplier))
在其他日期,当策略开始执行时,首先通过self.pm.holding_securities检查持有的股票代码,并与版块成分比较确定未持有的股票代码。
stockholdings = self.pm.holding_securities
noholdings = set(self.symbol) - stockholdings
stockvalue = self.balance/len(noholdings)
对于已持有的股票,计算最近$m$天的active return,若超过self.activeReturnArray均值的一定范围,就将该股票卖出。
for stock in list(stockholdings):
curRet = self.activeReturnArray[stock][-1]
avgRet = np.mean(self.activeReturnArray[stock][:-1])
stdRet = np.std(self.activeReturnArray[stock][:-1])
if curRet >= avgRet + self.std_multiplier * stdRet:
curPosition = self.pm.positions[stock].curr_size
stock_quote = quote_dic.get(stock)
self.sell(stock_quote, stock_quote.close, curPosition)
反之,对于未持有的股票,若其active return低于均值的一定范围,就将其买入。
for stock in list(noholdings):
curRet = self.activeReturnArray[stock][-1]
avgRet = np.mean(self.activeReturnArray[stock][:-1])
stdRet = np.std(self.activeReturnArray[stock][:-1])
if curRet < avgRet - self.std_multiplier * stdRet:
stock_quote = quote_dic.get(stock)
self.buy(stock_quote, stock_quote.close,
np.floor(stockvalue/stock_quote.close/self.multiplier))
此外,我们在框架中on_trade_ind()中实现了仓位管理。在策略初始化时,我们将组合中的现金设为初始资金。
self.init_balance = props.get('init_balance')
self.balance = self.init_balance
此后,每买入一只股票,我们将self.balance减去相应市值;每卖出一只股票,将self.balance加上相应市值。
def on_trade_ind(self, ind):
if ind.entrust_action == 'buy':
self.balance -= ind.fill_price * ind.fill_size * self.multiplier
elif ind.entrust_action == 'sell':
self.balance += ind.fill_price * ind.fill_size * self.multiplier
print(ind)
四. 回测结果¶
交易API¶
本产品提供了交易API,可用于委托、撤单、查询账户、查询持仓、查询委托、查询成交、批量下单等,以下是用法
导入接口¶
在python程序里面导入module,然后用注册的用户帐号登录就可以使用交易接口进行交易。
引入模块¶
from jaqs.trade.tradeapi import TradeApi
登录¶
tapi = TradeApi(addr="tcp://gw.quantos.org:8901") # tcp://gw.quantos.org:8901是仿真系统地址
user_info, msg = tapi.login("demo", "666666") # 示例账户,用户需要改为自己注册的账户
使用用户名、密码登陆, 如果成功,返回用户可用的策略帐号列表
定义并设置回调接口¶
TradeApi通过回调函数方式通知用户事件。事件包括三种:订单状态、成交回报、委托任务执行状态。
订单状态推送
def on_orderstatus(order): print "on_orderstatus:" #, order for key in order: print "%20s : %s" % (key, str(order[key])) print ""
成交回报推送
def on_trade(trade): print "on_trade:" for key in trade: print "%20s : %s" % (key, str(trade[key])) print ""
委托任务执行状态推送,通常可以忽略该回调函数
def on_taskstatus(task): print "on_taskstatus:" for key in task: print "%20s : %s" % (key, str(task[key])) print ""
设置回调函数
tapi.set_ordstatus_callback(on_orderstatus)
tapi.set_trade_callback(on_trade)
tapi.set_task_callback(on_taskstatus)
选择使用的策略帐号¶
sid, msg = tapi.use_strategy(1)
print "msg: ", msg
print "sid: ", sid
查询Portfolio¶
返回当前的策略帐号的Universe中所有标的的净持仓,包括持仓为0的标的。
df, msg = tapi.query_portfolio()
print "msg: ", msg
print df
查询当前策略帐号的所有持仓¶
Short两个方向都有持仓,这里是返回两条记录 | 返回的 size 不带方向,全部为 正
df, msg = tapi.query_position()
print "msg: ", msg
print df
单标的下单¶
SellToday, SellYesterday | 返回 task_id
task_id, msg = tapi.place_order("000025.SZ", "Buy", 57, 100)
print "msg:", msg
print "task_id:", task_id
目标持仓下单¶
# goal_protfolio
# 参数:目标持仓
# 返回:(result, msg)
# result: 成功或失败
# msg: 错误原因
# 注意:目标持仓中必须包括所有的代码的持仓,即使不修改
# 先查询当前的持仓,
portfolio, msg = tapi.goal_portfolio(goal, algo, algo_param)
print "msg", msg
print "portfolio", portfolio
portfolio撤单¶
# stop_portfolio
# 撤单, 撤销所有portfolio订单
tapi.stop_portfolio()
批量下单(1)¶
place_batch_order,指定绝对size和交易类型
# place_batch_order
# 返回task_id, msg。
orders = [
{"security":"600030.SH", "action" : "Buy", "price": 16, "size":1000},
{"security":"600519.SH", "action" : "Buy", "price": 320, "size":1000},
]
task_id, msg = tapi.place_batch_order(orders)
print task_id
print msg
批量下单(2)¶
basket_order,指定变化量,不指定交易方向,由系统根据正负号来确定
# 批量下单2:basket_order
#
# 返回task_id, msg。
orders = [
{"security":"601857.SH", "ref_price": 8.40, "inc_size":1000},
{"security":"601997.SH", "ref_price": 14.540, "inc_size":20000},
]
task_id, msg = tapi.basket_order(orders)
print task_id
print msg
实盘交易¶
使用JAQS进行回测与实盘运行的代码具有高一致性,回测满意后,只需以下几点改动,即可接入实盘/模拟撮合:
- 使用实盘交易的交易接口:将
BacktestTradeApi
替换为RealTimeTradeApi
- 使用实盘交易主程序:将
EventBacktestInstance
替换为EventLiveTradeInstance
- 在数据接口
RemoteDataService
中订阅所要交易品种的行情 - 在主程序最后添加
time.sleep(9999999)
. 保证在事件循环运行中,主程序不会提前终止 - 实时行情均为逐笔或Tick数据,即使订阅多个品种,行情数据仍会逐个到达
strategy.on_tick()
函数
这里我们以双均线策略为例,展示实盘运行的代码:
props = {'symbol': 'rb1801.SHF'}
tapi = RealTimeTradeApi()
ins = EventLiveTradeInstance()
tapi.use_strategy(3)
ds = RemoteDataService()
strat = DoubleMaStrategy()
pm = PortfolioManager()
context = model.Context(data_api=ds, trade_api=tapi,
instance=ins, strategy=strat, pm=pm)
ins.init_from_config(props)
ds.subscribe(props['symbol'])
ins.run()
time.sleep(999999)
程序运行后,行情、策略、交易会在不同的线程内运行,我们只需要在on_tick
中进行下单,在on_trade
,
on_order_status
中处理交易回报即可。
正常收到行情如下:
rb1801.SHF 20171116-211319500 (BID) 229@3889.00 | 3891.00@12 (ASK)
Fast MA = 3889.89 Slow MA = 3889.81
rb1801.SHF 20171116-211320000 (BID) 224@3889.00 | 3891.00@13 (ASK)
Fast MA = 3889.93 Slow MA = 3889.83
rb1801.SHF 20171116-211320500 (BID) 223@3889.00 | 3891.00@5 (ASK)
Fast MA = 3889.89 Slow MA = 3889.85
rb1801.SHF 20171116-211321000 (BID) 223@3889.00 | 3891.00@5 (ASK)
Fast MA = 3889.93 Slow MA = 3889.88
rb1801.SHF 20171116-211321500 (BID) 24@3890.00 | 3891.00@5 (ASK)
Fast MA = 3890.00 Slow MA = 3889.92
如果发生下单、成交,则会收到如下回报:
rb1801.SHF 20171116-211319000 (BID) 230@3889.00 | 3891.00@6 (ASK)
Fast MA = 3889.93 Slow MA = 3889.79
Strategy on order status:
New | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1
Strategy on order status:
Accepted | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1
Strategy on trade:
20171115( 211319) Buy rb1801.SHF@3.000 size = 1
Strategy on order status:
Filled | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1
Strategy on task ind:
task_id = 81116000001 | task_status = Stopped | task_algo =
task_msg =
DONE Execution Report (00'00"039):
+---------+------+----------+------+----+---------+-----+----------+----+---------+------------+-------+-------+--------+---------+
|underlyer| ba_id| symbol|jzcode|side|is_filled|price|fill_price|size|fill_size|pending_size|cancels|rejects|entrusts| duration|
+---------+------+----------+------+----+---------+-----+----------+----+---------+------------+-------+-------+--------+---------+
| 0|800000|rb1801.SHF| 27509|Long| Y| 3.0| 3.0000| 1| 1| 0| 0| 0| 1|00'00"033|
+---------+------+----------+------+----+---------+-----+----------+----+---------+------------+-------+-------+--------+---------+