Exemplo n.º 1
0
 def __init__(self, api, symbol_list, backtest_timestamp, *args, **kwargs):
     self.__dict__["_api"] = api
     self.__dict__["_symbol_list"] = symbol_list
     self.__dict__["_backtest_timestamp"] = backtest_timestamp
     self.__dict__["_columns"] = [
         "ins_class", "instrument_id", "instrument_name", "price_tick",
         "volume_multiple", "max_limit_order_volume",
         "max_market_order_volume", "underlying_symbol", "strike_price",
         "exchange_id", "product_id", "expired", "expire_datetime",
         "expire_rest_days", "delivery_year", "delivery_month",
         "last_exercise_datetime", "exercise_year", "exercise_month",
         "option_class", "upper_limit", "lower_limit", "pre_settlement",
         "pre_open_interest", "pre_close", "trading_time_day",
         "trading_time_night"
     ]
     default_quote = Quote(None)
     data = [{
         k: (s if k == "instrument_id" else default_quote.get(k, None))
         for k in self.__dict__["_columns"]
     } for s in symbol_list]
     super(TqSymbolDataFrame,
           self).__init__(data=data,
                          columns=self.__dict__["_columns"],
                          *args,
                          **kwargs)
     self.__dict__["_task"] = api.create_task(self.async_update(),
                                              _caller_api=True)
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    def __init__(
            self, account_id, init_balance,
            trade_class: Union[Type[SimTrade], Type[SimTradeStock]]) -> None:
        self._account_id = account_id
        super(BaseSim, self).__init__()

        self.trade_log = {}  # 日期->交易记录及收盘时的权益及持仓
        self.tqsdk_stat = {}  # 回测结束后储存回测报告信息
        self._init_balance = init_balance
        self._current_datetime = "1990-01-01 00:00:00.000000"  # 当前行情时间(最新的 quote 时间)
        self._trading_day_end = "1990-01-01 18:00:00.000000"
        self._local_time_record = float("nan")  # 记录获取最新行情时的本地时间
        self._sim_trade = trade_class(
            account_key=self._account_key,
            account_id=self._account_id,
            init_balance=self._init_balance,
            get_trade_timestamp=self._get_trade_timestamp,
            is_in_trading_time=self._is_in_trading_time)
        self._data = Entity()
        self._data._instance_entity([])
        self._prototype = {
            "quotes": {
                "#": Quote(self),  # 行情的数据原型
            }
        }
        self._quote_tasks = {}
Exemplo n.º 4
0
def _symbols_to_quotes(symbols, keys=set(Quote(None).keys())):
    """将 symbols 转为 quotes,只输出 keys 包括的字段"""
    result = symbols.get("result", {})
    quotes = {}
    for k in result:
        for symbol in result[k]:
            quote = quotes.setdefault(symbol["instrument_id"], {})
            quote.update(_convert_symbol_to_quote(symbol, keys))
            if symbol.get("underlying"):
                for edge in symbol["underlying"]["edges"]:
                    underlying_symbol = edge["node"]
                    if "underlying_symbol" in keys:
                        quote["underlying_symbol"] = underlying_symbol["instrument_id"]
                    underlying_quote = quotes.setdefault(underlying_symbol["instrument_id"], {})
                    underlying_quote.update(_convert_symbol_to_quote(underlying_symbol, keys))
                    # 为期权合约补充 delivery_year delivery_month 商品期权根据标的赋值;金融期权与 exercise_year exercise_month 相同
                    # 为期权补充 delivery_year delivery_month 完全是为了兼容旧版合约服务
                    for key in ["delivery_year", "delivery_month"]:
                        if key in keys and symbol["class"] == "OPTION":
                            if symbol["exchange_id"] in ["DCE", "CZCE", "SHFE"]:
                                quote[key] = underlying_quote[key]
                            if symbol["exchange_id"] == "CFFEX" and "last_exercise_datetime" in symbol:
                                if key == "delivery_year":
                                    quote[key] = datetime.fromtimestamp(symbol["last_exercise_datetime"] / 1e9).year
                                else:
                                    quote[key] = datetime.fromtimestamp(symbol["last_exercise_datetime"] / 1e9).month
    for k in quotes:
        if quotes[k].get("ins_class", "") == "COMBINE":
            # 为组合合约补充 volume_multiple
            leg1_quote = quotes.get(quotes[k].get("leg1_symbol", ""), {})
            if leg1_quote:
                if leg1_quote.get("volume_multiple"):
                    quotes[k]["volume_multiple"] = leg1_quote["volume_multiple"]
    return quotes
Exemplo n.º 5
0
 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()
Exemplo n.º 6
0
 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()
Exemplo n.º 7
0
 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()
