QMT Python API

策略回测_投研端

策略回测

qmt://docs/python
Python
1# coding:gbk
2 
3import pandas as pd
4import numpy as np
5 
6class G():
7 pass
8 
9g = G()
10 
11def init(C):
12 # ------------------------参数设定-----------------------------
13 g.his_st = {}
14 g.s = get_stock_list_in_sector("上证50") # 获取沪深A股股票列表
15 # g.s = get_stock_list_in_sector("沪深300") # 获取沪深300股票列表
16 # print(g.s)
17 # g.s = ['000001.SZ']
18 g.day = 0
19 g.holdings = {i: 0 for i in g.s}
20 g.weight = [0.1] * 10
21 g.buypoint = {}
22 g.money = 1000000 # C.capital
23 g.accid = 'test'
24 g.profit = 0
25 # 因子权重
26 g.buy_num = 10 # 买排名前5的股票,在过滤中会用到
27 g.per_money = g.money / g.buy_num * 0.95
28 
29 
30 
31def after_init(C):
32 # ------------------------量价数据获取-----------------------------
33 data = C.get_market_data_ex([], g.s, period='1d', dividend_type='front_ratio',
34 fill_data=True)
35 close_df = get_df_ex(data,"close")
36 # print(close_df)
37 open_df = get_df_ex(data,"open")
38 low_df = get_df_ex(data,"low")
39 high_df = get_df_ex(data,"high")
40 volume_df = get_df_ex(data,"volume")
41 amount_df = get_df_ex(data,"amount")
42 preclose_df = get_df_ex(data,"preClose")
43 
44 # ------------------------基础数据获取-----------------------------
45 # 将 g.s 中的全部股票的 TotalVolume 都获取出来,组合成一个 DataFrame
46 # 例如 g.s 中有 10 个股票,那么下面的代码就会返回一个 1 行 10 列的 DataFrame
47 # 该 DataFrame 的 index 是股票代码,columns 是 TotalVolume
48 # 该 DataFrame 的数据是每个股票的 TotalVolume,请给出代码:
49 # C.get_instrumentdetail('600000.SH')['TotalVolume']
50 
51 # 使用字典推导来获取每个股票的TotalVolume,注意在内置 Python 环境的拼写
52 total_volumes = {stock: C.get_instrumentdetail(stock)['TotalVolumn'] for stock in g.s}
53 # print(total_volumes)
54 
55 # 将字典转换为DataFrame,但先转化为一个嵌套字典
56 df_total_volume = pd.DataFrame({k: v for k, v in total_volumes.items()}, index=['TotalVolumn'])
57 # print(df_total_volume)
58 # exit()
59 
60 # ------------------------财务数据获取-----------------------------
61 
62 # ------------------------因子1计算及处理--------------------------------
63 # 1. 市值因子: 用市值因子 = 股票收盘价 * 股票总股本,要利用好,要求对应列名相乘
64 factor = close_df * df_total_volume.loc['TotalVolumn']
65 # print(factor)
66 # exit()
67 
68 # ------------------------因子2计算及处理--------------------------------
69 # 判断 close_df 中的 code 上市时间大于120天
70 stock_opendate_filter = filter_opendate_qmt(C, close_df, 120)
71 # print(stock_opendate_filter)
72 
73 # ------------------------上市日期过滤处理--------------------------------
74 # 两个布尔值的 DataFrame 对应相乘过滤掉每个交易日上市不足120天的
75 factor *= stock_opendate_filter.astype(int).replace(0, np.nan)
76 # print(factor)
77 # ------------------------排序处理-----------------------------------
78 # 对 factor 每行在一行内进行排序
79 factor_sorted = rank_filter(factor, 10, ascending=True, method='min', na_option='keep')
80 # print(factor_sorted)
81 # exit()
82 
83 # ------------------------因子组合得到布尔值信号--------------------------------
84 
85 # 确保没有未来数据的影响,将因子数据向后移动一天
86 g.factor_df = factor_sorted.shift(1) #
87 g.close_df = close_df.shift(1) # 为了计算收益率,将收盘价向后移动一天
88 g.open_df = open_df
89 g.stock_opendate_filter = stock_opendate_filter
90 
91def handlebar(C):
92 # 获取当前 K 线位置
93 d = C.barpos
94 # 获取当前 K 线时间
95 backtest_time = timetag_to_datetime(C.get_bar_timetag(C.barpos), "%Y%m%d")
96 # print(g.factor_df)
97 # print(backtest_time)
98 factor_series = g.factor_df.loc[backtest_time]
99 # factor_series.sort_values(ascending=True,inplace=True)
100 # sl = factor_series.index.tolist()
101 buy_list = daily_filter(factor_series, backtest_time)
102 print(backtest_time, buy_list)
103 # exit()
104 
105 # 获取持仓
106 hold = get_holdings(g.accid, 'stock')
107 need_sell = [s for s in hold if s not in buy_list]
108 print('\t\t\t\t\t\t\t', backtest_time, 'sell list', need_sell)
109 
110 # 卖出
111 for s in need_sell:
112 price = g.open_df.loc[backtest_time, s]
113 vol = hold[s]['持仓数量']
114 passorder(24, 1101, g.accid, s, 11, price, vol, C)
115 
116 # 获取持仓
117 hold = get_holdings(g.accid, 'stock')
118 asset = get_trade_detail_data(g.accid, 'stock', 'account')
119 # cash = asset[0].m_dAvailable
120 buy_num = g.buy_num - len(hold)
121 buy_list = [s for s in buy_list if s not in hold]
122 
123 # 买入
124 if buy_num > 0 and buy_list:
125 buy_list = buy_list[:buy_num]
126 # money = cash/buy_num
127 print(backtest_time, 'buy list', buy_list)
128 for s in buy_list:
129 price = g.open_df.loc[backtest_time, s]
130 if price > 0:
131 passorder(23, 1102, g.accid, s, 11, float(price), g.per_money, C)
132 
133 
134def daily_filter(factor_series, backtest_time):
135 # 将 factor_series 中值 True 的index,转化成列表
136 print(len(factor_series))
137 sl = factor_series[factor_series].index.tolist()
138 print(len(sl))
139 # exit()
140 # st过滤
141 sl = [s for s in sl if not is_st(s, backtest_time)]
142 sl = sorted(sl, key=lambda k: factor_series.loc[k])
143 return sl[:g.buy_num]
144 
145 
146def is_st(s, date):
147 # 判断某日在历史上是不是st *st
148 st_dict = g.his_st.get(s, {})
149 if not st_dict:
150 return False
151 else:
152 st = st_dict.get('ST', []) + st_dict.get('*ST', [])
153 for start, end in st:
154 if start <= date <= end:
155 return True
156 
157 
158def rank_filter(df: pd.DataFrame, N: int, axis=1, ascending=False, method="max", na_option="keep") -> pd.DataFrame:
159 """
160 Args:
161 df: 标准数据的df
162 N: 判断是否是前N名
163 axis: 默认是横向排序
164 ascending : 默认是降序排序
165 na_option : 默认保留nan值,但不参与排名
166 Return:
167 pd.DataFrame:一个全是bool值的df
168 """
169 _df = df.copy()
170 
171 _df = _df.rank(axis=axis, ascending=ascending, method=method, na_option=na_option)
172 
173 return _df <= N
174 
175def get_df_ex(data:dict,field:str) -> pd.DataFrame:
176 '''
177 ToDo:用于在使用get_market_data_ex的情况下,取到标准df
178
179 Args:
180 data: get_market_data_ex返回的dict
181 field: ['time', 'open', 'high', 'low', 'close', 'volume','amount', 'settelementPrice', 'openInterest', 'preClose', 'suspendFlag']
182
183 Return:
184 一个以时间为index,标的为columns的df
185
186 '''
187 _index = data[list(data.keys())[0]].index.tolist()
188 _columns = list(data.keys())
189 df = pd.DataFrame(index=_index,columns=_columns)
190 for i in _columns:
191 df[i] = data[i][field]
192 return df
193 
194 
195def filter_opendate_qmt(C, df: pd.DataFrame, n: int) -> pd.DataFrame:
196 '''
197 
198 ToDo: 判断传入的df.columns中,上市天数是否大于N日,返回的值是一个全是bool值的df
199 
200 Args:
201 C:contextinfo类
202 df:index为时间,columns为stock_code的df,目的是为了和策略中的其他df对齐
203 n:用于判断上市天数的参数,如要判断是否上市120天,则填写
204 Return:pd.DataFrame
205 
206 '''
207 # print(df.index)
208 local_df = pd.DataFrame(index=df.index, columns=df.columns)
209 # print(local_df)
210 # print(type(list(local_df.index)[0]))
211 stock_list = df.columns
212 # 这里的索引数据类型不一样
213 stock_opendate = {i: str(C.get_instrumentdetail(i)["OpenDate"]) for i in stock_list}
214 # stock_opendate = {i: C.get_instrumentdetail(i)["OpenDate"] for i in stock_list}
215 # print(type(stock_opendate["000001.SZ"]), stock_opendate["000001.SZ"])
216 # print("+================================+\n")
217
218 for stock, date in stock_opendate.items():
219 local_df.at[date, stock] = 1
220
221 df_fill = local_df.fillna(method="ffill")
222 
223 result = df_fill.expanding().sum() >= n
224 # print(result)
225 return result
226 
227def get_holdings(accid, datatype):
228 '''
229 Arg:
230 accondid:账户id
231 datatype:
232 'FUTURE':期货
233 'STOCK':股票
234 ......
235 return:
236 {股票名:{'手数':int,"持仓成本":float,'浮动盈亏':float,"可用余额":int}}
237 '''
238 PositionInfo_dict = {}
239 resultlist = get_trade_detail_data(accid, datatype, 'POSITION')
240 for obj in resultlist:
241 PositionInfo_dict[obj.m_strInstrumentID + "." + obj.m_strExchangeID] = {
242 "持仓数量": obj.m_nVolume,
243 "持仓成本": obj.m_dOpenPrice,
244 "浮动盈亏": obj.m_dFloatProfit,
245 "可用数量": obj.m_nCanUseVolume
246 }
247 return PositionInfo_dict
248 
249 
250 

智能助手

咨询式 AI · 带入当前文档

智能助手加载中...