Пример #1
0
    def get_mi_contracts_from_exchange(self, exchange):
        contracts = self.get_contracts(exchange)

        if len(contracts) == 0:
            print(u'异常,未能获取{}下合约信息'.format(exchange))
            return []

        mi_contracts = []

        short_contract_dict = {}

        for contract in contracts:
            # 排除指数合约
            code = contract.get('code')
            if code[-2:] in ['L9', 'L8', 'L0', 'L1', 'L2', 'L3', '50'] or \
                    (exchange == Exchange.CFFEX and code[-3:] in ['300', '500']):
                continue
            short_symbol = get_underlying_symbol(code).upper()
            contract_list = short_contract_dict.get(short_symbol, [])
            contract_list.append(contract)
            short_contract_dict.update({short_symbol: contract_list})

        for k, v in short_contract_dict.items():
            sorted_list = sorted(v, key=lambda c: c['ZongLiang'])

            mi_contracts.append(sorted_list[-1])

        return mi_contracts
Пример #2
0
    def get_price(self, symbol: str):
        """获取最新价格"""
        tdx_symbol = symbol.upper().replace('_', '')

        short_symbol = get_underlying_symbol(tdx_symbol).upper()
        if tdx_symbol.endswith('99'):
            query_symbol = tdx_symbol.replace('99', 'L9')
        else:
            query_symbol = get_full_symbol(tdx_symbol)

        if query_symbol != tdx_symbol:
            self.write_log('转换合约:{}=>{}'.format(tdx_symbol, query_symbol))

        tdx_index_symbol = short_symbol + 'L9'
        self.connect()
        if self.api is None:
            return 0
        market_id = self.symbol_market_dict.get(tdx_index_symbol, 0)

        _res = self.api.get_instrument_quote(market_id, query_symbol)
        if not isinstance(_res, list):
            return 0
        if len(_res) == 0:
            return 0

        return float(_res[0].get('price', 0))
Пример #3
0
def get_tdx_marketid(symbol):
    """普通合约/指数合约=》tdx合约所在市场id"""
    underlying_symbol = get_underlying_symbol(symbol)
    tdx_index_symbol = underlying_symbol.upper() + 'L9'
    market_id = INIT_TDX_MARKET_MAP.get(tdx_index_symbol, None)
    if market_id is None:
        raise KeyError(f'{tdx_index_symbol}不存在INIT_TDX_MARKET_MAP中')
    return market_id
Пример #4
0
 def _get_vn_exchange(self, symbol):
     """获取"""
     underlying_symbol = get_underlying_symbol(symbol).upper()
     info = self.future_contracts.get(underlying_symbol, None)
     if info:
         return Exchange(info.get('exchange'))
     else:
         market_id = get_tdx_marketid(symbol)
         return Tdx_Vn_Exchange_Map.get(str(market_id), Exchange.INE)
Пример #5
0
    def update_mi_contracts(self):
        # 连接通达信,获取主力合约
        if not self.api:
            self.connect()

        mi_contract_quote_list = self.get_mi_contracts2()

        self.write_log(u'一共获取:{}个主力合约:{}'.format(
            len(mi_contract_quote_list),
            [c.get('code') for c in mi_contract_quote_list]))
        should_save = False
        # 逐一更新主力合约数据
        for mi_contract in mi_contract_quote_list:
            tdx_market_id = mi_contract.get('market')
            full_symbol = mi_contract.get('code')
            underlying_symbol = get_underlying_symbol(full_symbol).upper()
            if underlying_symbol in ['SC', 'NR']:
                vn_exchange = Exchange.INE
            else:
                vn_exchange = Tdx_Vn_Exchange_Map.get(str(tdx_market_id))
            mi_symbol = get_real_symbol_by_exchange(full_symbol, vn_exchange)

            # 更新登记 短合约:真实主力合约
            self.write_log('{},{},{},{},{}'.format(tdx_market_id, full_symbol,
                                                   underlying_symbol,
                                                   mi_symbol, vn_exchange))
            if underlying_symbol in self.future_contracts:
                info = self.future_contracts.get(underlying_symbol)
                if mi_symbol > info.get('mi_symbol'):
                    self.write_log(u'主力合约变化:{} =>{}'.format(
                        info.get('mi_symbol'), mi_symbol))
                    info.update({
                        'mi_symbol': mi_symbol,
                        'full_symbol': full_symbol
                    })
                    self.future_contracts.update({underlying_symbol: info})
                    should_save = True
            else:
                # 添加到新合约中
                # 这里缺少size和price_tick, margin_rate,当ctp_gateway启动时,会自动补充和修正完毕
                info = {
                    "underlying_symbol": underlying_symbol,
                    "mi_symbol": mi_symbol,
                    "full_symbol": full_symbol,
                    "exchange": vn_exchange.value
                }
                self.write_log(
                    u'新合约:{}, 需要待ctp连接后更新合约的size/price_tick/margin_rate'.
                    format(info))
                self.future_contracts.update({underlying_symbol: info})
                should_save = True

        if should_save:
            save_future_contracts(self.future_contracts)