Exemplo n.º 8
0
    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
Exemplo n.º 9
0
 async def _run(self, api, sim_send_chan, sim_recv_chan, md_send_chan, md_recv_chan):
     """回测task"""
     self._api = api
     self._sim_send_chan = sim_send_chan
     self._sim_recv_chan = sim_recv_chan
     self._md_send_chan = md_send_chan
     self._md_recv_chan = md_recv_chan
     self._quotes_all_keys = set(Quote(None).keys())
     self._quotes_all_keys = self._quotes_all_keys.union({'margin', 'commission'})
     # 以下字段合约服务也会请求,但是不应该记在 quotes 中,quotes 中的这些字段应该有行情服务负责
     self._quotes_all_keys.difference_update({'pre_open_interest', 'pre_close', 'upper_limit', 'lower_limit'})
     sim_task = self._api.create_task(self._sim_handler())
     try:
         async for pack in self._md_recv_chan:
             if pack.get("aid") == "rtn_data":
                 data = pack.setdefault("data", [])
                 # 对于收到的数据,全部转发给下游
                 # 对于合约服务信息,query_id 为 PYSDK_quote_xxx 开头的,一定是请求了合约的全部合约信息,需要转为 quotes 转发给下游
                 for d in data:
                     for query_id, query_result in d.get("symbols", {}).items():
                         if query_result:
                             if query_result.get("error", None):
                                 raise Exception(f"查询合约服务报错 {query_result['error']}")
                             elif query_id.startswith("PYSDK_quote"):
                                 quotes = self._api._symbols_to_quotes(query_result, self._quotes_all_keys)
                                 for quote in quotes.values():
                                     if not (quote["ins_class"] == "OPTION" and quote["exchange_id"] == "SSE"):
                                         # quotes 中的 pre_settlement 字段应该由行情服务负责,行情没有上交所期权的 pre_settlement,需要从合约服务取,其他合约不变
                                         quote.pop("pre_settlement", None)
                                 data.append(
                                     {"quotes": quotes}
                                 )
                                 self._md_send_chan.send_nowait({
                                     "aid": "ins_query",
                                     "query_id": query_id,
                                     "query": ""
                                 })
             await self._sim_recv_chan.send(pack)
     finally:
         sim_task.cancel()
         await asyncio.gather(sim_task, return_exceptions=True)
Exemplo n.º 10
0
 def _quotes_to_dataframe(self, quotes):
     default_quote = Quote(None)
     for col in self.__dict__["_columns"]:
         if col == "expire_rest_days":
             current_dt = self._api._get_current_datetime().timestamp()
             self.loc[:, col] = [
                 _get_expire_rest_days(quotes[s]['expire_datetime'],
                                       current_dt)
                 if quotes[s].get('expire_datetime') else float('nan')
                 for s in self.__dict__["_symbol_list"]
             ]
         elif col == "trading_time_day" or col == "trading_time_night":
             k = 'day' if col == "trading_time_day" else 'night'
             self.loc[:, col] = Series([
                 self._get_trading_time(quotes, s, k)
                 for s in self.__dict__["_symbol_list"]
             ])
         else:
             self.loc[:, col] = Series([
                 quotes[s].get(col, default_quote[col])
                 for s in self.__dict__["_symbol_list"]
             ])
Exemplo n.º 11
0
    def __init__(self, init_balance: float = 10000000.0, account_id: str = None) -> None:
        """
        Args:
            init_balance (float): [可选]初始资金, 默认为一千万

            account_id (str): [可选]帐号, 默认为 TQSIM

        Example::

            # 修改TqSim模拟帐号的初始资金为100000
            from tqsdk import TqApi, TqSim, TqAuth
            api = TqApi(TqSim(init_balance=100000), auth=TqAuth("信易账户", "账户密码"))

        """
        self.trade_log = {}  # 日期->交易记录及收盘时的权益及持仓
        self.tqsdk_stat = {}  # 回测结束后储存回测报告信息
        self._account_id = "TQSIM" if account_id is None else account_id
        self._account_type = "FUTURE"
        self._broker_id = "TQSIM" if self._account_type == "FUTURE" else "TQSIM_STOCK"
        self._account_key = str(id(self))
        self._init_balance = float(init_balance)
        if self._init_balance <= 0:
            raise Exception("初始资金(init_balance) %s 错误, 请检查 init_balance 是否填写正确" % (init_balance))
        self._current_datetime = "1990-01-01 00:00:00.000000"  # 当前行情时间(最新的 quote 时间)
        self._trading_day_end = "1990-01-01 18:00:00.000000"
        self._local_time_record = float("nan")  # 记录获取最新行情时的本地时间
        self._sim_trade = SimTrade(account_key=self._account_key, init_balance=self._init_balance,
                                     get_trade_timestamp=self._get_trade_timestamp,
                                     is_in_trading_time=self._is_in_trading_time)
        self._data = Entity()
        self._data._instance_entity([])
        self._prototype = {
            "quotes": {
                "#": Quote(self),  # 行情的数据原型
            }
        }
        self._quote_tasks = {}
