QMT Python API

原生Python_策略回测_投研端

原生Python_策略回测

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

智能助手

咨询式 AI · 带入当前文档

智能助手加载中...