Пример #6
0
    def get_history_transaction_data(self,
                                     symbol: str,
                                     trading_date,
                                     cache_folder: str = None):
        """获取当某一交易日的历史成交记录"""
        ret_datas = []
        # trading_date, 转换为数字类型得日期
        if isinstance(trading_date, datetime):
            trading_date = trading_date.strftime('%Y%m%d')
        if isinstance(trading_date, str):
            trading_date = int(trading_date.replace('-', ''))

        self.connect()

        cache_symbol = symbol
        cache_date = str(trading_date)

        max_data_size = sys.maxsize
        symbol = symbol.upper()
        if '99' in symbol:
            # 查询的是指数合约
            symbol = symbol.replace('99', 'L9')
            tdx_index_symbol = symbol
        else:
            # 查询的是普通合约
            tdx_index_symbol = get_underlying_symbol(symbol).upper() + 'L9'
        q_size = QSIZE * 5
        # 每秒 2个, 10小时
        max_data_size = 1000000

        # 优先从缓存加载
        if cache_folder:
            buffer_data = self.load_cache(cache_folder, cache_symbol,
                                          cache_date)
            if buffer_data:
                self.write_log(u'使用缓存文件')
                return True, buffer_data

        self.write_log(u'开始下载{} 历史{}分笔数据'.format(trading_date, symbol))
        cur_trading_date = get_trading_date()
        if trading_date == int(cur_trading_date.replace('-', '')):
            return self.get_transaction_data(symbol)
        try:
            _datas = []
            _pos = 0

            while True:
                _res = self.api.get_history_transaction_data(
                    market=self.symbol_market_dict.get(tdx_index_symbol, 0),
                    date=trading_date,
                    code=symbol,
                    start=_pos,
                    count=q_size)
                if _res is not None:
                    for d in _res:
                        dt = d.pop('date')
                        # 星期1~星期6
                        if dt.hour >= 20 and 1 < dt.isoweekday() <= 6:
                            dt = dt - timedelta(days=1)
                            d.update({'datetime': dt})
                        elif dt.hour >= 20 and dt.isoweekday() == 1:
                            # 星期一取得20点后数据
                            dt = dt - timedelta(days=3)
                            d.update({'datetime': dt})
                        elif dt.hour < 8 and dt.isoweekday() == 1:
                            # 星期一取得8点前数据
                            dt = dt - timedelta(days=3)
                            d.update({'datetime': dt})
                        elif dt.hour >= 20 and dt.isoweekday() == 7:
                            # 星期天取得20点后数据,肯定是星期五夜盘
                            dt = dt - timedelta(days=2)
                            d.update({'datetime': dt})
                        elif dt.isoweekday() == 7:
                            # 星期日取得其他时间,必然是 星期六凌晨的数据
                            dt = dt - timedelta(days=1)
                            d.update({'datetime': dt})
                        else:
                            d.update({'datetime': dt})
                        # 接口有bug,返回价格*1000,所以要除以1000
                        d.update({'price': d.get('price', 0) / 1000})
                    _datas = sorted(_res, key=lambda s: s['datetime']) + _datas
                _pos += min(q_size, len(_res))

                if _res is not None and len(_res) > 0:
                    self.write_log(u'分段取分笔数据:{} ~{}, {}条,累计:{}条'.format(
                        _res[0]['datetime'], _res[-1]['datetime'], len(_res),
                        _pos))
                else:
                    break

                if len(_datas) >= max_data_size:
                    break

            if len(_datas) == 0:
                self.write_error(u'{}分笔成交数据获取为空'.format(trading_date))
                return False, _datas

            # 缓存文件
            if cache_folder:
                self.save_cache(cache_folder, cache_symbol, cache_date, _datas)

            return True, _datas

        except Exception as ex:
            self.write_error(
                'exception in get_transaction_data:{},{},{}'.format(
                    symbol, str(ex), traceback.format_exc()))
            self.write_error(u'当前异常服务器信息:{}'.format(self.best_ip))
            self.write_log(u'重置连接')
            self.api = None
            self.connect(is_reconnect=True)
            return False, ret_datas