Exemplo n.º 12
0
 async def _run(self, api, api_send_chan, api_recv_chan, md_send_chan, md_recv_chan):
     """模拟交易task"""
     self._api = api
     self._tqsdk_backtest = {}  # 储存可能的回测信息
     self._tqsdk_stat = {}  # 回测结束后储存回测报告信息
     self._logger = api._logger.getChild("TqSim")  # 调试信息输出
     self._api_send_chan = api_send_chan
     self._api_recv_chan = api_recv_chan
     self._md_send_chan = md_send_chan
     self._md_recv_chan = md_recv_chan
     self._pending_peek = False
     self._diffs = []
     self._account = {
         "currency": "CNY",
         "pre_balance": self._init_balance,
         "static_balance": self._init_balance,
         "balance": self._init_balance,
         "available": self._init_balance,
         "float_profit": 0.0,
         "position_profit": 0.0,  # 期权没有持仓盈亏
         "close_profit": 0.0,
         "frozen_margin": 0.0,
         "margin": 0.0,
         "frozen_commission": 0.0,
         "commission": 0.0,
         "frozen_premium": 0.0,
         "premium": 0.0,
         "deposit": 0.0,
         "withdraw": 0.0,
         "risk_ratio": 0.0,
         "market_value": 0.0,
         "ctp_balance": float("nan"),
         "ctp_available": float("nan"),
     }
     self._positions = {}
     self._orders = {}
     self._data = Entity()
     self._data._instance_entity([])
     self._prototype = {
         "quotes": {
             "#": Quote(self),  # 行情的数据原型
         }
     }
     self._quote_tasks = {}
     self._all_subscribe = set()  # 客户端+模拟交易模块订阅的合约集合
     # 是否已经发送初始账户信息
     self._has_send_init_account = False
     md_task = self._api.create_task(self._md_handler())  # 将所有 md_recv_chan 上收到的包投递到 api_send_chan 上
     try:
         async for pack in self._api_send_chan:
             self._logger.debug("TqSim message received: %s", pack)
             if "_md_recv" in pack:
                 if pack["aid"] == "rtn_data":
                     self._md_recv(pack)  # md_recv 中会发送 wait_count 个 quotes 包给各个 quote_chan
                     await asyncio.gather(*[quote_task["quote_chan"].join() for quote_task in self._quote_tasks.values()])
                     await self._send_diff()
             elif pack["aid"] == "subscribe_quote":
                 await self._subscribe_quote(set(pack["ins_list"].split(",")))
             elif pack["aid"] == "peek_message":
                 self._pending_peek = True
                 await self._send_diff()
                 if self._pending_peek:  # 控制"peek_message"发送: 当没有新的事件需要用户处理时才推进到下一个行情
                     await self._md_send_chan.send(pack)
             elif pack["aid"] == "insert_order":
                 symbol = pack["exchange_id"] + "." + pack["instrument_id"]
                 if symbol not in self._quote_tasks:
                     quote_chan = TqChan(self._api)
                     order_chan = TqChan(self._api)
                     self._quote_tasks[symbol] = {
                         "quote_chan": quote_chan,
                         "order_chan": order_chan,
                         "task": self._api.create_task(self._quote_handler(symbol, quote_chan, order_chan))
                     }
                 await self._quote_tasks[symbol]["order_chan"].send(pack)
             elif pack["aid"] == "cancel_order":
                 # pack 里只有 order_id 信息,发送到每一个合约的 order_chan, 交由 quote_task 判断是不是当前合约下的委托单
                 for symbol in self._quote_tasks:
                     await self._quote_tasks[symbol]["order_chan"].send(pack)
             else:
                 await self._md_send_chan.send(pack)
             if self._tqsdk_backtest != {} and self._tqsdk_backtest["current_dt"] >= self._tqsdk_backtest["end_dt"] \
                     and not self._tqsdk_stat:
                 # 回测情况下,把 _send_stat_report 在循环中回测结束时执行
                 await self._send_stat_report()
     finally:
         if not self._tqsdk_stat:
             await self._send_stat_report()
         md_task.cancel()
         tasks = [md_task]
         for symbol in self._quote_tasks:
             self._quote_tasks[symbol]["task"].cancel()
             tasks.append(self._quote_tasks[symbol]["task"])
         await asyncio.gather(*tasks, return_exceptions=True)