def get_order(self, order_id: Optional[str] = None) -> Union[SecurityOrder, Entity]: """ 获取用户委托单信息 Args: order_id (str): [可选]单号, 不填单号则返回所有委托单 Returns: :py:class:`~tqsdk.objs.SecurityOrder`: 当指定了 order_id 时, 返回一个委托单对象引用。 \ 其内容将在 :py:meth:`~tqsdk.api.TqApi.wait_update` 时更新。 不填 order_id 参数调用本函数, 将返回包含用户所有委托单的一个 ``tqsdk.objs.Entity`` 对象引用, \ 使用方法与 dict 一致, 其中每个元素的 key 为委托单号, value为 :py:class:`~tqsdk.objs.SecurityOrder` 注意: 在刚下单后, tqsdk 还没有收到回单信息时, 此对象中各项内容为空 Example:: from tqsdk import TqApi, TqAuth, TqKqStock tqkqstock = TqKqStock() api = TqApi(account=tqkqstock, auth=TqAuth("信易账户", "账户密码")) order = tqkqstock.get_order('委托单Id') print(f"委托股数 {order.volume_orign}, 剩余股数 {order.volume_left}") api.close() """ api = _get_api_instance(self) if order_id: return _get_obj(api._data, ["trade", self._account_key, "orders", order_id], SecurityOrder(api)) return _get_obj(api._data, ["trade", self._account_key, "orders"])
def get_trade(self, trade_id: Optional[str] = None) -> Union[SecurityTrade, Entity]: """ 获取用户成交信息 Args: trade_id (str): [可选]成交号, 不填成交号则返回所有委托单 Returns: :py:class:`~tqsdk.objs.SecurityTrade`: 当指定了trade_id时, 返回一个成交对象引用. \ 其内容将在 :py:meth:`~tqsdk.api.TqApi.wait_update` 时更新. 不填trade_id参数调用本函数, 将返回包含用户当前交易日所有成交记录的一个 ``tqsdk.objs.Entity`` 对象引用, 使用方法与dict一致, \ 其中每个元素的key为成交号, value为 :py:class:`~tqsdk.objs.SecurityTrade` 推荐优先使用 :py:meth:`~tqsdk.objs.SecurityOrder.trade_records` 获取某个委托单的相应成交记录, 仅当确有需要时才使用本函数. Example:: from tqsdk import TqApi, TqAuth, TqKqStock tqkqstock = TqKqStock() api = TqApi(account=tqkqstock, auth=TqAuth("信易账户", "账户密码")) trades = tqkqstock.get_trade('委托单Id') [print(trade.trade_id, f"成交股数 {trade.volume}, 成交价格 {trade.price}") for trade in trades] api.close() """ api = _get_api_instance(self) if trade_id: return _get_obj(api._data, ["trade", self._account_key, "trades", trade_id], SecurityTrade(api)) return _get_obj(api._data, ["trade", self._account_key, "trades"])
def _is_all_received(self): set_chart_packs = {k: v for k, v in self._resend_request.items() if v.get("aid") == "set_chart"} # 处理 seriesl(k线/tick) if not all([v.items() <= _get_obj(self._data, ["charts", k, "state"]).items() for k, v in set_chart_packs.items()]): return False # 如果当前请求还没收齐回应, 不应继续处理 # 在接收并处理完成指令后, 此时发送给客户端的数据包中的 left_id或right_id 至少有一个不是-1 , 并且 mdhis_more_data是False;否则客户端需要继续等待数据完全发送 if not all([(_get_obj(self._data, ["charts", k]).get("left_id", -1) != -1 or _get_obj(self._data, ["charts", k]).get("right_id", -1) != -1) and not self._data.get("mdhis_more_data", True) for k in set_chart_packs.keys()]): return False # 如果当前所有数据未接收完全(定位信息还没收到, 或数据序列还没收到), 不应继续处理 all_received = True # 订阅K线数据完全接收标志 for k, v in set_chart_packs.items(): # 判断已订阅的数据是否接收完全 for symbol in v["ins_list"].split(","): if symbol: path = ["klines", symbol, str(v["duration"])] if v["duration"] != 0 else ["ticks", symbol] serial = _get_obj(self._data, path) if serial.get("last_id", -1) == -1: all_received = False break if not all_received: break if not all_received: return False # 处理实时行情quote if self._data.get("ins_list", "") != self._resend_request.get("subscribe_quote", {}).get("ins_list", ""): return False # 如果实时行情quote未接收完全, 不应继续处理 return True
def get_position(self, symbol: Optional[str] = None) -> Union[SecurityPosition, Entity]: """ 获取用户持仓信息 Args: symbol (str): [可选]合约代码, 不填则返回所有持仓 Returns: :py:class:`~tqsdk.objs.SecurityPosition`: 当指定了 symbol 时, 返回一个持仓对象引用。 其内容将在 :py:meth:`~tqsdk.api.TqApi.wait_update` 时更新。 不填 symbol 参数调用本函数, 将返回包含用户所有持仓的一个 ``tqsdk.objs.Entity`` 对象引用, 使用方法与dict一致, \ 其中每个元素的 key 为合约代码, value 为 :py:class:`~tqsdk.objs.SecurityPosition`。 Example:: from tqsdk import TqApi, TqAuth, TqKqStock tqkqstock = TqKqStock() api = TqApi(account=tqkqstock, auth=TqAuth("信易账户", "账户密码")) position = tqkqstock.get_position('SSE.10003624') print(f"建仓日期 {position.create_date}, 持仓数量 {position.volume}") api.close() """ api = _get_api_instance(self) if symbol: return _get_obj(api._data, ["trade", self._account_key, "positions", symbol], Position(api)) return _get_obj(api._data, ["trade", self._account_key, "positions"])
async def _update_dividend_factor(self, symbol): quote = self._api.get_quote(symbol) df = get_dividend_df(quote.stock_dividend_ratio, quote.cash_dividend_ratio) between = df["datetime"].between( self._start_dt_nano, self._end_dt_nano) # 只需要开始时间~结束时间之间的复权因子 df["pre_close"] = float('nan') for i in df[between].index: chart_info = { "aid": "set_chart", "chart_id": _generate_uuid("PYSDK_data_factor"), "ins_list": symbol, "duration": 86400 * 1000000000, "view_width": 2, "focus_datetime": int(df.iloc[i].datetime), "focus_position": 1 } await self._api._send_chan.send(chart_info) chart = _get_obj(self._api._data, ["charts", chart_info["chart_id"]]) serial = _get_obj( self._api._data, ["klines", symbol, str(86400000000000)]) try: async with self._api.register_update_notify() as update_chan: async for _ in update_chan: if not (chart_info.items() <= _get_obj( chart, ["state"]).items()): continue # 当前请求还没收齐回应, 不应继续处理 left_id = chart.get("left_id", -1) right_id = chart.get("right_id", -1) if (left_id == -1 and right_id == -1) or self._api._data.get( "mdhis_more_data", True) or serial.get( "last_id", -1) == -1: continue # 定位信息还没收到, 或数据序列还没收到, 合约的数据是否收到 last_item = serial["data"].get(str(left_id), {}) # 复权时间点的昨收盘 df.loc[ i, 'pre_close'] = last_item['close'] if last_item.get( 'close') else float('nan') break finally: await self._api._send_chan.send({ "aid": "set_chart", "chart_id": chart_info["chart_id"], "ins_list": "", "duration": 86400000000000, "view_width": 2 }) df["factor"] = (df["pre_close"] - df["cash_dividend"] ) / df["pre_close"] / (1 + df["stock_dividend"]) df["factor"].fillna(1, inplace=True) return df
async def download_symbol_dur(s, dur, api, server_type, chart_ids): chart_info = { "aid": "set_chart", "chart_id": chart_ids[dur]["chart_id"], "ins_list": s, "duration": int(dur * 1e9), "view_width": DATA_LENGTH } await api._send_chan.send(chart_info) chart = _get_obj(api._data, ["charts", chart_info["chart_id"]]) path = ["klines", s, str(int(dur * 1e9))] if dur > 0 else ["ticks", s] serial = _get_obj(api._data, path) end_time = time() + 10 async with api.register_update_notify() as chan: async for _ in chan: left_id = chart.get("left_id", -1) right_id = chart.get("right_id", -1) last_id = serial.get("last_id", -1) if (right_id > -1 and last_id > -1) and api._data.get( "mdhis_more_data", True) is False: chart_ids[dur]["is_timeout"] = 0 chart_ids[dur]["left_id"] = left_id chart_ids[dur]["right_id"] = right_id chart_ids[dur]["last_id"] = last_id break await api._send_chan.send({ "aid": "set_chart", "chart_id": chart_ids[dur]["chart_id"], "ins_list": "", "duration": int(dur * 1e9), "view_width": DATA_LENGTH, })
def get_order(self, order_id: Optional[str] = None) -> Union[Order, Entity]: """ 获取用户委托单信息 Args: order_id (str): [可选]单号, 不填单号则返回所有委托单 Returns: :py:class:`~tqsdk.objs.Order`: 当指定了 order_id 时, 返回一个委托单对象引用。 \ 其内容将在 :py:meth:`~tqsdk.api.TqApi.wait_update` 时更新。 不填 order_id 参数调用本函数, 将返回包含用户所有委托单的一个 ``tqsdk.objs.Entity`` 对象引用, \ 使用方法与dict一致, 其中每个元素的key为委托单号, value为 :py:class:`~tqsdk.objs.Order` 注意: 在刚下单后, tqsdk 还没有收到回单信息时, 此对象中各项内容为空 Example1:: # 获取当前总挂单手数 from tqsdk import TqApi, TqAuth tqacc = TqAccount("N南华期货", "123456", "123456") api = TqApi(account=tqacc, auth=TqAuth("信易账户", "账户密码")) orders = tqacc.get_order() while True: api.wait_update() print(sum(order.volume_left for oid, order in orders.items() if order.status == "ALIVE")) # 预计的输出是这样的: 3 3 0 ... Example2:: # 多账户模式下, 分别获取各账户挂单手数 from tqsdk import TqApi, TqAuth, TqMultiAccount, TqAccount, TqKq, TqSim account = TqAccount("N南华期货", "123456", "123456") tqkq = TqKq() tqsim = TqSim() api = TqApi(TqMultiAccount([account, tqkq, tqsim]), auth=TqAuth("信易账户", "账户密码")) orders1 = account.get_order() orders2 = tqkq.get_order() orders3 = tqsim.get_order() print(f"账户 1 挂单手数 {sum(order.volume_left for order in orders1.values() if order.status == "ALIVE")}, ", f"账户 2 挂单手数 {sum(order.volume_left for order in orders2.values() if order.status == "ALIVE")}, ", f"账户 3 挂单手数 {sum(order.volume_left for order in orders3.values() if order.status == "ALIVE")}") order = account.get_order(order_id="订单号") print(order) api.close() """ api = _get_api_instance(self) if order_id: return _get_obj(api._data, ["trade", self._account_key, "orders", order_id], Order(api)) return _get_obj(api._data, ["trade", self._account_key, "orders"])
def get_position(self, symbol: Optional[str] = None) -> Union[Position, Entity]: """ 获取用户持仓信息 Args: symbol (str): [可选]合约代码, 不填则返回所有持仓 Returns: :py:class:`~tqsdk.objs.Position`: 当指定了 symbol 时, 返回一个持仓对象引用。 其内容将在 :py:meth:`~tqsdk.api.TqApi.wait_update` 时更新。 不填 symbol 参数调用本函数, 将返回包含用户所有持仓的一个 ``tqsdk.objs.Entity`` 对象引用, 使用方法与dict一致, \ 其中每个元素的 key 为合约代码, value 为 :py:class:`~tqsdk.objs.Position`。 注意: 为保留一些可供用户查询的历史信息, 如 volume_long_yd(本交易日开盘前的多头持仓手数) 等字段, 因此服务器会返回当天已平仓合约( pos_long 和 pos_short 等字段为0)的持仓信息 Example1:: # 获取 DCE.m2109 当前浮动盈亏 from tqsdk import TqApi, TqAuth, TqAccount tqacc = TqAccount("N南华期货", "123456", "123456") api = TqApi(account=tqacc, auth=TqAuth("信易账户", "账户密码")) position = tqacc.get_position("DCE.m2109") print(position.float_profit_long + position.float_profit_short) while api.wait_update(): print(position.float_profit_long + position.float_profit_short) # 预计的输出是这样的: 300.0 330.0 ... Example2:: # 多账户模式下, 分别获取各账户浮动盈亏 from tqsdk import TqApi, TqAuth, TqMultiAccount, TqAccount, TqKq, TqSim account = TqAccount("N南华期货", "123456", "123456") tqkq = TqKq() tqsim = TqSim() api = TqApi(TqMultiAccount([account, tqkq, tqsim]), auth=TqAuth("信易账户", "账户密码")) position1 = account.get_position("DCE.m2101") position2 = tqkq.get_position("DCE.m2101") position3 = tqsim.get_position("DCE.m2101") print(f"账户 1 'DCE.m2101' 浮动盈亏 {position1.float_profit_long + position1.float_profit_short}, ", f"账户 2 'DCE.m2101' 浮动盈亏 {position2.float_profit_long + position2.float_profit_short}, ", f"账户 3 'DCE.m2101' 浮动盈亏 {position3.float_profit_long + position3.float_profit_short}") api.close() """ api = _get_api_instance(self) if symbol: return _get_obj(api._data, ["trade", self._account_key, "positions", symbol], Position(api)) return _get_obj(api._data, ["trade", self._account_key, "positions"])
def _gc_data(self): # api 应该删除的数据 diff need_rangeset = {} for ins, dur in self._serials: if dur == 0: # tick 在发送数据过程中已经回收内存 continue symbol_list = ins.split(',') for s in symbol_list: need_rangeset.setdefault((s, dur), []) main_serial = _get_obj(self._data, ["klines", symbol_list[0], str(dur)]) main_serial_rangeset = self._sended_to_api.get((symbol_list[0], dur), []) # 此 request 还没有给 api 发送过任何数据时为 [] if not main_serial_rangeset: continue last_id = main_serial_rangeset[-1][-1] - 1 assert last_id > -1 need_rangeset[(symbol_list[0], dur)] = _rangeset_range_union(need_rangeset[(symbol_list[0], dur)], (last_id - 8963, last_id + 1)) for symbol in symbol_list[1:]: symbol_need_rangeset = [] symbol_binding = main_serial.get("binding", {}).get(symbol, {}) if symbol_binding: for i in range(last_id - 8963, last_id + 1): other_id = symbol_binding.get(str(i)) if other_id: symbol_need_rangeset = _rangeset_range_union(symbol_need_rangeset, (other_id, other_id + 1)) if symbol_need_rangeset: need_rangeset[(symbol, dur)] = _rangeset_union(need_rangeset[(symbol, dur)], symbol_need_rangeset) gc_rangeset = {} for key, rs in self._sended_to_api.items(): gc_rangeset[key] = _rangeset_difference(rs, need_rangeset.get(key, [])) # 更新 self._sended_to_api for key, rs in gc_rangeset.items(): self._sended_to_api[key] = _rangeset_difference(self._sended_to_api[key], rs) gc_klines_diff = {} for (symbol, dur), rs in gc_rangeset.items(): gc_klines_diff.setdefault(symbol, {}) gc_klines_diff[symbol][str(dur)] = {"data": {}} serial = _get_obj(self._data, ["klines", symbol, str(dur)]) serial_binding = serial.get("binding", None) if serial_binding: gc_klines_diff[symbol][str(dur)]["binding"] = {s: {} for s in serial_binding.keys()} for start_id, end_id in rs: for i in range(start_id, end_id): gc_klines_diff[symbol][str(dur)]["data"][str(i)] = None if serial_binding: for s, s_binding in serial_binding.items(): gc_klines_diff[symbol][str(dur)]["binding"][s][str(i)] = None return {"klines": gc_klines_diff}
def _generate_ext_diff(self): """" 补充 quote, position 额外字段 此函数在 send_diff() 才会调用, self._datetime_state.data_ready 一定为 True, 调用 self._datetime_state.get_current_dt() 一定有正确的当前时间 """ for d in self._diffs: if d.get('quotes', None): self._update_quotes(d) pend_diff = {} _simple_merge_diff(pend_diff, self._get_positions_pend_diff()) orders_set = set() # 计算过委托单,is_dead、is_online、is_error orders_price_set = set() # 根据成交计算哪些 order 需要重新计算平均成交价 trade_price for path in self._diffs_paths: if path[2] == 'orders': _, account_key, _, order_id, _ = path if (account_key, order_id) not in orders_set: orders_set.add((account_key, order_id)) order = _get_obj( self._data, ['trade', account_key, 'orders', order_id]) if order: pend_order = pend_diff.setdefault( 'trade', {}).setdefault(account_key, {}).setdefault( 'orders', {}).setdefault(order_id, {}) pend_order['is_dead'] = order['status'] == "FINISHED" pend_order['is_online'] = order[ 'exchange_order_id'] != "" and order[ 'status'] == "ALIVE" pend_order['is_error'] = order[ 'exchange_order_id'] == "" and order[ 'status'] == "FINISHED" elif path[2] == 'trades': _, account_key, _, trade_id = path trade = _get_obj(self._data, path) order_id = trade.get('order_id', '') if order_id: orders_price_set.add( ('trade', account_key, 'orders', order_id)) for path in orders_price_set: _, account_key, _, order_id = path trade_price = self._get_trade_price(account_key, order_id) if trade_price == trade_price: pend_order = pend_diff.setdefault('trade', {}).setdefault( account_key, {}).setdefault('orders', {}).setdefault(order_id, {}) pend_order['trade_price'] = trade_price self._diffs_paths = set() return pend_diff
def __init__(self, api: TqApi) -> None: """ 创建 TqNotify 实例 Args: api (tqsdk.api.TqApi): TqApi 实例 Example:: from tqsdk import TqApi, TqAuth, TqKq, TqNotify api = TqApi(account=TqKq(), auth=TqAuth("信易账户", "账户密码")) tqNotify = TqNotify(api) # 构造实例类 while True: api.wait_update() # 每次调用返回距离上一次调用 tqNotify.get_notifies() 之后产生的通知列表,没有的话返回 [] notify_list = tqNotify.get_notifies() for notify in notify_list: print(notify) # 打印出通知内容 # send_message(notify['content']) 可以发送通知到其他工具 """ self._api = api self._notify = _get_obj(self._api._data, ["notify"]) # 用户未读取过的通知,用 list 类型尽量保证用户读到通知的顺序和进程收到的顺序一致,但是不能完全保证 self._unread_notifies_list = [ k for k in self._notify if not k.startswith("_") ] # 已经添加到 _unread_notifies 的通知 self._processed_notifies_set = { k for k in self._notify if not k.startswith("_") } self._task = self._api.create_task(self._run())
def set_commission(self, symbol: str, commission: float=float('nan')): """ 设置指定合约模拟交易的每手手续费。 Args: symbol (str): 合约代码 commission (float): 每手手续费 Returns: float: 设置的每手手续费 Example:: from tqsdk import TqSim, TqApi, TqAuth sim = TqSim() api = TqApi(sim, auth=TqAuth("信易账户", "账户密码")) sim.set_commission("SHFE.cu2112", 50) print(sim.get_commission("SHFE.cu2112")) """ if commission != commission: raise Exception("合约手续费不可以设置为 float('nan')") quote = _get_obj(self._data, ["quotes", symbol], Quote(self._api if hasattr(self, "_api") else None)) quote["user_commission"] = commission if self._quote_tasks.get(symbol): self._quote_tasks[symbol]["quote_chan"].send_nowait({ "quotes": {symbol: {"user_commission": commission}} }) return commission
async def async_update(self): await self.__dict__["_api"]._ensure_symbol_async( self.__dict__["_symbol"]) ranking_id = _generate_uuid("PYSDK_rank") self.__dict__["_api"].create_task(self._get_ranking_data(ranking_id), _caller_api=True) # 错误会抛给 api 处理 symbol_rankings = _get_obj(self.__dict__["_api"]._data, ["_symbol_rankings"]) async with self.__dict__["_api"].register_update_notify( symbol_rankings) as update_chan: async for _ in update_chan: content = symbol_rankings.get(ranking_id, None) if content is None: continue data = self._content_to_list(content) for i, d in enumerate(data): self.loc[i] = d self.dropna(subset=[self.__dict__["_ranking_type"]], inplace=True) self.sort_values( by=['datetime', self.__dict__["_ranking_type"]], inplace=True, ignore_index=True) # 读完数据,清空数据 await self.__dict__["_api"]._ws_md_recv_chan.send({ "aid": "rtn_data", "data": [{ "_symbol_rankings": { ranking_id: None } }] }) return self
async def _ensure_quotes(self): await self._ensure_symbols() self._api._auth._has_md_grants([q._path[-1] for q in self]) # 权限检查 # 发送的请求会请求到所有字段,如果是期权也会请求标的的合约信息 underlying_symbols = set( [q.underlying_symbol for q in self if q.underlying_symbol]) need_quotes = set([q._path[-1] for q in self]).union(underlying_symbols) if need_quotes - self._api._requests["quotes"] != set(): self._api._requests["quotes"] = self._api._requests[ "quotes"].union(need_quotes) self._api._send_pack({ "aid": "subscribe_quote", "ins_list": ",".join(self._api._requests["quotes"]), }) if all([q.datetime != "" for q in self]): return self all_quotes = self + [ _get_obj(self._api._data, ["quotes", s], self._api._prototype["quotes"]["#"]) for s in underlying_symbols ] async with self._api.register_update_notify(self) as update_chan: async for _ in update_chan: if all([q.datetime != "" for q in all_quotes]): return self
async def _query_graphql_async(api, query_id, query): api._send_pack({"aid": "ins_query", "query_id": query_id, "query": query}) symbols = _get_obj(api._data, ["symbols"]) async with api.register_update_notify(symbols) as update_chan: async for _ in update_chan: s = symbols.get(query_id, {}) if s.get("query") == query: break
async def _ensure_quote(self, symbol, quote_chan): """quote收到行情后返回""" quote = _get_obj(self._data, ["quotes", symbol], Quote(self._api)) _register_update_chan(quote, quote_chan) if quote.get("datetime", ""): return quote.copy() async for _ in quote_chan: quote_chan.task_done() if quote.get("datetime", ""): return quote.copy()
def orders(self): tdict = _get_obj(self._api._data, ["trade", self._path[1], "orders"]) fts = { order_id: order for order_id, order in tdict.items() if (not order_id.startswith("_")) and order.instrument_id == self.instrument_id and order.exchange_id == self.exchange_id and order.status == "ALIVE" } return fts
def orders(self): """ 与此持仓相关的开仓/平仓挂单 :return: dict, 其中每个元素的key为委托单ID, value为 :py:class:`~tqsdk.objs.Order` """ tdict = _get_obj(self._api._data, ["trade", self._api._account._account_id, "orders"]) fts = {order_id: order for order_id, order in tdict.items() if (not order_id.startswith( "_")) and order.instrument_id == self.instrument_id and order.exchange_id == self.exchange_id and order.status == "ALIVE"} return fts
def trade_records(self): """ 成交记录 :return: dict, 其中每个元素的key为成交ID, value为 :py:class:`~tqsdk.objs.Trade` """ tdict = _get_obj(self._api._data, ["trade", self._api._account._account_id, "trades"]) fts = {trade_id: trade for trade_id, trade in tdict.items() if (not trade_id.startswith("_")) and trade.order_id == self.order_id} return fts
def download_symbol_dur(s, dur, api, file_name): csv_file = open(file_name, 'w', newline='') csv_writer = csv.writer(csv_file, dialect='excel') data_cols = KLINES_COLS if dur > 0 else TICKS_COLS csv_writer.writerow(["id", "datetime_nano", "datetime"] + data_cols) chart_info = { "aid": "set_chart", "chart_id": _generate_uuid("PYSDK_downloader"), "ins_list": s, "duration": int(dur*1e9), "view_width": DATA_LENGTH } api._send_chan.send_nowait(chart_info) chart = _get_obj(api._data, ["charts", chart_info["chart_id"]]) path = ["klines", s, str(int(dur*1e9))] if dur > 0 else ["ticks", s] serial = _get_obj(api._data, path) end_time = time() + 10 while True: api.wait_update(end_time) left_id = chart.get("left_id", -1) right_id = chart.get("right_id", -1) last_id = serial.get("last_id", -1) if (right_id > -1 and last_id > -1) and api._data.get("mdhis_more_data", True) is False: for current_id in range(max(left_id, 0), right_id + 1): item = serial["data"].get(str(current_id), {}) row = [str(current_id), item["datetime"], _nano_to_str(item["datetime"])] for col in data_cols: row.append(item.get(col, "#N/A")) csv_writer.writerow(row) break elif time() > end_time: print(f"request timeout {s} {dur} {file_name}") timeout_writer.writerow([s, dur, file_name]) break csv_file.close() api._send_chan.send_nowait({ "aid": "set_chart", "chart_id": chart_info["chart_id"], "ins_list": "", "duration": int(dur*1e9), "view_width": DATA_LENGTH, })
async def _ensure_quote_info(self, symbol, quote_chan): """quote收到合约信息后返回""" quote = _get_obj(self._data, ["quotes", symbol], Quote(self._api)) if quote.get("price_tick") == quote.get("price_tick"): return quote.copy() if quote.get("price_tick") != quote.get("price_tick"): await self._md_send_chan.send(_query_for_quote(symbol)) async for _ in quote_chan: quote_chan.task_done() if quote.get("price_tick") == quote.get("price_tick"): return quote.copy()
async def _ensure_quote(self, ins): # 在接新版合约服务器后,合约信息程序运行过程中查询得到的,这里不再能保证合约一定存在,需要添加 quote 默认值 quote = _get_obj(self._data, ["quotes", ins], BtQuote(self._api)) if math.isnan(quote.get("price_tick")): query_pack = _query_for_quote(ins) await self._md_send_chan.send(query_pack) async with TqChan(self._api, last_only=True) as update_chan: quote["_listener"].add(update_chan) while math.isnan(quote.get("price_tick")): await update_chan.recv() if ins not in self._quotes or self._quotes[ins]["min_duration"] > 60000000000: await self._ensure_serial(ins, 60000000000)
def get_trade(self, trade_id: Optional[str] = None) -> Union[Trade, Entity]: """ 获取用户成交信息 Args: trade_id (str): [可选]成交号, 不填成交号则返回所有委托单 Returns: :py:class:`~tqsdk.objs.Trade`: 当指定了trade_id时, 返回一个成交对象引用. \ 其内容将在 :py:meth:`~tqsdk.api.TqApi.wait_update` 时更新. 不填trade_id参数调用本函数, 将返回包含用户当前交易日所有成交记录的一个tqsdk.objs.Entity对象引用, 使用方法与dict一致, \ 其中每个元素的key为成交号, value为 :py:class:`~tqsdk.objs.Trade` 推荐优先使用 :py:meth:`~tqsdk.objs.Order.trade_records` 获取某个委托单的相应成交记录, 仅当确有需要时才使用本函数. Example:: # 多账户模式下, 分别获取各账户的成交记录 from tqsdk import TqApi, TqAuth, TqMultiAccount account = TqAccount("N南华期货", "123456", "123456") tqkq = TqKq() tqsim = TqSim() api = TqApi(TqMultiAccount([account, tqkq, tqsim]), auth=TqAuth("信易账户", "账户密码")) trades1 = account.get_trade() trades2 = tqkq.get_trade() trades3 = tqsim.get_trade() print(trades1) print(trades2) print(trades3) api.close() """ api = _get_api_instance(self) if trade_id: return _get_obj(api._data, ["trade", self._account_key, "trades", trade_id], Trade(api)) return _get_obj(api._data, ["trade", self._account_key, "trades"])
def download_symbol_dur(s, dur, api, file_name, download_finished): chart_info = { "aid": "set_chart", "chart_id": _generate_uuid("PYSDK_downloader"), "ins_list": s, "duration": int(dur*1e9), "view_width": DATA_LENGTH } await api._send_chan.send(chart_info) chart = _get_obj(api._data, ["charts", chart_info["chart_id"]]) path = ["klines", s, str(int(dur*1e9))] if dur > 0 else ["ticks", s] serial = _get_obj(api._data, path) async with api.register_update_notify() as chan: async for _ in chan: right_id = chart.get("right_id", -1) last_id = serial.get("last_id", -1) if (right_id > -1 and last_id > -1) and api._data.get("mdhis_more_data", True) is False: break # 数据收到 csv_file = open(file_name, 'w', newline='') csv_writer = csv.writer(csv_file, dialect='excel') data_cols = KLINES_COLS if dur > 0 else TICKS_COLS csv_writer.writerow(["id", "datetime_nano", "datetime"] + data_cols) left_id = chart.get("left_id", -1) right_id = chart.get("right_id", -1) for current_id in range(max(left_id, 0), right_id + 1): item = serial["data"].get(str(current_id), {}) row = [str(current_id), item["datetime"], _nano_to_str(item["datetime"])] for col in data_cols: row.append(item.get(col, "#N/A")) csv_writer.writerow(row) csv_file.close() await api._send_chan.send({ "aid": "set_chart", "chart_id": chart_info["chart_id"], "ins_list": "", "duration": int(dur * 1e9), "view_width": DATA_LENGTH, }) download_finished[dur] = True
async def _ensure_quote(self, symbol, quote_chan): """quote收到行情以及合约信息后返回""" quote = _get_obj(self._data, ["quotes", symbol], Quote(self._api)) _register_update_chan(quote, quote_chan) if quote.get("datetime", "") and quote.get("price_tick") == quote.get("price_tick"): return quote.copy() if quote.get("price_tick") != quote.get("price_tick"): # 对于没有合约信息的 quote,发送查询合约信息的请求 await self._md_send_chan.send(_query_for_quote(symbol)) async for _ in quote_chan: quote_chan.task_done() if quote.get("datetime", "") and quote.get("price_tick") == quote.get("price_tick"): return quote.copy()
def trade_price(self): """ 平均成交价 :return: 当委托单部分成交或全部成交时, 返回成交部分的平均成交价. 无任何成交时, 返回 nan """ tdict = _get_obj(self._api._data, ["trade", self._api._account._account_id, "trades"]) sum_volume = sum([trade.volume for trade_id, trade in tdict.items() if (not trade_id.startswith("_")) and trade.order_id == self.order_id]) if sum_volume == 0: return float('nan') sum_amount = sum([trade.volume * trade.price for trade_id, trade in tdict.items() if (not trade_id.startswith("_")) and trade.order_id == self.order_id]) return sum_amount / sum_volume
def set_margin(self, symbol: str, margin: float = float('nan')): """ 设置指定合约模拟交易的每手保证金。 Args: symbol (str): 合约代码 (只支持期货合约) margin (float): 每手保证金 Returns: float: 设置的每手保证金 Example:: from tqsdk import TqSim, TqApi, TqAuth sim = TqSim() api = TqApi(sim, auth=TqAuth("信易账户", "账户密码")) sim.set_margin("SHFE.cu2112", 26000) print(sim.get_margin("SHFE.cu2112")) """ if margin != margin: raise Exception("合约手续费不可以设置为 float('nan')") quote = _get_obj(self._data, ["quotes", symbol], Quote(self._api if hasattr(self, "_api") else None)) quote["user_margin"] = margin if self._quote_tasks.get(symbol): self._quote_tasks[symbol]["quote_chan"].send_nowait( {"quotes": { symbol: { "user_margin": margin } }}) # 当用户设置保证金时,用户应该得到的效果是: # 在调用 sim.set_margin() 之后,立即调用 api.get_position(symbol),得到的 margin 字段应该按照新设置的保证金调整过,而且中间没有收到过行情更新包 # 以下代码可以保证这个效果,说明: # 1. 持仓已经调整过: # sim_trade 中持仓的 future_margin 字段更新,margin 会同时调整,那么 api 中持仓的 future_margin 更新时,margin 一定也已经更新 # 2. 中间没有收到过行情更新包: # 前提1:根据 diff 协议,sim 收到 peek_message 时,会将缓存的 diffs 发给用户,当缓存的 diffs 为空,会转发 peek_message; # 前提2:api.wait_update() 会等到所有 task 都执行到 pending 状态,然后发送 peek_message 给 sim # 当用户代码执行到 sim.set_margin(),立即向 quote_chan 中发送一个数据包,quote_task 就会到 ready 状态,此时调用 wait_update(), # 到所有 task 执行到 pending 状态时,sim 的 diffs 中有数据了,此时收到 api 发来 peek_message 不会转发给上游,用户会先收到 sim 本身的账户数据, # 在下一次 wait_update,sim 的 diffs 为空,才会收到行情数据 # 在回测时,以下代码应该只经历一次 wait_update while margin != self.get_position(symbol).get("future_margin"): self._api.wait_update() return margin
async def _update_time_from_md(self): """监听行情更新并记录当时本地时间的task""" try: chan = TqChan(self._api, last_only=True) self._api.register_update_notify(self._quote, chan) # quote有更新时: 更新记录的时间 if isinstance(self._api._backtest, TqBacktest): # 回测情况下,在收到回测时间有更新的时候,也需要更新记录的时间 self._api.register_update_notify( _get_obj(self._api._data, ["_tqsdk_backtest"]), chan) async for _ in chan: self._local_time_record = time.time() - 0.005 # 更新最新行情时间时的本地时间 self._local_time_record_update_chan.send_nowait( True) # 通知记录的时间有更新 finally: await chan.close()
async def _query_graphql(self): pack = {"query": self._query} symbols = _get_obj(self._api._data, ["symbols"]) query_result = None for symbol in symbols.values(): if symbol.items() >= pack.items(): # 检查是否发送过相同的请求 query_result = symbol if query_result is None: await _query_graphql_async(self._api, self._query_id, self._query) query_result = symbols.get(self._query_id) self += self._filter(query_result) if isinstance(self._api._backtest, TqBacktest): # 回测时,清空缓存的请求 self._api._send_pack({ "aid": "ins_query", "query_id": self._query_id, "query": "" }) return self
def get_account(self) -> SecurityAccount: """ 获取用户账户资金信息 Returns: :py:class:`~tqsdk.objs.SecurityAccount`: 返回一个账户对象引用. 其内容将在 :py:meth:`~tqsdk.api.TqApi.wait_update` 时更新 Example1:: # 获取当前浮动盈亏 from tqsdk import TqApi, TqAuth tqacc = TqAccount("N南华期货", "123456", "123456") api = TqApi(account=tqacc, auth=TqAuth("信易账户", "账户密码")) account = tqacc.get_account() print(account.float_profit) # 预计的输出是这样的: 2180.0 ... Example2:: # 多账户模式下, 分别获取各账户浮动盈亏 from tqsdk import TqApi, TqAuth, TqMultiAccount, TqAccount, TqKq, TqSim account = TqAccount("N南华期货", "123456", "123456") tqkq = TqKq() tqsim = TqSim() api = TqApi(TqMultiAccount([account, tqkq, tqsim]), auth=TqAuth("信易账户", "账户密码")) account1 = account.get_account() account2 = tqkq.get_account() account3 = tqsim.get_account() print(f"账户 1 浮动盈亏 {account1.float_profit}, 账户 2 浮动盈亏 {account2.float_profit}, 账户 3 浮动盈亏 {account3.float_profit}") api.close() """ api = _get_api_instance(self) return _get_obj(api._data, ["trade", self._account_key, "accounts", "CNY"], SecurityAccount(api))