Пример #7
0
    def get_transaction_data(self, symbol):
        """获取当前交易日的历史成交记录"""
        ret_datas = []
        max_data_size = sys.maxsize
        symbol = symbol.upper()
        if '99' in symbol:
            # 查询的是指数合约
            symbol = symbol.replace('99', 'L9')
            tdx_index_symbol = symbol
        else:
            # 查询的是普通合约
            tdx_index_symbol = get_underlying_symbol(symbol).upper() + 'L9'

        self.connect()

        q_size = QSIZE * 5
        # 每秒 2个, 10小时
        max_data_size = 1000000

        self.write_log(u'开始下载{}当日分笔数据'.format(symbol))

        try:
            _datas = []
            _pos = 0

            while True:
                _res = self.api.get_transaction_data(
                    market=self.symbol_market_dict.get(tdx_index_symbol, 0),
                    code=symbol,
                    start=_pos,
                    count=q_size)
                if _res is not None:
                    for d in _res:
                        dt = d.pop('date')
                        # 星期1~星期6
                        if dt.hour >= 20 and 1 < dt.isoweekday() <= 6:
                            dt = dt - timedelta(days=1)
                        elif dt.hour >= 20 and dt.isoweekday() == 1:
                            # 星期一取得20点后数据
                            dt = dt - timedelta(days=3)
                        elif dt.hour < 8 and dt.isoweekday() == 1:
                            # 星期一取得8点前数据
                            dt = dt - timedelta(days=3)
                        elif dt.hour >= 20 and dt.isoweekday() == 7:
                            # 星期天取得20点后数据,肯定是星期五夜盘
                            dt = dt - timedelta(days=2)
                        elif dt.isoweekday() == 7:
                            # 星期日取得其他时间,必然是 星期六凌晨的数据
                            dt = dt - timedelta(days=1)

                        d.update({'datetime': dt})
                        # 接口有bug,返回价格*1000,所以要除以1000
                        d.update({'price': d.get('price', 0) / 1000})
                    _datas = sorted(_res, key=lambda s: s['datetime']) + _datas
                _pos += min(q_size, len(_res))

                if _res is not None and len(_res) > 0:
                    self.write_log(u'分段取分笔数据:{} ~{}, {}条,累计:{}条'.format(
                        _res[0]['datetime'], _res[-1]['datetime'], len(_res),
                        _pos))
                else:
                    break

                if len(_datas) >= max_data_size:
                    break

            if len(_datas) == 0:
                self.write_error(u'{}分笔成交数据获取为空')

            return True, _datas

        except Exception as ex:
            self.write_error(
                'exception in get_transaction_data:{},{},{}'.format(
                    symbol, str(ex), traceback.format_exc()))
            self.write_error(u'当前异常服务器信息:{}'.format(self.best_ip))
            self.write_log(u'重置连接')
            self.api = None
            self.connect(is_reconnect=True)
            return False, ret_datas
