XtQuant-pro


NameXtQuant-pro JSON
Version 1.2.5 PyPI version JSON
download
home_pagehttps://github.com/yanjlee/XtQuant
Summary迅投QMT接口相关介绍和常用功能封装.
upload_time2024-06-01 08:45:22
maintainerNone
docs_urlNone
authoryanjlee
requires_pythonNone
licenseNone
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # XtQuant
迅投QMT接口相关介绍和常用功能封装

## 目录

- [XtQuant](#xtquant)
  - [目录](#目录)
  - [xtquant介绍](#xtquant介绍)
  - [行情接口分析](#行情接口分析)
    - [行情接口概况](#行情接口概况)
    - [实战:历史行情数据下载](#实战历史行情数据下载)
    - [实战:历史行情批量缓存](#实战历史行情批量缓存)
    - [实战:历史行情转存数据库](#实战历史行情转存数据库)
    - [实战:实时行情订阅](#实战实时行情订阅)
  - [未完待续......](#未完待续)

----

## xtquant介绍

**迅投QMT极速策略交易系统** 是一款专门针对券商、期货公司、信托等机构的高净值客户开发设计的集行情显示,投资研究,产品交易于一身,并自备完整风控系统的综合性平台。其自带投研量化平台可以灵活实现CTA,无风险套利等多种量化策略,并能够对策略进行回测检验和自动化交易。目前大部分券商都有支持策略交易,目前已知的像国金、国盛、国信、海通、华鑫等券商均有对普通用户开放,在开通资金门槛、功能阉割和佣金费率方面可能有一些差异,目前部分券商股票佣金可低至万1,可极大降低量化交易摩擦成本。

![策略回测系统](misc/strategy.png)

`xtquant`是`QMT`官方内置的`XtMiniQmt`极简客户端对应的Python接口,目前支持的版本为3.6~3.8,可支持历史行情下载、实时数据订阅、外部数据访问、普通账户和两融账户交易(需开通相关权限),对量化交易支持的比较完善,跟极速策略交易系统相比最主要的优势是简洁、灵活,不局限在bar、kline的事件触发,可以容易地集成多种数据源进行综合分析。相关文档可在仓库[文档](xtquant/doc)中详细阅读。

`QMT`内置的Python版本为3.6,第一次使用的话需手动下载相关的库,或直接拷贝已经下载好的`xtquant`库。


![](misc/setting.png)


`XtMiniQmt.exe`存在于QMT安装目录下的`bin.x64`子目录中, `xtquant`库默认安装在`bin.x64\Lib\site-packages`中。

内置的Python版本较老,对于一些较新的库支持有限,因此,如果我们想在自定义的`Python`中调用,如`Python3.8`,只需将`xtquant`拷贝到我们自己python安装目录的`Lib\site-packages`中便可,这里我的安装路径是 C:\ProgramData\Anaconda3\Lib\site-packages\xtquant。

`xtquant`主要包含两大块:
- **xtdata**:`xtdata`提供和`MiniQmt`的交互接口,本质是和`MiniQmt`建立连接,由`MiniQmt`处理行情数据请求,再把结果回传返回到`python`层。需要注意的是这个模块的使用目前并不需要登录,因此只要安装了`QMT`,就可以无门槛的使用其提供的数据服务。
- **xttrader**:`xttrader`是基于迅投`MiniQMT`衍生出来的一套完善的Python策略运行框架,对外以Python库的形式提供策略交易所需要的交易相关的API接口。该接口需开通A股实盘版权限方可使用。

在运行使用`XtQuant`的程序前需要先启动`MiniQMT`客户端。通常有两种方式,一种是直接启动极简QMT客户端`XtMiniQmt.exe`

![极简客户端](misc/XtMiniQmt.png)

如果登录时提示没有相关权限,可尝试启动QMT量化交易终端`XtItClient.exe`,在登录界面选择极简模式

![极简客户端](misc/XtItClient.png)



## 行情接口分析

QMT行情有两套不同的处理逻辑:
- 数据查询接口:使用时需要先确保MiniQmt已有所需要的数据,如果不足可以通过补充数据接口补充,再调用数据获取接口获取。适用于少量的实时行情数据和大批量的历史行情数据。
- 订阅接口:直接设置数据回调,数据到来时会由回调返回。订阅接收到的数据一般会保存下来,同种数据不需要再单独补充。适用于大批量的实时行情数据。

按照类别,主要有以下四类:
- 行情数据(K线数据、分笔数据,订阅和主动获取的接口)
- 财务数据
- 合约基础信息
- 基础行情数据板块分类信息等基础信息

### 行情接口概况

首先导入行情库:
``` python
from xtquant import xtdata
print(dir(xtdata))
```
可以看到行情主要分为以下几个模块:
- 实时行情订阅:subscribe* 系列
- 基本信息和行情查询:get_* 系列
- 历史数据订阅: download_* 系列 
- 历史数据处理: get_local_data

针对数据存储目录,默认为`xtdata.data_dir=../userdata_mini/datadir`, 按照官方文档的说明似乎可以任意设置,但实操下来却发现没起到作用。因此,如果默认存储空间有限的话,我们可以将其移动到有较大空间的地方,然后创建一个快捷方式指向原来的地方,避免磁盘空间被耗尽。

### 实战:历史行情数据下载

QMT提供的历史行情下载接口有两个:
- 单支股票下载:download_history_data(stock_code, period, start_time='', end_time='')
- 批量股票下载:download_history_data2(stock_list, period, start_time='', end_time='',callback=None)

其中各个参数具体含义如下:
- stock_code:股票名,以`code.exchange`的形式表示,exchange可从如下品种中选择
    - 上海证券(SH), 如`510050.SH`
    - 深圳证券(SZ), 如`159919.SZ`
    - 上海期权(SHO), 如`10004268.SHO`
    - 深圳期权(SZO), 如`90000967.SZO`
    - 中国金融期货(CFFEX), 如`IC07.CFFEX`
    - 郑州商品期货(CZCE), 如`SR05.CZCE`
    - 大连商品期货(DCE), 如`m2212.DCE`
    - 上海期货(SHFE), 如`wr2209.SHFE`
    - 能源中心(INE), 如`sc00.INE`
    - 香港联交所(HK), 如`00700.HK`
- stock_list, 股票列表,如['510050.SH', '159919.SZ']
- period, 数据周期,可选`1m`、`5m`、`1d`、`tick`, 分别表示1分钟K线、5分钟K线、1天K线、分笔数据
- start_time, 数据起始时间,格式YYYYMMDD/YYYYMMDDhhmmss/YYYYMMDDhhmmss.milli,如 "20200427" "20200427093000" "20200427093000.000"
- end_time,数据结束时间,格式同start_time

如果运行如下代码,下载深圳市场300ETF期权`沪深300ETF购9月4900`标的的tick行情,就会在`userdata_mini\datadir\SZO\0\90000967`目录下生成以日为单位的tick数据:

``` python
import pandas as pd
from xtquant import xtdata

xtdata.download_history_data('90000967.SZO', period='tick')
data = xtdata.get_local_data(field_list=[], stock_code=['90000967.SZO'], period='tick', count=10)

df = pd.DataFrame(data['90000967.SZO'])
print(df.iloc[-1])

```

![数据文件](misc/data_file.png)

上述二进制文件是无法直接读取的,这里通过`get_local_data`接口进行数据文件的解析,便可解码已经下载的上述tick行情,包含Unix时间戳、K线、买五卖五快照信息等:

![tick行情](misc/option_tick_data.png)

注意到这里的Unix时间戳是精确到毫秒的,可以通过datetime转换成字符型:
``` Python
import datetime
df['datetime'] = df['time'].apply(lambda x: datetime.datetime.fromtimestamp(x / 1000.0))
print(df)
```

![tick行情](misc/option_tick_timestamp.png)

### 实战:历史行情批量缓存

1、获取股票名称列表

QMT的行情函数暂时不能获取可转债列表,因此这里使用`akshare`库进行相关元数据的获取,使用前确保已安装。`akshare`库本身的功能十分强大,后续将详细展开,这里先不赘述。

首先导入相关的包:
```python
from xtquant import xtdata
import akshare as ak
from tqdm import tqdm
import pandas as pd
```

第一个接口是获取包含历史转债代码的列表,以方便同步历史数据,可转债上海市场以11开头,深圳市场以12开头,这里需要将akshare中来自东方财富的数据与QMT进行代码的对齐:
```python
def get_bond_history():
    bond_zh_cov_df = ak.bond_zh_cov()
    # 排除至今未上市的转债
    bond_zh_cov_df =  bond_zh_cov_df[bond_zh_cov_df['上市时间'] <= datetime.date.today()]
    stock_code_list, bond_code_list = [], []
    for _, row in bond_zh_cov_df.iterrows():
        if row['债券代码'].startswith('11'):
            market = '.SH'
        else:
            market = '.SZ'
        stock_code_list.append(row['正股代码'] + market)
        bond_code_list.append(row['债券代码'] + market)
    return stock_code_list, bond_code_list
```

第二个接口是获取实时转债代码的列表,以方便增量更新,避免重复下载:
```python
def get_bond_spot():
    bond_cov_comparison_df = ak.bond_cov_comparison()
    # 排除至今未上市的转债
    bond_cov_comparison_df =  bond_cov_comparison_df[bond_cov_comparison_df['上市日期'] !='-']

    stock_code_list, bond_code_list = [], []
    for _, row in bond_cov_comparison_df.iterrows():
        if row['转债代码'].startswith('11'):
            market = '.SH'
        else:
            market = '.SZ'
        stock_code_list.append(row['正股代码'] + market)
        bond_code_list.append(row['转债代码'] + market)
    return stock_code_list, bond_code_list
```

第三个接口是获取A股市场的沪深指数、所有A股、ETF、债券列表等股票代码,以便下载K线数据:
```python
def get_shse_a_list():
    '''
    获取沪深指数、所有A股、ETF、债券列表
    '''
    index_code = ['000001.SH', '399001.SZ', '399006.SZ', '000688.SH', '000300.SH', '000016.SH', '000905.SH', '000852.SH'] # 上证指数、深证成指、创业板指、科创50、沪深300、上证50、中证500、中证1000
    a_code = xtdata.get_stock_list_in_sector('沪深A股')
    etf_code =  xtdata.get_stock_list_in_sector('沪深ETF')
    #bond_code = [i for i in xtdata.get_stock_list_in_sector('沪深债券') if i[:3] in {'110',  '111', '113', '118', '123', '127', '128'}]
    bond_code = get_bond_history()[-1]

    return index_code + a_code + etf_code + bond_code
```

2、批量下载可转债tick数据

通过控制参数`init`来决定是否增量下载(以天为粒度):
```python
def download_history_bond_tick(init=1):
    '''
    下载历史转债tick数据(20200401起)
    '''
    # 初始化:获取转债及其正股代码
    if init:
        # 包含历史过期代码
        stock_code_list, bond_code_list = get_bond_history()
    else:
        # 仅当日代码
        stock_code_list, bond_code_list = get_bond_spot()
    
    # 数据下载目录
    data_dir = 'E:\\QMT\\userdata_mini\\datadir\\'
    for stock, bond in tqdm(zip(stock_code_list, bond_code_list), total=len(stock_code_list)):
        print("开始下载:股票 {}, 转债 {}".format(stock, bond))
        # 上海转债: 已下载的数据
        if bond.endswith("SH"):
            dir_path = data_dir + "\\SH\\0\\" + bond.split('.', 1)[0]
        # 深圳转债:已下载的数据
        else:
            dir_path = data_dir + "\\SZ\\0\\" + bond.split('.', 1)[0]
        
        start_date = '20200401' # QMT支持的最久数据时间
        # 如果路径存在,断点续传,重设起点下载时间
        if os.path.exists(dir_path):
            downloaded = os.listdir(dir_path)
            # 获取已下载的最大日期,作为本次同步的起始时间
            if len(downloaded) > 0:
                start_date = max(downloaded).split('.', 1)[0]
            
        xtdata.download_history_data(stock_code=bond, period='tick', start_time=start_date)

```

3、批量下载K线

通过传入参数`start_time`设置起始下载时间,参数`period`设置K线类型:
- 1m: 1分钟K线
- 1d: 1日K线

```python
def download_history_kline(start_time='', period='1m'):
    '''
    下载历史K线数据
    '''
    code_list = get_shse_a_list()
    print("本次开始下载的时间为:", datetime.datetime.now().strftime("%Y%m%d%H%M%S"))
    for code in tqdm(code_list):
        xtdata.download_history_data(code, period=period, start_time=start_time)

```

经过漫长的等待,本地便会有历史数据的缓存了,存储的目录形式为`datadir\SH\{0|60|86400}\{code}`,便于我们进一步加工处理。

### 实战:历史行情转存数据库

历史行情数据从格式上看分为tick数据和K线数据两大类,针对这两类的数据我们分别处理。

1、tick数据预处理

首先读取本地缓存数据,这里以南航转债(`110075.SH`)为例,需要注意的是tick数据包含了集合竞价时段,成交量/额是按日累计的,因此需要做一定的转换。
```python
from xtquant import xtdata
import pandas as pd
import datetime

def get_local_tick_data(code='110075.SH', start_time='19700101'):
    # 获取本地数据
    df = xtdata.get_local_data(stock_code=[code], period='tick', field_list=['time', 'open', 'lastPrice', 'high', 'low', 'lastClose', 'volume', 'amount', 'askPrice', 'bidPrice', 'askVol', 'bidVol'], start_time=start_time, end_time=start_time)

    # 转成DataFRame
    df = pd.DataFrame(df[code])
    if len(df) < 1:
        return df

    # 日期处理
    df['trade_time'] = df['time'].apply(lambda x: datetime.datetime.fromtimestamp(x / 1000.0)) # , cn_tz
    df['trade_day'] = df['trade_time'].apply(lambda x: x.date())
    df['trade_minute'] = df['trade_time'].apply(lambda x: x.hour * 60 + x.minute)
    df['trade_second'] = df['trade_time'].apply(lambda x: x.hour * 3600 + x.minute * 60 + x.second)
    df = df[df.trade_second <= 54001] # 排除盘后交易
    df = df[df.trade_second >= 33840] # 保留最后一分钟的集合竞价数据
    df = df.reset_index(drop=True)

    # 重新计算成交量、成交额
    df['volume_deal'] = df.groupby(['trade_day'])['volume'].diff(periods=1).fillna(0)
    df['amount_deal'] = df.groupby(['trade_day'])['amount'].diff(periods=1).fillna(0)

    # 重新选择列
    df['code'] = '110075.SH'
    df['close'] = df['lastPrice'] # 收盘
    df['last'] = df['lastClose'] # 昨收
    df = df[['code', 'trade_time', 'trade_day', 'trade_minute', 'open', 'close', 'high', 'low', 'last', 'volume', 'amount', 'volume_deal', 'amount_deal', 'askPrice', 'bidPrice', 'askVol', 'bidVol']]

    return df

df = get_local_tick_data(code='110075.SH', start_time='20220630')
print(df.iloc[-1])
```

最终,我们得到诸如下图的tick存储数据:

![](misc/tick_data_store.png)

2、K线数据预处理

读取本地缓存数据,这里以行业板块指数为例,首先获取行业指数,然后查询详情,获取元数据:

```python
def get_sector_list():
    '''
    获取沪深指数、行业指数
    '''
    sector_1 = xtdata.get_stock_list_in_sector('证监会行业板块指数')
    sector_1 = [(i, xtdata.get_instrument_detail(i)['InstrumentName'], '证监会一级行业') for i in sector_1]

    sector_2 = xtdata.get_stock_list_in_sector('板块指数')
    sector_2 = [(i, xtdata.get_instrument_detail(i)['InstrumentName'], '证监会二级行业') for i in sector_2 if i.startswith('23')]


    index_code = [('000001.SH', '上证指数', '大盘指数'), ('399001.SZ', '深证成指', '大盘指数'), ('399006.SZ', '创业板指', '大盘指数'), ('000688.SH', '科创50', '大盘指数'), ('000300.SH', '沪深300', '大盘指数'), ('000016.SH', '上证50', '大盘指数'), ('000905.SH', '中证500', '大盘指数'), ('000852.SH', '中证1000', '大盘指数')]

    code_list = {i[0]: i[1:] for i in sector_1 + sector_2 + index_code}

    return code_list
```

然后处理本地行情,这里以证监会二级行业行业中的餐饮业(`230130.BKZS`)为例:
``` python
def get_local_kline_data(code='230130.BKZS', start_time='20200101', period='1d', code_list =  get_sector_list()):
    # 获取本地数据
    df = xtdata.get_local_data(stock_code=[code], period='1d', field_list=['time', 'open', 'close', 'high', 'low', 'volume', 'amount'], start_time=start_time, end_time=datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
    df = pd.concat([df[i].T.rename(columns={code:i}) for i in ['time', 'open', 'close', 'high', 'low', 'volume', 'amount']], axis=1)

    if len(df) < 1:
        return df

    # 时间转换
    df['trade_day'] = df['time'].apply(lambda x: datetime.datetime.fromtimestamp(x / 1000.0).date())

    # 重新选择列
    df['code'] = code
    df['sector_name'] = df['code'].apply(lambda x: code_list[x][0])
    df['sector_type'] = df['code'].apply(lambda x: code_list[x][1])
    df = df[['code', 'trade_day', 'sector_name', 'sector_type', 'open', 'close', 'high', 'low', 'volume', 'amount']]

    return df
```
![](misc/kline_data_store.png)

3、Clickhouse数据库设计

这里选用Clickhouse,而不是MySQL的主要原因是性能问题。行情数据一旦写入,几乎不会更新,并且量非常大,没有复杂的表关联,MySQL在这种场景下主要的问题是存储空间占用多、读写慢,而ClickHouse主要用于在线分析处理查询(OLAP),具有高效的数据压缩、向量引擎、列式存储特性,非常适合金融行情数据存储。

```SQL
create database xtquant

CREATE TABLE IF NOT EXISTS xtquant.bond_tick
(
    code String, 
    trade_time DateTime('Asia/Shanghai'), 
    trade_day Date, 
    trade_minute Int16, 
    open Nullable(Float32), 
    close Nullable(Float32), 
    high Nullable(Float32), 
    low Nullable(Float32), 
    last Nullable(Float32), 
    volume Nullable(Float64), 
    amount Nullable(Float64), 
    volume_deal Nullable(Float32), 
    amount_deal Nullable(Float32), 
    askPrice Array(Nullable(Float32)), 
    bidPrice Array(Nullable(Float32)), 
    askVol Array(Nullable(Float32)), 
    bidVol Array(Nullable(Float32))
)
ENGINE = ReplacingMergeTree()
ORDER BY (trade_time, code, trade_day)

CREATE TABLE IF NOT EXISTS xtquant.sector_1d
(
    code String, 
    trade_day Date, 
    sector_name String,
    sector_type String,
    open Nullable(Float32), 
    close Nullable(Float32), 
    high Nullable(Float32), 
    low Nullable(Float32), 
    volume Nullable(Float64), 
    amount Nullable(Float64)
)
ENGINE = ReplacingMergeTree()
ORDER BY (trade_day, code)
```

4、Clickhouse数据批量写入

设计好数据表后,利用`clickhouse_driver`库提供的接口将数据同步到数据库中。

对于tick数据,按天来遍历插入,对于k线数据,则直接存入,为了增量同步,写入时可查询已有数据的最大时间,避免重复写。

```python
import os
from clickhouse_driver import Client
from tqdm import tqdm

storage_client = Client('10.0.16.11', password='******', settings={'use_numpy': True})

# 可转债tick数据
# 获取可转债列表
_, bond_code_list = get_bond_history()
for code in tqdm(bond_code_list):
    start_date = storage_client.execute("select max(trade_day) from xtquant.bond_tick where code='{}'".format(code))
    start_date = str(start_date[0][0]).replace('-', '')
    start_date = max(start_date, '20200401')
    trade_dates = xtdata.get_trading_dates('SH', start_time=start_date, end_time=datetime.date.today().strftime('%Y%m%d'))
    for day in trade_dates:
        day = datetime.datetime.fromtimestamp(day / 1000.0).strftime('%Y%m%d')
    df = get_local_tick_data(code=code, start_time=day)
    if len(df) > 0:
        storage_client.insert_dataframe('INSERT INTO xtquant.bond_tick VALUES', df)

# 行业1d数据
# 获取行业列表
sector_code_list = get_sector_list()
for code in tqdm(sector_code_list):
    start_date = storage_client.execute("select max(trade_day) from xtquant.sector_1d where code='{}'".format(code))
    start_date = str(start_date[0][0]).replace('-', '')
    start_date = max(start_date, '20100101')
    df = get_local_kline_data(code=code, start_time=start_date, period='1d', code_list =  sector_code_list)
    if len(df) > 0:
        storage_client.insert_dataframe('INSERT INTO xtquant.sector_1d VALUES', df)
```

运行如上代码后,我们便可在clickhouse客户端中查询到已经写入的数据:

![](misc/clickhouse_query.png)

### 实战:实时行情订阅


未完待续......
---

欢迎关注我的公众号“**量化实战**”,原创技术文章第一时间推送。

![公众号](misc/qrcode.png)

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/yanjlee/XtQuant",
    "name": "XtQuant-pro",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": null,
    "author": "yanjlee",
    "author_email": "yanjlee@163.com",
    "download_url": "https://files.pythonhosted.org/packages/9c/d2/2a01b55c9a0683f0e4d105c17edf8e392208fd94ef9531b65484e8c3e673/xtquant_pro-1.2.5.tar.gz",
    "platform": null,
    "description": "# XtQuant\r\n\u8fc5\u6295QMT\u63a5\u53e3\u76f8\u5173\u4ecb\u7ecd\u548c\u5e38\u7528\u529f\u80fd\u5c01\u88c5\r\n\r\n## \u76ee\u5f55\r\n\r\n- [XtQuant](#xtquant)\r\n  - [\u76ee\u5f55](#\u76ee\u5f55)\r\n  - [xtquant\u4ecb\u7ecd](#xtquant\u4ecb\u7ecd)\r\n  - [\u884c\u60c5\u63a5\u53e3\u5206\u6790](#\u884c\u60c5\u63a5\u53e3\u5206\u6790)\r\n    - [\u884c\u60c5\u63a5\u53e3\u6982\u51b5](#\u884c\u60c5\u63a5\u53e3\u6982\u51b5)\r\n    - [\u5b9e\u6218\uff1a\u5386\u53f2\u884c\u60c5\u6570\u636e\u4e0b\u8f7d](#\u5b9e\u6218\u5386\u53f2\u884c\u60c5\u6570\u636e\u4e0b\u8f7d)\r\n    - [\u5b9e\u6218\uff1a\u5386\u53f2\u884c\u60c5\u6279\u91cf\u7f13\u5b58](#\u5b9e\u6218\u5386\u53f2\u884c\u60c5\u6279\u91cf\u7f13\u5b58)\r\n    - [\u5b9e\u6218\uff1a\u5386\u53f2\u884c\u60c5\u8f6c\u5b58\u6570\u636e\u5e93](#\u5b9e\u6218\u5386\u53f2\u884c\u60c5\u8f6c\u5b58\u6570\u636e\u5e93)\r\n    - [\u5b9e\u6218\uff1a\u5b9e\u65f6\u884c\u60c5\u8ba2\u9605](#\u5b9e\u6218\u5b9e\u65f6\u884c\u60c5\u8ba2\u9605)\r\n  - [\u672a\u5b8c\u5f85\u7eed......](#\u672a\u5b8c\u5f85\u7eed)\r\n\r\n----\r\n\r\n## xtquant\u4ecb\u7ecd\r\n\r\n**\u8fc5\u6295QMT\u6781\u901f\u7b56\u7565\u4ea4\u6613\u7cfb\u7edf** \u662f\u4e00\u6b3e\u4e13\u95e8\u9488\u5bf9\u5238\u5546\u3001\u671f\u8d27\u516c\u53f8\u3001\u4fe1\u6258\u7b49\u673a\u6784\u7684\u9ad8\u51c0\u503c\u5ba2\u6237\u5f00\u53d1\u8bbe\u8ba1\u7684\u96c6\u884c\u60c5\u663e\u793a\uff0c\u6295\u8d44\u7814\u7a76\uff0c\u4ea7\u54c1\u4ea4\u6613\u4e8e\u4e00\u8eab\uff0c\u5e76\u81ea\u5907\u5b8c\u6574\u98ce\u63a7\u7cfb\u7edf\u7684\u7efc\u5408\u6027\u5e73\u53f0\u3002\u5176\u81ea\u5e26\u6295\u7814\u91cf\u5316\u5e73\u53f0\u53ef\u4ee5\u7075\u6d3b\u5b9e\u73b0CTA\uff0c\u65e0\u98ce\u9669\u5957\u5229\u7b49\u591a\u79cd\u91cf\u5316\u7b56\u7565\uff0c\u5e76\u80fd\u591f\u5bf9\u7b56\u7565\u8fdb\u884c\u56de\u6d4b\u68c0\u9a8c\u548c\u81ea\u52a8\u5316\u4ea4\u6613\u3002\u76ee\u524d\u5927\u90e8\u5206\u5238\u5546\u90fd\u6709\u652f\u6301\u7b56\u7565\u4ea4\u6613\uff0c\u76ee\u524d\u5df2\u77e5\u7684\u50cf\u56fd\u91d1\u3001\u56fd\u76db\u3001\u56fd\u4fe1\u3001\u6d77\u901a\u3001\u534e\u946b\u7b49\u5238\u5546\u5747\u6709\u5bf9\u666e\u901a\u7528\u6237\u5f00\u653e\uff0c\u5728\u5f00\u901a\u8d44\u91d1\u95e8\u69db\u3001\u529f\u80fd\u9609\u5272\u548c\u4f63\u91d1\u8d39\u7387\u65b9\u9762\u53ef\u80fd\u6709\u4e00\u4e9b\u5dee\u5f02\uff0c\u76ee\u524d\u90e8\u5206\u5238\u5546\u80a1\u7968\u4f63\u91d1\u53ef\u4f4e\u81f3\u4e071\uff0c\u53ef\u6781\u5927\u964d\u4f4e\u91cf\u5316\u4ea4\u6613\u6469\u64e6\u6210\u672c\u3002\r\n\r\n![\u7b56\u7565\u56de\u6d4b\u7cfb\u7edf](misc/strategy.png)\r\n\r\n`xtquant`\u662f`QMT`\u5b98\u65b9\u5185\u7f6e\u7684`XtMiniQmt`\u6781\u7b80\u5ba2\u6237\u7aef\u5bf9\u5e94\u7684Python\u63a5\u53e3\uff0c\u76ee\u524d\u652f\u6301\u7684\u7248\u672c\u4e3a3.6~3.8\uff0c\u53ef\u652f\u6301\u5386\u53f2\u884c\u60c5\u4e0b\u8f7d\u3001\u5b9e\u65f6\u6570\u636e\u8ba2\u9605\u3001\u5916\u90e8\u6570\u636e\u8bbf\u95ee\u3001\u666e\u901a\u8d26\u6237\u548c\u4e24\u878d\u8d26\u6237\u4ea4\u6613(\u9700\u5f00\u901a\u76f8\u5173\u6743\u9650)\uff0c\u5bf9\u91cf\u5316\u4ea4\u6613\u652f\u6301\u7684\u6bd4\u8f83\u5b8c\u5584\uff0c\u8ddf\u6781\u901f\u7b56\u7565\u4ea4\u6613\u7cfb\u7edf\u76f8\u6bd4\u6700\u4e3b\u8981\u7684\u4f18\u52bf\u662f\u7b80\u6d01\u3001\u7075\u6d3b\uff0c\u4e0d\u5c40\u9650\u5728bar\u3001kline\u7684\u4e8b\u4ef6\u89e6\u53d1\uff0c\u53ef\u4ee5\u5bb9\u6613\u5730\u96c6\u6210\u591a\u79cd\u6570\u636e\u6e90\u8fdb\u884c\u7efc\u5408\u5206\u6790\u3002\u76f8\u5173\u6587\u6863\u53ef\u5728\u4ed3\u5e93[\u6587\u6863](xtquant/doc)\u4e2d\u8be6\u7ec6\u9605\u8bfb\u3002\r\n\r\n`QMT`\u5185\u7f6e\u7684Python\u7248\u672c\u4e3a3.6\uff0c\u7b2c\u4e00\u6b21\u4f7f\u7528\u7684\u8bdd\u9700\u624b\u52a8\u4e0b\u8f7d\u76f8\u5173\u7684\u5e93\uff0c\u6216\u76f4\u63a5\u62f7\u8d1d\u5df2\u7ecf\u4e0b\u8f7d\u597d\u7684`xtquant`\u5e93\u3002\r\n\r\n\r\n![](misc/setting.png)\r\n\r\n\r\n`XtMiniQmt.exe`\u5b58\u5728\u4e8eQMT\u5b89\u88c5\u76ee\u5f55\u4e0b\u7684`bin.x64`\u5b50\u76ee\u5f55\u4e2d, `xtquant`\u5e93\u9ed8\u8ba4\u5b89\u88c5\u5728`bin.x64\\Lib\\site-packages`\u4e2d\u3002\r\n\r\n\u5185\u7f6e\u7684Python\u7248\u672c\u8f83\u8001\uff0c\u5bf9\u4e8e\u4e00\u4e9b\u8f83\u65b0\u7684\u5e93\u652f\u6301\u6709\u9650\uff0c\u56e0\u6b64\uff0c\u5982\u679c\u6211\u4eec\u60f3\u5728\u81ea\u5b9a\u4e49\u7684`Python`\u4e2d\u8c03\u7528\uff0c\u5982`Python3.8`\uff0c\u53ea\u9700\u5c06`xtquant`\u62f7\u8d1d\u5230\u6211\u4eec\u81ea\u5df1python\u5b89\u88c5\u76ee\u5f55\u7684`Lib\\site-packages`\u4e2d\u4fbf\u53ef\uff0c\u8fd9\u91cc\u6211\u7684\u5b89\u88c5\u8def\u5f84\u662f C:\\ProgramData\\Anaconda3\\Lib\\site-packages\\xtquant\u3002\r\n\r\n`xtquant`\u4e3b\u8981\u5305\u542b\u4e24\u5927\u5757\uff1a\r\n- **xtdata**\uff1a`xtdata`\u63d0\u4f9b\u548c`MiniQmt`\u7684\u4ea4\u4e92\u63a5\u53e3\uff0c\u672c\u8d28\u662f\u548c`MiniQmt`\u5efa\u7acb\u8fde\u63a5\uff0c\u7531`MiniQmt`\u5904\u7406\u884c\u60c5\u6570\u636e\u8bf7\u6c42\uff0c\u518d\u628a\u7ed3\u679c\u56de\u4f20\u8fd4\u56de\u5230`python`\u5c42\u3002\u9700\u8981\u6ce8\u610f\u7684\u662f\u8fd9\u4e2a\u6a21\u5757\u7684\u4f7f\u7528\u76ee\u524d\u5e76\u4e0d\u9700\u8981\u767b\u5f55\uff0c\u56e0\u6b64\u53ea\u8981\u5b89\u88c5\u4e86`QMT`,\u5c31\u53ef\u4ee5\u65e0\u95e8\u69db\u7684\u4f7f\u7528\u5176\u63d0\u4f9b\u7684\u6570\u636e\u670d\u52a1\u3002\r\n- **xttrader**\uff1a`xttrader`\u662f\u57fa\u4e8e\u8fc5\u6295`MiniQMT`\u884d\u751f\u51fa\u6765\u7684\u4e00\u5957\u5b8c\u5584\u7684Python\u7b56\u7565\u8fd0\u884c\u6846\u67b6\uff0c\u5bf9\u5916\u4ee5Python\u5e93\u7684\u5f62\u5f0f\u63d0\u4f9b\u7b56\u7565\u4ea4\u6613\u6240\u9700\u8981\u7684\u4ea4\u6613\u76f8\u5173\u7684API\u63a5\u53e3\u3002\u8be5\u63a5\u53e3\u9700\u5f00\u901aA\u80a1\u5b9e\u76d8\u7248\u6743\u9650\u65b9\u53ef\u4f7f\u7528\u3002\r\n\r\n\u5728\u8fd0\u884c\u4f7f\u7528`XtQuant`\u7684\u7a0b\u5e8f\u524d\u9700\u8981\u5148\u542f\u52a8`MiniQMT`\u5ba2\u6237\u7aef\u3002\u901a\u5e38\u6709\u4e24\u79cd\u65b9\u5f0f\uff0c\u4e00\u79cd\u662f\u76f4\u63a5\u542f\u52a8\u6781\u7b80QMT\u5ba2\u6237\u7aef`XtMiniQmt.exe`\r\n\r\n![\u6781\u7b80\u5ba2\u6237\u7aef](misc/XtMiniQmt.png)\r\n\r\n\u5982\u679c\u767b\u5f55\u65f6\u63d0\u793a\u6ca1\u6709\u76f8\u5173\u6743\u9650\uff0c\u53ef\u5c1d\u8bd5\u542f\u52a8QMT\u91cf\u5316\u4ea4\u6613\u7ec8\u7aef`XtItClient.exe`,\u5728\u767b\u5f55\u754c\u9762\u9009\u62e9\u6781\u7b80\u6a21\u5f0f\r\n\r\n![\u6781\u7b80\u5ba2\u6237\u7aef](misc/XtItClient.png)\r\n\r\n\r\n\r\n## \u884c\u60c5\u63a5\u53e3\u5206\u6790\r\n\r\nQMT\u884c\u60c5\u6709\u4e24\u5957\u4e0d\u540c\u7684\u5904\u7406\u903b\u8f91\uff1a\r\n- \u6570\u636e\u67e5\u8be2\u63a5\u53e3\uff1a\u4f7f\u7528\u65f6\u9700\u8981\u5148\u786e\u4fddMiniQmt\u5df2\u6709\u6240\u9700\u8981\u7684\u6570\u636e\uff0c\u5982\u679c\u4e0d\u8db3\u53ef\u4ee5\u901a\u8fc7\u8865\u5145\u6570\u636e\u63a5\u53e3\u8865\u5145\uff0c\u518d\u8c03\u7528\u6570\u636e\u83b7\u53d6\u63a5\u53e3\u83b7\u53d6\u3002\u9002\u7528\u4e8e\u5c11\u91cf\u7684\u5b9e\u65f6\u884c\u60c5\u6570\u636e\u548c\u5927\u6279\u91cf\u7684\u5386\u53f2\u884c\u60c5\u6570\u636e\u3002\r\n- \u8ba2\u9605\u63a5\u53e3\uff1a\u76f4\u63a5\u8bbe\u7f6e\u6570\u636e\u56de\u8c03\uff0c\u6570\u636e\u5230\u6765\u65f6\u4f1a\u7531\u56de\u8c03\u8fd4\u56de\u3002\u8ba2\u9605\u63a5\u6536\u5230\u7684\u6570\u636e\u4e00\u822c\u4f1a\u4fdd\u5b58\u4e0b\u6765\uff0c\u540c\u79cd\u6570\u636e\u4e0d\u9700\u8981\u518d\u5355\u72ec\u8865\u5145\u3002\u9002\u7528\u4e8e\u5927\u6279\u91cf\u7684\u5b9e\u65f6\u884c\u60c5\u6570\u636e\u3002\r\n\r\n\u6309\u7167\u7c7b\u522b\uff0c\u4e3b\u8981\u6709\u4ee5\u4e0b\u56db\u7c7b\uff1a\r\n- \u884c\u60c5\u6570\u636e\uff08K\u7ebf\u6570\u636e\u3001\u5206\u7b14\u6570\u636e\uff0c\u8ba2\u9605\u548c\u4e3b\u52a8\u83b7\u53d6\u7684\u63a5\u53e3\uff09\r\n- \u8d22\u52a1\u6570\u636e\r\n- \u5408\u7ea6\u57fa\u7840\u4fe1\u606f\r\n- \u57fa\u7840\u884c\u60c5\u6570\u636e\u677f\u5757\u5206\u7c7b\u4fe1\u606f\u7b49\u57fa\u7840\u4fe1\u606f\r\n\r\n### \u884c\u60c5\u63a5\u53e3\u6982\u51b5\r\n\r\n\u9996\u5148\u5bfc\u5165\u884c\u60c5\u5e93\uff1a\r\n``` python\r\nfrom xtquant import xtdata\r\nprint(dir(xtdata))\r\n```\r\n\u53ef\u4ee5\u770b\u5230\u884c\u60c5\u4e3b\u8981\u5206\u4e3a\u4ee5\u4e0b\u51e0\u4e2a\u6a21\u5757\uff1a\r\n- \u5b9e\u65f6\u884c\u60c5\u8ba2\u9605\uff1asubscribe* \u7cfb\u5217\r\n- \u57fa\u672c\u4fe1\u606f\u548c\u884c\u60c5\u67e5\u8be2\uff1aget_* \u7cfb\u5217\r\n- \u5386\u53f2\u6570\u636e\u8ba2\u9605\uff1a download_* \u7cfb\u5217 \r\n- \u5386\u53f2\u6570\u636e\u5904\u7406\uff1a get_local_data\r\n\r\n\u9488\u5bf9\u6570\u636e\u5b58\u50a8\u76ee\u5f55\uff0c\u9ed8\u8ba4\u4e3a`xtdata.data_dir=../userdata_mini/datadir`, \u6309\u7167\u5b98\u65b9\u6587\u6863\u7684\u8bf4\u660e\u4f3c\u4e4e\u53ef\u4ee5\u4efb\u610f\u8bbe\u7f6e\uff0c\u4f46\u5b9e\u64cd\u4e0b\u6765\u5374\u53d1\u73b0\u6ca1\u8d77\u5230\u4f5c\u7528\u3002\u56e0\u6b64\uff0c\u5982\u679c\u9ed8\u8ba4\u5b58\u50a8\u7a7a\u95f4\u6709\u9650\u7684\u8bdd\uff0c\u6211\u4eec\u53ef\u4ee5\u5c06\u5176\u79fb\u52a8\u5230\u6709\u8f83\u5927\u7a7a\u95f4\u7684\u5730\u65b9\uff0c\u7136\u540e\u521b\u5efa\u4e00\u4e2a\u5feb\u6377\u65b9\u5f0f\u6307\u5411\u539f\u6765\u7684\u5730\u65b9\uff0c\u907f\u514d\u78c1\u76d8\u7a7a\u95f4\u88ab\u8017\u5c3d\u3002\r\n\r\n### \u5b9e\u6218\uff1a\u5386\u53f2\u884c\u60c5\u6570\u636e\u4e0b\u8f7d\r\n\r\nQMT\u63d0\u4f9b\u7684\u5386\u53f2\u884c\u60c5\u4e0b\u8f7d\u63a5\u53e3\u6709\u4e24\u4e2a\uff1a\r\n- \u5355\u652f\u80a1\u7968\u4e0b\u8f7d\uff1adownload_history_data(stock_code, period, start_time='', end_time='')\r\n- \u6279\u91cf\u80a1\u7968\u4e0b\u8f7d\uff1adownload_history_data2(stock_list, period, start_time='', end_time='',callback=None)\r\n\r\n\u5176\u4e2d\u5404\u4e2a\u53c2\u6570\u5177\u4f53\u542b\u4e49\u5982\u4e0b\uff1a\r\n- stock_code\uff1a\u80a1\u7968\u540d\uff0c\u4ee5`code.exchange`\u7684\u5f62\u5f0f\u8868\u793a\uff0cexchange\u53ef\u4ece\u5982\u4e0b\u54c1\u79cd\u4e2d\u9009\u62e9\r\n    - \u4e0a\u6d77\u8bc1\u5238(SH), \u5982`510050.SH`\r\n    - \u6df1\u5733\u8bc1\u5238(SZ), \u5982`159919.SZ`\r\n    - \u4e0a\u6d77\u671f\u6743(SHO), \u5982`10004268.SHO`\r\n    - \u6df1\u5733\u671f\u6743(SZO), \u5982`90000967.SZO`\r\n    - \u4e2d\u56fd\u91d1\u878d\u671f\u8d27(CFFEX), \u5982`IC07.CFFEX`\r\n    - \u90d1\u5dde\u5546\u54c1\u671f\u8d27(CZCE), \u5982`SR05.CZCE`\r\n    - \u5927\u8fde\u5546\u54c1\u671f\u8d27(DCE), \u5982`m2212.DCE`\r\n    - \u4e0a\u6d77\u671f\u8d27(SHFE), \u5982`wr2209.SHFE`\r\n    - \u80fd\u6e90\u4e2d\u5fc3(INE), \u5982`sc00.INE`\r\n    - \u9999\u6e2f\u8054\u4ea4\u6240(HK), \u5982`00700.HK`\r\n- stock_list, \u80a1\u7968\u5217\u8868\uff0c\u5982['510050.SH', '159919.SZ']\r\n- period, \u6570\u636e\u5468\u671f\uff0c\u53ef\u9009`1m`\u3001`5m`\u3001`1d`\u3001`tick`, \u5206\u522b\u8868\u793a1\u5206\u949fK\u7ebf\u30015\u5206\u949fK\u7ebf\u30011\u5929K\u7ebf\u3001\u5206\u7b14\u6570\u636e\r\n- start_time, \u6570\u636e\u8d77\u59cb\u65f6\u95f4\uff0c\u683c\u5f0fYYYYMMDD/YYYYMMDDhhmmss/YYYYMMDDhhmmss.milli\uff0c\u5982 \"20200427\" \"20200427093000\" \"20200427093000.000\"\r\n- end_time\uff0c\u6570\u636e\u7ed3\u675f\u65f6\u95f4\uff0c\u683c\u5f0f\u540cstart_time\r\n\r\n\u5982\u679c\u8fd0\u884c\u5982\u4e0b\u4ee3\u7801\uff0c\u4e0b\u8f7d\u6df1\u5733\u5e02\u573a300ETF\u671f\u6743`\u6caa\u6df1300ETF\u8d2d9\u67084900`\u6807\u7684\u7684tick\u884c\u60c5\uff0c\u5c31\u4f1a\u5728`userdata_mini\\datadir\\SZO\\0\\90000967`\u76ee\u5f55\u4e0b\u751f\u6210\u4ee5\u65e5\u4e3a\u5355\u4f4d\u7684tick\u6570\u636e\uff1a\r\n\r\n``` python\r\nimport pandas as pd\r\nfrom xtquant import xtdata\r\n\r\nxtdata.download_history_data('90000967.SZO', period='tick')\r\ndata = xtdata.get_local_data(field_list=[], stock_code=['90000967.SZO'], period='tick', count=10)\r\n\r\ndf = pd.DataFrame(data['90000967.SZO'])\r\nprint(df.iloc[-1])\r\n\r\n```\r\n\r\n![\u6570\u636e\u6587\u4ef6](misc/data_file.png)\r\n\r\n\u4e0a\u8ff0\u4e8c\u8fdb\u5236\u6587\u4ef6\u662f\u65e0\u6cd5\u76f4\u63a5\u8bfb\u53d6\u7684\uff0c\u8fd9\u91cc\u901a\u8fc7`get_local_data`\u63a5\u53e3\u8fdb\u884c\u6570\u636e\u6587\u4ef6\u7684\u89e3\u6790\uff0c\u4fbf\u53ef\u89e3\u7801\u5df2\u7ecf\u4e0b\u8f7d\u7684\u4e0a\u8ff0tick\u884c\u60c5\uff0c\u5305\u542bUnix\u65f6\u95f4\u6233\u3001K\u7ebf\u3001\u4e70\u4e94\u5356\u4e94\u5feb\u7167\u4fe1\u606f\u7b49\uff1a\r\n\r\n![tick\u884c\u60c5](misc/option_tick_data.png)\r\n\r\n\u6ce8\u610f\u5230\u8fd9\u91cc\u7684Unix\u65f6\u95f4\u6233\u662f\u7cbe\u786e\u5230\u6beb\u79d2\u7684\uff0c\u53ef\u4ee5\u901a\u8fc7datetime\u8f6c\u6362\u6210\u5b57\u7b26\u578b\uff1a\r\n``` Python\r\nimport datetime\r\ndf['datetime'] = df['time'].apply(lambda x: datetime.datetime.fromtimestamp(x / 1000.0))\r\nprint(df)\r\n```\r\n\r\n![tick\u884c\u60c5](misc/option_tick_timestamp.png)\r\n\r\n### \u5b9e\u6218\uff1a\u5386\u53f2\u884c\u60c5\u6279\u91cf\u7f13\u5b58\r\n\r\n1\u3001\u83b7\u53d6\u80a1\u7968\u540d\u79f0\u5217\u8868\r\n\r\nQMT\u7684\u884c\u60c5\u51fd\u6570\u6682\u65f6\u4e0d\u80fd\u83b7\u53d6\u53ef\u8f6c\u503a\u5217\u8868\uff0c\u56e0\u6b64\u8fd9\u91cc\u4f7f\u7528`akshare`\u5e93\u8fdb\u884c\u76f8\u5173\u5143\u6570\u636e\u7684\u83b7\u53d6\uff0c\u4f7f\u7528\u524d\u786e\u4fdd\u5df2\u5b89\u88c5\u3002`akshare`\u5e93\u672c\u8eab\u7684\u529f\u80fd\u5341\u5206\u5f3a\u5927\uff0c\u540e\u7eed\u5c06\u8be6\u7ec6\u5c55\u5f00\uff0c\u8fd9\u91cc\u5148\u4e0d\u8d58\u8ff0\u3002\r\n\r\n\u9996\u5148\u5bfc\u5165\u76f8\u5173\u7684\u5305\uff1a\r\n```python\r\nfrom xtquant import xtdata\r\nimport akshare as ak\r\nfrom tqdm import tqdm\r\nimport pandas as pd\r\n```\r\n\r\n\u7b2c\u4e00\u4e2a\u63a5\u53e3\u662f\u83b7\u53d6\u5305\u542b\u5386\u53f2\u8f6c\u503a\u4ee3\u7801\u7684\u5217\u8868\uff0c\u4ee5\u65b9\u4fbf\u540c\u6b65\u5386\u53f2\u6570\u636e\uff0c\u53ef\u8f6c\u503a\u4e0a\u6d77\u5e02\u573a\u4ee511\u5f00\u5934\uff0c\u6df1\u5733\u5e02\u573a\u4ee512\u5f00\u5934\uff0c\u8fd9\u91cc\u9700\u8981\u5c06akshare\u4e2d\u6765\u81ea\u4e1c\u65b9\u8d22\u5bcc\u7684\u6570\u636e\u4e0eQMT\u8fdb\u884c\u4ee3\u7801\u7684\u5bf9\u9f50\uff1a\r\n```python\r\ndef get_bond_history():\r\n    bond_zh_cov_df = ak.bond_zh_cov()\r\n    # \u6392\u9664\u81f3\u4eca\u672a\u4e0a\u5e02\u7684\u8f6c\u503a\r\n    bond_zh_cov_df =  bond_zh_cov_df[bond_zh_cov_df['\u4e0a\u5e02\u65f6\u95f4'] <= datetime.date.today()]\r\n    stock_code_list, bond_code_list = [], []\r\n    for _, row in bond_zh_cov_df.iterrows():\r\n        if row['\u503a\u5238\u4ee3\u7801'].startswith('11'):\r\n            market = '.SH'\r\n        else:\r\n            market = '.SZ'\r\n        stock_code_list.append(row['\u6b63\u80a1\u4ee3\u7801'] + market)\r\n        bond_code_list.append(row['\u503a\u5238\u4ee3\u7801'] + market)\r\n    return stock_code_list, bond_code_list\r\n```\r\n\r\n\u7b2c\u4e8c\u4e2a\u63a5\u53e3\u662f\u83b7\u53d6\u5b9e\u65f6\u8f6c\u503a\u4ee3\u7801\u7684\u5217\u8868\uff0c\u4ee5\u65b9\u4fbf\u589e\u91cf\u66f4\u65b0\uff0c\u907f\u514d\u91cd\u590d\u4e0b\u8f7d\uff1a\r\n```python\r\ndef get_bond_spot():\r\n    bond_cov_comparison_df = ak.bond_cov_comparison()\r\n    # \u6392\u9664\u81f3\u4eca\u672a\u4e0a\u5e02\u7684\u8f6c\u503a\r\n    bond_cov_comparison_df =  bond_cov_comparison_df[bond_cov_comparison_df['\u4e0a\u5e02\u65e5\u671f'] !='-']\r\n\r\n    stock_code_list, bond_code_list = [], []\r\n    for _, row in bond_cov_comparison_df.iterrows():\r\n        if row['\u8f6c\u503a\u4ee3\u7801'].startswith('11'):\r\n            market = '.SH'\r\n        else:\r\n            market = '.SZ'\r\n        stock_code_list.append(row['\u6b63\u80a1\u4ee3\u7801'] + market)\r\n        bond_code_list.append(row['\u8f6c\u503a\u4ee3\u7801'] + market)\r\n    return stock_code_list, bond_code_list\r\n```\r\n\r\n\u7b2c\u4e09\u4e2a\u63a5\u53e3\u662f\u83b7\u53d6A\u80a1\u5e02\u573a\u7684\u6caa\u6df1\u6307\u6570\u3001\u6240\u6709A\u80a1\u3001ETF\u3001\u503a\u5238\u5217\u8868\u7b49\u80a1\u7968\u4ee3\u7801\uff0c\u4ee5\u4fbf\u4e0b\u8f7dK\u7ebf\u6570\u636e\uff1a\r\n```python\r\ndef get_shse_a_list():\r\n    '''\r\n    \u83b7\u53d6\u6caa\u6df1\u6307\u6570\u3001\u6240\u6709A\u80a1\u3001ETF\u3001\u503a\u5238\u5217\u8868\r\n    '''\r\n    index_code = ['000001.SH', '399001.SZ', '399006.SZ', '000688.SH', '000300.SH', '000016.SH', '000905.SH', '000852.SH'] # \u4e0a\u8bc1\u6307\u6570\u3001\u6df1\u8bc1\u6210\u6307\u3001\u521b\u4e1a\u677f\u6307\u3001\u79d1\u521b50\u3001\u6caa\u6df1300\u3001\u4e0a\u8bc150\u3001\u4e2d\u8bc1500\u3001\u4e2d\u8bc11000\r\n    a_code = xtdata.get_stock_list_in_sector('\u6caa\u6df1A\u80a1')\r\n    etf_code =  xtdata.get_stock_list_in_sector('\u6caa\u6df1ETF')\r\n    #bond_code = [i for i in xtdata.get_stock_list_in_sector('\u6caa\u6df1\u503a\u5238') if i[:3] in {'110',  '111', '113', '118', '123', '127', '128'}]\r\n    bond_code = get_bond_history()[-1]\r\n\r\n    return index_code + a_code + etf_code + bond_code\r\n```\r\n\r\n2\u3001\u6279\u91cf\u4e0b\u8f7d\u53ef\u8f6c\u503atick\u6570\u636e\r\n\r\n\u901a\u8fc7\u63a7\u5236\u53c2\u6570`init`\u6765\u51b3\u5b9a\u662f\u5426\u589e\u91cf\u4e0b\u8f7d(\u4ee5\u5929\u4e3a\u7c92\u5ea6)\uff1a\r\n```python\r\ndef download_history_bond_tick(init=1):\r\n    '''\r\n    \u4e0b\u8f7d\u5386\u53f2\u8f6c\u503atick\u6570\u636e(20200401\u8d77)\r\n    '''\r\n    # \u521d\u59cb\u5316\uff1a\u83b7\u53d6\u8f6c\u503a\u53ca\u5176\u6b63\u80a1\u4ee3\u7801\r\n    if init:\r\n        # \u5305\u542b\u5386\u53f2\u8fc7\u671f\u4ee3\u7801\r\n        stock_code_list, bond_code_list = get_bond_history()\r\n    else:\r\n        # \u4ec5\u5f53\u65e5\u4ee3\u7801\r\n        stock_code_list, bond_code_list = get_bond_spot()\r\n    \r\n    # \u6570\u636e\u4e0b\u8f7d\u76ee\u5f55\r\n    data_dir = 'E:\\\\QMT\\\\userdata_mini\\\\datadir\\\\'\r\n    for stock, bond in tqdm(zip(stock_code_list, bond_code_list), total=len(stock_code_list)):\r\n        print(\"\u5f00\u59cb\u4e0b\u8f7d\uff1a\u80a1\u7968 {}, \u8f6c\u503a {}\".format(stock, bond))\r\n        # \u4e0a\u6d77\u8f6c\u503a: \u5df2\u4e0b\u8f7d\u7684\u6570\u636e\r\n        if bond.endswith(\"SH\"):\r\n            dir_path = data_dir + \"\\\\SH\\\\0\\\\\" + bond.split('.', 1)[0]\r\n        # \u6df1\u5733\u8f6c\u503a\uff1a\u5df2\u4e0b\u8f7d\u7684\u6570\u636e\r\n        else:\r\n            dir_path = data_dir + \"\\\\SZ\\\\0\\\\\" + bond.split('.', 1)[0]\r\n        \r\n        start_date = '20200401' # QMT\u652f\u6301\u7684\u6700\u4e45\u6570\u636e\u65f6\u95f4\r\n        # \u5982\u679c\u8def\u5f84\u5b58\u5728\uff0c\u65ad\u70b9\u7eed\u4f20\uff0c\u91cd\u8bbe\u8d77\u70b9\u4e0b\u8f7d\u65f6\u95f4\r\n        if os.path.exists(dir_path):\r\n            downloaded = os.listdir(dir_path)\r\n            # \u83b7\u53d6\u5df2\u4e0b\u8f7d\u7684\u6700\u5927\u65e5\u671f\uff0c\u4f5c\u4e3a\u672c\u6b21\u540c\u6b65\u7684\u8d77\u59cb\u65f6\u95f4\r\n            if len(downloaded) > 0:\r\n                start_date = max(downloaded).split('.', 1)[0]\r\n            \r\n        xtdata.download_history_data(stock_code=bond, period='tick', start_time=start_date)\r\n\r\n```\r\n\r\n3\u3001\u6279\u91cf\u4e0b\u8f7dK\u7ebf\r\n\r\n\u901a\u8fc7\u4f20\u5165\u53c2\u6570`start_time`\u8bbe\u7f6e\u8d77\u59cb\u4e0b\u8f7d\u65f6\u95f4\uff0c\u53c2\u6570`period`\u8bbe\u7f6eK\u7ebf\u7c7b\u578b:\r\n- 1m: 1\u5206\u949fK\u7ebf\r\n- 1d: 1\u65e5K\u7ebf\r\n\r\n```python\r\ndef download_history_kline(start_time='', period='1m'):\r\n    '''\r\n    \u4e0b\u8f7d\u5386\u53f2K\u7ebf\u6570\u636e\r\n    '''\r\n    code_list = get_shse_a_list()\r\n    print(\"\u672c\u6b21\u5f00\u59cb\u4e0b\u8f7d\u7684\u65f6\u95f4\u4e3a\uff1a\", datetime.datetime.now().strftime(\"%Y%m%d%H%M%S\"))\r\n    for code in tqdm(code_list):\r\n        xtdata.download_history_data(code, period=period, start_time=start_time)\r\n\r\n```\r\n\r\n\u7ecf\u8fc7\u6f2b\u957f\u7684\u7b49\u5f85\uff0c\u672c\u5730\u4fbf\u4f1a\u6709\u5386\u53f2\u6570\u636e\u7684\u7f13\u5b58\u4e86\uff0c\u5b58\u50a8\u7684\u76ee\u5f55\u5f62\u5f0f\u4e3a`datadir\\SH\\{0|60|86400}\\{code}`\uff0c\u4fbf\u4e8e\u6211\u4eec\u8fdb\u4e00\u6b65\u52a0\u5de5\u5904\u7406\u3002\r\n\r\n### \u5b9e\u6218\uff1a\u5386\u53f2\u884c\u60c5\u8f6c\u5b58\u6570\u636e\u5e93\r\n\r\n\u5386\u53f2\u884c\u60c5\u6570\u636e\u4ece\u683c\u5f0f\u4e0a\u770b\u5206\u4e3atick\u6570\u636e\u548cK\u7ebf\u6570\u636e\u4e24\u5927\u7c7b\uff0c\u9488\u5bf9\u8fd9\u4e24\u7c7b\u7684\u6570\u636e\u6211\u4eec\u5206\u522b\u5904\u7406\u3002\r\n\r\n1\u3001tick\u6570\u636e\u9884\u5904\u7406\r\n\r\n\u9996\u5148\u8bfb\u53d6\u672c\u5730\u7f13\u5b58\u6570\u636e\uff0c\u8fd9\u91cc\u4ee5\u5357\u822a\u8f6c\u503a(`110075.SH`)\u4e3a\u4f8b\uff0c\u9700\u8981\u6ce8\u610f\u7684\u662ftick\u6570\u636e\u5305\u542b\u4e86\u96c6\u5408\u7ade\u4ef7\u65f6\u6bb5\uff0c\u6210\u4ea4\u91cf/\u989d\u662f\u6309\u65e5\u7d2f\u8ba1\u7684\uff0c\u56e0\u6b64\u9700\u8981\u505a\u4e00\u5b9a\u7684\u8f6c\u6362\u3002\r\n```python\r\nfrom xtquant import xtdata\r\nimport pandas as pd\r\nimport datetime\r\n\r\ndef get_local_tick_data(code='110075.SH', start_time='19700101'):\r\n    # \u83b7\u53d6\u672c\u5730\u6570\u636e\r\n    df = xtdata.get_local_data(stock_code=[code], period='tick', field_list=['time', 'open', 'lastPrice', 'high', 'low', 'lastClose', 'volume', 'amount', 'askPrice', 'bidPrice', 'askVol', 'bidVol'], start_time=start_time, end_time=start_time)\r\n\r\n    # \u8f6c\u6210DataFRame\r\n    df = pd.DataFrame(df[code])\r\n    if len(df) < 1:\r\n        return df\r\n\r\n    # \u65e5\u671f\u5904\u7406\r\n    df['trade_time'] = df['time'].apply(lambda x: datetime.datetime.fromtimestamp(x / 1000.0)) # , cn_tz\r\n    df['trade_day'] = df['trade_time'].apply(lambda x: x.date())\r\n    df['trade_minute'] = df['trade_time'].apply(lambda x: x.hour * 60 + x.minute)\r\n    df['trade_second'] = df['trade_time'].apply(lambda x: x.hour * 3600 + x.minute * 60 + x.second)\r\n    df = df[df.trade_second <= 54001] # \u6392\u9664\u76d8\u540e\u4ea4\u6613\r\n    df = df[df.trade_second >= 33840] # \u4fdd\u7559\u6700\u540e\u4e00\u5206\u949f\u7684\u96c6\u5408\u7ade\u4ef7\u6570\u636e\r\n    df = df.reset_index(drop=True)\r\n\r\n    # \u91cd\u65b0\u8ba1\u7b97\u6210\u4ea4\u91cf\u3001\u6210\u4ea4\u989d\r\n    df['volume_deal'] = df.groupby(['trade_day'])['volume'].diff(periods=1).fillna(0)\r\n    df['amount_deal'] = df.groupby(['trade_day'])['amount'].diff(periods=1).fillna(0)\r\n\r\n    # \u91cd\u65b0\u9009\u62e9\u5217\r\n    df['code'] = '110075.SH'\r\n    df['close'] = df['lastPrice'] # \u6536\u76d8\r\n    df['last'] = df['lastClose'] # \u6628\u6536\r\n    df = df[['code', 'trade_time', 'trade_day', 'trade_minute', 'open', 'close', 'high', 'low', 'last', 'volume', 'amount', 'volume_deal', 'amount_deal', 'askPrice', 'bidPrice', 'askVol', 'bidVol']]\r\n\r\n    return df\r\n\r\ndf = get_local_tick_data(code='110075.SH', start_time='20220630')\r\nprint(df.iloc[-1])\r\n```\r\n\r\n\u6700\u7ec8\uff0c\u6211\u4eec\u5f97\u5230\u8bf8\u5982\u4e0b\u56fe\u7684tick\u5b58\u50a8\u6570\u636e:\r\n\r\n![](misc/tick_data_store.png)\r\n\r\n2\u3001K\u7ebf\u6570\u636e\u9884\u5904\u7406\r\n\r\n\u8bfb\u53d6\u672c\u5730\u7f13\u5b58\u6570\u636e\uff0c\u8fd9\u91cc\u4ee5\u884c\u4e1a\u677f\u5757\u6307\u6570\u4e3a\u4f8b\uff0c\u9996\u5148\u83b7\u53d6\u884c\u4e1a\u6307\u6570\uff0c\u7136\u540e\u67e5\u8be2\u8be6\u60c5\uff0c\u83b7\u53d6\u5143\u6570\u636e:\r\n\r\n```python\r\ndef get_sector_list():\r\n    '''\r\n    \u83b7\u53d6\u6caa\u6df1\u6307\u6570\u3001\u884c\u4e1a\u6307\u6570\r\n    '''\r\n    sector_1 = xtdata.get_stock_list_in_sector('\u8bc1\u76d1\u4f1a\u884c\u4e1a\u677f\u5757\u6307\u6570')\r\n    sector_1 = [(i, xtdata.get_instrument_detail(i)['InstrumentName'], '\u8bc1\u76d1\u4f1a\u4e00\u7ea7\u884c\u4e1a') for i in sector_1]\r\n\r\n    sector_2 = xtdata.get_stock_list_in_sector('\u677f\u5757\u6307\u6570')\r\n    sector_2 = [(i, xtdata.get_instrument_detail(i)['InstrumentName'], '\u8bc1\u76d1\u4f1a\u4e8c\u7ea7\u884c\u4e1a') for i in sector_2 if i.startswith('23')]\r\n\r\n\r\n    index_code = [('000001.SH', '\u4e0a\u8bc1\u6307\u6570', '\u5927\u76d8\u6307\u6570'), ('399001.SZ', '\u6df1\u8bc1\u6210\u6307', '\u5927\u76d8\u6307\u6570'), ('399006.SZ', '\u521b\u4e1a\u677f\u6307', '\u5927\u76d8\u6307\u6570'), ('000688.SH', '\u79d1\u521b50', '\u5927\u76d8\u6307\u6570'), ('000300.SH', '\u6caa\u6df1300', '\u5927\u76d8\u6307\u6570'), ('000016.SH', '\u4e0a\u8bc150', '\u5927\u76d8\u6307\u6570'), ('000905.SH', '\u4e2d\u8bc1500', '\u5927\u76d8\u6307\u6570'), ('000852.SH', '\u4e2d\u8bc11000', '\u5927\u76d8\u6307\u6570')]\r\n\r\n    code_list = {i[0]: i[1:] for i in sector_1 + sector_2 + index_code}\r\n\r\n    return code_list\r\n```\r\n\r\n\u7136\u540e\u5904\u7406\u672c\u5730\u884c\u60c5\uff0c\u8fd9\u91cc\u4ee5\u8bc1\u76d1\u4f1a\u4e8c\u7ea7\u884c\u4e1a\u884c\u4e1a\u4e2d\u7684\u9910\u996e\u4e1a(`230130.BKZS`)\u4e3a\u4f8b:\r\n``` python\r\ndef get_local_kline_data(code='230130.BKZS', start_time='20200101', period='1d', code_list =  get_sector_list()):\r\n    # \u83b7\u53d6\u672c\u5730\u6570\u636e\r\n    df = xtdata.get_local_data(stock_code=[code], period='1d', field_list=['time', 'open', 'close', 'high', 'low', 'volume', 'amount'], start_time=start_time, end_time=datetime.datetime.now().strftime('%Y%m%d%H%M%S'))\r\n    df = pd.concat([df[i].T.rename(columns={code:i}) for i in ['time', 'open', 'close', 'high', 'low', 'volume', 'amount']], axis=1)\r\n\r\n    if len(df) < 1:\r\n        return df\r\n\r\n    # \u65f6\u95f4\u8f6c\u6362\r\n    df['trade_day'] = df['time'].apply(lambda x: datetime.datetime.fromtimestamp(x / 1000.0).date())\r\n\r\n    # \u91cd\u65b0\u9009\u62e9\u5217\r\n    df['code'] = code\r\n    df['sector_name'] = df['code'].apply(lambda x: code_list[x][0])\r\n    df['sector_type'] = df['code'].apply(lambda x: code_list[x][1])\r\n    df = df[['code', 'trade_day', 'sector_name', 'sector_type', 'open', 'close', 'high', 'low', 'volume', 'amount']]\r\n\r\n    return df\r\n```\r\n![](misc/kline_data_store.png)\r\n\r\n3\u3001Clickhouse\u6570\u636e\u5e93\u8bbe\u8ba1\r\n\r\n\u8fd9\u91cc\u9009\u7528Clickhouse\uff0c\u800c\u4e0d\u662fMySQL\u7684\u4e3b\u8981\u539f\u56e0\u662f\u6027\u80fd\u95ee\u9898\u3002\u884c\u60c5\u6570\u636e\u4e00\u65e6\u5199\u5165\uff0c\u51e0\u4e4e\u4e0d\u4f1a\u66f4\u65b0\uff0c\u5e76\u4e14\u91cf\u975e\u5e38\u5927\uff0c\u6ca1\u6709\u590d\u6742\u7684\u8868\u5173\u8054\uff0cMySQL\u5728\u8fd9\u79cd\u573a\u666f\u4e0b\u4e3b\u8981\u7684\u95ee\u9898\u662f\u5b58\u50a8\u7a7a\u95f4\u5360\u7528\u591a\u3001\u8bfb\u5199\u6162\uff0c\u800cClickHouse\u4e3b\u8981\u7528\u4e8e\u5728\u7ebf\u5206\u6790\u5904\u7406\u67e5\u8be2\uff08OLAP\uff09\uff0c\u5177\u6709\u9ad8\u6548\u7684\u6570\u636e\u538b\u7f29\u3001\u5411\u91cf\u5f15\u64ce\u3001\u5217\u5f0f\u5b58\u50a8\u7279\u6027\uff0c\u975e\u5e38\u9002\u5408\u91d1\u878d\u884c\u60c5\u6570\u636e\u5b58\u50a8\u3002\r\n\r\n```SQL\r\ncreate database xtquant\r\n\r\nCREATE TABLE IF NOT EXISTS xtquant.bond_tick\r\n(\r\n    code String, \r\n    trade_time DateTime('Asia/Shanghai'), \r\n    trade_day Date, \r\n    trade_minute Int16, \r\n    open Nullable(Float32), \r\n    close Nullable(Float32), \r\n    high Nullable(Float32), \r\n    low Nullable(Float32), \r\n    last Nullable(Float32), \r\n    volume Nullable(Float64), \r\n    amount Nullable(Float64), \r\n    volume_deal Nullable(Float32), \r\n    amount_deal Nullable(Float32), \r\n    askPrice Array(Nullable(Float32)), \r\n    bidPrice Array(Nullable(Float32)), \r\n    askVol Array(Nullable(Float32)), \r\n    bidVol Array(Nullable(Float32))\r\n)\r\nENGINE = ReplacingMergeTree()\r\nORDER BY (trade_time, code, trade_day)\r\n\r\nCREATE TABLE IF NOT EXISTS xtquant.sector_1d\r\n(\r\n    code String, \r\n    trade_day Date, \r\n    sector_name String,\r\n    sector_type String,\r\n    open Nullable(Float32), \r\n    close Nullable(Float32), \r\n    high Nullable(Float32), \r\n    low Nullable(Float32), \r\n    volume Nullable(Float64), \r\n    amount Nullable(Float64)\r\n)\r\nENGINE = ReplacingMergeTree()\r\nORDER BY (trade_day, code)\r\n```\r\n\r\n4\u3001Clickhouse\u6570\u636e\u6279\u91cf\u5199\u5165\r\n\r\n\u8bbe\u8ba1\u597d\u6570\u636e\u8868\u540e\uff0c\u5229\u7528`clickhouse_driver`\u5e93\u63d0\u4f9b\u7684\u63a5\u53e3\u5c06\u6570\u636e\u540c\u6b65\u5230\u6570\u636e\u5e93\u4e2d\u3002\r\n\r\n\u5bf9\u4e8etick\u6570\u636e\uff0c\u6309\u5929\u6765\u904d\u5386\u63d2\u5165\uff0c\u5bf9\u4e8ek\u7ebf\u6570\u636e\uff0c\u5219\u76f4\u63a5\u5b58\u5165\uff0c\u4e3a\u4e86\u589e\u91cf\u540c\u6b65\uff0c\u5199\u5165\u65f6\u53ef\u67e5\u8be2\u5df2\u6709\u6570\u636e\u7684\u6700\u5927\u65f6\u95f4\uff0c\u907f\u514d\u91cd\u590d\u5199\u3002\r\n\r\n```python\r\nimport os\r\nfrom clickhouse_driver import Client\r\nfrom tqdm import tqdm\r\n\r\nstorage_client = Client('10.0.16.11', password='******', settings={'use_numpy': True})\r\n\r\n# \u53ef\u8f6c\u503atick\u6570\u636e\r\n# \u83b7\u53d6\u53ef\u8f6c\u503a\u5217\u8868\r\n_, bond_code_list = get_bond_history()\r\nfor code in tqdm(bond_code_list):\r\n    start_date = storage_client.execute(\"select max(trade_day) from xtquant.bond_tick where code='{}'\".format(code))\r\n    start_date = str(start_date[0][0]).replace('-', '')\r\n    start_date = max(start_date, '20200401')\r\n    trade_dates = xtdata.get_trading_dates('SH', start_time=start_date, end_time=datetime.date.today().strftime('%Y%m%d'))\r\n    for day in trade_dates:\r\n        day = datetime.datetime.fromtimestamp(day / 1000.0).strftime('%Y%m%d')\r\n    df = get_local_tick_data(code=code, start_time=day)\r\n    if len(df) > 0:\r\n        storage_client.insert_dataframe('INSERT INTO xtquant.bond_tick VALUES', df)\r\n\r\n# \u884c\u4e1a1d\u6570\u636e\r\n# \u83b7\u53d6\u884c\u4e1a\u5217\u8868\r\nsector_code_list = get_sector_list()\r\nfor code in tqdm(sector_code_list):\r\n    start_date = storage_client.execute(\"select max(trade_day) from xtquant.sector_1d where code='{}'\".format(code))\r\n    start_date = str(start_date[0][0]).replace('-', '')\r\n    start_date = max(start_date, '20100101')\r\n    df = get_local_kline_data(code=code, start_time=start_date, period='1d', code_list =  sector_code_list)\r\n    if len(df) > 0:\r\n        storage_client.insert_dataframe('INSERT INTO xtquant.sector_1d VALUES', df)\r\n```\r\n\r\n\u8fd0\u884c\u5982\u4e0a\u4ee3\u7801\u540e\uff0c\u6211\u4eec\u4fbf\u53ef\u5728clickhouse\u5ba2\u6237\u7aef\u4e2d\u67e5\u8be2\u5230\u5df2\u7ecf\u5199\u5165\u7684\u6570\u636e\uff1a\r\n\r\n![](misc/clickhouse_query.png)\r\n\r\n### \u5b9e\u6218\uff1a\u5b9e\u65f6\u884c\u60c5\u8ba2\u9605\r\n\r\n\r\n\u672a\u5b8c\u5f85\u7eed......\r\n---\r\n\r\n\u6b22\u8fce\u5173\u6ce8\u6211\u7684\u516c\u4f17\u53f7\u201c**\u91cf\u5316\u5b9e\u6218**\u201d\uff0c\u539f\u521b\u6280\u672f\u6587\u7ae0\u7b2c\u4e00\u65f6\u95f4\u63a8\u9001\u3002\r\n\r\n![\u516c\u4f17\u53f7](misc/qrcode.png)\r\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "\u8fc5\u6295QMT\u63a5\u53e3\u76f8\u5173\u4ecb\u7ecd\u548c\u5e38\u7528\u529f\u80fd\u5c01\u88c5.",
    "version": "1.2.5",
    "project_urls": {
        "Homepage": "https://github.com/yanjlee/XtQuant"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "773774330f36cd1f1b70785f66e5337f298ed40ef435b4f36597e662ba07c521",
                "md5": "b7310b594aa0f8e27dcc31d2896cd6fe",
                "sha256": "cbdf782693b07a360b28735e455ac261f96b8ce64ffb216bab9b25a69b2f8a00"
            },
            "downloads": -1,
            "filename": "XtQuant_pro-1.2.5-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "b7310b594aa0f8e27dcc31d2896cd6fe",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 25100,
            "upload_time": "2024-06-01T08:45:19",
            "upload_time_iso_8601": "2024-06-01T08:45:19.737020Z",
            "url": "https://files.pythonhosted.org/packages/77/37/74330f36cd1f1b70785f66e5337f298ed40ef435b4f36597e662ba07c521/XtQuant_pro-1.2.5-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9cd22a01b55c9a0683f0e4d105c17edf8e392208fd94ef9531b65484e8c3e673",
                "md5": "574b1a56fd490be1e152396ce7c9b6a6",
                "sha256": "b1a768f04b85bb7e8b7bc6361da97a9c549e7d4b4c3eb564635ac4dcd62a6cec"
            },
            "downloads": -1,
            "filename": "xtquant_pro-1.2.5.tar.gz",
            "has_sig": false,
            "md5_digest": "574b1a56fd490be1e152396ce7c9b6a6",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 27442,
            "upload_time": "2024-06-01T08:45:22",
            "upload_time_iso_8601": "2024-06-01T08:45:22.055476Z",
            "url": "https://files.pythonhosted.org/packages/9c/d2/2a01b55c9a0683f0e4d105c17edf8e392208fd94ef9531b65484e8c3e673/xtquant_pro-1.2.5.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-06-01 08:45:22",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "yanjlee",
    "github_project": "XtQuant",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "xtquant-pro"
}
        
Elapsed time: 0.30921s