Пример #8
0
    def get_bars(self,
                 symbol: str,
                 period: str,
                 callback: Callable = None,
                 bar_freq: int = 1,
                 start_dt: datetime = None,
                 end_dt: datetime = None,
                 return_bar=True):
        """
        返回k线数据
        symbol:合约
        period: 周期: 1min,3min,5min,15min,30min,1day,3day,1hour,2hour,4hour,6hour,12hour
        callback: 逐一bar去驱动回调函数, 只有 return_bar = True时才回调
        bar_freq: 回调时的参数
        start_dt: 取数据的开始时间
        end_dt: 取数据的结束时间
        return_bar: 返回 第二个数据内容,True:BarData, False:dict
        """

        ret_bars = []
        if '.' in symbol:
            symbol = symbol.split('.')[0]
        tdx_symbol = symbol.upper().replace('_', '')
        tdx_symbol = tdx_symbol.replace('99', 'L9')
        underlying_symbol = get_underlying_symbol(symbol).upper()
        tdx_index_symbol = underlying_symbol + 'L9'
        vn_exchange = self._get_vn_exchange(underlying_symbol)

        self.connect()
        if self.api is None:
            return False, ret_bars

        if period not in PERIOD_MAPPING.keys():
            self.write_error(u'{} 周期{}不在下载清单中: {}'.format(
                datetime.now(), period, list(PERIOD_MAPPING.keys())))
            return False, ret_bars

        tdx_period = PERIOD_MAPPING.get(period)

        if start_dt is None:
            self.write_log(u'没有设置开始时间,缺省为10天前')
            qry_start_date = datetime.now() - timedelta(days=10)
        else:
            qry_start_date = start_dt
        if end_dt is None:
            self.write_log(u'没有设置结束时间,缺省为当日')
            end_date = datetime.combine(datetime.now() + timedelta(days=1),
                                        time(ALL_MARKET_END_HOUR, 0))
        else:
            end_date = end_dt
        if qry_start_date > end_date:
            qry_start_date = end_date
        self.write_log('{}开始下载tdx:{} {}数据, {} to {}.'.format(
            datetime.now(), tdx_symbol, period, qry_start_date, end_date))
        # print('{}开始下载tdx:{} {}数据, {} to {}.'.format(datetime.now(), tdx_symbol, tdx_period, last_date, end_date))

        try:
            _start_date = end_date
            _bars = []
            _pos = 0
            while _start_date > qry_start_date:
                _res = self.api.get_instrument_bars(
                    tdx_period,
                    self.symbol_market_dict.get(tdx_index_symbol,
                                                0), tdx_symbol, _pos, QSIZE)
                if _res is not None:
                    _bars = _res + _bars
                _pos += QSIZE
                if _res is not None and len(_res) > 0:
                    _start_date = _res[0]['datetime']
                    _start_date = datetime.strptime(_start_date,
                                                    '%Y-%m-%d %H:%M')
                    # self.write_log(u'分段取数据开始:{}'.format(_start_date))
                else:
                    break
            if len(_bars) == 0:
                self.write_error('{} Handling {}, len1={}..., continue'.format(
                    str(datetime.now()), tdx_symbol, len(_bars)))
                return False, ret_bars

            current_datetime = datetime.now()
            data = self.api.to_df(_bars)
            data = data.assign(datetime=to_datetime(data['datetime']))
            data = data.assign(ticker=symbol)
            data['instrument_id'] = data['ticker']
            data['symbol'] = symbol
            data = data.drop([
                'year', 'month', 'day', 'hour', 'minute', 'price', 'amount',
                'ticker'
            ],
                             errors='ignore',
                             axis=1)
            data = data.rename(index=str,
                               columns={
                                   'position': 'open_interest',
                                   'trade': 'volume',
                               })
            if len(data) == 0:
                print('{} Handling {}, len2={}..., continue'.format(
                    str(datetime.now()), tdx_symbol, len(data)))
                return False, ret_bars

            data['total_turnover'] = data['volume'] * data['close']
            data["limit_down"] = 0
            data["limit_up"] = 999999
            data['trading_day'] = data['datetime']
            data['trading_day'] = data['trading_day'].apply(
                lambda x: (x.strftime('%Y-%m-%d')))
            monday_ts = data['datetime'].dt.weekday == 0  # 星期一
            night_ts1 = data['datetime'].dt.hour > ALL_MARKET_END_HOUR
            night_ts2 = data['datetime'].dt.hour < ALL_MARKET_BEGIN_HOUR
            data.loc[night_ts1, 'datetime'] -= timedelta(
                days=1)  # 所有日期的夜盘(21:00~24:00), 减一天
            monday_ts1 = monday_ts & night_ts1  # 星期一的夜盘(21:00~24:00), 再减两天
            data.loc[monday_ts1, 'datetime'] -= timedelta(days=2)
            monday_ts2 = monday_ts & night_ts2  # 星期一的夜盘(00:00~04:00), 再减两天
            data.loc[monday_ts2, 'datetime'] -= timedelta(days=2)
            # data['datetime'] -= timedelta(minutes=1) # 直接给Strategy使用, RiceQuant格式, 不需要减1分钟
            # data['dt_datetime'] = data['datetime']
            data['date'] = data['datetime'].apply(lambda x:
                                                  (x.strftime('%Y-%m-%d')))
            data['time'] = data['datetime'].apply(lambda x:
                                                  (x.strftime('%H:%M:%S')))
            # data['datetime'] = data['datetime'].apply(lambda x: float(x.strftime('%Y%m%d%H%M%S')))
            data = data.set_index('datetime', drop=False)
            if return_bar:
                self.write_log('dataframe => [bars]')
                for index, row in data.iterrows():
                    add_bar = BarData(gateway_name='tdx',
                                      symbol=symbol,
                                      exchange=vn_exchange,
                                      datetime=index)
                    try:
                        add_bar.date = row['date']
                        add_bar.time = row['time']
                        add_bar.trading_day = row['trading_day']
                        add_bar.open_price = float(row['open'])
                        add_bar.high_price = float(row['high'])
                        add_bar.low_price = float(row['low'])
                        add_bar.close_price = float(row['close'])
                        add_bar.volume = float(row['volume'])
                        add_bar.openInterest = float(row['open_interest'])
                    except Exception as ex:
                        self.write_error(
                            'error when convert bar:{},ex:{},t:{}'.format(
                                row, str(ex), traceback.format_exc()))
                        # print('error when convert bar:{},ex:{},t:{}'.format(row, str(ex), traceback.format_exc()))
                        return False, ret_bars

                    if start_dt is not None and index < start_dt:
                        continue
                    ret_bars.append(add_bar)

                    if callback is not None:
                        freq = bar_freq
                        bar_is_completed = True
                        if period != '1min' and index == data['datetime'][-1]:
                            # 最后一个bar,可能是不完整的,强制修改
                            # - 5min修改后freq基本正确
                            # - 1day在VNPY合成时不关心已经收到多少Bar, 所以影响也不大
                            # - 但其它分钟周期因为不好精确到每个品种, 修改后的freq可能有错
                            if index > current_datetime:
                                bar_is_completed = False
                                # 根据秒数算的话,要+1,例如13:31,freq=31,第31根bar
                                freq = NUM_MINUTE_MAPPING[period] - int(
                                    (index - current_datetime).total_seconds()
                                    / 60)
                        callback(add_bar, bar_is_completed, freq)
            else:
                self.write_log('dataframe => [ dict ]')
                ret_bars = list(data.T.to_dict().values())
            return True, ret_bars
        except Exception as ex:
            self.write_error('exception in get:{},{},{}'.format(
                tdx_symbol, str(ex), traceback.format_exc()))
            # print('exception in get:{},{},{}'.format(tdx_symbol,str(ex), traceback.format_exc()))
            self.write_log(u'重置连接')
            self.api = None
            self.connect(is_reconnect=True)
            return False, ret_bars