[
  {
    "path": ".coveragerc",
    "content": "[run]\nomit =\n        src/zvt/recorders/*\n        src/zvt/autocode/*\n        src/zvt/samples/*\n        *__init__*\n"
  },
  {
    "path": ".github/workflows/build.yaml",
    "content": "name: build\n\non: [push]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [3.12]\n\n    steps:\n      - uses: actions/checkout@v2\n      - name: Set up Python ${{ matrix.python-version }}\n        uses: actions/setup-python@v2\n        with:\n          python-version: ${{ matrix.python-version }}\n      - name: Cache pip\n        uses: actions/cache@v4\n        with:\n          # This path is specific to Ubuntu\n          path: ~/.cache/pip\n          # Look to see if there is a cache hit for the corresponding requirements file\n          key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}\n          restore-keys: |\n            ${{ runner.os }}-pip-\n            ${{ runner.os }}-\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi\n      - name: Test with pytest\n        run: |\n          pip install pytest\n          pip install pytest-cov\n          pytest ./tests --cov-config=.coveragerc --cov-report=xml --cov=./src/zvt --ignore=tests/recorders/ --ignore=tests/domain/\n      - name: Codecov\n        uses: codecov/codecov-action@v2\n        with:\n          verbose: true"
  },
  {
    "path": ".github/workflows/package.yaml",
    "content": "name: package\n\non:\n  release:\n    types: [published]\n\njobs:\n  deploy:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Python\n      uses: actions/setup-python@v2\n      with:\n        python-version: '3.12'\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install build\n    - name: Build package\n      run: python -m build\n    - name: Publish package\n      uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29\n      with:\n        user: __token__\n        password: ${{ secrets.PYPI }}"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n#idea\n.idea\n.vscode\n# Distribution / packaging\n.Python\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n.hypothesis/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\ndocs/source/api/_autosummary/\n\n# PyBuilder\ntarget/\n\n# IPython Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# dotenv\n.env\n\n# virtualenv\nvenv/\nENV/\n/ve/\n\n# Spyder project settings\n.spyderproject\n\n# Rope project settings\n.ropeproject\n\n/zvt-home\n/zvt-test-home\n\n# Local/VIP extensions (not part of open source)\nzvt_vip/\n*.tar.gz\n\n.pytest_cache/\n\n# LibreOffice locks\n.~lock.*#\n\nnode_modules/\n\n*running.ipynb\n\n.DS_Store\n\na.json"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "ci:\n  autoupdate_schedule: monthly\nrepos:\n  - repo: https://github.com/psf/black\n    rev: 24.10.0\n    hooks:\n      - id: black\n#  - repo: https://github.com/PyCQA/flake8\n#    rev: 4.0.1\n#    hooks:\n#      - id: flake8\n#        additional_dependencies:\n#          - flake8-bugbear\n#          - flake8-implicit-str-concat\n#  - repo: https://github.com/pre-commit/pre-commit-hooks\n#    rev: v4.0.1\n#    hooks:\n#      - id: fix-byte-order-marker\n#      - id: trailing-whitespace\n#      - id: end-of-file-fixer\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "version: 2\nbuild:\n  os: \"ubuntu-20.04\"\n  tools:\n    python: \"3.12\"\npython:\n  install:\n    - requirements: requirements/docs.txt\n    - method: pip\n      path: .\nsphinx:\n  builder: dirhtml\n  fail_on_warning: false\n  configuration: docs/source/conf.py\n#formats:\n#  - pdf\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023-2026 zvtvz\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include *.md\ninclude *.txt\ninclude LICENSE\nrecursive-include src/zvt/* *"
  },
  {
    "path": "README-cn.md",
    "content": "[![github](https://img.shields.io/github/stars/zvtvz/zvt.svg)](https://github.com/zvtvz/zvt)\n[![image](https://img.shields.io/pypi/v/zvt.svg)](https://pypi.org/project/zvt/)\n[![image](https://img.shields.io/pypi/l/zvt.svg)](https://pypi.org/project/zvt/)\n[![image](https://img.shields.io/pypi/pyversions/zvt.svg)](https://pypi.org/project/zvt/)\n[![build](https://github.com/zvtvz/zvt/actions/workflows/build.yaml/badge.svg)](https://github.com/zvtvz/zvt/actions/workflows/build.yml)\n[![package](https://github.com/zvtvz/zvt/actions/workflows/package.yaml/badge.svg)](https://github.com/zvtvz/zvt/actions/workflows/package.yaml)\n[![Documentation Status](https://readthedocs.org/projects/zvt/badge/?version=latest)](https://zvt.readthedocs.io/en/latest/?badge=latest)\n[![codecov.io](https://codecov.io/github/zvtvz/zvt/coverage.svg?branch=master)](https://codecov.io/github/zvtvz/zvt)\n[![Downloads](https://pepy.tech/badge/zvt/month)](https://pepy.tech/project/zvt)\n\n**缘起**\n\n[炒股的三大原理](https://mp.weixin.qq.com/s/FoFR63wFSQIE_AyFubkZ6Q)\n\n**声明**\n\n本项目目前不保证任何向后兼容性，请谨慎升级。  \n随着作者思想的变化，一些以前觉得重要的东西可能也变得不重要，从而可能不会进行维护。  \n而一些新的东西的加入对你是否有用，需要自己去评估。\n\n\n**Read this in other languages: [English](README-cn.md).**  \n\n**详细文档:[https://zvt.readthedocs.io/en/latest/](https://zvt.readthedocs.io/en/latest/)**\n\n## 市场模型\nZVT 将市场抽象为如下的模型:\n\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/view.png'/></p>\n\n* TradableEntity (交易标的)\n* ActorEntity (市场参与者)\n* EntityEvent (交易标的 和 市场参与者 发生的事件)\n\n## 快速开始\n\n### 安装\n```\npython3 -m pip install -U zvt\n```\n\n### 使用展示\n\n#### 主界面\n\n#### Dash & Plotly UI\n> 适用于回测和研究，不太适用于实时行情和用户交互\n\n安装完成后，在命令行下输入 zvt\n```shell\nzvt\n```\n打开 [http://127.0.0.1:8050/](http://127.0.0.1:8050/)\n\n> 这里展示的例子依赖后面的下载历史数据，数据更新请参考后面文档\n\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/zvt-factor.png'/></p>\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/zvt-trader.png'/></p>\n\n> 系统的核心概念是可视化的，界面的名称与其一一对应，因此也是统一可扩展的。\n\n> 你可以在你喜欢的ide里编写和运行策略，然后运行界面查看其相关的标的，因子，信号和净值展示。\n\n#### 前后端分离的UI\n> 更灵活和可扩展，更适合于处理实时行情和用户交互，结合ZVT的动态tag系统，提供了一种量化结合主观的交易方式\n\n- 初始化tag系统\n\n运行以下脚本:  \n\nhttps://github.com/zvtvz/zvt/blob/master/src/zvt/tasks/init_tag_system.py\nhttps://github.com/zvtvz/zvt/blob/master/src/zvt/tasks/stock_pool_runner.py\nhttps://github.com/zvtvz/zvt/blob/master/src/zvt/tasks/qmt_data_runner.py\nhttps://github.com/zvtvz/zvt/blob/master/src/zvt/tasks/qmt_tick_runner.py\n\n- 安装 uvicorn\n```shell\npip install uvicorn\n```\n- 运行 zvt server\n\n安装完成后，在命令行下输入 zvt_server\n```shell\nzvt_server\n```\n或者从代码运行:\nhttps://github.com/zvtvz/zvt/blob/master/src/zvt/zvt_server.py\n\n- api 文档 \n\nopen [http://127.0.0.1:8090/docs](http://127.0.0.1:8090/docs)\n\n- 部署前端\n\n前端代码: https://github.com/zvtvz/zvt_ui\n\n修改前端环境文件:\nhttps://github.com/zvtvz/zvt_ui/blob/main/.env\n\n设置 {your server IP}, 即zvt_server服务的地址\n\n```text\nNEXT_PUBLIC_SERVER = {your server IP}\n```\n\n然后参考前端的readme启动前端服务\n\n打开 [http://127.0.0.1:3000/trade](http://127.0.0.1:3000/trade)\n\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/big-picture.jpg'/></p>\n\n#### 见证奇迹的时刻\n```\n>>> from zvt.domain import Stock, Stock1dHfqKdata\n>>> from zvt.ml import MaStockMLMachine\n>>> Stock.record_data(provider=\"em\")\n>>> entity_ids = [\"stock_sz_000001\", \"stock_sz_000338\", \"stock_sh_601318\"]\n>>> Stock1dHfqKdata.record_data(provider=\"em\", entity_ids=entity_ids, sleeping_time=1)\n>>> machine = MaStockMLMachine(entity_ids=[\"stock_sz_000001\"], data_provider=\"em\")\n>>> machine.train()\n>>> machine.predict()\n>>> machine.draw_result(entity_id=\"stock_sz_000001\")\n```\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/pred_close.png'/></p>\n\n> 以上几行代码实现了：数据的抓取，持久化，增量更新，机器学习，预测，展示结果。\n> 熟悉系统的核心概念后，可以应用到市场中的任何标的。\n\n### 核心概念\n```\n>>> from zvt.domain import *\n```\n\n### TradableEntity (交易标的)\n\n#### A股交易标的\n```\n>>> Stock.record_data()\n>>> df = Stock.query_data(index='code')\n>>> print(df)\n\n                     id        entity_id  timestamp entity_type exchange    code   name  list_date end_date\ncode\n000001  stock_sz_000001  stock_sz_000001 1991-04-03       stock       sz  000001   平安银行 1991-04-03     None\n000002  stock_sz_000002  stock_sz_000002 1991-01-29       stock       sz  000002  万  科Ａ 1991-01-29     None\n000004  stock_sz_000004  stock_sz_000004 1990-12-01       stock       sz  000004   国华网安 1990-12-01     None\n000005  stock_sz_000005  stock_sz_000005 1990-12-10       stock       sz  000005   世纪星源 1990-12-10     None\n000006  stock_sz_000006  stock_sz_000006 1992-04-27       stock       sz  000006   深振业Ａ 1992-04-27     None\n...                 ...              ...        ...         ...      ...     ...    ...        ...      ...\n605507  stock_sh_605507  stock_sh_605507 2021-08-02       stock       sh  605507   国邦医药 2021-08-02     None\n605577  stock_sh_605577  stock_sh_605577 2021-08-24       stock       sh  605577   龙版传媒 2021-08-24     None\n605580  stock_sh_605580  stock_sh_605580 2021-08-19       stock       sh  605580   恒盛能源 2021-08-19     None\n605588  stock_sh_605588  stock_sh_605588 2021-08-12       stock       sh  605588   冠石科技 2021-08-12     None\n605589  stock_sh_605589  stock_sh_605589 2021-08-10       stock       sh  605589   圣泉集团 2021-08-10     None\n\n[4136 rows x 9 columns]\n```\n\n#### 美股交易标的\n```\n>>> Stockus.record_data()\n>>> df = Stockus.query_data(index='code')\n>>> print(df)\n\n                       id            entity_id timestamp entity_type exchange  code                         name list_date end_date\ncode\nA          stockus_nyse_A       stockus_nyse_A       NaT     stockus     nyse     A                          安捷伦      None     None\nAA        stockus_nyse_AA      stockus_nyse_AA       NaT     stockus     nyse    AA                         美国铝业      None     None\nAAC      stockus_nyse_AAC     stockus_nyse_AAC       NaT     stockus     nyse   AAC      Ares Acquisition Corp-A      None     None\nAACG  stockus_nasdaq_AACG  stockus_nasdaq_AACG       NaT     stockus   nasdaq  AACG    ATA Creativity Global ADR      None     None\nAACG    stockus_nyse_AACG    stockus_nyse_AACG       NaT     stockus     nyse  AACG    ATA Creativity Global ADR      None     None\n...                   ...                  ...       ...         ...      ...   ...                          ...       ...      ...\nZWRK  stockus_nasdaq_ZWRK  stockus_nasdaq_ZWRK       NaT     stockus   nasdaq  ZWRK    Z-Work Acquisition Corp-A      None     None\nZY      stockus_nasdaq_ZY    stockus_nasdaq_ZY       NaT     stockus   nasdaq    ZY                 Zymergen Inc      None     None\nZYME    stockus_nyse_ZYME    stockus_nyse_ZYME       NaT     stockus     nyse  ZYME                Zymeworks Inc      None     None\nZYNE  stockus_nasdaq_ZYNE  stockus_nasdaq_ZYNE       NaT     stockus   nasdaq  ZYNE  Zynerba Pharmaceuticals Inc      None     None\nZYXI  stockus_nasdaq_ZYXI  stockus_nasdaq_ZYXI       NaT     stockus   nasdaq  ZYXI                    Zynex Inc      None     None\n\n[5826 rows x 9 columns]\n\n>>> Stockus.query_data(code='AAPL')\n                    id            entity_id timestamp entity_type exchange  code name list_date end_date\n0  stockus_nasdaq_AAPL  stockus_nasdaq_AAPL      None     stockus   nasdaq  AAPL   苹果      None     None\n```\n\n#### 港股交易标的\n```\n>>> Stockhk.record_data()\n>>> df = Stockhk.query_data(index='code')\n>>> print(df)\n\n                     id         entity_id timestamp entity_type exchange   code    name list_date end_date\ncode\n00001  stockhk_hk_00001  stockhk_hk_00001       NaT     stockhk       hk  00001      长和      None     None\n00002  stockhk_hk_00002  stockhk_hk_00002       NaT     stockhk       hk  00002    中电控股      None     None\n00003  stockhk_hk_00003  stockhk_hk_00003       NaT     stockhk       hk  00003  香港中华煤气      None     None\n00004  stockhk_hk_00004  stockhk_hk_00004       NaT     stockhk       hk  00004   九龙仓集团      None     None\n00005  stockhk_hk_00005  stockhk_hk_00005       NaT     stockhk       hk  00005    汇丰控股      None     None\n...                 ...               ...       ...         ...      ...    ...     ...       ...      ...\n09996  stockhk_hk_09996  stockhk_hk_09996       NaT     stockhk       hk  09996  沛嘉医疗-B      None     None\n09997  stockhk_hk_09997  stockhk_hk_09997       NaT     stockhk       hk  09997    康基医疗      None     None\n09998  stockhk_hk_09998  stockhk_hk_09998       NaT     stockhk       hk  09998    光荣控股      None     None\n09999  stockhk_hk_09999  stockhk_hk_09999       NaT     stockhk       hk  09999    网易-S      None     None\n80737  stockhk_hk_80737  stockhk_hk_80737       NaT     stockhk       hk  80737  湾区发展-R      None     None\n\n[2597 rows x 9 columns]\n\n>>> df[df.code=='00700']\n\n                    id         entity_id timestamp entity_type exchange   code  name list_date end_date\n2112  stockhk_hk_00700  stockhk_hk_00700      None     stockhk       hk  00700  腾讯控股      None     None\n\n```\n\n#### 还有更多\n```\n>>> from zvt.contract import *\n>>> zvt_context.tradable_schema_map\n\n{'stockus': zvt.domain.meta.stockus_meta.Stockus,\n 'stockhk': zvt.domain.meta.stockhk_meta.Stockhk,\n 'index': zvt.domain.meta.index_meta.Index,\n 'etf': zvt.domain.meta.etf_meta.Etf,\n 'stock': zvt.domain.meta.stock_meta.Stock,\n 'block': zvt.domain.meta.block_meta.Block,\n 'fund': zvt.domain.meta.fund_meta.Fund}\n```\n\n其中key为交易标的的类型，value为其schema，系统为schema提供了统一的 **记录(record_data)** 和 **查询(query_data)** 方法。\n\n```\n>>> Index.record_data()\n>>> df=Index.query_data(filters=[Index.category=='scope',Index.exchange='sh'])\n>>> print(df)\n                 id        entity_id  timestamp entity_type exchange    code    name  list_date end_date publisher category  base_point\n0   index_sh_000001  index_sh_000001 1990-12-19       index       sh  000001    上证指数 1991-07-15     None   csindex    scope      100.00\n1   index_sh_000002  index_sh_000002 1990-12-19       index       sh  000002    Ａ股指数 1992-02-21     None   csindex    scope      100.00\n2   index_sh_000003  index_sh_000003 1992-02-21       index       sh  000003    B股指数 1992-08-17     None   csindex    scope      100.00\n3   index_sh_000010  index_sh_000010 2002-06-28       index       sh  000010   上证180 2002-07-01     None   csindex    scope     3299.06\n4   index_sh_000016  index_sh_000016 2003-12-31       index       sh  000016    上证50 2004-01-02     None   csindex    scope     1000.00\n..              ...              ...        ...         ...      ...     ...     ...        ...      ...       ...      ...         ...\n25  index_sh_000020  index_sh_000020 2007-12-28       index       sh  000020    中型综指 2008-05-12     None   csindex    scope     1000.00\n26  index_sh_000090  index_sh_000090 2009-12-31       index       sh  000090    上证流通 2010-12-02     None   csindex    scope     1000.00\n27  index_sh_930903  index_sh_930903 2012-12-31       index       sh  930903    中证Ａ股 2016-10-18     None   csindex    scope     1000.00\n28  index_sh_000688  index_sh_000688 2019-12-31       index       sh  000688    科创50 2020-07-23     None   csindex    scope     1000.00\n29  index_sh_931643  index_sh_931643 2019-12-31       index       sh  931643  科创创业50 2021-06-01     None   csindex    scope     1000.00\n\n[30 rows x 12 columns]\n\n```\n\n### EntityEvent (交易标的 发生的事件)\n有了交易标的，才有交易标的 发生的事。\n\n#### 行情数据\n交易标的 **行情schema** 遵从如下的规则:\n```\n{entity_shema}{level}{adjust_type}Kdata\n```\n* entity_schema\n\n就是前面说的TradableEntity，比如Stock,Stockus等。\n\n* level\n```\n>>> for level in IntervalLevel:\n        print(level.value)\n```\n\n* adjust type\n```\n>>> for adjust_type in AdjustType:\n        print(adjust_type.value)\n```\n> 注意: 为了兼容历史数据，前复权是个例外，{adjust_type}不填\n\n前复权\n```\n>>> Stock1dKdata.record_data(code='000338', provider='em')\n>>> df = Stock1dKdata.query_data(code='000338', provider='em')\n>>> print(df)\n\n                              id        entity_id  timestamp provider    code  name level   open  close   high    low     volume      turnover  change_pct  turnover_rate\n0     stock_sz_000338_2007-04-30  stock_sz_000338 2007-04-30     None  000338  潍柴动力    1d   2.33   2.00   2.40   1.87   207375.0  1.365189e+09      3.2472         0.1182\n1     stock_sz_000338_2007-05-08  stock_sz_000338 2007-05-08     None  000338  潍柴动力    1d   2.11   1.94   2.20   1.87    86299.0  5.563198e+08     -0.0300         0.0492\n2     stock_sz_000338_2007-05-09  stock_sz_000338 2007-05-09     None  000338  潍柴动力    1d   1.90   1.81   1.94   1.66    93823.0  5.782065e+08     -0.0670         0.0535\n3     stock_sz_000338_2007-05-10  stock_sz_000338 2007-05-10     None  000338  潍柴动力    1d   1.78   1.85   1.98   1.75    47720.0  2.999226e+08      0.0221         0.0272\n4     stock_sz_000338_2007-05-11  stock_sz_000338 2007-05-11     None  000338  潍柴动力    1d   1.81   1.73   1.81   1.66    39273.0  2.373126e+08     -0.0649         0.0224\n...                          ...              ...        ...      ...     ...   ...   ...    ...    ...    ...    ...        ...           ...         ...            ...\n3426  stock_sz_000338_2021-08-27  stock_sz_000338 2021-08-27     None  000338  潍柴动力    1d  19.39  20.30  20.30  19.25  1688497.0  3.370241e+09      0.0601         0.0398\n3427  stock_sz_000338_2021-08-30  stock_sz_000338 2021-08-30     None  000338  潍柴动力    1d  20.30  20.09  20.31  19.78  1187601.0  2.377957e+09     -0.0103         0.0280\n3428  stock_sz_000338_2021-08-31  stock_sz_000338 2021-08-31     None  000338  潍柴动力    1d  20.20  20.07  20.63  19.70  1143985.0  2.295195e+09     -0.0010         0.0270\n3429  stock_sz_000338_2021-09-01  stock_sz_000338 2021-09-01     None  000338  潍柴动力    1d  19.98  19.68  19.98  19.15  1218697.0  2.383841e+09     -0.0194         0.0287\n3430  stock_sz_000338_2021-09-02  stock_sz_000338 2021-09-02     None  000338  潍柴动力    1d  19.71  19.85  19.97  19.24  1023545.0  2.012006e+09      0.0086         0.0241\n\n[3431 rows x 15 columns]\n\n>>> Stockus1dKdata.record_data(code='AAPL', provider='em')\n>>> df = Stockus1dKdata.query_data(code='AAPL', provider='em')\n>>> print(df)\n\n                                  id            entity_id  timestamp provider  code name level    open   close    high     low      volume      turnover  change_pct  turnover_rate\n0     stockus_nasdaq_AAPL_1984-09-07  stockus_nasdaq_AAPL 1984-09-07     None  AAPL   苹果    1d   -5.59   -5.59   -5.58   -5.59   2981600.0  0.000000e+00      0.0000         0.0002\n1     stockus_nasdaq_AAPL_1984-09-10  stockus_nasdaq_AAPL 1984-09-10     None  AAPL   苹果    1d   -5.59   -5.59   -5.58   -5.59   2346400.0  0.000000e+00      0.0000         0.0001\n2     stockus_nasdaq_AAPL_1984-09-11  stockus_nasdaq_AAPL 1984-09-11     None  AAPL   苹果    1d   -5.58   -5.58   -5.58   -5.58   5444000.0  0.000000e+00      0.0018         0.0003\n3     stockus_nasdaq_AAPL_1984-09-12  stockus_nasdaq_AAPL 1984-09-12     None  AAPL   苹果    1d   -5.58   -5.59   -5.58   -5.59   4773600.0  0.000000e+00     -0.0018         0.0003\n4     stockus_nasdaq_AAPL_1984-09-13  stockus_nasdaq_AAPL 1984-09-13     None  AAPL   苹果    1d   -5.58   -5.58   -5.58   -5.58   7429600.0  0.000000e+00      0.0018         0.0004\n...                              ...                  ...        ...      ...   ...  ...   ...     ...     ...     ...     ...         ...           ...         ...            ...\n8765  stockus_nasdaq_AAPL_2021-08-27  stockus_nasdaq_AAPL 2021-08-27     None  AAPL   苹果    1d  147.48  148.60  148.75  146.83  55802388.0  8.265452e+09      0.0072         0.0034\n8766  stockus_nasdaq_AAPL_2021-08-30  stockus_nasdaq_AAPL 2021-08-30     None  AAPL   苹果    1d  149.00  153.12  153.49  148.61  90956723.0  1.383762e+10      0.0304         0.0055\n8767  stockus_nasdaq_AAPL_2021-08-31  stockus_nasdaq_AAPL 2021-08-31     None  AAPL   苹果    1d  152.66  151.83  152.80  151.29  86453117.0  1.314255e+10     -0.0084         0.0052\n8768  stockus_nasdaq_AAPL_2021-09-01  stockus_nasdaq_AAPL 2021-09-01     None  AAPL   苹果    1d  152.83  152.51  154.98  152.34  80313711.0  1.235321e+10      0.0045         0.0049\n8769  stockus_nasdaq_AAPL_2021-09-02  stockus_nasdaq_AAPL 2021-09-02     None  AAPL   苹果    1d  153.87  153.65  154.72  152.40  71171317.0  1.093251e+10      0.0075         0.0043\n\n[8770 rows x 15 columns]\n```\n后复权\n```\n>>> Stock1dHfqKdata.record_data(code='000338', provider='em')\n>>> df = Stock1dHfqKdata.query_data(code='000338', provider='em')\n>>> print(df)\n\n                              id        entity_id  timestamp provider    code  name level    open   close    high     low     volume      turnover  change_pct  turnover_rate\n0     stock_sz_000338_2007-04-30  stock_sz_000338 2007-04-30     None  000338  潍柴动力    1d   70.00   64.93   71.00   62.88   207375.0  1.365189e+09      2.1720         0.1182\n1     stock_sz_000338_2007-05-08  stock_sz_000338 2007-05-08     None  000338  潍柴动力    1d   66.60   64.00   68.00   62.88    86299.0  5.563198e+08     -0.0143         0.0492\n2     stock_sz_000338_2007-05-09  stock_sz_000338 2007-05-09     None  000338  潍柴动力    1d   63.32   62.00   63.88   59.60    93823.0  5.782065e+08     -0.0313         0.0535\n3     stock_sz_000338_2007-05-10  stock_sz_000338 2007-05-10     None  000338  潍柴动力    1d   61.50   62.49   64.48   61.01    47720.0  2.999226e+08      0.0079         0.0272\n4     stock_sz_000338_2007-05-11  stock_sz_000338 2007-05-11     None  000338  潍柴动力    1d   61.90   60.65   61.90   59.70    39273.0  2.373126e+08     -0.0294         0.0224\n...                          ...              ...        ...      ...     ...   ...   ...     ...     ...     ...     ...        ...           ...         ...            ...\n3426  stock_sz_000338_2021-08-27  stock_sz_000338 2021-08-27     None  000338  潍柴动力    1d  331.97  345.95  345.95  329.82  1688497.0  3.370241e+09      0.0540         0.0398\n3427  stock_sz_000338_2021-08-30  stock_sz_000338 2021-08-30     None  000338  潍柴动力    1d  345.95  342.72  346.10  337.96  1187601.0  2.377957e+09     -0.0093         0.0280\n3428  stock_sz_000338_2021-08-31  stock_sz_000338 2021-08-31     None  000338  潍柴动力    1d  344.41  342.41  351.02  336.73  1143985.0  2.295195e+09     -0.0009         0.0270\n3429  stock_sz_000338_2021-09-01  stock_sz_000338 2021-09-01     None  000338  潍柴动力    1d  341.03  336.42  341.03  328.28  1218697.0  2.383841e+09     -0.0175         0.0287\n3430  stock_sz_000338_2021-09-02  stock_sz_000338 2021-09-02     None  000338  潍柴动力    1d  336.88  339.03  340.88  329.67  1023545.0  2.012006e+09      0.0078         0.0241\n\n[3431 rows x 15 columns]\n```\n\n#### 财务因子\n```\n>>> FinanceFactor.record_data(code='000338')\n>>> FinanceFactor.query_data(code='000338',columns=FinanceFactor.important_cols(),index='timestamp')\n\n            basic_eps  total_op_income    net_profit  op_income_growth_yoy  net_profit_growth_yoy     roe    rota  gross_profit_margin  net_margin  timestamp\ntimestamp\n2002-12-31        NaN     1.962000e+07  2.471000e+06                   NaN                    NaN     NaN     NaN               0.2068      0.1259 2002-12-31\n2003-12-31       1.27     3.574000e+09  2.739000e+08              181.2022               109.8778  0.7729  0.1783               0.2551      0.0766 2003-12-31\n2004-12-31       1.75     6.188000e+09  5.369000e+08                0.7313                 0.9598  0.3245  0.1474               0.2489      0.0868 2004-12-31\n2005-12-31       0.93     5.283000e+09  3.065000e+08               -0.1463                -0.4291  0.1327  0.0603               0.2252      0.0583 2005-12-31\n2006-03-31       0.33     1.859000e+09  1.079000e+08                   NaN                    NaN     NaN     NaN                  NaN      0.0598 2006-03-31\n...               ...              ...           ...                   ...                    ...     ...     ...                  ...         ...        ...\n2020-08-28       0.59     9.449000e+10  4.680000e+09                0.0400                -0.1148  0.0983  0.0229               0.1958      0.0603 2020-08-28\n2020-10-31       0.90     1.474000e+11  7.106000e+09                0.1632                 0.0067  0.1502  0.0347               0.1949      0.0590 2020-10-31\n2021-03-31       1.16     1.975000e+11  9.207000e+09                0.1327                 0.0112  0.1919  0.0444               0.1931      0.0571 2021-03-31\n2021-04-30       0.42     6.547000e+10  3.344000e+09                0.6788                 0.6197  0.0622  0.0158               0.1916      0.0667 2021-04-30\n2021-08-31       0.80     1.264000e+11  6.432000e+09                0.3375                 0.3742  0.1125  0.0287               0.1884      0.0653 2021-08-31\n\n[66 rows x 10 columns]\n```\n\n#### 财务三张表\n```\n#资产负债表\n>>> BalanceSheet.record_data(code='000338')\n#利润表\n>>> IncomeStatement.record_data(code='000338')\n#现金流量表\n>>> CashFlowStatement.record_data(code='000338')\n```\n\n#### 还有更多\n```\n>>> zvt_context.schemas\n[zvt.domain.dividend_financing.DividendFinancing,\n zvt.domain.dividend_financing.DividendDetail,\n zvt.domain.dividend_financing.SpoDetail...]\n```\n\nzvt_context.schemas为系统支持的schema,schema即表结构，即数据，其字段含义的查看方式如下：\n\n* help\n\n输入schema.按tab提示其包含的字段，或者.help()\n```\n>>> FinanceFactor.help()\n```\n\n* 源码\n\n[domain](https://github.com/zvtvz/zvt/tree/master/src/zvt/domain)里的文件为schema的定义，查看相应字段的注释即可。\n\n通过以上的例子，你应该掌握了统一的记录数据的方法：\n\n> Schema.record_data(provider='your provider',codes='the codes')\n\n注意可选参数provider，其代表数据提供商，一个schema可以有多个provider，这是系统稳定的基石。\n\n查看**已实现**的provider\n```\n>>> Stock.provider_map_recorder\n{'joinquant': zvt.recorders.joinquant.meta.jq_stock_meta_recorder.JqChinaStockRecorder,\n 'exchange': zvt.recorders.exchange.exchange_stock_meta_recorder.ExchangeStockMetaRecorder,\n 'em': zvt.recorders.em.meta.em_stock_meta_recorder.EMStockRecorder,\n 'eastmoney': zvt.recorders.eastmoney.meta.eastmoney_stock_meta_recorder.EastmoneyChinaStockListRecorder}\n\n```\n你可以使用任意一个provider来获取数据，默认使用第一个。\n\n再举个例子，股票板块数据获取：\n```\n>>> Block.provider_map_recorder\n{'eastmoney': zvt.recorders.eastmoney.meta.eastmoney_block_meta_recorder.EastmoneyChinaBlockRecorder,\n 'sina': zvt.recorders.sina.meta.sina_block_recorder.SinaBlockRecorder}\n\n>>> Block.record_data(provider='sina')\nBlock registered recorders:{'eastmoney': <class 'zvt.recorders.eastmoney.meta.china_stock_category_recorder.EastmoneyChinaBlockRecorder'>, 'sina': <class 'zvt.recorders.sina.meta.sina_china_stock_category_recorder.SinaChinaBlockRecorder'>}\n2020-03-04 23:56:48,931  INFO  MainThread  finish record sina blocks:industry\n2020-03-04 23:56:49,450  INFO  MainThread  finish record sina blocks:concept\n```\n\n再多了解一点record_data：\n* 参数code[单个]，codes[多个]代表需要抓取的股票代码\n* 不传入code,codes则是全市场抓取\n* 该方法会把数据存储到本地并只做增量更新\n\n定时任务的方式更新可参考[定时更新](https://github.com/zvtvz/zvt/blob/master/examples/data_runner)\n\n#### 全市场选股\n查询数据使用的是query_data方法，把全市场的数据记录下来后，就可以在本地快速查询需要的数据了。\n\n一个例子：2018年年报 roe>8% 营收增长>8% 的前20个股\n```\n>>> df=FinanceFactor.query_data(filters=[FinanceFactor.roe>0.08,FinanceFactor.report_period=='year',FinanceFactor.op_income_growth_yoy>0.08],start_timestamp='2019-01-01',order=FinanceFactor.roe.desc(),limit=20,columns=[\"code\"]+FinanceFactor.important_cols(),index='code')\n\n          code  basic_eps  total_op_income    net_profit  op_income_growth_yoy  net_profit_growth_yoy     roe    rota  gross_profit_margin  net_margin  timestamp\ncode\n000048  000048     2.7350     4.919000e+09  1.101000e+09                0.4311                 1.5168  0.7035  0.1988               0.5243      0.2355 2020-04-30\n000912  000912     0.3500     4.405000e+09  3.516000e+08                0.1796                 1.2363  4.7847  0.0539               0.2175      0.0795 2019-03-20\n002207  002207     0.2200     3.021000e+08  5.189000e+07                0.1600                 1.1526  1.1175  0.1182               0.1565      0.1718 2020-04-27\n002234  002234     5.3300     3.276000e+09  1.610000e+09                0.8023                 3.2295  0.8361  0.5469               0.5968      0.4913 2020-04-21\n002458  002458     3.7900     3.584000e+09  2.176000e+09                1.4326                 4.9973  0.8318  0.6754               0.6537      0.6080 2020-02-20\n...        ...        ...              ...           ...                   ...                    ...     ...     ...                  ...         ...        ...\n600701  600701    -3.6858     7.830000e+08 -3.814000e+09                1.3579                -0.0325  1.9498 -0.7012               0.4173     -4.9293 2020-04-29\n600747  600747    -1.5600     3.467000e+08 -2.290000e+09                2.1489                -0.4633  3.1922 -1.5886               0.0378     -6.6093 2020-06-30\n600793  600793     1.6568     1.293000e+09  1.745000e+08                0.1164                 0.8868  0.7490  0.0486               0.1622      0.1350 2019-04-30\n600870  600870     0.0087     3.096000e+07  4.554000e+06                0.7773                 1.3702  0.7458  0.0724               0.2688      0.1675 2019-03-30\n688169  688169    15.6600     4.205000e+09  7.829000e+08                0.3781                 1.5452  0.7172  0.4832               0.3612      0.1862 2020-04-28\n\n[20 rows x 11 columns]\n```\n\n以上，你应该会回答如下的三个问题了：\n* 有什么数据?\n* 如何记录数据?\n* 如何查询数据?\n\n更高级的用法以及扩展数据，可以参考详细文档里的数据部分。\n\n### 写个策略\n有了 **交易标的** 和 **交易标的发生的事**，就可以写策略了。\n\n所谓策略回测，无非就是，重复以下过程：\n#### 在某时间点，找到符合条件的标的，对其进行买卖，看其表现。\n\n系统支持两种模式:\n* solo (随意的)\n\n在 某个时间 根据发生的事件 计算条件 并买卖\n\n* formal (正式的)\n\n系统设计的二维索引多标的计算模型\n\n#### 一个很随便的人(solo)\n嗯，这个策略真的很随便，就像我们大部分时间做的那样。\n> 报表出来的时，我看一下报表，机构加仓超过5%我就买入，机构减仓超过50%我就卖出。\n\n代码如下:\n```\n# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.api import get_recent_report_date\nfrom zvt.contract import ActorType, AdjustType\nfrom zvt.domain import StockActorSummary, Stock1dKdata\nfrom zvt.trader import StockTrader\nfrom zvt.utils import pd_is_not_null, is_same_date, to_pd_timestamp\n\n\nclass FollowIITrader(StockTrader):\n    finish_date = None\n\n    def on_time(self, timestamp: pd.Timestamp):\n        recent_report_date = to_pd_timestamp(get_recent_report_date(timestamp))\n        if self.finish_date and is_same_date(recent_report_date, self.finish_date):\n            return\n        filters = [StockActorSummary.actor_type == ActorType.raised_fund.value,\n                   StockActorSummary.report_date == recent_report_date]\n\n        if self.entity_ids:\n            filters = filters + [StockActorSummary.entity_id.in_(self.entity_ids)]\n\n        df = StockActorSummary.query_data(filters=filters)\n\n        if pd_is_not_null(df):\n            self.logger.info(f'{df}')\n            self.finish_date = recent_report_date\n\n        long_df = df[df['change_ratio'] > 0.05]\n        short_df = df[df['change_ratio'] < -0.5]\n        try:\n            self.trade_the_targets(due_timestamp=timestamp, happen_timestamp=timestamp,\n                                   long_selected=set(long_df['entity_id'].to_list()),\n                                   short_selected=set(short_df['entity_id'].to_list()))\n        except Exception as e:\n            self.logger.error(e)\n\n\nif __name__ == '__main__':\n    entity_id = 'stock_sh_600519'\n    Stock1dKdata.record_data(entity_id=entity_id, provider='em')\n    StockActorSummary.record_data(entity_id=entity_id, provider='em')\n    FollowIITrader(start_timestamp='2002-01-01', end_timestamp='2021-01-01', entity_ids=[entity_id],\n                   provider='em', adjust_type=AdjustType.qfq, profit_threshold=None).run()\n```\n\n所以，写一个策略其实还是很简单的嘛。\n你可以发挥想象力，社保重仓买买买，外资重仓买买买，董事长跟小姨子跑了卖卖卖......\n\n然后，刷新一下[http://127.0.0.1:8050/](http://127.0.0.1:8050/)，看你运行策略的performance\n\n更多可参考[策略例子](https://github.com/zvtvz/zvt/tree/master/examples/trader)\n\n#### 严肃一点(formal)\n简单的计算可以通过query_data来完成，这里说的是系统设计的二维索引多标的计算模型。\n\n下面以技术因子为例对**计算流程**进行说明:\n```\nIn [7]: from zvt.factors import *\nIn [8]: factor = BullFactor(codes=['000338','601318'],start_timestamp='2019-01-01',end_timestamp='2019-06-10', transformer=MacdTransformer(count_live_dead=True))\n```\n### data_df\ndata_df为factor的原始数据，即通过query_data从数据库读取到的数据,为一个**二维索引**DataFrame\n```\nIn [11]: factor.data_df\nOut[11]:\n                           level   high                          id        entity_id   open    low  timestamp  close\nentity_id       timestamp\nstock_sh_601318 2019-01-02    1d  54.91  stock_sh_601318_2019-01-02  stock_sh_601318  54.78  53.70 2019-01-02  53.94\n                2019-01-03    1d  55.06  stock_sh_601318_2019-01-03  stock_sh_601318  53.91  53.82 2019-01-03  54.42\n                2019-01-04    1d  55.71  stock_sh_601318_2019-01-04  stock_sh_601318  54.03  53.98 2019-01-04  55.31\n                2019-01-07    1d  55.88  stock_sh_601318_2019-01-07  stock_sh_601318  55.80  54.64 2019-01-07  55.03\n                2019-01-08    1d  54.83  stock_sh_601318_2019-01-08  stock_sh_601318  54.79  53.96 2019-01-08  54.54\n...                          ...    ...                         ...              ...    ...    ...        ...    ...\nstock_sz_000338 2019-06-03    1d  11.04  stock_sz_000338_2019-06-03  stock_sz_000338  10.93  10.74 2019-06-03  10.81\n                2019-06-04    1d  10.85  stock_sz_000338_2019-06-04  stock_sz_000338  10.84  10.57 2019-06-04  10.73\n                2019-06-05    1d  10.92  stock_sz_000338_2019-06-05  stock_sz_000338  10.87  10.59 2019-06-05  10.59\n                2019-06-06    1d  10.71  stock_sz_000338_2019-06-06  stock_sz_000338  10.59  10.49 2019-06-06  10.65\n                2019-06-10    1d  11.05  stock_sz_000338_2019-06-10  stock_sz_000338  10.73  10.71 2019-06-10  11.02\n\n[208 rows x 8 columns]\n```\n\n### factor_df\nfactor_df为transformer对data_df进行计算后得到的数据，设计因子即对[transformer](https://github.com/zvtvz/zvt/blob/master/src/zvt/contract/factor.py#L34)进行扩展，例子中用的是MacdTransformer()。\n\n```\nIn [12]: factor.factor_df\nOut[12]:\n                           level   high                          id        entity_id   open    low  timestamp  close      diff       dea      macd\nentity_id       timestamp\nstock_sh_601318 2019-01-02    1d  54.91  stock_sh_601318_2019-01-02  stock_sh_601318  54.78  53.70 2019-01-02  53.94       NaN       NaN       NaN\n                2019-01-03    1d  55.06  stock_sh_601318_2019-01-03  stock_sh_601318  53.91  53.82 2019-01-03  54.42       NaN       NaN       NaN\n                2019-01-04    1d  55.71  stock_sh_601318_2019-01-04  stock_sh_601318  54.03  53.98 2019-01-04  55.31       NaN       NaN       NaN\n                2019-01-07    1d  55.88  stock_sh_601318_2019-01-07  stock_sh_601318  55.80  54.64 2019-01-07  55.03       NaN       NaN       NaN\n                2019-01-08    1d  54.83  stock_sh_601318_2019-01-08  stock_sh_601318  54.79  53.96 2019-01-08  54.54       NaN       NaN       NaN\n...                          ...    ...                         ...              ...    ...    ...        ...    ...       ...       ...       ...\nstock_sz_000338 2019-06-03    1d  11.04  stock_sz_000338_2019-06-03  stock_sz_000338  10.93  10.74 2019-06-03  10.81 -0.121336 -0.145444  0.048215\n                2019-06-04    1d  10.85  stock_sz_000338_2019-06-04  stock_sz_000338  10.84  10.57 2019-06-04  10.73 -0.133829 -0.143121  0.018583\n                2019-06-05    1d  10.92  stock_sz_000338_2019-06-05  stock_sz_000338  10.87  10.59 2019-06-05  10.59 -0.153260 -0.145149 -0.016223\n                2019-06-06    1d  10.71  stock_sz_000338_2019-06-06  stock_sz_000338  10.59  10.49 2019-06-06  10.65 -0.161951 -0.148509 -0.026884\n                2019-06-10    1d  11.05  stock_sz_000338_2019-06-10  stock_sz_000338  10.73  10.71 2019-06-10  11.02 -0.137399 -0.146287  0.017776\n\n[208 rows x 11 columns]\n```\n\n### result_df\nresult_df为可用于选股器的**二维索引**DataFrame，通过对data_df或factor_df计算来实现。\n该例子在计算macd之后，利用factor_df,黄白线在0轴上为True,否则为False，[具体代码](https://github.com/zvtvz/zvt/blob/master/src/zvt/factors/technical_factor.py#L56)\n\n```\nIn [14]: factor.result_df\nOut[14]:\n                            score\nentity_id       timestamp\nstock_sh_601318 2019-01-02  False\n                2019-01-03  False\n                2019-01-04  False\n                2019-01-07  False\n                2019-01-08  False\n...                           ...\nstock_sz_000338 2019-06-03  False\n                2019-06-04  False\n                2019-06-05  False\n                2019-06-06  False\n                2019-06-10  False\n\n[208 rows x 1 columns]\n```\n\nresult_df的格式如下：\n\n\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/result_df.png'/></p>\n\nfilter_result 为 True 或 False, score_result 取值为 0 到 1。\n\n\n结合选股器和回测，整个流程如下：\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/flow.png'/></p>\n\n## 环境设置（可选）\n```\n>>> from zvt import *\n>>> zvt_env\n{'zvt_home': '/Users/foolcage/zvt-home',\n 'data_path': '/Users/foolcage/zvt-home/data',\n 'tmp_path': '/Users/foolcage/zvt-home/tmp',\n 'ui_path': '/Users/foolcage/zvt-home/ui',\n 'log_path': '/Users/foolcage/zvt-home/logs'}\n\n>>> zvt_config \n```\n\n* jq_username 聚宽数据用户名\n* jq_password 聚宽数据密码\n* smtp_host 邮件服务器host\n* smtp_port 邮件服务器端口\n* email_username smtp邮箱账户\n* email_password smtp邮箱密码\n* wechat_app_id\n* wechat_app_secrect\n\n```\n>>> init_config(current_config=zvt_config, jq_username='xxx', jq_password='yyy')\n```\n> 通用的配置方式为: init_config(current_config=zvt_config, **kv)\n\n### 历史数据\n\nZVT支持数据增量更新，用户之间可以共享历史数据，这样可以节省很多时间。\n\n#### 数据源\n> 新UI实时行情的计算基于QMT数据源，需要开通的同学可联系作者。\n\n项目数据支持多provider，在数据schema一致性的基础上，可根据需要进行选择和扩展，目前支持新浪，东财，交易所等免费数据。\n\n#### 数据的设计上是让provider来适配schema,而不是反过来，这样即使某provider不可用了，换一个即可，不会影响整个系统的使用。\n\n但免费数据的缺点是显而易见的:不稳定，爬取清洗数据耗时耗力，维护代价巨大，且随时可能不可用。  \n个人建议：如果只是学习研究，可以使用免费数据；如果是真正有意投身量化，还是选一家可靠的数据提供商。\n\n\n> 项目中大部分的免费数据目前都是比较稳定的，且做过严格测试，特别是东财的数据，可放心使用\n\n> 添加其他数据提供商， 请参考[数据扩展教程](https://zvtvz.github.io/zvt/#/data_extending)\n\n## 开发\n\n### clone代码\n\n```\ngit clone https://github.com/zvtvz/zvt.git\n```\n\n设置项目的virtual env(python>=3.6),安装依赖\n```\npip3 install -r requirements.txt\npip3 install pytest\n```\n\n### 测试案例\npycharm导入工程(推荐,你也可以使用其他ide)，然后pytest跑测试案例\n\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/pytest.jpg'/></p>\n\n大部分功能使用都可以从tests里面参考\n\n## 贡献\n期待能有更多的开发者参与到 zvt 的开发中来，我会保证尽快 Reivew PR 并且及时回复。但提交 PR 请确保\n\n先看一下[1分钟代码规范](https://github.com/zvtvz/zvt/blob/master/code_of_conduct.md)\n\n1. 通过所有单元测试，如若是新功能，请为其新增单元测试\n2. 遵守开发规范\n3. 如若需要，请更新相对应的文档\n\n也非常欢迎开发者能为 zvt 提供更多的示例，共同来完善文档。\n\n## 请作者喝杯咖啡\n\n如果你觉得项目对你有帮助,可以请作者喝杯咖啡  \n<img src=\"https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/alipay-cn.png\" width=\"25%\" alt=\"Alipay\">　　　　　\n<img src=\"https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/wechat-cn.png\" width=\"25%\" alt=\"Wechat\">\n\n## 联系方式  \n\n目前只接受开户朋友的进群申请，微信号 foolcage  \n<img src=\"https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/wechat.jpeg\" width=\"25%\" alt=\"Wechat\">\n\n------\n微信公众号:  \n<img src=\"https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/gongzhonghao.jpg\" width=\"25%\" alt=\"Wechat\">\n\n知乎专栏:  \nhttps://zhuanlan.zhihu.com/automoney\n\n## Thanks\n<p><a href=https://www.jetbrains.com/?from=zvt><img src=\"https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/jetbrains.png\" width=\"25%\" alt=\"jetbrains\"></a></p>\n"
  },
  {
    "path": "README.md",
    "content": "[![github](https://img.shields.io/github/stars/zvtvz/zvt.svg)](https://github.com/zvtvz/zvt)\n[![image](https://img.shields.io/pypi/v/zvt.svg)](https://pypi.org/project/zvt/)\n[![image](https://img.shields.io/pypi/l/zvt.svg)](https://pypi.org/project/zvt/)\n[![image](https://img.shields.io/pypi/pyversions/zvt.svg)](https://pypi.org/project/zvt/)\n[![build](https://github.com/zvtvz/zvt/actions/workflows/build.yaml/badge.svg)](https://github.com/zvtvz/zvt/actions/workflows/build.yml)\n[![package](https://github.com/zvtvz/zvt/actions/workflows/package.yaml/badge.svg)](https://github.com/zvtvz/zvt/actions/workflows/package.yaml)\n[![Documentation Status](https://readthedocs.org/projects/zvt/badge/?version=latest)](https://zvt.readthedocs.io/en/latest/?badge=latest)\n[![codecov.io](https://codecov.io/github/zvtvz/zvt/coverage.svg?branch=master)](https://codecov.io/github/zvtvz/zvt)\n[![Downloads](https://pepy.tech/badge/zvt/month)](https://pepy.tech/project/zvt)\n\n**The origin of ZVT**\n\n[The Three Major Principles of Stock Trading](https://mp.weixin.qq.com/s/FoFR63wFSQIE_AyFubkZ6Q)\n\n**Declaration**\n\nThis project does not currently guarantee any backward compatibility, so please upgrade with caution.    \nAs the author's thoughts evolve, some things that were once considered important may become less so, and thus may not be maintained.    \nWhether the addition of some new elements will be useful to you needs to be assessed by yourself.\n\n**Read this in other languages: [中文](README-cn.md).**  \n\n**Read the docs:[https://zvt.readthedocs.io/en/latest/](https://zvt.readthedocs.io/en/latest/)**\n\n### Install\n```\npython3 -m pip install -U zvt\n```\n\n### Main ui\n\n#### Dash & Plotly UI\n\n> It's good for backtest and research, but it is not applicable for real-time market data and user interaction.\n\nAfter the installation is complete, enter zvt on the command line\n```shell\nzvt\n```\nopen [http://127.0.0.1:8050/](http://127.0.0.1:8050/)\n\n> The example shown here relies on data, factor, trader, please read [docs](https://zvt.readthedocs.io/en/latest/)\n\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/zvt-factor.png'/></p>\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/zvt-trader.png'/></p>\n\n> The core concept of the system is visual, and the name of the interface corresponds to it one-to-one, so it is also uniform and extensible.\n\n> You can write and run the strategy in your favorite ide, and then view its related targets, factor, signal and performance on the UI.\n\n#### Rest api and standalone UI\n> It is more flexible and more scalable, more suitable for handling real-time market data and user interaction. \n> Combined with the dynamic tag system provided by ZVT, it offers a trading approach that combines AI with human intervention.\n\n- Init tag system\n\nrun following scripts:  \n\nhttps://github.com/zvtvz/zvt/blob/master/src/zvt/tasks/init_tag_system.py\nhttps://github.com/zvtvz/zvt/blob/master/src/zvt/tasks/stock_pool_runner.py\nhttps://github.com/zvtvz/zvt/blob/master/src/zvt/tasks/qmt_data_runner.py\nhttps://github.com/zvtvz/zvt/blob/master/src/zvt/tasks/qmt_tick_runner.py\n\n- Install uvicorn\n```shell\npip install uvicorn\n```\n- Run zvt server\n\nAfter the installation is complete, enter zvt_server on the command line\n```shell\nzvt_server\n```\nOr run it from source code:\nhttps://github.com/zvtvz/zvt/blob/master/src/zvt/zvt_server.py\n\n- Check the api docs \n\nopen [http://127.0.0.1:8090/docs](http://127.0.0.1:8090/docs)\n\n- Deploy the front end service\n\nFront end source code: https://github.com/zvtvz/zvt_ui\n\nChange the env file:\nhttps://github.com/zvtvz/zvt_ui/blob/main/.env\n\nSet {your server IP} to zvt_server IP\n\n```text\nNEXT_PUBLIC_SERVER = {your server IP}\n```\n\nThen refer to the frontend's README to start the frontend service.\n\nopen [http://127.0.0.1:3000/trade](http://127.0.0.1:3000/trade)\n\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/big-picture.jpg'/></p>\n\n### Behold, the power of zvt:\n```\n>>> from zvt.domain import Stock, Stock1dHfqKdata\n>>> from zvt.ml import MaStockMLMachine\n>>> Stock.record_data(provider=\"em\")\n>>> entity_ids = [\"stock_sz_000001\", \"stock_sz_000338\", \"stock_sh_601318\"]\n>>> Stock1dHfqKdata.record_data(provider=\"em\", entity_ids=entity_ids, sleeping_time=1)\n>>> machine = MaStockMLMachine(entity_ids=[\"stock_sz_000001\"], data_provider=\"em\")\n>>> machine.train()\n>>> machine.predict()\n>>> machine.draw_result(entity_id=\"stock_sz_000001\")\n```\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/pred_close.png'/></p>\n\n> The few lines of code above has done: data capture, persistence, incremental update, machine learning, prediction, and display results.\n> Once you are familiar with the core concepts of the system, you can apply it to any target in the market.\n\n### Data\n\n#### China stock\n```\n>>> from zvt.domain import *\n>>> Stock.record_data(provider=\"em\")\n>>> df = Stock.query_data(provider=\"em\", index='code')\n>>> print(df)\n\n                     id        entity_id  timestamp entity_type exchange    code   name  list_date end_date\ncode\n000001  stock_sz_000001  stock_sz_000001 1991-04-03       stock       sz  000001   平安银行 1991-04-03     None\n000002  stock_sz_000002  stock_sz_000002 1991-01-29       stock       sz  000002  万  科Ａ 1991-01-29     None\n000004  stock_sz_000004  stock_sz_000004 1990-12-01       stock       sz  000004   国华网安 1990-12-01     None\n000005  stock_sz_000005  stock_sz_000005 1990-12-10       stock       sz  000005   世纪星源 1990-12-10     None\n000006  stock_sz_000006  stock_sz_000006 1992-04-27       stock       sz  000006   深振业Ａ 1992-04-27     None\n...                 ...              ...        ...         ...      ...     ...    ...        ...      ...\n605507  stock_sh_605507  stock_sh_605507 2021-08-02       stock       sh  605507   国邦医药 2021-08-02     None\n605577  stock_sh_605577  stock_sh_605577 2021-08-24       stock       sh  605577   龙版传媒 2021-08-24     None\n605580  stock_sh_605580  stock_sh_605580 2021-08-19       stock       sh  605580   恒盛能源 2021-08-19     None\n605588  stock_sh_605588  stock_sh_605588 2021-08-12       stock       sh  605588   冠石科技 2021-08-12     None\n605589  stock_sh_605589  stock_sh_605589 2021-08-10       stock       sh  605589   圣泉集团 2021-08-10     None\n\n[4136 rows x 9 columns]\n```\n\n#### USA stock\n```\n>>> Stockus.record_data()\n>>> df = Stockus.query_data(index='code')\n>>> print(df)\n\n                       id            entity_id timestamp entity_type exchange  code                         name list_date end_date\ncode\nA          stockus_nyse_A       stockus_nyse_A       NaT     stockus     nyse     A                          安捷伦      None     None\nAA        stockus_nyse_AA      stockus_nyse_AA       NaT     stockus     nyse    AA                         美国铝业      None     None\nAAC      stockus_nyse_AAC     stockus_nyse_AAC       NaT     stockus     nyse   AAC      Ares Acquisition Corp-A      None     None\nAACG  stockus_nasdaq_AACG  stockus_nasdaq_AACG       NaT     stockus   nasdaq  AACG    ATA Creativity Global ADR      None     None\nAACG    stockus_nyse_AACG    stockus_nyse_AACG       NaT     stockus     nyse  AACG    ATA Creativity Global ADR      None     None\n...                   ...                  ...       ...         ...      ...   ...                          ...       ...      ...\nZWRK  stockus_nasdaq_ZWRK  stockus_nasdaq_ZWRK       NaT     stockus   nasdaq  ZWRK    Z-Work Acquisition Corp-A      None     None\nZY      stockus_nasdaq_ZY    stockus_nasdaq_ZY       NaT     stockus   nasdaq    ZY                 Zymergen Inc      None     None\nZYME    stockus_nyse_ZYME    stockus_nyse_ZYME       NaT     stockus     nyse  ZYME                Zymeworks Inc      None     None\nZYNE  stockus_nasdaq_ZYNE  stockus_nasdaq_ZYNE       NaT     stockus   nasdaq  ZYNE  Zynerba Pharmaceuticals Inc      None     None\nZYXI  stockus_nasdaq_ZYXI  stockus_nasdaq_ZYXI       NaT     stockus   nasdaq  ZYXI                    Zynex Inc      None     None\n\n[5826 rows x 9 columns]\n\n>>> Stockus.query_data(code='AAPL')\n                    id            entity_id timestamp entity_type exchange  code name list_date end_date\n0  stockus_nasdaq_AAPL  stockus_nasdaq_AAPL      None     stockus   nasdaq  AAPL   苹果      None     None\n```\n\n#### Hong Kong stock\n```\n>>> Stockhk.record_data()\n>>> df = Stockhk.query_data(index='code')\n>>> print(df)\n\n                     id         entity_id timestamp entity_type exchange   code    name list_date end_date\ncode\n00001  stockhk_hk_00001  stockhk_hk_00001       NaT     stockhk       hk  00001      长和      None     None\n00002  stockhk_hk_00002  stockhk_hk_00002       NaT     stockhk       hk  00002    中电控股      None     None\n00003  stockhk_hk_00003  stockhk_hk_00003       NaT     stockhk       hk  00003  香港中华煤气      None     None\n00004  stockhk_hk_00004  stockhk_hk_00004       NaT     stockhk       hk  00004   九龙仓集团      None     None\n00005  stockhk_hk_00005  stockhk_hk_00005       NaT     stockhk       hk  00005    汇丰控股      None     None\n...                 ...               ...       ...         ...      ...    ...     ...       ...      ...\n09996  stockhk_hk_09996  stockhk_hk_09996       NaT     stockhk       hk  09996  沛嘉医疗-B      None     None\n09997  stockhk_hk_09997  stockhk_hk_09997       NaT     stockhk       hk  09997    康基医疗      None     None\n09998  stockhk_hk_09998  stockhk_hk_09998       NaT     stockhk       hk  09998    光荣控股      None     None\n09999  stockhk_hk_09999  stockhk_hk_09999       NaT     stockhk       hk  09999    网易-S      None     None\n80737  stockhk_hk_80737  stockhk_hk_80737       NaT     stockhk       hk  80737  湾区发展-R      None     None\n\n[2597 rows x 9 columns]\n\n>>> df[df.code=='00700']\n\n                    id         entity_id timestamp entity_type exchange   code  name list_date end_date\n2112  stockhk_hk_00700  stockhk_hk_00700      None     stockhk       hk  00700  腾讯控股      None     None\n\n```\n\n#### And more\n```\n>>> from zvt.contract import *\n>>> zvt_context.tradable_schema_map\n\n{'stockus': zvt.domain.meta.stockus_meta.Stockus,\n 'stockhk': zvt.domain.meta.stockhk_meta.Stockhk,\n 'index': zvt.domain.meta.index_meta.Index,\n 'etf': zvt.domain.meta.etf_meta.Etf,\n 'stock': zvt.domain.meta.stock_meta.Stock,\n 'block': zvt.domain.meta.block_meta.Block,\n 'fund': zvt.domain.meta.fund_meta.Fund}\n```\n\nThe key is tradable entity type, and the value is the schema. The system provides unified **record (record_data)** and **query (query_data)** methods for the schema.\n\n```\n>>> Index.record_data()\n>>> df=Index.query_data(filters=[Index.category=='scope',Index.exchange='sh'])\n>>> print(df)\n                 id        entity_id  timestamp entity_type exchange    code    name  list_date end_date publisher category  base_point\n0   index_sh_000001  index_sh_000001 1990-12-19       index       sh  000001    上证指数 1991-07-15     None   csindex    scope      100.00\n1   index_sh_000002  index_sh_000002 1990-12-19       index       sh  000002    Ａ股指数 1992-02-21     None   csindex    scope      100.00\n2   index_sh_000003  index_sh_000003 1992-02-21       index       sh  000003    B股指数 1992-08-17     None   csindex    scope      100.00\n3   index_sh_000010  index_sh_000010 2002-06-28       index       sh  000010   上证180 2002-07-01     None   csindex    scope     3299.06\n4   index_sh_000016  index_sh_000016 2003-12-31       index       sh  000016    上证50 2004-01-02     None   csindex    scope     1000.00\n..              ...              ...        ...         ...      ...     ...     ...        ...      ...       ...      ...         ...\n25  index_sh_000020  index_sh_000020 2007-12-28       index       sh  000020    中型综指 2008-05-12     None   csindex    scope     1000.00\n26  index_sh_000090  index_sh_000090 2009-12-31       index       sh  000090    上证流通 2010-12-02     None   csindex    scope     1000.00\n27  index_sh_930903  index_sh_930903 2012-12-31       index       sh  930903    中证Ａ股 2016-10-18     None   csindex    scope     1000.00\n28  index_sh_000688  index_sh_000688 2019-12-31       index       sh  000688    科创50 2020-07-23     None   csindex    scope     1000.00\n29  index_sh_931643  index_sh_931643 2019-12-31       index       sh  931643  科创创业50 2021-06-01     None   csindex    scope     1000.00\n\n[30 rows x 12 columns]\n\n```\n\n### EntityEvent\nWe have tradable entity and then events about them.\n\n#### Market quotes\nthe TradableEntity quote schema follows the following rules:\n```\n{entity_shema}{level}{adjust_type}Kdata\n```\n* entity_schema\n\nTradableEntity class，e.g., Stock,Stockus.\n\n* level\n```\n>>> for level in IntervalLevel:\n        print(level.value)\n```\n\n* adjust type\n```\n>>> for adjust_type in AdjustType:\n        print(adjust_type.value)\n```\n\n> Note: In order to be compatible with historical data, the pre-reset is an exception, {adjust_type} is left empty\n\nqfq\n```\n>>> Stock1dKdata.record_data(code='000338', provider='em')\n>>> df = Stock1dKdata.query_data(code='000338', provider='em')\n>>> print(df)\n\n                              id        entity_id  timestamp provider    code  name level   open  close   high    low     volume      turnover  change_pct  turnover_rate\n0     stock_sz_000338_2007-04-30  stock_sz_000338 2007-04-30     None  000338  潍柴动力    1d   2.33   2.00   2.40   1.87   207375.0  1.365189e+09      3.2472         0.1182\n1     stock_sz_000338_2007-05-08  stock_sz_000338 2007-05-08     None  000338  潍柴动力    1d   2.11   1.94   2.20   1.87    86299.0  5.563198e+08     -0.0300         0.0492\n2     stock_sz_000338_2007-05-09  stock_sz_000338 2007-05-09     None  000338  潍柴动力    1d   1.90   1.81   1.94   1.66    93823.0  5.782065e+08     -0.0670         0.0535\n3     stock_sz_000338_2007-05-10  stock_sz_000338 2007-05-10     None  000338  潍柴动力    1d   1.78   1.85   1.98   1.75    47720.0  2.999226e+08      0.0221         0.0272\n4     stock_sz_000338_2007-05-11  stock_sz_000338 2007-05-11     None  000338  潍柴动力    1d   1.81   1.73   1.81   1.66    39273.0  2.373126e+08     -0.0649         0.0224\n...                          ...              ...        ...      ...     ...   ...   ...    ...    ...    ...    ...        ...           ...         ...            ...\n3426  stock_sz_000338_2021-08-27  stock_sz_000338 2021-08-27     None  000338  潍柴动力    1d  19.39  20.30  20.30  19.25  1688497.0  3.370241e+09      0.0601         0.0398\n3427  stock_sz_000338_2021-08-30  stock_sz_000338 2021-08-30     None  000338  潍柴动力    1d  20.30  20.09  20.31  19.78  1187601.0  2.377957e+09     -0.0103         0.0280\n3428  stock_sz_000338_2021-08-31  stock_sz_000338 2021-08-31     None  000338  潍柴动力    1d  20.20  20.07  20.63  19.70  1143985.0  2.295195e+09     -0.0010         0.0270\n3429  stock_sz_000338_2021-09-01  stock_sz_000338 2021-09-01     None  000338  潍柴动力    1d  19.98  19.68  19.98  19.15  1218697.0  2.383841e+09     -0.0194         0.0287\n3430  stock_sz_000338_2021-09-02  stock_sz_000338 2021-09-02     None  000338  潍柴动力    1d  19.71  19.85  19.97  19.24  1023545.0  2.012006e+09      0.0086         0.0241\n\n[3431 rows x 15 columns]\n\n>>> Stockus1dKdata.record_data(code='AAPL', provider='em')\n>>> df = Stockus1dKdata.query_data(code='AAPL', provider='em')\n>>> print(df)\n\n                                  id            entity_id  timestamp provider  code name level    open   close    high     low      volume      turnover  change_pct  turnover_rate\n0     stockus_nasdaq_AAPL_1984-09-07  stockus_nasdaq_AAPL 1984-09-07     None  AAPL   苹果    1d   -5.59   -5.59   -5.58   -5.59   2981600.0  0.000000e+00      0.0000         0.0002\n1     stockus_nasdaq_AAPL_1984-09-10  stockus_nasdaq_AAPL 1984-09-10     None  AAPL   苹果    1d   -5.59   -5.59   -5.58   -5.59   2346400.0  0.000000e+00      0.0000         0.0001\n2     stockus_nasdaq_AAPL_1984-09-11  stockus_nasdaq_AAPL 1984-09-11     None  AAPL   苹果    1d   -5.58   -5.58   -5.58   -5.58   5444000.0  0.000000e+00      0.0018         0.0003\n3     stockus_nasdaq_AAPL_1984-09-12  stockus_nasdaq_AAPL 1984-09-12     None  AAPL   苹果    1d   -5.58   -5.59   -5.58   -5.59   4773600.0  0.000000e+00     -0.0018         0.0003\n4     stockus_nasdaq_AAPL_1984-09-13  stockus_nasdaq_AAPL 1984-09-13     None  AAPL   苹果    1d   -5.58   -5.58   -5.58   -5.58   7429600.0  0.000000e+00      0.0018         0.0004\n...                              ...                  ...        ...      ...   ...  ...   ...     ...     ...     ...     ...         ...           ...         ...            ...\n8765  stockus_nasdaq_AAPL_2021-08-27  stockus_nasdaq_AAPL 2021-08-27     None  AAPL   苹果    1d  147.48  148.60  148.75  146.83  55802388.0  8.265452e+09      0.0072         0.0034\n8766  stockus_nasdaq_AAPL_2021-08-30  stockus_nasdaq_AAPL 2021-08-30     None  AAPL   苹果    1d  149.00  153.12  153.49  148.61  90956723.0  1.383762e+10      0.0304         0.0055\n8767  stockus_nasdaq_AAPL_2021-08-31  stockus_nasdaq_AAPL 2021-08-31     None  AAPL   苹果    1d  152.66  151.83  152.80  151.29  86453117.0  1.314255e+10     -0.0084         0.0052\n8768  stockus_nasdaq_AAPL_2021-09-01  stockus_nasdaq_AAPL 2021-09-01     None  AAPL   苹果    1d  152.83  152.51  154.98  152.34  80313711.0  1.235321e+10      0.0045         0.0049\n8769  stockus_nasdaq_AAPL_2021-09-02  stockus_nasdaq_AAPL 2021-09-02     None  AAPL   苹果    1d  153.87  153.65  154.72  152.40  71171317.0  1.093251e+10      0.0075         0.0043\n\n[8770 rows x 15 columns]\n```\n\nhfq\n```\n>>> Stock1dHfqKdata.record_data(code='000338', provider='em')\n>>> df = Stock1dHfqKdata.query_data(code='000338', provider='em')\n>>> print(df)\n\n                              id        entity_id  timestamp provider    code  name level    open   close    high     low     volume      turnover  change_pct  turnover_rate\n0     stock_sz_000338_2007-04-30  stock_sz_000338 2007-04-30     None  000338  潍柴动力    1d   70.00   64.93   71.00   62.88   207375.0  1.365189e+09      2.1720         0.1182\n1     stock_sz_000338_2007-05-08  stock_sz_000338 2007-05-08     None  000338  潍柴动力    1d   66.60   64.00   68.00   62.88    86299.0  5.563198e+08     -0.0143         0.0492\n2     stock_sz_000338_2007-05-09  stock_sz_000338 2007-05-09     None  000338  潍柴动力    1d   63.32   62.00   63.88   59.60    93823.0  5.782065e+08     -0.0313         0.0535\n3     stock_sz_000338_2007-05-10  stock_sz_000338 2007-05-10     None  000338  潍柴动力    1d   61.50   62.49   64.48   61.01    47720.0  2.999226e+08      0.0079         0.0272\n4     stock_sz_000338_2007-05-11  stock_sz_000338 2007-05-11     None  000338  潍柴动力    1d   61.90   60.65   61.90   59.70    39273.0  2.373126e+08     -0.0294         0.0224\n...                          ...              ...        ...      ...     ...   ...   ...     ...     ...     ...     ...        ...           ...         ...            ...\n3426  stock_sz_000338_2021-08-27  stock_sz_000338 2021-08-27     None  000338  潍柴动力    1d  331.97  345.95  345.95  329.82  1688497.0  3.370241e+09      0.0540         0.0398\n3427  stock_sz_000338_2021-08-30  stock_sz_000338 2021-08-30     None  000338  潍柴动力    1d  345.95  342.72  346.10  337.96  1187601.0  2.377957e+09     -0.0093         0.0280\n3428  stock_sz_000338_2021-08-31  stock_sz_000338 2021-08-31     None  000338  潍柴动力    1d  344.41  342.41  351.02  336.73  1143985.0  2.295195e+09     -0.0009         0.0270\n3429  stock_sz_000338_2021-09-01  stock_sz_000338 2021-09-01     None  000338  潍柴动力    1d  341.03  336.42  341.03  328.28  1218697.0  2.383841e+09     -0.0175         0.0287\n3430  stock_sz_000338_2021-09-02  stock_sz_000338 2021-09-02     None  000338  潍柴动力    1d  336.88  339.03  340.88  329.67  1023545.0  2.012006e+09      0.0078         0.0241\n\n[3431 rows x 15 columns]\n```\n\n#### Finance factor\n```\n>>> FinanceFactor.record_data(code='000338')\n>>> FinanceFactor.query_data(code='000338',columns=FinanceFactor.important_cols(),index='timestamp')\n\n            basic_eps  total_op_income    net_profit  op_income_growth_yoy  net_profit_growth_yoy     roe    rota  gross_profit_margin  net_margin  timestamp\ntimestamp\n2002-12-31        NaN     1.962000e+07  2.471000e+06                   NaN                    NaN     NaN     NaN               0.2068      0.1259 2002-12-31\n2003-12-31       1.27     3.574000e+09  2.739000e+08              181.2022               109.8778  0.7729  0.1783               0.2551      0.0766 2003-12-31\n2004-12-31       1.75     6.188000e+09  5.369000e+08                0.7313                 0.9598  0.3245  0.1474               0.2489      0.0868 2004-12-31\n2005-12-31       0.93     5.283000e+09  3.065000e+08               -0.1463                -0.4291  0.1327  0.0603               0.2252      0.0583 2005-12-31\n2006-03-31       0.33     1.859000e+09  1.079000e+08                   NaN                    NaN     NaN     NaN                  NaN      0.0598 2006-03-31\n...               ...              ...           ...                   ...                    ...     ...     ...                  ...         ...        ...\n2020-08-28       0.59     9.449000e+10  4.680000e+09                0.0400                -0.1148  0.0983  0.0229               0.1958      0.0603 2020-08-28\n2020-10-31       0.90     1.474000e+11  7.106000e+09                0.1632                 0.0067  0.1502  0.0347               0.1949      0.0590 2020-10-31\n2021-03-31       1.16     1.975000e+11  9.207000e+09                0.1327                 0.0112  0.1919  0.0444               0.1931      0.0571 2021-03-31\n2021-04-30       0.42     6.547000e+10  3.344000e+09                0.6788                 0.6197  0.0622  0.0158               0.1916      0.0667 2021-04-30\n2021-08-31       0.80     1.264000e+11  6.432000e+09                0.3375                 0.3742  0.1125  0.0287               0.1884      0.0653 2021-08-31\n\n[66 rows x 10 columns]\n```\n\n#### Three financial tables\n```\n>>> BalanceSheet.record_data(code='000338')\n>>> IncomeStatement.record_data(code='000338')\n>>> CashFlowStatement.record_data(code='000338')\n```\n\n#### And more\n```\n>>> zvt_context.schemas\n[zvt.domain.dividend_financing.DividendFinancing,\n zvt.domain.dividend_financing.DividendDetail,\n zvt.domain.dividend_financing.SpoDetail...]\n```\n\nAll schemas is registered in zvt_context.schemas, **schema** is table, data structure.\nThe fields and meaning could be checked in following ways:\n\n* help\n\ntype the schema. and press tab to show its fields or .help()\n```\n>>> FinanceFactor.help()\n```\n\n* source code\n\nSchemas defined in [domain](https://github.com/zvtvz/zvt/tree/master/src/zvt/domain)\n\nFrom above examples, you should know the unified way of recording data:\n\n> Schema.record_data(provider='your provider',codes='the codes')\n\nNote the optional parameter provider, which represents the data provider. \nA schema can have multiple providers, which is the cornerstone of system stability.\n\nCheck the provider has been implemented:\n```\n>>> Stock.provider_map_recorder\n{'joinquant': zvt.recorders.joinquant.meta.jq_stock_meta_recorder.JqChinaStockRecorder,\n 'exchange': zvt.recorders.exchange.exchange_stock_meta_recorder.ExchangeStockMetaRecorder,\n 'em': zvt.recorders.em.meta.em_stock_meta_recorder.EMStockRecorder,\n 'eastmoney': zvt.recorders.eastmoney.meta.eastmoney_stock_meta_recorder.EastmoneyChinaStockListRecorder}\n\n```\nYou can use any provider to get the data, the first one is used by default.\n\nOne more example, the stock sector data recording:\n```\n>>> Block.provider_map_recorder\n{'eastmoney': zvt.recorders.eastmoney.meta.eastmoney_block_meta_recorder.EastmoneyChinaBlockRecorder,\n 'sina': zvt.recorders.sina.meta.sina_block_recorder.SinaBlockRecorder}\n\n>>> Block.record_data(provider='sina')\nBlock registered recorders:{'eastmoney': <class 'zvt.recorders.eastmoney.meta.china_stock_category_recorder.EastmoneyChinaBlockRecorder'>, 'sina': <class 'zvt.recorders.sina.meta.sina_china_stock_category_recorder.SinaChinaBlockRecorder'>}\n2020-03-04 23:56:48,931  INFO  MainThread  finish record sina blocks:industry\n2020-03-04 23:56:49,450  INFO  MainThread  finish record sina blocks:concept\n```\n\nLearn more about record_data\n\n* The parameter code[single], codes[multiple] represent the stock codes to be recorded\n* Recording the whole market if not set code, codes\n* This method will store the data locally and only do incremental updates\n\nRefer to the scheduling recoding way[data runner](https://github.com/zvtvz/zvt/blob/master/examples/data_runner)\n\n#### Market-wide stock selection\n\nAfter recording the data of the whole market, you can quickly query the required data locally.\n\nAn example: the top 20 stocks with roe>8% and revenue growth>8% in the 2018 annual report\n```\n>>> df=FinanceFactor.query_data(filters=[FinanceFactor.roe>0.08,FinanceFactor.report_period=='year',FinanceFactor.op_income_growth_yoy>0.08],start_timestamp='2019-01-01',order=FinanceFactor.roe.desc(),limit=20,columns=[\"code\"]+FinanceFactor.important_cols(),index='code')\n\n          code  basic_eps  total_op_income    net_profit  op_income_growth_yoy  net_profit_growth_yoy     roe    rota  gross_profit_margin  net_margin  timestamp\ncode\n000048  000048     2.7350     4.919000e+09  1.101000e+09                0.4311                 1.5168  0.7035  0.1988               0.5243      0.2355 2020-04-30\n000912  000912     0.3500     4.405000e+09  3.516000e+08                0.1796                 1.2363  4.7847  0.0539               0.2175      0.0795 2019-03-20\n002207  002207     0.2200     3.021000e+08  5.189000e+07                0.1600                 1.1526  1.1175  0.1182               0.1565      0.1718 2020-04-27\n002234  002234     5.3300     3.276000e+09  1.610000e+09                0.8023                 3.2295  0.8361  0.5469               0.5968      0.4913 2020-04-21\n002458  002458     3.7900     3.584000e+09  2.176000e+09                1.4326                 4.9973  0.8318  0.6754               0.6537      0.6080 2020-02-20\n...        ...        ...              ...           ...                   ...                    ...     ...     ...                  ...         ...        ...\n600701  600701    -3.6858     7.830000e+08 -3.814000e+09                1.3579                -0.0325  1.9498 -0.7012               0.4173     -4.9293 2020-04-29\n600747  600747    -1.5600     3.467000e+08 -2.290000e+09                2.1489                -0.4633  3.1922 -1.5886               0.0378     -6.6093 2020-06-30\n600793  600793     1.6568     1.293000e+09  1.745000e+08                0.1164                 0.8868  0.7490  0.0486               0.1622      0.1350 2019-04-30\n600870  600870     0.0087     3.096000e+07  4.554000e+06                0.7773                 1.3702  0.7458  0.0724               0.2688      0.1675 2019-03-30\n688169  688169    15.6600     4.205000e+09  7.829000e+08                0.3781                 1.5452  0.7172  0.4832               0.3612      0.1862 2020-04-28\n\n[20 rows x 11 columns]\n```\n\nSo, you should be able to answer the following three questions now:\n* What data is there?\n* How to record data?\n* How to query data?\n\nFor more advanced usage and extended data, please refer to the data section in the detailed document.\n\n### Write strategy\nNow we could write strategy basing on TradableEntity and EntityEvent.\nThe so-called strategy backtesting is nothing but repeating the following process：\n\n#### At a certain time, find the targets which matching conditions, buy and sell them, and see the performance.\n\nTwo modes to write strategy:\n* solo (free style)\n\nAt a certain time, calculate conditions according to the events, buy and sell\n\n* formal\n\nThe calculation model of the two-dimensional index and multi-entity\n\n#### a too simple,sometimes naive person (solo)\nWell, this strategy is really too simple,sometimes naive, as we do most of the time.\n> When the report comes out, I look at the report. \n> If the institution increases its position by more than 5%, I will buy it, and if the institution reduces its position by more than 50%, I will sell it.\n\nShow you the code:\n```\n# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.api import get_recent_report_date\nfrom zvt.contract import ActorType, AdjustType\nfrom zvt.domain import StockActorSummary, Stock1dKdata\nfrom zvt.trader import StockTrader\nfrom zvt.utils import pd_is_not_null, is_same_date, to_pd_timestamp\n\n\nclass FollowIITrader(StockTrader):\n    finish_date = None\n\n    def on_time(self, timestamp: pd.Timestamp):\n        recent_report_date = to_pd_timestamp(get_recent_report_date(timestamp))\n        if self.finish_date and is_same_date(recent_report_date, self.finish_date):\n            return\n        filters = [StockActorSummary.actor_type == ActorType.raised_fund.value,\n                   StockActorSummary.report_date == recent_report_date]\n\n        if self.entity_ids:\n            filters = filters + [StockActorSummary.entity_id.in_(self.entity_ids)]\n\n        df = StockActorSummary.query_data(filters=filters)\n\n        if pd_is_not_null(df):\n            self.logger.info(f'{df}')\n            self.finish_date = recent_report_date\n\n        long_df = df[df['change_ratio'] > 0.05]\n        short_df = df[df['change_ratio'] < -0.5]\n        try:\n            self.trade_the_targets(due_timestamp=timestamp, happen_timestamp=timestamp,\n                                   long_selected=set(long_df['entity_id'].to_list()),\n                                   short_selected=set(short_df['entity_id'].to_list()))\n        except Exception as e:\n            self.logger.error(e)\n\n\nif __name__ == '__main__':\n    entity_id = 'stock_sh_600519'\n    Stock1dKdata.record_data(entity_id=entity_id, provider='em')\n    StockActorSummary.record_data(entity_id=entity_id, provider='em')\n    FollowIITrader(start_timestamp='2002-01-01', end_timestamp='2021-01-01', entity_ids=[entity_id],\n                   provider='em', adjust_type=AdjustType.qfq, profit_threshold=None).run()\n```\n\nSo, writing a strategy is not that complicated.\nJust use your imagination, find the relation of the price and the events.\n\nThen refresh [http://127.0.0.1:8050/](http://127.0.0.1:8050/)，check the performance of your strategy.\n\nMore examples is in [Strategy example](https://github.com/zvtvz/zvt/tree/master/examples/trader)\n\n#### Be serious (formal)\nSimple calculation can be done through query_data.\nNow it's time to introduce the two-dimensional index multi-entity calculation model.\n\nTakes technical factors as an example to illustrate the **calculation process**:\n```\nIn [7]: from zvt.factors import *\nIn [8]: factor = BullFactor(codes=['000338','601318'],start_timestamp='2019-01-01',end_timestamp='2019-06-10', transformer=MacdTransformer(count_live_dead=True))\n```\n### data_df\n\n**two-dimensional index** DataFrame read from the schema by query_data.\n```\nIn [11]: factor.data_df\nOut[11]:\n                           level   high                          id        entity_id   open    low  timestamp  close\nentity_id       timestamp\nstock_sh_601318 2019-01-02    1d  54.91  stock_sh_601318_2019-01-02  stock_sh_601318  54.78  53.70 2019-01-02  53.94\n                2019-01-03    1d  55.06  stock_sh_601318_2019-01-03  stock_sh_601318  53.91  53.82 2019-01-03  54.42\n                2019-01-04    1d  55.71  stock_sh_601318_2019-01-04  stock_sh_601318  54.03  53.98 2019-01-04  55.31\n                2019-01-07    1d  55.88  stock_sh_601318_2019-01-07  stock_sh_601318  55.80  54.64 2019-01-07  55.03\n                2019-01-08    1d  54.83  stock_sh_601318_2019-01-08  stock_sh_601318  54.79  53.96 2019-01-08  54.54\n...                          ...    ...                         ...              ...    ...    ...        ...    ...\nstock_sz_000338 2019-06-03    1d  11.04  stock_sz_000338_2019-06-03  stock_sz_000338  10.93  10.74 2019-06-03  10.81\n                2019-06-04    1d  10.85  stock_sz_000338_2019-06-04  stock_sz_000338  10.84  10.57 2019-06-04  10.73\n                2019-06-05    1d  10.92  stock_sz_000338_2019-06-05  stock_sz_000338  10.87  10.59 2019-06-05  10.59\n                2019-06-06    1d  10.71  stock_sz_000338_2019-06-06  stock_sz_000338  10.59  10.49 2019-06-06  10.65\n                2019-06-10    1d  11.05  stock_sz_000338_2019-06-10  stock_sz_000338  10.73  10.71 2019-06-10  11.02\n\n[208 rows x 8 columns]\n```\n\n### factor_df\n**two-dimensional index** DataFrame which calculating using data_df by [transformer](https://github.com/zvtvz/zvt/blob/master/src/zvt/contract/factor.py#L34)\ne.g., MacdTransformer.\n```\nIn [12]: factor.factor_df\nOut[12]:\n                           level   high                          id        entity_id   open    low  timestamp  close      diff       dea      macd\nentity_id       timestamp\nstock_sh_601318 2019-01-02    1d  54.91  stock_sh_601318_2019-01-02  stock_sh_601318  54.78  53.70 2019-01-02  53.94       NaN       NaN       NaN\n                2019-01-03    1d  55.06  stock_sh_601318_2019-01-03  stock_sh_601318  53.91  53.82 2019-01-03  54.42       NaN       NaN       NaN\n                2019-01-04    1d  55.71  stock_sh_601318_2019-01-04  stock_sh_601318  54.03  53.98 2019-01-04  55.31       NaN       NaN       NaN\n                2019-01-07    1d  55.88  stock_sh_601318_2019-01-07  stock_sh_601318  55.80  54.64 2019-01-07  55.03       NaN       NaN       NaN\n                2019-01-08    1d  54.83  stock_sh_601318_2019-01-08  stock_sh_601318  54.79  53.96 2019-01-08  54.54       NaN       NaN       NaN\n...                          ...    ...                         ...              ...    ...    ...        ...    ...       ...       ...       ...\nstock_sz_000338 2019-06-03    1d  11.04  stock_sz_000338_2019-06-03  stock_sz_000338  10.93  10.74 2019-06-03  10.81 -0.121336 -0.145444  0.048215\n                2019-06-04    1d  10.85  stock_sz_000338_2019-06-04  stock_sz_000338  10.84  10.57 2019-06-04  10.73 -0.133829 -0.143121  0.018583\n                2019-06-05    1d  10.92  stock_sz_000338_2019-06-05  stock_sz_000338  10.87  10.59 2019-06-05  10.59 -0.153260 -0.145149 -0.016223\n                2019-06-06    1d  10.71  stock_sz_000338_2019-06-06  stock_sz_000338  10.59  10.49 2019-06-06  10.65 -0.161951 -0.148509 -0.026884\n                2019-06-10    1d  11.05  stock_sz_000338_2019-06-10  stock_sz_000338  10.73  10.71 2019-06-10  11.02 -0.137399 -0.146287  0.017776\n\n[208 rows x 11 columns]\n```\n\n### result_df\n**two-dimensional index** DataFrame which calculating using factor_df or(and) data_df.\nIt's used by TargetSelector.\n\ne.g.,[macd](https://github.com/zvtvz/zvt/blob/master/src/zvt/factors/technical_factor.py#L56)\n\n```\nIn [14]: factor.result_df\nOut[14]:\n                            filter_result\nentity_id       timestamp\nstock_sh_601318 2019-01-02  False\n                2019-01-03  False\n                2019-01-04  False\n                2019-01-07  False\n                2019-01-08  False\n...                           ...\nstock_sz_000338 2019-06-03  False\n                2019-06-04  False\n                2019-06-05  False\n                2019-06-06  False\n                2019-06-10  False\n\n[208 rows x 1 columns]\n```\n\nThe  format of result_df is as follows:\n\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/result_df.png'/></p>\n\nfilter_result is True or False, score_result is from 0 to 1\n\nCombining the stock picker and backtesting, the whole process is as follows:\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/flow.png'/></p>\n\n## Env settings（optional）\n```\n>>> from zvt import *\n>>> zvt_env\n{'zvt_home': '/Users/foolcage/zvt-home',\n 'data_path': '/Users/foolcage/zvt-home/data',\n 'tmp_path': '/Users/foolcage/zvt-home/tmp',\n 'ui_path': '/Users/foolcage/zvt-home/ui',\n 'log_path': '/Users/foolcage/zvt-home/logs'}\n\n>>> zvt_config \n```\n\n* jq_username 聚宽数据用户名\n* jq_password 聚宽数据密码\n* smtp_host 邮件服务器host\n* smtp_port 邮件服务器端口\n* email_username smtp邮箱账户\n* email_password smtp邮箱密码\n* wechat_app_id\n* wechat_app_secrect\n\n```\n>>> init_config(current_config=zvt_config, jq_username='xxx', jq_password='yyy')\n```\n>  config others this way: init_config(current_config=zvt_config, **kv)\n\n### History data\n\nZVT supports incremental data updates, and sharing historical data among users is encouraged for time-saving efficiency\n\n#### Data providers\n> The new UI's real-time quotes are based on the QMT data source. To obtain access, please contact the author.\n\nthe data could be updated from different provider, this make the system stable.\n\n> add other providers， [Data extension tutorial](https://zvtvz.github.io/zvt/#/data_extending)\n\n## Development\n\n### Clone\n\n```\ngit clone https://github.com/zvtvz/zvt.git\n```\n\nset up virtual env(python>=3.8),install requirements\n```\npip3 install -r requirements.txt\npip3 install pytest\n```\n\n### Tests\n```shell\npytest ./tests --ignore=tests/recorders/ \n```\n\n<p align=\"center\"><img src='https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/pytest.jpg'/></p>\n\nMost of the features can be referenced from the tests\n\n## Contribution\n\n[code of conduct](https://github.com/zvtvz/zvt/blob/master/code_of_conduct.md)\n\n1. Pass all unit tests, if it is a new feature, please add a new unit test for it\n2. Compliance with development specifications\n3. If necessary, please update the corresponding document\n\nDevelopers are also very welcome to provide more examples for zvt, and work together to improve the documentation.\n\n## Buy me a coffee\n\n<img src=\"https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/alipay-cn.png\" width=\"25%\" alt=\"Alipay\">　　　　　\n<img src=\"https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/wechat-cn.png\" width=\"25%\" alt=\"Wechat\">\n\n## Contact\n\nwechat:foolcage  \n<img src=\"https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/wechat.jpeg\" width=\"25%\" alt=\"Wechat\">\n\n------\nwechat subscription:  \n<img src=\"https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/gongzhonghao.jpg\" width=\"25%\" alt=\"Wechat\">\n\nzhihu:  \nhttps://zhuanlan.zhihu.com/automoney\n\n## Thanks\n<p><a href=https://www.jetbrains.com/?from=zvt><img src=\"https://raw.githubusercontent.com/zvtvz/zvt/master/docs/imgs/jetbrains.png\" width=\"25%\" alt=\"jetbrains\"></a></p>\n"
  },
  {
    "path": "api-tests/create_stock_pool_info.http",
    "content": "POST http://127.0.0.1:8090/api/work/create_stock_pool_info\naccept: application/json\nContent-Type: application/json\n\n{\n  \"stock_pool_name\": \"核心资产\",\n  \"stock_pool_type\": \"custom\"\n}"
  },
  {
    "path": "api-tests/create_stock_pools.http",
    "content": "POST http://127.0.0.1:8090/api/work/create_stock_pools\naccept: application/json\nContent-Type: application/json\n\n{\n  \"stock_pool_name\": \"核心资产\",\n  \"entity_ids\": [\n    \"stock_sh_600519\"\n  ]\n}"
  },
  {
    "path": "api-tests/del_stock_pool.http",
    "content": "DELETE http://127.0.0.1:8090/api/work/delete_stock_pool?stock_pool_name=今日异动\naccept: application/json\nContent-Type: application/json\n"
  },
  {
    "path": "api-tests/event/create_stock_topic.http",
    "content": "POST http://127.0.0.1:8090/api/event/create_stock_topic\naccept: application/json\nContent-Type: application/json\n\n{\n  \"name\": \"特斯拉FSD入华\",\n  \"desc\": \"Tesla Al在社交媒体平台“X”上发帖称，特斯拉计划明年第一季度在中国和欧洲推出被其称为“全自动驾驶”(Full Self Driving)的高级驾驶辅助系统，目前正在等待监管部门的批准。\",\n  \"created_timestamp\": \"2024-09-05 09:00:00\",\n  \"trigger_date\": \"2024-09-05\",\n  \"due_date\": \"2025-01-01\",\n  \"main_tag\": \"车路云\",\n  \"sub_tag_list\": [\n    \"自动驾驶\",\n    \"车路云\"\n  ]\n}\n\n\n\n"
  },
  {
    "path": "api-tests/event/get_stock_event.http",
    "content": "GET http://127.0.0.1:8090/api/event/get_stock_event?entity_id=stock_sz_000034\naccept: application/json\n\n\n\n"
  },
  {
    "path": "api-tests/event/get_stock_news_analysis.http",
    "content": "GET http://127.0.0.1:8090/api/event/get_stock_news_analysis\naccept: application/json\n\n\n\n"
  },
  {
    "path": "api-tests/event/get_tag_suggestions_stats.http",
    "content": "GET http://127.0.0.1:8090/api/event/get_tag_suggestions_stats\naccept: application/json\n\n\n\n"
  },
  {
    "path": "api-tests/event/ignore_stock_news.http",
    "content": "POST http://127.0.0.1:8090/api/event/ignore_stock_news\naccept: application/json\nContent-Type: application/json\n\n{\n  \"news_id\": \"stock_sz_000034_2024-07-17 16:08:17\"\n}\n\n\n\n"
  },
  {
    "path": "api-tests/event/query_stock_topic.http",
    "content": "POST http://127.0.0.1:8090/api/event/query_stock_topic\naccept: application/json\nContent-Type: application/json\n\n{\n  \"limit\": 20\n}\n\n\n\n"
  },
  {
    "path": "api-tests/event/update_stock_topic.http",
    "content": "POST http://127.0.0.1:8090/api/event/update_stock_topic\naccept: application/json\nContent-Type: application/json\n\n{\n  \"id\": \"admin_特斯拉FSD入华\",\n  \"desc\": \"Tesla Al在社交媒体平台“X”上发帖称，特斯拉计划明年第一季度在中国和欧洲推出被其称为“全自动驾驶”(Full Self Driving)的高级驾驶辅助系统，目前正在等待监管部门的批准。\",\n  \"created_timestamp\": \"2024-09-05 09:00:00\",\n  \"trigger_date\": \"2024-09-05\",\n  \"due_date\": \"2025-01-01\",\n  \"main_tag\": \"车路云\",\n  \"sub_tag_list\": [\n    \"自动驾驶\",\n    \"车路云\"\n  ]\n}\n\n\n"
  },
  {
    "path": "api-tests/factor/get_factors.http",
    "content": "GET http://127.0.0.1:8090/api/factor/get_factors\naccept: application/json\n\n\n\n"
  },
  {
    "path": "api-tests/factor/query_factor_result.http",
    "content": "POST http://127.0.0.1:8090/api/factor/query_factor_result\naccept: application/json\nContent-Type: application/json\n\n\n{\n  \"entity_ids\": [\n    \"stock_sz_300133\"\n  ],\n  \"factor_name\": \"LiveOrDeadFactor\"\n}\n"
  },
  {
    "path": "api-tests/get_main_tags_in_stock_pool.http",
    "content": "GET http://127.0.0.1:8090/api/work/get_main_tags_in_stock_pool?stock_pool_name=大局\naccept: application/json\n\n\n\n"
  },
  {
    "path": "api-tests/get_stock_pool_info.http",
    "content": "GET http://127.0.0.1:8090/api/work/get_stock_pool_info\naccept: application/json\n\n\n\n"
  },
  {
    "path": "api-tests/get_stock_pools.http",
    "content": "GET http://127.0.0.1:8090/api/work/get_stock_pools?stock_pool_name=main_line\naccept: application/json\n\n\n\n"
  },
  {
    "path": "api-tests/tag/batch_set_stock_tags.http",
    "content": "POST http://127.0.0.1:8090/api/work/batch_set_stock_tags\naccept: application/json\nContent-Type: application/json\n\n\n{\n  \"tag\": \"脑机接口\",\n  \"tag_type\": \"sub_tag\",\n  \"entity_ids\": [\n      \"stock_sh_600775\",\n      \"stock_sz_002173\",\n      \"stock_sz_301293\",\n      \"stock_sz_300753\",\n      \"stock_sz_300430\",\n      \"stock_sz_002243\"\n    ],\n  \"tag_reason\": \"脑机接口消息刺激\"\n}\n"
  },
  {
    "path": "api-tests/tag/build_main_tag_industry_relation.http",
    "content": "POST http://127.0.0.1:8090/api/work/build_main_tag_industry_relation\naccept: application/json\nContent-Type: application/json\n\n{\n  \"main_tag\": \"商业航天\",\n  \"industry_list\": [\n    \"航天航空\"\n  ]\n}"
  },
  {
    "path": "api-tests/tag/build_main_tag_sub_tag_relation.http",
    "content": "POST http://127.0.0.1:8090/api/work/build_main_tag_sub_tag_relation\naccept: application/json\nContent-Type: application/json\n\n{\n  \"main_tag\": \"商业航天\",\n  \"sub_tag_list\": [\n    \"航天概念\",\n    \"天基互联\",\n    \"北斗导航\",\n    \"通用航空\"\n  ],\n  \"activate\": true\n}"
  },
  {
    "path": "api-tests/tag/build_stock_tags.http",
    "content": "POST http://127.0.0.1:8090/api/work/build_stock_tags\naccept: application/json\nContent-Type: application/json\n\n[\n  {\n    \"entity_id\": \"stock_sz_002085\",\n    \"name\": \"万丰奥威\",\n    \"main_tag\": \"低空经济\",\n    \"main_tag_reason\": \"2023年12月27日回复称,公司钻石eDA40纯电动飞机已成功首飞;eVTOL项目已联动海外钻石技术开发团队,在绿色、智能、垂直起降等方面的设计体现未来领域应用场景。\",\n    \"sub_tag\": \"低空经济\",\n    \"sub_tag_reason\": \"2023年12月27日回复称,公司钻石eDA40纯电动飞机已成功首飞;eVTOL项目已联动海外钻石技术开发团队,在绿色、智能、垂直起降等方面的设计体现未来领域应用场景。\",\n    \"active_hidden_tags\": null\n  }\n]"
  },
  {
    "path": "api-tests/tag/change_main_tag.http",
    "content": "POST http://127.0.0.1:8090/api/work/change_main_tag\naccept: application/json\nContent-Type: application/json\n\n{\n  \"current_main_tag\": \"医疗器械\",\n  \"new_main_tag\": \"医药\"\n}"
  },
  {
    "path": "api-tests/tag/create_hidden_tag_info.http",
    "content": "POST http://127.0.0.1:8090/api/work/create_hidden_tag_info\naccept: application/json\nContent-Type: application/json\n\n{\n  \"tag\": \"中字头\",\n  \"tag_reason\": \"央企，国资委控股\"\n}"
  },
  {
    "path": "api-tests/tag/create_main_tag_info.http",
    "content": "POST http://127.0.0.1:8090/api/work/create_main_tag_info\naccept: application/json\nContent-Type: application/json\n\n{\n  \"tag\": \"稳定币\",\n  \"tag_reason\": \"数字货币的一种，通常与法定货币挂钩，旨在减少价格波动。\"\n}"
  },
  {
    "path": "api-tests/tag/create_sub_tag_info.http",
    "content": "POST http://127.0.0.1:8090/api/work/create_sub_tag_info\naccept: application/json\nContent-Type: application/json\n\n{\n  \"tag\": \"低空经济\",\n  \"tag_reason\": \"低空经济是飞行器和各种产业形态的融合，例如\\\"无人机+配送\\\"、\\\"直升机或evto载人飞行\\\"、\\\"无人机+应急救援\\\"、\\\"无人机+工业场景巡检\\\"等\"\n}"
  },
  {
    "path": "api-tests/tag/del_hidden_tag.http",
    "content": "DELETE http://127.0.0.1:8090/api/work/delete_hidden_tag?tag=妖\naccept: application/json\n\n\n"
  },
  {
    "path": "api-tests/tag/del_main_tag.http",
    "content": "DELETE http://127.0.0.1:8090/api/work/delete_main_tag?tag=国企\naccept: application/json\n\n\n"
  },
  {
    "path": "api-tests/tag/del_sub_tag.http",
    "content": "DELETE http://127.0.0.1:8090/api/work/delete_sub_tag?tag=天然气\naccept: application/json\n\n\n"
  },
  {
    "path": "api-tests/tag/get_hidden_tag_info.http",
    "content": "GET http://127.0.0.1:8090/api/work/get_hidden_tag_info\naccept: application/json\n\n\n"
  },
  {
    "path": "api-tests/tag/get_industry_info.http",
    "content": "GET http://127.0.0.1:8090/api/work/get_industry_info\naccept: application/json\n\n\n"
  },
  {
    "path": "api-tests/tag/get_main_tag_industry_relation.http",
    "content": "GET http://127.0.0.1:8090/api/work/get_main_tag_industry_relation?main_tag=AI\naccept: application/json\n\n\n"
  },
  {
    "path": "api-tests/tag/get_main_tag_info.http",
    "content": "GET http://127.0.0.1:8090/api/work/get_main_tag_info\naccept: application/json\n\n\n"
  },
  {
    "path": "api-tests/tag/get_main_tag_sub_tag_relation.http",
    "content": "GET http://127.0.0.1:8090/api/work/get_main_tag_sub_tag_relation?main_tag=军工\naccept: application/json\n\n\n"
  },
  {
    "path": "api-tests/tag/get_stock_tag_options.http",
    "content": "GET http://127.0.0.1:8090/api/work/get_stock_tag_options?entity_id=stock_sh_600733\naccept: application/json\n\n\n"
  },
  {
    "path": "api-tests/tag/get_sub_tag_info.http",
    "content": "GET http://127.0.0.1:8090/api/work/get_sub_tag_info\naccept: application/json\n\n\n"
  },
  {
    "path": "api-tests/tag/query_simple_stock_tags.http",
    "content": "POST http://127.0.0.1:8090/api/work/query_simple_stock_tags\naccept: application/json\nContent-Type: application/json\n\n\n{\n  \"entity_ids\": [\n    \"stock_sz_002085\",\n    \"stock_sz_000099\",\n    \"stock_sz_002130\"\n  ]\n}"
  },
  {
    "path": "api-tests/tag/query_stock_tag_stats.http",
    "content": "POST http://127.0.0.1:8090/api/work/query_stock_tag_stats\naccept: application/json\nContent-Type: application/json\n\n{\n  \"stock_pool_name\": \"main_line\"\n}"
  },
  {
    "path": "api-tests/tag/query_stock_tags.http",
    "content": "POST http://127.0.0.1:8090/api/work/query_stock_tags\naccept: application/json\nContent-Type: application/json\n\n\n{\n  \"entity_ids\": [\n    \"stock_sz_000099\",\n    \"stock_sz_002085\",\n    \"stock_sz_001696\"\n  ]\n}\n"
  },
  {
    "path": "api-tests/tag/set_stock_tags.http",
    "content": "POST http://127.0.0.1:8090/api/work/set_stock_tags\naccept: application/json\nContent-Type: application/json\n\n{\n    \"entity_id\": \"stock_sz_001366\",\n    \"name\": \"播恩集团\",\n    \"main_tag\": \"医药\",\n    \"main_tag_reason\": \"合成生物概念\",\n    \"main_tags\": {\n      \"农业\": \"来自行业:农牧饲渔\"\n    },\n    \"sub_tag\": \"生物医药\",\n    \"sub_tag_reason\": \"合成生物概念\",\n    \"sub_tags\": null,\n    \"active_hidden_tags\": null\n  }"
  },
  {
    "path": "api-tests/test.http",
    "content": "\n"
  },
  {
    "path": "api-tests/trading/build_query_stock_quote_setting.http",
    "content": "POST http://127.0.0.1:8090/api/trading/build_query_stock_quote_setting\naccept: application/json\nContent-Type: application/json\n\n{\n  \"stock_pool_name\": \"main_line\",\n  \"main_tags\": [\"低空经济\",\"新能源\"]\n}"
  },
  {
    "path": "api-tests/trading/build_trading_plan.http",
    "content": "POST http://127.0.0.1:8090/api/trading/build_trading_plan\naccept: application/json\nContent-Type: application/json\n\n{\n  \"stock_id\": \"stock_sz_300133\",\n  \"trading_date\": \"2024-04-23\",\n  \"expected_open_pct\": 0.02,\n  \"buy_price\": 6.9,\n  \"sell_price\": null,\n  \"trading_reason\": \"主线\",\n  \"trading_signal_type\": \"open_long\"\n}\n"
  },
  {
    "path": "api-tests/trading/get_current_trading_plan.http",
    "content": "GET http://127.0.0.1:8090/api/trading/get_current_trading_plan\naccept: application/json\n\n\n\n"
  },
  {
    "path": "api-tests/trading/get_future_trading_plan.http",
    "content": "GET http://127.0.0.1:8090/api/trading/get_future_trading_plan\naccept: application/json\n\n\n\n"
  },
  {
    "path": "api-tests/trading/get_query_stock_quote_setting.http",
    "content": "GET http://127.0.0.1:8090/api/trading/get_query_stock_quote_setting\naccept: application/json\n\n\n\n"
  },
  {
    "path": "api-tests/trading/get_quote_stats.http",
    "content": "GET http://127.0.0.1:8090/api/trading/get_quote_stats\naccept: application/json\n"
  },
  {
    "path": "api-tests/trading/query_kdata.http",
    "content": "POST http://127.0.0.1:8090/api/trading/query_kdata\naccept: application/json\nContent-Type: application/json\n\n\n{\n  \"data_provider\": \"em\",\n  \"entity_ids\": [\n    \"stock_sz_002085\",\n    \"stock_sz_300133\"\n  ],\n  \"adjust_type\": \"hfq\"\n}\n"
  },
  {
    "path": "api-tests/trading/query_stock_quotes.http",
    "content": "POST http://127.0.0.1:8090/api/trading/query_stock_quotes\naccept: application/json\nContent-Type: application/json\n\n{\n  \"main_tag\": \"低空经济\",\n  \"stock_pool_name\": \"vol_up\",\n  \"limit\": 500,\n  \"order_by_field\": \"change_pct\"\n}\n"
  },
  {
    "path": "api-tests/trading/query_tag_quotes.http",
    "content": "POST http://127.0.0.1:8090/api/trading/query_tag_quotes\naccept: application/json\nContent-Type: application/json\n\n{\n  \"main_tags\": [\n    \"低空经济\",\n    \"半导体\",\n    \"化工\",\n    \"消费电子\"\n  ],\n  \"stock_pool_name\": \"main_line\"\n}\n"
  },
  {
    "path": "api-tests/trading/query_trading_plan.http",
    "content": "POST http://127.0.0.1:8090/api/trading/query_trading_plan\naccept: application/json\nContent-Type: application/json\n\n{\n  \"time_range\": {\n    \"relative_time_range\": {\n      \"interval\": -30,\n      \"time_unit\": \"day\"\n    }\n  }\n}\n"
  },
  {
    "path": "api-tests/trading/query_ts.http",
    "content": "POST http://127.0.0.1:8090/api/trading/query_ts\naccept: application/json\nContent-Type: application/json\n\n\n{\n  \"entity_ids\": [\n    \"stock_sz_002085\",\n    \"stock_sz_300133\"\n  ]\n}\n"
  },
  {
    "path": "build.sh",
    "content": "python3 setup.py sdist bdist_wheel"
  },
  {
    "path": "code_of_conduct.md",
    "content": "##  1. 命名\n* snake_case适用于\n  * variable(变量)\n  * package(包)\n  * module(模块)\n  * function(函数)\n  * method(方法)\n\n* CamelCase适用于\n  * class(类)\n\n## 2. 对外接口\n各module对外暴露的接口应显式声明于__all__,例子:\n\n[contract模块](https://github.com/zvtvz/zvt/blob/master/zvt/contract/__init__.py)以此结尾:\n```\n__all__ = ['IntervalLevel', 'Mixin', 'NormalMixin', 'EntityMixin', 'NormalEntityMixin', 'zvt_context']\n```\n\n## 3. 引入接口\n不要使用 from zvt.module import *,你要什么就import什么，不要污染。\n\n## 4. 没了\n\nNo api is the best api.\n\nCode as comment."
  },
  {
    "path": "docs/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the environment for the first two.\nSPHINXOPTS    ?=\nSPHINXBUILD   ?= sphinx-build\nSOURCEDIR     = source\nBUILDDIR      = build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sphinx-build\r\n)\r\nset SOURCEDIR=source\r\nset BUILDDIR=build\r\n\r\nif \"%1\" == \"\" goto help\r\n\r\n%SPHINXBUILD% >NUL 2>NUL\r\nif errorlevel 9009 (\r\n\techo.\r\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\r\n\techo.installed, then set the SPHINXBUILD environment variable to point\r\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\r\n\techo.may add the Sphinx directory to PATH.\r\n\techo.\r\n\techo.If you don't have Sphinx installed, grab it from\r\n\techo.https://www.sphinx-doc.org/\r\n\texit /b 1\r\n)\r\n\r\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\r\ngoto end\r\n\r\n:help\r\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\r\n\r\n:end\r\npopd\r\n"
  },
  {
    "path": "docs/source/_templates/custom-class-template.rst",
    "content": "{{ objname | escape | underline}}\n\n.. currentmodule:: {{ module }}\n\n.. autoclass:: {{ objname }}\n   :members:\n   :show-inheritance:\n   :inherited-members:\n\n   {% block methods %}\n   .. automethod:: __init__\n\n\n   {% endblock %}\n"
  },
  {
    "path": "docs/source/_templates/custom-module-template.rst",
    "content": "{{ fullname | escape | underline}}\n\n.. automodule:: {{ fullname }}\n\n   {% block attributes %}\n   {% if attributes %}\n   .. rubric:: {{ _('Module Attributes') }}\n\n   .. autosummary::\n      :toctree:\n   {% for item in attributes %}\n      {{ item }}\n   {%- endfor %}\n   {% endif %}\n   {% endblock %}\n\n   {% block functions %}\n   {% if functions %}\n   .. rubric:: {{ _('Functions') }}\n\n   .. autosummary::\n      :toctree:\n   {% for item in functions %}\n      {{ item }}\n   {%- endfor %}\n   {% endif %}\n   {% endblock %}\n\n   {% block classes %}\n   {% if classes %}\n   .. rubric:: {{ _('Classes') }}\n\n   .. autosummary::\n      :toctree:\n      :template: custom-class-template.rst\n   {% for item in classes %}\n      {{ item }}\n   {%- endfor %}\n   {% endif %}\n   {% endblock %}\n\n   {% block exceptions %}\n   {% if exceptions %}\n   .. rubric:: {{ _('Exceptions') }}\n\n   .. autosummary::\n      :toctree:\n   {% for item in exceptions %}\n      {{ item }}\n   {%- endfor %}\n   {% endif %}\n   {% endblock %}\n\n{% block modules %}\n{% if modules %}\n.. rubric:: Modules\n\n.. autosummary::\n   :toctree:\n   :template: custom-module-template.rst\n   :recursive:\n{% for item in modules %}\n   {{ item }}\n{%- endfor %}\n{% endif %}\n{% endblock %}\n"
  },
  {
    "path": "docs/source/_templates/sidebarlogo.html",
    "content": "<p>\n  <iframe src=\"https://ghbtns.com/github-btn.html?user=zvtvz&repo=zvt&type=watch&count=true&size=large\"\n    allowtransparency=\"true\" frameborder=\"0\" scrolling=\"0\" width=\"200px\" height=\"35px\"></iframe>\n</p>\n"
  },
  {
    "path": "docs/source/api/index.rst",
    "content": "API\n===\n\n.. autosummary::\n   :toctree: _autosummary\n   :template: custom-module-template.rst\n   :recursive:\n\n    zvt\n    zvt.api\n    zvt.autocode\n    zvt.consts\n    zvt.common\n    zvt.contract\n    zvt.domain\n    zvt.factors\n    zvt.fill_project\n    zvt.informer\n    zvt.misc\n    zvt.ml\n    zvt.plugin\n    zvt.recorders\n    zvt.rest\n    zvt.samples\n    zvt.tag\n    zvt.trader\n    zvt.trading\n    zvt.ui\n    zvt.utils"
  },
  {
    "path": "docs/source/conf.py",
    "content": "# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common options. For a full\n# list see the documentation:\n# https://www.sphinx-doc.org/en/master/usage/configuration.html\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nimport os\nimport sys\n\nsys.path.insert(0, os.path.abspath(\"../../src\"))\n\n\n# -- Project information -----------------------------------------------------\n\nproject = \"zvt\"\ncopyright = \"2026, foolcage\"\nauthor = \"foolcage\"\n\n# The full version, including alpha/beta/rc tags\nrelease = \"0.13.5\"\n\n\n# -- General configuration ---------------------------------------------------\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    \"sphinx.ext.autodoc\",\n    \"sphinx.ext.autosummary\",\n    \"IPython.sphinxext.ipython_directive\",\n    \"IPython.sphinxext.ipython_console_highlighting\",\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = [\"_templates\"]\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path.\nexclude_patterns = []\n\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = \"alabaster\"\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = [\"_static\"]\n\nhtml_sidebars = {\n    \"**\": [\"sidebarlogo.html\", \"globaltoc.html\", \"sourcelink.html\", \"searchbox.html\", \"localtoc.html\", \"relations.html\"]\n}\n\nautosummary_generate = True\n\nautodoc_default_options = {\n    \"member-order\": \"bysource\",\n}\n"
  },
  {
    "path": "docs/source/contributor.rst",
    "content": "====================\nContributor\n====================\n\nThanks, my friends.\n\n* 隋鹏飞\n* `Tiger <https://github.com/Tigerno1>`_\n* 蓝小球\n* 青玄\n* \\*崇\n* \\*阳\n* 张凯\n* Rubicon_陈小虾\n* `看门大爷 <https://github.com/Mr-later>`_\n* 芝华\n* 雨多田光\n* 叶金荣\n* 冷冷\n* Landy\n* Sunny\n* Ccc国毅\n* Jim Tong\n* tom\n* 小七\n* 小强\n* Cascara Latte- 刘可烁\n"
  },
  {
    "path": "docs/source/core_concepts.rst",
    "content": "====================\nCore concepts\n====================\n\nEntity\n    the existence described by itself, classification of existential concepts.\n\nEntityEvent\n    the event happened on the entity.\n\nTradableEntity\n    the Entity could be traded, e.g, Stock, Future.\n\nActorEntity\n    the Entity acting in market, e.g, Fund, Individual, Government.\n\nIntervalLevel\n    repeated fixed time interval, e.g, 5m, 1d.\n\nSchema\n    the data structure with fields, one schema could have multiple storage with different Providers.\n\nKdata(Quote)\n    the candlestick data with OHLC.\n\nProvider\n    the data provider.\n\nStorage\n    the sql database, e.g, sqlite, mysql.\n\nRecorder\n    class for recording data for Schema.\n\nFactor\n    data describing market.It's computed from from Schema, and save as new Schema if need.\n\nTargetSelector\n    the class select targets according to Factor.\n\nTrader\n    the backtest engine using TargetSelector, MLMachine or free style.\n\nTagger\n    classify TradableEntity by different dimensions, could be used as ml category feature.\n\nMLMachine\n    the ml engine.\n\nTradingSignal\n    the signal contains information about how to trade.\n\nDrawer\n    the class for draw charts.\n\nIntent\n    what do you want to do, e.g. compare, distribute, composite.\n\nNormal data\n    the pandas dataframe with multiple index which level 0 is entity_id and level 1 is timestamp:\n\n===============                 ==========        =====   =====   =====   =====\nentity_id                       timestamp         col1    col2    col3    col4\n===============                 ==========        =====   =====   =====   =====\nstock_sz_000338                 2020-05-05        1.2     0.5     0.3     a\n...                             2020-05-06        1.0     0.7     0.2     b\nstock_sz_000778                 2020-05-05        1.2     0.5     0.3     a\n...                             2020-05-06        1.0     0.7     0.2     b\n===============                 ==========        =====   =====   =====   =====\n"
  },
  {
    "path": "docs/source/data/adding_new_entity.rst",
    "content": ".. _adding_new_entity:\n\n=================\nAdding new entity\n=================\n\nIt's human nature to like the new and hate the old. Adding new TradableEntity is easy in zvt.\n\nAdding new entity is nothing than a specific case of :ref:`Adding data <extending_data.add_data>`.\nLet's show the key steps below which add :class:`~.zvt.domain.meta.future_meta.Future`.\n\nDefine entity Schema\n--------------------------\n\n::\n\n    # -*- coding: utf-8 -*-\n    from sqlalchemy.ext.declarative import declarative_base\n\n    from zvt.contract.register import register_schema, register_entity\n    from zvt.contract.schema import TradableEntity\n\n    FutureMetaBase = declarative_base()\n\n\n    @register_entity(entity_type=\"future\")\n    class Future(FutureMetaBase, TradableEntity):\n        __tablename__ = \"future\"\n\n\n    register_schema(providers=[\"em\"], db_name=\"future_meta\", schema_base=FutureMetaBase)\n\nImplement recorder for the entity\n---------------------------------\n\n::\n\n    from zvt.contract.api import df_to_db\n    from zvt.contract.recorder import Recorder\n    from zvt.domain import Future\n    from zvt.recorders.em import em_api\n\n\n    class EMFutureRecorder(Recorder):\n        provider = \"em\"\n        data_schema = Future\n\n        def run(self):\n            df = em_api.get_tradable_list(entity_type=\"future\")\n            self.logger.info(df)\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nDefine OHLC schema(kdata) for the entity\n----------------------------------------\n\nzvt provide a standard way to generate OHLC schema for the tradable entity.\nAll `OHLC schemas <https://github.com/zvtvz/zvt/blob/master/src/zvt/domain/quotes>`_ is generated by\n`fill project script <https://github.com/zvtvz/zvt/blob/master/src/zvt/fill_project.py>`_.\n\ne.g generate Future OHLC schema.\n\n::\n\n    gen_kdata_schema(\n        pkg=\"zvt\",\n        providers=[\"em\"],\n        entity_type=\"future\",\n        levels=[IntervalLevel.LEVEL_1DAY],\n        entity_in_submodule=True,\n    )\n\nThe OHLC schema definition principle is: **one level one file**\n\nSo we would define a common OHLC schema for each entity type in `quotes module <https://github.com/zvtvz/zvt/blob/master/src/zvt/domain/quotes/__init__.py>`_.\n\ne.g. Future common OHLC schema\n\n::\n\n    class FutureKdataCommon(KdataCommon):\n        #: 持仓量\n        interest = Column(Float)\n        #: 结算价\n        settlement = Column(Float)\n        #: 涨跌幅(按收盘价)\n        # change_pct = Column(Float)\n        #: 涨跌幅(按结算价)\n        change_pct1 = Column(Float)\n\nAnd we could relate the common kdata schema with the recorder and route level to specific schema automatically.\n\nImplement recorder for OHLC schema(kdata)\n-----------------------------------------\n\nCheck `em quotes recorder <https://github.com/zvtvz/zvt/blob/master/src/zvt/recorders/em/quotes/em_kdata_recorder.py>`_ for\nthe details.\n\n::\n\n    class EMFutureKdataRecorder(BaseEMStockKdataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Future\n\n    data_schema = FutureKdataCommon\n\n\nUse them in zvt way\n-------------------\n\nFetch the entity list:\n\n::\n\n    >>> from zvt.domain import Future\n    >>> Future.record_data()\n    >>> df = Future.query_data()\n    >>> print(df)\n\n                     id        entity_id  timestamp entity_type exchange code    name  list_date end_date\n    0   future_cffex_IC  future_cffex_IC        NaT      future    cffex   IC  中证当月连续        NaT     None\n    1   future_cffex_IF  future_cffex_IF        NaT      future    cffex   IF  沪深当月连续        NaT     None\n    2   future_cffex_IH  future_cffex_IH        NaT      future    cffex   IH  上证当月连续        NaT     None\n    3    future_cffex_T   future_cffex_T        NaT      future    cffex    T  十债当季连续        NaT     None\n    4   future_cffex_TF  future_cffex_TF        NaT      future    cffex   TF  五债当季连续        NaT     None\n    ..              ...              ...        ...         ...      ...  ...     ...        ...      ...\n    65    future_ine_LU    future_ine_LU 2020-06-22      future      ine   LU  低硫燃油主力 2020-06-22     None\n    66   future_czce_PF   future_czce_PF 2020-10-12      future     czce   PF    短纤主力 2020-10-12     None\n    67    future_ine_BC    future_ine_BC 2020-11-19      future      ine   BC   国际铜主力 2020-11-19     None\n    68    future_dce_LH    future_dce_LH 2021-01-08      future      dce   LH    生猪主力 2021-01-08     None\n    69   future_czce_PK   future_czce_PK 2021-02-01      future     czce   PK    花生主力 2021-02-01     None\n\n    [70 rows x 9 columns]\n\nFetch the quotes:\n\n::\n\n    >>> from zvt.domain import Future1dKdata\n    >>> Future1dKdata.record_data(code=\"CU\")\n    >>> df = Future1dKdata.query_data(code=\"CU\")\n    >>> print(df)\n\n                                 id       entity_id  timestamp provider code  name level     open    close     high      low    volume      turnover  change_pct  turnover_rate interest settlement change_pct1\n    0     future_shfe_CU_1996-04-03  future_shfe_CU 1996-04-03       em   CU  沪铜主力    1d  22930.0  22840.0  23000.0  22840.0     353.0  0.000000e+00      0.0000            0.0     None       None        None\n    1     future_shfe_CU_1996-04-04  future_shfe_CU 1996-04-04       em   CU  沪铜主力    1d  22700.0  22750.0  22820.0  22650.0     251.0  0.000000e+00     -0.0039            0.0     None       None        None\n    2     future_shfe_CU_1996-04-05  future_shfe_CU 1996-04-05       em   CU  沪铜主力    1d  22520.0  22780.0  22820.0  22500.0     298.0  0.000000e+00      0.0013            0.0     None       None        None\n    3     future_shfe_CU_1996-04-08  future_shfe_CU 1996-04-08       em   CU  沪铜主力    1d  22660.0  22650.0  22680.0  22600.0      98.0  0.000000e+00     -0.0057            0.0     None       None        None\n    4     future_shfe_CU_1996-04-09  future_shfe_CU 1996-04-09       em   CU  沪铜主力    1d  22830.0  22810.0  22860.0  22810.0      56.0  0.000000e+00      0.0071            0.0     None       None        None\n    ...                         ...             ...        ...      ...  ...   ...   ...      ...      ...      ...      ...       ...           ...         ...            ...      ...        ...         ...\n    6343  future_shfe_CU_2022-04-21  future_shfe_CU 2022-04-21       em   CU  沪铜主力    1d  74140.0  74480.0  74750.0  74140.0   48008.0  1.787678e+10     -0.0004            0.0     None       None        None\n    6344  future_shfe_CU_2022-04-22  future_shfe_CU 2022-04-22       em   CU  沪铜主力    1d  74800.0  75010.0  75200.0  74690.0   58874.0  2.205633e+10      0.0073            0.0     None       None        None\n    6345  future_shfe_CU_2022-04-25  future_shfe_CU 2022-04-25       em   CU  沪铜主力    1d  74900.0  73660.0  75190.0  73660.0  107455.0  3.989090e+10     -0.0168            0.0     None       None        None\n    6346  future_shfe_CU_2022-04-26  future_shfe_CU 2022-04-26       em   CU  沪铜主力    1d  73170.0  73260.0  73750.0  72500.0  113019.0  4.130931e+10     -0.0132            0.0     None       None        None\n    6347  future_shfe_CU_2022-04-27  future_shfe_CU 2022-04-27       em   CU  沪铜主力    1d  72990.0  73100.0  73560.0  72910.0   61563.0  2.254089e+10      0.0000            0.0     None       None        None\n\n    [6348 rows x 18 columns]\n"
  },
  {
    "path": "docs/source/data/data_concepts.rst",
    "content": "=============\nData concepts\n=============\n\n\n.. _data.tradable_entity:\n\nTradableEntity\n------------------------------\n:class:`~.zvt.contract.schema.TradableEntity` is anything could be traded, it could be :class:`~.zvt.domain.meta.stock_meta.Stock`,\n:class:`~.zvt.domain.meta.etf_meta.Etf`, :class:`~.zvt.domain.meta.index_meta.Index`, future, cryptocurrency, or even a **sports match**.\n\nLet's start with the real world tradable entities: China stock and USA stock ——— the world's most involved trading targets.\n\nrecord and query stock:\n::\n\n    >>> from zvt.domain import Stock\n    >>> Stock.record_data()\n    >>> df = Stock.query_data()\n    >>> print(df)\n                       id        entity_id  timestamp entity_type exchange    code   name  list_date end_date\n    0     stock_sh_600651  stock_sh_600651 1986-09-26       stock       sh  600651  *ST飞乐 1986-09-26     None\n    1     stock_sz_000004  stock_sz_000004 1990-12-01       stock       sz  000004   国华网安 1990-12-01     None\n    2     stock_sz_000005  stock_sz_000005 1990-12-10       stock       sz  000005   世纪星源 1990-12-10     None\n    3     stock_sh_600601  stock_sh_600601 1990-12-19       stock       sh  600601   方正科技 1990-12-19     None\n    4     stock_sh_600602  stock_sh_600602 1990-12-19       stock       sh  600602   云赛智联 1990-12-19     None\n    ...               ...              ...        ...         ...      ...     ...    ...        ...      ...\n    4615  stock_sh_603176  stock_sh_603176 2021-12-31       stock       sh  603176   汇通集团 2021-12-31     None\n    4616  stock_sh_600941  stock_sh_600941 2022-01-05       stock       sh  600941   中国移动 2022-01-05     None\n    4617  stock_sh_688262  stock_sh_688262 2022-01-06       stock       sh  688262   国芯科技 2022-01-06     None\n    4618  stock_sh_688176  stock_sh_688176 2022-01-07       stock       sh  688176   亚虹医药 2022-01-07     None\n    4619  stock_sz_301159  stock_sz_301159 2022-01-07       stock       sz  301159    N三维 2022-01-07     None\n\n    [4620 rows x 9 columns]\n\nrecord and query stockus:\n::\n\n    >>> from zvt.domain import Stockus\n    >>> Stockus.record_data()\n    >>> df = Stockus.query_data()\n    >>> print(df)\n                           id            entity_id timestamp entity_type exchange  code                             name list_date end_date\n    0     stockus_nasdaq_CMII  stockus_nasdaq_CMII      None     stockus   nasdaq  CMII        CM Life Sciences II Inc-A      None     None\n    1     stockus_nasdaq_CBLI  stockus_nasdaq_CBLI      None     stockus   nasdaq  CBLI                      Cytocom Inc      None     None\n    2       stockus_nyse_XPOw    stockus_nyse_XPOw      None     stockus     nyse  XPOw             XPO Logistics Inc WI      None     None\n    3        stockus_nyse_WRI     stockus_nyse_WRI      None     stockus     nyse   WRI                        魏因加滕房地产投资      None     None\n    4       stockus_nyse_PROS    stockus_nyse_PROS      None     stockus     nyse  PROS              ProSight Global Inc      None     None\n    ...                   ...                  ...       ...         ...      ...   ...                              ...       ...      ...\n    6345    stockus_nyse_FATH    stockus_nyse_FATH      None     stockus     nyse  FATH  Fathom Digital Manufacturing Co      None     None\n    6346    stockus_nyse_LOCL    stockus_nyse_LOCL      None     stockus     nyse  LOCL                Local Bounti Corp      None     None\n    6347     stockus_nyse_VLD     stockus_nyse_VLD      None     stockus     nyse   VLD                       Velo3D Inc      None     None\n    6348     stockus_nyse_AHI     stockus_nyse_AHI      None     stockus     nyse   AHI   Advanced Human Imaging Ltd ADR      None     None\n    6349    stockus_nyse_HLGN    stockus_nyse_HLGN      None     stockus     nyse  HLGN                     Heliogen Inc      None     None\n\n| what about other tradable entities?\n| Show current registered tradable entity type and its schema:\n\n::\n\n    >>> from zvt.contract import zvt_context\n    >>> zvt_context.tradable_schema_map\n    {'stockus': zvt.domain.meta.stockus_meta.Stockus,\n     'stockhk': zvt.domain.meta.stockhk_meta.Stockhk,\n     'index': zvt.domain.meta.index_meta.Index,\n     'etf': zvt.domain.meta.etf_meta.Etf,\n     'stock': zvt.domain.meta.stock_meta.Stock,\n     'block': zvt.domain.meta.block_meta.Block,\n     'fund': zvt.domain.meta.fund_meta.Fund}\n\nThe key is **entity_type** and the value is its :ref:`Schema<data.schema>`.\n\nFrom intuition, stockhk should be stock of hongkong:\n::\n\n    >>> from zvt.domain import Stockhk\n    >>> Stockhk.record_data()\n    >>> df = Stockhk.query_data(index='code')\n    >>> print(df)\n\n                         id         entity_id timestamp entity_type exchange   code    name list_date end_date\n    code\n    00001  stockhk_hk_00001  stockhk_hk_00001       NaT     stockhk       hk  00001      长和      None     None\n    00002  stockhk_hk_00002  stockhk_hk_00002       NaT     stockhk       hk  00002    中电控股      None     None\n    00003  stockhk_hk_00003  stockhk_hk_00003       NaT     stockhk       hk  00003  香港中华煤气      None     None\n    00004  stockhk_hk_00004  stockhk_hk_00004       NaT     stockhk       hk  00004   九龙仓集团      None     None\n    00005  stockhk_hk_00005  stockhk_hk_00005       NaT     stockhk       hk  00005    汇丰控股      None     None\n    ...                 ...               ...       ...         ...      ...    ...     ...       ...      ...\n    09996  stockhk_hk_09996  stockhk_hk_09996       NaT     stockhk       hk  09996  沛嘉医疗-B      None     None\n    09997  stockhk_hk_09997  stockhk_hk_09997       NaT     stockhk       hk  09997    康基医疗      None     None\n    09998  stockhk_hk_09998  stockhk_hk_09998       NaT     stockhk       hk  09998    光荣控股      None     None\n    09999  stockhk_hk_09999  stockhk_hk_09999       NaT     stockhk       hk  09999    网易-S      None     None\n    80737  stockhk_hk_80737  stockhk_hk_80737       NaT     stockhk       hk  80737  湾区发展-R      None     None\n\n    [2597 rows x 9 columns]\n\n    >>> df[df.code=='00700']\n\n                        id         entity_id timestamp entity_type exchange   code  name list_date end_date\n    2112  stockhk_hk_00700  stockhk_hk_00700      None     stockhk       hk  00700  腾讯控股      None     None\n\nFrom intuition, other tradable entities could be added to the system and used in the same way.\nJust follow :ref:`Add tradable entity <extending_data.tradable_entity>`\n\n.. _data.actor_entity:\n\nActorEntity\n------------------------------\n:class:`~.zvt.contract.schema.ActorEntity` is the beings acting in the market, it could be government,\ncompany, fund or individual.\n\n::\n\n    >>> from zvt.domain import StockInstitutionalInvestorHolder\n    >>> entity_ids = [\"stock_sz_000338\", \"stock_sz_000001\"]\n    >>> StockInstitutionalInvestorHolder.record_data(entity_ids=entity_ids)\n    >>> df = StockInstitutionalInvestorHolder.query_data(entity_ids=entity_ids)\n    >>> print(df)\n                                                          id        entity_id  timestamp    code  name                 actor_id   actor_type actor_code         actor_name report_period report_date  holding_numbers  holding_ratio  holding_values\n    0       stock_sz_000001_1998-06-30_raised_fund_cn_184688  stock_sz_000001 1998-06-30  000001  平安银行    raised_fund_cn_184688  raised_fund     184688               基金开元     half_year  1998-06-30     1.896697e+06       0.001771    3.269906e+07\n    1       stock_sz_000001_1998-09-30_raised_fund_cn_184688  stock_sz_000001 1998-09-30  000001  平安银行    raised_fund_cn_184688  raised_fund     184688               基金开元       season3  1998-09-30     2.634093e+06       0.002460    4.151331e+07\n    2       stock_sz_000001_1998-12-31_raised_fund_cn_184688  stock_sz_000001 1998-12-31  000001  平安银行    raised_fund_cn_184688  raised_fund     184688               基金开元          year  1998-12-31     2.673900e+06       0.002497    3.992133e+07\n    3       stock_sz_000001_1999-03-31_raised_fund_cn_184688  stock_sz_000001 1999-03-31  000001  平安银行    raised_fund_cn_184688  raised_fund     184688               基金开元       season1  1999-03-31     2.378977e+06       0.002221    3.256820e+07\n    4       stock_sz_000001_1999-06-30_raised_fund_cn_500005  stock_sz_000001 1999-06-30  000001  平安银行    raised_fund_cn_500005  raised_fund     500005               基金汉盛     half_year  1999-06-30     4.989611e+06       0.004659    1.386613e+08\n    ...                                                  ...              ...        ...     ...   ...                      ...          ...        ...                ...           ...         ...              ...            ...             ...\n    22463      stock_sz_000338_2021-09-30_broker_cn_71067063  stock_sz_000338 2021-09-30  000338  潍柴动力       broker_cn_71067063       broker   71067063          东方红信和添安4号       season3  2021-09-30     5.000000e+04       0.000012    8.580000e+05\n    22464  stock_sz_000338_2021-09-30_corporation_cn_1003...  stock_sz_000338 2021-09-30  000338  潍柴动力  corporation_cn_10030838  corporation   10030838         潍柴控股集团有限公司       season3  2021-09-30     1.422551e+09       0.018071    2.441097e+10\n    22465  stock_sz_000338_2021-09-30_corporation_cn_1067...  stock_sz_000338 2021-09-30  000338  潍柴动力  corporation_cn_10671586  corporation   10671586         香港中央结算有限公司       season3  2021-09-30     4.992710e+08       0.117713    8.567490e+09\n    22466  stock_sz_000338_2021-09-30_corporation_cn_1019...  stock_sz_000338 2021-09-30  000338  潍柴动力  corporation_cn_10196008  corporation   10196008       中国证券金融股份有限公司       season3  2021-09-30     1.636089e+08       0.038574    2.807529e+09\n    22467  stock_sz_000338_2021-09-30_corporation_cn_1008...  stock_sz_000338 2021-09-30  000338  潍柴动力  corporation_cn_10086358  corporation   10086358  奥地利IVM技术咨询维也纳有限公司       season3  2021-09-30     1.139387e+08       0.026863    1.955188e+09\n\n    [22468 rows x 14 columns]\n\n.. note::\n\n    A good actor should know the good or bad actors in history, more importantly,\n    the mind behind them.\n\n.. _data.schema:\n\nSchema\n------------------------------\nData structure describing :class:`~.zvt.contract.schema.TradableEntity`, :class:`~.zvt.contract.schema.ActorEntity` or events happen on them.\nPhysically it's table with columns in sql database. One schema could have multiple storage\nwith different providers.\n\n.. _data.schema_usage:\n\nFrom specific to general, all zvt schema usage is in the same way.\n\n* from zvt.domain import {Schema}\n* {Schema}.record_data\n* {Schema}.query_data\n\nExplore :py:mod:`~.zvt.domain` for pre defined schemas. And check :ref:`record_data & query_data details <record_and_query>`\n\n::\n\n    >>> from zvt.domain import *\n    >>> entity_ids = [\"stock_sz_000338\", \"stock_sz_000001\"]\n    >>> Stock1dHfqKdata.record_data(entity_ids=entity_ids, provider=\"em\")\n    >>> df = Stock1dHfqKdata.query_data(entity_ids=entity_ids, provider=\"em\")\n    >>> print(df)\n\n                                   id        entity_id  timestamp provider    code  name level     open    close     high      low     volume      turnover  change_pct  turnover_rate\n    0      stock_sz_000001_1991-04-03  stock_sz_000001 1991-04-03       em  000001  平安银行    1d    49.00    49.00    49.00    49.00        1.0  5.000000e+03      0.2250         0.0000\n    1      stock_sz_000001_1991-04-04  stock_sz_000001 1991-04-04       em  000001  平安银行    1d    48.76    48.76    48.76    48.76        3.0  1.500000e+04     -0.0049         0.0000\n    2      stock_sz_000001_1991-04-05  stock_sz_000001 1991-04-05       em  000001  平安银行    1d    48.52    48.52    48.52    48.52        2.0  1.000000e+04     -0.0049         0.0000\n    3      stock_sz_000001_1991-04-06  stock_sz_000001 1991-04-06       em  000001  平安银行    1d    48.28    48.28    48.28    48.28        7.0  3.400000e+04     -0.0049         0.0000\n    4      stock_sz_000001_1991-04-08  stock_sz_000001 1991-04-08       em  000001  平安银行    1d    48.04    48.04    48.04    48.04        2.0  1.000000e+04     -0.0050         0.0000\n    ...                           ...              ...        ...      ...     ...   ...   ...      ...      ...      ...      ...        ...           ...         ...            ...\n    10859  stock_sz_000338_2022-01-10  stock_sz_000338 2022-01-10       em  000338  潍柴动力    1d   314.38   314.38   320.37   312.69   956271.0  1.735153e+09      0.0149         0.0190\n    10860  stock_sz_000001_2022-01-11  stock_sz_000001 2022-01-11       em  000001  平安银行    1d  2974.07  2998.45  3019.58  2954.57  1581999.0  2.752485e+09      0.0121         0.0082\n    10861  stock_sz_000338_2022-01-11  stock_sz_000338 2022-01-11       em  000338  潍柴动力    1d   312.69   307.01   314.23   306.70   812187.0  1.444640e+09     -0.0234         0.0161\n    10862  stock_sz_000001_2022-01-12  stock_sz_000001 2022-01-12       em  000001  平安银行    1d  2998.45  2931.82  3004.95  2915.56  1502164.0  2.561266e+09     -0.0222         0.0077\n    10863  stock_sz_000338_2022-01-12  stock_sz_000338 2022-01-12       em  000338  潍柴动力    1d   307.01   305.78   309.62   302.86   882165.0  1.542044e+09     -0.0040         0.0175\n\n    [10864 rows x 15 columns]\n\nThe data of the schema is recorded in local database by default and could be updated incrementally.\n\nFind them in this way:\n\n::\n\n    {Schema}.get_storages()\n\ne.g.\n\n::\n\n    >>> Stock1dHfqKdata.get_storages()\n    [Engine(sqlite:////Users/foolcage/zvt-home/data/joinquant_stock_1d_hfq_kdata.db?check_same_thread=False),\n     Engine(sqlite:////Users/foolcage/zvt-home/data/em_stock_1d_hfq_kdata.db?check_same_thread=False)]\n\nIntervalLevel\n------------------------------\n:class:`~.zvt.contract.IntervalLevel` is repeated fixed time interval, e.g, 5m, 1d.\nIt's used in OHLC data for describing time window.\n\n::\n\n    >>> from zvt.contract import *\n    >>> for level in IntervalLevel:\n    >>>     print(level.value)\n    tick\n    1m\n    5m\n    15m\n    30m\n    1h\n    4h\n    1d\n    1wk\n    1mon\n\nKdata(Quote, OHLC)\n------------------------------\nthe candlestick data with OHLC.\n\nthe :class:`~.zvt.contract.schema.TradableEntity` quote schema name follows below rules:\n\n::\n\n    {entity_shema}{level}{adjust_type}Kdata\n\n* entity_schema\n\nTradableEntity class，e.g. Stock,Stockus.\n\n* level\n\nIntervalLevel value, e.g. 1d,1wk.\n\n* adjust type\n\npre adjusted(qfq), post adjusted(hfq), or not adjusted(bfq).\n\n::\n\n    >>> for adjust_type in AdjustType:\n    >>>     print(adjust_type.value)\n\n.. note::\n\n    In order to be compatible with historical data, the qfq is an exception, {adjust_type} is left empty\n\nThe pre defined kdata schema could be found in :py:mod:`~.zvt.domain.quotes`, it's separated by\nentity_schema, level, and adjust type.\n\ne.g. Stock1dHfqKdata means China Stock daily hfq quotes.\n\n::\n\n    >>> from zvt.domain import Stock1dHfqKdata\n    >>> Stock1dHfqKdata.record_data(code='000338', provider='em')\n    >>> df = Stock1dHfqKdata.query_data(code='000338', provider='em')\n    >>> print(df)\n\n                                  id        entity_id  timestamp provider    code  name level    open   close    high     low     volume      turnover  change_pct  turnover_rate\n    0     stock_sz_000338_2007-04-30  stock_sz_000338 2007-04-30     None  000338  潍柴动力    1d   70.00   64.93   71.00   62.88   207375.0  1.365189e+09      2.1720         0.1182\n    1     stock_sz_000338_2007-05-08  stock_sz_000338 2007-05-08     None  000338  潍柴动力    1d   66.60   64.00   68.00   62.88    86299.0  5.563198e+08     -0.0143         0.0492\n    2     stock_sz_000338_2007-05-09  stock_sz_000338 2007-05-09     None  000338  潍柴动力    1d   63.32   62.00   63.88   59.60    93823.0  5.782065e+08     -0.0313         0.0535\n    3     stock_sz_000338_2007-05-10  stock_sz_000338 2007-05-10     None  000338  潍柴动力    1d   61.50   62.49   64.48   61.01    47720.0  2.999226e+08      0.0079         0.0272\n    4     stock_sz_000338_2007-05-11  stock_sz_000338 2007-05-11     None  000338  潍柴动力    1d   61.90   60.65   61.90   59.70    39273.0  2.373126e+08     -0.0294         0.0224\n    ...                          ...              ...        ...      ...     ...   ...   ...     ...     ...     ...     ...        ...           ...         ...            ...\n    3426  stock_sz_000338_2021-08-27  stock_sz_000338 2021-08-27     None  000338  潍柴动力    1d  331.97  345.95  345.95  329.82  1688497.0  3.370241e+09      0.0540         0.0398\n    3427  stock_sz_000338_2021-08-30  stock_sz_000338 2021-08-30     None  000338  潍柴动力    1d  345.95  342.72  346.10  337.96  1187601.0  2.377957e+09     -0.0093         0.0280\n    3428  stock_sz_000338_2021-08-31  stock_sz_000338 2021-08-31     None  000338  潍柴动力    1d  344.41  342.41  351.02  336.73  1143985.0  2.295195e+09     -0.0009         0.0270\n    3429  stock_sz_000338_2021-09-01  stock_sz_000338 2021-09-01     None  000338  潍柴动力    1d  341.03  336.42  341.03  328.28  1218697.0  2.383841e+09     -0.0175         0.0287\n    3430  stock_sz_000338_2021-09-02  stock_sz_000338 2021-09-02     None  000338  潍柴动力    1d  336.88  339.03  340.88  329.67  1023545.0  2.012006e+09      0.0078         0.0241\n\n    [3431 rows x 15 columns]\n\n\ne.g. Stock30mHfqKdata means China Stock 30 minutes hfq quotes.\n\n::\n\n    >>> from zvt.domain import Stock30mHfqKdata\n    >>> Stock30mHfqKdata.record_data(code='000338', provider='em')\n    >>> df = Stock30mHfqKdata.query_data(code='000338', provider='em')\n    >>> print(df)\n\n                                              id        entity_id           timestamp provider    code  name level    open   close    high     low    volume     turnover  change_pct  turnover_rate\n    0    stock_sz_000338_2022-01-07T10:00:00.000  stock_sz_000338 2022-01-07 10:00:00       em  000338  潍柴动力   30m  312.23  313.77  317.30  310.70  288036.0  521397671.0      0.0049         0.0057\n    1    stock_sz_000338_2022-01-07T10:30:00.000  stock_sz_000338 2022-01-07 10:30:00       em  000338  潍柴动力   30m  313.92  313.77  315.15  312.54  111887.0  201667653.0      0.0000         0.0022\n    2    stock_sz_000338_2022-01-07T11:00:00.000  stock_sz_000338 2022-01-07 11:00:00       em  000338  潍柴动力   30m  313.77  314.07  314.69  313.00   80072.0  144303962.0      0.0010         0.0016\n    3    stock_sz_000338_2022-01-07T11:30:00.000  stock_sz_000338 2022-01-07 11:30:00       em  000338  潍柴动力   30m  314.23  316.23  316.84  313.61  160797.0  291742498.0      0.0069         0.0032\n    4    stock_sz_000338_2022-01-07T13:30:00.000  stock_sz_000338 2022-01-07 13:30:00       em  000338  潍柴动力   30m  316.23  314.07  316.99  314.07  115775.0  210236422.0     -0.0068         0.0023\n    ..                                       ...              ...                 ...      ...     ...   ...   ...     ...     ...     ...     ...       ...          ...         ...            ...\n    251  stock_sz_000338_2022-02-28T11:30:00.000  stock_sz_000338 2022-02-28 11:30:00       em  000338  潍柴动力   30m  268.15  268.30  268.61  267.99   34581.0   52053276.0      0.0006         0.0007\n    252  stock_sz_000338_2022-02-28T13:30:00.000  stock_sz_000338 2022-02-28 13:30:00       em  000338  潍柴动力   30m  268.46  268.46  268.61  268.15   38019.0   57268380.0      0.0006         0.0008\n    253  stock_sz_000338_2022-02-28T14:00:00.000  stock_sz_000338 2022-02-28 14:00:00       em  000338  潍柴动力   30m  268.46  269.22  269.53  268.30   41713.0   62994140.0      0.0028         0.0008\n    254  stock_sz_000338_2022-02-28T14:30:00.000  stock_sz_000338 2022-02-28 14:30:00       em  000338  潍柴动力   30m  269.22  269.22  269.53  268.61   40815.0   61676966.0      0.0000         0.0008\n    255  stock_sz_000338_2022-02-28T15:00:00.000  stock_sz_000338 2022-02-28 15:00:00       em  000338  潍柴动力   30m  269.07  269.84  269.84  268.76   60190.0   91032952.0      0.0023         0.0012\n\n    [256 rows x 15 columns]\n\nFinanceFactor\n------------------------------\nThe usage is same as other entity events.\n\n::\n\n    >>> from zvt.domain import FinanceFactor\n    >>> FinanceFactor.record_data(code='000338')\n    >>> FinanceFactor.query_data(code='000338',columns=FinanceFactor.important_cols(),index='timestamp')\n\n                basic_eps  total_op_income    net_profit  op_income_growth_yoy  net_profit_growth_yoy     roe    rota  gross_profit_margin  net_margin  timestamp\n    timestamp\n    2002-12-31        NaN     1.962000e+07  2.471000e+06                   NaN                    NaN     NaN     NaN               0.2068      0.1259 2002-12-31\n    2003-12-31       1.27     3.574000e+09  2.739000e+08              181.2022               109.8778  0.7729  0.1783               0.2551      0.0766 2003-12-31\n    2004-12-31       1.75     6.188000e+09  5.369000e+08                0.7313                 0.9598  0.3245  0.1474               0.2489      0.0868 2004-12-31\n    2005-12-31       0.93     5.283000e+09  3.065000e+08               -0.1463                -0.4291  0.1327  0.0603               0.2252      0.0583 2005-12-31\n    2006-03-31       0.33     1.859000e+09  1.079000e+08                   NaN                    NaN     NaN     NaN                  NaN      0.0598 2006-03-31\n    ...               ...              ...           ...                   ...                    ...     ...     ...                  ...         ...        ...\n    2020-08-28       0.59     9.449000e+10  4.680000e+09                0.0400                -0.1148  0.0983  0.0229               0.1958      0.0603 2020-08-28\n    2020-10-31       0.90     1.474000e+11  7.106000e+09                0.1632                 0.0067  0.1502  0.0347               0.1949      0.0590 2020-10-31\n    2021-03-31       1.16     1.975000e+11  9.207000e+09                0.1327                 0.0112  0.1919  0.0444               0.1931      0.0571 2021-03-31\n    2021-04-30       0.42     6.547000e+10  3.344000e+09                0.6788                 0.6197  0.0622  0.0158               0.1916      0.0667 2021-04-30\n    2021-08-31       0.80     1.264000e+11  6.432000e+09                0.3375                 0.3742  0.1125  0.0287               0.1884      0.0653 2021-08-31\n\n    [66 rows x 10 columns]\n\nThree financial tables\n\n::\n\n    >>> BalanceSheet.record_data(code='000338')\n    >>> IncomeStatement.record_data(code='000338')\n    >>> CashFlowStatement.record_data(code='000338')\n\n.. note::\n    Just remember, all :ref:`schema usage <data.schema_usage>` is in the same way.\n"
  },
  {
    "path": "docs/source/data/extending_data.rst",
    "content": ".. _extending_data:\n\n==============\nExtend data\n==============\n\n\nData structure\n--------------------------\nzvt use sql database(default sqlite, and easy extend to others) to store data\nand provide uniform api to record and query data.(Thanks to the great `Sqlalchemy <https://github.com/sqlalchemy/sqlalchemy>`_)\n\nAs we know, :ref:`TradableEntity<data.tradable_entity>` and :ref:`ActorEntity<data.actor_entity>` is everything about the market, data is the events happen on them.\nThe Schema of the data would always in following structure:\n\n.. image:: ../_static/data_structure.png\n\nIt shows how we define the Entity in the market:\n\n* entity_type\n\ntradable entity type, e.g. stock, future, bond and so on.\n\n* exchange\n\nthe exchange of entity\n\n* code\n\nthe code of the entity\n\nNaturally the id of the entity is defined:\n\n::\n\n    {entity_type}_{exchange}_{code}\n\nAnd the common fields:\n\n* entity_id\n\nentity id\n\n* timestamp\n\nhappen time\n\n* id\n\nthe id of the record, most of time, it's:\n\n::\n\n{entity_id}_{timestamp}\n\nData definition\n--------------------------\n\nzvt data is defined by three concepts:\n\n* Database\n\nThe rule is one module define one db.\n\n* Schema\n\nThe Schema defined in the module belongs to the Database defined inside.\n\n* Provider\n\nThe Database could have different providers\n\nMany pre supported data is in `zvt.domain modules <https://github.com/zvtvz/zvt/blob/master/src/zvt/domain>`_,\ne.g. `Finance data <https://github.com/zvtvz/zvt/blob/master/src/zvt/domain/fundamental/finance.py>`_, you could\nrefer them when you want to extend data.\n\n\n.. _extending_data.add_data:\n\nKey steps to add data\n--------------------------\n\nLet's show the key steps to add data.\n\n1. Intent\n~~~~~~~~~~~~~~~~~~~~\nI want to add some data about the news on Stock.\n\n2. Find data source\n~~~~~~~~~~~~~~~~~~~~\nThe data source could be whatever you can see in the web ——— just write some\nweb crawlers to get them, e.g, most of `pre-implemented recorders <https://github.com/zvtvz/zvt/tree/master/src/zvt/recorders>`_.\nAnd you could use data from commercial data provider in the same way, e.g. `joinquant recorders <https://github.com/zvtvz/zvt/tree/master/src/zvt/recorders/joinquant>`_.\n\nLet's show the crawler way here:\n\nOpen `eastmoney wap <https://wap.eastmoney.com/quote/stock/0.002572.html>`_ and Find `news api <https://np-listapi.eastmoney.com/comm/wap/getListInfo?cb=callback&client=wap&type=1&mTypeAndCode=0.002572&pageSize=20&pageIndex=1&callback=jQuery1830017478247906740352_1644568731256&_=1644568879493>`_\n\nThe news item result:\n\n::\n\n    {\n      \"Art_ShowTime\": \"2022-02-09 14:12:30\",\n      \"Art_Image\": \"\",\n      \"Art_MediaName\": \"每日经济新闻\",\n      \"Art_Code\": \"202202092271136373\",\n      \"Art_Title\": \"首创证券维持索菲亚买入评级 公司简评报告：多重因素影响短期压制业绩 看好2022年利润修复\",\n      \"Art_SortStart\": \"1644387150036373\",\n      \"Art_VideoCount\": 0,\n      \"Art_OriginUrl\": \"http://finance.eastmoney.com/news/1354,202202092271136373.html\",\n      \"Art_Url\": \"http://finance.eastmoney.com/a/202202092271136373.html\"\n    }\n\n3. Define Schema\n~~~~~~~~~~~~~~~~~~~~\n\nNo matter what the format of the data outside zvt is, we use zvt simple and\nuniform contract to define them.\n\n::\n\n    >>> from sqlalchemy import Column, String\n    >>> from sqlalchemy.orm import declarative_base\n    >>> from zvt.contract import Mixin\n    >>> from zvt.contract.register import register_schema\n    >>> NewsBase = declarative_base()\n    >>> class StockNews(NewsBase, Mixin):\n    >>>     __tablename__ = \"stock_news\"\n    >>>     #: news title\n    >>>     news_title = Column(String)\n    >>>\n    >>> register_schema(providers=[\"em\"], db_name=\"stock_news\", schema_base=NewsBase, entity_type=\"stock\")\n\nCheck the defined db\n\n::\n\n    >>> StockNews.get_storages()\n\n    [Engine(sqlite:////Users/foolcage/zvt-home/data/em_stock_news.db?check_same_thread=False)]\n\nAs you see, the db file name format is:\n\n::\n\n    {provider}_{db_name}\n\nIf you have another provider, e.g. sina, just register it too:\n\n::\n\n    >>> register_schema(providers=[\"em\", \"sina\"], db_name=\"stock_news\", schema_base=NewsBase, entity_type=\"stock\")\n\nAnd you could find another db file:\n\n::\n\n    >>> StockNews.get_storages()\n\n    [Engine(sqlite:////Users/foolcage/zvt-home/data/sina_stock_news.db?check_same_thread=False)]\n\nThe advantage of this mechanism is:\n\n* schema is the way you want\n* provider could be switched seamlessly\n\n4. Implement recorder\n~~~~~~~~~~~~~~~~~~~~~\nLet's implement a recorder for StockNews.\n\n::\n\n    import pandas as pd\n    from zvt.contract.api import df_to_db\n    from zvt.contract.recorder import FixedCycleDataRecorder\n    from zvt.domain import Stock\n    from zvt.domain.misc.stock_news import StockNews\n    from zvt.recorders.em import em_api\n\n\n    class EMStockNewsRecorder(FixedCycleDataRecorder):\n        original_page_url = \"https://wap.eastmoney.com/quote/stock/0.002572.html\"\n        url = \"https://np-listapi.eastmoney.com/comm/wap/getListInfo?cb=callback&client=wap&type=1&mTypeAndCode=0.002572&pageSize=200&pageIndex={}&callback=jQuery1830017478247906740352_1644568731256&_=1644568879493\"\n\n        # the entity class you record for\n        entity_schema = Stock\n        # connect schema with recorder\n        data_schema = StockNews\n        # entity data provider\n        entity_provider = \"em\"\n        # data schema provider\n        provider = \"em\"\n\n        def record(self, entity, start, end, size, timestamps):\n            news = em_api.get_news(entity_id=entity.id)\n            df = pd.DataFrame.from_records(news)\n            self.logger.info(df)\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nAfter that, you could use the data in zvt way:\n\n::\n\n    >>> StockNews.record_data(provider=\"em\", entity_id=\"stock_sz_002572\")\n    >>> df = StockNews.query_data(entity_id=\"stock_sz_002572\")\n    >>> print(df)\n\n                                              id        entity_id           timestamp                                     news_title\n    0    stock_sz_002572_2019-05-20 19:50:42  stock_sz_002572 2019-05-20 19:50:42                索菲亚（002572.SZ）：股价回撤超65% 是低吸机会吗?\n    1    stock_sz_002572_2019-05-24 22:39:47  stock_sz_002572 2019-05-24 22:39:47                             索菲亚拟发行不超过5亿元超短期融资券\n    2    stock_sz_002572_2019-05-24 22:48:54  stock_sz_002572 2019-05-24 22:48:54              索菲亚(002572.SZ)拟终止发行不超10亿元的可转换公司债券\n    3    stock_sz_002572_2019-06-04 14:06:42  stock_sz_002572 2019-06-04 14:06:42              索菲亚(002572.SZ)截至5月底已累计回购2.02亿元的股份\n    4    stock_sz_002572_2019-06-13 06:24:44  stock_sz_002572 2019-06-13 06:24:44                                索菲亚功臣王飚能否扶起汉森中国\n    ..                                   ...              ...                 ...                                            ...\n    367  stock_sz_002572_2022-02-07 14:04:40  stock_sz_002572 2022-02-07 14:04:40         开源证券维持索菲亚买入评级 近期获6份券商研报关注 目标均价涨幅59.82%\n    368  stock_sz_002572_2022-02-07 15:32:46  stock_sz_002572 2022-02-07 15:32:46                             【调研快报】索菲亚接待机构投资者调研\n    369  stock_sz_002572_2022-02-08 16:50:37  stock_sz_002572 2022-02-08 16:50:37                             索菲亚：公司承接了容东片区安置房项目\n    370  stock_sz_002572_2022-02-08 21:59:00  stock_sz_002572 2022-02-08 21:59:00       9亿坏账拖累净利下降九成 家具巨头索菲亚“甩包袱”起跑 腰斩的股价能否趁势抬头？\n    371  stock_sz_002572_2022-02-09 14:12:30  stock_sz_002572 2022-02-09 14:12:30  首创证券维持索菲亚买入评级 公司简评报告：多重因素影响短期压制业绩 看好2022年利润修复\n\n    [372 rows x 4 columns]\n\n\n.. _extending_data.tradable_entity:\n\nAdd new TradableEntity\n--------------------------\nIt's human nature to like the new and hate the old. Adding new TradableEntity is easy in zvt.\n\nAnd from a higher perspective, trading is everywhere. you make trading everytime when you make the\ndecision.\n\nSo you could treat Country as TradableEntity and make trading when making decision where to live or invest.\n\nLet's show the key steps to add new TradableEntity.\n\n1. Define entity Schema\n~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n    # -*- coding: utf-8 -*-\n\n    from sqlalchemy import Column, String, Float\n    from sqlalchemy.orm import declarative_base\n\n    from zvt.contract.schema import TradableEntity\n    from zvt.contract.register import register_schema, register_entity\n\n    CountryMetaBase = declarative_base()\n\n\n    @register_entity(entity_type=\"country\")\n    class Country(CountryMetaBase, TradableEntity):\n        __tablename__ = \"country\"\n\n        #: 区域\n        #: region\n        region = Column(String(length=128))\n        #: 首都\n        #: capital city\n        capital_city = Column(String(length=128))\n        #: 收入水平\n        #: income level\n        income_level = Column(String(length=64))\n        #: 贷款类型\n        #: lending type\n        lending_type = Column(String(length=64))\n        #: 经度\n        #: longitude\n        longitude = Column(Float)\n        #: 纬度\n        #: latitude\n        latitude = Column(Float)\n\n\n    register_schema(providers=[\"wb\"], db_name=\"country_meta\", schema_base=CountryMetaBase)\n\nentity_type, exchange and code define the entity, for country, it's in following way:\n\n::\n\n    entity_type = \"country\"\n    exchange = \"galaxy\"\n    code = \"iso code\"\n\ne.g. country_galaxy_CN = China, country_galaxy_US = United States of America\n\n\n2. Implement recorder for the entity\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n    from zvt.contract.api import df_to_db\n    from zvt.contract.recorder import Recorder\n    from zvt.domain.meta.country_meta import Country\n    from zvt.recorders.wb import wb_api\n\n\n    class WBCountryRecorder(Recorder):\n        provider = \"wb\"\n        data_schema = Country\n\n        def run(self):\n            df = wb_api.get_countries()\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n3. Define schema for the entity\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\ne.g treasury yield of the country\n::\n\n    # -*- coding: utf-8 -*-\n    from sqlalchemy import Column, String, Float\n    from sqlalchemy.orm import declarative_base\n\n    from zvt.contract import Mixin\n    from zvt.contract.register import register_schema\n\n    CurrencyBase = declarative_base()\n\n\n    class TreasuryYield(CurrencyBase, Mixin):\n        __tablename__ = \"treasury_yield\"\n\n        code = Column(String(length=32))\n\n        # 2年期\n        yield_2 = Column(Float)\n        # 5年期\n        yield_5 = Column(Float)\n        # 10年期\n        yield_10 = Column(Float)\n        # 30年期\n        yield_30 = Column(Float)\n\n\n    register_schema(providers=[\"em\"], db_name=\"currency\", schema_base=CurrencyBase)\n\nAnd the `recorder <https://github.com/zvtvz/zvt/blob/master/src/zvt/recorders/em/macro/em_treasury_yield_recorder.py>`_ for the schema\n\n4. Use them in zvt way\n~~~~~~~~~~~~~~~~~~~~~~\n\nFind the rich country:\n\n::\n\n    >>> from zvt.domain import Country\n    >>> Country.record_data()\n    >>> df = Country.query_data()\n    >>> df[df['income_level']=='High income']\n\n                       id          entity_id timestamp entity_type exchange code                    name list_date end_date                      region      capital_city income_level    lending_type   longitude   latitude\n    0    country_galaxy_AW  country_galaxy_AW      None     country   galaxy   AW                   Aruba      None     None  Latin America & Caribbean         Oranjestad  High income  Not classified  -70.016700  12.516700\n    7    country_galaxy_AD  country_galaxy_AD      None     country   galaxy   AD                 Andorra      None     None       Europe & Central Asia  Andorra la Vella  High income  Not classified    1.521800  42.507500\n    9    country_galaxy_AE  country_galaxy_AE      None     country   galaxy   AE    United Arab Emirates      None     None  Middle East & North Africa         Abu Dhabi  High income  Not classified   54.370500  24.476400\n    13   country_galaxy_AG  country_galaxy_AG      None     country   galaxy   AG     Antigua and Barbuda      None     None  Latin America & Caribbean       Saint John's  High income            IBRD  -61.845600  17.117500\n    14   country_galaxy_AU  country_galaxy_AU      None     country   galaxy   AU               Australia      None     None         East Asia & Pacific          Canberra  High income  Not classified  149.129000 -35.282000\n    ..                 ...                ...       ...         ...      ...  ...                     ...       ...      ...                         ...               ...          ...             ...         ...        ...\n    277  country_galaxy_TW  country_galaxy_TW      None     country   galaxy   TW           Taiwan, China      None     None         East Asia & Pacific                    High income  Not classified         NaN        NaN\n    282  country_galaxy_UY  country_galaxy_UY      None     country   galaxy   UY                 Uruguay      None     None  Latin America & Caribbean         Montevideo  High income            IBRD  -56.067500 -34.894100\n    283  country_galaxy_US  country_galaxy_US      None     country   galaxy   US           United States      None     None               North America   Washington D.C.  High income  Not classified  -77.032000  38.889500\n    287  country_galaxy_VG  country_galaxy_VG      None     country   galaxy   VG  British Virgin Islands      None     None  Latin America & Caribbean          Road Town  High income  Not classified  -64.623056  18.431389\n    288  country_galaxy_VI  country_galaxy_VI      None     country   galaxy   VI   Virgin Islands (U.S.)      None     None  Latin America & Caribbean   Charlotte Amalie  High income  Not classified  -64.896300  18.335800\n\n    [80 rows x 15 columns]\n\n\nCompare treasury yields of different maturities:\n\n::\n\n    >>> from zvt.domain import TreasuryYield\n    >>> from zvt.api.intent import compare\n    >>> TreasuryYield.record_data()\n    >>> compare(codes=[\"US\"], schema=TreasuryYield, columns=[\"yield_2\", \"yield_10\", \"yield_30\"])\n\n.. image:: ../_static/compare_yields.png"
  },
  {
    "path": "docs/source/data/index.rst",
    "content": "==========\nData\n==========\n\n| Without data, quant becomes a castle in the air.\n| So, what exactly is data?\n| zvt makes a concise and unified abstraction of data:\n\n    In the world of zvt, there are two kinds of entities, one is :ref:`tradable entity <data.tradable_entity>`,\n    the other is :ref:`actor entity <data.actor_entity>`. Data is the events happened on them.\n\n.. image:: ../_static/view.png\n\n.. note::\n\n    Philosophically, entity is the existence described by itself, classification of existential concepts.\n\n.. toctree::\n    :maxdepth: 2\n\n    data_concepts\n    record_and_query\n    extending_data\n    adding_new_entity\n    trading_anything\n"
  },
  {
    "path": "docs/source/data/record_and_query.rst",
    "content": ".. _record_and_query:\n\n==============\nRecord & query\n==============\n\n\nDualism\n--------------------------\nIn and out, write and read, and so record and query.\n\n\nRecord data\n--------------------------\n\nQuery data\n--------------------------\n"
  },
  {
    "path": "docs/source/data/trading_anything.rst",
    "content": ".. trading_anything:\n\n=======================\nCapital without country\n=======================\n\nFrom a higher perspective, trading is everywhere. you make trading everytime when you make the\ndecision.\n\nSo you could treat Country as TradableEntity and make trading when making decision where to live or invest.\n\nIt's nothing than a specific case of :ref:`Adding new entity <adding_new_entity>`.\n\nLet's show the key steps below.\n\nDefine entity Schema\n--------------------------\n\n::\n\n    # -*- coding: utf-8 -*-\n\n    from sqlalchemy import Column, String, Float\n    from sqlalchemy.orm import declarative_base\n\n    from zvt.contract.schema import TradableEntity\n    from zvt.contract.register import register_schema, register_entity\n\n    CountryMetaBase = declarative_base()\n\n\n    @register_entity(entity_type=\"country\")\n    class Country(CountryMetaBase, TradableEntity):\n        __tablename__ = \"country\"\n\n        #: 区域\n        #: region\n        region = Column(String(length=128))\n        #: 首都\n        #: capital city\n        capital_city = Column(String(length=128))\n        #: 收入水平\n        #: income level\n        income_level = Column(String(length=64))\n        #: 贷款类型\n        #: lending type\n        lending_type = Column(String(length=64))\n        #: 经度\n        #: longitude\n        longitude = Column(Float)\n        #: 纬度\n        #: latitude\n        latitude = Column(Float)\n\n\n    register_schema(providers=[\"wb\"], db_name=\"country_meta\", schema_base=CountryMetaBase)\n\nentity_type, exchange and code define the entity, for country, it's in following way:\n\n::\n\n    entity_type = \"country\"\n    exchange = \"galaxy\"\n    code = \"iso code\"\n\ne.g. country_galaxy_CN = China, country_galaxy_US = United States of America\n\n\nImplement recorder for the entity\n---------------------------------\n\n::\n\n    from zvt.contract.api import df_to_db\n    from zvt.contract.recorder import Recorder\n    from zvt.domain.meta.country_meta import Country\n    from zvt.recorders.wb import wb_api\n\n\n    class WBCountryRecorder(Recorder):\n        provider = \"wb\"\n        data_schema = Country\n\n        def run(self):\n            df = wb_api.get_countries()\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\nDefine schema for the entity\n----------------------------\n\ne.g treasury yield of the country\n::\n\n    # -*- coding: utf-8 -*-\n    from sqlalchemy import Column, String, Float\n    from sqlalchemy.orm import declarative_base\n\n    from zvt.contract import Mixin\n    from zvt.contract.register import register_schema\n\n    MonetaryBase = declarative_base()\n\n\n    class TreasuryYield(MonetaryBase, Mixin):\n        __tablename__ = \"treasury_yield\"\n\n        code = Column(String(length=32))\n\n        # 2年期\n        yield_2 = Column(Float)\n        # 5年期\n        yield_5 = Column(Float)\n        # 10年期\n        yield_10 = Column(Float)\n        # 30年期\n        yield_30 = Column(Float)\n\n\n    register_schema(providers=[\"em\"], db_name=\"monetary\", schema_base=MonetaryBase)\n    # the __all__ is generated\n    __all__ = [\"TreasuryYield\"]\n\nAnd the `recorder <https://github.com/zvtvz/zvt/blob/master/src/zvt/recorders/em/macro/em_treasury_yield_recorder.py>`_ for the schema\n\nUse them in zvt way\n-------------------\n\nFind the rich country:\n\n::\n\n    >>> from zvt.domain import Country\n    >>> Country.record_data()\n    >>> df = Country.query_data()\n    >>> df[df['income_level']=='High income']\n\n                       id          entity_id timestamp entity_type exchange code                    name list_date end_date                      region      capital_city income_level    lending_type   longitude   latitude\n    0    country_galaxy_AW  country_galaxy_AW      None     country   galaxy   AW                   Aruba      None     None  Latin America & Caribbean         Oranjestad  High income  Not classified  -70.016700  12.516700\n    7    country_galaxy_AD  country_galaxy_AD      None     country   galaxy   AD                 Andorra      None     None       Europe & Central Asia  Andorra la Vella  High income  Not classified    1.521800  42.507500\n    9    country_galaxy_AE  country_galaxy_AE      None     country   galaxy   AE    United Arab Emirates      None     None  Middle East & North Africa         Abu Dhabi  High income  Not classified   54.370500  24.476400\n    13   country_galaxy_AG  country_galaxy_AG      None     country   galaxy   AG     Antigua and Barbuda      None     None  Latin America & Caribbean       Saint John's  High income            IBRD  -61.845600  17.117500\n    14   country_galaxy_AU  country_galaxy_AU      None     country   galaxy   AU               Australia      None     None         East Asia & Pacific          Canberra  High income  Not classified  149.129000 -35.282000\n    ..                 ...                ...       ...         ...      ...  ...                     ...       ...      ...                         ...               ...          ...             ...         ...        ...\n    277  country_galaxy_TW  country_galaxy_TW      None     country   galaxy   TW           Taiwan, China      None     None         East Asia & Pacific                    High income  Not classified         NaN        NaN\n    282  country_galaxy_UY  country_galaxy_UY      None     country   galaxy   UY                 Uruguay      None     None  Latin America & Caribbean         Montevideo  High income            IBRD  -56.067500 -34.894100\n    283  country_galaxy_US  country_galaxy_US      None     country   galaxy   US           United States      None     None               North America   Washington D.C.  High income  Not classified  -77.032000  38.889500\n    287  country_galaxy_VG  country_galaxy_VG      None     country   galaxy   VG  British Virgin Islands      None     None  Latin America & Caribbean          Road Town  High income  Not classified  -64.623056  18.431389\n    288  country_galaxy_VI  country_galaxy_VI      None     country   galaxy   VI   Virgin Islands (U.S.)      None     None  Latin America & Caribbean   Charlotte Amalie  High income  Not classified  -64.896300  18.335800\n\n    [80 rows x 15 columns]\n\n\nCompare treasury yields of different maturities:\n\n::\n\n    >>> from zvt.domain import TreasuryYield\n    >>> from zvt.api.intent import compare\n    >>> TreasuryYield.record_data()\n    >>> compare(codes=[\"US\"], schema=TreasuryYield, columns=[\"yield_2\", \"yield_10\", \"yield_30\"])\n\n.. image:: ../_static/compare_yields.png"
  },
  {
    "path": "docs/source/drawer/drawer_concepts.rst",
    "content": "===============\nDrawer concepts\n===============\n\n\nIntent\n------------------------------\nThere are many kinds of charts, but the intent is much less. zvt classify\nintent as compare, distribute and composite.\n\nWe could analyze the entity itself or look at it in the whole market by\ncomparing with others. :ref:`NormalData<factor.normal_data>` uses\ndifferent structure to express different intents.\n\nCompare\n------------------------------\nBased on zvt's world view and data model, you could compare anything you care about.\n\nThink about that you want to compare S&P 500 index with Shanghai index. It's easy\nand comfortable with no pain.\n\n::\n\n    >>> from zvt.api.intent import compare\n    >>> from zvt.domain import Indexus1dKdata, Index, Indexus, Index1dKdata\n    >>> Index.record_data(provider=\"em\")\n    >>> Indexus.record_data(provider=\"em\")\n    >>> Index1dKdata.record_data(entity_id=\"index_sh_000001\", provider=\"em\")\n    >>> Indexus1dKdata.record_data(entity_id=\"indexus_us_SPX\", provider=\"em\")\n    >>> compare(entity_ids=[\"index_sh_000001\", \"indexus_us_SPX\"], start_timestamp=\"2000-01-01\", scale_value=100)\n\n\n.. image:: ../_static/compare_cn_us.png\n\nTry some other style:\n\n::\n\n    >>> compare(entity_ids=[\"index_sh_000001\", \"indexus_us_SPX\"], start_timestamp=\"2000-01-01\",  schema_map_columns={Index1dKdata:[\"close\"],Indexus1dKdata:[\"close\"]})\n"
  },
  {
    "path": "docs/source/drawer/index.rst",
    "content": "==========\nDrawer\n==========\n\n| A picture is worth a thousand words.\n| So, what exactly is behind the picture?\n| it's drawer in zvt:\n\n    Drawer is who can draw the picture with NormalData by Intent.\n\n.. toctree::\n    :maxdepth: 2\n\n    drawer_concepts\n\n\n"
  },
  {
    "path": "docs/source/factor/extending_factor.rst",
    "content": ".. _factor.extending_factor:\n\n================\nExtending Factor\n================\n\nRethink NormalData\n--------------------------\nNormal data format is as below:\n\n.. image:: ../_static/normal_data.png\n\n| Why use this format?\n| The reason is that human is comfortable for two-dimensional space and\n  high-dimensional space could be reduced to it.\n\n\nObviously, It's complete and consistent. You could **calculate oneself in time intervals**\nor **calculate with others in specific time or intervals**. And it's easy to analyze  the\ndata with charts by Intent.\n\nFactor data structure\n--------------------------\nThe factor computing flow is as below:\n\n.. image:: ../_static/factor_flow.png\n\n* data_df\n\nNormalData df read from the schema.\n\n* factor_df\n\nNormalData df computed by data_df using use :class:`~.zvt.contract.factor.Transformer`, :class:`~.zvt.contract.factor.Accumulator`\nor your custom logic.\n\n* result_df\n\nNormalData df containing columns **filter_result** and(or) **score_result**\nwhich calculated using factor_df or(and) data_df.\nFilter_result is True or False, score_result is from 0 to 1.\nYou could use TargetSelector to select targets at specific time when\nfilter_result is True and(or) score_result >=0.8 by default or do more\nprecise control by setting other arguments.\n\nLet's take BullFactor to illustrate the calculation process:\n::\n\n    >>> from zvt.factors.macd import BullFactor\n    >>> from zvt.domain import Stock1dHfqKdata\n    >>> entity_ids = [\"stock_sh_601318\", \"stock_sz_000338\"]\n    >>> Stock1dHfqKdata.record_data(entity_ids=entity_ids, provider=\"em\")\n    >>> factor = BullFactor(entity_ids=entity_ids, provider=\"em\", start_timestamp='2019-01-01', end_timestamp='2019-06-10')\n\ncheck the dfs:\n::\n\n    >>> factor.data_df\n    >>> factor.factor_df\n    >>> factor.result_df\n\n.. _factor.write_transformer:\n\nWrite transformer\n--------------------------\nTransformer works as bellow:\n\n.. image:: ../_static/transformer.png\n\nWhat's in your mind is the NormalData format, and then practice the skills to\nmanipulate it.\n\nYou could use other ta lib with it easily, e.g Bollinger Bands using `TA lib <https://github.com/bukosabino/ta#>`_\n\ninstall ta at first:\n::\n\n    pip install --upgrade ta\n\nwrite boll transformer and factor:\n::\n\n    from typing import Optional, List\n\n    import pandas as pd\n    from ta.volatility import BollingerBands\n\n    from zvt.contract.factor import *\n    from zvt.factors import TechnicalFactor\n\n\n    class BollTransformer(Transformer):\n        def transform_one(self, entity_id, df: pd.DataFrame) -> pd.DataFrame:\n            indicator_bb = BollingerBands(close=df[\"close\"], window=20, window_dev=2)\n\n            # Add Bollinger Bands features\n            df[\"bb_bbm\"] = indicator_bb.bollinger_mavg()\n            df[\"bb_bbh\"] = indicator_bb.bollinger_hband()\n            df[\"bb_bbl\"] = indicator_bb.bollinger_lband()\n\n            # Add Bollinger Band high indicator\n            df[\"bb_bbhi\"] = indicator_bb.bollinger_hband_indicator()\n\n            # Add Bollinger Band low indicator\n            df[\"bb_bbli\"] = indicator_bb.bollinger_lband_indicator()\n\n            # Add Width Size Bollinger Bands\n            df[\"bb_bbw\"] = indicator_bb.bollinger_wband()\n\n            # Add Percentage Bollinger Bands\n            df[\"bb_bbp\"] = indicator_bb.bollinger_pband()\n            return df\n\n\n    class BollFactor(TechnicalFactor):\n        transformer = BollTransformer()\n\n        def drawer_factor_df_list(self) -> Optional[List[pd.DataFrame]]:\n            # set the factor to show\n            return [self.factor_df[[\"bb_bbm\", \"bb_bbh\", \"bb_bbl\"]]]\n\n        def compute_result(self):\n            # set filter_result, which bb_bbli=1.0 buy and bb_bbhi=1.0 sell\n            super().compute_result()\n            self.result_df = (self.factor_df[\"bb_bbli\"] - self.factor_df[\"bb_bbhi\"]).to_frame(name=\"filter_result\")\n            self.result_df[self.result_df == 0] = None\n            self.result_df[self.result_df == 1] = True\n            self.result_df[self.result_df == -1] = False\n\nLet's show it:\n::\n\n    >>> from zvt.domain import Stock1dHfqKdata\n\n    >>> provider = \"em\"\n    >>> entity_ids = [\"stock_sz_000338\", \"stock_sh_601318\"]\n    >>> Stock1dHfqKdata.record_data(entity_ids=entity_ids, provider=provider,)\n    >>> factor = BollFactor(\n    >>> entity_ids=entity_ids, provider=provider, entity_provider=provider, start_timestamp=\"2019-01-01\"\n    >>> )\n    >>> factor.draw(show=True)\n\n.. image:: ../_static/boll_factor.png\n\nMost of ta lib support calculate single target by default, so we implement\ntransform_one of the Transformer. If you want to calculate many targets at\nthe same time you could implement transform directly and it would be faster.\n\nAnd Transformer is stateless, so it's easy to reuse in different factor if need.\n\nFactor with IntervalLevel\n--------------------------\nAfter you write the transformer and construct the factor, it's easy to use in\ndifferent levels.\n\nLet's use BollFactor in IntervalLevel 30m:\n::\n\n    >>> from zvt.domain import Stock30mHfqKdata\n\n    >>> provider = \"em\"\n    >>> entity_ids = [\"stock_sz_000338\", \"stock_sh_601318\"]\n\n    >>> Stock30mHfqKdata.record_data(entity_ids=entity_ids, provider=provider)\n    >>> factor = BollFactor(\n            entity_ids=entity_ids, provider=provider, entity_provider=provider, start_timestamp=\"2021-01-01\"\n        )\n    >>> factor.draw(show=True)\n\nStream computing\n--------------------------\nThe data is coming continuously and the factor using the data need computing\ncontinuously too.\n\nIt's simple and straightforward:\n\n* {Schema}.record_data in one process\n* {Factor}.move_on which call {Schema}.query_data in another process\n\n.. image:: ../_static/stream.png\n\nWe keep the simple enough philosophy: single process and thread. Enjoy\nprogramming and make everything clear.\n\nFactor persistence\n--------------------------\nGetting data and computing factor continuously is cool.\nBut...If It took a long time to calculate the factor and crashed.How would you feel?\n\n.. image:: ../_static/bear.gif\n    :align: center\n\nSelect the targets\n--------------------------\nYou could select the targets according result_df of the factor by yourself.\nOr use TargetSelector do it for you.\n\n.. image:: ../_static/factor_result.png\n\n\nWrite accumulator\n--------------------------\n\n.. image:: ../_static/accumulator.png\n"
  },
  {
    "path": "docs/source/factor/factor_concepts.rst",
    "content": ".. _factor.factor_concepts:\n\n===============\nFactor concepts\n===============\n\n.. _factor.normal_data:\n\nNormal data\n------------------------------\n:class:`~.zvt.contract.normal_data.NormalData` is the data containing pandas dataframe\nwith multiple index which level 0 named entity_id and level 1 named timestamp:\n\n===============                 ==========        =====   =====   =====   =====\nentity_id                       timestamp         col1    col2    col3    col4\n===============                 ==========        =====   =====   =====   =====\nstock_sz_000338                 2020-05-05        1.2     0.5     0.3     a\n...                             2020-05-06        1.0     0.7     0.2     b\nstock_sz_000778                 2020-05-05        1.2     0.5     0.3     a\n...                             2020-05-06        1.0     0.7     0.2     b\n===============                 ==========        =====   =====   =====   =====\n\nThis data structure is used heavily in zvt computing, you should be familiar with it.\n`Pandas multiple index guide <https://pandas.pydata.org/pandas-docs/stable/user_guide/advanced.html#>`_ is\na good start.\n\nQuery data returning normal data in this way:\n\n::\n\n    {Schema}.query_data(index=[\"entity_id, timestamp\"])\n\ne.g.\n\n::\n\n    >>> from zvt.domain import *\n    >>> entity_ids = [\"stock_sz_000338\", \"stock_sz_000001\"]\n    >>> Stock1dHfqKdata.record_data(entity_ids=entity_ids, provider=\"em\")\n    >>> df = Stock1dHfqKdata.query_data(entity_ids=entity_ids, provider=\"em\", index=[\"entity_id\", \"timestamp\"])\n    >>> print(df)\n\n                                                        id        entity_id  timestamp provider    code  name level    open   close    high     low    volume      turnover  change_pct  turnover_rate\n    entity_id       timestamp\n    stock_sz_000001 1991-04-03  stock_sz_000001_1991-04-03  stock_sz_000001 1991-04-03       em  000001  平安银行    1d   49.00   49.00   49.00   49.00       1.0  5.000000e+03      0.2250         0.0000\n                    1991-04-04  stock_sz_000001_1991-04-04  stock_sz_000001 1991-04-04       em  000001  平安银行    1d   48.76   48.76   48.76   48.76       3.0  1.500000e+04     -0.0049         0.0000\n                    1991-04-05  stock_sz_000001_1991-04-05  stock_sz_000001 1991-04-05       em  000001  平安银行    1d   48.52   48.52   48.52   48.52       2.0  1.000000e+04     -0.0049         0.0000\n                    1991-04-06  stock_sz_000001_1991-04-06  stock_sz_000001 1991-04-06       em  000001  平安银行    1d   48.28   48.28   48.28   48.28       7.0  3.400000e+04     -0.0049         0.0000\n                    1991-04-08  stock_sz_000001_1991-04-08  stock_sz_000001 1991-04-08       em  000001  平安银行    1d   48.04   48.04   48.04   48.04       2.0  1.000000e+04     -0.0050         0.0000\n    ...                                                ...              ...        ...      ...     ...   ...   ...     ...     ...     ...     ...       ...           ...         ...            ...\n    stock_sz_000338 2022-01-17  stock_sz_000338_2022-01-17  stock_sz_000338 2022-01-17       em  000338  潍柴动力    1d  296.26  297.64  298.71  293.49  504866.0  8.546921e+08      0.0026         0.0100\n                    2022-01-18  stock_sz_000338_2022-01-18  stock_sz_000338 2022-01-18       em  000338  潍柴动力    1d  298.10  300.87  302.71  296.10  622455.0  1.064735e+09      0.0109         0.0124\n                    2022-01-19  stock_sz_000338_2022-01-19  stock_sz_000338 2022-01-19       em  000338  潍柴动力    1d  299.64  299.48  304.24  298.56  610096.0  1.049195e+09     -0.0046         0.0121\n                    2022-01-20  stock_sz_000338_2022-01-20  stock_sz_000338 2022-01-20       em  000338  潍柴动力    1d  298.10  294.87  299.18  290.11  812949.0  1.361764e+09     -0.0154         0.0161\n                    2022-01-21  stock_sz_000338_2022-01-21  stock_sz_000338 2022-01-21       em  000338  潍柴动力    1d  292.72  287.04  293.34  284.58  754156.0  1.234360e+09     -0.0266         0.0150\n\n    [10878 rows x 15 columns]\n\n\n.. _factor.factor:\n\nFactor\n------------------------------\n:class:`~.zvt.contract.factor.Factor` is a computing facility to build *factor* according your mind ——— algorithm.\nIt reads data from schema, use :class:`~.zvt.contract.factor.Transformer`, :class:`~.zvt.contract.factor.Accumulator`\nor your custom logic to compute, and save the result to new schema if need.\nIt also provides a standard way to evaluate the targets which could be used by :class:`~.zvt.factors.target_selector.TargetSelector`\nand :class:`~.zvt.trader.trader.Trader` for backtesting or real trading.\n\nTransformer\n------------------------------\nComputing factor which depends on input data only.\nHere is an example: :class:`~.zvt.factors.algorithm.MaTransformer`\n\nAccumulator\n------------------------------\nComputing factor which depends on input data and previous result of the factor.\nHere is an example: :class:`~.zvt.factors.ma.ma_stats_factor.MaStatsAccumulator.`\n\nLet's have a look by example:\n\n::\n\n    >>> from zvt.factors import GoldCrossFactor\n    >>> from zvt.domain import Stock1dHfqKdata\n    >>> entity_ids = [\"stock_sz_000338\"]\n    >>> Stock1dHfqKdata.record_data(entity_ids=entity_ids, provider=\"em\")\n    >>> factor = GoldCrossFactor(entity_ids=entity_ids, provider=\"em\", start_timestamp=\"2018-01-01\")\n    >>> print(factor.factor_df)\n    >>> print(factor.result_df)\n    >>> factor.draw(show=True)\n                               level      turnover    high                          id    open     low        entity_id  timestamp   close  turnover_rate     volume      diff       dea      macd  live   bull  live_count\n    entity_id       timestamp\n    stock_sz_000338 2018-01-02    1d  8.325588e+08  145.97  stock_sz_000338_2018-01-02  141.21  141.06  stock_sz_000338 2018-01-02  145.67         0.0225   972471.0       NaN       NaN       NaN    -1  False          -1\n                    2018-01-03    1d  7.530370e+08  147.66  stock_sz_000338_2018-01-03  146.13  144.29  stock_sz_000338 2018-01-03  144.44         0.0202   870225.0       NaN       NaN       NaN    -1  False          -2\n                    2018-01-04    1d  4.917067e+08  145.51  stock_sz_000338_2018-01-04  144.75  143.67  stock_sz_000338 2018-01-04  145.21         0.0133   574335.0       NaN       NaN       NaN    -1  False          -3\n                    2018-01-05    1d  5.282211e+08  146.59  stock_sz_000338_2018-01-05  146.44  143.21  stock_sz_000338 2018-01-05  143.21         0.0143   616244.0       NaN       NaN       NaN    -1  False          -4\n                    2018-01-08    1d  1.255871e+09  150.43  stock_sz_000338_2018-01-08  143.82  143.82  stock_sz_000338 2018-01-08  150.12         0.0331  1426567.0       NaN       NaN       NaN    -1  False          -5\n    ...                          ...           ...     ...                         ...     ...     ...              ...        ...     ...            ...        ...       ...       ...       ...   ...    ...         ...\n                    2022-01-17    1d  8.546921e+08  298.71  stock_sz_000338_2022-01-17  296.26  293.49  stock_sz_000338 2022-01-17  297.64         0.0100   504866.0 -1.386687  1.781615 -6.336603    -1  False         -12\n                    2022-01-18    1d  1.064735e+09  302.71  stock_sz_000338_2022-01-18  298.10  296.10  stock_sz_000338 2022-01-18  300.87         0.0124   622455.0 -1.694421  1.086407 -5.561657    -1  False         -13\n                    2022-01-19    1d  1.049195e+09  304.24  stock_sz_000338_2022-01-19  299.64  298.56  stock_sz_000338 2022-01-19  299.48         0.0121   610096.0 -2.027097  0.463707 -4.981607    -1  False         -14\n                    2022-01-20    1d  1.361764e+09  299.18  stock_sz_000338_2022-01-20  298.10  290.11  stock_sz_000338 2022-01-20  294.87         0.0161   812949.0 -2.632389 -0.155513 -4.953753    -1  False         -15\n                    2022-01-21    1d  1.234360e+09  293.34  stock_sz_000338_2022-01-21  292.72  284.58  stock_sz_000338 2022-01-21  287.04         0.0150   754156.0 -3.701237 -0.864657 -5.673159    -1  False         -16\n\n    [987 rows x 17 columns]\n                                filter_result\n    entity_id       timestamp\n    stock_sz_000338 2018-01-02          False\n                    2018-01-03          False\n                    2018-01-04          False\n                    2018-01-05          False\n                    2018-01-08          False\n    ...                                   ...\n                    2022-01-17          False\n                    2022-01-18          False\n                    2022-01-19          False\n                    2022-01-20          False\n                    2022-01-21          False\n\n    [987 rows x 1 columns]\n\n.. image:: ../_static/factor_draw.png\n\nFollow :ref:`Extending factor <factor.extending_factor>` to do the funny part.\n\n\nTargetSelector\n------------------------------\nThe class select targets according to Factors.\nYou could calculate factors in the whole market and use selector to choose the targets.\n\n::\n\n    from zvt.contract import IntervalLevel\n    from zvt.factors.target_selector import TargetSelector\n    from zvt.factors.ma.ma_factor import CrossMaFactor\n\n    entity_ids = [\"stock_sz_000338\"]\n    entity_type = \"stock\"\n    start_timestamp = \"2018-01-01\"\n    end_timestamp = \"2019-06-30\"\n    my_selector = TargetSelector(\n        entity_ids=entity_ids, entity_schema=entity_type, start_timestamp=start_timestamp, end_timestamp=end_timestamp\n    )\n    # add the factors\n    my_selector.add_factor(\n        CrossMaFactor(\n            entity_provider=\"em\",\n            provider=\"em\",\n            entity_ids=entity_ids,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            computing_window=10,\n            windows=[5, 10],\n            need_persist=False,\n            level=IntervalLevel.LEVEL_1DAY,\n            adjust_type=\"hfq\",\n        )\n    )\n    my_selector.run()\n    print(my_selector.open_long_df)\n    print(my_selector.open_short_df)\n    my_selector.get_open_long_targets(\"2019-06-27\")\n\nIf not set entity_ids arguments, the selected targets would be in whole market.\nAnd it provides get_open_long_targets function to select targets on the timestamp.\nFor multiple targets backtesting, this pre computed factor would be very fast."
  },
  {
    "path": "docs/source/factor/index.rst",
    "content": "==========\nFactor\n==========\n\n| Without factor, the heart of quant is empty.\n| So, what exactly is factor?\n| zvt makes a concise and unified abstraction of factor:\n\n    In the world of zvt, factor is data describing market.\n    It's computed from from Schema, and save as new Schema if need.\n\n.. toctree::\n    :maxdepth: 2\n\n    factor_concepts\n    extending_factor\n\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": "Welcome to zvt\n==============\n\nContents\n--------\n\n.. toctree::\n    :maxdepth: 2\n\n    install\n    intro\n    data/index\n    factor/index\n    trader/index\n    ml/index\n    drawer/index\n    contributor\n    api/index\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/source/install.rst",
    "content": ".. _install:\n\nInstallation of zvt\n========================\n\nThis part of the documentation covers the installation of zvt.\nThe first step to using any software package is getting it properly installed.\n\n\nPython version support\n----------------------\n\nOfficially Python 3.7, and 3.8.\n\n\n$ python -m pip install -U zvt\n--------------------------------\n\nTo install zvt, simply run this simple command in your terminal of choice::\n\n    $ python -m pip install -U zvt\n\n\nGet the Source Code\n-------------------\n\nzvt is actively developed on GitHub, where the code is\n`always available <https://github.com/zvtvz/zvt>`_.\n\nYou can clone the public repository::\n\n    $ git clone git://github.com/zvtvz/zvt.git\n\nOnce you have a copy of the source, you can embed it in your own Python\npackage, or install it into your site-packages easily::\n\n    $ cd zvt\n    $ python -m pip install .\n"
  },
  {
    "path": "docs/source/intro.rst",
    "content": "==========\nIntro\n==========\n\nThis is a short introduction to zvt, you would learn the basic usage and\nglance a global view here.\n\nRethink market and programming\n------------------------------\nFor practical trading, complex algorithm is fragile, complex algorithm building\non complex facility is more fragile, complex algorithm building on complex\nfacility by complex team is more and more fragile.\n\nzvt want to provide a simple facility for building straightforward algorithm, it\nshould be:\n\n* **use the most basic programming concepts**\n\n* **concise abstraction of the market**\n\n* **correctness is obvious**\n\nCore concepts building zvt\n------------------------------\n| Technologies come and technologies go, but market insight is forever.\n| Your world is built by core concepts inside you, so it's you.\n| zvt world is built by core concepts inside market, so it's zvt.\n| We would show how core concepts building zvt in four aspects：\n\n.. toctree::\n    :maxdepth: 2\n\n    data/index\n    factor/index\n    trader/index\n    ml/index\n"
  },
  {
    "path": "docs/source/ml/index.rst",
    "content": "====================\nMachine learning\n====================\n\n| In today's world, you're not fashion without machine learning.\n| So, what could zvt do with ML?\n| Being a fashion-feeling person, zvt love ML in this way:\n\n    zvt provide a simple facility to define X and y.\n    you could adapt any sophisticated ML library to zvt.\n\nTagger\n------------------------------\n:class:`~.zvt.tag.tag.Tagger` is a computing facility for classifying\nTradableEntity by different dimensions as tag which could be used as\nml category feature.\n\nPeople are divided into groups, things are grouped together.\n\n.. image:: ../_static/tag.png\n\nThe most important part for trading is which category is suitable to trade\nnow. ML could help you find which category perform better in different cases\n——— deterministic factor.\n\nThe tag could be **priori** or calculated from characteristics of the entity ——— some factor.\n\nMLMachine\n------------------------------\nthe ml engine.\n"
  },
  {
    "path": "docs/source/trader/index.rst",
    "content": "==========\nTrader\n==========\n\n| What we want in the end is to trade and make money.\n| So, what exactly is Trader?\n\n    Trader is a simple facility helping you backtesting and generating trading signals\n    using Factor, TargetSelector, MLMachine or you custom free style algorithm.\n\n.. toctree::\n    :maxdepth: 2\n\n    trader_concepts\n    extending_trader\n\n"
  },
  {
    "path": "docs/source/trader/trader_concepts.rst",
    "content": "====================\nTrader\n====================\n\nTrader\n------------------------------\nthe backtest engine using TargetSelector, MLMachine or free style.\n\nTradingSignal\n------------------------------\nthe signal contains information about how to trade."
  },
  {
    "path": "docs/source/usage.rst",
    "content": "====================\nUsage\n====================\n\nHere show some use cases of zvt.\n\nData\n------------------------------\n\nFactor\n------------------------------\n\nBacktesting\n------------------------------\n\nMachine learning\n------------------------------\n\nDraw by intent\n------------------------------\n\nInformer\n------------------------------"
  },
  {
    "path": "docs/storage_config.md",
    "content": "# Storage 配置说明 (Phase 2)\n\n在 `config.json`（或 `zvt_home/config.json`）中可添加 `storage` 配置块，用于自定义存储路径和路由。\n\n## 配置结构\n\n```json\n{\n  \"storage\": {\n    \"base_path\": null,\n    \"path_template\": null,\n    \"storage_routes\": {},\n    \"schema_providers\": {}\n  }\n}\n```\n\n### 字段说明\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| `base_path` | string \\| null | 数据根路径。`null` 时使用 `zvt_env[\"data_path\"]`（即 `zvt_home/data`） |\n| `path_template` | string \\| null | 路径模板。占位符：`{base_path}`, `{provider}`, `{db_name}`, `{storage_id}`。`null` 时使用默认规则 |\n| `storage_routes` | object | 路由覆盖。键格式 `\"provider\\|db_name\"`，值为自定义 `storage_id` |\n| `schema_providers` | object | 无 Recorder 的 schema 的 provider 映射。键为 `db_name`，值为 `[\"provider\"]` 数组。用于 fallback 或 internal schema |\n\n### 默认行为（不配置时）\n\n- `storage_id = \"{provider}_{db_name}\"`（如 `em_stock_meta`）\n- 路径：`{data_path}/{provider}/{provider}_{db_name}.db`\n\n### 示例\n\n**自定义 base_path：**\n```json\n{\n  \"storage\": {\n    \"base_path\": \"/custom/data/path\"\n  }\n}\n```\n\n**自定义 path_template（扁平目录）：**\n```json\n{\n  \"storage\": {\n    \"path_template\": \"{base_path}/{storage_id}.db\"\n  }\n}\n```\n\n**自定义 storage_routes（某 provider 使用不同 storage_id）：**\n```json\n{\n  \"storage\": {\n    \"storage_routes\": {\n      \"qmt|stock_1d_kdata\": \"qmt_realtime_stock_1d\"\n    }\n  }\n}\n```\n\n## Internal Schema（内部 schema）\n\n业务逻辑类数据（如 `zvt_info`、`trader_info`、`stock_tags`）与外部数据源无关，通过 `register_schema(..., internal=True)` 标记为 internal。Internal schema 只做存储路由，不参与 provider 切换。其 provider（如 `zvt`）在 `schema_providers` 中配置，用于确定存储路径。\n"
  },
  {
    "path": "examples/README.MD",
    "content": "The examples are updating with master source code.\n"
  },
  {
    "path": "examples/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "examples/data_runner/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "examples/data_runner/actor_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt import init_log\nfrom zvt.domain import (\n    StockInstitutionalInvestorHolder,\n    StockTopTenFreeHolder,\n    StockActorSummary,\n)\nfrom zvt.utils.recorder_utils import run_data_recorder\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n\n@sched.scheduled_job(\"cron\", hour=1, minute=00, day_of_week=2)\ndef record_actor_data(data_provider=\"em\", entity_provider=\"em\"):\n    run_data_recorder(\n        domain=StockInstitutionalInvestorHolder,\n        data_provider=data_provider,\n        entity_provider=entity_provider,\n        day_data=True,\n    )\n    run_data_recorder(\n        domain=StockTopTenFreeHolder, data_provider=data_provider, entity_provider=entity_provider, day_data=True\n    )\n    run_data_recorder(\n        domain=StockActorSummary, data_provider=data_provider, entity_provider=entity_provider, day_data=True\n    )\n\n\nif __name__ == \"__main__\":\n    init_log(\"actor_runner.log\")\n\n    record_actor_data()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/data_runner/finance_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt import init_log\nfrom zvt.domain import (\n    Stock,\n    StockDetail,\n    FinanceFactor,\n    BalanceSheet,\n    IncomeStatement,\n    CashFlowStatement,\n)\nfrom zvt.utils.recorder_utils import run_data_recorder\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n\n@sched.scheduled_job(\"cron\", hour=1, minute=00, day_of_week=5)\ndef record_actor_data(data_provider=\"eastmoney\", entity_provider=\"eastmoney\"):\n    run_data_recorder(domain=Stock, data_provider=data_provider)\n    run_data_recorder(domain=StockDetail, data_provider=data_provider)\n    run_data_recorder(domain=FinanceFactor, data_provider=data_provider, entity_provider=entity_provider, day_data=True)\n    run_data_recorder(domain=BalanceSheet, data_provider=data_provider, entity_provider=entity_provider, day_data=True)\n    run_data_recorder(\n        domain=IncomeStatement, data_provider=data_provider, entity_provider=entity_provider, day_data=True\n    )\n    run_data_recorder(\n        domain=CashFlowStatement, data_provider=data_provider, entity_provider=entity_provider, day_data=True\n    )\n\n\nif __name__ == \"__main__\":\n    init_log(\"finance_runner.log\")\n\n    record_actor_data()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/data_runner/index_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt import init_log\nfrom zvt.consts import IMPORTANT_INDEX\nfrom zvt.domain import Index, Index1dKdata, IndexStock\nfrom zvt.utils.recorder_utils import run_data_recorder\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n\n# 自行更改定定时运行时间\n@sched.scheduled_job(\"cron\", hour=1, minute=00, day_of_week=5)\ndef record_index():\n    run_data_recorder(domain=Index, data_provider=\"exchange\")\n    # 默认只抓取 国证1000 国证2000 国证成长 国证价值 的组成个股\n    index_ids = [\"index_sz_399311\", \"index_sz_399303\", \"index_sz_399370\", \"index_sz_399371\"]\n    run_data_recorder(domain=IndexStock, data_provider=\"exchange\", entity_provider=\"exchange\", entity_ids=index_ids)\n\n\n@sched.scheduled_job(\"cron\", hour=16, minute=20)\ndef record_index_kdata():\n    run_data_recorder(domain=Index, data_provider=\"em\")\n    run_data_recorder(\n        domain=Index1dKdata, data_provider=\"em\", entity_provider=\"em\", codes=IMPORTANT_INDEX, day_data=True\n    )\n\n\nif __name__ == \"__main__\":\n    init_log(\"index_runner.log\")\n\n    record_index()\n    record_index_kdata()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/data_runner/joinquant_fund_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt import init_log\nfrom zvt.domain import Fund, FundStock, StockValuation\nfrom zvt.utils.recorder_utils import run_data_recorder\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n\n# 周6抓取\n@sched.scheduled_job(\"cron\", hour=10, minute=00, day_of_week=5)\ndef record_fund_data(data_provider=\"joinquant\", entity_provider=\"joinquant\"):\n    # 基金\n    run_data_recorder(domain=Fund, data_provider=data_provider, sleeping_time=0)\n    # 基金持仓\n    run_data_recorder(domain=FundStock, data_provider=data_provider, entity_provider=entity_provider, sleeping_time=0)\n    # 个股估值\n    run_data_recorder(\n        domain=StockValuation,\n        data_provider=data_provider,\n        entity_provider=entity_provider,\n        sleeping_time=0,\n        day_data=True,\n    )\n\n\nif __name__ == \"__main__\":\n    init_log(\"joinquant_fund_runner.log\")\n\n    record_fund_data()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/data_runner/joinquant_kdata_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt.domain import StockTradeDay\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt.utils.recorder_utils import run_data_recorder\nfrom zvt import init_log\nfrom zvt.domain import Stock, Stock1dHfqKdata\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n\n@sched.scheduled_job(\"cron\", hour=15, minute=30)\ndef record_stock_data(data_provider=\"joinquant\", entity_provider=\"joinquant\"):\n    # A股标的\n    run_data_recorder(domain=Stock, data_provider=data_provider, force_update=False)\n    # 交易日\n    run_data_recorder(domain=StockTradeDay, data_provider=data_provider)\n    # A股后复权行情\n    run_data_recorder(\n        domain=Stock1dHfqKdata,\n        data_provider=data_provider,\n        entity_provider=entity_provider,\n        day_data=True,\n        sleeping_time=0,\n    )\n\n\nif __name__ == \"__main__\":\n    init_log(\"joinquant_kdata_runner.log\")\n\n    record_stock_data()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/data_runner/kdata_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom examples.report_utils import inform\nfrom examples.utils import get_hot_topics\nfrom zvt import init_log, zvt_config\nfrom zvt.api.selector import get_entity_ids_by_filter\nfrom zvt.domain import (\n    Stock,\n    Stock1dHfqKdata,\n    Stockhk,\n    Stockhk1dHfqKdata,\n    Block,\n    Block1dKdata,\n    BlockCategory,\n    Index,\n    Index1dKdata,\n    StockNews,\n    LimitUpInfo,\n)\nfrom zvt.informer import EmailInformer\nfrom zvt.utils.time_utils import current_date\nfrom zvt.utils.recorder_utils import run_data_recorder\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n\n@sched.scheduled_job(\"cron\", hour=16, minute=30, day_of_week=\"mon-fri\")\ndef record_stock_news(data_provider=\"em\"):\n    normal_stock_ids = get_entity_ids_by_filter(\n        provider=\"em\", ignore_delist=True, ignore_st=False, ignore_new_stock=False\n    )\n\n    run_data_recorder(\n        entity_ids=normal_stock_ids,\n        day_data=True,\n        domain=StockNews,\n        data_provider=data_provider,\n        force_update=False,\n        sleeping_time=2,\n    )\n\n\ndef report_limit_up():\n    latest_data = LimitUpInfo.query_data(order=LimitUpInfo.timestamp.desc(), limit=1, return_type=\"domain\")\n    timestamp = latest_data[0].timestamp\n    df = LimitUpInfo.query_data(start_timestamp=timestamp, end_timestamp=timestamp, columns=[\"code\", \"name\", \"reason\"])\n    df[\"reason\"] = df[\"reason\"].str.split(\"+\")\n    print(df)\n    EmailInformer().send_message(zvt_config[\"email_username\"], f\"{timestamp} 热门报告\", f\"{df}\")\n\n\ndef report_hot_topics():\n    topics_long = get_hot_topics(days_ago=20)\n    topics_short = get_hot_topics(days_ago=5)\n\n    set1 = set(topics_long.keys())\n    set2 = set(topics_short.keys())\n\n    same = set1 & set2\n    print(same)\n\n    old_topics = set1 - set2\n    print(old_topics)\n    new_topics = set2 - set1\n    print(new_topics)\n\n    msg = f\"\"\"\n  一直热门:{same}\n  ---:{old_topics}\n  +++:{new_topics}\n\n  长期统计:{topics_long}\n  短期统计:{topics_short}\n    \"\"\"\n\n    print(msg)\n    EmailInformer().send_message(zvt_config[\"email_username\"], f\"{current_date()} 热门报告\", msg)\n\n\n@sched.scheduled_job(\"cron\", hour=15, minute=30, day_of_week=\"mon-fri\")\ndef record_stock_data(data_provider=\"em\", entity_provider=\"em\", sleeping_time=0):\n    email_action = EmailInformer()\n    # 涨停数据\n    run_data_recorder(domain=LimitUpInfo, data_provider=None, force_update=False)\n    report_limit_up()\n\n    # A股指数\n    run_data_recorder(domain=Index, data_provider=data_provider, force_update=False)\n    # A股指数行情\n    run_data_recorder(\n        domain=Index1dKdata,\n        data_provider=data_provider,\n        entity_provider=entity_provider,\n        day_data=True,\n        sleeping_time=sleeping_time,\n    )\n\n    # 板块(概念，行业)\n    run_data_recorder(domain=Block, entity_provider=entity_provider, data_provider=entity_provider, force_update=False)\n    # 板块行情(概念，行业)\n    run_data_recorder(\n        domain=Block1dKdata,\n        entity_provider=entity_provider,\n        data_provider=entity_provider,\n        day_data=True,\n        sleeping_time=sleeping_time,\n    )\n    # run_data_recorder(\n    #     domain=BlockStock,\n    #     entity_provider=entity_provider,\n    #     data_provider=entity_provider,\n    #     sleeping_time=sleeping_time,\n    # )\n\n    # 报告新概念和行业\n    df = Block.query_data(\n        filters=[Block.category == BlockCategory.concept.value],\n        order=Block.list_date.desc(),\n        index=\"entity_id\",\n        limit=7,\n    )\n\n    inform(\n        action=email_action,\n        entity_ids=df.index.tolist(),\n        target_date=current_date(),\n        title=\"report 新概念\",\n        entity_provider=entity_provider,\n        entity_type=\"block\",\n        em_group=None,\n        em_group_over_write=False,\n    )\n\n    # A股标的\n    run_data_recorder(domain=Stock, data_provider=data_provider, force_update=False)\n    # A股后复权行情\n    normal_stock_ids = get_entity_ids_by_filter(\n        provider=\"em\", ignore_delist=True, ignore_st=False, ignore_new_stock=False\n    )\n\n    run_data_recorder(\n        entity_ids=normal_stock_ids,\n        domain=Stock1dHfqKdata,\n        data_provider=data_provider,\n        entity_provider=entity_provider,\n        day_data=True,\n        sleeping_time=sleeping_time,\n        return_unfinished=True,\n    )\n\n\n@sched.scheduled_job(\"cron\", hour=16, minute=30, day_of_week=\"mon-fri\")\ndef record_stockhk_data(data_provider=\"em\", entity_provider=\"em\", sleeping_time=2):\n    # 港股标的\n    run_data_recorder(domain=Stockhk, data_provider=data_provider, force_update=False)\n    # 港股后复权行情\n    df = Stockhk.query_data(filters=[Stockhk.south == True], index=\"entity_id\")\n    run_data_recorder(\n        domain=Stockhk1dHfqKdata,\n        entity_ids=df.index.tolist(),\n        data_provider=data_provider,\n        entity_provider=entity_provider,\n        day_data=True,\n        sleeping_time=sleeping_time,\n    )\n\n\nif __name__ == \"__main__\":\n    init_log(\"kdata_runner.log\")\n\n    record_stock_data()\n    record_stockhk_data()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/data_runner/sina_data_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt import init_log\nfrom zvt.domain import *\nfrom zvt.utils.recorder_utils import run_data_recorder\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n\n@sched.scheduled_job(\"cron\", hour=15, minute=30, day_of_week=3)\ndef record_block():\n    run_data_recorder(domain=Block, data_provider=\"sina\")\n    run_data_recorder(domain=Block, data_provider=\"sina\", entity_provider=\"sina\")\n\n\n@sched.scheduled_job(\"cron\", hour=15, minute=30)\ndef record_money_flow():\n    run_data_recorder(domain=BlockMoneyFlow, data_provider=\"sina\", entity_provider=\"sina\", day_data=True)\n\n\nif __name__ == \"__main__\":\n    init_log(\"sina_data_runner.log\")\n\n    record_block()\n    record_money_flow()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/data_runner/trading_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\nfrom sqlalchemy import or_, and_\n\nfrom examples.report_utils import inform\nfrom zvt import init_log\nfrom zvt.api.kdata import get_latest_kdata_date\nfrom zvt.api.selector import get_big_players\nfrom zvt.domain import (\n    DragonAndTiger,\n    Stock1dHfqKdata,\n)\nfrom zvt.informer import EmailInformer\nfrom zvt.utils.recorder_utils import run_data_recorder\nfrom zvt.utils.time_utils import date_time_by_interval, current_date, to_pd_timestamp\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n\n@sched.scheduled_job(\"cron\", hour=18, minute=00, day_of_week=\"mon-fri\")\ndef record_dragon_tiger(data_provider=\"em\", entity_provider=\"em\", sleeping_time=2):\n    # 龙虎榜数据\n    run_data_recorder(\n        domain=DragonAndTiger,\n        data_provider=data_provider,\n        entity_provider=entity_provider,\n        day_data=True,\n        sleeping_time=sleeping_time,\n    )\n\n    email_action = EmailInformer()\n    # recent year\n    start_timestamp = date_time_by_interval(current_date(), -400)\n    # 最近一年牛x的营业部\n    players = get_big_players(start_timestamp=start_timestamp)\n\n    # 最近30天有牛x的营业部上榜的个股\n    recent_date = date_time_by_interval(current_date(), -30)\n    selected = []\n    for player in players:\n        filters = [\n            or_(\n                and_(DragonAndTiger.dep1 == player, DragonAndTiger.dep1_rate >= 5),\n                and_(DragonAndTiger.dep2 == player, DragonAndTiger.dep2_rate >= 5),\n                and_(DragonAndTiger.dep3 == player, DragonAndTiger.dep3_rate >= 5),\n                and_(DragonAndTiger.dep4 == player, DragonAndTiger.dep4_rate >= 5),\n                and_(DragonAndTiger.dep5 == player, DragonAndTiger.dep5_rate >= 5),\n            )\n        ]\n        df = DragonAndTiger.query_data(\n            start_timestamp=recent_date,\n            filters=filters,\n            columns=[DragonAndTiger.timestamp, DragonAndTiger.entity_id, DragonAndTiger.code, DragonAndTiger.name],\n            index=\"entity_id\",\n        )\n        selected = selected + df.index.tolist()\n\n    if selected:\n        selected = list(set(selected))\n\n    target_date = get_latest_kdata_date(provider=data_provider, entity_type=\"stock\", adjust_type=\"hfq\")\n    df = Stock1dHfqKdata.query_data(\n        provider=data_provider,\n        entity_ids=selected,\n        filters=[\n            Stock1dHfqKdata.turnover_rate > 0.02,\n            Stock1dHfqKdata.timestamp == to_pd_timestamp(target_date),\n            Stock1dHfqKdata.turnover > 300000000,\n        ],\n        index=[\"entity_id\"],\n    )\n    inform(\n        action=email_action,\n        entity_ids=df.index.tolist(),\n        target_date=current_date(),\n        title=\"report 龙虎榜\",\n        entity_provider=entity_provider,\n        entity_type=\"stock\",\n        em_group=\"重要指数\",\n        em_group_over_write=False,\n    )\n\n\nif __name__ == \"__main__\":\n    init_log(\"trading_runner.log\")\n\n    record_dragon_tiger()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/factors/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "examples/factors/boll_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import Optional, List\n\nimport pandas as pd\nfrom ta.volatility import BollingerBands\n\nfrom zvt.contract.factor import Transformer\nfrom zvt.factors.technical_factor import TechnicalFactor\n\n\nclass BollTransformer(Transformer):\n    def transform_one(self, entity_id, df: pd.DataFrame) -> pd.DataFrame:\n        indicator_bb = BollingerBands(close=df[\"close\"], window=20, window_dev=2)\n\n        # Add Bollinger Bands features\n        df[\"bb_bbm\"] = indicator_bb.bollinger_mavg()\n        df[\"bb_bbh\"] = indicator_bb.bollinger_hband()\n        df[\"bb_bbl\"] = indicator_bb.bollinger_lband()\n\n        # Add Bollinger Band high indicator\n        df[\"bb_bbhi\"] = indicator_bb.bollinger_hband_indicator()\n\n        # Add Bollinger Band low indicator\n        df[\"bb_bbli\"] = indicator_bb.bollinger_lband_indicator()\n\n        # Add Width Size Bollinger Bands\n        df[\"bb_bbw\"] = indicator_bb.bollinger_wband()\n\n        # Add Percentage Bollinger Bands\n        df[\"bb_bbp\"] = indicator_bb.bollinger_pband()\n        return df\n\n\nclass BollFactor(TechnicalFactor):\n    transformer = BollTransformer()\n\n    def drawer_factor_df_list(self) -> Optional[List[pd.DataFrame]]:\n        return [self.factor_df[[\"bb_bbm\", \"bb_bbh\", \"bb_bbl\"]]]\n\n    def compute_result(self):\n        super().compute_result()\n        self.result_df = (self.factor_df[\"bb_bbli\"] - self.factor_df[\"bb_bbhi\"]).to_frame(name=\"filter_result\")\n        self.result_df[self.result_df == 0] = None\n        self.result_df[self.result_df == 1] = True\n        self.result_df[self.result_df == -1] = False\n\n\nif __name__ == \"__main__\":\n    from zvt.domain import Stock1dHfqKdata\n\n    provider = \"em\"\n    entity_ids = [\"stock_sz_000338\", \"stock_sh_601318\"]\n    Stock1dHfqKdata.record_data(entity_ids=entity_ids, provider=provider)\n    factor = BollFactor(\n        entity_ids=entity_ids, provider=provider, entity_provider=provider, start_timestamp=\"2019-01-01\"\n    )\n    factor.draw(show=True)\n\n    from zvt.domain import Stock30mHfqKdata\n\n    provider = \"em\"\n    entity_ids = [\"stock_sz_000338\", \"stock_sh_601318\"]\n\n    Stock30mHfqKdata.record_data(entity_ids=entity_ids, provider=provider)\n    factor = BollFactor(\n        entity_ids=entity_ids, provider=provider, entity_provider=provider, start_timestamp=\"2021-01-01\"\n    )\n    factor.draw(show=True)\n"
  },
  {
    "path": "examples/factors/fundamental_selector.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain import BalanceSheet\nfrom zvt.factors.fundamental.finance_factor import GoodCompanyFactor\nfrom zvt.factors.target_selector import TargetSelector\n\n\nclass FundamentalSelector(TargetSelector):\n    def init_factors(self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, level):\n        # 核心资产=(高ROE 高现金流 高股息 低应收 低资本开支 低财务杠杆 有增长)\n        # 高roe 高现金流 低财务杠杆 有增长\n        factor1 = GoodCompanyFactor(\n            entity_ids=entity_ids,\n            codes=codes,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            provider=\"eastmoney\",\n        )\n\n        self.factors.append(factor1)\n\n        # 高股息 低应收\n        factor2 = GoodCompanyFactor(\n            data_schema=BalanceSheet,\n            entity_ids=entity_ids,\n            codes=codes,\n            columns=[BalanceSheet.accounts_receivable],\n            filters=[BalanceSheet.accounts_receivable <= 0.3 * BalanceSheet.total_current_assets],\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            provider=\"eastmoney\",\n            col_period_threshold=None,\n        )\n        self.factors.append(factor2)\n\n\nif __name__ == \"__main__\":\n    selector: TargetSelector = FundamentalSelector(start_timestamp=\"2015-01-01\", end_timestamp=\"2019-06-30\")\n    selector.run()\n    print(selector.get_targets(\"2019-06-30\"))\n"
  },
  {
    "path": "examples/factors/tech_factor.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom typing import Type, List, Union\n\nimport pandas as pd\n\nfrom zvt.contract import AdjustType, TradableEntity, IntervalLevel\nfrom zvt.contract.factor import Transformer, Accumulator\nfrom zvt.domain import Stock\nfrom zvt.factors.macd.macd_factor import MacdFactor\nfrom zvt.factors.transformers import CrossMaTransformer\n\n\nclass BullAndUpFactor(MacdFactor):\n    def __init__(\n        self,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        transformer: Transformer = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n        adjust_type: Union[AdjustType, str] = None,\n        turnover_threshold=400000000,\n        turnover_rate_threshold=0.02,\n    ) -> None:\n        self.turnover_threshold = turnover_threshold\n        self.turnover_rate_threshold = turnover_rate_threshold\n\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n        )\n\n    def compute_result(self):\n        super().compute_result()\n        t = CrossMaTransformer(windows=[5, 120, 250])\n        self.factor_df = t.transform(self.factor_df)\n        s = (self.factor_df[\"turnover\"] > self.turnover_threshold) & (\n            self.factor_df[\"turnover_rate\"] > self.turnover_rate_threshold\n        )\n        self.result_df = (self.factor_df[\"filter_result\"] & self.factor_df[\"bull\"] & s).to_frame(name=\"filter_result\")\n"
  },
  {
    "path": "examples/hot.json",
    "content": "{\n  \"减肥药\": [\n    \"减肥药\"\n  ],\n  \"房地产\": [\n    \"房地产\",\n    \"新型城镇化\",\n    \"棚改\",\n    \"建材\"\n  ],\n  \"新型工业化\": [\n    \"新型工业化\",\n    \"工业母机\"\n  ],\n  \"华为\": [\n    \"华为\",\n    \"mate60 pro,mate\",\n    \"星闪\",\n    \"问界\",\n    \"麒麟\",\n    \"昇腾\",\n    \"鸿蒙\"\n  ],\n  \"新能源\": [\n    \"新能源\",\n    \"锂电,锂电池\",\n    \"钠离子电池\",\n    \"光伏\",\n    \"太阳能\",\n    \"储能\",\n    \"TOPCON电池\",\n    \"风电\",\n    \"核电\"\n  ],\n  \"新能车\": [\n    \"新能车,新能源汽车\",\n    \"整车,汽车整车\",\n    \"汽车零部件,汽车零件\",\n    \"无人驾驶\",\n    \"压铸一体化,一体化压铸\"\n  ],\n  \"人工智能\": [\n    \"人工智能,AI\",\n    \"GPT,CHATGPT\",\n    \"算力\"\n  ],\n  \"机器人\": [\n    \"机器人\",\n    \"减速器\",\n    \"伺服,伺服系统\",\n    \"控制系统\",\n    \"电机\"\n  ],\n  \"核心资产\": [\n    \"核心资产\",\n    \"消费,白酒,食品,饮料\",\n    \"白马\",\n    \"沪深300\",\n    \"基金重仓\",\n    \"上证50\"\n  ],\n  \"一带一路\": [\n    \"一带一路\",\n    \"人民币国际化\",\n    \"跨境支付\"\n  ]\n}\n"
  },
  {
    "path": "examples/intent/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "examples/intent/intent.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.intent import compare\nfrom zvt.domain import Indexus1dKdata, Index, Indexus, Index1dKdata, Currency1dKdata\nfrom zvt.domain import TreasuryYield\n\n\ndef china_vs_us_stock():\n    # 上证，道琼斯指数\n    Index.record_data()\n    Indexus.record_data()\n    Index1dKdata.record_data(entity_id=\"index_sh_000001\")\n    Indexus1dKdata.record_data(entity_id=\"indexus_us_SPX\")\n    compare(entity_ids=[\"index_sh_000001\", \"indexus_us_SPX\"], start_timestamp=\"2000-01-01\", scale_value=100)\n\n\ndef us_yield_and_stock():\n    # 美债收益率，道琼斯指数\n    entity_ids = [\"country_galaxy_US\", \"indexus_us_SPX\"]\n    compare(\n        entity_ids=entity_ids,\n        start_timestamp=\"1990-01-01\",\n        scale_value=None,\n        schema_map_columns={TreasuryYield: [\"yield_2\", \"yield_5\"], Indexus1dKdata: [\"close\"]},\n    )\n\n\ndef commodity_and_stock():\n    # 江西铜业，沪铜\n    entity_ids = [\"stock_sh_600362\", \"future_shfe_CU\"]\n    compare(\n        entity_ids=entity_ids,\n        start_timestamp=\"2005-01-01\",\n        scale_value=100,\n    )\n\n\ndef compare_metal():\n    # 沪铜,沪铝,螺纹钢\n    entity_ids = [\"future_shfe_CU\", \"future_shfe_AL\", \"future_shfe_RB\"]\n    compare(\n        entity_ids=entity_ids,\n        start_timestamp=\"2009-04-01\",\n        scale_value=100,\n    )\n\n\ndef compare_udi_and_stock():\n    # 美股指数\n    # Indexus.record_data()\n    entity_ids = [\"indexus_us_NDX\", \"indexus_us_SPX\", \"indexus_us_UDI\"]\n    # Indexus1dKdata.record_data(entity_ids=entity_ids, sleeping_time=0)\n    compare(\n        entity_ids=entity_ids,\n        start_timestamp=\"2015-01-01\",\n        scale_value=100,\n        schema_map_columns={Indexus1dKdata: [\"close\"]},\n    )\n\n\ndef compare_cny_and_stock():\n    Currency1dKdata.record_data(entity_id=\"currency_forex_USDCNYC\")\n    entity_ids = [\"index_sh_000001\", \"currency_forex_USDCNYC\"]\n    compare(\n        entity_ids=entity_ids,\n        start_timestamp=\"2005-01-01\",\n        scale_value=100,\n        schema_map_columns={Currency1dKdata: [\"close\"], Index1dKdata: [\"close\"]},\n    )\n\n\nif __name__ == \"__main__\":\n    # compare_kline()\n    # us_yield_and_stock()\n    # commodity_and_stock()\n    # compare_metal()\n    # compare_udi_and_stock()\n    compare_cny_and_stock()\n"
  },
  {
    "path": "examples/main_line_hidden_tags.json",
    "content": "[\n  \"次新股\"\n]"
  },
  {
    "path": "examples/main_line_tags.json",
    "content": "[\n  \"智能机器\",\n  \"半导体\",\n  \"AI\"\n]"
  },
  {
    "path": "examples/migration.py",
    "content": "# -*- coding: utf-8 -*-\nfrom datetime import datetime\nfrom typing import Dict\n\nfrom pydantic import BaseModel, ConfigDict\nfrom sqlalchemy import Column, String, JSON\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.api import get_db_session\nfrom zvt.contract.register import register_schema\nfrom zvt.contract.schema import Mixin\n\nZvtInfoBase = declarative_base()\n\n\nclass User(Mixin, ZvtInfoBase):\n    __tablename__ = \"user\"\n    added_col = Column(String)\n    json_col = Column(JSON)\n\n\nclass UserModel(BaseModel):\n    model_config = ConfigDict(from_attributes=True)\n\n    id: str\n    entity_id: str\n    timestamp: datetime\n    added_col: str\n    json_col: Dict\n\n\nregister_schema(providers=[\"zvt\"], db_name=\"test\", schema_base=ZvtInfoBase)\n\nif __name__ == \"__main__\":\n    user_model = UserModel(\n        id=\"user_cn_jack_2020-01-01\",\n        entity_id=\"user_cn_jack\",\n        timestamp=\"2020-01-01\",\n        added_col=\"test\",\n        json_col={\"a\": 1},\n    )\n    session = get_db_session(provider=\"zvt\", data_schema=User)\n\n    user = session.query(User).filter(User.id == \"user_cn_jack_2020-01-01\").first()\n    print(UserModel.validate(user))\n"
  },
  {
    "path": "examples/ml/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "examples/ml/sgd.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sklearn.linear_model import SGDClassifier, SGDRegressor\nfrom sklearn.pipeline import make_pipeline\nfrom sklearn.preprocessing import StandardScaler\n\nfrom zvt.ml import MaStockMLMachine\n\n\ndef sgd_classification():\n    machine = MaStockMLMachine(data_provider=\"em\", entity_ids=[\"stock_sz_000001\"], label_method=\"behavior_cls\")\n    clf = make_pipeline(StandardScaler(), SGDClassifier(max_iter=1000, tol=1e-3))\n    machine.train(model=clf)\n    machine.predict()\n    machine.draw_result(entity_id=\"stock_sz_000001\")\n\n\ndef sgd_regressor():\n    machine = MaStockMLMachine(data_provider=\"em\", entity_ids=[\"stock_sz_000001\"], label_method=\"raw\")\n    reg = make_pipeline(StandardScaler(), SGDRegressor(max_iter=1000, tol=1e-3))\n    machine.train(model=reg)\n    machine.predict()\n    machine.draw_result(entity_id=\"stock_sz_000001\")\n\n\nif __name__ == \"__main__\":\n    sgd_classification()\n    sgd_regressor()\n"
  },
  {
    "path": "examples/query_snippet.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import func\n\nfrom zvt.api.selector import get_entity_ids_by_filter\nfrom zvt.contract import Exchange\nfrom zvt.domain import Stock, BlockStock\nfrom zvt.recorders.em import em_api\nfrom zvt.tag.tag_schemas import StockTags\n\n\ndef query_json():\n\n    df = StockTags.query_data(\n        filters=[func.json_extract(StockTags.sub_tags, '$.\"低空经济\"') != None], columns=[StockTags.sub_tags]\n    )\n    print(df)\n\n\ndef get_stocks_has_tag():\n    df = StockTags.query_data(filters=[StockTags.latest.is_(True)], columns=[StockTags.entity_id])\n    return df[\"entity_id\"].tolist()\n\n\ndef get_stocks_without_tag():\n    entity_ids = get_entity_ids_by_filter(provider=\"em\", ignore_delist=True, ignore_st=True, ignore_new_stock=False)\n    stocks_has_tag = get_stocks_has_tag()\n    return list(set(entity_ids) - set(stocks_has_tag))\n\n\ndef get_all_delist_stocks():\n    stocks = []\n    df1 = em_api.get_tradable_list(entity_type=\"stock\", exchange=Exchange.sh)\n    stocks = stocks + df1[\"entity_id\"].tolist()\n    df2 = em_api.get_tradable_list(entity_type=\"stock\", exchange=Exchange.sz)\n    stocks = stocks + df2[\"entity_id\"].tolist()\n    df3 = em_api.get_tradable_list(entity_type=\"stock\", exchange=Exchange.bj)\n    stocks = stocks + df3[\"entity_id\"].tolist()\n    return stocks\n\n\ndef get_block_stocks(name=\"低空经济\"):\n    df = BlockStock.query_data(provider=\"em\", filters=[BlockStock.name == name], columns=[BlockStock.stock_id])\n    return df[\"stock_id\"].tolist()\n\n\ndef get_sub_tag_stocks(tag=\"低空经济\"):\n    df = StockTags.query_data(\n        provider=\"zvt\",\n        filters=[func.json_extract(StockTags.sub_tags, f'$.\"{tag}\"') != None],\n        columns=[StockTags.entity_id],\n    )\n    return df[\"entity_id\"].tolist()\n\n\nif __name__ == \"__main__\":\n    # a = get_block_stocks()\n    # b = get_sub_tag_stocks()\n    # print(set(a) - set(b))\n    print(Stock.query_data(provider=\"em\", return_type=\"dict\"))\n"
  },
  {
    "path": "examples/report_utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\nfrom typing import Type\n\nfrom examples.tag_utils import group_stocks_by_tag, get_main_line_tags, get_main_line_hidden_tags\nfrom examples.utils import msg_group_stocks_by_topic\nfrom zvt import zvt_config\nfrom zvt.api.kdata import get_latest_kdata_date, get_kdata_schema, default_adjust_type\nfrom zvt.api.selector import get_limit_up_stocks\nfrom zvt.api.stats import get_top_performance_entities_by_periods, get_top_volume_entities, TopType\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.api import get_entities, get_entity_schema\nfrom zvt.contract.factor import Factor, TargetType\nfrom zvt.domain import StockNews\nfrom zvt.informer import EmailInformer\nfrom zvt.informer.inform_utils import add_to_eastmoney\nfrom zvt.utils.time_utils import date_time_by_interval\n\nlogger = logging.getLogger(\"__name__\")\n\n\ndef inform(\n    action: EmailInformer,\n    entity_ids,\n    target_date,\n    title,\n    entity_provider,\n    entity_type,\n    em_group,\n    em_group_over_write,\n    em_group_over_write_tag=False,\n    group_by_topic=False,\n    group_by_tag=True,\n    special_hidden_tag=\"北交所\",\n):\n    msg = \"no targets\"\n    if entity_ids:\n        entities = get_entities(\n            provider=entity_provider, entity_type=entity_type, entity_ids=entity_ids, return_type=\"domain\"\n        )\n        entities = [entity for entity in entities if entity.entity_id in entity_ids]\n        print(len(entities))\n        print(len(entity_ids))\n        assert len(entities) == len(entity_ids)\n\n        if group_by_topic and (entity_type == \"stock\"):\n            StockNews.record_data(\n                entity_ids=entity_ids,\n                provider=\"em\",\n                force_update=False,\n                sleeping_time=0.05,\n                day_data=True,\n            )\n\n            msg = msg_group_stocks_by_topic(entities=entities, threshold=1, days_ago=60)\n            logger.info(msg)\n            action.send_message(zvt_config[\"email_username\"], f\"{target_date} {title}\", msg)\n\n        if group_by_tag and (entity_type == \"stock\"):\n            main_line_hidden_tags = get_main_line_hidden_tags()\n            sorted_entities = group_stocks_by_tag(\n                entities=entities, hidden_tags=main_line_hidden_tags + [special_hidden_tag]\n            )\n            msg = \"\"\n            main_line = []\n            others = []\n            special = []\n            main_line_tags = get_main_line_tags()\n            for index, (tag, stocks) in enumerate(sorted_entities):\n                msg = msg + f\"^^^^^^ {tag}[{len(stocks)}/{len(entities)}] ^^^^^^\\n\"\n                msg = msg + \"\\n\".join([f\"{stock.name}({stock.code})\" for stock in stocks]) + \"\\n\"\n                if tag == special_hidden_tag:\n                    special = special + stocks\n                elif (not main_line_tags) and (tag != \"未知\") and (index < 3):\n                    main_line = main_line + stocks\n                elif main_line_tags and (tag in main_line_tags):\n                    main_line = main_line + stocks\n                elif main_line_hidden_tags and (tag in main_line_hidden_tags):\n                    main_line = main_line + stocks\n                else:\n                    others = others + stocks\n\n            # 主线\n            if main_line:\n                codes = [entity.code for entity in main_line]\n                add_to_eastmoney(codes=codes, entity_type=entity_type, group=\"主线\", over_write=em_group_over_write_tag)\n\n            # 其他\n            if others:\n                codes = [entity.code for entity in others]\n                if not em_group:\n                    em_group = \"其他\"\n                add_to_eastmoney(codes=codes, entity_type=entity_type, group=em_group, over_write=em_group_over_write)\n            # 特别处理\n            if special:\n                codes = [entity.code for entity in special]\n                add_to_eastmoney(\n                    codes=codes, entity_type=entity_type, group=special_hidden_tag, over_write=em_group_over_write_tag\n                )\n        else:\n            if em_group:\n                try:\n                    codes = [entity.code for entity in entities]\n                    add_to_eastmoney(\n                        codes=codes, entity_type=entity_type, group=em_group, over_write=em_group_over_write\n                    )\n                except Exception as e:\n                    action.send_message(\n                        zvt_config[\"email_username\"],\n                        f\"{target_date} {title} error\",\n                        f\"{target_date} {title} error: {e}\",\n                    )\n\n            infos = [f\"{entity.name}({entity.code})\" for entity in entities]\n            msg = \"\\n\".join(infos) + \"\\n\"\n\n    logger.info(msg)\n    action.send_message(zvt_config[\"email_username\"], f\"{target_date} {title}\", msg)\n\n\ndef report_targets(\n    factor_cls: Type[Factor],\n    entity_provider,\n    data_provider,\n    title,\n    entity_type=\"stock\",\n    informer: EmailInformer = None,\n    em_group=None,\n    em_group_over_write=True,\n    em_group_over_write_tag=False,\n    filter_by_volume=True,\n    adjust_type=None,\n    start_timestamp=\"2019-01-01\",\n    **factor_kv,\n):\n    logger.info(\n        f\"entity_provider: {entity_provider}, data_provider: {data_provider}, entity_type: {entity_type}, start_timestamp: {start_timestamp}\"\n    )\n    error_count = 0\n\n    while error_count <= 10:\n        try:\n            if not adjust_type:\n                adjust_type = default_adjust_type(entity_type=entity_type)\n\n            target_date = get_latest_kdata_date(\n                provider=data_provider, entity_type=entity_type, adjust_type=adjust_type\n            )\n            logger.info(f\"target_date :{target_date}\")\n\n            current_entity_pool = None\n            if filter_by_volume:\n                # 成交量\n                vol_df = get_top_volume_entities(\n                    entity_type=entity_type,\n                    start_timestamp=date_time_by_interval(target_date, -30),\n                    end_timestamp=target_date,\n                    adjust_type=adjust_type,\n                    pct=0.4,\n                    data_provider=data_provider,\n                )\n                current_entity_pool = vol_df.index.tolist()\n                logger.info(f\"current_entity_pool({len(current_entity_pool)}): {current_entity_pool}\")\n\n            kdata_schema = get_kdata_schema(entity_type, level=IntervalLevel.LEVEL_1DAY, adjust_type=adjust_type)\n            filters = []\n            if \"turnover_threshold\" in factor_kv:\n                filters = filters + [kdata_schema.turnover >= factor_kv.get(\"turnover_threshold\")]\n            if \"turnover_rate_threshold\" in factor_kv:\n                filters = filters + [kdata_schema.turnover_rate >= factor_kv.get(\"turnover_rate_threshold\")]\n            if filters:\n                filters = filters + [kdata_schema.timestamp == target_date]\n                kdata_df = kdata_schema.query_data(\n                    provider=data_provider, filters=filters, columns=[\"entity_id\", \"timestamp\"], index=\"entity_id\"\n                )\n                if current_entity_pool:\n                    current_entity_pool = set(current_entity_pool) & set(kdata_df.index.tolist())\n                else:\n                    current_entity_pool = kdata_df.index.tolist()\n\n            if \"entity_ids\" in factor_kv:\n                if current_entity_pool:\n                    current_entity_pool = set(current_entity_pool) & set(factor_kv.pop(\"entity_ids\"))\n                else:\n                    current_entity_pool = set(factor_kv.pop(\"entity_ids\"))\n\n            # add the factor\n            entity_schema = get_entity_schema(entity_type=entity_type)\n            tech_factor = factor_cls(\n                entity_schema=entity_schema,\n                entity_provider=entity_provider,\n                provider=data_provider,\n                entity_ids=current_entity_pool,\n                start_timestamp=start_timestamp,\n                end_timestamp=target_date,\n                adjust_type=adjust_type,\n                **factor_kv,\n            )\n\n            long_stocks = tech_factor.get_targets(timestamp=target_date, target_type=TargetType.positive)\n\n            inform(\n                informer,\n                entity_ids=long_stocks,\n                target_date=target_date,\n                title=f\"{entity_type} {title}({len(long_stocks)})\",\n                entity_provider=entity_provider,\n                entity_type=entity_type,\n                em_group=em_group,\n                em_group_over_write=em_group_over_write,\n                em_group_over_write_tag=em_group_over_write_tag,\n            )\n\n            break\n        except Exception as e:\n            logger.exception(\"report error:{}\".format(e))\n            time.sleep(60 * 3)\n            error_count = error_count + 1\n            if error_count == 10:\n                informer.send_message(\n                    zvt_config[\"email_username\"],\n                    f\"report {entity_type}{factor_cls.__name__} error\",\n                    f\"report {entity_type}{factor_cls.__name__} error: {e}\",\n                )\n\n\ndef report_top_entities(\n    entity_provider,\n    data_provider,\n    periods=None,\n    ignore_new_stock=True,\n    ignore_st=True,\n    entity_ids=None,\n    entity_type=\"stock\",\n    adjust_type=None,\n    top_count=30,\n    turnover_threshold=100000000,\n    turnover_rate_threshold=0.02,\n    informer: EmailInformer = None,\n    title=\"最强\",\n    em_group=None,\n    em_group_over_write=True,\n    em_group_over_write_tag=False,\n    return_type=TopType.positive,\n    include_limit_up=False,\n):\n    error_count = 0\n\n    if not adjust_type:\n        adjust_type = default_adjust_type(entity_type=entity_type)\n\n    while error_count <= 10:\n        try:\n            target_date = get_latest_kdata_date(\n                provider=data_provider, entity_type=entity_type, adjust_type=adjust_type\n            )\n\n            selected, real_period = get_top_performance_entities_by_periods(\n                entity_provider=entity_provider,\n                data_provider=data_provider,\n                periods=periods,\n                ignore_new_stock=ignore_new_stock,\n                ignore_st=ignore_st,\n                entity_ids=entity_ids,\n                entity_type=entity_type,\n                adjust_type=adjust_type,\n                top_count=top_count,\n                turnover_threshold=turnover_threshold,\n                turnover_rate_threshold=turnover_rate_threshold,\n                return_type=return_type,\n            )\n\n            if include_limit_up and (entity_type == \"stock\"):\n                limit_up_stocks = get_limit_up_stocks(timestamp=target_date)\n                if limit_up_stocks:\n                    selected = list(set(selected + limit_up_stocks))\n\n            inform(\n                informer,\n                entity_ids=selected,\n                target_date=target_date,\n                title=f\"{entity_type} {title}({len(selected)})\",\n                entity_provider=entity_provider,\n                entity_type=entity_type,\n                em_group=em_group,\n                em_group_over_write=em_group_over_write,\n                em_group_over_write_tag=em_group_over_write_tag,\n            )\n            return real_period\n        except Exception as e:\n            logger.exception(\"report error:{}\".format(e))\n            time.sleep(30)\n            error_count = error_count + 1\n\n\nif __name__ == \"__main__\":\n    report_top_entities(\n        entity_type=\"block\",\n        entity_provider=\"em\",\n        data_provider=\"em\",\n        top_count=10,\n        periods=[365, 750],\n        ignore_new_stock=False,\n        ignore_st=False,\n        adjust_type=None,\n        turnover_threshold=50000000,\n        turnover_rate_threshold=0.005,\n        em_group=None,\n        em_group_over_write=False,\n        return_type=TopType.negative,\n    )\n\n# the __all__ is generated\n__all__ = [\"report_targets\", \"report_top_entities\"]\n"
  },
  {
    "path": "examples/reports/__init__.py",
    "content": "# -*- coding: utf-8 -*-\nimport datetime\nimport json\nimport os\nfrom typing import List\n\nfrom sqlalchemy import or_\n\nfrom zvt.api.utils import float_to_pct_str\nfrom zvt.contract import ActorType\nfrom zvt.domain import FinanceFactor, BalanceSheet, IncomeStatement, Stock, StockActorSummary\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_pd_timestamp, now_date_time_str\n\n\ndef get_subscriber_emails():\n    emails_file = os.path.abspath(os.path.join(os.path.dirname(__file__), \"subscriber_emails.json\"))\n    with open(emails_file) as f:\n        return json.load(f)\n\n\ndef risky_company(the_date=to_pd_timestamp(now_date_time_str()), income_yoy=-0.1, profit_yoy=-0.1, entity_ids=None):\n    codes = []\n    start_timestamp = to_pd_timestamp(the_date) - datetime.timedelta(130)\n    # 营收降，利润降,流动比率低，速动比率低\n    finance_filter = or_(\n        FinanceFactor.op_income_growth_yoy < income_yoy,\n        FinanceFactor.net_profit_growth_yoy <= profit_yoy,\n        FinanceFactor.current_ratio < 0.7,\n        FinanceFactor.quick_ratio < 0.5,\n    )\n    df = FinanceFactor.query_data(\n        entity_ids=entity_ids, start_timestamp=start_timestamp, filters=[finance_filter], columns=[\"code\"]\n    )\n    if pd_is_not_null(df):\n        codes = codes + df.code.tolist()\n\n    # 高应收，高存货，高商誉\n    balance_filter = (\n        BalanceSheet.accounts_receivable + BalanceSheet.inventories + BalanceSheet.goodwill\n    ) > BalanceSheet.total_equity\n    df = BalanceSheet.query_data(\n        entity_ids=entity_ids, start_timestamp=start_timestamp, filters=[balance_filter], columns=[\"code\"]\n    )\n    if pd_is_not_null(df):\n        codes = codes + df.code.tolist()\n\n    # 应收>利润*1/2\n    df1 = BalanceSheet.query_data(\n        entity_ids=entity_ids,\n        start_timestamp=start_timestamp,\n        columns=[BalanceSheet.code, BalanceSheet.accounts_receivable],\n    )\n    if pd_is_not_null(df1):\n        df1.drop_duplicates(subset=\"code\", keep=\"last\", inplace=True)\n        df1 = df1.set_index(\"code\", drop=True).sort_index()\n\n    df2 = IncomeStatement.query_data(\n        entity_ids=entity_ids,\n        start_timestamp=start_timestamp,\n        columns=[IncomeStatement.code, IncomeStatement.net_profit],\n    )\n    if pd_is_not_null(df2):\n        df2.drop_duplicates(subset=\"code\", keep=\"last\", inplace=True)\n        df2 = df2.set_index(\"code\", drop=True).sort_index()\n\n    if pd_is_not_null(df1) and pd_is_not_null(df2):\n        codes = codes + df1[df1.accounts_receivable > df2.net_profit / 2].index.tolist()\n\n    return list(set(codes))\n\n\ndef stocks_with_info(stocks: List[Stock]):\n    infos = []\n    for stock in stocks:\n        info = f\"{stock.name}({stock.code})\"\n        summary: List[StockActorSummary] = StockActorSummary.query_data(\n            entity_id=stock.entity_id,\n            order=StockActorSummary.timestamp.desc(),\n            filters=[StockActorSummary.actor_type == ActorType.raised_fund.value],\n            limit=1,\n            return_type=\"domain\",\n        )\n        if summary:\n            info = (\n                info\n                + f\"([{summary[0].timestamp}]共{summary[0].actor_count}家基金持股占比:{float_to_pct_str(summary[0].holding_ratio)}, 变化: {float_to_pct_str(summary[0].change_ratio)})\"\n            )\n\n        summary: List[StockActorSummary] = StockActorSummary.query_data(\n            entity_id=stock.entity_id,\n            order=StockActorSummary.timestamp.desc(),\n            filters=[StockActorSummary.actor_type == ActorType.qfii.value],\n            limit=1,\n            return_type=\"domain\",\n        )\n        if summary:\n            info = (\n                info\n                + f\"([{summary[0].timestamp}]共{summary[0].actor_count}家qfii持股占比:{float_to_pct_str(summary[0].holding_ratio)}, 变化: {float_to_pct_str(summary[0].change_ratio)})\"\n            )\n\n        infos.append(info)\n    return infos\n\n\nif __name__ == \"__main__\":\n    print(get_subscriber_emails())\n"
  },
  {
    "path": "examples/reports/report_bull.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom examples.factors.tech_factor import BullAndUpFactor\nfrom examples.report_utils import report_targets\nfrom zvt import init_log\nfrom zvt.api.kdata import get_latest_kdata_date\nfrom zvt.api.selector import get_middle_and_big_stock\nfrom zvt.contract import AdjustType\nfrom zvt.informer import EmailInformer\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\nemail_informer = EmailInformer()\n\n\n@sched.scheduled_job(\"cron\", hour=18, minute=0, day_of_week=\"mon-fri\")\ndef report_bull():\n    target_date = get_latest_kdata_date(entity_type=\"stock\", adjust_type=AdjustType.hfq, provider=\"em\")\n    entity_ids = get_middle_and_big_stock(timestamp=target_date)\n\n    report_targets(\n        factor_cls=BullAndUpFactor,\n        entity_provider=\"em\",\n        data_provider=\"em\",\n        title=\"bull股票\",\n        entity_type=\"stock\",\n        informer=email_informer,\n        em_group=\"bull股票\",\n        em_group_over_write=False,\n        filter_by_volume=False,\n        adjust_type=AdjustType.hfq,\n        start_timestamp=\"2019-01-01\",\n        turnover_threshold=300000000,\n        turnover_rate_threshold=0.02,\n        entity_ids=entity_ids,\n    )\n    report_targets(\n        factor_cls=BullAndUpFactor,\n        entity_provider=\"em\",\n        data_provider=\"em\",\n        title=\"bull板块\",\n        entity_type=\"block\",\n        informer=email_informer,\n        em_group=\"bull股票\",\n        em_group_over_write=False,\n        filter_by_volume=False,\n        adjust_type=AdjustType.qfq,\n        start_timestamp=\"2019-01-01\",\n        turnover_threshold=10000000000,\n        turnover_rate_threshold=0.02,\n    )\n\n\nif __name__ == \"__main__\":\n    init_log(\"report_bull.log\")\n\n    report_bull()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/reports/report_core_compay.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom examples.factors.fundamental_selector import FundamentalSelector\nfrom examples.reports import get_subscriber_emails, stocks_with_info\nfrom zvt import init_log, zvt_config\nfrom zvt.contract.api import get_entities\nfrom zvt.domain import Stock\nfrom zvt.factors.target_selector import TargetSelector\nfrom zvt.informer.inform_utils import add_to_eastmoney\nfrom zvt.informer.informer import EmailInformer\nfrom zvt.utils.time_utils import now_pd_timestamp, to_date_time_str\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n\n# 基本面选股 每周一次即可 基本无变化\n@sched.scheduled_job(\"cron\", hour=16, minute=0, day_of_week=\"6\")\ndef report_core_company():\n    while True:\n        error_count = 0\n        email_action = EmailInformer()\n\n        try:\n            # StockTradeDay.record_data(provider='joinquant')\n            # Stock.record_data(provider='joinquant')\n            # FinanceFactor.record_data(provider='eastmoney')\n            # BalanceSheet.record_data(provider='eastmoney')\n\n            target_date = to_date_time_str(now_pd_timestamp())\n\n            my_selector: TargetSelector = FundamentalSelector(start_timestamp=\"2016-01-01\", end_timestamp=target_date)\n            my_selector.run()\n\n            long_targets = my_selector.get_open_long_targets(timestamp=target_date)\n            if long_targets:\n                stocks = get_entities(\n                    provider=\"joinquant\", entity_schema=Stock, entity_ids=long_targets, return_type=\"domain\"\n                )\n\n                # add them to eastmoney\n                try:\n                    codes = [stock.code for stock in stocks]\n                    add_to_eastmoney(codes=codes, entity_type=\"stock\", group=\"core\")\n                except Exception as e:\n                    email_action.send_message(\n                        zvt_config[\"email_username\"],\n                        f\"report_core_company error\",\n                        \"report_core_company error:{}\".format(e),\n                    )\n\n                infos = stocks_with_info(stocks)\n                msg = \"\\n\".join(infos)\n            else:\n                msg = \"no targets\"\n\n            logger.info(msg)\n\n            email_action.send_message(get_subscriber_emails(), f\"{to_date_time_str(target_date)} 核心资产选股结果\", msg)\n            break\n        except Exception as e:\n            logger.exception(\"report_core_company error:{}\".format(e))\n            time.sleep(60 * 3)\n            error_count = error_count + 1\n            if error_count == 10:\n                email_action.send_message(\n                    zvt_config[\"email_username\"], f\"report_core_company error\", \"report_core_company error:{}\".format(e)\n                )\n\n\nif __name__ == \"__main__\":\n    init_log(\"report_core_company.log\")\n\n    report_core_company()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/reports/report_tops.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom examples.report_utils import report_top_entities, inform\nfrom zvt import init_log\nfrom zvt.api.stats import TopType, get_latest_kdata_date\nfrom zvt.contract import AdjustType\nfrom zvt.domain import Block, BlockCategory\nfrom zvt.factors.top_stocks import get_top_stocks\nfrom zvt.informer import EmailInformer\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\nemail_informer = EmailInformer()\n\n\n@sched.scheduled_job(\"cron\", hour=17, minute=0, day_of_week=\"mon-fri\")\ndef report_top_stocks():\n    # compute_top_stocks()\n    provider = \"em\"\n    entity_type = \"stock\"\n    target_date = get_latest_kdata_date(provider=provider, entity_type=entity_type, adjust_type=AdjustType.hfq)\n    selected = get_top_stocks(target_date=target_date, return_type=\"short\")\n\n    inform(\n        email_informer,\n        entity_ids=selected,\n        target_date=target_date,\n        title=f\"stock 短期最强({len(selected)})\",\n        entity_provider=provider,\n        entity_type=entity_type,\n        em_group=\"短期最强\",\n        em_group_over_write=True,\n        em_group_over_write_tag=True,\n    )\n    selected = get_top_stocks(target_date=target_date, return_type=\"long\")\n\n    inform(\n        email_informer,\n        entity_ids=selected,\n        target_date=target_date,\n        title=f\"stock 中期最强({len(selected)})\",\n        entity_provider=provider,\n        entity_type=entity_type,\n        em_group=\"中期最强\",\n        em_group_over_write=True,\n        em_group_over_write_tag=False,\n    )\n\n    # report_top_entities(\n    #     entity_type=\"stock\",\n    #     entity_provider=\"em\",\n    #     data_provider=\"em\",\n    #     periods=[365, 750],\n    #     ignore_new_stock=False,\n    #     ignore_st=True,\n    #     adjust_type=None,\n    #     top_count=25,\n    #     turnover_threshold=100000000,\n    #     turnover_rate_threshold=0.01,\n    #     informer=email_informer,\n    #     em_group=\"谁有我惨\",\n    #     em_group_over_write=True,\n    #     return_type=TopType.negative,\n    # )\n\n\n@sched.scheduled_job(\"cron\", hour=17, minute=30, day_of_week=\"mon-fri\")\ndef report_top_blocks():\n    df = Block.query_data(filters=[Block.category == BlockCategory.industry.value], index=\"entity_id\")\n\n    entity_ids = df.index.tolist()\n    report_top_entities(\n        entity_type=\"block\",\n        entity_provider=\"em\",\n        data_provider=\"em\",\n        periods=[*range(2, 30)],\n        ignore_new_stock=False,\n        ignore_st=False,\n        adjust_type=None,\n        top_count=10,\n        turnover_threshold=0,\n        turnover_rate_threshold=0,\n        informer=email_informer,\n        em_group=\"最强行业\",\n        title=\"最强行业\",\n        em_group_over_write=True,\n        return_type=TopType.positive,\n        entity_ids=entity_ids,\n    )\n\n    df = Block.query_data(filters=[Block.category == BlockCategory.concept.value], index=\"entity_id\")\n    df = df[~df.name.str.contains(\"昨日\")]\n    entity_ids = df.index.tolist()\n    report_top_entities(\n        entity_type=\"block\",\n        entity_provider=\"em\",\n        data_provider=\"em\",\n        periods=[*range(2, 30)],\n        ignore_new_stock=False,\n        ignore_st=False,\n        adjust_type=None,\n        top_count=10,\n        turnover_threshold=0,\n        turnover_rate_threshold=0,\n        informer=email_informer,\n        em_group=\"最强概念\",\n        title=\"最强概念\",\n        em_group_over_write=True,\n        return_type=TopType.positive,\n        entity_ids=entity_ids,\n    )\n\n\n@sched.scheduled_job(\"cron\", hour=17, minute=30, day_of_week=\"mon-fri\")\ndef report_top_stockhks():\n    report_top_entities(\n        entity_type=\"stockhk\",\n        entity_provider=\"em\",\n        data_provider=\"em\",\n        top_count=10,\n        periods=[*range(1, 15)],\n        ignore_new_stock=False,\n        ignore_st=False,\n        adjust_type=None,\n        turnover_threshold=30000000,\n        turnover_rate_threshold=0.01,\n        informer=email_informer,\n        em_group=\"短期最强\",\n        title=\"短期最强\",\n        em_group_over_write=False,\n        return_type=TopType.positive,\n    )\n\n    report_top_entities(\n        entity_type=\"stockhk\",\n        entity_provider=\"em\",\n        data_provider=\"em\",\n        top_count=10,\n        periods=[30, 50],\n        ignore_new_stock=True,\n        ignore_st=False,\n        adjust_type=None,\n        turnover_threshold=30000000,\n        turnover_rate_threshold=0.01,\n        informer=email_informer,\n        em_group=\"中期最强\",\n        title=\"中期最强\",\n        em_group_over_write=False,\n        return_type=TopType.positive,\n    )\n\n    # report_top_entities(\n    #     entity_type=\"stockhk\",\n    #     entity_provider=\"em\",\n    #     data_provider=\"em\",\n    #     top_count=20,\n    #     periods=[365, 750],\n    #     ignore_new_stock=True,\n    #     ignore_st=False,\n    #     adjust_type=None,\n    #     turnover_threshold=50000000,\n    #     turnover_rate_threshold=0.005,\n    #     informer=email_informer,\n    #     em_group=\"谁有我惨\",\n    #     em_group_over_write=False,\n    #     return_type=TopType.negative,\n    # )\n\n\nif __name__ == \"__main__\":\n    init_log(\"report_tops.log\")\n\n    report_top_stocks()\n    # report_top_blocks()\n    report_top_stockhks()\n\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/reports/report_vol_up.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom zvt.factors.ma import VolumeUpMaFactor\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom examples.report_utils import report_targets, inform\nfrom zvt import init_log\nfrom zvt.api.kdata import get_latest_kdata_date\nfrom zvt.contract import AdjustType\nfrom zvt.factors.top_stocks import get_top_stocks\nfrom zvt.informer import EmailInformer\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\nemail_informer = EmailInformer()\n\n\n@sched.scheduled_job(\"cron\", hour=17, minute=0, day_of_week=\"mon-fri\")\ndef report_vol_up_stocks():\n    provider = \"em\"\n    entity_type = \"stock\"\n    target_date = get_latest_kdata_date(provider=provider, entity_type=entity_type, adjust_type=AdjustType.hfq)\n    selected = get_top_stocks(target_date=target_date, return_type=\"small_vol_up\")\n\n    inform(\n        email_informer,\n        entity_ids=selected,\n        target_date=target_date,\n        title=f\"stock 放量突破(半)年线小市值股票({len(selected)})\",\n        entity_provider=provider,\n        entity_type=entity_type,\n        em_group=\"年线股票\",\n        em_group_over_write=True,\n        em_group_over_write_tag=False,\n    )\n    selected = get_top_stocks(target_date=target_date, return_type=\"big_vol_up\")\n\n    inform(\n        email_informer,\n        entity_ids=selected,\n        target_date=target_date,\n        title=f\"stock 放量突破(半)年线大市值股票({len(selected)})\",\n        entity_provider=provider,\n        entity_type=entity_type,\n        em_group=\"年线股票\",\n        em_group_over_write=False,\n        em_group_over_write_tag=False,\n    )\n\n\n@sched.scheduled_job(\"cron\", hour=17, minute=30, day_of_week=\"mon-fri\")\ndef report_vol_up_stockhks():\n    report_targets(\n        factor_cls=VolumeUpMaFactor,\n        entity_provider=\"em\",\n        data_provider=\"em\",\n        informer=email_informer,\n        em_group=\"年线股票\",\n        title=\"放量突破(半)年线港股\",\n        entity_type=\"stockhk\",\n        em_group_over_write=False,\n        filter_by_volume=False,\n        adjust_type=AdjustType.hfq,\n        start_timestamp=\"2021-01-01\",\n        # factor args\n        windows=[120, 250],\n        over_mode=\"or\",\n        up_intervals=60,\n        turnover_threshold=100000000,\n        turnover_rate_threshold=0.01,\n    )\n\n\nif __name__ == \"__main__\":\n    init_log(\"report_vol_up.log\")\n\n    report_vol_up_stocks()\n    report_vol_up_stockhks()\n    sched.start()\n\n    sched._thread.join()\n"
  },
  {
    "path": "examples/reports/subscriber_emails.json",
    "content": "[\n  \"test@qq.com\",\n  \"test1@qq.com\"\n]\n"
  },
  {
    "path": "examples/requirements.txt",
    "content": "zvt >= 0.10.1\napscheduler >= 3.4.0\ntabulate>=0.8.8\nta"
  },
  {
    "path": "examples/research/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "examples/research/dragon_and_tiger.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.selector import get_big_players, get_player_success_rate\nfrom zvt.domain import DragonAndTiger\nfrom zvt.utils.time_utils import date_time_by_interval, current_date\n\nif __name__ == \"__main__\":\n    provider = \"em\"\n    DragonAndTiger.record_data(provider=provider)\n    end_timestamp = date_time_by_interval(current_date(), -60)\n    # recent year\n    start_timestamp = date_time_by_interval(end_timestamp, -400)\n    print(f\"{start_timestamp} to {end_timestamp}\")\n    players = get_big_players(start_timestamp=start_timestamp, end_timestamp=end_timestamp)\n    print(players)\n    df = get_player_success_rate(\n        start_timestamp=start_timestamp, end_timestamp=end_timestamp, intervals=[3, 5, 10], players=players\n    )\n    print(df)\n"
  },
  {
    "path": "examples/research/top_dragon_tiger.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import Optional, Type, List, Union\n\nimport pandas as pd\n\nfrom zvt.api.selector import get_players\nfrom zvt.api.stats import get_top_performance_by_month\nfrom zvt.contract import TradableEntity, IntervalLevel, AdjustType\nfrom zvt.contract.factor import Transformer, Accumulator\nfrom zvt.domain import Stock\nfrom zvt.factors.technical_factor import TechnicalFactor\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import pre_month_start_date, date_time_by_interval\n\n\ndef top_dragon_and_tiger(data_provider=\"em\", start_timestamp=\"2021-01-01\", end_timestamp=\"2022-01-01\"):\n    dfs = []\n    for start_date, end_date, df in get_top_performance_by_month(\n        start_timestamp=start_timestamp, end_timestamp=end_timestamp, list_days=250, data_provider=data_provider\n    ):\n        pre_month_start = pre_month_start_date(start_date)\n        for entity_id in df.index[:30]:\n            players = get_players(\n                entity_id=entity_id,\n                start_timestamp=date_time_by_interval(start_date, 15),\n                end_timestamp=end_timestamp,\n                provider=data_provider,\n                direction=\"in\",\n            )\n            print(players)\n            dfs.append(players)\n\n    player_df = pd.concat(dfs, sort=True)\n    return player_df.sort_index(level=[0, 1])\n\n\nclass DragonTigerFactor(TechnicalFactor):\n    def __init__(\n        self,\n        entity_id: str,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = \"em\",\n        entity_provider: str = \"em\",\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        transformer: Transformer = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n        adjust_type: Union[AdjustType, str] = None,\n    ) -> None:\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            [entity_id],\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n        )\n        self.player_df = get_players(\n            entity_id=entity_id,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            provider=\"em\",\n            direction=\"in\",\n        )\n\n    def drawer_annotation_df(self) -> Optional[pd.DataFrame]:\n        def order_type_flag(df):\n            return \"<br>\".join(df.tolist())\n\n        if pd_is_not_null(self.player_df):\n            annotation_df = self.player_df.copy()\n            annotation_df[\"value\"] = self.factor_df.loc[annotation_df.index][\"close\"]\n            annotation_df[\"flag\"] = annotation_df[[\"dep1\", \"dep2\", \"dep3\", \"dep4\", \"dep5\"]].apply(\n                lambda x: order_type_flag(x), axis=1\n            )\n            annotation_df[\"color\"] = \"#ff7f0e\"\n            return annotation_df\n\n\nif __name__ == \"__main__\":\n    top_dragon_and_tiger()\n    # Stock1dHfqKdata.record_data(entity_id=\"stock_sz_002561\", provider=\"em\")\n    # f = DragonTigerFactor(entity_id=\"stock_sz_002561\", provider=\"em\")\n    # f.draw(show=True)\n"
  },
  {
    "path": "examples/research/top_tags.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.stats import get_top_performance_by_month\nfrom zvt.domain import Stock1dHfqKdata\nfrom zvt.utils.time_utils import date_time_by_interval, month_end_date, is_same_date\n\n\n# 每月涨幅前30，市值90%分布在100亿以下\n# 重复上榜的有1/4左右\n# 连续两个月上榜的1/10左右\ndef top_tags(data_provider=\"em\", start_timestamp=\"2020-01-01\", end_timestamp=\"2021-01-01\"):\n    records = []\n    for _, timestamp, df in get_top_performance_by_month(\n        start_timestamp=start_timestamp, end_timestamp=end_timestamp, list_days=250, data_provider=data_provider\n    ):\n        for entity_id in df.index[:30]:\n            query_timestamp = timestamp\n            while True:\n                kdata = Stock1dHfqKdata.query_data(\n                    provider=data_provider,\n                    entity_id=entity_id,\n                    start_timestamp=query_timestamp,\n                    order=Stock1dHfqKdata.timestamp.asc(),\n                    limit=1,\n                    return_type=\"domain\",\n                )\n                if not kdata or kdata[0].turnover_rate == 0:\n                    if is_same_date(query_timestamp, month_end_date(query_timestamp)):\n                        break\n                    query_timestamp = date_time_by_interval(query_timestamp)\n                    continue\n                cap = kdata[0].turnover / kdata[0].turnover_rate\n                break\n\n            records.append(\n                {\"entity_id\": entity_id, \"timestamp\": timestamp, \"cap\": cap, \"score\": df.loc[entity_id, \"score\"]}\n            )\n\n    return records\n\n\nif __name__ == \"__main__\":\n    print(top_tags())\n"
  },
  {
    "path": "examples/result.json",
    "content": "{}"
  },
  {
    "path": "examples/stock_tags.json",
    "content": "[\n  {\n    \"code\": \"000972\",\n    \"name\": \"中基健康\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002269\",\n    \"name\": \"美邦服饰\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002762\",\n    \"name\": \"金发拉比\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603170\",\n    \"name\": \"宝立食品\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600257\",\n    \"name\": \"大湖股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002803\",\n    \"name\": \"吉宏股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002186\",\n    \"name\": \"全 聚 德\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600365\",\n    \"name\": \"ST通葡\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600252\",\n    \"name\": \"中恒集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002419\",\n    \"name\": \"天虹股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600771\",\n    \"name\": \"广誉远\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600655\",\n    \"name\": \"豫园股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000501\",\n    \"name\": \"武商集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600332\",\n    \"name\": \"白云山\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002183\",\n    \"name\": \"怡 亚 通\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002374\",\n    \"name\": \"中锐股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002342\",\n    \"name\": \"巨力索具\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600382\",\n    \"name\": \"广东明珠\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600696\",\n    \"name\": \"岩石股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603919\",\n    \"name\": \"金徽酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603779\",\n    \"name\": \"威龙股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603777\",\n    \"name\": \"来伊份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600059\",\n    \"name\": \"古越龙山\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000860\",\n    \"name\": \"顺鑫农业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600467\",\n    \"name\": \"好当家\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603299\",\n    \"name\": \"苏盐井神\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000995\",\n    \"name\": \"*ST皇台\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002646\",\n    \"name\": \"天佑德酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000596\",\n    \"name\": \"古井贡酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600559\",\n    \"name\": \"老白干酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600702\",\n    \"name\": \"舍得酒业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601579\",\n    \"name\": \"会稽山\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600153\",\n    \"name\": \"建发股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603589\",\n    \"name\": \"口子窖\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600809\",\n    \"name\": \"山西汾酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600197\",\n    \"name\": \"伊力特\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600238\",\n    \"name\": \"海南椰岛\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000568\",\n    \"name\": \"泸州老窖\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603198\",\n    \"name\": \"迎驾贡酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600199\",\n    \"name\": \"金种子酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000930\",\n    \"name\": \"中粮科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"600779\",\n    \"name\": \"水井坊\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600519\",\n    \"name\": \"贵州茅台\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600381\",\n    \"name\": \"青海春天\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603369\",\n    \"name\": \"今世缘\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000858\",\n    \"name\": \"五 粮 液\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000799\",\n    \"name\": \"酒鬼酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"002304\",\n    \"name\": \"洋河股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600679\",\n    \"name\": \"上海凤凰\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"603983\",\n    \"name\": \"丸美股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301187\",\n    \"name\": \"欧圣电气\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600493\",\n    \"name\": \"凤竹纺织\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605136\",\n    \"name\": \"丽人丽妆\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600448\",\n    \"name\": \"华纺股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600439\",\n    \"name\": \"瑞贝卡\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000521\",\n    \"name\": \"长虹美菱\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603499\",\n    \"name\": \"翔港科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301193\",\n    \"name\": \"家联科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600935\",\n    \"name\": \"华塑股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301376\",\n    \"name\": \"致欧科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"603863\",\n    \"name\": \"松炀资源\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002615\",\n    \"name\": \"哈尔斯\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000850\",\n    \"name\": \"华茂股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"001209\",\n    \"name\": \"洪兴股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001216\",\n    \"name\": \"华瓷股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"001368\",\n    \"name\": \"通达创智\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001238\",\n    \"name\": \"浙江正特\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"000025\",\n    \"name\": \"特力A\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603661\",\n    \"name\": \"恒林股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002059\",\n    \"name\": \"云南旅游\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"001387\",\n    \"name\": \"雪祺电气\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"603600\",\n    \"name\": \"永艺股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600156\",\n    \"name\": \"华升股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601113\",\n    \"name\": \"华鼎股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002731\",\n    \"name\": \"萃华珠宝\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600400\",\n    \"name\": \"红豆股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605180\",\n    \"name\": \"华生科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000592\",\n    \"name\": \"平潭发展\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002357\",\n    \"name\": \"富临运业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603958\",\n    \"name\": \"哈森股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002969\",\n    \"name\": \"嘉美包装\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000428\",\n    \"name\": \"华天酒店\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600882\",\n    \"name\": \"妙可蓝多\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600697\",\n    \"name\": \"欧亚集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300740\",\n    \"name\": \"水羊股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000888\",\n    \"name\": \"峨眉山Ａ\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002356\",\n    \"name\": \"赫美集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000610\",\n    \"name\": \"西安旅游\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605188\",\n    \"name\": \"国光连锁\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000861\",\n    \"name\": \"海印股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603199\",\n    \"name\": \"九华旅游\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600706\",\n    \"name\": \"曲江文旅\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600824\",\n    \"name\": \"益民集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002820\",\n    \"name\": \"桂发祥\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603719\",\n    \"name\": \"良品铺子\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"003000\",\n    \"name\": \"劲仔食品\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600828\",\n    \"name\": \"茂业商业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002561\",\n    \"name\": \"徐家汇\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600280\",\n    \"name\": \"中央商场\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000679\",\n    \"name\": \"大连友谊\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300783\",\n    \"name\": \"三只松鼠\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000017\",\n    \"name\": \"深中华A\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000523\",\n    \"name\": \"红棉股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603518\",\n    \"name\": \"锦泓集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603877\",\n    \"name\": \"太平鸟\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601086\",\n    \"name\": \"国芳集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603231\",\n    \"name\": \"索宝蛋白\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"002719\",\n    \"name\": \"麦趣尔\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000558\",\n    \"name\": \"莱茵体育\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600628\",\n    \"name\": \"新世界\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600630\",\n    \"name\": \"龙头股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600250\",\n    \"name\": \"南京商旅\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605080\",\n    \"name\": \"浙江自然\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603099\",\n    \"name\": \"长白山\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002033\",\n    \"name\": \"丽江股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600593\",\n    \"name\": \"大连圣亚\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002780\",\n    \"name\": \"三夫户外\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000978\",\n    \"name\": \"桂林旅游\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688363\",\n    \"name\": \"华熙生物\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002612\",\n    \"name\": \"朗姿股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300896\",\n    \"name\": \"爱美客\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000715\",\n    \"name\": \"中兴商业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603536\",\n    \"name\": \"惠发食品\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002495\",\n    \"name\": \"佳隆股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"601566\",\n    \"name\": \"九牧王\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603711\",\n    \"name\": \"香飘飘\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"688577\",\n    \"name\": \"浙海德曼\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"003025\",\n    \"name\": \"思进智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603289\",\n    \"name\": \"泰瑞机器\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002708\",\n    \"name\": \"光洋股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300276\",\n    \"name\": \"三丰智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603211\",\n    \"name\": \"晋拓股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603095\",\n    \"name\": \"越剑智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603015\",\n    \"name\": \"弘讯科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"301083\",\n    \"name\": \"百胜智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002931\",\n    \"name\": \"锋龙股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002611\",\n    \"name\": \"东方精工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002527\",\n    \"name\": \"新时达\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002523\",\n    \"name\": \"天桥起重\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603767\",\n    \"name\": \"中马传动\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002031\",\n    \"name\": \"巨轮智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002421\",\n    \"name\": \"达实智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002337\",\n    \"name\": \"赛象科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002347\",\n    \"name\": \"泰尔股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002403\",\n    \"name\": \"爱仕达\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603990\",\n    \"name\": \"麦迪科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603810\",\n    \"name\": \"丰山集团\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"601886\",\n    \"name\": \"江河集团\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002897\",\n    \"name\": \"意华股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002805\",\n    \"name\": \"丰元股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603959\",\n    \"name\": \"百利科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002333\",\n    \"name\": \"罗普斯金\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003027\",\n    \"name\": \"同兴环保\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002129\",\n    \"name\": \"TCL中环\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002459\",\n    \"name\": \"晶澳科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600732\",\n    \"name\": \"爱旭股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605366\",\n    \"name\": \"宏柏新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000635\",\n    \"name\": \"英力特\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"603026\",\n    \"name\": \"胜华新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"605399\",\n    \"name\": \"晨光新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002211\",\n    \"name\": \"宏达新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600207\",\n    \"name\": \"安彩高科\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002487\",\n    \"name\": \"大金重工\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001239\",\n    \"name\": \"永达股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603312\",\n    \"name\": \"西典新能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"688503\",\n    \"name\": \"聚和材料\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"002868\",\n    \"name\": \"绿康生化\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600819\",\n    \"name\": \"耀皮玻璃\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603398\",\n    \"name\": \"沐邦高科\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002466\",\n    \"name\": \"天齐锂业\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603051\",\n    \"name\": \"鹿山新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688408\",\n    \"name\": \"中信博\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600499\",\n    \"name\": \"科达制造\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"688472\",\n    \"name\": \"阿特斯\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"001212\",\n    \"name\": \"中旗新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605389\",\n    \"name\": \"长龄液压\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002176\",\n    \"name\": \"江特电机\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000546\",\n    \"name\": \"金圆股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"301152\",\n    \"name\": \"天力锂能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"002132\",\n    \"name\": \"恒星科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603276\",\n    \"name\": \"恒兴新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"002738\",\n    \"name\": \"中矿资源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"688063\",\n    \"name\": \"派能科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002641\",\n    \"name\": \"公元股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300769\",\n    \"name\": \"德方纳米\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002192\",\n    \"name\": \"融捷股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000657\",\n    \"name\": \"中钨高新\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"002667\",\n    \"name\": \"威领股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000982\",\n    \"name\": \"中银绒业\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002326\",\n    \"name\": \"永太科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002665\",\n    \"name\": \"首航高科\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000695\",\n    \"name\": \"滨海能源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600586\",\n    \"name\": \"金晶科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603097\",\n    \"name\": \"江苏华辰\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002012\",\n    \"name\": \"凯恩股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000762\",\n    \"name\": \"西藏矿业\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"300125\",\n    \"name\": \"聆达股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"605378\",\n    \"name\": \"野马电池\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605158\",\n    \"name\": \"华达新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002756\",\n    \"name\": \"永兴材料\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603200\",\n    \"name\": \"上海洗霸\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603663\",\n    \"name\": \"三祥新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000155\",\n    \"name\": \"川能动力\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300473\",\n    \"name\": \"德尔股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301511\",\n    \"name\": \"德福科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"300530\",\n    \"name\": \"领湃科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002882\",\n    \"name\": \"金龙羽\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002341\",\n    \"name\": \"新纶新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"301238\",\n    \"name\": \"瑞泰新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002610\",\n    \"name\": \"爱康科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688717\",\n    \"name\": \"艾罗能源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"600615\",\n    \"name\": \"丰华股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300842\",\n    \"name\": \"帝科股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600805\",\n    \"name\": \"悦达投资\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688032\",\n    \"name\": \"禾迈股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300751\",\n    \"name\": \"迈为股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002660\",\n    \"name\": \"茂硕电源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002323\",\n    \"name\": \"雅博股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301168\",\n    \"name\": \"通灵股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300093\",\n    \"name\": \"金刚光伏\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601096\",\n    \"name\": \"宏盛华源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"000821\",\n    \"name\": \"京山轻机\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300763\",\n    \"name\": \"锦浪科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002329\",\n    \"name\": \"皇氏集团\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"003022\",\n    \"name\": \"联泓新科\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"001269\",\n    \"name\": \"欧晶科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"688390\",\n    \"name\": \"固德威\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603628\",\n    \"name\": \"清源股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605117\",\n    \"name\": \"德业股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002306\",\n    \"name\": \"中科云网\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002578\",\n    \"name\": \"闽发铝业\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002783\",\n    \"name\": \"凯龙股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603333\",\n    \"name\": \"尚纬股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603577\",\n    \"name\": \"汇金通\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002529\",\n    \"name\": \"海源复材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002634\",\n    \"name\": \"棒杰股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603396\",\n    \"name\": \"金辰股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301266\",\n    \"name\": \"宇邦新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603212\",\n    \"name\": \"赛伍技术\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"301278\",\n    \"name\": \"快可电子\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603739\",\n    \"name\": \"蔚蓝生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001366\",\n    \"name\": \"播恩集团\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"000078\",\n    \"name\": \"海王生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"301526\",\n    \"name\": \"国际复材\",\n    \"tag\": \"PEEK材料\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"301076\",\n    \"name\": \"新瀚新材\",\n    \"tag\": \"PEEK材料\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002915\",\n    \"name\": \"中欣氟材\",\n    \"tag\": \"PEEK材料\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603392\",\n    \"name\": \"万泰生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"001300\",\n    \"name\": \"三柏硕\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"600678\",\n    \"name\": \"四川金顶\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002855\",\n    \"name\": \"捷荣技术\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002888\",\n    \"name\": \"惠威科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002981\",\n    \"name\": \"朝阳科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002577\",\n    \"name\": \"雷柏科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002786\",\n    \"name\": \"银宝山新\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"300100\",\n    \"name\": \"双林股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002703\",\n    \"name\": \"浙江世宝\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603529\",\n    \"name\": \"爱玛科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603390\",\n    \"name\": \"通达电气\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603107\",\n    \"name\": \"上海汽配\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603040\",\n    \"name\": \"新坐标\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600480\",\n    \"name\": \"凌云股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"002976\",\n    \"name\": \"瑞玛精密\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"601975\",\n    \"name\": \"招商南油\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601022\",\n    \"name\": \"宁波远洋\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"600178\",\n    \"name\": \"东安动力\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"603266\",\n    \"name\": \"天龙股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300492\",\n    \"name\": \"华图山鼎\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605020\",\n    \"name\": \"永和股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000691\",\n    \"name\": \"亚太实业\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300505\",\n    \"name\": \"川金诺\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002442\",\n    \"name\": \"龙星化工\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000422\",\n    \"name\": \"湖北宜化\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600610\",\n    \"name\": \"中毅达\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000985\",\n    \"name\": \"大庆华科\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"301000\",\n    \"name\": \"肇民科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003007\",\n    \"name\": \"直真科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"\"\n  },\n  {\n    \"code\": \"300620\",\n    \"name\": \"光库科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"\"\n  },\n  {\n    \"code\": \"603825\",\n    \"name\": \"华扬联众\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300502\",\n    \"name\": \"新易盛\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300418\",\n    \"name\": \"昆仑万维\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300394\",\n    \"name\": \"天孚通信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000070\",\n    \"name\": \"特发信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"\"\n  },\n  {\n    \"code\": \"002230\",\n    \"name\": \"科大讯飞\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600186\",\n    \"name\": \"莲花健康\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300949\",\n    \"name\": \"奥雅股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002771\",\n    \"name\": \"真视通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600449\",\n    \"name\": \"宁夏建材\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"000628\",\n    \"name\": \"高新发展\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"688685\",\n    \"name\": \"迈信林\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000936\",\n    \"name\": \"华西股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603083\",\n    \"name\": \"剑桥科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300570\",\n    \"name\": \"太辰光\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301205\",\n    \"name\": \"联特科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"603206\",\n    \"name\": \"嘉环科技\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300941\",\n    \"name\": \"创识科技\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605365\",\n    \"name\": \"立达信\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603038\",\n    \"name\": \"华立股份\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000676\",\n    \"name\": \"智度股份\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300531\",\n    \"name\": \"优博讯\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002197\",\n    \"name\": \"证通电子\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000158\",\n    \"name\": \"常山北明\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"003029\",\n    \"name\": \"吉大正元\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300456\",\n    \"name\": \"赛微电子\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002178\",\n    \"name\": \"延华智能\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"301337\",\n    \"name\": \"亚华电子\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"002819\",\n    \"name\": \"东方中科\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"003032\",\n    \"name\": \"传智教育\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"688609\",\n    \"name\": \"九联科技\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"301236\",\n    \"name\": \"软通动力\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"001339\",\n    \"name\": \"智微智能\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"300663\",\n    \"name\": \"科蓝软件\",\n    \"tag\": \"鸿蒙\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002181\",\n    \"name\": \"粤传媒\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600892\",\n    \"name\": \"大晟文化\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603721\",\n    \"name\": \"中广天择\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603230\",\n    \"name\": \"内蒙新华\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600825\",\n    \"name\": \"新华传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000793\",\n    \"name\": \"华闻集团\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600088\",\n    \"name\": \"中视传媒\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"300781\",\n    \"name\": \"因赛集团\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603598\",\n    \"name\": \"引力传媒\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"605577\",\n    \"name\": \"龙版传媒\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603729\",\n    \"name\": \"龙韵股份\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603608\",\n    \"name\": \"天创时尚\",\n    \"tag\": \"传媒\",\n    \"reason\": \"互联网营销\"\n  },\n  {\n    \"code\": \"601595\",\n    \"name\": \"上海电影\",\n    \"tag\": \"传媒\",\n    \"reason\": \"互联网营销\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300364\",\n    \"name\": \"中文在线\",\n    \"tag\": \"传媒\",\n    \"reason\": \"短剧\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002103\",\n    \"name\": \"广博股份\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301302\",\n    \"name\": \"华如科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300081\",\n    \"name\": \"恒信东方\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300968\",\n    \"name\": \"格林精密\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300076\",\n    \"name\": \"GQY视讯\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002635\",\n    \"name\": \"安洁科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002587\",\n    \"name\": \"奥拓电子\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300691\",\n    \"name\": \"联合光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300793\",\n    \"name\": \"佳禾智能\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000810\",\n    \"name\": \"创维数字\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002189\",\n    \"name\": \"中光学\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"002975\",\n    \"name\": \"博杰股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603052\",\n    \"name\": \"可川科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"002241\",\n    \"name\": \"歌尔股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"001314\",\n    \"name\": \"亿道信息\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"002395\",\n    \"name\": \"双象股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300556\",\n    \"name\": \"丝路视觉\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002952\",\n    \"name\": \"亚世光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300232\",\n    \"name\": \"洲明科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300323\",\n    \"name\": \"华灿光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301383\",\n    \"name\": \"天键股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"002655\",\n    \"name\": \"共达电声\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"688496\",\n    \"name\": \"清越科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"605218\",\n    \"name\": \"伟时电子\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605178\",\n    \"name\": \"时空科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605118\",\n    \"name\": \"力鼎光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603722\",\n    \"name\": \"阿科力\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300269\",\n    \"name\": \"联建光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300264\",\n    \"name\": \"佳创视讯\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603466\",\n    \"name\": \"风语筑\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002876\",\n    \"name\": \"三利谱\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002962\",\n    \"name\": \"五方光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300812\",\n    \"name\": \"易天股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000016\",\n    \"name\": \"深康佳Ａ\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"002449\",\n    \"name\": \"国星光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002517\",\n    \"name\": \"恺英网络\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002632\",\n    \"name\": \"道明光学\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"301011\",\n    \"name\": \"华立科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600071\",\n    \"name\": \"凤凰光学\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"003015\",\n    \"name\": \"日久光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603373\",\n    \"name\": \"安邦护卫\",\n    \"tag\": \"安防\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"603021\",\n    \"name\": \"山东华鹏\",\n    \"tag\": \"安防\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600506\",\n    \"name\": \"统一股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600689\",\n    \"name\": \"上海三毛\",\n    \"tag\": \"外销\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600228\",\n    \"name\": \"返利科技\",\n    \"tag\": \"外销\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003003\",\n    \"name\": \"天元股份\",\n    \"tag\": \"外销\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001379\",\n    \"name\": \"腾达科技\",\n    \"tag\": \"外销\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"300729\",\n    \"name\": \"乐歌股份\",\n    \"tag\": \"外销\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603280\",\n    \"name\": \"南方路机\",\n    \"tag\": \"外销\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"301367\",\n    \"name\": \"怡和嘉业\",\n    \"tag\": \"外销\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"002423\",\n    \"name\": \"中粮资本\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"600830\",\n    \"name\": \"香溢融通\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"603093\",\n    \"name\": \"南华期货\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600705\",\n    \"name\": \"中航产融\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"600643\",\n    \"name\": \"爱建集团\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000617\",\n    \"name\": \"中油资本\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"600390\",\n    \"name\": \"五矿资本\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"001236\",\n    \"name\": \"弘业期货\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600446\",\n    \"name\": \"金证股份\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301315\",\n    \"name\": \"威士顿\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"601336\",\n    \"name\": \"新华保险\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601601\",\n    \"name\": \"中国太保\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"601319\",\n    \"name\": \"中国人保\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"000627\",\n    \"name\": \"天茂集团\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601628\",\n    \"name\": \"中国人寿\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"601318\",\n    \"name\": \"中国平安\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"601136\",\n    \"name\": \"首创证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"601456\",\n    \"name\": \"国联证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601059\",\n    \"name\": \"信达证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601555\",\n    \"name\": \"东吴证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601099\",\n    \"name\": \"太平洋\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002945\",\n    \"name\": \"华林证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600999\",\n    \"name\": \"招商证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600906\",\n    \"name\": \"财达证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600837\",\n    \"name\": \"海通证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600095\",\n    \"name\": \"湘财股份\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601688\",\n    \"name\": \"华泰证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002939\",\n    \"name\": \"长城证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600061\",\n    \"name\": \"国投资本\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601211\",\n    \"name\": \"国泰君安\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000783\",\n    \"name\": \"长江证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601990\",\n    \"name\": \"南京证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601066\",\n    \"name\": \"中信建投\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002500\",\n    \"name\": \"山西证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000776\",\n    \"name\": \"广发证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000750\",\n    \"name\": \"国海证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601377\",\n    \"name\": \"兴业证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600958\",\n    \"name\": \"东方证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601878\",\n    \"name\": \"浙商证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002797\",\n    \"name\": \"第一创业\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002736\",\n    \"name\": \"国信证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000686\",\n    \"name\": \"东北证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600918\",\n    \"name\": \"中泰证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601108\",\n    \"name\": \"财通证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000728\",\n    \"name\": \"国元证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601901\",\n    \"name\": \"方正证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600909\",\n    \"name\": \"华安证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600030\",\n    \"name\": \"中信证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601162\",\n    \"name\": \"天风证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002673\",\n    \"name\": \"西部证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600109\",\n    \"name\": \"国金证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002926\",\n    \"name\": \"华西证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600369\",\n    \"name\": \"西南证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600864\",\n    \"name\": \"哈投股份\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600621\",\n    \"name\": \"华鑫股份\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600155\",\n    \"name\": \"华创云信\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002670\",\n    \"name\": \"国盛金控\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000166\",\n    \"name\": \"申万宏源\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601198\",\n    \"name\": \"东兴证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601696\",\n    \"name\": \"中银证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601788\",\n    \"name\": \"光大证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601881\",\n    \"name\": \"中国银河\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"601375\",\n    \"name\": \"中原证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601995\",\n    \"name\": \"中金公司\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601236\",\n    \"name\": \"红塔证券\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"000712\",\n    \"name\": \"锦龙股份\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600919\",\n    \"name\": \"江苏银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601860\",\n    \"name\": \"紫金银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600015\",\n    \"name\": \"华夏银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002839\",\n    \"name\": \"张家港行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600908\",\n    \"name\": \"无锡银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601818\",\n    \"name\": \"光大银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601128\",\n    \"name\": \"常熟银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601997\",\n    \"name\": \"贵阳银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601187\",\n    \"name\": \"厦门银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601665\",\n    \"name\": \"齐鲁银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601939\",\n    \"name\": \"建设银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601166\",\n    \"name\": \"兴业银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601528\",\n    \"name\": \"瑞丰银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601169\",\n    \"name\": \"北京银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002142\",\n    \"name\": \"宁波银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601229\",\n    \"name\": \"上海银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002807\",\n    \"name\": \"江阴银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600928\",\n    \"name\": \"西安银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601398\",\n    \"name\": \"工商银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"603323\",\n    \"name\": \"苏农银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601288\",\n    \"name\": \"农业银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601658\",\n    \"name\": \"邮储银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601577\",\n    \"name\": \"长沙银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601328\",\n    \"name\": \"交通银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600926\",\n    \"name\": \"杭州银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002936\",\n    \"name\": \"郑州银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002958\",\n    \"name\": \"青农商行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601825\",\n    \"name\": \"沪农商行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601009\",\n    \"name\": \"南京银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002948\",\n    \"name\": \"青岛银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601916\",\n    \"name\": \"浙商银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601077\",\n    \"name\": \"渝农商行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601988\",\n    \"name\": \"中国银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"601838\",\n    \"name\": \"成都银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001227\",\n    \"name\": \"兰州银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601998\",\n    \"name\": \"中信银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600000\",\n    \"name\": \"浦发银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002966\",\n    \"name\": \"苏州银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000001\",\n    \"name\": \"平安银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600016\",\n    \"name\": \"民生银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600036\",\n    \"name\": \"招商银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"601963\",\n    \"name\": \"重庆银行\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600816\",\n    \"name\": \"建元信托\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600053\",\n    \"name\": \"九鼎投资\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002177\",\n    \"name\": \"御银股份\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601519\",\n    \"name\": \"大智慧\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000532\",\n    \"name\": \"华金资本\",\n    \"tag\": \"金融\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600318\",\n    \"name\": \"新力金融\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603106\",\n    \"name\": \"恒银科技\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605111\",\n    \"name\": \"新洁能\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"688486\",\n    \"name\": \"龙迅股份\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"300623\",\n    \"name\": \"捷捷微电\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002559\",\n    \"name\": \"亚威股份\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002654\",\n    \"name\": \"万润科技\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000037\",\n    \"name\": \"深南电A\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"301012\",\n    \"name\": \"扬电科技\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000777\",\n    \"name\": \"中核科技\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"000966\",\n    \"name\": \"长源电力\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600744\",\n    \"name\": \"华银电力\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"002255\",\n    \"name\": \"海陆重工\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002471\",\n    \"name\": \"中超控股\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603530\",\n    \"name\": \"神马电力\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605011\",\n    \"name\": \"杭州热电\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"605167\",\n    \"name\": \"利柏特\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001332\",\n    \"name\": \"锡装股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"300875\",\n    \"name\": \"捷强装备\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002728\",\n    \"name\": \"特一药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002750\",\n    \"name\": \"龙津药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300181\",\n    \"name\": \"佐力药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000590\",\n    \"name\": \"启迪药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"300534\",\n    \"name\": \"陇神戎发\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000766\",\n    \"name\": \"通化金马\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600272\",\n    \"name\": \"开开实业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002432\",\n    \"name\": \"九安医疗\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603168\",\n    \"name\": \"莎普爱思\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688222\",\n    \"name\": \"成都先导\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000820\",\n    \"name\": \"神雾节能\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300197\",\n    \"name\": \"节能铁汉\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"300172\",\n    \"name\": \"中电环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603955\",\n    \"name\": \"大千生态\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000010\",\n    \"name\": \"美丽生态\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002140\",\n    \"name\": \"东华科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"605069\",\n    \"name\": \"正和生态\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603291\",\n    \"name\": \"联合水务\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"300262\",\n    \"name\": \"巴安水务\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603029\",\n    \"name\": \"天鹅股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002758\",\n    \"name\": \"浙农股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600354\",\n    \"name\": \"敦煌种业\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003042\",\n    \"name\": \"中农联合\",\n    \"tag\": \"农业\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"603909\",\n    \"name\": \"建发合诚\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605286\",\n    \"name\": \"同力日升\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002789\",\n    \"name\": \"建艺集团\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600284\",\n    \"name\": \"浦东建设\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600639\",\n    \"name\": \"浦东金桥\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600684\",\n    \"name\": \"珠江股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"603682\",\n    \"name\": \"锦和商管\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000029\",\n    \"name\": \"深深房A\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600622\",\n    \"name\": \"光大嘉宝\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"300989\",\n    \"name\": \"蕾奥规划\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000736\",\n    \"name\": \"中交地产\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"600708\",\n    \"name\": \"光明地产\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600648\",\n    \"name\": \"外高桥\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000014\",\n    \"name\": \"沙河股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002377\",\n    \"name\": \"国创高新\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603778\",\n    \"name\": \"国晟科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000608\",\n    \"name\": \"阳光股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"605287\",\n    \"name\": \"德才股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600657\",\n    \"name\": \"信达地产\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600234\",\n    \"name\": \"科新发展\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000656\",\n    \"name\": \"金科股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300917\",\n    \"name\": \"特发服务\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600675\",\n    \"name\": \"中华企业\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"002116\",\n    \"name\": \"中国海诚\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"300342\",\n    \"name\": \"天银机电\",\n    \"tag\": \"卫星\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"603131\",\n    \"name\": \"上海沪工\",\n    \"tag\": \"卫星\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"000058\",\n    \"name\": \"深赛格\",\n    \"tag\": \"卫星\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"300405\",\n    \"name\": \"科隆股份\",\n    \"tag\": \"氢能\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"688551\",\n    \"name\": \"科威尔\",\n    \"tag\": \"氢能\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603798\",\n    \"name\": \"康普顿\",\n    \"tag\": \"氢能\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"601226\",\n    \"name\": \"华电重工\",\n    \"tag\": \"氢能\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"603213\",\n    \"name\": \"镇洋发展\",\n    \"tag\": \"氢能\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600860\",\n    \"name\": \"京城股份\",\n    \"tag\": \"氢能\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002381\",\n    \"name\": \"双箭股份\",\n    \"tag\": \"养老\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002172\",\n    \"name\": \"澳洋健康\",\n    \"tag\": \"养老\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002162\",\n    \"name\": \"悦心健康\",\n    \"tag\": \"养老\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002614\",\n    \"name\": \"奥佳华\",\n    \"tag\": \"养老\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603610\",\n    \"name\": \"麒盛科技\",\n    \"tag\": \"养老\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"002174\",\n    \"name\": \"游族网络\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002919\",\n    \"name\": \"名臣健康\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002168\",\n    \"name\": \"惠程科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002605\",\n    \"name\": \"姚记科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000720\",\n    \"name\": \"新能泰山\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"300374\",\n    \"name\": \"中铁装配\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"中字头\"\n  },\n  {\n    \"code\": \"603329\",\n    \"name\": \"上海雅仕\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000505\",\n    \"name\": \"京粮控股\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600171\",\n    \"name\": \"上海贝岭\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"央企\"\n  },\n  {\n    \"code\": \"600653\",\n    \"name\": \"申华控股\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600629\",\n    \"name\": \"华建集团\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600119\",\n    \"name\": \"长江投资\",\n    \"tag\": \"金融\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301313\",\n    \"name\": \"凡拓数创\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301396\",\n    \"name\": \"宏景科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301209\",\n    \"name\": \"联合化学\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688152\",\n    \"name\": \"麒麟信安\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688489\",\n    \"name\": \"三未信安\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688435\",\n    \"name\": \"英方软件\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688292\",\n    \"name\": \"浩瀚深度\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301270\",\n    \"name\": \"汉仪股份\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301339\",\n    \"name\": \"通行宝\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301165\",\n    \"name\": \"锐捷网络\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688448\",\n    \"name\": \"磁谷科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688475\",\n    \"name\": \"萤石网络\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301380\",\n    \"name\": \"挖金客\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301311\",\n    \"name\": \"昆船智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301283\",\n    \"name\": \"聚胶股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301368\",\n    \"name\": \"丰立智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301379\",\n    \"name\": \"天山电子\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301277\",\n    \"name\": \"新天地\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688031\",\n    \"name\": \"星环科技-U\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301297\",\n    \"name\": \"富乐德\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301377\",\n    \"name\": \"鼎泰高科\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301171\",\n    \"name\": \"易点天下\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688506\",\n    \"name\": \"百利天恒-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688244\",\n    \"name\": \"永信至诚\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301231\",\n    \"name\": \"荣信文化\",\n    \"tag\": \"教育\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301285\",\n    \"name\": \"鸿日达\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688403\",\n    \"name\": \"汇成股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301290\",\n    \"name\": \"东星医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301330\",\n    \"name\": \"熵基科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301361\",\n    \"name\": \"众智科技\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301255\",\n    \"name\": \"通力科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301366\",\n    \"name\": \"一博科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301317\",\n    \"name\": \"鑫磊股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688203\",\n    \"name\": \"海正生材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688141\",\n    \"name\": \"杰华特\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688132\",\n    \"name\": \"邦彦技术\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301115\",\n    \"name\": \"建科股份\",\n    \"tag\": \"专业服务\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301328\",\n    \"name\": \"维峰电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688525\",\n    \"name\": \"佰维存储\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688351\",\n    \"name\": \"微电生理-U\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688143\",\n    \"name\": \"长盈通\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301398\",\n    \"name\": \"星源卓镁\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301365\",\n    \"name\": \"矩阵股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301391\",\n    \"name\": \"卡莱特\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688401\",\n    \"name\": \"路维光电\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301161\",\n    \"name\": \"唯万密封\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688362\",\n    \"name\": \"甬矽电子\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301389\",\n    \"name\": \"隆扬电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301276\",\n    \"name\": \"嘉曼服饰\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688426\",\n    \"name\": \"康为世纪\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688410\",\n    \"name\": \"山外山\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301326\",\n    \"name\": \"捷邦科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688428\",\n    \"name\": \"诺诚健华-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301349\",\n    \"name\": \"信德新材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301309\",\n    \"name\": \"万得凯\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688420\",\n    \"name\": \"美腾科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688480\",\n    \"name\": \"赛恩斯\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688498\",\n    \"name\": \"源杰科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688419\",\n    \"name\": \"耐科装备\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301282\",\n    \"name\": \"金禄电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688455\",\n    \"name\": \"科捷智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688416\",\n    \"name\": \"恒烁股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688409\",\n    \"name\": \"富创精密\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301359\",\n    \"name\": \"东南电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688291\",\n    \"name\": \"金橙子\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688387\",\n    \"name\": \"信科移动-U\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688061\",\n    \"name\": \"灿瑞科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301388\",\n    \"name\": \"欣灵电气\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688147\",\n    \"name\": \"微导纳米\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301327\",\n    \"name\": \"华宝新能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688459\",\n    \"name\": \"哈铁科技\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301176\",\n    \"name\": \"逸豪新材\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301335\",\n    \"name\": \"天元宠物\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688432\",\n    \"name\": \"有研硅\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301105\",\n    \"name\": \"鸿铭股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301321\",\n    \"name\": \"翰博高新\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688485\",\n    \"name\": \"九州一轨\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688035\",\n    \"name\": \"德邦科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688391\",\n    \"name\": \"钜泉科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301300\",\n    \"name\": \"远翔新材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688381\",\n    \"name\": \"帝奥微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301267\",\n    \"name\": \"华厦眼科\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301280\",\n    \"name\": \"珠城科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301299\",\n    \"name\": \"卓创资讯\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688439\",\n    \"name\": \"振华风光\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688073\",\n    \"name\": \"毕得医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688275\",\n    \"name\": \"万润新能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688376\",\n    \"name\": \"美埃科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301223\",\n    \"name\": \"中荣股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301273\",\n    \"name\": \"瑞晨环保\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301301\",\n    \"name\": \"川宁生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688370\",\n    \"name\": \"丛麟科技\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688392\",\n    \"name\": \"骄成超声\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301338\",\n    \"name\": \"凯格精机\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301265\",\n    \"name\": \"华新环保\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301296\",\n    \"name\": \"新巨丰\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688271\",\n    \"name\": \"联影医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688084\",\n    \"name\": \"晶品特装\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688247\",\n    \"name\": \"宣泰医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688172\",\n    \"name\": \"燕东微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688372\",\n    \"name\": \"伟测科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688252\",\n    \"name\": \"天德钰\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301363\",\n    \"name\": \"美好医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301369\",\n    \"name\": \"联动科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688137\",\n    \"name\": \"近岸蛋白\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688293\",\n    \"name\": \"奥浦迈\",\n    \"tag\": \"专业服务\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301319\",\n    \"name\": \"唯特偶\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301230\",\n    \"name\": \"泓博医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301227\",\n    \"name\": \"森鹰窗业\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301356\",\n    \"name\": \"天振股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688184\",\n    \"name\": \"帕瓦股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301331\",\n    \"name\": \"恩威医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688114\",\n    \"name\": \"华大智造\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301316\",\n    \"name\": \"慧博云通\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301508\",\n    \"name\": \"中机认检\",\n    \"tag\": \"专业服务\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688716\",\n    \"name\": \"中研股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601083\",\n    \"name\": \"锦江航运\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601061\",\n    \"name\": \"中信金属\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301395\",\n    \"name\": \"仁信新材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301516\",\n    \"name\": \"中远通\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301172\",\n    \"name\": \"君逸数码\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001367\",\n    \"name\": \"海森药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688629\",\n    \"name\": \"华丰科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603137\",\n    \"name\": \"恒尚节能\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301293\",\n    \"name\": \"三博脑科\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301429\",\n    \"name\": \"森泰股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603172\",\n    \"name\": \"万丰股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601033\",\n    \"name\": \"永兴股份\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688469\",\n    \"name\": \"芯联集成-U\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301325\",\n    \"name\": \"曼恩斯特\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301499\",\n    \"name\": \"维科精密\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301505\",\n    \"name\": \"苏州规划\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688535\",\n    \"name\": \"华海诚科\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601065\",\n    \"name\": \"江盐集团\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301558\",\n    \"name\": \"三态股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301533\",\n    \"name\": \"威马农机\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301448\",\n    \"name\": \"开创电气\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001286\",\n    \"name\": \"陕西能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301332\",\n    \"name\": \"德尔玛\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688507\",\n    \"name\": \"索辰科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301390\",\n    \"name\": \"经纬股份\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301469\",\n    \"name\": \"恒达新材\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301323\",\n    \"name\": \"新莱福\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301519\",\n    \"name\": \"舜禹股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301517\",\n    \"name\": \"陕西华达\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301355\",\n    \"name\": \"南王科技\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688563\",\n    \"name\": \"航材股份\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301503\",\n    \"name\": \"智迪科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301507\",\n    \"name\": \"民生健康\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688539\",\n    \"name\": \"高华科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301303\",\n    \"name\": \"真兰仪表\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301314\",\n    \"name\": \"科瑞思\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301370\",\n    \"name\": \"国科恒泰\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301373\",\n    \"name\": \"凌玮科技\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301487\",\n    \"name\": \"盟固利\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301568\",\n    \"name\": \"思泰克\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688576\",\n    \"name\": \"西山科技\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301202\",\n    \"name\": \"朗威股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603275\",\n    \"name\": \"众辰科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001324\",\n    \"name\": \"长青科技\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301371\",\n    \"name\": \"敷尔佳\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688631\",\n    \"name\": \"莱斯信息\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001360\",\n    \"name\": \"南矿集团\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301225\",\n    \"name\": \"恒勃股份\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301468\",\n    \"name\": \"博盈特焊\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603135\",\n    \"name\": \"中重科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603325\",\n    \"name\": \"博隆技术\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301500\",\n    \"name\": \"飞南资源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301559\",\n    \"name\": \"中集环科\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001282\",\n    \"name\": \"三联锻造\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301408\",\n    \"name\": \"华人健康\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301428\",\n    \"name\": \"世纪恒通\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301305\",\n    \"name\": \"朗坤环境\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301489\",\n    \"name\": \"思泉新材\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301232\",\n    \"name\": \"飞沃科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301210\",\n    \"name\": \"金杨股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001326\",\n    \"name\": \"联域股份\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601133\",\n    \"name\": \"柏诚股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688307\",\n    \"name\": \"中润光学\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001287\",\n    \"name\": \"中电港\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688522\",\n    \"name\": \"纳睿雷达\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688653\",\n    \"name\": \"康希通信\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301386\",\n    \"name\": \"未来电器\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301262\",\n    \"name\": \"海看股份\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301529\",\n    \"name\": \"福赛科技\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301439\",\n    \"name\": \"泓淋电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301555\",\n    \"name\": \"惠柏新材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301292\",\n    \"name\": \"海科新源\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688347\",\n    \"name\": \"华虹公司\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301291\",\n    \"name\": \"明阳电气\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301528\",\n    \"name\": \"多浦乐\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301518\",\n    \"name\": \"长华化学\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688671\",\n    \"name\": \"碧兴物联\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301357\",\n    \"name\": \"北方长龙\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603273\",\n    \"name\": \"天元智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001378\",\n    \"name\": \"德冠新材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603119\",\n    \"name\": \"浙江荣泰\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301418\",\n    \"name\": \"协昌科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603270\",\n    \"name\": \"金帝股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300870\",\n    \"name\": \"欧陆通\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000899\",\n    \"name\": \"赣能股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"600212\",\n    \"name\": \"绿能慧充\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": null\n  },\n  {\n    \"code\": \"301141\",\n    \"name\": \"中科磁业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688570\",\n    \"name\": \"天玛智控\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688334\",\n    \"name\": \"西高院\",\n    \"tag\": \"专业服务\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301246\",\n    \"name\": \"宏源药业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300804\",\n    \"name\": \"广康生化\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301566\",\n    \"name\": \"达利凯普\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301272\",\n    \"name\": \"英华特\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301515\",\n    \"name\": \"港通医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001328\",\n    \"name\": \"登康口腔\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688623\",\n    \"name\": \"双元科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301295\",\n    \"name\": \"美硕科技\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301399\",\n    \"name\": \"英特科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301512\",\n    \"name\": \"智信精密\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688343\",\n    \"name\": \"云天励飞-U\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603075\",\n    \"name\": \"热威股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301413\",\n    \"name\": \"安培龙\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688479\",\n    \"name\": \"友车科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301203\",\n    \"name\": \"国泰环保\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301372\",\n    \"name\": \"科净源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603193\",\n    \"name\": \"润本股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300904\",\n    \"name\": \"威力传动\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688638\",\n    \"name\": \"誉辰智能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688549\",\n    \"name\": \"中巨芯-U\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603125\",\n    \"name\": \"常青科技\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301281\",\n    \"name\": \"科源制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001358\",\n    \"name\": \"兴欣新材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301446\",\n    \"name\": \"福事特\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688562\",\n    \"name\": \"航天软件\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301358\",\n    \"name\": \"湖南裕能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688450\",\n    \"name\": \"光格科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001373\",\n    \"name\": \"翔腾新材\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301382\",\n    \"name\": \"蜂助手\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301421\",\n    \"name\": \"波长光电\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688523\",\n    \"name\": \"航天环宇\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301310\",\n    \"name\": \"鑫宏业\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301170\",\n    \"name\": \"锡南科技\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301381\",\n    \"name\": \"赛维时代\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688593\",\n    \"name\": \"新相微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688651\",\n    \"name\": \"盛邦安全\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301353\",\n    \"name\": \"普莱得\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688543\",\n    \"name\": \"国科军工\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301362\",\n    \"name\": \"民爆光电\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688573\",\n    \"name\": \"信宇人\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301378\",\n    \"name\": \"通达海\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301397\",\n    \"name\": \"溯联股份\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301322\",\n    \"name\": \"绿通科技\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688552\",\n    \"name\": \"航天南湖\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688720\",\n    \"name\": \"艾森股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301548\",\n    \"name\": \"崇德科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301251\",\n    \"name\": \"威尔高\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688352\",\n    \"name\": \"颀中科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301456\",\n    \"name\": \"盘古智能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688502\",\n    \"name\": \"茂莱光学\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301252\",\n    \"name\": \"同星科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301488\",\n    \"name\": \"豪恩汽电\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603062\",\n    \"name\": \"麦加芯彩\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301287\",\n    \"name\": \"康力源\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301329\",\n    \"name\": \"信音电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301510\",\n    \"name\": \"固高科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301459\",\n    \"name\": \"丰茂股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688657\",\n    \"name\": \"浩辰软件\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301578\",\n    \"name\": \"辰奕智能\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301393\",\n    \"name\": \"昊帆生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301419\",\n    \"name\": \"阿莱德\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688646\",\n    \"name\": \"逸飞激光\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301260\",\n    \"name\": \"格力博\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688582\",\n    \"name\": \"芯动联科\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688693\",\n    \"name\": \"锴威特\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603004\",\n    \"name\": \"鼎龙科技\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688602\",\n    \"name\": \"康鹏科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688146\",\n    \"name\": \"中船特气\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688443\",\n    \"name\": \"智翔金泰-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688548\",\n    \"name\": \"广钢气体\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301261\",\n    \"name\": \"恒工精密\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301387\",\n    \"name\": \"光大同创\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301348\",\n    \"name\": \"蓝箭电子\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301550\",\n    \"name\": \"斯菱股份\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688652\",\n    \"name\": \"京仪装备\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301486\",\n    \"name\": \"致尚科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688610\",\n    \"name\": \"埃科光电\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688620\",\n    \"name\": \"安凯微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688429\",\n    \"name\": \"时创能源\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301567\",\n    \"name\": \"贝隆精密\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301345\",\n    \"name\": \"涛涛车业\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301320\",\n    \"name\": \"豪江智能\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301307\",\n    \"name\": \"美利信\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688603\",\n    \"name\": \"天承科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688531\",\n    \"name\": \"日联科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001306\",\n    \"name\": \"夏厦精密\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688515\",\n    \"name\": \"裕太微-U\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688458\",\n    \"name\": \"美芯晟\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001380\",\n    \"name\": \"华纬科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688591\",\n    \"name\": \"泰凌微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688361\",\n    \"name\": \"中科飞测-U\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301509\",\n    \"name\": \"金凯生科\",\n    \"tag\": \"减肥药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301157\",\n    \"name\": \"华塑科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301498\",\n    \"name\": \"乖宝宠物\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688627\",\n    \"name\": \"精智达\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688512\",\n    \"name\": \"慧智微-U\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688592\",\n    \"name\": \"司南导航\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688433\",\n    \"name\": \"华曙高科\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688702\",\n    \"name\": \"盛科通信-U\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688484\",\n    \"name\": \"南芯科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301577\",\n    \"name\": \"C美信\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688249\",\n    \"name\": \"晶合集成\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688478\",\n    \"name\": \"晶升股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301525\",\n    \"name\": \"儒竞科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301520\",\n    \"name\": \"万邦医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301360\",\n    \"name\": \"荣旗科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603296\",\n    \"name\": \"华勤技术\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688648\",\n    \"name\": \"中邮科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688581\",\n    \"name\": \"安杰思\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688719\",\n    \"name\": \"爱科赛博\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688612\",\n    \"name\": \"威迈斯\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603375\",\n    \"name\": \"C盛景微\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873122\",\n    \"name\": \"中纺标\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601068\",\n    \"name\": \"中铝国际\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000151\",\n    \"name\": \"中成股份\",\n    \"tag\": \"外销\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601857\",\n    \"name\": \"中国石油\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000928\",\n    \"name\": \"中钢国际\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601800\",\n    \"name\": \"中国交建\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002401\",\n    \"name\": \"中远海科\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000797\",\n    \"name\": \"中国武夷\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600938\",\n    \"name\": \"中国海油\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600056\",\n    \"name\": \"中国医药\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601111\",\n    \"name\": \"中国国航\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002051\",\n    \"name\": \"中工国际\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601088\",\n    \"name\": \"中国神华\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600737\",\n    \"name\": \"中粮糖业\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601600\",\n    \"name\": \"中国铝业\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601919\",\n    \"name\": \"中远海控\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000927\",\n    \"name\": \"中国铁物\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601668\",\n    \"name\": \"中国建筑\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301215\",\n    \"name\": \"中汽股份\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600720\",\n    \"name\": \"中交设计\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600428\",\n    \"name\": \"中远海特\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600007\",\n    \"name\": \"中国国贸\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001213\",\n    \"name\": \"中铁特货\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000758\",\n    \"name\": \"中色股份\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600328\",\n    \"name\": \"中盐化工\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601669\",\n    \"name\": \"中国电建\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600941\",\n    \"name\": \"中国移动\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600787\",\n    \"name\": \"中储股份\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600489\",\n    \"name\": \"中金黄金\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601949\",\n    \"name\": \"中国出版\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600482\",\n    \"name\": \"中国动力\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600339\",\n    \"name\": \"中油工程\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601117\",\n    \"name\": \"中国化学\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600730\",\n    \"name\": \"中国高科\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601989\",\n    \"name\": \"中国重工\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600050\",\n    \"name\": \"中国联通\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601898\",\n    \"name\": \"中煤能源\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601016\",\n    \"name\": \"节能风电\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601618\",\n    \"name\": \"中国中冶\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603126\",\n    \"name\": \"中材节能\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300114\",\n    \"name\": \"中航电测\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601985\",\n    \"name\": \"中国核电\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601186\",\n    \"name\": \"中国铁建\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600176\",\n    \"name\": \"中国巨石\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300140\",\n    \"name\": \"节能环境\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003816\",\n    \"name\": \"中国广核\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600158\",\n    \"name\": \"中体产业\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600528\",\n    \"name\": \"中铁工业\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600026\",\n    \"name\": \"中远海能\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601390\",\n    \"name\": \"中国中铁\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600150\",\n    \"name\": \"中国船舶\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600916\",\n    \"name\": \"中国黄金\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000066\",\n    \"name\": \"中国长城\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000519\",\n    \"name\": \"中兵红箭\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000831\",\n    \"name\": \"中国稀土\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601728\",\n    \"name\": \"中国电信\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601106\",\n    \"name\": \"中国一重\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601766\",\n    \"name\": \"中国中车\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300962\",\n    \"name\": \"中金辐照\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000099\",\n    \"name\": \"中信海直\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000951\",\n    \"name\": \"中国重汽\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000009\",\n    \"name\": \"中国宝安\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002080\",\n    \"name\": \"中材科技\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601858\",\n    \"name\": \"中国科传\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601179\",\n    \"name\": \"中国西电\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601868\",\n    \"name\": \"中国能建\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600536\",\n    \"name\": \"中国软件\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601611\",\n    \"name\": \"中国核建\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600028\",\n    \"name\": \"中国石化\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002057\",\n    \"name\": \"中钢天源\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688119\",\n    \"name\": \"中钢洛耐\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000881\",\n    \"name\": \"中广核技\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601888\",\n    \"name\": \"中国中免\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600764\",\n    \"name\": \"中国海防\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601598\",\n    \"name\": \"中国外运\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600118\",\n    \"name\": \"中国卫星\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600685\",\n    \"name\": \"中船防务\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688009\",\n    \"name\": \"中国通号\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301058\",\n    \"name\": \"中粮科工\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603860\",\n    \"name\": \"中公高科\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600862\",\n    \"name\": \"中航高科\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600977\",\n    \"name\": \"中国电影\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601698\",\n    \"name\": \"中国卫通\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600970\",\n    \"name\": \"中材国际\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688128\",\n    \"name\": \"中国电研\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300847\",\n    \"name\": \"中船汉光\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601965\",\n    \"name\": \"中国汽研\",\n    \"tag\": \"中字头\",\n    \"hidden_tag\": \"中字头\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"872808\",\n    \"name\": \"曙光数创\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300548\",\n    \"name\": \"博创科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430139\",\n    \"name\": \"华岭股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600409\",\n    \"name\": \"三友化工\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835640\",\n    \"name\": \"富士达\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002049\",\n    \"name\": \"紫光国微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000666\",\n    \"name\": \"经纬纺机\",\n    \"tag\": \"金融\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600551\",\n    \"name\": \"时代出版\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600636\",\n    \"name\": \"国新文化\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002320\",\n    \"name\": \"海峡股份\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600508\",\n    \"name\": \"上海能源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600962\",\n    \"name\": \"国投中鲁\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000819\",\n    \"name\": \"岳阳兴长\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600458\",\n    \"name\": \"时代新材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600647\",\n    \"name\": \"*ST同达\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600396\",\n    \"name\": \"*ST金山\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000999\",\n    \"name\": \"华润三九\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000717\",\n    \"name\": \"中南股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600640\",\n    \"name\": \"国脉文化\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002305\",\n    \"name\": \"南国置业\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000423\",\n    \"name\": \"东阿阿胶\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600048\",\n    \"name\": \"保利发展\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600058\",\n    \"name\": \"五矿发展\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600961\",\n    \"name\": \"株冶集团\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000839\",\n    \"name\": \"ST国安\",\n    \"tag\": \"央企\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300188\",\n    \"name\": \"国投智能\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000031\",\n    \"name\": \"大悦城\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000566\",\n    \"name\": \"海南海药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600750\",\n    \"name\": \"江中药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600495\",\n    \"name\": \"晋西车轴\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600345\",\n    \"name\": \"长江通信\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600217\",\n    \"name\": \"中再资环\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001979\",\n    \"name\": \"招商蛇口\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601808\",\n    \"name\": \"中海油服\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002314\",\n    \"name\": \"南山控股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000798\",\n    \"name\": \"中水渔业\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002205\",\n    \"name\": \"国统股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000069\",\n    \"name\": \"华侨城Ａ\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600501\",\n    \"name\": \"航天晨光\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000698\",\n    \"name\": \"沈阳化工\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601005\",\n    \"name\": \"重庆钢铁\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600808\",\n    \"name\": \"马钢股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600343\",\n    \"name\": \"航天动力\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600011\",\n    \"name\": \"华能国际\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000807\",\n    \"name\": \"云铝股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600688\",\n    \"name\": \"上海石化\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600148\",\n    \"name\": \"长春一东\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600019\",\n    \"name\": \"宝钢股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600027\",\n    \"name\": \"华电国际\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000059\",\n    \"name\": \"华锦股份\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600292\",\n    \"name\": \"远达环保\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000830\",\n    \"name\": \"鲁西化工\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600099\",\n    \"name\": \"林海股份\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600710\",\n    \"name\": \"苏美达\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300334\",\n    \"name\": \"津膜科技\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000554\",\n    \"name\": \"泰山石油\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001965\",\n    \"name\": \"招商公路\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000825\",\n    \"name\": \"太钢不锈\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000883\",\n    \"name\": \"湖北能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000708\",\n    \"name\": \"中信特钢\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688248\",\n    \"name\": \"南网科技\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601872\",\n    \"name\": \"招商轮船\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601718\",\n    \"name\": \"际华集团\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000878\",\n    \"name\": \"云南铜业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000065\",\n    \"name\": \"北方国际\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001872\",\n    \"name\": \"招商港口\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600886\",\n    \"name\": \"国投电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000898\",\n    \"name\": \"鞍钢股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600698\",\n    \"name\": \"湖南天雁\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600973\",\n    \"name\": \"宝胜股份\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600262\",\n    \"name\": \"北方股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301137\",\n    \"name\": \"哈焊华通\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000958\",\n    \"name\": \"电投产融\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601918\",\n    \"name\": \"新集能源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600863\",\n    \"name\": \"内蒙华电\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600581\",\n    \"name\": \"八一钢铁\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300425\",\n    \"name\": \"中建环能\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000786\",\n    \"name\": \"北新建材\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000962\",\n    \"name\": \"东方钽业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601880\",\n    \"name\": \"辽港股份\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300388\",\n    \"name\": \"节能国祯\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600795\",\n    \"name\": \"国电电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600184\",\n    \"name\": \"光电股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600029\",\n    \"name\": \"南方航空\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600115\",\n    \"name\": \"中国东航\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002462\",\n    \"name\": \"嘉事堂\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600433\",\n    \"name\": \"冠豪高新\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000028\",\n    \"name\": \"国药一致\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600320\",\n    \"name\": \"振华重工\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000877\",\n    \"name\": \"天山股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002066\",\n    \"name\": \"瑞泰科技\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600500\",\n    \"name\": \"中化国际\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601212\",\n    \"name\": \"白银有色\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000731\",\n    \"name\": \"四川美丰\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600550\",\n    \"name\": \"保变电气\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600299\",\n    \"name\": \"安迪苏\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000800\",\n    \"name\": \"一汽解放\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300212\",\n    \"name\": \"易华录\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000778\",\n    \"name\": \"新兴铸管\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600313\",\n    \"name\": \"农发种业\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600517\",\n    \"name\": \"国网英大\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002246\",\n    \"name\": \"北化股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000553\",\n    \"name\": \"安道麦A\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600236\",\n    \"name\": \"桂冠电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600198\",\n    \"name\": \"大唐电信\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002039\",\n    \"name\": \"黔源电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002302\",\n    \"name\": \"西部建设\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600283\",\n    \"name\": \"钱江水利\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000761\",\n    \"name\": \"本钢板材\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000008\",\n    \"name\": \"神州高铁\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600062\",\n    \"name\": \"华润双鹤\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002368\",\n    \"name\": \"太极股份\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600230\",\n    \"name\": \"沧州大化\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601866\",\n    \"name\": \"中远海发\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601608\",\n    \"name\": \"中信重工\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600900\",\n    \"name\": \"长江电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600980\",\n    \"name\": \"北矿科技\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000826\",\n    \"name\": \"启迪环境\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300597\",\n    \"name\": \"吉大通信\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000875\",\n    \"name\": \"吉电股份\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001914\",\n    \"name\": \"招商积余\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600025\",\n    \"name\": \"华能水电\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600582\",\n    \"name\": \"天地科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600523\",\n    \"name\": \"贵航股份\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600876\",\n    \"name\": \"凯盛新能\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000629\",\n    \"name\": \"钒钛股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002037\",\n    \"name\": \"保利联合\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600967\",\n    \"name\": \"内蒙一机\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600505\",\n    \"name\": \"西昌电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000032\",\n    \"name\": \"深桑达Ａ\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601816\",\n    \"name\": \"京沪高铁\",\n    \"tag\": \"统一大市场\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603698\",\n    \"name\": \"航天工程\",\n    \"tag\": \"专业服务\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600392\",\n    \"name\": \"盛和资源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000815\",\n    \"name\": \"美利云\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600379\",\n    \"name\": \"宝光股份\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601006\",\n    \"name\": \"大秦铁路\",\n    \"tag\": \"统一大市场\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600583\",\n    \"name\": \"海油工程\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300446\",\n    \"name\": \"航天智造\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000503\",\n    \"name\": \"国新健康\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600560\",\n    \"name\": \"金自天正\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600579\",\n    \"name\": \"克劳斯\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600135\",\n    \"name\": \"乐凯胶片\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600497\",\n    \"name\": \"驰宏锌锗\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300070\",\n    \"name\": \"碧水源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601968\",\n    \"name\": \"宝钢包装\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000068\",\n    \"name\": \"华控赛格\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600963\",\n    \"name\": \"岳阳林纸\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601399\",\n    \"name\": \"国机重装\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601038\",\n    \"name\": \"一拖股份\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600598\",\n    \"name\": \"北大荒\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600125\",\n    \"name\": \"铁龙物流\",\n    \"tag\": \"统一大市场\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002128\",\n    \"name\": \"电投能源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000788\",\n    \"name\": \"北大医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600855\",\n    \"name\": \"航天长峰\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002232\",\n    \"name\": \"启明信息\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300087\",\n    \"name\": \"荃银高科\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600101\",\n    \"name\": \"明星电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600129\",\n    \"name\": \"太极集团\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600845\",\n    \"name\": \"宝信软件\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000768\",\n    \"name\": \"中航西飞\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002265\",\n    \"name\": \"建设工业\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300797\",\n    \"name\": \"钢研纳克\",\n    \"tag\": \"专业服务\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300523\",\n    \"name\": \"辰安科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300788\",\n    \"name\": \"中信出版\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603060\",\n    \"name\": \"国检集团\",\n    \"tag\": \"专业服务\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601991\",\n    \"name\": \"大唐发电\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600875\",\n    \"name\": \"东方电气\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600871\",\n    \"name\": \"石化油服\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600726\",\n    \"name\": \"华电能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600469\",\n    \"name\": \"风神股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600452\",\n    \"name\": \"涪陵电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600444\",\n    \"name\": \"国机通用\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600435\",\n    \"name\": \"北方导航\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600271\",\n    \"name\": \"航天信息\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600161\",\n    \"name\": \"天坛生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600037\",\n    \"name\": \"歌华有线\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301048\",\n    \"name\": \"金鹰重工\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300105\",\n    \"name\": \"龙源技术\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002179\",\n    \"name\": \"中航光电\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000410\",\n    \"name\": \"沈阳机床\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002025\",\n    \"name\": \"航天电器\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001289\",\n    \"name\": \"龙源电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002268\",\n    \"name\": \"电科网安\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600038\",\n    \"name\": \"中直股份\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600335\",\n    \"name\": \"国机汽车\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000969\",\n    \"name\": \"安泰科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600476\",\n    \"name\": \"湘邮科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600116\",\n    \"name\": \"三峡水利\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600372\",\n    \"name\": \"中航机载\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600206\",\n    \"name\": \"有研新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000851\",\n    \"name\": \"高鸿股份\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002063\",\n    \"name\": \"远光软件\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301175\",\n    \"name\": \"中科环保\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600995\",\n    \"name\": \"南网储能\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000663\",\n    \"name\": \"永安林业\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000547\",\n    \"name\": \"航天发展\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002258\",\n    \"name\": \"利尔化学\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600990\",\n    \"name\": \"四创电子\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600776\",\n    \"name\": \"东方通信\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000998\",\n    \"name\": \"隆平高科\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600100\",\n    \"name\": \"同方股份\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600131\",\n    \"name\": \"国网信通\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000920\",\n    \"name\": \"沃顿科技\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600968\",\n    \"name\": \"海油发展\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600106\",\n    \"name\": \"重庆路桥\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002190\",\n    \"name\": \"成飞集成\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600406\",\n    \"name\": \"国电南瑞\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000591\",\n    \"name\": \"太阳能\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600601\",\n    \"name\": \"方正科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000727\",\n    \"name\": \"冠捷科技\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002017\",\n    \"name\": \"东信和平\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600905\",\n    \"name\": \"三峡能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002226\",\n    \"name\": \"江南化工\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000901\",\n    \"name\": \"航天科技\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600021\",\n    \"name\": \"上海电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600151\",\n    \"name\": \"航天机电\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688297\",\n    \"name\": \"中无人机\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600316\",\n    \"name\": \"洪都航空\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600084\",\n    \"name\": \"中信尼雅\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003035\",\n    \"name\": \"南网能源\",\n    \"tag\": \"专业服务\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002046\",\n    \"name\": \"国机精工\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600511\",\n    \"name\": \"国药股份\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688396\",\n    \"name\": \"华润微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002544\",\n    \"name\": \"智能机器\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000922\",\n    \"name\": \"佳电股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600760\",\n    \"name\": \"中航沈飞\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603128\",\n    \"name\": \"华贸物流\",\n    \"tag\": \"统一大市场\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000970\",\n    \"name\": \"中科三环\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600391\",\n    \"name\": \"航发科技\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301090\",\n    \"name\": \"华润材料\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688285\",\n    \"name\": \"高铁电气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600879\",\n    \"name\": \"航天电子\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600138\",\n    \"name\": \"中青旅\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603013\",\n    \"name\": \"亚普股份\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000625\",\n    \"name\": \"长安汽车\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002590\",\n    \"name\": \"万安科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603897\",\n    \"name\": \"长城科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000862\",\n    \"name\": \"银星能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300527\",\n    \"name\": \"中船应急\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600893\",\n    \"name\": \"航发动力\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600850\",\n    \"name\": \"电科数字\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600562\",\n    \"name\": \"国睿科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002415\",\n    \"name\": \"海康威视\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000021\",\n    \"name\": \"深科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688722\",\n    \"name\": \"同益中\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688509\",\n    \"name\": \"正元地信\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688151\",\n    \"name\": \"华强科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600877\",\n    \"name\": \"电科芯片\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300024\",\n    \"name\": \"智能机器\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000050\",\n    \"name\": \"深天马Ａ\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688425\",\n    \"name\": \"铁建重工\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000733\",\n    \"name\": \"振华科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688568\",\n    \"name\": \"中科星图\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002106\",\n    \"name\": \"莱宝高科\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600389\",\n    \"name\": \"江山股份\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002222\",\n    \"name\": \"福晶科技\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000026\",\n    \"name\": \"飞亚达\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688779\",\n    \"name\": \"长远锂科\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601156\",\n    \"name\": \"东航物流\",\n    \"tag\": \"统一大市场\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688190\",\n    \"name\": \"云路股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003009\",\n    \"name\": \"中天火箭\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600195\",\n    \"name\": \"中牧股份\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300396\",\n    \"name\": \"迪瑞医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600498\",\n    \"name\": \"烽火通信\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601798\",\n    \"name\": \"蓝科高新\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000938\",\n    \"name\": \"紫光股份\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000550\",\n    \"name\": \"江铃汽车\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688187\",\n    \"name\": \"时代电气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002389\",\n    \"name\": \"航天彩虹\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688015\",\n    \"name\": \"交控科技\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600765\",\n    \"name\": \"中航重机\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601333\",\n    \"name\": \"广深铁路\",\n    \"tag\": \"统一大市场\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600072\",\n    \"name\": \"中船科技\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600268\",\n    \"name\": \"国电南自\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000738\",\n    \"name\": \"航发控制\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300455\",\n    \"name\": \"航天智装\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000400\",\n    \"name\": \"许继电气\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603000\",\n    \"name\": \"人民网\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002643\",\n    \"name\": \"万润股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002732\",\n    \"name\": \"燕塘乳业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002777\",\n    \"name\": \"久远银海\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603888\",\n    \"name\": \"新华网\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603927\",\n    \"name\": \"中科软\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600420\",\n    \"name\": \"国药现代\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300516\",\n    \"name\": \"久之洋\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688586\",\n    \"name\": \"江航装备\",\n    \"tag\": \"军工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300557\",\n    \"name\": \"理工光科\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002338\",\n    \"name\": \"奥普光电\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600312\",\n    \"name\": \"平高电气\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688295\",\n    \"name\": \"中复神鹰\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300747\",\n    \"name\": \"锐科激光\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300073\",\n    \"name\": \"当升科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688456\",\n    \"name\": \"有研粉材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600552\",\n    \"name\": \"凯盛科技\",\n    \"tag\": \"VR\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300294\",\n    \"name\": \"博雅生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600081\",\n    \"name\": \"东风科技\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002281\",\n    \"name\": \"光迅科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002916\",\n    \"name\": \"深南电路\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603019\",\n    \"name\": \"中科曙光\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600378\",\n    \"name\": \"昊华科技\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600189\",\n    \"name\": \"泉阳泉\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000852\",\n    \"name\": \"石化机械\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000537\",\n    \"name\": \"中绿电\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688707\",\n    \"name\": \"振华新材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600006\",\n    \"name\": \"东风汽车\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300678\",\n    \"name\": \"中科信息\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688041\",\n    \"name\": \"海光信息\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002935\",\n    \"name\": \"天奥电子\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300034\",\n    \"name\": \"钢研高纳\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688375\",\n    \"name\": \"国博电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688569\",\n    \"name\": \"铁科轨道\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003031\",\n    \"name\": \"中瓷电子\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600486\",\n    \"name\": \"扬农化工\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600775\",\n    \"name\": \"南京熊猫\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603970\",\n    \"name\": \"中农立华\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001223\",\n    \"name\": \"欧克科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001299\",\n    \"name\": \"美能能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603130\",\n    \"name\": \"云中马\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603163\",\n    \"name\": \"圣晖集成\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001222\",\n    \"name\": \"源飞宠物\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001301\",\n    \"name\": \"尚太科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001298\",\n    \"name\": \"好上好\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001256\",\n    \"name\": \"炜冈科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001322\",\n    \"name\": \"箭牌家居\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001259\",\n    \"name\": \"利仁科技\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603237\",\n    \"name\": \"五芳斋\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001255\",\n    \"name\": \"博菲电气\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001231\",\n    \"name\": \"农心科技\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001333\",\n    \"name\": \"光华股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001331\",\n    \"name\": \"胜通能源\",\n    \"tag\": \"统一大市场\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001338\",\n    \"name\": \"永顺泰\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001330\",\n    \"name\": \"博纳影业\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603151\",\n    \"name\": \"邦基科技\",\n    \"tag\": \"农业\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603057\",\n    \"name\": \"紫燕食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001283\",\n    \"name\": \"豪鹏科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603255\",\n    \"name\": \"鼎际得\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603182\",\n    \"name\": \"嘉华股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001260\",\n    \"name\": \"坤泰股份\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001225\",\n    \"name\": \"和泰机电\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600925\",\n    \"name\": \"苏能股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601121\",\n    \"name\": \"宝地矿业\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001337\",\n    \"name\": \"四川黄金\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603073\",\n    \"name\": \"彩蝶实业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001376\",\n    \"name\": \"百通能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603162\",\n    \"name\": \"海通发展\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001278\",\n    \"name\": \"一彬科技\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603190\",\n    \"name\": \"亚通精工\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603065\",\n    \"name\": \"宿迁联盛\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603307\",\n    \"name\": \"扬州金泉\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603282\",\n    \"name\": \"亚光股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001311\",\n    \"name\": \"多利科技\",\n    \"tag\": \"汽车\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603173\",\n    \"name\": \"福斯达\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603153\",\n    \"name\": \"上海建科\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002883\",\n    \"name\": \"中设股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603061\",\n    \"name\": \"金海通\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603281\",\n    \"name\": \"江瀚新材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": \"次新股\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603648\",\n    \"name\": \"畅联股份\",\n    \"tag\": \"统一大市场\",\n    \"hidden_tag\": \"央企\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900948\",\n    \"name\": \"伊泰Ｂ股\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601699\",\n    \"name\": \"潞安环能\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601225\",\n    \"name\": \"陕西煤业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601101\",\n    \"name\": \"昊华能源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601001\",\n    \"name\": \"晋控煤业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600546\",\n    \"name\": \"山煤国际\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000983\",\n    \"name\": \"山西焦煤\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000937\",\n    \"name\": \"冀中能源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601666\",\n    \"name\": \"平煤股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600985\",\n    \"name\": \"淮北矿业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600188\",\n    \"name\": \"兖矿能源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600123\",\n    \"name\": \"兰花科创\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600971\",\n    \"name\": \"恒源煤电\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600348\",\n    \"name\": \"华阳股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600997\",\n    \"name\": \"开滦股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600395\",\n    \"name\": \"盘江股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000552\",\n    \"name\": \"甘肃能化\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600740\",\n    \"name\": \"山西焦化\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600725\",\n    \"name\": \"云维股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000571\",\n    \"name\": \"新大洲A\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000723\",\n    \"name\": \"美锦能源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600397\",\n    \"name\": \"安源煤业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600758\",\n    \"name\": \"辽宁能源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601015\",\n    \"name\": \"陕西黑猫\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601011\",\n    \"name\": \"宝泰隆\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600121\",\n    \"name\": \"郑州煤电\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600792\",\n    \"name\": \"云煤能源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600403\",\n    \"name\": \"大有能源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600408\",\n    \"name\": \"安泰集团\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"839167\",\n    \"name\": \"同享科技\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871634\",\n    \"name\": \"新威凌\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000688\",\n    \"name\": \"国城矿业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000933\",\n    \"name\": \"神火股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601168\",\n    \"name\": \"西部矿业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000630\",\n    \"name\": \"铜陵有色\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600362\",\n    \"name\": \"江西铜业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600219\",\n    \"name\": \"南山铝业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002532\",\n    \"name\": \"天山铝业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002203\",\n    \"name\": \"海亮股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688122\",\n    \"name\": \"西部超导\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000060\",\n    \"name\": \"中金岭南\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601137\",\n    \"name\": \"博威合金\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688257\",\n    \"name\": \"新锐股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600888\",\n    \"name\": \"新疆众和\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000751\",\n    \"name\": \"锌业股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601702\",\n    \"name\": \"华峰铝业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601677\",\n    \"name\": \"明泰铝业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002379\",\n    \"name\": \"宏创控股\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600595\",\n    \"name\": \"中孚实业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603876\",\n    \"name\": \"鼎胜新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000612\",\n    \"name\": \"焦作万方\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002160\",\n    \"name\": \"常铝股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002171\",\n    \"name\": \"楚江新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000426\",\n    \"name\": \"兴业银锡\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600255\",\n    \"name\": \"鑫科材料\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688786\",\n    \"name\": \"悦安新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600531\",\n    \"name\": \"豫光金铅\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002988\",\n    \"name\": \"豪美新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300855\",\n    \"name\": \"图南股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603132\",\n    \"name\": \"金徽股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601609\",\n    \"name\": \"金田股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000737\",\n    \"name\": \"北方铜业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600490\",\n    \"name\": \"鹏欣资源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600361\",\n    \"name\": \"创新新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601388\",\n    \"name\": \"怡球资源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688102\",\n    \"name\": \"斯瑞新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002182\",\n    \"name\": \"宝武镁业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002540\",\n    \"name\": \"亚太科技\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300337\",\n    \"name\": \"银邦股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002824\",\n    \"name\": \"和胜股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603115\",\n    \"name\": \"海星股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002501\",\n    \"name\": \"利源股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003038\",\n    \"name\": \"鑫铂股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600331\",\n    \"name\": \"宏达股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600338\",\n    \"name\": \"西藏珠峰\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300697\",\n    \"name\": \"电工合金\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300930\",\n    \"name\": \"屹通新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000633\",\n    \"name\": \"合金投资\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000603\",\n    \"name\": \"盛达资源\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002996\",\n    \"name\": \"顺博合金\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688231\",\n    \"name\": \"隆达股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603937\",\n    \"name\": \"丽岛新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603527\",\n    \"name\": \"众源新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603978\",\n    \"name\": \"深圳新星\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002295\",\n    \"name\": \"精艺股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300963\",\n    \"name\": \"中洲特材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300828\",\n    \"name\": \"锐新科技\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603045\",\n    \"name\": \"福达合金\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600768\",\n    \"name\": \"宁波富邦\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002114\",\n    \"name\": \"罗平锌电\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300986\",\n    \"name\": \"志特新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002806\",\n    \"name\": \"华锋股份\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300489\",\n    \"name\": \"光智科技\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300057\",\n    \"name\": \"万顺新材\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601020\",\n    \"name\": \"华钰矿业\",\n    \"tag\": \"资源\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002235\",\n    \"name\": \"安妮股份\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600576\",\n    \"name\": \"祥源文旅\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002115\",\n    \"name\": \"三维通信\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600052\",\n    \"name\": \"东望时代\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834021\",\n    \"name\": \"流金科技\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300295\",\n    \"name\": \"三六五网\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600373\",\n    \"name\": \"中文传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600136\",\n    \"name\": \"*ST明诚\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601928\",\n    \"name\": \"凤凰传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601098\",\n    \"name\": \"中南传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603999\",\n    \"name\": \"读者传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300987\",\n    \"name\": \"川网传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601019\",\n    \"name\": \"山东出版\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601999\",\n    \"name\": \"出版传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601811\",\n    \"name\": \"新华文轩\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000719\",\n    \"name\": \"中原传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300770\",\n    \"name\": \"新媒股份\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601921\",\n    \"name\": \"浙版传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601801\",\n    \"name\": \"皖新传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600757\",\n    \"name\": \"长江传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002027\",\n    \"name\": \"分众传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300413\",\n    \"name\": \"芒果超媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600959\",\n    \"name\": \"江苏有线\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000156\",\n    \"name\": \"华数传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002238\",\n    \"name\": \"天威视讯\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605168\",\n    \"name\": \"三人行\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000802\",\n    \"name\": \"北京文化\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600637\",\n    \"name\": \"东方明珠\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002343\",\n    \"name\": \"慈文传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601929\",\n    \"name\": \"吉视传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600861\",\n    \"name\": \"北京人力\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300795\",\n    \"name\": \"米奥会展\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600229\",\n    \"name\": \"城市传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601900\",\n    \"name\": \"南方传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600986\",\n    \"name\": \"浙文互联\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000607\",\n    \"name\": \"华媒控股\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300251\",\n    \"name\": \"光线传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000917\",\n    \"name\": \"电广传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002292\",\n    \"name\": \"奥飞娱乐\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002858\",\n    \"name\": \"力盛体育\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300654\",\n    \"name\": \"世纪天鸿\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300182\",\n    \"name\": \"捷成股份\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002400\",\n    \"name\": \"省广集团\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603096\",\n    \"name\": \"新经典\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300133\",\n    \"name\": \"华策影视\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002739\",\n    \"name\": \"万达电影\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300860\",\n    \"name\": \"锋尚文化\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000681\",\n    \"name\": \"视觉中国\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603103\",\n    \"name\": \"横店影视\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301052\",\n    \"name\": \"果麦文化\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300662\",\n    \"name\": \"科锐国际\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000665\",\n    \"name\": \"湖北广电\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002712\",\n    \"name\": \"思美传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002502\",\n    \"name\": \"ST鼎龙\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300071\",\n    \"name\": \"福石控股\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301102\",\n    \"name\": \"兆讯传媒\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000892\",\n    \"name\": \"欢瑞世纪\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300063\",\n    \"name\": \"天龙集团\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600936\",\n    \"name\": \"广西广电\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002699\",\n    \"name\": \"*ST美盛\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600556\",\n    \"name\": \"天下秀\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300805\",\n    \"name\": \"电声股份\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300792\",\n    \"name\": \"壹网壹创\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301025\",\n    \"name\": \"读客文化\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300058\",\n    \"name\": \"蓝色光标\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300027\",\n    \"name\": \"华谊兄弟\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600831\",\n    \"name\": \"广电网络\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300291\",\n    \"name\": \"百纳千成\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603533\",\n    \"name\": \"掌阅科技\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300242\",\n    \"name\": \"佳云科技\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300280\",\n    \"name\": \"紫天科技\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300612\",\n    \"name\": \"宣亚国际\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600996\",\n    \"name\": \"贵广网络\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300426\",\n    \"name\": \"唐德影视\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600358\",\n    \"name\": \"国旅联合\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300061\",\n    \"name\": \"旗天科技\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002905\",\n    \"name\": \"金逸影视\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002995\",\n    \"name\": \"天地在线\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300528\",\n    \"name\": \"幸福蓝海\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300947\",\n    \"name\": \"德必集团\",\n    \"tag\": \"传媒\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900937\",\n    \"name\": \"华电B股\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900957\",\n    \"name\": \"凌云Ｂ股\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601908\",\n    \"name\": \"京运通\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002608\",\n    \"name\": \"江苏国信\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600674\",\n    \"name\": \"川投能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600023\",\n    \"name\": \"浙能电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600642\",\n    \"name\": \"申能股份\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000690\",\n    \"name\": \"宝新能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000027\",\n    \"name\": \"深圳能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600098\",\n    \"name\": \"广州发展\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600578\",\n    \"name\": \"京能电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"200539\",\n    \"name\": \"粤电力Ｂ\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600163\",\n    \"name\": \"中闽能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600157\",\n    \"name\": \"永泰能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000543\",\n    \"name\": \"皖能电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000791\",\n    \"name\": \"甘肃能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600483\",\n    \"name\": \"福能股份\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000767\",\n    \"name\": \"晋控电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000539\",\n    \"name\": \"粤电力Ａ\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600982\",\n    \"name\": \"宁波能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600509\",\n    \"name\": \"天富能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601619\",\n    \"name\": \"嘉泽新能\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601778\",\n    \"name\": \"晶科科技\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603693\",\n    \"name\": \"江苏新能\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600032\",\n    \"name\": \"浙江新能\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605580\",\n    \"name\": \"恒盛能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001896\",\n    \"name\": \"豫能控股\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600868\",\n    \"name\": \"梅雁吉祥\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600167\",\n    \"name\": \"联美控股\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600821\",\n    \"name\": \"金开新能\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000993\",\n    \"name\": \"闽东电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600979\",\n    \"name\": \"广安爱众\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000040\",\n    \"name\": \"东旭蓝天\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000600\",\n    \"name\": \"建投能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000531\",\n    \"name\": \"穗恒运Ａ\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600644\",\n    \"name\": \"乐山电力\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000722\",\n    \"name\": \"湖南发展\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600310\",\n    \"name\": \"广西能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002015\",\n    \"name\": \"协鑫能科\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001258\",\n    \"name\": \"立新能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605028\",\n    \"name\": \"世茂能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600969\",\n    \"name\": \"郴电国际\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600780\",\n    \"name\": \"通宝能源\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000601\",\n    \"name\": \"韶能股份\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002616\",\n    \"name\": \"长青集团\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600149\",\n    \"name\": \"廊坊发展\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"200037\",\n    \"name\": \"深南电B\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600719\",\n    \"name\": \"大连热电\",\n    \"tag\": \"电力\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831010\",\n    \"name\": \"凯添燃气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600803\",\n    \"name\": \"新奥股份\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000669\",\n    \"name\": \"ST金鸿\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605368\",\n    \"name\": \"蓝天燃气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002259\",\n    \"name\": \"ST升达\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603318\",\n    \"name\": \"水发燃气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600956\",\n    \"name\": \"新天绿能\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601139\",\n    \"name\": \"深圳燃气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002911\",\n    \"name\": \"佛燃能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600681\",\n    \"name\": \"百川能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600635\",\n    \"name\": \"大众公用\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603053\",\n    \"name\": \"成都燃气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002267\",\n    \"name\": \"陕天然气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605090\",\n    \"name\": \"九丰能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002700\",\n    \"name\": \"ST浩源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000968\",\n    \"name\": \"蓝焰控股\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600917\",\n    \"name\": \"重庆燃气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600617\",\n    \"name\": \"国新能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600903\",\n    \"name\": \"贵州燃气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605169\",\n    \"name\": \"洪通燃气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300332\",\n    \"name\": \"天壕能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603689\",\n    \"name\": \"皖天然气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000407\",\n    \"name\": \"胜利股份\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600333\",\n    \"name\": \"长春燃气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603393\",\n    \"name\": \"新天然气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300435\",\n    \"name\": \"中泰股份\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000421\",\n    \"name\": \"南京公用\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603706\",\n    \"name\": \"东方环宇\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603080\",\n    \"name\": \"新疆火炬\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300483\",\n    \"name\": \"首华燃气\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000593\",\n    \"name\": \"德龙汇能\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600256\",\n    \"name\": \"广汇能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600777\",\n    \"name\": \"新潮能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000096\",\n    \"name\": \"广聚能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002778\",\n    \"name\": \"中晟高科\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002221\",\n    \"name\": \"东华能源\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603353\",\n    \"name\": \"和顺石油\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600759\",\n    \"name\": \"ST洲际\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600387\",\n    \"name\": \"ST海越\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000637\",\n    \"name\": \"ST实华\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000159\",\n    \"name\": \"国际实业\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300839\",\n    \"name\": \"博汇股份\",\n    \"tag\": \"公用\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"839273\",\n    \"name\": \"一致魔芋\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"839371\",\n    \"name\": \"欧福蛋业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833429\",\n    \"name\": \"康比特\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836422\",\n    \"name\": \"润普食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831726\",\n    \"name\": \"朱老六\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836826\",\n    \"name\": \"盖世食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832023\",\n    \"name\": \"田野股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300997\",\n    \"name\": \"欢乐家\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001318\",\n    \"name\": \"阳光乳业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300898\",\n    \"name\": \"熊猫乳品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300175\",\n    \"name\": \"朗源股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300106\",\n    \"name\": \"西部牧业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002840\",\n    \"name\": \"华统股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832786\",\n    \"name\": \"骑士乳业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605499\",\n    \"name\": \"东鹏饮料\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600873\",\n    \"name\": \"梅花生物\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603288\",\n    \"name\": \"海天味业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600872\",\n    \"name\": \"中炬高新\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001219\",\n    \"name\": \"青岛食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603156\",\n    \"name\": \"养元饮品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603027\",\n    \"name\": \"千禾味业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002726\",\n    \"name\": \"龙大美食\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600887\",\n    \"name\": \"伊利股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002991\",\n    \"name\": \"甘源食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300908\",\n    \"name\": \"仲景食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300791\",\n    \"name\": \"仙乐健康\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"200019\",\n    \"name\": \"深粮B\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002847\",\n    \"name\": \"盐津铺子\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002557\",\n    \"name\": \"洽洽食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300858\",\n    \"name\": \"科拓生物\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000895\",\n    \"name\": \"双汇发展\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002650\",\n    \"name\": \"加加食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002507\",\n    \"name\": \"涪陵榨菜\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603886\",\n    \"name\": \"元祖股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600597\",\n    \"name\": \"光明乳业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000848\",\n    \"name\": \"承德露露\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300999\",\n    \"name\": \"金龙鱼\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301206\",\n    \"name\": \"三元生物\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300146\",\n    \"name\": \"汤臣倍健\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002597\",\n    \"name\": \"金禾实业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600429\",\n    \"name\": \"三元股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002216\",\n    \"name\": \"三全食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600305\",\n    \"name\": \"恒顺醋业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605567\",\n    \"name\": \"春雪食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603043\",\n    \"name\": \"广州酒家\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603235\",\n    \"name\": \"天新药业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600298\",\n    \"name\": \"安琪酵母\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605300\",\n    \"name\": \"佳禾食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600073\",\n    \"name\": \"上海梅林\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605179\",\n    \"name\": \"一鸣食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002481\",\n    \"name\": \"双塔食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001215\",\n    \"name\": \"千味央厨\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605016\",\n    \"name\": \"百龙创园\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002946\",\n    \"name\": \"新乳业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000716\",\n    \"name\": \"黑芝麻\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603517\",\n    \"name\": \"绝味食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603102\",\n    \"name\": \"百合股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002910\",\n    \"name\": \"庄园牧场\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605337\",\n    \"name\": \"李子园\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002286\",\n    \"name\": \"保龄宝\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600300\",\n    \"name\": \"维维股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603345\",\n    \"name\": \"安井食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603317\",\n    \"name\": \"天味食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600419\",\n    \"name\": \"天润乳业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000529\",\n    \"name\": \"广弘控股\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605089\",\n    \"name\": \"味知香\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002570\",\n    \"name\": \"贝因美\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000911\",\n    \"name\": \"广农糖业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605338\",\n    \"name\": \"巴比食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002852\",\n    \"name\": \"道道全\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300915\",\n    \"name\": \"海融科技\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605388\",\n    \"name\": \"均瑶健康\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002702\",\n    \"name\": \"海欣食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603697\",\n    \"name\": \"有友食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003030\",\n    \"name\": \"祖名股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300094\",\n    \"name\": \"国联水产\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603696\",\n    \"name\": \"安记食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301116\",\n    \"name\": \"益客食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605339\",\n    \"name\": \"南侨食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002515\",\n    \"name\": \"金字火腿\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002956\",\n    \"name\": \"西麦食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002330\",\n    \"name\": \"得利斯\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002582\",\n    \"name\": \"好想你\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603020\",\n    \"name\": \"爱普股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300973\",\n    \"name\": \"立高食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600866\",\n    \"name\": \"星湖科技\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603079\",\n    \"name\": \"圣达生物\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002661\",\n    \"name\": \"克明食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603866\",\n    \"name\": \"桃李面包\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603755\",\n    \"name\": \"日辰股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300765\",\n    \"name\": \"新诺威\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300741\",\n    \"name\": \"华宝股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002695\",\n    \"name\": \"煌上煌\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605077\",\n    \"name\": \"华康股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000639\",\n    \"name\": \"西王食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002626\",\n    \"name\": \"金达威\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300892\",\n    \"name\": \"品渥食品\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605198\",\n    \"name\": \"安德利\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300829\",\n    \"name\": \"金丹科技\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688089\",\n    \"name\": \"嘉必优\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600191\",\n    \"name\": \"华资实业\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900923\",\n    \"name\": \"百联Ｂ股\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000759\",\n    \"name\": \"中百集团\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600838\",\n    \"name\": \"上海九百\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600306\",\n    \"name\": \"*ST商城\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000417\",\n    \"name\": \"合肥百货\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600729\",\n    \"name\": \"重庆百货\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600785\",\n    \"name\": \"新华百货\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"200058\",\n    \"name\": \"深赛格B\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002416\",\n    \"name\": \"爱施德\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603101\",\n    \"name\": \"汇嘉时代\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002818\",\n    \"name\": \"富森美\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002697\",\n    \"name\": \"红旗连锁\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601933\",\n    \"name\": \"永辉超市\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601010\",\n    \"name\": \"文峰股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601828\",\n    \"name\": \"美凯龙\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601366\",\n    \"name\": \"利群股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600827\",\n    \"name\": \"百联股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600814\",\n    \"name\": \"杭州解百\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000906\",\n    \"name\": \"浙商中拓\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600694\",\n    \"name\": \"大商股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600712\",\n    \"name\": \"南宁百货\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002187\",\n    \"name\": \"广百股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600859\",\n    \"name\": \"王府井\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600858\",\n    \"name\": \"银座股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600738\",\n    \"name\": \"丽尚国潮\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600415\",\n    \"name\": \"小商品城\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600778\",\n    \"name\": \"友好集团\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301078\",\n    \"name\": \"孩子王\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002344\",\n    \"name\": \"海宁皮城\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600287\",\n    \"name\": \"江苏舜天\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600865\",\n    \"name\": \"百大集团\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000564\",\n    \"name\": \"ST大集\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603214\",\n    \"name\": \"爱婴室\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601116\",\n    \"name\": \"三江购物\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600790\",\n    \"name\": \"轻纺城\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000419\",\n    \"name\": \"通程控股\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600682\",\n    \"name\": \"南京新百\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600857\",\n    \"name\": \"宁波中百\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002024\",\n    \"name\": \"ST易购\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300622\",\n    \"name\": \"博士眼镜\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000882\",\n    \"name\": \"华联股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600693\",\n    \"name\": \"东百集团\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000785\",\n    \"name\": \"居然之家\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002277\",\n    \"name\": \"友阿股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000701\",\n    \"name\": \"厦门信达\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603708\",\n    \"name\": \"家家悦\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002251\",\n    \"name\": \"*ST步高\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600774\",\n    \"name\": \"汉商集团\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603123\",\n    \"name\": \"翠微股份\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002336\",\n    \"name\": \"人人乐\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300126\",\n    \"name\": \"锐奇股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603960\",\n    \"name\": \"克来机电\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603901\",\n    \"name\": \"永创智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002214\",\n    \"name\": \"大立科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002690\",\n    \"name\": \"美亚光电\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002444\",\n    \"name\": \"巨星科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300002\",\n    \"name\": \"神州泰岳\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300124\",\n    \"name\": \"汇川技术\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000961\",\n    \"name\": \"中南建设\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000333\",\n    \"name\": \"美的集团\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300308\",\n    \"name\": \"中际旭创\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688218\",\n    \"name\": \"江苏北人\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600282\",\n    \"name\": \"南钢股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300802\",\n    \"name\": \"矩子科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002131\",\n    \"name\": \"利欧股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688155\",\n    \"name\": \"先惠技术\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002367\",\n    \"name\": \"康力电梯\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600522\",\n    \"name\": \"中天科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003021\",\n    \"name\": \"兆威机电\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002599\",\n    \"name\": \"盛通股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002526\",\n    \"name\": \"山东矿机\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600346\",\n    \"name\": \"恒力石化\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600215\",\n    \"name\": \"派斯林\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603656\",\n    \"name\": \"泰禾智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600503\",\n    \"name\": \"华丽家族\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002067\",\n    \"name\": \"景兴纸业\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002747\",\n    \"name\": \"埃斯顿\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600835\",\n    \"name\": \"上海机电\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300415\",\n    \"name\": \"伊之密\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603011\",\n    \"name\": \"合锻智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603025\",\n    \"name\": \"大豪科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300433\",\n    \"name\": \"蓝思科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300112\",\n    \"name\": \"万讯自控\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002892\",\n    \"name\": \"科力尔\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600894\",\n    \"name\": \"广日股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688255\",\n    \"name\": \"凯尔达\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300450\",\n    \"name\": \"先导智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600565\",\n    \"name\": \"迪马股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002600\",\n    \"name\": \"领益智造\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300420\",\n    \"name\": \"五洋停车\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300607\",\n    \"name\": \"拓斯达\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300532\",\n    \"name\": \"今天国际\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002903\",\n    \"name\": \"宇环数控\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600728\",\n    \"name\": \"佳都科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002472\",\n    \"name\": \"双环传动\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002957\",\n    \"name\": \"科瑞技术\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002229\",\n    \"name\": \"鸿博股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603486\",\n    \"name\": \"科沃斯\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000404\",\n    \"name\": \"长虹华意\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002097\",\n    \"name\": \"山河智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601138\",\n    \"name\": \"工业富联\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002011\",\n    \"name\": \"盾安环境\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002184\",\n    \"name\": \"海得控制\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300115\",\n    \"name\": \"长盈精密\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300193\",\n    \"name\": \"佳士科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000837\",\n    \"name\": \"秦川机床\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002547\",\n    \"name\": \"春兴精工\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002689\",\n    \"name\": \"远大智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605056\",\n    \"name\": \"咸亨国际\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002090\",\n    \"name\": \"金智科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000988\",\n    \"name\": \"华工科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000413\",\n    \"name\": \"东旭光电\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688003\",\n    \"name\": \"天准科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002722\",\n    \"name\": \"物产金轮\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300201\",\n    \"name\": \"海伦哲\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688025\",\n    \"name\": \"杰普特\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002535\",\n    \"name\": \"林州重机\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002917\",\n    \"name\": \"金奥博\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600699\",\n    \"name\": \"均胜电子\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300382\",\n    \"name\": \"斯莱克\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300475\",\n    \"name\": \"香农芯创\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002008\",\n    \"name\": \"大族激光\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300131\",\n    \"name\": \"英唐智控\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300097\",\n    \"name\": \"智云股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000584\",\n    \"name\": \"ST工智\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002583\",\n    \"name\": \"海能达\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300810\",\n    \"name\": \"中科海讯\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300293\",\n    \"name\": \"蓝英装备\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688165\",\n    \"name\": \"埃夫特-U\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002073\",\n    \"name\": \"软控股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300195\",\n    \"name\": \"长荣股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603508\",\n    \"name\": \"思维列控\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300173\",\n    \"name\": \"福能东方\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603416\",\n    \"name\": \"信捷电气\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603030\",\n    \"name\": \"*ST全筑\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002380\",\n    \"name\": \"科远智慧\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002625\",\n    \"name\": \"光启技术\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002026\",\n    \"name\": \"山东威达\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300044\",\n    \"name\": \"赛为智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002358\",\n    \"name\": \"森源电气\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300161\",\n    \"name\": \"华中数控\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300580\",\n    \"name\": \"贝斯特\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002139\",\n    \"name\": \"拓邦股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002441\",\n    \"name\": \"众业达\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688277\",\n    \"name\": \"天智航-U\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300486\",\n    \"name\": \"东杰智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002979\",\n    \"name\": \"雷赛智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688558\",\n    \"name\": \"国盛智科\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301112\",\n    \"name\": \"信邦智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002228\",\n    \"name\": \"合兴包装\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002209\",\n    \"name\": \"达 意 隆\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002348\",\n    \"name\": \"高乐股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300249\",\n    \"name\": \"依米康\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002497\",\n    \"name\": \"雅化集团\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000795\",\n    \"name\": \"英洛华\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003019\",\n    \"name\": \"宸展光电\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002334\",\n    \"name\": \"英威腾\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300307\",\n    \"name\": \"慈星股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002698\",\n    \"name\": \"博实股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300134\",\n    \"name\": \"大富科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603496\",\n    \"name\": \"恒为科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002283\",\n    \"name\": \"天润工业\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600288\",\n    \"name\": \"大恒科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688090\",\n    \"name\": \"瑞松科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002363\",\n    \"name\": \"隆基机械\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300853\",\n    \"name\": \"申昊科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002009\",\n    \"name\": \"天奇股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300466\",\n    \"name\": \"赛摩智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603203\",\n    \"name\": \"快克智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300278\",\n    \"name\": \"华昌达\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002006\",\n    \"name\": \"精工科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688022\",\n    \"name\": \"瀚川智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001696\",\n    \"name\": \"宗申动力\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002520\",\n    \"name\": \"日发精机\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600843\",\n    \"name\": \"上工申贝\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300461\",\n    \"name\": \"田中精机\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301199\",\n    \"name\": \"迈赫股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300154\",\n    \"name\": \"瑞凌股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300358\",\n    \"name\": \"楚天科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688290\",\n    \"name\": \"景业智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301298\",\n    \"name\": \"东利机械\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300222\",\n    \"name\": \"科大智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002846\",\n    \"name\": \"英联股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603895\",\n    \"name\": \"天永智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835579\",\n    \"name\": \"机科股份\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600520\",\n    \"name\": \"文一科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300472\",\n    \"name\": \"新元科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300099\",\n    \"name\": \"尤洛卡\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300391\",\n    \"name\": \"长药控股\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688306\",\n    \"name\": \"均普智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300279\",\n    \"name\": \"和晶科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002376\",\n    \"name\": \"新北洋\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300543\",\n    \"name\": \"朗科智能\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300865\",\n    \"name\": \"大宏立\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300282\",\n    \"name\": \"*ST三盛\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002427\",\n    \"name\": \"尤夫股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835237\",\n    \"name\": \"力佳科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833523\",\n    \"name\": \"德瑞锂电\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000616\",\n    \"name\": \"*ST海投\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833781\",\n    \"name\": \"瑞奇智造\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836239\",\n    \"name\": \"长虹能源\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835185\",\n    \"name\": \"贝特瑞\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873152\",\n    \"name\": \"天宏锂电\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834033\",\n    \"name\": \"康普化学\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"830809\",\n    \"name\": \"安达科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603032\",\n    \"name\": \"德新科技\",\n    \"tag\": \"智能机器\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002801\",\n    \"name\": \"微光股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300037\",\n    \"name\": \"新宙邦\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000651\",\n    \"name\": \"格力电器\",\n    \"tag\": \"大消费\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600039\",\n    \"name\": \"四川路桥\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300068\",\n    \"name\": \"南都电源\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603717\",\n    \"name\": \"天域生态\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601238\",\n    \"name\": \"广汽集团\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002601\",\n    \"name\": \"龙佰集团\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002741\",\n    \"name\": \"光华科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600673\",\n    \"name\": \"东阳光\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600426\",\n    \"name\": \"华鲁恒升\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688819\",\n    \"name\": \"天能股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002371\",\n    \"name\": \"北方华创\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600143\",\n    \"name\": \"金发科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300750\",\n    \"name\": \"宁德时代\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000408\",\n    \"name\": \"藏格矿业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600096\",\n    \"name\": \"云天化\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002594\",\n    \"name\": \"比亚迪\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300991\",\n    \"name\": \"创益通\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600160\",\n    \"name\": \"巨化股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000063\",\n    \"name\": \"中兴通讯\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603826\",\n    \"name\": \"坤彩科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603665\",\n    \"name\": \"康隆达\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600210\",\n    \"name\": \"紫江企业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000792\",\n    \"name\": \"盐湖股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601678\",\n    \"name\": \"滨化股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002077\",\n    \"name\": \"大港股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603379\",\n    \"name\": \"三美股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600111\",\n    \"name\": \"北方稀土\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300207\",\n    \"name\": \"欣旺达\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002108\",\n    \"name\": \"沧州明珠\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300014\",\n    \"name\": \"亿纬锂能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300801\",\n    \"name\": \"泰和科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600232\",\n    \"name\": \"金鹰股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600549\",\n    \"name\": \"厦门钨业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000559\",\n    \"name\": \"万向钱潮\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688557\",\n    \"name\": \"兰剑智能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600309\",\n    \"name\": \"万华化学\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301219\",\n    \"name\": \"腾远钴业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002245\",\n    \"name\": \"蔚蓝锂芯\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300438\",\n    \"name\": \"鹏辉能源\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300497\",\n    \"name\": \"富祥药业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000570\",\n    \"name\": \"苏常柴Ａ\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600839\",\n    \"name\": \"四川长虹\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300587\",\n    \"name\": \"天铁股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002074\",\n    \"name\": \"国轩高科\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601222\",\n    \"name\": \"林洋能源\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688006\",\n    \"name\": \"杭可科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600516\",\n    \"name\": \"方大炭素\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688772\",\n    \"name\": \"珠海冠宇\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300487\",\n    \"name\": \"蓝晓科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300457\",\n    \"name\": \"赢合科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001203\",\n    \"name\": \"大中矿业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002812\",\n    \"name\": \"恩捷股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603799\",\n    \"name\": \"华友钴业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605589\",\n    \"name\": \"圣泉集团\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002386\",\n    \"name\": \"天原股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300217\",\n    \"name\": \"东方电热\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688360\",\n    \"name\": \"德马科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601311\",\n    \"name\": \"骆驼股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300116\",\n    \"name\": \"保力新\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300019\",\n    \"name\": \"硅宝科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002709\",\n    \"name\": \"天赐材料\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603010\",\n    \"name\": \"万盛股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600525\",\n    \"name\": \"长园集团\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002460\",\n    \"name\": \"赣锋锂业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603659\",\n    \"name\": \"璞泰来\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600955\",\n    \"name\": \"维远股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300510\",\n    \"name\": \"金冠股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002418\",\n    \"name\": \"康盛股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300035\",\n    \"name\": \"中科电气\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688599\",\n    \"name\": \"天合光能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300567\",\n    \"name\": \"精测电子\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300390\",\n    \"name\": \"天华新能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300568\",\n    \"name\": \"星源材质\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603679\",\n    \"name\": \"华体科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002240\",\n    \"name\": \"盛新锂能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600869\",\n    \"name\": \"远东股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603067\",\n    \"name\": \"振华股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300376\",\n    \"name\": \"易事特\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300041\",\n    \"name\": \"回天新材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688516\",\n    \"name\": \"奥特维\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688559\",\n    \"name\": \"海目星\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601969\",\n    \"name\": \"海南矿业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002045\",\n    \"name\": \"国光电器\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002091\",\n    \"name\": \"江苏国泰\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002121\",\n    \"name\": \"科陆电子\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002089\",\n    \"name\": \"*ST新海\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002734\",\n    \"name\": \"利民股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300545\",\n    \"name\": \"联得装备\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688518\",\n    \"name\": \"联赢激光\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002426\",\n    \"name\": \"胜利精密\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002407\",\n    \"name\": \"多氟多\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600152\",\n    \"name\": \"维科技术\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002733\",\n    \"name\": \"雄韬股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603283\",\n    \"name\": \"赛腾股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000976\",\n    \"name\": \"ST华铁\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600773\",\n    \"name\": \"西藏城投\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002309\",\n    \"name\": \"ST中利\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002145\",\n    \"name\": \"中核钛白\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002340\",\n    \"name\": \"格林美\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300128\",\n    \"name\": \"锦富技术\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300080\",\n    \"name\": \"易成新能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002850\",\n    \"name\": \"科达利\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002256\",\n    \"name\": \"兆新股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002052\",\n    \"name\": \"ST同洲\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688005\",\n    \"name\": \"容百科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002312\",\n    \"name\": \"川发龙蟒\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603196\",\n    \"name\": \"日播时尚\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688529\",\n    \"name\": \"豪森智能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600711\",\n    \"name\": \"盛屯矿业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603220\",\n    \"name\": \"中贝通信\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603303\",\n    \"name\": \"得邦照明\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002518\",\n    \"name\": \"科士达\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688499\",\n    \"name\": \"利元亨\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600731\",\n    \"name\": \"湖南海利\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603806\",\n    \"name\": \"福斯特\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600478\",\n    \"name\": \"科力远\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300538\",\n    \"name\": \"同益股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300082\",\n    \"name\": \"奥克股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601777\",\n    \"name\": \"力帆科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002759\",\n    \"name\": \"天际股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300919\",\n    \"name\": \"中伟股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002539\",\n    \"name\": \"云图控股\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300198\",\n    \"name\": \"纳川股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603399\",\n    \"name\": \"吉翔股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300428\",\n    \"name\": \"立中集团\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688045\",\n    \"name\": \"必易微\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688353\",\n    \"name\": \"华盛锂电\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300707\",\n    \"name\": \"威唐工业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002298\",\n    \"name\": \"中电兴发\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688567\",\n    \"name\": \"孚能科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603823\",\n    \"name\": \"百合花\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600854\",\n    \"name\": \"春兰股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300062\",\n    \"name\": \"中能电气\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002877\",\n    \"name\": \"智能自控\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301121\",\n    \"name\": \"紫建电子\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600884\",\n    \"name\": \"杉杉股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000049\",\n    \"name\": \"德赛电池\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300565\",\n    \"name\": \"科信技术\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002125\",\n    \"name\": \"湘潭电化\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002591\",\n    \"name\": \"恒大高新\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300953\",\n    \"name\": \"震裕科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000545\",\n    \"name\": \"金浦钛业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603617\",\n    \"name\": \"君禾股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002324\",\n    \"name\": \"普利特\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002068\",\n    \"name\": \"黑猫股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002598\",\n    \"name\": \"山东章鼓\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300409\",\n    \"name\": \"道氏技术\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301150\",\n    \"name\": \"中一科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002056\",\n    \"name\": \"横店东磁\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301086\",\n    \"name\": \"鸿富瀚\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688345\",\n    \"name\": \"博力威\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600110\",\n    \"name\": \"诺德股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002076\",\n    \"name\": \"星光股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002584\",\n    \"name\": \"西陇科学\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688116\",\n    \"name\": \"天奈科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002662\",\n    \"name\": \"京威股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001217\",\n    \"name\": \"华尔泰\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688148\",\n    \"name\": \"芳源股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300018\",\n    \"name\": \"中元股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600067\",\n    \"name\": \"冠城大通\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300876\",\n    \"name\": \"蒙泰高新\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603906\",\n    \"name\": \"龙蟠科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300586\",\n    \"name\": \"美联新材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002346\",\n    \"name\": \"柘中股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300501\",\n    \"name\": \"海顺新材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300317\",\n    \"name\": \"珈伟新能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300077\",\n    \"name\": \"国民技术\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300490\",\n    \"name\": \"华自科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301217\",\n    \"name\": \"铜冠铜箔\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300340\",\n    \"name\": \"科恒股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000636\",\n    \"name\": \"风华高科\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300618\",\n    \"name\": \"寒锐钴业\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603928\",\n    \"name\": \"兴业股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300655\",\n    \"name\": \"晶瑞电材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300432\",\n    \"name\": \"富临精工\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688598\",\n    \"name\": \"金博股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688560\",\n    \"name\": \"明冠新材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300327\",\n    \"name\": \"中颖电子\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002617\",\n    \"name\": \"露笑科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600241\",\n    \"name\": \"时代万恒\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300647\",\n    \"name\": \"超频三\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300117\",\n    \"name\": \"嘉寓股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002213\",\n    \"name\": \"大为股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002859\",\n    \"name\": \"洁美科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300484\",\n    \"name\": \"蓝海华腾\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832522\",\n    \"name\": \"纳科诺尔\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002136\",\n    \"name\": \"安 纳 达\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300520\",\n    \"name\": \"科大国创\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603186\",\n    \"name\": \"华正新材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300410\",\n    \"name\": \"正业科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300631\",\n    \"name\": \"久吾高科\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300283\",\n    \"name\": \"温州宏丰\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300582\",\n    \"name\": \"英飞特\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000973\",\n    \"name\": \"佛塑科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300890\",\n    \"name\": \"翔丰华\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300985\",\n    \"name\": \"致远新能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603618\",\n    \"name\": \"杭电股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600367\",\n    \"name\": \"红星发展\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600330\",\n    \"name\": \"天通股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300619\",\n    \"name\": \"金银河\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688733\",\n    \"name\": \"壹石通\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300585\",\n    \"name\": \"奥联电子\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688173\",\n    \"name\": \"希荻微\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688097\",\n    \"name\": \"博众精工\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300477\",\n    \"name\": \"合纵科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300980\",\n    \"name\": \"祥源新材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833284\",\n    \"name\": \"灵鸽科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603335\",\n    \"name\": \"迪生力\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688388\",\n    \"name\": \"嘉元科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002580\",\n    \"name\": \"圣阳股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603088\",\n    \"name\": \"宁波精达\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688325\",\n    \"name\": \"赛微微电\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688383\",\n    \"name\": \"新益昌\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300252\",\n    \"name\": \"金信诺\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002455\",\n    \"name\": \"百川股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688595\",\n    \"name\": \"芯海科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300648\",\n    \"name\": \"星云股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688181\",\n    \"name\": \"八亿时空\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688778\",\n    \"name\": \"厦钨新能\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002823\",\n    \"name\": \"凯中精密\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605376\",\n    \"name\": \"博迁新材\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300518\",\n    \"name\": \"新迅达\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002288\",\n    \"name\": \"超华科技\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601515\",\n    \"name\": \"东峰集团\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831627\",\n    \"name\": \"力王股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301180\",\n    \"name\": \"万祥科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002141\",\n    \"name\": \"贤丰控股\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603315\",\n    \"name\": \"福鞍股份\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300998\",\n    \"name\": \"宁波方正\",\n    \"tag\": \"赛道\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900940\",\n    \"name\": \"大名城B\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900903\",\n    \"name\": \"大众Ｂ股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000671\",\n    \"name\": \"ST阳光城\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000048\",\n    \"name\": \"京基智农\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900932\",\n    \"name\": \"陆家Ｂ股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900911\",\n    \"name\": \"金桥Ｂ股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900902\",\n    \"name\": \"市北B股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900912\",\n    \"name\": \"外高Ｂ股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900939\",\n    \"name\": \"汇丽B\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"900928\",\n    \"name\": \"临港B股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600239\",\n    \"name\": \"云南城投\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600683\",\n    \"name\": \"京投发展\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600692\",\n    \"name\": \"亚通股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"200029\",\n    \"name\": \"深深房Ｂ\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000090\",\n    \"name\": \"天健集团\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600848\",\n    \"name\": \"上海临港\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600325\",\n    \"name\": \"华发股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000897\",\n    \"name\": \"津滨发展\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600266\",\n    \"name\": \"城建发展\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600383\",\n    \"name\": \"金地集团\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002244\",\n    \"name\": \"滨江集团\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"200011\",\n    \"name\": \"深物业B\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000002\",\n    \"name\": \"万  科Ａ\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000006\",\n    \"name\": \"深振业Ａ\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000011\",\n    \"name\": \"深物业A\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601155\",\n    \"name\": \"新城控股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600823\",\n    \"name\": \"ST世茂\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600791\",\n    \"name\": \"京能置业\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000036\",\n    \"name\": \"华联控股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600895\",\n    \"name\": \"张江高科\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600064\",\n    \"name\": \"南京高科\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600340\",\n    \"name\": \"华夏幸福\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600649\",\n    \"name\": \"城投控股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600159\",\n    \"name\": \"大龙地产\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000517\",\n    \"name\": \"荣安地产\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600376\",\n    \"name\": \"首开股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000402\",\n    \"name\": \"金 融 街\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600736\",\n    \"name\": \"苏州高新\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600603\",\n    \"name\": \"广汇物流\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600638\",\n    \"name\": \"新黄浦\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600208\",\n    \"name\": \"新湖中宝\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000886\",\n    \"name\": \"海南高速\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000631\",\n    \"name\": \"顺发恒业\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600807\",\n    \"name\": \"济南高新\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601512\",\n    \"name\": \"中新集团\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600748\",\n    \"name\": \"上实发展\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000863\",\n    \"name\": \"三湘印象\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600606\",\n    \"name\": \"绿地控股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600663\",\n    \"name\": \"陆家嘴\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000042\",\n    \"name\": \"中洲控股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000965\",\n    \"name\": \"天保基建\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600173\",\n    \"name\": \"卧龙地产\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000718\",\n    \"name\": \"苏宁环球\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000514\",\n    \"name\": \"渝 开 发\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600162\",\n    \"name\": \"香江控股\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600716\",\n    \"name\": \"凤凰股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002016\",\n    \"name\": \"世荣兆业\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600094\",\n    \"name\": \"大名城\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000809\",\n    \"name\": \"铁岭新城\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600322\",\n    \"name\": \"津投城开\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600743\",\n    \"name\": \"华远地产\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600082\",\n    \"name\": \"海泰发展\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000609\",\n    \"name\": \"中迪投资\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600533\",\n    \"name\": \"栖霞建设\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600665\",\n    \"name\": \"天地源\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002133\",\n    \"name\": \"广宇集团\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000926\",\n    \"name\": \"福星股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002208\",\n    \"name\": \"合肥城建\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600510\",\n    \"name\": \"黑牡丹\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600185\",\n    \"name\": \"格力地产\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000620\",\n    \"name\": \"*ST新联\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000573\",\n    \"name\": \"粤宏远Ａ\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"200056\",\n    \"name\": \"皇庭B\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000838\",\n    \"name\": \"财信发展\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600463\",\n    \"name\": \"空港股份\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600641\",\n    \"name\": \"万业企业\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600246\",\n    \"name\": \"万通发展\",\n    \"tag\": \"房地产\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430300\",\n    \"name\": \"辰光医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832278\",\n    \"name\": \"鹿得医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"870199\",\n    \"name\": \"倍益康\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"872925\",\n    \"name\": \"锦好医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603658\",\n    \"name\": \"安图生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688301\",\n    \"name\": \"奕瑞科技\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688198\",\n    \"name\": \"佰仁医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688617\",\n    \"name\": \"惠泰医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300832\",\n    \"name\": \"新产业\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688050\",\n    \"name\": \"爱博医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688399\",\n    \"name\": \"硕世生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688289\",\n    \"name\": \"圣湘生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688366\",\n    \"name\": \"昊海生科\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688575\",\n    \"name\": \"亚辉龙\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002223\",\n    \"name\": \"鱼跃医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688016\",\n    \"name\": \"心脉医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688217\",\n    \"name\": \"睿昂基因\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688626\",\n    \"name\": \"翔宇医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301093\",\n    \"name\": \"华兰股份\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300677\",\n    \"name\": \"英科医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688298\",\n    \"name\": \"东方生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300760\",\n    \"name\": \"迈瑞医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002030\",\n    \"name\": \"达安基因\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300406\",\n    \"name\": \"九强生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688212\",\n    \"name\": \"澳华内镜\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688075\",\n    \"name\": \"安旭生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605369\",\n    \"name\": \"拱东医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300633\",\n    \"name\": \"开立医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300676\",\n    \"name\": \"华大基因\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688767\",\n    \"name\": \"博拓生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300049\",\n    \"name\": \"福瑞股份\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600529\",\n    \"name\": \"山东药玻\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300529\",\n    \"name\": \"健帆生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688677\",\n    \"name\": \"海泰新光\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300003\",\n    \"name\": \"乐普医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688105\",\n    \"name\": \"诺唯赞\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688580\",\n    \"name\": \"伟思医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300685\",\n    \"name\": \"艾德生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300171\",\n    \"name\": \"东富龙\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301087\",\n    \"name\": \"可孚医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688139\",\n    \"name\": \"海尔生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002901\",\n    \"name\": \"大博医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002932\",\n    \"name\": \"明德生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603387\",\n    \"name\": \"基蛋生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603880\",\n    \"name\": \"ST南卫\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000710\",\n    \"name\": \"贝瑞基因\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300595\",\n    \"name\": \"欧普康视\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300463\",\n    \"name\": \"迈克生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300482\",\n    \"name\": \"万孚生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688317\",\n    \"name\": \"之江生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002022\",\n    \"name\": \"科华生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603987\",\n    \"name\": \"康德莱\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300439\",\n    \"name\": \"美康生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300642\",\n    \"name\": \"透景生命\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688389\",\n    \"name\": \"普门科技\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600587\",\n    \"name\": \"新华医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688606\",\n    \"name\": \"奥泰生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688338\",\n    \"name\": \"赛科希德\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688273\",\n    \"name\": \"麦澜德\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300314\",\n    \"name\": \"戴维医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688068\",\n    \"name\": \"热景生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300206\",\n    \"name\": \"理邦仪器\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300298\",\n    \"name\": \"三诺生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688161\",\n    \"name\": \"威高骨科\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002950\",\n    \"name\": \"奥美医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300289\",\n    \"name\": \"利德曼\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300238\",\n    \"name\": \"冠昊生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603309\",\n    \"name\": \"维力医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688236\",\n    \"name\": \"春立医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688013\",\n    \"name\": \"天臣医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300318\",\n    \"name\": \"博晖创新\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603301\",\n    \"name\": \"振德医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300639\",\n    \"name\": \"凯普生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300981\",\n    \"name\": \"中红医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603976\",\n    \"name\": \"正川股份\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002382\",\n    \"name\": \"蓝帆医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300869\",\n    \"name\": \"康泰医学\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300326\",\n    \"name\": \"凯利泰\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300453\",\n    \"name\": \"三鑫医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002551\",\n    \"name\": \"尚荣医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301234\",\n    \"name\": \"五洲医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688656\",\n    \"name\": \"浩欧博\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688468\",\n    \"name\": \"科美诊断\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300653\",\n    \"name\": \"正海生物\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301060\",\n    \"name\": \"兰卫医学\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002817\",\n    \"name\": \"黄山胶囊\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688108\",\n    \"name\": \"赛诺医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688085\",\n    \"name\": \"三友医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301122\",\n    \"name\": \"采纳股份\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600055\",\n    \"name\": \"万东医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300246\",\n    \"name\": \"宝莱特\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301097\",\n    \"name\": \"天益医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300753\",\n    \"name\": \"爱朋医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688067\",\n    \"name\": \"爱威科技\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300562\",\n    \"name\": \"乐心医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688314\",\n    \"name\": \"康拓医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688393\",\n    \"name\": \"安必平\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688358\",\n    \"name\": \"祥生医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688607\",\n    \"name\": \"康众医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300030\",\n    \"name\": \"阳普医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688613\",\n    \"name\": \"奥精医疗\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688029\",\n    \"name\": \"南微医学\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603122\",\n    \"name\": \"合富中国\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000668\",\n    \"name\": \"荣丰控股\",\n    \"tag\": \"医疗器械\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836433\",\n    \"name\": \"大唐药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833266\",\n    \"name\": \"生物谷\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832566\",\n    \"name\": \"梓橦宫\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300039\",\n    \"name\": \"上海凯宝\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000538\",\n    \"name\": \"云南白药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600538\",\n    \"name\": \"国发股份\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600998\",\n    \"name\": \"九州通\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002424\",\n    \"name\": \"贵州百灵\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601607\",\n    \"name\": \"上海医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000513\",\n    \"name\": \"丽珠集团\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600518\",\n    \"name\": \"ST康美\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600436\",\n    \"name\": \"片仔癀\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600976\",\n    \"name\": \"健民集团\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002737\",\n    \"name\": \"葵花药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002198\",\n    \"name\": \"嘉应制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000623\",\n    \"name\": \"吉林敖东\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000950\",\n    \"name\": \"重药控股\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600566\",\n    \"name\": \"济川药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603883\",\n    \"name\": \"老百姓\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600993\",\n    \"name\": \"马应龙\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600085\",\n    \"name\": \"同仁堂\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002019\",\n    \"name\": \"亿帆医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600103\",\n    \"name\": \"青山纸业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600285\",\n    \"name\": \"羚锐制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000963\",\n    \"name\": \"华东医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603858\",\n    \"name\": \"步长制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002727\",\n    \"name\": \"一心堂\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600380\",\n    \"name\": \"健康元\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002793\",\n    \"name\": \"罗欣药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600572\",\n    \"name\": \"康恩贝\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000661\",\n    \"name\": \"长春高新\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002603\",\n    \"name\": \"以岭药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002275\",\n    \"name\": \"桂林三金\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603368\",\n    \"name\": \"柳药集团\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600479\",\n    \"name\": \"千金药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600276\",\n    \"name\": \"恒瑞医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600351\",\n    \"name\": \"亚宝药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002589\",\n    \"name\": \"瑞康医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000650\",\n    \"name\": \"仁和药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002435\",\n    \"name\": \"长江健康\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603896\",\n    \"name\": \"寿仙谷\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301015\",\n    \"name\": \"百洋医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002773\",\n    \"name\": \"康弘药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300009\",\n    \"name\": \"安科生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300485\",\n    \"name\": \"赛升药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002020\",\n    \"name\": \"京新药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002349\",\n    \"name\": \"精华制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600422\",\n    \"name\": \"昆药集团\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600881\",\n    \"name\": \"亚泰集团\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002873\",\n    \"name\": \"新天药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000989\",\n    \"name\": \"九 芝 堂\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600211\",\n    \"name\": \"西藏药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000919\",\n    \"name\": \"金陵药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300086\",\n    \"name\": \"康芝药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600222\",\n    \"name\": \"太龙药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002287\",\n    \"name\": \"奇正藏药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000705\",\n    \"name\": \"浙江震元\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688658\",\n    \"name\": \"悦康药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600664\",\n    \"name\": \"哈药股份\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002219\",\n    \"name\": \"新里程\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300434\",\n    \"name\": \"金石亚药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600329\",\n    \"name\": \"达仁堂\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000153\",\n    \"name\": \"丰原药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002433\",\n    \"name\": \"*ST太安\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300026\",\n    \"name\": \"红日药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002317\",\n    \"name\": \"众生药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002390\",\n    \"name\": \"信邦制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003020\",\n    \"name\": \"立方制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300158\",\n    \"name\": \"振东制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603567\",\n    \"name\": \"珍宝岛\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600671\",\n    \"name\": \"*ST目药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002412\",\n    \"name\": \"汉森制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600594\",\n    \"name\": \"益佰制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600535\",\n    \"name\": \"天士力\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688799\",\n    \"name\": \"华纳药厂\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002872\",\n    \"name\": \"ST天圣\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300267\",\n    \"name\": \"尔康制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002644\",\n    \"name\": \"佛慈制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603811\",\n    \"name\": \"诚意药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600829\",\n    \"name\": \"人民同泰\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600227\",\n    \"name\": \"赤天化\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688566\",\n    \"name\": \"吉贝尔\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300683\",\n    \"name\": \"海特生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300878\",\n    \"name\": \"维康药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002566\",\n    \"name\": \"益盛药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600557\",\n    \"name\": \"康缘药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002864\",\n    \"name\": \"盘龙药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300430\",\n    \"name\": \"诚益通\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600613\",\n    \"name\": \"神奇制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301111\",\n    \"name\": \"粤万年青\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300404\",\n    \"name\": \"博济医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600668\",\n    \"name\": \"尖峰集团\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600223\",\n    \"name\": \"福瑞达\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300519\",\n    \"name\": \"新光药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600080\",\n    \"name\": \"金花股份\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002675\",\n    \"name\": \"东诚药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000790\",\n    \"name\": \"华神科技\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002107\",\n    \"name\": \"沃华医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000955\",\n    \"name\": \"欣龙控股\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605199\",\n    \"name\": \"葫芦娃\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300147\",\n    \"name\": \"香雪制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002907\",\n    \"name\": \"华森制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300006\",\n    \"name\": \"莱美药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603998\",\n    \"name\": \"方盛制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002082\",\n    \"name\": \"万邦德\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000931\",\n    \"name\": \"中 关 村\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603139\",\n    \"name\": \"康惠制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688329\",\n    \"name\": \"艾隆科技\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300254\",\n    \"name\": \"仟源医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300412\",\n    \"name\": \"迦南科技\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300016\",\n    \"name\": \"北陆药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002826\",\n    \"name\": \"易明医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603439\",\n    \"name\": \"贵州三力\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603963\",\n    \"name\": \"大理药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000908\",\n    \"name\": \"景峰医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837403\",\n    \"name\": \"康农种业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300108\",\n    \"name\": \"ST吉药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430047\",\n    \"name\": \"诺思兰德\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833230\",\n    \"name\": \"欧康医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"839729\",\n    \"name\": \"永顺生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837344\",\n    \"name\": \"三元基因\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832982\",\n    \"name\": \"锦波生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833575\",\n    \"name\": \"康乐卫士\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688253\",\n    \"name\": \"英诺特\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002007\",\n    \"name\": \"华兰生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688488\",\n    \"name\": \"艾迪药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688278\",\n    \"name\": \"特宝生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688336\",\n    \"name\": \"三生国健\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002252\",\n    \"name\": \"上海莱士\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300122\",\n    \"name\": \"智飞生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688276\",\n    \"name\": \"百克生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000403\",\n    \"name\": \"派林生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688331\",\n    \"name\": \"荣昌生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688739\",\n    \"name\": \"成大生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603590\",\n    \"name\": \"康辰药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688520\",\n    \"name\": \"神州细胞-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688163\",\n    \"name\": \"赛伦生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688062\",\n    \"name\": \"迈威生物-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600867\",\n    \"name\": \"通化东宝\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002038\",\n    \"name\": \"双鹭药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300357\",\n    \"name\": \"我武生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688235\",\n    \"name\": \"百济神州-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300601\",\n    \"name\": \"康泰生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688180\",\n    \"name\": \"君实生物-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300142\",\n    \"name\": \"沃森生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301207\",\n    \"name\": \"华兰疫苗\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300204\",\n    \"name\": \"舒泰神\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002581\",\n    \"name\": \"未名医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603087\",\n    \"name\": \"甘李药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301047\",\n    \"name\": \"义翘神州\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000518\",\n    \"name\": \"四环生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688382\",\n    \"name\": \"益方生物-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600530\",\n    \"name\": \"ST交昂\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300841\",\n    \"name\": \"康华生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688197\",\n    \"name\": \"首药控股-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688176\",\n    \"name\": \"亚虹医药-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688193\",\n    \"name\": \"仁度生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002693\",\n    \"name\": \"双成药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600645\",\n    \"name\": \"中源协和\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688185\",\n    \"name\": \"康希诺\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688687\",\n    \"name\": \"凯因科技\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688373\",\n    \"name\": \"盟科药业-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688302\",\n    \"name\": \"海创药业-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688136\",\n    \"name\": \"科兴制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000534\",\n    \"name\": \"万泽股份\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300239\",\n    \"name\": \"东宝生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002880\",\n    \"name\": \"卫光生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688177\",\n    \"name\": \"百奥泰\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688319\",\n    \"name\": \"欧林生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688265\",\n    \"name\": \"南模生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688046\",\n    \"name\": \"药康生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002437\",\n    \"name\": \"誉衡药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300199\",\n    \"name\": \"翰宇药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300255\",\n    \"name\": \"常山药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002294\",\n    \"name\": \"信立泰\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300558\",\n    \"name\": \"贝达药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600267\",\n    \"name\": \"海正药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002422\",\n    \"name\": \"科伦药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600196\",\n    \"name\": \"复星医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000739\",\n    \"name\": \"普洛药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600216\",\n    \"name\": \"浙江医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000597\",\n    \"name\": \"东北制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688578\",\n    \"name\": \"艾力斯\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002653\",\n    \"name\": \"海思科\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002755\",\n    \"name\": \"奥赛康\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002399\",\n    \"name\": \"海普瑞\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688189\",\n    \"name\": \"南新制药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002923\",\n    \"name\": \"润都股份\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688166\",\n    \"name\": \"博瑞医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300149\",\n    \"name\": \"睿智医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688321\",\n    \"name\": \"微芯生物\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688221\",\n    \"name\": \"前沿生物-U\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300725\",\n    \"name\": \"药石科技\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688131\",\n    \"name\": \"皓元医药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603127\",\n    \"name\": \"昭衍新药\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002898\",\n    \"name\": \"赛隆药业\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688356\",\n    \"name\": \"键凯科技\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300436\",\n    \"name\": \"广生堂\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002900\",\n    \"name\": \"哈三联\",\n    \"tag\": \"医药\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002173\",\n    \"name\": \"创新医疗\",\n    \"tag\": \"脑机\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300459\",\n    \"name\": \"汤姆猫\",\n    \"tag\": \"脑机\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002195\",\n    \"name\": \"岩山科技\",\n    \"tag\": \"脑机\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002602\",\n    \"name\": \"世纪华通\",\n    \"tag\": \"脑机\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300007\",\n    \"name\": \"汉威科技\",\n    \"tag\": \"脑机\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002362\",\n    \"name\": \"汉王科技\",\n    \"tag\": \"脑机\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300052\",\n    \"name\": \"中青宝\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300017\",\n    \"name\": \"网宿科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300442\",\n    \"name\": \"润泽科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000818\",\n    \"name\": \"航锦科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002042\",\n    \"name\": \"华孚时尚\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002575\",\n    \"name\": \"群兴玩具\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000977\",\n    \"name\": \"浪潮信息\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002902\",\n    \"name\": \"铭普光磁\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300166\",\n    \"name\": \"东方国信\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688327\",\n    \"name\": \"云从科技-UW\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603881\",\n    \"name\": \"数据港\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300339\",\n    \"name\": \"润和软件\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300113\",\n    \"name\": \"顺网科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688008\",\n    \"name\": \"澜起科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300383\",\n    \"name\": \"光环新网\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300657\",\n    \"name\": \"弘信电子\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002929\",\n    \"name\": \"润建股份\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600589\",\n    \"name\": \"*ST榕泰\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600804\",\n    \"name\": \"ST鹏博士\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000925\",\n    \"name\": \"众合科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002822\",\n    \"name\": \"中装建设\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600666\",\n    \"name\": \"ST瑞德\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002875\",\n    \"name\": \"安奈儿\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300020\",\n    \"name\": \"银江技术\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603636\",\n    \"name\": \"南威软件\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002657\",\n    \"name\": \"中科金财\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837092\",\n    \"name\": \"汉鑫科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300458\",\n    \"name\": \"全志科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300474\",\n    \"name\": \"景嘉微\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300738\",\n    \"name\": \"奥飞数据\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300344\",\n    \"name\": \"立方数科\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300846\",\n    \"name\": \"首都在线\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300798\",\n    \"name\": \"锦鸡股份\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002912\",\n    \"name\": \"中新赛克\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688047\",\n    \"name\": \"龙芯中科\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300571\",\n    \"name\": \"平治信息\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300826\",\n    \"name\": \"测绘股份\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301085\",\n    \"name\": \"亚康股份\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688135\",\n    \"name\": \"利扬芯片\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603629\",\n    \"name\": \"利通电子\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688589\",\n    \"name\": \"力合微\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688158\",\n    \"name\": \"优刻得-W\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300053\",\n    \"name\": \"航宇微\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300609\",\n    \"name\": \"汇纳科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688256\",\n    \"name\": \"寒武纪-U\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688049\",\n    \"name\": \"炬芯科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"839493\",\n    \"name\": \"并行科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300825\",\n    \"name\": \"阿尔特\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688787\",\n    \"name\": \"海天瑞声\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301308\",\n    \"name\": \"江波龙\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688099\",\n    \"name\": \"晶晨股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300373\",\n    \"name\": \"扬杰科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688120\",\n    \"name\": \"华海清科\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300661\",\n    \"name\": \"圣邦股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300042\",\n    \"name\": \"朗科科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688981\",\n    \"name\": \"中芯国际\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688332\",\n    \"name\": \"中科蓝讯\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688728\",\n    \"name\": \"格科微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688766\",\n    \"name\": \"普冉股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688123\",\n    \"name\": \"聚辰股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002156\",\n    \"name\": \"通富微电\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688153\",\n    \"name\": \"唯捷创芯\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688130\",\n    \"name\": \"晶华微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688126\",\n    \"name\": \"沪硅产业\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688279\",\n    \"name\": \"峰岹科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688012\",\n    \"name\": \"中微公司\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600584\",\n    \"name\": \"长电科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300666\",\n    \"name\": \"江丰电子\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002079\",\n    \"name\": \"苏州固锝\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688072\",\n    \"name\": \"拓荆科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688002\",\n    \"name\": \"睿创微纳\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605588\",\n    \"name\": \"冠石科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300782\",\n    \"name\": \"卓胜微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002185\",\n    \"name\": \"华天科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688167\",\n    \"name\": \"炬光科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688082\",\n    \"name\": \"盛美上海\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603290\",\n    \"name\": \"斯达半导\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688368\",\n    \"name\": \"晶丰明源\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603160\",\n    \"name\": \"汇顶科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603986\",\n    \"name\": \"兆易创新\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688200\",\n    \"name\": \"华峰测控\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603893\",\n    \"name\": \"瑞芯微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688052\",\n    \"name\": \"纳芯微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300139\",\n    \"name\": \"晓程科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600460\",\n    \"name\": \"士兰微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603501\",\n    \"name\": \"韦尔股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688385\",\n    \"name\": \"复旦微电\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300604\",\n    \"name\": \"长川科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688608\",\n    \"name\": \"恒玄科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000670\",\n    \"name\": \"盈方微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688536\",\n    \"name\": \"思瑞浦\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300493\",\n    \"name\": \"润欣科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300223\",\n    \"name\": \"北京君正\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688213\",\n    \"name\": \"思特威-W\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688037\",\n    \"name\": \"芯源微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301050\",\n    \"name\": \"雷电微力\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688521\",\n    \"name\": \"芯原股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688661\",\n    \"name\": \"和林微纳\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605358\",\n    \"name\": \"立昂微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688261\",\n    \"name\": \"东微半导\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600360\",\n    \"name\": \"华微电子\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300613\",\n    \"name\": \"富瀚微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688209\",\n    \"name\": \"英集芯\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300672\",\n    \"name\": \"国科微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688107\",\n    \"name\": \"安路科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688259\",\n    \"name\": \"创耀科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688230\",\n    \"name\": \"芯导科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688380\",\n    \"name\": \"中微半导\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300046\",\n    \"name\": \"台基股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002119\",\n    \"name\": \"康强电子\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300706\",\n    \"name\": \"阿石创\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688286\",\n    \"name\": \"敏芯股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688220\",\n    \"name\": \"翱捷科技-U\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688138\",\n    \"name\": \"清溢光电\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301095\",\n    \"name\": \"广立微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688018\",\n    \"name\": \"乐鑫科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688270\",\n    \"name\": \"臻镭科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001270\",\n    \"name\": \"铖昌科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688508\",\n    \"name\": \"芯朋微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688689\",\n    \"name\": \"银河微电\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300831\",\n    \"name\": \"派瑞股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603005\",\n    \"name\": \"晶方科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603068\",\n    \"name\": \"博通集成\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300671\",\n    \"name\": \"富满微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003026\",\n    \"name\": \"中晶科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688711\",\n    \"name\": \"宏微科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688048\",\n    \"name\": \"长光华芯\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688110\",\n    \"name\": \"东芯股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688262\",\n    \"name\": \"国芯科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688699\",\n    \"name\": \"明微电子\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688601\",\n    \"name\": \"力芯微\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688798\",\n    \"name\": \"艾为电子\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688234\",\n    \"name\": \"天岳先进\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688233\",\n    \"name\": \"神工股份\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001309\",\n    \"name\": \"德明利\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603933\",\n    \"name\": \"睿能科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688216\",\n    \"name\": \"气派科技\",\n    \"tag\": \"半导体\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832876\",\n    \"name\": \"慧为智能\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603633\",\n    \"name\": \"徕木股份\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831167\",\n    \"name\": \"鑫汇科\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003028\",\n    \"name\": \"振邦智能\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001308\",\n    \"name\": \"康冠科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688036\",\n    \"name\": \"传音控股\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002881\",\n    \"name\": \"美格智能\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002681\",\n    \"name\": \"奋达科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002475\",\n    \"name\": \"立讯精密\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300735\",\n    \"name\": \"光弘科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301067\",\n    \"name\": \"显盈科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002925\",\n    \"name\": \"盈趣科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002937\",\n    \"name\": \"兴瑞科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605277\",\n    \"name\": \"新亚电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300866\",\n    \"name\": \"安克创新\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300256\",\n    \"name\": \"星星科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002841\",\n    \"name\": \"视源股份\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300032\",\n    \"name\": \"金龙机电\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002351\",\n    \"name\": \"漫步者\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002993\",\n    \"name\": \"奥海科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300322\",\n    \"name\": \"硕贝德\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603133\",\n    \"name\": \"*ST碳元\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300679\",\n    \"name\": \"电连技术\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002402\",\n    \"name\": \"和而泰\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300136\",\n    \"name\": \"信维通信\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603380\",\n    \"name\": \"易德龙\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002782\",\n    \"name\": \"可立克\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600898\",\n    \"name\": \"ST美讯\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688260\",\n    \"name\": \"昀冢科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002861\",\n    \"name\": \"瀛通通讯\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301123\",\n    \"name\": \"奕东电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002866\",\n    \"name\": \"传艺科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688007\",\n    \"name\": \"光峰科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600745\",\n    \"name\": \"闻泰科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300686\",\n    \"name\": \"智动力\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603626\",\n    \"name\": \"科森科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600130\",\n    \"name\": \"波导股份\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002369\",\n    \"name\": \"卓翼科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300709\",\n    \"name\": \"精研科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002885\",\n    \"name\": \"京泉华\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300951\",\n    \"name\": \"博硕科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300787\",\n    \"name\": \"海能实业\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600203\",\n    \"name\": \"福日电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603327\",\n    \"name\": \"福蓉科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300684\",\n    \"name\": \"中石科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300976\",\n    \"name\": \"达瑞电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300916\",\n    \"name\": \"朗特智能\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301318\",\n    \"name\": \"维海德\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300857\",\n    \"name\": \"协创数据\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002947\",\n    \"name\": \"恒铭达\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688678\",\n    \"name\": \"福立旺\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688683\",\n    \"name\": \"莱尔科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301135\",\n    \"name\": \"瑞德智能\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601231\",\n    \"name\": \"环旭电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300843\",\n    \"name\": \"胜蓝股份\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603595\",\n    \"name\": \"东尼电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300956\",\n    \"name\": \"英力股份\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300602\",\n    \"name\": \"飞荣达\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"872190\",\n    \"name\": \"雷神科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002055\",\n    \"name\": \"得润电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603890\",\n    \"name\": \"春秋电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833346\",\n    \"name\": \"威贸电子\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301182\",\n    \"name\": \"凯旺科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300822\",\n    \"name\": \"贝仕达克\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001229\",\n    \"name\": \"魅视科技\",\n    \"tag\": \"消费电子\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300045\",\n    \"name\": \"华力创通\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300098\",\n    \"name\": \"高新兴\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002512\",\n    \"name\": \"达华智能\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002413\",\n    \"name\": \"雷科防务\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002023\",\n    \"name\": \"海特高新\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002669\",\n    \"name\": \"康达新材\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002829\",\n    \"name\": \"星网宇达\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300123\",\n    \"name\": \"亚光科技\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002446\",\n    \"name\": \"盛路通信\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300366\",\n    \"name\": \"创意信息\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002465\",\n    \"name\": \"海格通信\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300101\",\n    \"name\": \"振芯科技\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300903\",\n    \"name\": \"科翔股份\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688418\",\n    \"name\": \"震有科技\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300447\",\n    \"name\": \"全信股份\",\n    \"tag\": \"卫星\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002624\",\n    \"name\": \"完美世界\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300624\",\n    \"name\": \"万兴科技\",\n    \"tag\": \"AI\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002558\",\n    \"name\": \"巨人网络\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002123\",\n    \"name\": \"梦网科技\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300059\",\n    \"name\": \"东方财富\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000560\",\n    \"name\": \"我爱我家\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"830799\",\n    \"name\": \"艾融软件\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": \"北交所\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002970\",\n    \"name\": \"锐明技术\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002093\",\n    \"name\": \"国脉科技\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300033\",\n    \"name\": \"同花顺\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000829\",\n    \"name\": \"天音控股\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300608\",\n    \"name\": \"思特奇\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300542\",\n    \"name\": \"新晨科技\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300290\",\n    \"name\": \"荣科科技\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300496\",\n    \"name\": \"中科创达\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000997\",\n    \"name\": \"新 大 陆\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300352\",\n    \"name\": \"北信源\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000004\",\n    \"name\": \"国华网安\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000555\",\n    \"name\": \"神州信息\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300579\",\n    \"name\": \"数字认证\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002261\",\n    \"name\": \"拓维信息\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300250\",\n    \"name\": \"初灵信息\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300036\",\n    \"name\": \"超图软件\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300598\",\n    \"name\": \"诚迈科技\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300333\",\n    \"name\": \"兆日科技\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301221\",\n    \"name\": \"光庭信息\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002869\",\n    \"name\": \"金溢科技\",\n    \"tag\": \"鸿蒙\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000893\",\n    \"name\": \"亚钾国际\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000902\",\n    \"name\": \"新洋丰\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002470\",\n    \"name\": \"金正大\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002274\",\n    \"name\": \"华昌化工\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002588\",\n    \"name\": \"史丹利\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600141\",\n    \"name\": \"兴发集团\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002999\",\n    \"name\": \"天禾股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002538\",\n    \"name\": \"司尔特\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600078\",\n    \"name\": \"ST澄星\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300387\",\n    \"name\": \"富邦股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002170\",\n    \"name\": \"芭田股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000912\",\n    \"name\": \"泸天化\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002556\",\n    \"name\": \"辉隆股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600470\",\n    \"name\": \"六国化工\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600691\",\n    \"name\": \"阳煤化工\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603077\",\n    \"name\": \"和邦生物\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603979\",\n    \"name\": \"金诚信\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603993\",\n    \"name\": \"洛阳钼业\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002895\",\n    \"name\": \"川恒股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000713\",\n    \"name\": \"丰乐种业\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002237\",\n    \"name\": \"恒邦股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301118\",\n    \"name\": \"恒光股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300437\",\n    \"name\": \"清水源\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002942\",\n    \"name\": \"新农股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002409\",\n    \"name\": \"雅克科技\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600596\",\n    \"name\": \"新安股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600727\",\n    \"name\": \"鲁北化工\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688669\",\n    \"name\": \"聚石化学\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300610\",\n    \"name\": \"晨化股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688639\",\n    \"name\": \"华恒生物\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002001\",\n    \"name\": \"新 和 成\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600226\",\n    \"name\": \"瀚叶股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002648\",\n    \"name\": \"卫星化学\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002064\",\n    \"name\": \"华峰化学\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600352\",\n    \"name\": \"浙江龙盛\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600319\",\n    \"name\": \"亚星化学\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600425\",\n    \"name\": \"青松建化\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000683\",\n    \"name\": \"远兴能源\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301149\",\n    \"name\": \"隆华新材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300834\",\n    \"name\": \"星辉环材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301069\",\n    \"name\": \"凯盛新材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300848\",\n    \"name\": \"美瑞新材\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603980\",\n    \"name\": \"吉华集团\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000953\",\n    \"name\": \"河化股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600618\",\n    \"name\": \"氯碱化工\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300641\",\n    \"name\": \"正丹股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688268\",\n    \"name\": \"华特气体\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600423\",\n    \"name\": \"柳化股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301065\",\n    \"name\": \"本立科技\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300886\",\n    \"name\": \"华业香料\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600800\",\n    \"name\": \"渤海化学\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002971\",\n    \"name\": \"和远气体\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603867\",\n    \"name\": \"新化股份\",\n    \"tag\": \"化工\",\n    \"hidden_tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600651\",\n    \"name\": \"飞乐音响\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600602\",\n    \"name\": \"云赛智联\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600654\",\n    \"name\": \"ST中安\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000005\",\n    \"name\": \"ST星源\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000012\",\n    \"name\": \"南  玻Ａ\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600604\",\n    \"name\": \"市北高新\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600608\",\n    \"name\": \"ST沪科\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000007\",\n    \"name\": \"*ST全新\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000020\",\n    \"name\": \"深华发Ａ\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600605\",\n    \"name\": \"汇通能源\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600609\",\n    \"name\": \"金杯汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600611\",\n    \"name\": \"大众交通\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600612\",\n    \"name\": \"老凤祥\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600616\",\n    \"name\": \"金枫酒业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000019\",\n    \"name\": \"深粮控股\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600619\",\n    \"name\": \"海立股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600620\",\n    \"name\": \"天宸股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600623\",\n    \"name\": \"华谊集团\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000504\",\n    \"name\": \"南华生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600624\",\n    \"name\": \"复旦复华\",\n    \"tag\": \"创投\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600626\",\n    \"name\": \"申达股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600633\",\n    \"name\": \"浙数文化\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000506\",\n    \"name\": \"中润资源\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000507\",\n    \"name\": \"珠海港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000023\",\n    \"name\": \"ST深天\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000510\",\n    \"name\": \"新金路\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000509\",\n    \"name\": \"华塑控股\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600658\",\n    \"name\": \"电子城\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600650\",\n    \"name\": \"锦江在线\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600660\",\n    \"name\": \"福耀玻璃\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600661\",\n    \"name\": \"昂立教育\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600662\",\n    \"name\": \"外服控股\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600667\",\n    \"name\": \"太极实业\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000516\",\n    \"name\": \"国际医学\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600600\",\n    \"name\": \"青岛啤酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600676\",\n    \"name\": \"交运股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000030\",\n    \"name\": \"富奥股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000520\",\n    \"name\": \"凤凰航运\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000525\",\n    \"name\": \"ST红太阳\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000526\",\n    \"name\": \"学大教育\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600686\",\n    \"name\": \"金龙汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000524\",\n    \"name\": \"岭南控股\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000528\",\n    \"name\": \"柳    工\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600690\",\n    \"name\": \"海尔智家\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000541\",\n    \"name\": \"佛山照明\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000536\",\n    \"name\": \"华映科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000530\",\n    \"name\": \"冰山冷热\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000544\",\n    \"name\": \"中原环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000548\",\n    \"name\": \"湖南投资\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600802\",\n    \"name\": \"福建水泥\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600801\",\n    \"name\": \"华新水泥\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000533\",\n    \"name\": \"顺钠股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600811\",\n    \"name\": \"东方集团\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600810\",\n    \"name\": \"神马股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000551\",\n    \"name\": \"创元科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000563\",\n    \"name\": \"陕国投Ａ\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600812\",\n    \"name\": \"华北制药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600815\",\n    \"name\": \"厦工股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600817\",\n    \"name\": \"宇通重工\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600820\",\n    \"name\": \"隧道股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600818\",\n    \"name\": \"中路股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600826\",\n    \"name\": \"兰生股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600822\",\n    \"name\": \"上海物贸\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600833\",\n    \"name\": \"第一医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600834\",\n    \"name\": \"申通地铁\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600836\",\n    \"name\": \"上海易连\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600846\",\n    \"name\": \"同济科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600844\",\n    \"name\": \"丹化科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600841\",\n    \"name\": \"动力新科\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600847\",\n    \"name\": \"万里股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600851\",\n    \"name\": \"海欣股份\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600853\",\n    \"name\": \"龙建股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000565\",\n    \"name\": \"渝三峡Ａ\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000035\",\n    \"name\": \"中国天楹\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000039\",\n    \"name\": \"中集集团\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000034\",\n    \"name\": \"神州数码\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000561\",\n    \"name\": \"烽火电子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000567\",\n    \"name\": \"海德股份\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000557\",\n    \"name\": \"西部创业\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000572\",\n    \"name\": \"海马汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000045\",\n    \"name\": \"深纺织Ａ\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000576\",\n    \"name\": \"甘化科工\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000046\",\n    \"name\": \"*ST泛海\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600874\",\n    \"name\": \"创业环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000582\",\n    \"name\": \"北部湾港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600880\",\n    \"name\": \"博瑞传播\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600883\",\n    \"name\": \"博闻科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000586\",\n    \"name\": \"汇源通信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600885\",\n    \"name\": \"宏发股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600889\",\n    \"name\": \"南京化纤\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000589\",\n    \"name\": \"贵州轮胎\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000055\",\n    \"name\": \"方大集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000595\",\n    \"name\": \"宝塔实业\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000599\",\n    \"name\": \"青岛双星\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600707\",\n    \"name\": \"彩虹股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600703\",\n    \"name\": \"三安光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000598\",\n    \"name\": \"兴蓉环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600897\",\n    \"name\": \"厦门空港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600714\",\n    \"name\": \"金瑞矿业\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600704\",\n    \"name\": \"物产中大\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600717\",\n    \"name\": \"天津港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000401\",\n    \"name\": \"冀东水泥\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600718\",\n    \"name\": \"东软集团\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600722\",\n    \"name\": \"金牛化工\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600721\",\n    \"name\": \"百花医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000409\",\n    \"name\": \"云鼎科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600715\",\n    \"name\": \"文投控股\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600713\",\n    \"name\": \"南京医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000056\",\n    \"name\": \"皇庭国际\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600724\",\n    \"name\": \"宁波富达\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000411\",\n    \"name\": \"英特集团\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000415\",\n    \"name\": \"渤海租赁\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000416\",\n    \"name\": \"*ST民控\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600735\",\n    \"name\": \"新华锦\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000420\",\n    \"name\": \"吉林化纤\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600734\",\n    \"name\": \"ST实达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600733\",\n    \"name\": \"北汽蓝谷\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600739\",\n    \"name\": \"辽宁成大\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600741\",\n    \"name\": \"华域汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600742\",\n    \"name\": \"一汽富维\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000425\",\n    \"name\": \"徐工机械\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000430\",\n    \"name\": \"张家界\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600751\",\n    \"name\": \"海航科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000605\",\n    \"name\": \"渤海股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600746\",\n    \"name\": \"江苏索普\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600756\",\n    \"name\": \"浪潮软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600753\",\n    \"name\": \"庚星股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600755\",\n    \"name\": \"厦门国贸\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600761\",\n    \"name\": \"安徽合力\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600754\",\n    \"name\": \"锦江酒店\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600749\",\n    \"name\": \"西藏旅游\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000615\",\n    \"name\": \"*ST美谷\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000619\",\n    \"name\": \"海螺新材\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600766\",\n    \"name\": \"*ST园城\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600763\",\n    \"name\": \"通策医疗\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600769\",\n    \"name\": \"祥龙电业\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000622\",\n    \"name\": \"恒立实业\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600770\",\n    \"name\": \"综艺股份\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000632\",\n    \"name\": \"三木集团\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000638\",\n    \"name\": \"万方发展\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000652\",\n    \"name\": \"泰达股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000655\",\n    \"name\": \"金岭矿业\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000626\",\n    \"name\": \"远大控股\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000659\",\n    \"name\": \"珠海中富\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000672\",\n    \"name\": \"上峰水泥\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600783\",\n    \"name\": \"鲁信创投\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600784\",\n    \"name\": \"鲁银投资\",\n    \"tag\": \"创投\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600782\",\n    \"name\": \"新钢股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000677\",\n    \"name\": \"恒天海龙\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000678\",\n    \"name\": \"襄阳轴承\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000061\",\n    \"name\": \"农 产 品\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000682\",\n    \"name\": \"东方电子\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000680\",\n    \"name\": \"山推股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000685\",\n    \"name\": \"中山公用\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000062\",\n    \"name\": \"深圳华强\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600793\",\n    \"name\": \"宜宾纸业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600789\",\n    \"name\": \"鲁抗医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000692\",\n    \"name\": \"*ST惠天\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000700\",\n    \"name\": \"模塑科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600794\",\n    \"name\": \"保税科技\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000702\",\n    \"name\": \"正虹科技\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000697\",\n    \"name\": \"*ST炼石\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000703\",\n    \"name\": \"恒逸石化\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600796\",\n    \"name\": \"钱江生化\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600051\",\n    \"name\": \"宁波联合\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000711\",\n    \"name\": \"*ST京蓝\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000707\",\n    \"name\": \"双环科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000709\",\n    \"name\": \"河钢股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600797\",\n    \"name\": \"浙大网新\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600060\",\n    \"name\": \"海信视像\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600798\",\n    \"name\": \"宁波海运\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000721\",\n    \"name\": \"西安饮食\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600054\",\n    \"name\": \"黄山旅游\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600066\",\n    \"name\": \"宇通客车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600076\",\n    \"name\": \"康欣新材\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600063\",\n    \"name\": \"皖维高新\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000779\",\n    \"name\": \"甘咨询\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600057\",\n    \"name\": \"厦门象屿\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600070\",\n    \"name\": \"ST富润\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600079\",\n    \"name\": \"人福医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600083\",\n    \"name\": \"博信股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000498\",\n    \"name\": \"山东路桥\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000735\",\n    \"name\": \"罗 牛 山\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600075\",\n    \"name\": \"新疆天业\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000828\",\n    \"name\": \"东莞控股\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600089\",\n    \"name\": \"特变电工\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600097\",\n    \"name\": \"开创国际\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000782\",\n    \"name\": \"美达股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000752\",\n    \"name\": \"*ST西发\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000753\",\n    \"name\": \"漳州发展\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000755\",\n    \"name\": \"山西路桥\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000757\",\n    \"name\": \"浩物股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000796\",\n    \"name\": \"*ST凯撒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000729\",\n    \"name\": \"燕京啤酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000868\",\n    \"name\": \"安凯客车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000088\",\n    \"name\": \"盐 田 港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000756\",\n    \"name\": \"新华制药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600108\",\n    \"name\": \"亚盛集团\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000816\",\n    \"name\": \"智慧农业\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600128\",\n    \"name\": \"苏豪弘业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000789\",\n    \"name\": \"万年青\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600105\",\n    \"name\": \"永鼎股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000836\",\n    \"name\": \"富通信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000823\",\n    \"name\": \"超声电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600117\",\n    \"name\": \"*ST西钢\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600113\",\n    \"name\": \"浙江东日\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600132\",\n    \"name\": \"重庆啤酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600107\",\n    \"name\": \"美尔雅\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600104\",\n    \"name\": \"上汽集团\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600112\",\n    \"name\": \"ST天成\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600120\",\n    \"name\": \"浙江东方\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000889\",\n    \"name\": \"ST中嘉\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600133\",\n    \"name\": \"东湖高新\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600009\",\n    \"name\": \"上海机场\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000429\",\n    \"name\": \"粤高速Ａ\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000803\",\n    \"name\": \"山高环能\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600126\",\n    \"name\": \"杭钢股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000876\",\n    \"name\": \"新 希 望\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000880\",\n    \"name\": \"潍柴重机\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600137\",\n    \"name\": \"浪莎股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000089\",\n    \"name\": \"深圳机场\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600168\",\n    \"name\": \"武汉控股\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600127\",\n    \"name\": \"金健米业\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000801\",\n    \"name\": \"四川九洲\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000813\",\n    \"name\": \"德展健康\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000811\",\n    \"name\": \"冰轮环境\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600165\",\n    \"name\": \"宁科生物\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600166\",\n    \"name\": \"福田汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600170\",\n    \"name\": \"上海建工\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000812\",\n    \"name\": \"陕西金叶\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600180\",\n    \"name\": \"瑞茂通\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000822\",\n    \"name\": \"山东海化\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000856\",\n    \"name\": \"冀东装备\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600169\",\n    \"name\": \"太原重工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000581\",\n    \"name\": \"威孚高科\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600183\",\n    \"name\": \"生益科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600179\",\n    \"name\": \"安通控股\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600187\",\n    \"name\": \"国中水务\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000833\",\n    \"name\": \"粤桂股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600177\",\n    \"name\": \"雅戈尔\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000859\",\n    \"name\": \"国风新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600172\",\n    \"name\": \"黄河旋风\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600218\",\n    \"name\": \"全柴动力\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000887\",\n    \"name\": \"中鼎股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600192\",\n    \"name\": \"长城电工\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600201\",\n    \"name\": \"生物股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000890\",\n    \"name\": \"法尔胜\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000900\",\n    \"name\": \"现代投资\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000885\",\n    \"name\": \"城发环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600200\",\n    \"name\": \"江苏吴中\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000903\",\n    \"name\": \"云内动力\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600359\",\n    \"name\": \"新农开发\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000905\",\n    \"name\": \"厦门港务\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600182\",\n    \"name\": \"S佳通\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000909\",\n    \"name\": \"ST数源\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000913\",\n    \"name\": \"钱江摩托\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600193\",\n    \"name\": \"创兴资源\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600202\",\n    \"name\": \"哈空调\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600190\",\n    \"name\": \"锦州港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000915\",\n    \"name\": \"华特达因\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000929\",\n    \"name\": \"兰州黄河\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000910\",\n    \"name\": \"大亚圣象\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000921\",\n    \"name\": \"海信家电\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000923\",\n    \"name\": \"河钢资源\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000932\",\n    \"name\": \"华菱钢铁\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000935\",\n    \"name\": \"四川双马\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600213\",\n    \"name\": \"亚星客车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600220\",\n    \"name\": \"江苏阳光\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000948\",\n    \"name\": \"南天信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000949\",\n    \"name\": \"新乡化纤\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000952\",\n    \"name\": \"广济药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600221\",\n    \"name\": \"海航控股\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000959\",\n    \"name\": \"首钢股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000957\",\n    \"name\": \"中通客车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600225\",\n    \"name\": \"卓朗科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000960\",\n    \"name\": \"锡业股份\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000967\",\n    \"name\": \"盈峰环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600008\",\n    \"name\": \"首创环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000971\",\n    \"name\": \"ST高升\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600231\",\n    \"name\": \"凌钢股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600269\",\n    \"name\": \"赣粤高速\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600259\",\n    \"name\": \"广晟有色\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000301\",\n    \"name\": \"东方盛虹\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600258\",\n    \"name\": \"首旅酒店\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600233\",\n    \"name\": \"圆通速递\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000975\",\n    \"name\": \"银泰黄金\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600237\",\n    \"name\": \"铜峰电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600235\",\n    \"name\": \"民丰特纸\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000980\",\n    \"name\": \"众泰汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600248\",\n    \"name\": \"陕建股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000981\",\n    \"name\": \"山子股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000990\",\n    \"name\": \"诚志股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600301\",\n    \"name\": \"华锡有色\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600278\",\n    \"name\": \"东方创业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000987\",\n    \"name\": \"越秀资本\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000996\",\n    \"name\": \"*ST中期\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600289\",\n    \"name\": \"ST信通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600261\",\n    \"name\": \"阳光照明\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600277\",\n    \"name\": \"亿利洁能\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600279\",\n    \"name\": \"重庆港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600265\",\n    \"name\": \"ST景谷\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600293\",\n    \"name\": \"三峡新材\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600308\",\n    \"name\": \"华泰股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000157\",\n    \"name\": \"中联重科\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000869\",\n    \"name\": \"张  裕Ａ\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600366\",\n    \"name\": \"宁波韵升\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600290\",\n    \"name\": \"*ST华仪\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600281\",\n    \"name\": \"华阳新材\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600297\",\n    \"name\": \"广汇汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600243\",\n    \"name\": \"青海华鼎\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000488\",\n    \"name\": \"晨鸣纸业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600337\",\n    \"name\": \"美克家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600302\",\n    \"name\": \"标准股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600307\",\n    \"name\": \"酒钢宏兴\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600368\",\n    \"name\": \"五洲交通\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600323\",\n    \"name\": \"瀚蓝环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000726\",\n    \"name\": \"鲁  泰Ａ\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600303\",\n    \"name\": \"ST曙光\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600398\",\n    \"name\": \"海澜之家\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600399\",\n    \"name\": \"抚顺特钢\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600388\",\n    \"name\": \"龙净环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600336\",\n    \"name\": \"澳柯玛\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000725\",\n    \"name\": \"京东方Ａ\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600326\",\n    \"name\": \"西藏天路\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600377\",\n    \"name\": \"宁沪高速\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600033\",\n    \"name\": \"福建高速\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600386\",\n    \"name\": \"北巴传媒\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600558\",\n    \"name\": \"大西洋\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600010\",\n    \"name\": \"包钢股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600315\",\n    \"name\": \"上海家化\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600363\",\n    \"name\": \"联创光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600356\",\n    \"name\": \"恒丰纸业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600295\",\n    \"name\": \"鄂尔多斯\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600588\",\n    \"name\": \"用友网络\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600568\",\n    \"name\": \"ST中珠\",\n    \"tag\": null,\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600321\",\n    \"name\": \"正源股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600468\",\n    \"name\": \"百利电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600488\",\n    \"name\": \"津药药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600569\",\n    \"name\": \"安阳钢铁\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600539\",\n    \"name\": \"狮头股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600418\",\n    \"name\": \"江淮汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600599\",\n    \"name\": \"ST熊猫\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600567\",\n    \"name\": \"山鹰国际\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600548\",\n    \"name\": \"深高速\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600585\",\n    \"name\": \"海螺水泥\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600350\",\n    \"name\": \"山东高速\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600456\",\n    \"name\": \"宝钛股份\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600496\",\n    \"name\": \"精工钢构\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600580\",\n    \"name\": \"卧龙电驱\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600355\",\n    \"name\": \"精伦电子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600327\",\n    \"name\": \"大东方\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600590\",\n    \"name\": \"泰豪科技\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600561\",\n    \"name\": \"江西长运\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600416\",\n    \"name\": \"湘电股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600526\",\n    \"name\": \"菲达环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600592\",\n    \"name\": \"龙溪股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600515\",\n    \"name\": \"海南机场\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600577\",\n    \"name\": \"精达股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600371\",\n    \"name\": \"万向德农\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600571\",\n    \"name\": \"信雅达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600353\",\n    \"name\": \"旭光电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600563\",\n    \"name\": \"法拉电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600512\",\n    \"name\": \"腾达建设\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600012\",\n    \"name\": \"皖通高速\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600537\",\n    \"name\": \"亿晶光电\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600573\",\n    \"name\": \"惠泉啤酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600521\",\n    \"name\": \"华海药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600370\",\n    \"name\": \"三房巷\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600513\",\n    \"name\": \"联环药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600575\",\n    \"name\": \"淮河能源\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600375\",\n    \"name\": \"汉马科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600502\",\n    \"name\": \"安徽建工\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600481\",\n    \"name\": \"双良节能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600004\",\n    \"name\": \"白云机场\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600459\",\n    \"name\": \"贵研铂业\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600251\",\n    \"name\": \"冠农股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600273\",\n    \"name\": \"嘉化能源\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600031\",\n    \"name\": \"三一重工\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600475\",\n    \"name\": \"华光环能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600020\",\n    \"name\": \"中原高速\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600487\",\n    \"name\": \"亨通光电\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600547\",\n    \"name\": \"山东黄金\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600462\",\n    \"name\": \"ST九有\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600507\",\n    \"name\": \"方大特钢\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600477\",\n    \"name\": \"杭萧钢构\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600527\",\n    \"name\": \"江南高纤\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600545\",\n    \"name\": \"卓郎智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600570\",\n    \"name\": \"恒生电子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600540\",\n    \"name\": \"新赛股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600249\",\n    \"name\": \"两面针\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000100\",\n    \"name\": \"TCL科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600438\",\n    \"name\": \"通威股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600035\",\n    \"name\": \"楚天高速\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600543\",\n    \"name\": \"*ST莫高\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600455\",\n    \"name\": \"博通股份\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600405\",\n    \"name\": \"动力源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600960\",\n    \"name\": \"渤海汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600988\",\n    \"name\": \"赤峰黄金\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600410\",\n    \"name\": \"华胜天成\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600114\",\n    \"name\": \"东睦股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600992\",\n    \"name\": \"贵绳股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600491\",\n    \"name\": \"龙元建设\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600461\",\n    \"name\": \"洪城环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600421\",\n    \"name\": \"华嵘控股\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600966\",\n    \"name\": \"博汇纸业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600975\",\n    \"name\": \"新五丰\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002003\",\n    \"name\": \"伟星股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002004\",\n    \"name\": \"华邦健康\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002005\",\n    \"name\": \"ST德豪\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002002\",\n    \"name\": \"ST鸿达\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600022\",\n    \"name\": \"山东钢铁\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002010\",\n    \"name\": \"传化智联\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600981\",\n    \"name\": \"汇鸿集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600984\",\n    \"name\": \"建设机械\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002014\",\n    \"name\": \"永新股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600965\",\n    \"name\": \"福成股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002021\",\n    \"name\": \"*ST中捷\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600983\",\n    \"name\": \"惠而浦\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002028\",\n    \"name\": \"思源电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002029\",\n    \"name\": \"七 匹 狼\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600987\",\n    \"name\": \"航民股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002032\",\n    \"name\": \"苏 泊 尔\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002034\",\n    \"name\": \"旺能环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002035\",\n    \"name\": \"华帝股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002036\",\n    \"name\": \"联创电子\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002040\",\n    \"name\": \"南 京 港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002041\",\n    \"name\": \"登海种业\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002043\",\n    \"name\": \"兔 宝 宝\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002044\",\n    \"name\": \"美年健康\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002047\",\n    \"name\": \"宝鹰股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002048\",\n    \"name\": \"宁波华翔\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002050\",\n    \"name\": \"三花智控\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002053\",\n    \"name\": \"云南能投\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002054\",\n    \"name\": \"德美化工\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002058\",\n    \"name\": \"威尔泰\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002060\",\n    \"name\": \"粤 水 电\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002062\",\n    \"name\": \"宏润建设\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002061\",\n    \"name\": \"浙江交科\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002065\",\n    \"name\": \"东华软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002069\",\n    \"name\": \"獐子岛\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601588\",\n    \"name\": \"北辰实业\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600017\",\n    \"name\": \"日照港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002072\",\n    \"name\": \"凯瑞德\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002075\",\n    \"name\": \"沙钢股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600018\",\n    \"name\": \"上港集团\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002078\",\n    \"name\": \"太阳纸业\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002081\",\n    \"name\": \"金 螳 螂\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002084\",\n    \"name\": \"海鸥住工\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002083\",\n    \"name\": \"孚日股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002086\",\n    \"name\": \"*ST东洋\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002085\",\n    \"name\": \"万丰奥威\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002087\",\n    \"name\": \"*ST新纺\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002088\",\n    \"name\": \"鲁阳节能\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002092\",\n    \"name\": \"中泰化学\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002094\",\n    \"name\": \"青岛金王\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002095\",\n    \"name\": \"生 意 宝\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002098\",\n    \"name\": \"浔兴股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002096\",\n    \"name\": \"易普力\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002099\",\n    \"name\": \"海翔药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002100\",\n    \"name\": \"天康生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002101\",\n    \"name\": \"广东鸿图\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002102\",\n    \"name\": \"冠福股份\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002104\",\n    \"name\": \"恒宝股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002105\",\n    \"name\": \"信隆健康\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601002\",\n    \"name\": \"晋亿实业\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002111\",\n    \"name\": \"威海广泰\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002110\",\n    \"name\": \"三钢闽光\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002109\",\n    \"name\": \"兴化股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002112\",\n    \"name\": \"三变科技\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601003\",\n    \"name\": \"柳钢股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002117\",\n    \"name\": \"东港股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002120\",\n    \"name\": \"韵达股份\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002122\",\n    \"name\": \"汇洲智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002124\",\n    \"name\": \"天邦食品\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601007\",\n    \"name\": \"金陵饭店\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002127\",\n    \"name\": \"南极电商\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002126\",\n    \"name\": \"银轮股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002130\",\n    \"name\": \"沃尔核材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601008\",\n    \"name\": \"连云港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"000338\",\n    \"name\": \"潍柴动力\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002134\",\n    \"name\": \"天津普林\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002135\",\n    \"name\": \"东南网架\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002137\",\n    \"name\": \"实益达\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002138\",\n    \"name\": \"顺络电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002144\",\n    \"name\": \"宏达高科\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002146\",\n    \"name\": \"荣盛发展\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002148\",\n    \"name\": \"北纬科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002150\",\n    \"name\": \"通润装备\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002149\",\n    \"name\": \"西部材料\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002153\",\n    \"name\": \"石基信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002151\",\n    \"name\": \"北斗星通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002152\",\n    \"name\": \"广电运通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002154\",\n    \"name\": \"报 喜 鸟\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002155\",\n    \"name\": \"湖南黄金\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002159\",\n    \"name\": \"三特索道\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002157\",\n    \"name\": \"*ST 正邦\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002158\",\n    \"name\": \"汉钟精机\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002161\",\n    \"name\": \"远 望 谷\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002163\",\n    \"name\": \"海南发展\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002164\",\n    \"name\": \"宁波东力\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002166\",\n    \"name\": \"莱茵生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002167\",\n    \"name\": \"东方锆业\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002165\",\n    \"name\": \"红 宝 丽\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002169\",\n    \"name\": \"智光电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002175\",\n    \"name\": \"东方智造\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002180\",\n    \"name\": \"纳思达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002188\",\n    \"name\": \"中天服务\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002191\",\n    \"name\": \"劲嘉股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002194\",\n    \"name\": \"武汉凡谷\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002193\",\n    \"name\": \"如意集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002196\",\n    \"name\": \"方正电机\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002199\",\n    \"name\": \"东晶电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002200\",\n    \"name\": \"ST交投\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002202\",\n    \"name\": \"金风科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002201\",\n    \"name\": \"正威新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002204\",\n    \"name\": \"大连重工\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002206\",\n    \"name\": \"海 利 得\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002207\",\n    \"name\": \"准油股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002210\",\n    \"name\": \"飞马国际\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002212\",\n    \"name\": \"天融信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002215\",\n    \"name\": \"诺 普 信\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002217\",\n    \"name\": \"合力泰\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002218\",\n    \"name\": \"拓日新能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601958\",\n    \"name\": \"金钼股份\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601899\",\n    \"name\": \"紫金矿业\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002224\",\n    \"name\": \"三 力 士\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002225\",\n    \"name\": \"濮耐股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002227\",\n    \"name\": \"奥 特 迅\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002231\",\n    \"name\": \"奥维通信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002234\",\n    \"name\": \"民和股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002233\",\n    \"name\": \"塔牌集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002236\",\n    \"name\": \"大华股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002239\",\n    \"name\": \"奥特佳\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002242\",\n    \"name\": \"九阳股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002243\",\n    \"name\": \"力合科创\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002248\",\n    \"name\": \"华东数控\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002247\",\n    \"name\": \"聚力文化\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002250\",\n    \"name\": \"联化科技\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002249\",\n    \"name\": \"大洋电机\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002253\",\n    \"name\": \"川大智胜\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002254\",\n    \"name\": \"泰和新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002262\",\n    \"name\": \"恩华药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002263\",\n    \"name\": \"大东南\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002264\",\n    \"name\": \"新 华 都\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002266\",\n    \"name\": \"浙富控股\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002270\",\n    \"name\": \"华明装备\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002271\",\n    \"name\": \"东方雨虹\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002273\",\n    \"name\": \"水晶光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002272\",\n    \"name\": \"川润股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601727\",\n    \"name\": \"上海电气\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002276\",\n    \"name\": \"万马股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601107\",\n    \"name\": \"四川成渝\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002279\",\n    \"name\": \"久其软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002278\",\n    \"name\": \"神开股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002282\",\n    \"name\": \"博深股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002280\",\n    \"name\": \"联络互动\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002285\",\n    \"name\": \"世联行\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002284\",\n    \"name\": \"亚太股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002289\",\n    \"name\": \"ST宇顺\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002290\",\n    \"name\": \"禾盛新材\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002291\",\n    \"name\": \"遥望科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002293\",\n    \"name\": \"罗莱生活\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002296\",\n    \"name\": \"辉煌科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002297\",\n    \"name\": \"博云新材\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002299\",\n    \"name\": \"圣农发展\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002301\",\n    \"name\": \"齐心集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002300\",\n    \"name\": \"太阳电缆\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300012\",\n    \"name\": \"华测检测\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300010\",\n    \"name\": \"*ST豆神\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300015\",\n    \"name\": \"爱尔眼科\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300013\",\n    \"name\": \"新宁物流\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300011\",\n    \"name\": \"鼎汉技术\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300025\",\n    \"name\": \"华星创业\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300021\",\n    \"name\": \"大禹节水\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300008\",\n    \"name\": \"天海防务\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300001\",\n    \"name\": \"特锐德\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300005\",\n    \"name\": \"探路者\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300022\",\n    \"name\": \"吉峰科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300004\",\n    \"name\": \"南风股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002303\",\n    \"name\": \"美盈森\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002307\",\n    \"name\": \"北新路桥\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002308\",\n    \"name\": \"威创股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002311\",\n    \"name\": \"海大集团\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002310\",\n    \"name\": \"东方园林\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002313\",\n    \"name\": \"日海智能\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002315\",\n    \"name\": \"焦点科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002316\",\n    \"name\": \"亚联发展\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002319\",\n    \"name\": \"乐通股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002318\",\n    \"name\": \"久立特材\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002321\",\n    \"name\": \"华英农业\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002322\",\n    \"name\": \"理工能科\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002325\",\n    \"name\": \"洪涛股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300031\",\n    \"name\": \"宝通科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300029\",\n    \"name\": \"ST天龙\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002327\",\n    \"name\": \"富安娜\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002328\",\n    \"name\": \"新朋股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002331\",\n    \"name\": \"皖通科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300040\",\n    \"name\": \"九洲集团\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002332\",\n    \"name\": \"仙琚制药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002335\",\n    \"name\": \"科华数据\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300050\",\n    \"name\": \"世纪鼎利\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300043\",\n    \"name\": \"星辉娱乐\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300047\",\n    \"name\": \"天源迪科\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300048\",\n    \"name\": \"合康新能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601877\",\n    \"name\": \"正泰电器\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002339\",\n    \"name\": \"积成电子\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002345\",\n    \"name\": \"潮宏基\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002350\",\n    \"name\": \"北京科锐\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002352\",\n    \"name\": \"顺丰控股\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002353\",\n    \"name\": \"杰瑞股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002354\",\n    \"name\": \"天娱数科\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002355\",\n    \"name\": \"兴民智通\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300051\",\n    \"name\": \"三五互联\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300054\",\n    \"name\": \"鼎龙股份\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300056\",\n    \"name\": \"中创环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300055\",\n    \"name\": \"万邦达\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002360\",\n    \"name\": \"同德化工\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002361\",\n    \"name\": \"神剑股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002365\",\n    \"name\": \"永安药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002364\",\n    \"name\": \"中恒电气\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002366\",\n    \"name\": \"融发核电\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002370\",\n    \"name\": \"亚太药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002372\",\n    \"name\": \"伟星新材\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002373\",\n    \"name\": \"千方科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601188\",\n    \"name\": \"龙江交通\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601518\",\n    \"name\": \"吉林高速\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002375\",\n    \"name\": \"亚厦股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300066\",\n    \"name\": \"三川智慧\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300065\",\n    \"name\": \"海兰信\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601158\",\n    \"name\": \"重庆水务\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002378\",\n    \"name\": \"章源钨业\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002383\",\n    \"name\": \"合众思壮\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002384\",\n    \"name\": \"东山精密\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002385\",\n    \"name\": \"大北农\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002387\",\n    \"name\": \"维信诺\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002388\",\n    \"name\": \"新亚制程\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002391\",\n    \"name\": \"长青股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300067\",\n    \"name\": \"安诺其\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300069\",\n    \"name\": \"金利华电\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002393\",\n    \"name\": \"力生制药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002394\",\n    \"name\": \"联发股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002392\",\n    \"name\": \"北京利尔\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300075\",\n    \"name\": \"数字政通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300074\",\n    \"name\": \"华平股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300072\",\n    \"name\": \"海新能科\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601369\",\n    \"name\": \"陕鼓动力\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002397\",\n    \"name\": \"梦洁股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300079\",\n    \"name\": \"数码视讯\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300078\",\n    \"name\": \"思创医惠\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002398\",\n    \"name\": \"垒知集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002404\",\n    \"name\": \"嘉欣丝绸\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002408\",\n    \"name\": \"齐翔腾达\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002405\",\n    \"name\": \"四维图新\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002406\",\n    \"name\": \"远东传动\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300083\",\n    \"name\": \"创世纪\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300084\",\n    \"name\": \"海默科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002410\",\n    \"name\": \"广联达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300085\",\n    \"name\": \"银之杰\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300088\",\n    \"name\": \"长信科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002420\",\n    \"name\": \"毅昌科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002428\",\n    \"name\": \"云南锗业\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002425\",\n    \"name\": \"凯撒文化\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002429\",\n    \"name\": \"兆驰股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002431\",\n    \"name\": \"棕榈股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002430\",\n    \"name\": \"杭氧股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002434\",\n    \"name\": \"万里扬\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002436\",\n    \"name\": \"兴森科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002396\",\n    \"name\": \"星网锐捷\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002438\",\n    \"name\": \"江苏神通\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002439\",\n    \"name\": \"启明星辰\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300091\",\n    \"name\": \"金通灵\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601000\",\n    \"name\": \"唐山港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002440\",\n    \"name\": \"闰土股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002443\",\n    \"name\": \"金洲管道\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300092\",\n    \"name\": \"科新机电\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002445\",\n    \"name\": \"中南文化\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002414\",\n    \"name\": \"高德红外\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002448\",\n    \"name\": \"中原内配\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002451\",\n    \"name\": \"摩恩电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002454\",\n    \"name\": \"松芝股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002453\",\n    \"name\": \"华软科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002452\",\n    \"name\": \"长高电新\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300095\",\n    \"name\": \"华伍股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300096\",\n    \"name\": \"ST易联众\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601717\",\n    \"name\": \"郑煤机\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002456\",\n    \"name\": \"欧菲光\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002457\",\n    \"name\": \"青龙管业\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002458\",\n    \"name\": \"益生股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300102\",\n    \"name\": \"乾照光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300103\",\n    \"name\": \"达刚控股\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002461\",\n    \"name\": \"珠江啤酒\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002463\",\n    \"name\": \"沪电股份\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300107\",\n    \"name\": \"建新股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300110\",\n    \"name\": \"华仁药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300109\",\n    \"name\": \"新开源\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300111\",\n    \"name\": \"向日葵\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300118\",\n    \"name\": \"东方日升\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002468\",\n    \"name\": \"申通快递\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002469\",\n    \"name\": \"三维化学\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002467\",\n    \"name\": \"二六三\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002474\",\n    \"name\": \"榕基软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002476\",\n    \"name\": \"宝莫股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300119\",\n    \"name\": \"瑞普生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300120\",\n    \"name\": \"经纬辉开\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300121\",\n    \"name\": \"阳谷华泰\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002479\",\n    \"name\": \"富春环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002480\",\n    \"name\": \"新筑股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002478\",\n    \"name\": \"常宝股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601018\",\n    \"name\": \"宁波港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002482\",\n    \"name\": \"*ST广田\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002483\",\n    \"name\": \"润邦股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002484\",\n    \"name\": \"江海股份\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601177\",\n    \"name\": \"杭齿前进\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300127\",\n    \"name\": \"银河磁体\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002486\",\n    \"name\": \"嘉麟杰\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002485\",\n    \"name\": \"ST雪发\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300130\",\n    \"name\": \"新国都\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300129\",\n    \"name\": \"泰胜风能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002489\",\n    \"name\": \"浙江永强\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002488\",\n    \"name\": \"金固股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002490\",\n    \"name\": \"山东墨龙\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002491\",\n    \"name\": \"通鼎互联\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300132\",\n    \"name\": \"青松股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300135\",\n    \"name\": \"宝利国际\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002492\",\n    \"name\": \"恒基达鑫\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002493\",\n    \"name\": \"荣盛石化\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002494\",\n    \"name\": \"华斯股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300137\",\n    \"name\": \"先河环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300138\",\n    \"name\": \"晨光生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002496\",\n    \"name\": \"辉丰股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002498\",\n    \"name\": \"汉缆股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300141\",\n    \"name\": \"和顺电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002505\",\n    \"name\": \"鹏都农牧\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002506\",\n    \"name\": \"协鑫集成\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002504\",\n    \"name\": \"*ST弘高\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002508\",\n    \"name\": \"老板电器\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002511\",\n    \"name\": \"中顺洁柔\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002510\",\n    \"name\": \"天汽模\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002513\",\n    \"name\": \"蓝丰生化\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002514\",\n    \"name\": \"宝馨科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002519\",\n    \"name\": \"银河电子\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002516\",\n    \"name\": \"旷达科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300143\",\n    \"name\": \"盈康生命\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300144\",\n    \"name\": \"宋城演艺\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300145\",\n    \"name\": \"中金环境\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002522\",\n    \"name\": \"浙江众成\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002521\",\n    \"name\": \"齐峰新材\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300148\",\n    \"name\": \"天舟文化\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002524\",\n    \"name\": \"光正眼科\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300151\",\n    \"name\": \"昌红科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300150\",\n    \"name\": \"世纪瑞尔\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002528\",\n    \"name\": \"英飞拓\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601890\",\n    \"name\": \"亚星锚链\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300152\",\n    \"name\": \"新动力\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300153\",\n    \"name\": \"科泰电源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601126\",\n    \"name\": \"四方股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002530\",\n    \"name\": \"金财互联\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002531\",\n    \"name\": \"天顺风能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002533\",\n    \"name\": \"金杯电工\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601118\",\n    \"name\": \"海南橡胶\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300159\",\n    \"name\": \"新研股份\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300157\",\n    \"name\": \"新锦动力\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300155\",\n    \"name\": \"安居宝\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002534\",\n    \"name\": \"西子洁能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002537\",\n    \"name\": \"海联金汇\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002536\",\n    \"name\": \"飞龙股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300162\",\n    \"name\": \"雷曼光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300160\",\n    \"name\": \"秀强股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300163\",\n    \"name\": \"先锋新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300164\",\n    \"name\": \"通源石油\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601700\",\n    \"name\": \"风范股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002541\",\n    \"name\": \"鸿路钢构\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300167\",\n    \"name\": \"ST迪威迅\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300168\",\n    \"name\": \"万达信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300169\",\n    \"name\": \"天晟新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300165\",\n    \"name\": \"天瑞仪器\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002543\",\n    \"name\": \"万和电气\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002542\",\n    \"name\": \"中化岩土\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601799\",\n    \"name\": \"星宇股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601616\",\n    \"name\": \"广电电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300174\",\n    \"name\": \"元力股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300170\",\n    \"name\": \"汉得信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002546\",\n    \"name\": \"新联电子\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002545\",\n    \"name\": \"东方铁塔\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300177\",\n    \"name\": \"中海达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300179\",\n    \"name\": \"四方达\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300176\",\n    \"name\": \"派生科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002548\",\n    \"name\": \"金新农\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002550\",\n    \"name\": \"千红制药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002549\",\n    \"name\": \"凯美特气\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601216\",\n    \"name\": \"君正集团\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300184\",\n    \"name\": \"力源信息\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300183\",\n    \"name\": \"东软载波\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300180\",\n    \"name\": \"华峰超纤\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002553\",\n    \"name\": \"南方精工\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002552\",\n    \"name\": \"宝鼎科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002554\",\n    \"name\": \"惠博普\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601992\",\n    \"name\": \"金隅集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002555\",\n    \"name\": \"三七互娱\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002560\",\n    \"name\": \"通达股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300185\",\n    \"name\": \"通裕重工\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300187\",\n    \"name\": \"永清环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002564\",\n    \"name\": \"*ST天沃\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002562\",\n    \"name\": \"兄弟科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002563\",\n    \"name\": \"森马服饰\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300189\",\n    \"name\": \"神农科技\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300190\",\n    \"name\": \"维尔利\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300191\",\n    \"name\": \"潜能恒信\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601199\",\n    \"name\": \"江南水务\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002565\",\n    \"name\": \"顺灏股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300192\",\n    \"name\": \"科德教育\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300194\",\n    \"name\": \"福安药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002568\",\n    \"name\": \"百润股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002567\",\n    \"name\": \"唐人神\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300196\",\n    \"name\": \"长海股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300200\",\n    \"name\": \"高盟新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002572\",\n    \"name\": \"索菲亚\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002569\",\n    \"name\": \"ST步森\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002571\",\n    \"name\": \"德力股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300203\",\n    \"name\": \"聚光科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300205\",\n    \"name\": \"ST天喻\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002574\",\n    \"name\": \"明牌珠宝\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002573\",\n    \"name\": \"清新环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300210\",\n    \"name\": \"森远股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300208\",\n    \"name\": \"青岛中程\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300209\",\n    \"name\": \"ST有棵树\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002576\",\n    \"name\": \"通达动力\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300213\",\n    \"name\": \"佳讯飞鸿\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300211\",\n    \"name\": \"亿通科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601218\",\n    \"name\": \"吉鑫科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002579\",\n    \"name\": \"中京电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300215\",\n    \"name\": \"电科院\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300214\",\n    \"name\": \"日科化学\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601233\",\n    \"name\": \"桐昆股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300219\",\n    \"name\": \"鸿利智汇\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300218\",\n    \"name\": \"安利股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601208\",\n    \"name\": \"东材科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300220\",\n    \"name\": \"ST金运\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300221\",\n    \"name\": \"银禧科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601599\",\n    \"name\": \"浙文影业\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300224\",\n    \"name\": \"正海磁材\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300225\",\n    \"name\": \"金力泰\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002586\",\n    \"name\": \"*ST围海\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002585\",\n    \"name\": \"双星新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300228\",\n    \"name\": \"富瑞特装\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300227\",\n    \"name\": \"光韵达\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300226\",\n    \"name\": \"上海钢联\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601567\",\n    \"name\": \"三星医疗\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300231\",\n    \"name\": \"银信科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300229\",\n    \"name\": \"拓尔思\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300230\",\n    \"name\": \"永利股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300233\",\n    \"name\": \"金城医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300234\",\n    \"name\": \"开尔新材\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002595\",\n    \"name\": \"豪迈科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002593\",\n    \"name\": \"日上集团\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300237\",\n    \"name\": \"美晨生态\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300236\",\n    \"name\": \"上海新阳\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300235\",\n    \"name\": \"方直科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601058\",\n    \"name\": \"赛轮轮胎\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300240\",\n    \"name\": \"飞力达\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002596\",\n    \"name\": \"海南瑞泽\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300241\",\n    \"name\": \"瑞丰光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300243\",\n    \"name\": \"瑞丰高材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300245\",\n    \"name\": \"天玑科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300244\",\n    \"name\": \"迪安诊断\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300247\",\n    \"name\": \"融捷健康\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300248\",\n    \"name\": \"新开普\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002606\",\n    \"name\": \"大连电瓷\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002607\",\n    \"name\": \"中公教育\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601636\",\n    \"name\": \"旗滨集团\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002609\",\n    \"name\": \"捷顺科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601789\",\n    \"name\": \"宁波建工\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300253\",\n    \"name\": \"卫宁健康\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300257\",\n    \"name\": \"开山股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300258\",\n    \"name\": \"精锻科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002613\",\n    \"name\": \"北玻股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300259\",\n    \"name\": \"新天科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300261\",\n    \"name\": \"雅本化学\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300260\",\n    \"name\": \"新莱应材\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300265\",\n    \"name\": \"通光线缆\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300263\",\n    \"name\": \"隆华科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300268\",\n    \"name\": \"*ST佳沃\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300266\",\n    \"name\": \"兴源环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601633\",\n    \"name\": \"长城汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601996\",\n    \"name\": \"丰林集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002621\",\n    \"name\": \"美吉姆\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002620\",\n    \"name\": \"瑞和股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300270\",\n    \"name\": \"中威电子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002623\",\n    \"name\": \"亚玛顿\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002622\",\n    \"name\": \"皓宸医疗\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300271\",\n    \"name\": \"华宇软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601100\",\n    \"name\": \"恒立液压\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300272\",\n    \"name\": \"开能健康\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300274\",\n    \"name\": \"阳光电源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300275\",\n    \"name\": \"梅安森\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002628\",\n    \"name\": \"成都路桥\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002627\",\n    \"name\": \"三峡旅游\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002629\",\n    \"name\": \"仁智股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601028\",\n    \"name\": \"玉龙股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002631\",\n    \"name\": \"德尔未来\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002630\",\n    \"name\": \"华西能源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002592\",\n    \"name\": \"ST八菱\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002633\",\n    \"name\": \"申科股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300277\",\n    \"name\": \"海联讯\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002638\",\n    \"name\": \"勤上股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002636\",\n    \"name\": \"金安国纪\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002637\",\n    \"name\": \"赞宇科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002639\",\n    \"name\": \"雪人股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002640\",\n    \"name\": \"跨境通\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430090\",\n    \"name\": \"同辉信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002642\",\n    \"name\": \"荣联科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002645\",\n    \"name\": \"华宏科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002647\",\n    \"name\": \"仁东控股\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300281\",\n    \"name\": \"金明精机\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002651\",\n    \"name\": \"利君股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002649\",\n    \"name\": \"博彦科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300284\",\n    \"name\": \"苏交科\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300286\",\n    \"name\": \"安科瑞\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300285\",\n    \"name\": \"国瓷材料\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601360\",\n    \"name\": \"三六零\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002652\",\n    \"name\": \"扬子新材\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300287\",\n    \"name\": \"飞利信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300288\",\n    \"name\": \"朗玛信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002656\",\n    \"name\": \"ST摩登\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300292\",\n    \"name\": \"吴通控股\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002659\",\n    \"name\": \"凯文教育\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002658\",\n    \"name\": \"雪迪龙\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300296\",\n    \"name\": \"利亚德\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002663\",\n    \"name\": \"普邦股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002664\",\n    \"name\": \"信质集团\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300303\",\n    \"name\": \"聚飞光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300299\",\n    \"name\": \"富春股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300300\",\n    \"name\": \"海峡创新\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300301\",\n    \"name\": \"*ST长方\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300302\",\n    \"name\": \"同有科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300304\",\n    \"name\": \"云意电气\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002666\",\n    \"name\": \"德联集团\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300306\",\n    \"name\": \"远方信息\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300305\",\n    \"name\": \"裕兴股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601012\",\n    \"name\": \"隆基绿能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002668\",\n    \"name\": \"奥马电器\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300311\",\n    \"name\": \"任子行\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300310\",\n    \"name\": \"宜通世纪\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300313\",\n    \"name\": \"*ST天山\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603001\",\n    \"name\": \"ST奥康\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002672\",\n    \"name\": \"东江环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002671\",\n    \"name\": \"龙泉股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002674\",\n    \"name\": \"兴业科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300315\",\n    \"name\": \"掌趣科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300316\",\n    \"name\": \"晶盛机电\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603002\",\n    \"name\": \"宏昌电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603366\",\n    \"name\": \"日出东方\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300319\",\n    \"name\": \"麦捷科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300321\",\n    \"name\": \"同大股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002677\",\n    \"name\": \"浙江美大\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002676\",\n    \"name\": \"顺威股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002678\",\n    \"name\": \"珠江钢琴\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300320\",\n    \"name\": \"海达股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002679\",\n    \"name\": \"福建金森\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300324\",\n    \"name\": \"旋极信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601339\",\n    \"name\": \"百隆东方\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002685\",\n    \"name\": \"华东重机\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002682\",\n    \"name\": \"龙洲股份\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002683\",\n    \"name\": \"广东宏大\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300329\",\n    \"name\": \"海伦钢琴\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300328\",\n    \"name\": \"宜安科技\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300331\",\n    \"name\": \"苏大维格\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002686\",\n    \"name\": \"亿利达\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300335\",\n    \"name\": \"迪森股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002687\",\n    \"name\": \"乔治白\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002688\",\n    \"name\": \"金河生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603008\",\n    \"name\": \"喜临门\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300338\",\n    \"name\": \"开元教育\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300341\",\n    \"name\": \"麦克奥迪\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002691\",\n    \"name\": \"冀凯股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300345\",\n    \"name\": \"华民股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300343\",\n    \"name\": \"联创股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300346\",\n    \"name\": \"南大光电\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002692\",\n    \"name\": \"远程股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603766\",\n    \"name\": \"隆鑫通用\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002694\",\n    \"name\": \"顾地科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603003\",\n    \"name\": \"龙宇股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300347\",\n    \"name\": \"泰格医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300349\",\n    \"name\": \"金卡智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300348\",\n    \"name\": \"长亮科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300350\",\n    \"name\": \"华鹏飞\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002696\",\n    \"name\": \"百洋股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603167\",\n    \"name\": \"渤海轮渡\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300351\",\n    \"name\": \"永贵电器\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300354\",\n    \"name\": \"东华测试\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300353\",\n    \"name\": \"东土科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300355\",\n    \"name\": \"蒙草生态\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002701\",\n    \"name\": \"奥瑞金\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430017\",\n    \"name\": \"星昊医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"430198\",\n    \"name\": \"微创光电\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603699\",\n    \"name\": \"纽威股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300359\",\n    \"name\": \"全通教育\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002705\",\n    \"name\": \"新宝股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002706\",\n    \"name\": \"良信股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300360\",\n    \"name\": \"炬华科技\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603308\",\n    \"name\": \"应流股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002707\",\n    \"name\": \"众信旅游\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300368\",\n    \"name\": \"汇金股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300370\",\n    \"name\": \"安控科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300371\",\n    \"name\": \"汇中股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300365\",\n    \"name\": \"恒华科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603555\",\n    \"name\": \"ST贵人\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430476\",\n    \"name\": \"海能技术\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"430425\",\n    \"name\": \"乐创技术\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"430564\",\n    \"name\": \"天润科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"430556\",\n    \"name\": \"雅达股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002721\",\n    \"name\": \"*ST金一\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300378\",\n    \"name\": \"鼎捷软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300377\",\n    \"name\": \"赢时胜\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300375\",\n    \"name\": \"鹏翎股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300381\",\n    \"name\": \"溢多利\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002718\",\n    \"name\": \"友邦吊顶\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300379\",\n    \"name\": \"东方通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002714\",\n    \"name\": \"牧原股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002716\",\n    \"name\": \"金贵银业\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300380\",\n    \"name\": \"安硕信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002725\",\n    \"name\": \"跃岭股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300363\",\n    \"name\": \"博腾股份\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002723\",\n    \"name\": \"小崧股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300369\",\n    \"name\": \"绿盟科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002713\",\n    \"name\": \"东易日盛\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002715\",\n    \"name\": \"登云股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002717\",\n    \"name\": \"岭南股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300385\",\n    \"name\": \"雪浪环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300386\",\n    \"name\": \"飞天诚信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603006\",\n    \"name\": \"联明股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603328\",\n    \"name\": \"依顿电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430489\",\n    \"name\": \"佳先股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603009\",\n    \"name\": \"北特科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603111\",\n    \"name\": \"康尼机电\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300389\",\n    \"name\": \"艾比森\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300384\",\n    \"name\": \"三联虹普\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603100\",\n    \"name\": \"川仪股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603609\",\n    \"name\": \"禾丰股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"830879\",\n    \"name\": \"基康仪器\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603188\",\n    \"name\": \"亚邦股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300397\",\n    \"name\": \"天和防务\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300395\",\n    \"name\": \"菲利华\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002729\",\n    \"name\": \"好利科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300393\",\n    \"name\": \"中来股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603306\",\n    \"name\": \"华懋科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603169\",\n    \"name\": \"兰石重装\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002730\",\n    \"name\": \"电光科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300401\",\n    \"name\": \"花园生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300398\",\n    \"name\": \"飞凯材料\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300399\",\n    \"name\": \"天利科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603456\",\n    \"name\": \"九洲药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300400\",\n    \"name\": \"劲拓股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300402\",\n    \"name\": \"宝色股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603018\",\n    \"name\": \"华设集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603606\",\n    \"name\": \"东方电缆\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831152\",\n    \"name\": \"昆工科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300403\",\n    \"name\": \"汉宇集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603688\",\n    \"name\": \"石英股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430478\",\n    \"name\": \"峆一药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603988\",\n    \"name\": \"中电电机\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002724\",\n    \"name\": \"海洋王\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430718\",\n    \"name\": \"合肥高科\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603166\",\n    \"name\": \"福达股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831087\",\n    \"name\": \"秋乐种业\",\n    \"tag\": \"农业\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300408\",\n    \"name\": \"三环集团\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002735\",\n    \"name\": \"王子新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300407\",\n    \"name\": \"凯发电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603588\",\n    \"name\": \"高能环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430418\",\n    \"name\": \"苏轴股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603017\",\n    \"name\": \"中衡设计\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603889\",\n    \"name\": \"新澳股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300411\",\n    \"name\": \"金盾股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"830964\",\n    \"name\": \"润农节水\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603788\",\n    \"name\": \"宁波高发\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601021\",\n    \"name\": \"春秋航空\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603601\",\n    \"name\": \"再升科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601069\",\n    \"name\": \"西部黄金\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002740\",\n    \"name\": \"*ST爱迪\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300416\",\n    \"name\": \"苏试试验\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300419\",\n    \"name\": \"浩丰科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300417\",\n    \"name\": \"南华仪器\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603678\",\n    \"name\": \"火炬电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603686\",\n    \"name\": \"福龙马\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603899\",\n    \"name\": \"晨光股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603558\",\n    \"name\": \"健盛集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603611\",\n    \"name\": \"诺力股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"830974\",\n    \"name\": \"凯大催化\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002746\",\n    \"name\": \"仙坛股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603939\",\n    \"name\": \"益丰药房\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603898\",\n    \"name\": \"好莱客\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603222\",\n    \"name\": \"济民医疗\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300421\",\n    \"name\": \"力星股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002745\",\n    \"name\": \"木林森\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002743\",\n    \"name\": \"富煌钢构\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300422\",\n    \"name\": \"博世科\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002742\",\n    \"name\": \"ST三圣\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300427\",\n    \"name\": \"*ST红相\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300423\",\n    \"name\": \"昇辉科技\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603118\",\n    \"name\": \"共进股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603828\",\n    \"name\": \"柯利达\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603969\",\n    \"name\": \"银龙股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603997\",\n    \"name\": \"继峰股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831370\",\n    \"name\": \"新安洁\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831961\",\n    \"name\": \"创远信科\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603519\",\n    \"name\": \"立霸股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603268\",\n    \"name\": \"松发股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601689\",\n    \"name\": \"拓普集团\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002748\",\n    \"name\": \"世龙实业\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603012\",\n    \"name\": \"创力集团\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603158\",\n    \"name\": \"腾龙股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002749\",\n    \"name\": \"国光股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300429\",\n    \"name\": \"强力新材\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832149\",\n    \"name\": \"利尔达\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603338\",\n    \"name\": \"浙江鼎力\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603869\",\n    \"name\": \"新智认知\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831832\",\n    \"name\": \"科达自控\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831175\",\n    \"name\": \"派诺科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603818\",\n    \"name\": \"曲美家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603808\",\n    \"name\": \"歌力思\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300449\",\n    \"name\": \"汉邦高科\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300424\",\n    \"name\": \"航新科技\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300443\",\n    \"name\": \"金雷股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002752\",\n    \"name\": \"昇兴股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603703\",\n    \"name\": \"盛洋科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300441\",\n    \"name\": \"鲍斯股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300440\",\n    \"name\": \"运达科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300444\",\n    \"name\": \"双杰电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300445\",\n    \"name\": \"康斯特\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300448\",\n    \"name\": \"浩云科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603789\",\n    \"name\": \"星光农机\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603599\",\n    \"name\": \"广信股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603355\",\n    \"name\": \"莱克电气\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300414\",\n    \"name\": \"中光防雷\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300451\",\n    \"name\": \"创业慧康\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603718\",\n    \"name\": \"海利生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603989\",\n    \"name\": \"艾华集团\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603227\",\n    \"name\": \"雪峰科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300460\",\n    \"name\": \"惠伦晶体\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300452\",\n    \"name\": \"山河药辅\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603311\",\n    \"name\": \"金海高科\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603968\",\n    \"name\": \"醋化股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603022\",\n    \"name\": \"新通联\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603566\",\n    \"name\": \"普莱柯\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002753\",\n    \"name\": \"永东股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832469\",\n    \"name\": \"富恒新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"830832\",\n    \"name\": \"齐鲁华信\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832419\",\n    \"name\": \"路斯股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603885\",\n    \"name\": \"吉祥航空\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603023\",\n    \"name\": \"威帝股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603108\",\n    \"name\": \"润达医疗\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002757\",\n    \"name\": \"南兴股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300467\",\n    \"name\": \"迅游科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300468\",\n    \"name\": \"四方精创\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300462\",\n    \"name\": \"华铭智能\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603669\",\n    \"name\": \"灵康药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603918\",\n    \"name\": \"金桥信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603568\",\n    \"name\": \"伟明环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300465\",\n    \"name\": \"高伟达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603300\",\n    \"name\": \"华铁应急\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831768\",\n    \"name\": \"拾比佰\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"830839\",\n    \"name\": \"万通液压\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002763\",\n    \"name\": \"汇洁股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002761\",\n    \"name\": \"浙江建投\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300478\",\n    \"name\": \"杭州高新\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300464\",\n    \"name\": \"星徽股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603066\",\n    \"name\": \"音飞储存\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603616\",\n    \"name\": \"韩建河山\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002766\",\n    \"name\": \"索菱股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300471\",\n    \"name\": \"厚普股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002760\",\n    \"name\": \"凤形股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300476\",\n    \"name\": \"胜宏科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300469\",\n    \"name\": \"信息发展\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"830779\",\n    \"name\": \"武汉蓝电\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"601368\",\n    \"name\": \"绿城水务\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300479\",\n    \"name\": \"神思电子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300470\",\n    \"name\": \"中密控股\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002767\",\n    \"name\": \"先锋电子\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002765\",\n    \"name\": \"蓝黛科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"430510\",\n    \"name\": \"丰光精密\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831195\",\n    \"name\": \"三祥科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002772\",\n    \"name\": \"众兴菌业\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002776\",\n    \"name\": \"*ST柏龙\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603117\",\n    \"name\": \"ST万林\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603116\",\n    \"name\": \"红蜻蜓\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002775\",\n    \"name\": \"文科股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002769\",\n    \"name\": \"普路通\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603085\",\n    \"name\": \"天成自控\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603223\",\n    \"name\": \"恒通股份\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002768\",\n    \"name\": \"国恩股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300481\",\n    \"name\": \"濮阳惠成\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603838\",\n    \"name\": \"四通股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300488\",\n    \"name\": \"恒锋工具\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300480\",\n    \"name\": \"光力科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831641\",\n    \"name\": \"格利尔\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832735\",\n    \"name\": \"德源药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831305\",\n    \"name\": \"海希通讯\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831689\",\n    \"name\": \"克莱特\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832802\",\n    \"name\": \"保丽洁\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832225\",\n    \"name\": \"利通科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831278\",\n    \"name\": \"泰德股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831396\",\n    \"name\": \"许昌智能\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"430685\",\n    \"name\": \"新芝生物\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832145\",\n    \"name\": \"恒合股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"833171\",\n    \"name\": \"国航远洋\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831526\",\n    \"name\": \"凯华材料\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832491\",\n    \"name\": \"奥迪威\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832171\",\n    \"name\": \"志晟信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831445\",\n    \"name\": \"龙竹科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832175\",\n    \"name\": \"东方碳素\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300498\",\n    \"name\": \"温氏股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831856\",\n    \"name\": \"浩淼科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"833873\",\n    \"name\": \"中设咨询\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832978\",\n    \"name\": \"开特股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831039\",\n    \"name\": \"国义招标\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"834415\",\n    \"name\": \"恒拓开源\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603936\",\n    \"name\": \"博敏电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002779\",\n    \"name\": \"中坚科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833914\",\n    \"name\": \"远航精密\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603800\",\n    \"name\": \"道森股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833509\",\n    \"name\": \"同惠电子\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"834682\",\n    \"name\": \"球冠电缆\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300495\",\n    \"name\": \"*ST美尚\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833751\",\n    \"name\": \"惠同新材\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002785\",\n    \"name\": \"万里石\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833454\",\n    \"name\": \"同心传动\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300494\",\n    \"name\": \"盛天网络\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002787\",\n    \"name\": \"华源控股\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300491\",\n    \"name\": \"通合科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834062\",\n    \"name\": \"科润智控\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"835508\",\n    \"name\": \"殷图网联\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"833819\",\n    \"name\": \"颖泰生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832471\",\n    \"name\": \"美邦科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832651\",\n    \"name\": \"天罡股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300499\",\n    \"name\": \"高澜股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300500\",\n    \"name\": \"启迪设计\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603377\",\n    \"name\": \"东方时尚\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002788\",\n    \"name\": \"鹭燕医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002790\",\n    \"name\": \"瑞尔特\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834639\",\n    \"name\": \"晨光电缆\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603520\",\n    \"name\": \"司太立\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300503\",\n    \"name\": \"昊志机电\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835174\",\n    \"name\": \"五新隧装\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603861\",\n    \"name\": \"白云电器\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300506\",\n    \"name\": \"名家汇\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834765\",\n    \"name\": \"美之高\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002792\",\n    \"name\": \"通宇通讯\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002791\",\n    \"name\": \"坚朗五金\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835184\",\n    \"name\": \"国源科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603028\",\n    \"name\": \"赛福天\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832110\",\n    \"name\": \"雷特科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603701\",\n    \"name\": \"德宏股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603868\",\n    \"name\": \"飞科电器\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300508\",\n    \"name\": \"维宏股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603726\",\n    \"name\": \"朗迪集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300509\",\n    \"name\": \"新美星\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603822\",\n    \"name\": \"嘉澳环保\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002795\",\n    \"name\": \"永和智控\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300507\",\n    \"name\": \"苏奥传感\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603528\",\n    \"name\": \"多伦科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300511\",\n    \"name\": \"雪榕生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837212\",\n    \"name\": \"智新电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002796\",\n    \"name\": \"世嘉科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603339\",\n    \"name\": \"四方科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834475\",\n    \"name\": \"三友科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"834261\",\n    \"name\": \"一诺威\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002798\",\n    \"name\": \"帝欧家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300512\",\n    \"name\": \"中亚股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831834\",\n    \"name\": \"三维股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300513\",\n    \"name\": \"恒实科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002800\",\n    \"name\": \"ST天顺\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833580\",\n    \"name\": \"科创新材\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603737\",\n    \"name\": \"三棵树\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836419\",\n    \"name\": \"万德股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002799\",\n    \"name\": \"环球印务\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300515\",\n    \"name\": \"三德科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601127\",\n    \"name\": \"赛力斯\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833030\",\n    \"name\": \"立方控股\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002802\",\n    \"name\": \"洪汇新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603016\",\n    \"name\": \"新宏泰\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832885\",\n    \"name\": \"星辰科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"836263\",\n    \"name\": \"中航泰达\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300521\",\n    \"name\": \"爱司凯\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300522\",\n    \"name\": \"世名科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601966\",\n    \"name\": \"玲珑轮胎\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603069\",\n    \"name\": \"海汽集团\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300517\",\n    \"name\": \"海波重科\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832662\",\n    \"name\": \"方盛股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300525\",\n    \"name\": \"博思软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603322\",\n    \"name\": \"超讯通信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603569\",\n    \"name\": \"长久物流\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603159\",\n    \"name\": \"上海亚虹\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300535\",\n    \"name\": \"达威股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002808\",\n    \"name\": \"ST恒久\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300533\",\n    \"name\": \"冰川网络\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603515\",\n    \"name\": \"欧普照明\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603031\",\n    \"name\": \"安孚科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300540\",\n    \"name\": \"蜀道装备\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002809\",\n    \"name\": \"红墙股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603007\",\n    \"name\": \"ST花王\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002810\",\n    \"name\": \"山东赫达\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300537\",\n    \"name\": \"广信材料\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300539\",\n    \"name\": \"横河精密\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603090\",\n    \"name\": \"宏盛股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"838163\",\n    \"name\": \"方大新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603843\",\n    \"name\": \"正平股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"838810\",\n    \"name\": \"春光药装\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002811\",\n    \"name\": \"郑中设计\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601163\",\n    \"name\": \"三角轮胎\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300541\",\n    \"name\": \"先进数通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603189\",\n    \"name\": \"网达软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835179\",\n    \"name\": \"凯德石英\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"601500\",\n    \"name\": \"通用股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300536\",\n    \"name\": \"农尚环境\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603738\",\n    \"name\": \"泰晶科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300546\",\n    \"name\": \"雄帝科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300549\",\n    \"name\": \"优德精密\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300547\",\n    \"name\": \"川环科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603887\",\n    \"name\": \"城地香江\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603421\",\n    \"name\": \"鼎信通讯\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002813\",\n    \"name\": \"路畅科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002815\",\n    \"name\": \"崇达技术\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603313\",\n    \"name\": \"梦百合\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603816\",\n    \"name\": \"顾家家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836957\",\n    \"name\": \"汉维科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300551\",\n    \"name\": \"古鳌科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300555\",\n    \"name\": \"ST路通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300550\",\n    \"name\": \"和仁科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603859\",\n    \"name\": \"能科科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300553\",\n    \"name\": \"集智股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300552\",\n    \"name\": \"万集科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833075\",\n    \"name\": \"柏星龙\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603667\",\n    \"name\": \"五洲新春\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002816\",\n    \"name\": \"*ST和科\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603258\",\n    \"name\": \"电魂网络\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835207\",\n    \"name\": \"众诚科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603716\",\n    \"name\": \"塞力医疗\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300559\",\n    \"name\": \"佳发教育\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300560\",\n    \"name\": \"中富通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836077\",\n    \"name\": \"吉林碳谷\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"601882\",\n    \"name\": \"海天精工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"839719\",\n    \"name\": \"宁新新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603556\",\n    \"name\": \"海兴电力\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603977\",\n    \"name\": \"国泰集团\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300563\",\n    \"name\": \"神宇股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300566\",\n    \"name\": \"激智科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300561\",\n    \"name\": \"汇金科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002821\",\n    \"name\": \"凯莱英\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603727\",\n    \"name\": \"博迈科\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835670\",\n    \"name\": \"数字人\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"835857\",\n    \"name\": \"百甲科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603900\",\n    \"name\": \"莱绅通灵\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603336\",\n    \"name\": \"宏辉果蔬\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603819\",\n    \"name\": \"神力股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300569\",\n    \"name\": \"天能重工\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"839946\",\n    \"name\": \"华阳变速\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002825\",\n    \"name\": \"纳尔股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836504\",\n    \"name\": \"博迅生物\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"836149\",\n    \"name\": \"旭杰科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"836395\",\n    \"name\": \"朗鸿科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603319\",\n    \"name\": \"湘油泵\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603660\",\n    \"name\": \"苏州科达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603559\",\n    \"name\": \"ST通脉\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836414\",\n    \"name\": \"欧普泰\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300572\",\n    \"name\": \"安车检测\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603033\",\n    \"name\": \"三维股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002828\",\n    \"name\": \"贝肯能源\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300573\",\n    \"name\": \"兴齐眼药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603036\",\n    \"name\": \"如通股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002827\",\n    \"name\": \"高争民爆\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002830\",\n    \"name\": \"名雕股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603585\",\n    \"name\": \"苏利股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603389\",\n    \"name\": \"亚振家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603098\",\n    \"name\": \"森特股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002831\",\n    \"name\": \"裕同科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603878\",\n    \"name\": \"武进不锈\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300581\",\n    \"name\": \"晨曦航空\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300576\",\n    \"name\": \"容大感光\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300575\",\n    \"name\": \"中旗股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300577\",\n    \"name\": \"开润股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835368\",\n    \"name\": \"连城数控\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603058\",\n    \"name\": \"永吉股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002832\",\n    \"name\": \"比音勒芬\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"838030\",\n    \"name\": \"德众汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603298\",\n    \"name\": \"杭叉集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603218\",\n    \"name\": \"日月股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002833\",\n    \"name\": \"弘亚数控\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002835\",\n    \"name\": \"同为股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002836\",\n    \"name\": \"新宏泽\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002837\",\n    \"name\": \"英维克\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603239\",\n    \"name\": \"浙江仙通\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603929\",\n    \"name\": \"亚翔集成\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603444\",\n    \"name\": \"吉比特\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603035\",\n    \"name\": \"常熟汽饰\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300588\",\n    \"name\": \"熙菱信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603228\",\n    \"name\": \"景旺电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300583\",\n    \"name\": \"赛托生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002838\",\n    \"name\": \"道恩股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300591\",\n    \"name\": \"万里马\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603579\",\n    \"name\": \"荣泰健康\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300590\",\n    \"name\": \"移为通信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603639\",\n    \"name\": \"海利尔\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300584\",\n    \"name\": \"海辰药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603039\",\n    \"name\": \"泛微网络\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603690\",\n    \"name\": \"至纯科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300589\",\n    \"name\": \"江龙船艇\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300593\",\n    \"name\": \"新雷能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835985\",\n    \"name\": \"海泰新能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603668\",\n    \"name\": \"天马科技\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603165\",\n    \"name\": \"荣晟环保\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603337\",\n    \"name\": \"杰克股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002842\",\n    \"name\": \"翔鹭钨业\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300596\",\n    \"name\": \"利安隆\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603638\",\n    \"name\": \"艾迪精密\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603037\",\n    \"name\": \"凯众股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300592\",\n    \"name\": \"华凯易佰\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002843\",\n    \"name\": \"泰嘉股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833455\",\n    \"name\": \"汇隆活塞\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300599\",\n    \"name\": \"雄塑科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603429\",\n    \"name\": \"集友股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603358\",\n    \"name\": \"华达科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603966\",\n    \"name\": \"法兰泰克\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002845\",\n    \"name\": \"同兴达\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300578\",\n    \"name\": \"会畅通讯\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300600\",\n    \"name\": \"国瑞科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603089\",\n    \"name\": \"正裕工业\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300603\",\n    \"name\": \"立昂技术\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603677\",\n    \"name\": \"奇精机械\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603360\",\n    \"name\": \"百傲化学\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603177\",\n    \"name\": \"德创环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603637\",\n    \"name\": \"镇海股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300605\",\n    \"name\": \"恒锋信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300606\",\n    \"name\": \"金太阳\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603208\",\n    \"name\": \"江山欧派\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603615\",\n    \"name\": \"茶花股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603330\",\n    \"name\": \"天洋新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002848\",\n    \"name\": \"高斯贝尔\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603839\",\n    \"name\": \"安正时尚\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300615\",\n    \"name\": \"欣天科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603603\",\n    \"name\": \"*ST博天\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002849\",\n    \"name\": \"威星智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603817\",\n    \"name\": \"海峡环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300611\",\n    \"name\": \"美力科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833533\",\n    \"name\": \"骏创科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"600939\",\n    \"name\": \"重庆建工\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603238\",\n    \"name\": \"诺邦股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300617\",\n    \"name\": \"安靠智电\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603138\",\n    \"name\": \"海量数据\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603578\",\n    \"name\": \"三星新材\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002851\",\n    \"name\": \"麦格米特\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836942\",\n    \"name\": \"恒立钻具\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603908\",\n    \"name\": \"牧高笛\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300616\",\n    \"name\": \"尚品宅配\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603991\",\n    \"name\": \"至正股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002853\",\n    \"name\": \"皮阿诺\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603630\",\n    \"name\": \"拉芳家化\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603903\",\n    \"name\": \"中持股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300621\",\n    \"name\": \"维业股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603179\",\n    \"name\": \"新泉股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300625\",\n    \"name\": \"三雄极光\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300628\",\n    \"name\": \"亿联网络\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002856\",\n    \"name\": \"美芝股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300626\",\n    \"name\": \"华瑞股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300627\",\n    \"name\": \"华测导航\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002857\",\n    \"name\": \"三晖电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603768\",\n    \"name\": \"常青股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603388\",\n    \"name\": \"元成股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002774\",\n    \"name\": \"快意电梯\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300629\",\n    \"name\": \"新劲刚\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603833\",\n    \"name\": \"欧派家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603178\",\n    \"name\": \"圣龙股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300630\",\n    \"name\": \"普利制药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601228\",\n    \"name\": \"广州港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"832000\",\n    \"name\": \"安徽凤凰\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603041\",\n    \"name\": \"美思德\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601200\",\n    \"name\": \"上海环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300636\",\n    \"name\": \"同和药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300635\",\n    \"name\": \"中达安\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603385\",\n    \"name\": \"惠达卫浴\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603586\",\n    \"name\": \"金麒麟\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300632\",\n    \"name\": \"光莆股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603538\",\n    \"name\": \"美诺华\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603078\",\n    \"name\": \"江化微\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"838924\",\n    \"name\": \"广脉科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"839792\",\n    \"name\": \"东和新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002862\",\n    \"name\": \"实丰文化\",\n    \"tag\": \"传媒\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300637\",\n    \"name\": \"扬帆新材\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002860\",\n    \"name\": \"星帅尔\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603797\",\n    \"name\": \"联泰环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300638\",\n    \"name\": \"广和通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603050\",\n    \"name\": \"科林电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300640\",\n    \"name\": \"德艺文创\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837242\",\n    \"name\": \"建邦科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603225\",\n    \"name\": \"新凤鸣\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002863\",\n    \"name\": \"今飞凯达\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603803\",\n    \"name\": \"瑞斯康达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603081\",\n    \"name\": \"大丰实业\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603232\",\n    \"name\": \"格尔软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300554\",\n    \"name\": \"三超新材\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300645\",\n    \"name\": \"正元智慧\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002865\",\n    \"name\": \"钧达股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603920\",\n    \"name\": \"世运电路\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300514\",\n    \"name\": \"友讯达\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603787\",\n    \"name\": \"新日股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002867\",\n    \"name\": \"周大生\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603320\",\n    \"name\": \"迪贝电气\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"838227\",\n    \"name\": \"美登科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603505\",\n    \"name\": \"金石资源\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300650\",\n    \"name\": \"太龙股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603985\",\n    \"name\": \"恒润股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300649\",\n    \"name\": \"杭州园林\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300643\",\n    \"name\": \"万通智控\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603229\",\n    \"name\": \"奥翔药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603728\",\n    \"name\": \"鸣志电器\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300651\",\n    \"name\": \"金陵体育\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603926\",\n    \"name\": \"铁流股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837592\",\n    \"name\": \"华信永道\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603113\",\n    \"name\": \"金能科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603086\",\n    \"name\": \"先达股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002871\",\n    \"name\": \"伟隆股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603180\",\n    \"name\": \"金牌厨柜\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601952\",\n    \"name\": \"苏垦农发\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002870\",\n    \"name\": \"香山股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603488\",\n    \"name\": \"展鹏科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300652\",\n    \"name\": \"雷迪克\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603269\",\n    \"name\": \"海鸥股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603758\",\n    \"name\": \"秦安股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603197\",\n    \"name\": \"保隆科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300656\",\n    \"name\": \"民德电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835305\",\n    \"name\": \"云创数据\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603383\",\n    \"name\": \"顶点软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603855\",\n    \"name\": \"华荣股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603580\",\n    \"name\": \"艾艾精工\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300659\",\n    \"name\": \"中孚信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603042\",\n    \"name\": \"华脉科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300658\",\n    \"name\": \"延江股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300660\",\n    \"name\": \"江苏雷利\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002878\",\n    \"name\": \"元隆雅图\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834770\",\n    \"name\": \"艾能聚\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300665\",\n    \"name\": \"飞鹿股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603879\",\n    \"name\": \"永悦科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603226\",\n    \"name\": \"菲林格尔\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603326\",\n    \"name\": \"我乐家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834407\",\n    \"name\": \"驰诚股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603316\",\n    \"name\": \"诚邦股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300667\",\n    \"name\": \"必创科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300668\",\n    \"name\": \"杰恩设计\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836260\",\n    \"name\": \"中寰股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"838971\",\n    \"name\": \"天马新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603286\",\n    \"name\": \"日盈电子\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002886\",\n    \"name\": \"沃特股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871642\",\n    \"name\": \"通易航天\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603938\",\n    \"name\": \"三孚股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300669\",\n    \"name\": \"沪宁股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603801\",\n    \"name\": \"志邦家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834014\",\n    \"name\": \"特瑞斯\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300670\",\n    \"name\": \"大烨智能\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603331\",\n    \"name\": \"百达精工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833427\",\n    \"name\": \"华维设计\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002879\",\n    \"name\": \"长缆科技\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603305\",\n    \"name\": \"旭升集团\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603757\",\n    \"name\": \"大元泵业\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300673\",\n    \"name\": \"佩蒂股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002884\",\n    \"name\": \"凌霄泵业\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603612\",\n    \"name\": \"索通发展\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603707\",\n    \"name\": \"健友股份\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300675\",\n    \"name\": \"建科院\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603676\",\n    \"name\": \"卫信康\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300680\",\n    \"name\": \"隆盛科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300681\",\n    \"name\": \"英搏尔\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603730\",\n    \"name\": \"岱美股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603063\",\n    \"name\": \"禾望电气\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603233\",\n    \"name\": \"大参林\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002889\",\n    \"name\": \"东方嘉盛\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603357\",\n    \"name\": \"设计总院\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300682\",\n    \"name\": \"朗新集团\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002887\",\n    \"name\": \"绿茵生态\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002890\",\n    \"name\": \"弘宇股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300687\",\n    \"name\": \"赛意信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603535\",\n    \"name\": \"嘉诚国际\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300690\",\n    \"name\": \"双一科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603458\",\n    \"name\": \"勘设股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300689\",\n    \"name\": \"澄天伟业\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603602\",\n    \"name\": \"纵横通信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300688\",\n    \"name\": \"创业黑马\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601326\",\n    \"name\": \"秦港股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"835892\",\n    \"name\": \"中科美菱\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603776\",\n    \"name\": \"永安行\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603129\",\n    \"name\": \"春风动力\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603557\",\n    \"name\": \"ST起步\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002891\",\n    \"name\": \"中宠股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300692\",\n    \"name\": \"中环环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300696\",\n    \"name\": \"爱乐达\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300693\",\n    \"name\": \"盛弘股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603181\",\n    \"name\": \"皇马科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002896\",\n    \"name\": \"中大力德\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300698\",\n    \"name\": \"万马科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603359\",\n    \"name\": \"东珠生态\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300699\",\n    \"name\": \"光威复材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603500\",\n    \"name\": \"祥和实业\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603183\",\n    \"name\": \"建研院\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836892\",\n    \"name\": \"广咨国际\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603725\",\n    \"name\": \"天安新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603882\",\n    \"name\": \"金域医学\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300695\",\n    \"name\": \"兆丰股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603277\",\n    \"name\": \"银都股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603386\",\n    \"name\": \"骏亚科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300700\",\n    \"name\": \"岱勒新材\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603321\",\n    \"name\": \"梅轮电梯\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002899\",\n    \"name\": \"英派斯\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300701\",\n    \"name\": \"森霸传感\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002893\",\n    \"name\": \"京能热力\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834599\",\n    \"name\": \"同力股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603813\",\n    \"name\": \"原尚股份\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300703\",\n    \"name\": \"创源股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300702\",\n    \"name\": \"天宇股份\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603055\",\n    \"name\": \"台华新材\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603363\",\n    \"name\": \"傲农生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603136\",\n    \"name\": \"天目湖\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603378\",\n    \"name\": \"亚士创能\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603367\",\n    \"name\": \"辰欣药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300705\",\n    \"name\": \"九典制药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603110\",\n    \"name\": \"东方材料\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002906\",\n    \"name\": \"华阳集团\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300708\",\n    \"name\": \"聚灿光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603829\",\n    \"name\": \"洛凯股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300710\",\n    \"name\": \"万隆光电\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603683\",\n    \"name\": \"晶华新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002908\",\n    \"name\": \"德生科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603922\",\n    \"name\": \"金鸿顺\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603607\",\n    \"name\": \"京华激光\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300715\",\n    \"name\": \"凯伦股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002909\",\n    \"name\": \"集泰股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603260\",\n    \"name\": \"合盛硅业\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300712\",\n    \"name\": \"永福股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603912\",\n    \"name\": \"佳力图\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300711\",\n    \"name\": \"广哈通信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300713\",\n    \"name\": \"英可瑞\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603856\",\n    \"name\": \"东宏股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603507\",\n    \"name\": \"振江股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300720\",\n    \"name\": \"海川智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300717\",\n    \"name\": \"华信新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300718\",\n    \"name\": \"长盛轴承\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300716\",\n    \"name\": \"泉为科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300719\",\n    \"name\": \"安达维尔\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603916\",\n    \"name\": \"苏博特\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300722\",\n    \"name\": \"新余国科\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603076\",\n    \"name\": \"乐惠国际\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603278\",\n    \"name\": \"大业股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"839790\",\n    \"name\": \"联迪信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603605\",\n    \"name\": \"珀莱雅\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300721\",\n    \"name\": \"怡达股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300723\",\n    \"name\": \"一品红\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600933\",\n    \"name\": \"爱柯迪\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603619\",\n    \"name\": \"中曼石油\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603365\",\n    \"name\": \"水星家纺\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300726\",\n    \"name\": \"宏达电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603685\",\n    \"name\": \"晨丰科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300727\",\n    \"name\": \"润禾材料\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836221\",\n    \"name\": \"易实精密\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"870866\",\n    \"name\": \"绿亨科技\",\n    \"tag\": \"农业\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603809\",\n    \"name\": \"豪能股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871970\",\n    \"name\": \"大禹生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603848\",\n    \"name\": \"好太太\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002913\",\n    \"name\": \"奥士康\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603917\",\n    \"name\": \"合力科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300730\",\n    \"name\": \"科创信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300731\",\n    \"name\": \"科创新源\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300732\",\n    \"name\": \"设研院\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837663\",\n    \"name\": \"明阳科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"872351\",\n    \"name\": \"华光源海\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603477\",\n    \"name\": \"巨星农牧\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837006\",\n    \"name\": \"晟楠科技\",\n    \"tag\": \"军工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002918\",\n    \"name\": \"蒙娜丽莎\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002920\",\n    \"name\": \"德赛西威\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"838701\",\n    \"name\": \"豪声电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002921\",\n    \"name\": \"联诚精密\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"870976\",\n    \"name\": \"视声智能\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603655\",\n    \"name\": \"朗博科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002922\",\n    \"name\": \"伊戈尔\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603161\",\n    \"name\": \"科华控股\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300664\",\n    \"name\": \"鹏鹞环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300736\",\n    \"name\": \"百邦科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603056\",\n    \"name\": \"德邦股份\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300733\",\n    \"name\": \"西菱动力\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836675\",\n    \"name\": \"秉扬科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"830946\",\n    \"name\": \"森萱医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603356\",\n    \"name\": \"华菱精工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300737\",\n    \"name\": \"科顺股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603506\",\n    \"name\": \"南都物业\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300739\",\n    \"name\": \"明阳电路\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603516\",\n    \"name\": \"淳中科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603871\",\n    \"name\": \"嘉友国际\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300644\",\n    \"name\": \"南京聚隆\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603709\",\n    \"name\": \"中源家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002927\",\n    \"name\": \"泰永长征\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"870436\",\n    \"name\": \"大地电气\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603712\",\n    \"name\": \"七一二\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603680\",\n    \"name\": \"今创集团\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600901\",\n    \"name\": \"江苏金租\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603059\",\n    \"name\": \"倍加洁\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002928\",\n    \"name\": \"华夏航空\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833943\",\n    \"name\": \"优机股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"836717\",\n    \"name\": \"瑞星股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300634\",\n    \"name\": \"彩讯股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600929\",\n    \"name\": \"雪天盐业\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002930\",\n    \"name\": \"宏川智慧\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"838670\",\n    \"name\": \"恒进感应\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300504\",\n    \"name\": \"天邑股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603773\",\n    \"name\": \"沃格光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871396\",\n    \"name\": \"常辅股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603733\",\n    \"name\": \"仙鹤股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603348\",\n    \"name\": \"文灿股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603596\",\n    \"name\": \"伯特利\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300743\",\n    \"name\": \"天地数码\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603259\",\n    \"name\": \"药明康德\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300742\",\n    \"name\": \"*ST越博\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300454\",\n    \"name\": \"深信服\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300745\",\n    \"name\": \"欣锐科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300746\",\n    \"name\": \"汉嘉设计\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601330\",\n    \"name\": \"绿色动力\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603666\",\n    \"name\": \"亿嘉和\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"830896\",\n    \"name\": \"旺成科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603587\",\n    \"name\": \"地素时尚\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603650\",\n    \"name\": \"彤程新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603105\",\n    \"name\": \"芯能科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603713\",\n    \"name\": \"密尔克卫\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601869\",\n    \"name\": \"长飞光纤\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603657\",\n    \"name\": \"春光科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601606\",\n    \"name\": \"长城军工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300724\",\n    \"name\": \"捷佳伟创\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603192\",\n    \"name\": \"汇得科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002933\",\n    \"name\": \"新兴装备\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603297\",\n    \"name\": \"永新光学\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603790\",\n    \"name\": \"雅运股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002938\",\n    \"name\": \"鹏鼎控股\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603583\",\n    \"name\": \"捷昌驱动\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300748\",\n    \"name\": \"金力永磁\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300749\",\n    \"name\": \"顶固集创\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300694\",\n    \"name\": \"蠡湖股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002940\",\n    \"name\": \"昂利康\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300674\",\n    \"name\": \"宇信科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871553\",\n    \"name\": \"凯腾精工\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002941\",\n    \"name\": \"新疆交建\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603187\",\n    \"name\": \"海容冷链\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002943\",\n    \"name\": \"宇晶股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"839680\",\n    \"name\": \"广道数字\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300752\",\n    \"name\": \"隆利科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"833394\",\n    \"name\": \"民士达\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"838171\",\n    \"name\": \"邦德股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"832089\",\n    \"name\": \"禾昌聚合\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"837046\",\n    \"name\": \"亿能电力\",\n    \"tag\": \"电力\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831855\",\n    \"name\": \"浙江大农\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603185\",\n    \"name\": \"弘元绿能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300756\",\n    \"name\": \"金马游乐\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300757\",\n    \"name\": \"罗博特科\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603121\",\n    \"name\": \"华培动力\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"872374\",\n    \"name\": \"云里物里\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"870299\",\n    \"name\": \"灿能电力\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603332\",\n    \"name\": \"苏州龙杰\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601298\",\n    \"name\": \"青岛港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603700\",\n    \"name\": \"宁水集团\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601615\",\n    \"name\": \"明阳智能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300759\",\n    \"name\": \"康龙化成\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300755\",\n    \"name\": \"华致酒行\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603351\",\n    \"name\": \"威尔药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601865\",\n    \"name\": \"福莱特\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300761\",\n    \"name\": \"立华股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603956\",\n    \"name\": \"威派格\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300758\",\n    \"name\": \"七彩化学\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002949\",\n    \"name\": \"华阳国际\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300762\",\n    \"name\": \"上海瀚讯\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002951\",\n    \"name\": \"ST金时\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300766\",\n    \"name\": \"每日互动\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603681\",\n    \"name\": \"永冠新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300767\",\n    \"name\": \"震安科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"838275\",\n    \"name\": \"驱动力\",\n    \"tag\": \"农业\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300768\",\n    \"name\": \"迪普科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300771\",\n    \"name\": \"智莱科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300773\",\n    \"name\": \"拉卡拉\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300772\",\n    \"name\": \"运达股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603967\",\n    \"name\": \"中创物流\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002953\",\n    \"name\": \"日丰股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300778\",\n    \"name\": \"新城市\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603267\",\n    \"name\": \"鸿远电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"600989\",\n    \"name\": \"宝丰能源\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300777\",\n    \"name\": \"中简科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300776\",\n    \"name\": \"帝尔激光\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300775\",\n    \"name\": \"三角防务\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603982\",\n    \"name\": \"泉峰汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300779\",\n    \"name\": \"惠城环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002955\",\n    \"name\": \"鸿合科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"872541\",\n    \"name\": \"铁大科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300780\",\n    \"name\": \"德恩精工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603915\",\n    \"name\": \"国茂股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603217\",\n    \"name\": \"元利科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300594\",\n    \"name\": \"朗进科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836807\",\n    \"name\": \"奔朗新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300785\",\n    \"name\": \"值得买\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603236\",\n    \"name\": \"移远通信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603256\",\n    \"name\": \"宏和科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688020\",\n    \"name\": \"方邦股份\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688001\",\n    \"name\": \"华兴源创\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688066\",\n    \"name\": \"航天宏图\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688333\",\n    \"name\": \"铂力特\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688011\",\n    \"name\": \"新光光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688010\",\n    \"name\": \"福光股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688028\",\n    \"name\": \"沃尔德\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688019\",\n    \"name\": \"安集科技\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688088\",\n    \"name\": \"虹软科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688033\",\n    \"name\": \"天宜上佳\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300786\",\n    \"name\": \"国林科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603687\",\n    \"name\": \"大胜达\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603279\",\n    \"name\": \"景津装备\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603613\",\n    \"name\": \"国联股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603662\",\n    \"name\": \"柯力传感\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688188\",\n    \"name\": \"柏楚电子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002960\",\n    \"name\": \"青鸟消防\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002959\",\n    \"name\": \"小熊电器\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603992\",\n    \"name\": \"松霖科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300789\",\n    \"name\": \"唐源电气\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002961\",\n    \"name\": \"瑞达期货\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688168\",\n    \"name\": \"安博通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300790\",\n    \"name\": \"宇瞳光学\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688030\",\n    \"name\": \"山石网科\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603786\",\n    \"name\": \"科博达\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603815\",\n    \"name\": \"交建股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002965\",\n    \"name\": \"祥鑫科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688098\",\n    \"name\": \"申联生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002963\",\n    \"name\": \"豪尔赛\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300799\",\n    \"name\": \"*ST左江\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688369\",\n    \"name\": \"致远互联\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688058\",\n    \"name\": \"宝兰德\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688023\",\n    \"name\": \"安恒信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688202\",\n    \"name\": \"美迪西\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688199\",\n    \"name\": \"久日新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688299\",\n    \"name\": \"长阳科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688021\",\n    \"name\": \"奥福环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688288\",\n    \"name\": \"鸿泉物联\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300800\",\n    \"name\": \"力合科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002967\",\n    \"name\": \"广电计量\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300564\",\n    \"name\": \"筑博设计\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603489\",\n    \"name\": \"八方股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688101\",\n    \"name\": \"三达膜\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688300\",\n    \"name\": \"联瑞新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300796\",\n    \"name\": \"贝斯美\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688111\",\n    \"name\": \"金山办公\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300803\",\n    \"name\": \"指南针\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688196\",\n    \"name\": \"卓越新能\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300806\",\n    \"name\": \"斯迪克\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300808\",\n    \"name\": \"久量股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688310\",\n    \"name\": \"迈得医疗\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002968\",\n    \"name\": \"新大正\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688357\",\n    \"name\": \"建龙微纳\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688118\",\n    \"name\": \"普元信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300809\",\n    \"name\": \"华辰装备\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"831906\",\n    \"name\": \"舜宇精工\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688258\",\n    \"name\": \"卓易信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688039\",\n    \"name\": \"当虹科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300807\",\n    \"name\": \"天迈科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603995\",\n    \"name\": \"甬金股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002972\",\n    \"name\": \"科安达\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688078\",\n    \"name\": \"龙软科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300811\",\n    \"name\": \"铂科新材\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603109\",\n    \"name\": \"神驰机电\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688081\",\n    \"name\": \"兴图新科\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002973\",\n    \"name\": \"侨银股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688178\",\n    \"name\": \"万德斯\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300813\",\n    \"name\": \"泰林生物\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603551\",\n    \"name\": \"奥普家居\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836871\",\n    \"name\": \"派特尔\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688100\",\n    \"name\": \"威胜信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688026\",\n    \"name\": \"洁特生物\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688266\",\n    \"name\": \"泽璟制药-U\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688159\",\n    \"name\": \"有方科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300815\",\n    \"name\": \"玉禾田\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603195\",\n    \"name\": \"公牛集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300816\",\n    \"name\": \"艾可蓝\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688398\",\n    \"name\": \"赛特新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688186\",\n    \"name\": \"广大特材\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688080\",\n    \"name\": \"映翰通\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300818\",\n    \"name\": \"耐普矿机\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688208\",\n    \"name\": \"道通科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300820\",\n    \"name\": \"英杰电气\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300817\",\n    \"name\": \"双飞集团\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688169\",\n    \"name\": \"石头科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836208\",\n    \"name\": \"青矩技术\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603948\",\n    \"name\": \"建业股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"872392\",\n    \"name\": \"佳合科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"831304\",\n    \"name\": \"迪尔化工\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603949\",\n    \"name\": \"雪龙集团\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300819\",\n    \"name\": \"聚杰微纤\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300821\",\n    \"name\": \"东岳硅材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002977\",\n    \"name\": \"天箭科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837821\",\n    \"name\": \"则成电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300823\",\n    \"name\": \"建科机械\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688051\",\n    \"name\": \"佳华科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873339\",\n    \"name\": \"恒太照明\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603221\",\n    \"name\": \"爱丽家居\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688228\",\n    \"name\": \"开普云\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836720\",\n    \"name\": \"吉冈精密\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"839725\",\n    \"name\": \"惠丰钻石\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688096\",\n    \"name\": \"京源环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836961\",\n    \"name\": \"西磁科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300827\",\n    \"name\": \"上能电气\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"834058\",\n    \"name\": \"华洋赛车\",\n    \"tag\": \"公用\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"838402\",\n    \"name\": \"硅烷科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002980\",\n    \"name\": \"华盛昌\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002978\",\n    \"name\": \"安宁股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871857\",\n    \"name\": \"泓禧科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"872895\",\n    \"name\": \"花溪科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002982\",\n    \"name\": \"湘佳股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871753\",\n    \"name\": \"天纺标\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688318\",\n    \"name\": \"财富趋势\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002983\",\n    \"name\": \"芯瑞达\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688365\",\n    \"name\": \"光云科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002985\",\n    \"name\": \"北摩高科\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300830\",\n    \"name\": \"金现代\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002987\",\n    \"name\": \"京北方\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688466\",\n    \"name\": \"金科环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688588\",\n    \"name\": \"凌志软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300833\",\n    \"name\": \"浩洋股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605001\",\n    \"name\": \"威奥股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002990\",\n    \"name\": \"盛视科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300835\",\n    \"name\": \"龙磁科技\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603950\",\n    \"name\": \"长源东谷\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300836\",\n    \"name\": \"佰奥智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605288\",\n    \"name\": \"凯迪股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"870508\",\n    \"name\": \"丰安股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"002986\",\n    \"name\": \"宇新股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601827\",\n    \"name\": \"三峰环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300837\",\n    \"name\": \"浙矿股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688312\",\n    \"name\": \"燕麦科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300838\",\n    \"name\": \"浙江力诺\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688157\",\n    \"name\": \"松井股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002989\",\n    \"name\": \"中天精装\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688004\",\n    \"name\": \"博汇科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688106\",\n    \"name\": \"金宏气体\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605166\",\n    \"name\": \"聚合顺\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688505\",\n    \"name\": \"复旦张江\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300824\",\n    \"name\": \"北鼎股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688528\",\n    \"name\": \"秦川物联\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688600\",\n    \"name\": \"皖仪科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300845\",\n    \"name\": \"捷安高科\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688377\",\n    \"name\": \"迪威尔\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300840\",\n    \"name\": \"酷特智能\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688027\",\n    \"name\": \"国盾量子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688060\",\n    \"name\": \"云涌科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300849\",\n    \"name\": \"锦盛新材\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300852\",\n    \"name\": \"四会富仕\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300850\",\n    \"name\": \"新强联\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688309\",\n    \"name\": \"恒誉环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605108\",\n    \"name\": \"同庆楼\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688500\",\n    \"name\": \"*ST慧辰\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688579\",\n    \"name\": \"山大地纬\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300851\",\n    \"name\": \"交大思诺\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688069\",\n    \"name\": \"德林海\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688077\",\n    \"name\": \"大地熊\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688561\",\n    \"name\": \"奇安信-U\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300856\",\n    \"name\": \"科思股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871245\",\n    \"name\": \"威博液压\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603408\",\n    \"name\": \"建霖家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605222\",\n    \"name\": \"起帆电缆\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688311\",\n    \"name\": \"盟升电子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605318\",\n    \"name\": \"法狮龙\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002992\",\n    \"name\": \"宝明科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"838262\",\n    \"name\": \"太湖雪\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300859\",\n    \"name\": \"*ST西域\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605066\",\n    \"name\": \"天正电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688556\",\n    \"name\": \"高测股份\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688339\",\n    \"name\": \"亿华通-U\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605100\",\n    \"name\": \"华丰股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688313\",\n    \"name\": \"仕佳光子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688065\",\n    \"name\": \"凯赛生物\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605088\",\n    \"name\": \"冠盛股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688055\",\n    \"name\": \"龙腾光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688335\",\n    \"name\": \"复洁环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688229\",\n    \"name\": \"博睿数据\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688519\",\n    \"name\": \"南亚新材\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605333\",\n    \"name\": \"沪光股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603931\",\n    \"name\": \"格林达\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688379\",\n    \"name\": \"华光新材\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688596\",\n    \"name\": \"正帆科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605008\",\n    \"name\": \"长鸿高科\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300877\",\n    \"name\": \"金春股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300868\",\n    \"name\": \"杰美特\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300873\",\n    \"name\": \"海晨股份\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300863\",\n    \"name\": \"卡倍亿\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300864\",\n    \"name\": \"南大环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300872\",\n    \"name\": \"天阳科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300871\",\n    \"name\": \"回盛生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300862\",\n    \"name\": \"蓝盾光电\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300867\",\n    \"name\": \"圣元环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300861\",\n    \"name\": \"美畅股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605255\",\n    \"name\": \"天普股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605123\",\n    \"name\": \"派克新材\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688017\",\n    \"name\": \"绿的谐波\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688215\",\n    \"name\": \"瑞晟智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603155\",\n    \"name\": \"新亚强\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300879\",\n    \"name\": \"大叶股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300881\",\n    \"name\": \"盛德鑫泰\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300880\",\n    \"name\": \"迦南智能\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688550\",\n    \"name\": \"瑞联新材\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688513\",\n    \"name\": \"苑东生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688056\",\n    \"name\": \"莱伯泰科\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688378\",\n    \"name\": \"奥来德\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605006\",\n    \"name\": \"山东玻纤\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002997\",\n    \"name\": \"瑞鹄模具\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605003\",\n    \"name\": \"众望布艺\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688095\",\n    \"name\": \"福昕软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300883\",\n    \"name\": \"龙利得\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300885\",\n    \"name\": \"海昌新材\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300882\",\n    \"name\": \"万胜智能\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873169\",\n    \"name\": \"七丰精工\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"605009\",\n    \"name\": \"豪悦护理\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002984\",\n    \"name\": \"森麒麟\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605128\",\n    \"name\": \"上海沿浦\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300889\",\n    \"name\": \"爱克股份\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300887\",\n    \"name\": \"谱尼测试\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603112\",\n    \"name\": \"华翔股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300888\",\n    \"name\": \"稳健医疗\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300891\",\n    \"name\": \"惠云钛业\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605116\",\n    \"name\": \"奥锐特\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688127\",\n    \"name\": \"蓝特光学\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003006\",\n    \"name\": \"百亚股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837174\",\n    \"name\": \"宏裕包材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688526\",\n    \"name\": \"科前生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688156\",\n    \"name\": \"路德环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003005\",\n    \"name\": \"竞业达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003002\",\n    \"name\": \"壶化股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003008\",\n    \"name\": \"开普检测\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605050\",\n    \"name\": \"福然德\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300893\",\n    \"name\": \"松原股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300895\",\n    \"name\": \"铜牛信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603565\",\n    \"name\": \"中谷物流\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003010\",\n    \"name\": \"若羽臣\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"002998\",\n    \"name\": \"优彩资源\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688585\",\n    \"name\": \"上纬新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300897\",\n    \"name\": \"山科智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605018\",\n    \"name\": \"长华集团\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688093\",\n    \"name\": \"世华科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605099\",\n    \"name\": \"共创草坪\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003011\",\n    \"name\": \"海象新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003001\",\n    \"name\": \"中岩大地\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688330\",\n    \"name\": \"宏力达\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688386\",\n    \"name\": \"泛亚微透\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300899\",\n    \"name\": \"上海凯鑫\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605336\",\n    \"name\": \"帅丰电器\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003012\",\n    \"name\": \"东鹏控股\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601568\",\n    \"name\": \"北元集团\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605058\",\n    \"name\": \"澳弘电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688788\",\n    \"name\": \"科思科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003013\",\n    \"name\": \"地铁设计\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688129\",\n    \"name\": \"东来技术\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688179\",\n    \"name\": \"阿拉丁\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003016\",\n    \"name\": \"欣贺股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003017\",\n    \"name\": \"大洋生物\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836247\",\n    \"name\": \"华密新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"689009\",\n    \"name\": \"九号公司-WD\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300902\",\n    \"name\": \"国安达\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300901\",\n    \"name\": \"中胤时尚\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300900\",\n    \"name\": \"广联航空\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688133\",\n    \"name\": \"泰坦科技\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300905\",\n    \"name\": \"宝丽迪\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300906\",\n    \"name\": \"日月明\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003018\",\n    \"name\": \"金富科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605007\",\n    \"name\": \"五洲特纸\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688057\",\n    \"name\": \"金达莱\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688160\",\n    \"name\": \"步科股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300884\",\n    \"name\": \"狄耐克\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873527\",\n    \"name\": \"夜光明\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688219\",\n    \"name\": \"会通股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300909\",\n    \"name\": \"汇创达\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300907\",\n    \"name\": \"康平科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605068\",\n    \"name\": \"明新旭腾\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688777\",\n    \"name\": \"中控技术\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605177\",\n    \"name\": \"东亚药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003004\",\n    \"name\": \"声迅股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300910\",\n    \"name\": \"瑞丰新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605266\",\n    \"name\": \"健之佳\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605258\",\n    \"name\": \"协和电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300911\",\n    \"name\": \"亿田智能\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601686\",\n    \"name\": \"友发集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688590\",\n    \"name\": \"新致软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605183\",\n    \"name\": \"确成股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300913\",\n    \"name\": \"兆龙互连\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300912\",\n    \"name\": \"凯龙高科\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688308\",\n    \"name\": \"欧科亿\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688571\",\n    \"name\": \"杭华股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003023\",\n    \"name\": \"彩虹集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605151\",\n    \"name\": \"西上海\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605299\",\n    \"name\": \"舒华体育\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688510\",\n    \"name\": \"航亚科技\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836270\",\n    \"name\": \"天铭科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688668\",\n    \"name\": \"鼎通科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605500\",\n    \"name\": \"森林包装\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605186\",\n    \"name\": \"健麾信息\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300921\",\n    \"name\": \"南凌科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300918\",\n    \"name\": \"南山智尚\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871981\",\n    \"name\": \"晶赛科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300923\",\n    \"name\": \"研奥股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601956\",\n    \"name\": \"东贝集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688679\",\n    \"name\": \"通源环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300922\",\n    \"name\": \"天秦装备\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300920\",\n    \"name\": \"润阳科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605377\",\n    \"name\": \"华旺科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688698\",\n    \"name\": \"伟创电气\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688618\",\n    \"name\": \"三旺通信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300925\",\n    \"name\": \"法本信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688686\",\n    \"name\": \"奥普特\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605155\",\n    \"name\": \"西大门\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300894\",\n    \"name\": \"火星人\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873305\",\n    \"name\": \"九菱科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300928\",\n    \"name\": \"华安鑫创\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300927\",\n    \"name\": \"江天化学\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300926\",\n    \"name\": \"博俊科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003033\",\n    \"name\": \"征和工业\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605005\",\n    \"name\": \"合兴股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605228\",\n    \"name\": \"神通科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300935\",\n    \"name\": \"盈建科\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300929\",\n    \"name\": \"华骐环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605398\",\n    \"name\": \"新炬网络\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300931\",\n    \"name\": \"通用电梯\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688680\",\n    \"name\": \"海优新材\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300933\",\n    \"name\": \"中辰股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300932\",\n    \"name\": \"三友联众\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300936\",\n    \"name\": \"中英科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"870656\",\n    \"name\": \"N海昇\",\n    \"tag\": \"农业\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"300937\",\n    \"name\": \"药易购\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300938\",\n    \"name\": \"信测标准\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688350\",\n    \"name\": \"富淼科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300939\",\n    \"name\": \"秋田微\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003036\",\n    \"name\": \"泰坦股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605055\",\n    \"name\": \"迎丰股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688628\",\n    \"name\": \"优利德\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300940\",\n    \"name\": \"南极光\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"870204\",\n    \"name\": \"沪江材料\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"003037\",\n    \"name\": \"三和管桩\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688059\",\n    \"name\": \"华锐精密\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300946\",\n    \"name\": \"恒而达\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605081\",\n    \"name\": \"太和水\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688665\",\n    \"name\": \"四方光电\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300942\",\n    \"name\": \"易瑞生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688070\",\n    \"name\": \"纵横股份\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300943\",\n    \"name\": \"春晖智控\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300945\",\n    \"name\": \"曼卡龙\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688619\",\n    \"name\": \"罗普特\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605133\",\n    \"name\": \"嵘泰股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605268\",\n    \"name\": \"王力安防\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688183\",\n    \"name\": \"生益电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300948\",\n    \"name\": \"冠中生态\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605060\",\n    \"name\": \"联德股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605298\",\n    \"name\": \"必得科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605303\",\n    \"name\": \"园林股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871478\",\n    \"name\": \"巨能股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688079\",\n    \"name\": \"美迪凯\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688696\",\n    \"name\": \"极米科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300950\",\n    \"name\": \"德固特\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605208\",\n    \"name\": \"永茂泰\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003039\",\n    \"name\": \"顺控发展\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688328\",\n    \"name\": \"深科达\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688676\",\n    \"name\": \"金盘科技\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605122\",\n    \"name\": \"四方新材\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688083\",\n    \"name\": \"中望软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300952\",\n    \"name\": \"恒辉安防\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688667\",\n    \"name\": \"菱电电控\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688316\",\n    \"name\": \"青云科技-U\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688616\",\n    \"name\": \"西力科技\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"837748\",\n    \"name\": \"路桥信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688092\",\n    \"name\": \"爱科科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300959\",\n    \"name\": \"线上线下\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003040\",\n    \"name\": \"楚天龙\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688633\",\n    \"name\": \"星球石墨\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300955\",\n    \"name\": \"嘉亨家化\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300957\",\n    \"name\": \"贝泰妮\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603759\",\n    \"name\": \"海天股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688195\",\n    \"name\": \"腾景科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300958\",\n    \"name\": \"建工修复\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300960\",\n    \"name\": \"通业科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688109\",\n    \"name\": \"品茗科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300961\",\n    \"name\": \"深水海纳\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688659\",\n    \"name\": \"元琛科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688630\",\n    \"name\": \"芯碁微装\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688662\",\n    \"name\": \"富信科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300965\",\n    \"name\": \"恒宇信通\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003041\",\n    \"name\": \"真爱美家\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"003043\",\n    \"name\": \"华亚智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603324\",\n    \"name\": \"盛剑环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873167\",\n    \"name\": \"新赣江\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688191\",\n    \"name\": \"智洋创新\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688636\",\n    \"name\": \"智明达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300966\",\n    \"name\": \"共同药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688611\",\n    \"name\": \"杭州柯林\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300969\",\n    \"name\": \"恒帅股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300970\",\n    \"name\": \"华绿生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688315\",\n    \"name\": \"诺禾致源\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688663\",\n    \"name\": \"新风光\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300967\",\n    \"name\": \"晓鸣股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601279\",\n    \"name\": \"英利汽车\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300971\",\n    \"name\": \"博亚精工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605086\",\n    \"name\": \"龙高股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873223\",\n    \"name\": \"荣亿精密\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688533\",\n    \"name\": \"上声电子\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300972\",\n    \"name\": \"万辰集团\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688682\",\n    \"name\": \"霍莱沃\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300977\",\n    \"name\": \"深圳瑞捷\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300983\",\n    \"name\": \"尤安设计\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605098\",\n    \"name\": \"行动教育\",\n    \"tag\": \"教育\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688201\",\n    \"name\": \"信安世纪\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300975\",\n    \"name\": \"商络电子\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605289\",\n    \"name\": \"罗曼股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300979\",\n    \"name\": \"华利集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300978\",\n    \"name\": \"东箭科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300982\",\n    \"name\": \"苏文电能\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688323\",\n    \"name\": \"瑞华泰\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001201\",\n    \"name\": \"东瑞股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873576\",\n    \"name\": \"天力复合\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688395\",\n    \"name\": \"正弦电气\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001202\",\n    \"name\": \"炬申股份\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688113\",\n    \"name\": \"联测科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605305\",\n    \"name\": \"中际联合\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688655\",\n    \"name\": \"迅捷兴\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605196\",\n    \"name\": \"华通线缆\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688355\",\n    \"name\": \"明志科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300988\",\n    \"name\": \"津荣天宇\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300990\",\n    \"name\": \"同飞股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688565\",\n    \"name\": \"力源科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605488\",\n    \"name\": \"福莱新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001205\",\n    \"name\": \"盛航股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001206\",\n    \"name\": \"依依股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688660\",\n    \"name\": \"电气风电\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688076\",\n    \"name\": \"诺泰生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688359\",\n    \"name\": \"三孚新科\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300993\",\n    \"name\": \"玉马遮阳\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300992\",\n    \"name\": \"泰福泵业\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300614\",\n    \"name\": \"百川畅银\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603836\",\n    \"name\": \"海程邦达\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300995\",\n    \"name\": \"奇德新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605189\",\n    \"name\": \"富春染织\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688538\",\n    \"name\": \"和辉光电-U\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605296\",\n    \"name\": \"神农集团\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301001\",\n    \"name\": \"凯淳股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603511\",\n    \"name\": \"爱慕股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301005\",\n    \"name\": \"超捷股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301003\",\n    \"name\": \"江苏博云\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688117\",\n    \"name\": \"圣诺生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300996\",\n    \"name\": \"普联软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688625\",\n    \"name\": \"呈和科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605319\",\n    \"name\": \"无锡振华\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301002\",\n    \"name\": \"崧盛股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301006\",\n    \"name\": \"迈拓股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688269\",\n    \"name\": \"凯立新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301008\",\n    \"name\": \"宏昌科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605259\",\n    \"name\": \"绿田机械\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688700\",\n    \"name\": \"东威科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688681\",\n    \"name\": \"科汇股份\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301007\",\n    \"name\": \"德迈仕\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688597\",\n    \"name\": \"煜邦电力\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301009\",\n    \"name\": \"可靠股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688517\",\n    \"name\": \"金冠电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301010\",\n    \"name\": \"晶雪节能\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300984\",\n    \"name\": \"金沃股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688621\",\n    \"name\": \"阳光诺和\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688690\",\n    \"name\": \"纳微科技\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001207\",\n    \"name\": \"联科科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001208\",\n    \"name\": \"华菱线缆\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301004\",\n    \"name\": \"嘉益股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688367\",\n    \"name\": \"工大高科\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301019\",\n    \"name\": \"宁波色母\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301013\",\n    \"name\": \"利和兴\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603171\",\n    \"name\": \"税友股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301016\",\n    \"name\": \"雷尔伟\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301022\",\n    \"name\": \"海泰科\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688239\",\n    \"name\": \"航宇科技\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301017\",\n    \"name\": \"漱玉平民\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301021\",\n    \"name\": \"英诺激光\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301020\",\n    \"name\": \"密封科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605162\",\n    \"name\": \"新中港\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688226\",\n    \"name\": \"威腾电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301023\",\n    \"name\": \"江南奕帆\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301018\",\n    \"name\": \"申菱环境\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688789\",\n    \"name\": \"宏华数科\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301039\",\n    \"name\": \"中集车辆\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688305\",\n    \"name\": \"科德数控\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688087\",\n    \"name\": \"英科再生\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688038\",\n    \"name\": \"中科通达\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688793\",\n    \"name\": \"倍轻松\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301031\",\n    \"name\": \"中熔电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301027\",\n    \"name\": \"华蓝集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688501\",\n    \"name\": \"青达环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301026\",\n    \"name\": \"浩通科技\",\n    \"tag\": \"资源\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301028\",\n    \"name\": \"东亚机械\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688303\",\n    \"name\": \"大全能源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688800\",\n    \"name\": \"瑞可达\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301032\",\n    \"name\": \"新柴股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301030\",\n    \"name\": \"仕净科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301029\",\n    \"name\": \"怡合达\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688768\",\n    \"name\": \"容知日新\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301033\",\n    \"name\": \"迈普医学\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688296\",\n    \"name\": \"和达科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688718\",\n    \"name\": \"唯赛勃\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301024\",\n    \"name\": \"霍普股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301035\",\n    \"name\": \"润丰股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688071\",\n    \"name\": \"华依科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001210\",\n    \"name\": \"金房能源\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301036\",\n    \"name\": \"双乐股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688511\",\n    \"name\": \"天微电子\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301037\",\n    \"name\": \"保立佳\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688670\",\n    \"name\": \"金迪克\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605507\",\n    \"name\": \"国邦医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301040\",\n    \"name\": \"中环海陆\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301038\",\n    \"name\": \"深水规院\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300774\",\n    \"name\": \"倍杰特\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301042\",\n    \"name\": \"安联锐视\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001211\",\n    \"name\": \"双枪科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300964\",\n    \"name\": \"本川智能\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301043\",\n    \"name\": \"绿岛风\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301041\",\n    \"name\": \"金百泽\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300814\",\n    \"name\": \"中富电路\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300994\",\n    \"name\": \"久祺股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301045\",\n    \"name\": \"天禄科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300844\",\n    \"name\": \"山水比德\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301046\",\n    \"name\": \"能辉科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301049\",\n    \"name\": \"超越科技\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301051\",\n    \"name\": \"信濠光电\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688776\",\n    \"name\": \"国光电气\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301053\",\n    \"name\": \"远信工业\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688121\",\n    \"name\": \"卓然股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301055\",\n    \"name\": \"张小泉\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301056\",\n    \"name\": \"森赫股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605599\",\n    \"name\": \"菜百股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688091\",\n    \"name\": \"上海谊众\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301057\",\n    \"name\": \"汇隆新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688103\",\n    \"name\": \"国力股份\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688622\",\n    \"name\": \"禾信仪器\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301059\",\n    \"name\": \"金三江\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301061\",\n    \"name\": \"匠心家居\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301062\",\n    \"name\": \"上海艾录\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605033\",\n    \"name\": \"美邦股份\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688701\",\n    \"name\": \"卓锦股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"300854\",\n    \"name\": \"中兰环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605598\",\n    \"name\": \"上海港湾\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688697\",\n    \"name\": \"纽威数控\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301066\",\n    \"name\": \"万事利\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301070\",\n    \"name\": \"开勒股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301071\",\n    \"name\": \"力量钻石\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301063\",\n    \"name\": \"海锅股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301068\",\n    \"name\": \"大地海洋\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301072\",\n    \"name\": \"中捷精工\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301075\",\n    \"name\": \"多瑞医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301077\",\n    \"name\": \"星华新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301073\",\n    \"name\": \"君亭酒店\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"871694\",\n    \"name\": \"中裕科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"001218\",\n    \"name\": \"丽臣实业\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688272\",\n    \"name\": \"*ST富吉\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301080\",\n    \"name\": \"百普赛斯\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301079\",\n    \"name\": \"邵阳液压\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301081\",\n    \"name\": \"严牌股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605555\",\n    \"name\": \"德昌股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688737\",\n    \"name\": \"中自科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605566\",\n    \"name\": \"福莱蒽特\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688553\",\n    \"name\": \"汇宇制药-W\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688211\",\n    \"name\": \"中科微至\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"605138\",\n    \"name\": \"盛泰集团\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688280\",\n    \"name\": \"精进电动-UW\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301082\",\n    \"name\": \"久盛电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301089\",\n    \"name\": \"拓新药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301088\",\n    \"name\": \"戎美股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301091\",\n    \"name\": \"深城交\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001288\",\n    \"name\": \"运机集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301092\",\n    \"name\": \"争光股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301129\",\n    \"name\": \"瑞纳智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301169\",\n    \"name\": \"零点有数\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688162\",\n    \"name\": \"巨一科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301128\",\n    \"name\": \"强瑞技术\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301188\",\n    \"name\": \"力诺特玻\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301098\",\n    \"name\": \"金埔园林\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301178\",\n    \"name\": \"天亿马\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603048\",\n    \"name\": \"浙江黎明\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688182\",\n    \"name\": \"灿勤科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688232\",\n    \"name\": \"新点软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001267\",\n    \"name\": \"汇绿生态\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301185\",\n    \"name\": \"鸥玛软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603219\",\n    \"name\": \"富佳股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301119\",\n    \"name\": \"正强股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301099\",\n    \"name\": \"雅创电子\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301155\",\n    \"name\": \"海力风电\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301133\",\n    \"name\": \"金钟股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001317\",\n    \"name\": \"三羊马\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688112\",\n    \"name\": \"鼎阳科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301198\",\n    \"name\": \"喜悦智行\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301108\",\n    \"name\": \"洁雅股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301213\",\n    \"name\": \"观想科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301167\",\n    \"name\": \"建研设计\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301126\",\n    \"name\": \"达嘉维康\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301179\",\n    \"name\": \"泽宇智能\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688192\",\n    \"name\": \"迪哲医药-U\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688246\",\n    \"name\": \"嘉和美康\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603216\",\n    \"name\": \"梦天家居\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301177\",\n    \"name\": \"迪阿股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301138\",\n    \"name\": \"华研精机\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873132\",\n    \"name\": \"泰鹏智能\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603071\",\n    \"name\": \"物产环能\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301101\",\n    \"name\": \"明月镜片\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301100\",\n    \"name\": \"风光股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301096\",\n    \"name\": \"百诚医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301211\",\n    \"name\": \"亨迪药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301113\",\n    \"name\": \"雅艺科技\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"870357\",\n    \"name\": \"雅葆轩\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"600927\",\n    \"name\": \"永安期货\",\n    \"tag\": \"金融\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301186\",\n    \"name\": \"超达装备\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301190\",\n    \"name\": \"善水科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001296\",\n    \"name\": \"长江材料\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688210\",\n    \"name\": \"统联精密\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688206\",\n    \"name\": \"概伦电子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301189\",\n    \"name\": \"奥尼电子\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301166\",\n    \"name\": \"优宁维\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301127\",\n    \"name\": \"天源环保\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688227\",\n    \"name\": \"品高股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603176\",\n    \"name\": \"汇通集团\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301159\",\n    \"name\": \"三维天地\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873001\",\n    \"name\": \"纬达光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"001234\",\n    \"name\": \"泰慕士\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301196\",\n    \"name\": \"唯科科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301136\",\n    \"name\": \"招标股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301117\",\n    \"name\": \"佳缘科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301158\",\n    \"name\": \"德石股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301201\",\n    \"name\": \"诚达药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603150\",\n    \"name\": \"万朗磁塑\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688223\",\n    \"name\": \"晶科能源\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688171\",\n    \"name\": \"纬德信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301228\",\n    \"name\": \"实朴检测\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301235\",\n    \"name\": \"华康医疗\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301106\",\n    \"name\": \"骏成科技\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688225\",\n    \"name\": \"亚信安全\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688283\",\n    \"name\": \"坤恒顺维\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001313\",\n    \"name\": \"粤海饲料\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688267\",\n    \"name\": \"中触媒\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603215\",\n    \"name\": \"比依股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873593\",\n    \"name\": \"鼎智科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"301181\",\n    \"name\": \"标榜股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873665\",\n    \"name\": \"科强股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"301229\",\n    \"name\": \"纽泰格\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301130\",\n    \"name\": \"西点药业\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301200\",\n    \"name\": \"大族数控\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001266\",\n    \"name\": \"宏英智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688281\",\n    \"name\": \"华秦科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301218\",\n    \"name\": \"华是科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301222\",\n    \"name\": \"浙江恒威\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603070\",\n    \"name\": \"万控智造\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301110\",\n    \"name\": \"青木股份\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688115\",\n    \"name\": \"思林杰\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301131\",\n    \"name\": \"聚赛龙\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688175\",\n    \"name\": \"高凌信息\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603261\",\n    \"name\": \"立航科技\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688207\",\n    \"name\": \"格灵深瞳\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688282\",\n    \"name\": \"理工导航\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688150\",\n    \"name\": \"莱特光电\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301256\",\n    \"name\": \"华融化学\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301103\",\n    \"name\": \"何氏眼科\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688238\",\n    \"name\": \"和元生物\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301237\",\n    \"name\": \"和顺科技\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603209\",\n    \"name\": \"兴通股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301226\",\n    \"name\": \"祥明智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301258\",\n    \"name\": \"富士莱\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301216\",\n    \"name\": \"万凯新材\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301263\",\n    \"name\": \"泰恩康\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301268\",\n    \"name\": \"铭利达\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688337\",\n    \"name\": \"普源精电\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301151\",\n    \"name\": \"冠龙节能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301279\",\n    \"name\": \"金道科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301109\",\n    \"name\": \"军信股份\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688125\",\n    \"name\": \"安达智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301212\",\n    \"name\": \"联盛化学\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301163\",\n    \"name\": \"宏德股份\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301120\",\n    \"name\": \"新特电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688326\",\n    \"name\": \"经纬恒润-W\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301248\",\n    \"name\": \"杰创智能\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301148\",\n    \"name\": \"嘉戎技术\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301288\",\n    \"name\": \"清研环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301259\",\n    \"name\": \"艾布鲁\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603191\",\n    \"name\": \"望变电气\",\n    \"tag\": \"电力\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688320\",\n    \"name\": \"禾川科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301162\",\n    \"name\": \"国能日新\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688170\",\n    \"name\": \"德龙激光\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001228\",\n    \"name\": \"永泰运\",\n    \"tag\": \"统一大市场\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001319\",\n    \"name\": \"铭科精技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301257\",\n    \"name\": \"普蕊斯\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301153\",\n    \"name\": \"中科江南\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603272\",\n    \"name\": \"联翔股份\",\n    \"tag\": \"房地产\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301183\",\n    \"name\": \"东田微\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301107\",\n    \"name\": \"瑜欣电子\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688287\",\n    \"name\": \"观典防务\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301191\",\n    \"name\": \"菲菱科思\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873690\",\n    \"name\": \"捷众科技\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"301160\",\n    \"name\": \"翔楼新材\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688251\",\n    \"name\": \"井松智能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"838837\",\n    \"name\": \"华原股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"301125\",\n    \"name\": \"腾亚精工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688348\",\n    \"name\": \"昱能科技\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301286\",\n    \"name\": \"侨源股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301156\",\n    \"name\": \"美农生物\",\n    \"tag\": \"农业\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001226\",\n    \"name\": \"拓山重工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301220\",\n    \"name\": \"亚香股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688349\",\n    \"name\": \"三一重能\",\n    \"tag\": \"赛道\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301289\",\n    \"name\": \"国缆检测\",\n    \"tag\": \"专业服务\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001323\",\n    \"name\": \"慕思股份\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001316\",\n    \"name\": \"润贝航科\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"601089\",\n    \"name\": \"福元医药\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001268\",\n    \"name\": \"联合精密\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"873833\",\n    \"name\": \"美心翼申\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"688237\",\n    \"name\": \"超卓航科\",\n    \"tag\": \"军工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301239\",\n    \"name\": \"普瑞眼科\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301233\",\n    \"name\": \"盛帮股份\",\n    \"tag\": \"化工\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688400\",\n    \"name\": \"凌云光\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688322\",\n    \"name\": \"奥比中光-UW\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301208\",\n    \"name\": \"中亦科技\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301139\",\n    \"name\": \"元道通信\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688053\",\n    \"name\": \"思科瑞\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301312\",\n    \"name\": \"智立方\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001230\",\n    \"name\": \"劲旅环境\",\n    \"tag\": \"公用\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"001336\",\n    \"name\": \"楚环科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301306\",\n    \"name\": \"西测测试\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301269\",\n    \"name\": \"华大九天\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"603201\",\n    \"name\": \"常润股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301195\",\n    \"name\": \"北路智控\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688371\",\n    \"name\": \"菲沃泰\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301333\",\n    \"name\": \"诺思格\",\n    \"tag\": \"医药\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301197\",\n    \"name\": \"工大科雅\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688205\",\n    \"name\": \"德科立\",\n    \"tag\": \"VR\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301132\",\n    \"name\": \"满坤科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301192\",\n    \"name\": \"泰祥股份\",\n    \"tag\": \"汽车\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301336\",\n    \"name\": \"趣睡科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"836547\",\n    \"name\": \"无锡晶海\",\n    \"tag\": \"医药\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"873570\",\n    \"name\": \"坤博精工\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"834950\",\n    \"name\": \"迅安科技\",\n    \"tag\": \"消费电子\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"836699\",\n    \"name\": \"海达尔\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"870726\",\n    \"name\": \"鸿智科技\",\n    \"tag\": \"大消费\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"872953\",\n    \"name\": \"国子软件\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"873726\",\n    \"name\": \"卓兆点胶\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"873693\",\n    \"name\": \"阿为特\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"873679\",\n    \"name\": \"前进科技\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"873703\",\n    \"name\": \"广厦环能\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"871263\",\n    \"name\": \"莱赛激光\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"873806\",\n    \"name\": \"云星宇\",\n    \"tag\": \"AI\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"北交所\"\n  },\n  {\n    \"code\": \"603082\",\n    \"name\": \"C北自\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"301502\",\n    \"name\": \"N华阳智\",\n    \"tag\": \"智能机器\",\n    \"reason\": \"\",\n    \"hidden_tag\": \"次新股\"\n  },\n  {\n    \"code\": \"688709\",\n    \"name\": \"C华微\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"688584\",\n    \"name\": \"C合晶\",\n    \"tag\": \"半导体\",\n    \"reason\": \"\"\n  },\n  {\n    \"code\": \"301589\",\n    \"name\": \"C诺瓦\",\n    \"tag\": \"AI\",\n    \"reason\": \"\"\n  }\n]"
  },
  {
    "path": "examples/tag_utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport json\nimport os\nfrom collections import Counter\n\nfrom zvt.api.utils import china_stock_code_to_id, get_china_exchange\nfrom zvt.domain import BlockStock, Block, Stock, LimitUpInfo\n\n\ndef get_limit_up_reasons(entity_id):\n    info = LimitUpInfo.query_data(\n        entity_id=entity_id, order=LimitUpInfo.timestamp.desc(), limit=1, return_type=\"domain\"\n    )\n\n    topics = []\n    if info and info[0].reason:\n        topics = topics + info[0].reason.split(\"+\")\n    return topics\n\n\ndef get_concept(code):\n    with open(os.path.join(os.path.dirname(__file__), \"concept.json\")) as f:\n        concept_map = json.load(f)\n        concepts = [item for sublist in concept_map.values() for item in sublist]\n        df = BlockStock.query_data(filters=[BlockStock.stock_code == code, BlockStock.name.in_(concepts)])\n        return df[\"name\"].tolist()\n\n\ndef industry_to_tag(industry):\n    if industry in [\"风电设备\", \"电池\", \"光伏设备\", \"能源金属\", \"电源设备\"]:\n        return \"赛道\"\n    if industry in [\"半导体\", \"电子化学品\"]:\n        return \"半导体\"\n    if industry in [\"医疗服务\", \"中药\", \"化学制药\", \"生物制品\", \"医药商业\"]:\n        return \"医药\"\n    if industry in [\"医疗器械\"]:\n        return \"医疗器械\"\n    if industry in [\"教育\"]:\n        return \"教育\"\n    if industry in [\"贸易行业\", \"家用轻工\", \"造纸印刷\", \"酿酒行业\", \"珠宝首饰\", \"美容护理\", \"食品饮料\", \"旅游酒店\", \"商业百货\", \"纺织服装\", \"家电行业\"]:\n        return \"大消费\"\n    if industry in [\"小金属\", \"贵金属\", \"有色金属\", \"煤炭行业\"]:\n        return \"资源\"\n    if industry in [\"消费电子\", \"电子元件\"]:\n        return \"消费电子\"\n    if industry in [\"汽车零部件\", \"汽车服务\", \"汽车整车\"]:\n        return \"汽车\"\n    if industry in [\"电机\", \"通用设备\", \"专用设备\", \"仪器仪表\"]:\n        return \"智能机器\"\n    if industry in [\"电网设备\", \"电力行业\"]:\n        return \"电力\"\n    if industry in [\"光学光电子\"]:\n        return \"VR\"\n    if industry in [\"房地产开发\", \"房地产服务\", \"工程建设\", \"水泥建材\", \"装修装饰\", \"装修建材\", \"工程咨询服务\", \"钢铁行业\", \"工程机械\"]:\n        return \"房地产\"\n    if industry in [\"非金属材料\", \"包装材料\", \"化学制品\", \"化肥行业\", \"化学原料\", \"化纤行业\", \"塑料制品\", \"玻璃玻纤\", \"橡胶制品\"]:\n        return \"化工\"\n    if industry in [\"交运设备\", \"船舶制造\", \"航运港口\", \"公用事业\", \"燃气\", \"航空机场\", \"环保行业\", \"石油行业\", \"铁路公路\", \"采掘行业\"]:\n        return \"公用\"\n    if industry in [\"证券\", \"保险\", \"银行\", \"多元金融\"]:\n        return \"金融\"\n    if industry in [\"互联网服务\", \"软件开发\", \"计算机设备\", \"游戏\", \"通信服务\", \"通信设备\"]:\n        return \"AI\"\n    if industry in [\"文化传媒\"]:\n        return \"传媒\"\n    if industry in [\"农牧饲渔\", \"农药兽药\"]:\n        return \"农业\"\n    if industry in [\"物流行业\"]:\n        return \"统一大市场\"\n    if industry in [\"航天航空\", \"船舶制造\"]:\n        return \"军工\"\n    if industry in [\"专业服务\"]:\n        return \"专业服务\"\n\n\ndef build_default_tags(codes, provider=\"em\"):\n    df_block = Block.query_data(provider=provider, filters=[Block.category == \"industry\"])\n    industry_codes = df_block[\"code\"].tolist()\n    tags = []\n    for code in codes:\n        block_stocks = BlockStock.query_data(\n            provider=provider,\n            filters=[BlockStock.code.in_(industry_codes), BlockStock.stock_code == code],\n            return_type=\"domain\",\n        )\n        if block_stocks:\n            block_stock = block_stocks[0]\n            tags.append(\n                {\n                    \"code\": block_stock.stock_code,\n                    \"name\": block_stock.stock_name,\n                    \"tag\": industry_to_tag(block_stock.name),\n                    \"reason\": \"\",\n                }\n            )\n        else:\n            print(f\"no industry for {code}\")\n\n    return tags\n\n\ndef get_main_line_tags():\n    with open(os.path.join(os.path.dirname(__file__), \"main_line_tags.json\")) as f:\n        return json.load(f)\n\n\ndef get_main_line_hidden_tags():\n    with open(os.path.join(os.path.dirname(__file__), \"main_line_hidden_tags.json\")) as f:\n        return json.load(f)\n\n\ndef replace_tags(old_tag=\"仪器仪表\"):\n    with open(os.path.join(os.path.dirname(__file__), \"stock_tags.json\")) as f:\n        stock_tags = json.load(f)\n        for stock_tag in stock_tags:\n            if stock_tag[\"tag\"] == old_tag:\n                df = BlockStock.query_data(filters=[BlockStock.stock_code == stock_tag[\"code\"]])\n                blocks = df[\"name\"].tolist()\n                for block in blocks:\n                    tag = industry_to_tag(industry=block)\n                    if tag:\n                        stock_tag[\"tag\"] = tag\n                        break\n\n        with open(\"result.json\", \"w\") as json_file:\n            json.dump(stock_tags, json_file, indent=2, ensure_ascii=False)\n\n\ndef check_tags():\n    with open(os.path.join(os.path.dirname(__file__), \"stock_tags.json\")) as f:\n        stock_tags = json.load(f)\n        tags = set()\n        hidden_tags = set()\n        stocks = []\n        final_tags = []\n        for stock_tag in stock_tags:\n            stock_code = stock_tag[\"code\"]\n            if not stock_code.isdigit() or (len(stock_code) != 6):\n                print(stock_code)\n            tags.add(stock_tag[\"tag\"])\n            hidden_tags.add(stock_tag.get(\"hidden_tag\"))\n            if stock_code in stocks:\n                print(stock_tag)\n            else:\n                final_tags.append(stock_tag)\n            stocks.append(stock_code)\n\n        # with open(\"result.json\", \"w\") as json_file:\n        #     json.dump(final_tags, json_file, indent=2, ensure_ascii=False)\n\n        print(tags)\n        print(hidden_tags)\n        print(len(stocks))\n        count = Counter(stocks)\n        duplicates = [item for item, frequency in count.items() if frequency > 1]\n        print(duplicates)\n\n\ndef get_hidden_code(code):\n    exchange = get_china_exchange(code=code)\n    if exchange == \"bj\":\n        return \"北交所\"\n\n\ndef get_core_tag(codes):\n    # 从stock_tags.json读取\n    other_codes = []\n    with open(os.path.join(os.path.dirname(__file__), \"stock_tags.json\")) as f:\n        stock_tags = json.load(f)\n        code_tag_hidden_tag_list = [\n            (\n                stock_tag[\"code\"],\n                stock_tag[\"tag\"],\n                stock_tag.get(\"hidden_tag\") if stock_tag.get(\"hidden_tag\") else get_hidden_code(stock_tag[\"code\"]),\n            )\n            for stock_tag in stock_tags\n            if stock_tag[\"code\"] in codes\n        ]\n        other_codes = [code for code in codes if code not in [item[0] for item in code_tag_hidden_tag_list]]\n    for code in other_codes:\n        tags = get_limit_up_reasons(entity_id=china_stock_code_to_id(code=code))\n        if tags:\n            code_tag_hidden_tag_list.append((code, tags[0], None))\n        else:\n            code_tag_hidden_tag_list.append((code, \"未知\", get_hidden_code(code)))\n\n    return code_tag_hidden_tag_list\n\n\ndef group_stocks_by_tag(entities, hidden_tags=None):\n    code_entities_map = {entity.code: entity for entity in entities}\n\n    tag_stocks = {}\n    code_tag_hidden_tag_list = get_core_tag([entity.code for entity in entities])\n    for code, tag, hidden_tag in code_tag_hidden_tag_list:\n        if hidden_tags and (hidden_tag in hidden_tags):\n            tag_stocks.setdefault(hidden_tag, [])\n            tag_stocks.get(hidden_tag).append(code_entities_map.get(code))\n        if (tag != hidden_tag) or (not hidden_tags):\n            tag_stocks.setdefault(tag, [])\n            tag_stocks.get(tag).append(code_entities_map.get(code))\n\n    sorted_entities = sorted(tag_stocks.items(), key=lambda x: len(x[1]), reverse=True)\n\n    return sorted_entities\n\n\ndef build_stock_tags_by_block(block_name, tag, hidden_tag):\n    block_stocks = BlockStock.query_data(filters=[BlockStock.name == block_name], return_type=\"domain\")\n    datas = [\n        {\n            \"code\": block_stock.stock_code,\n            \"name\": block_stock.stock_name,\n            \"tag\": tag,\n            \"hidden_tag\": hidden_tag,\n            \"reason\": \"\",\n        }\n        for block_stock in block_stocks\n    ]\n\n    # Specify the file path where you want to save the JSON data\n    file_path = f\"{tag}.json\"\n\n    # Write JSON data to the file\n    with open(file_path, \"w\") as json_file:\n        json.dump(datas, json_file, indent=2, ensure_ascii=False)\n\n\ndef merge_tags(current_tags, added_tags, force_update=False):\n    code_tags_map = {item[\"code\"]: item for item in current_tags}\n\n    # Merge\n    for added_tag in added_tags:\n        code_from_added = added_tag[\"code\"]\n        if code_from_added not in code_tags_map:\n            current_tags.append(added_tag)\n        else:\n            # update hidden_tag from added_tag\n            if force_update or (not code_tags_map[code_from_added].get(\"hidden_tag\")):\n                code_tags_map[code_from_added][\"hidden_tag\"] = added_tag[\"hidden_tag\"]\n    return current_tags\n\n\ndef merge_tags_file(current_tags_file, added_tags_file, result_file, force_update=False):\n    # current_tags_file读取\n    with open(os.path.join(os.path.dirname(__file__), current_tags_file)) as f:\n        current_tags = json.load(f)\n    # added_tags_file读取\n    with open(os.path.join(os.path.dirname(__file__), added_tags_file)) as f:\n        added_tags = json.load(f)\n\n    current_tags = merge_tags(current_tags, added_tags, force_update)\n    with open(result_file, \"w\") as json_file:\n        json.dump(current_tags, json_file, indent=2, ensure_ascii=False)\n\n\ndef complete_tags():\n    with open(os.path.join(os.path.dirname(__file__), \"stock_tags.json\")) as f:\n        stock_tags = json.load(f)\n        current_codes = [stock_tag[\"code\"] for stock_tag in stock_tags]\n        df = Stock.query_data(\n            provider=\"em\",\n            filters=[\n                Stock.code.not_in(current_codes),\n                Stock.name.not_like(\"%退%\"),\n            ],\n        )\n\n        codes = df[\"code\"].tolist()\n        print(len(codes))\n        added_tags = build_default_tags(codes=codes, provider=\"em\")\n\n        with open(\"result.json\", \"w\") as json_file:\n            json.dump(stock_tags + added_tags, json_file, indent=2, ensure_ascii=False)\n\n\ndef refresh_hidden_tags():\n    with open(os.path.join(os.path.dirname(__file__), \"stock_tags.json\")) as f:\n        stock_tags = json.load(f)\n        for stock_tag in stock_tags:\n            if not stock_tag.get(\"hidden_tag\"):\n                exchange = get_china_exchange(code=stock_tag[\"code\"])\n                if exchange == \"bj\":\n                    stock_tag[\"hidden_tag\"] = \"北交所\"\n\n        with open(\"result.json\", \"w\") as json_file:\n            json.dump(stock_tags, json_file, indent=2, ensure_ascii=False)\n\n\nif __name__ == \"__main__\":\n    # build_stock_tags(block_name=\"化工原料\", tag=\"化工\", hidden_tag=None)\n    # merge_tags(tags_file=\"stock_tags.json\", hidden_tags_file=\"化工.json\", result_file=\"result.json\", force_update=False)\n    # replace_tags(old_tag=\"仪器仪表\")\n    # check_tags()\n    # complete_tags()\n    # refresh_hidden_tags()\n    print(get_concept(code=\"688787\"))\n"
  },
  {
    "path": "examples/trader/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "examples/trader/dragon_and_tiger_trader.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List, Union\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.factor import Factor, Transformer, Accumulator\nfrom zvt.domain import Stock, DragonAndTiger\nfrom zvt.trader import StockTrader\n\n\nclass DragonTigerFactor(Factor):\n    def __init__(\n        self,\n        provider: str = \"em\",\n        entity_provider: str = \"em\",\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = [DragonAndTiger.dep1 == \"机构专用\"],\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        transformer: Transformer = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n    ) -> None:\n        super().__init__(\n            DragonAndTiger,\n            Stock,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n        )\n\n    def compute_result(self):\n        self.factor_df[\"filter_result\"] = True\n        super().compute_result()\n\n\nclass MyTrader(StockTrader):\n    def init_factors(\n        self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, adjust_type=None\n    ):\n        return [\n            DragonTigerFactor(\n                entity_ids=entity_ids,\n                exchanges=exchanges,\n                codes=codes,\n                start_timestamp=start_timestamp,\n                end_timestamp=end_timestamp,\n            )\n        ]\n\n\nif __name__ == \"__main__\":\n    trader = MyTrader(start_timestamp=\"2020-01-01\", end_timestamp=\"2022-05-01\")\n    trader.run()\n"
  },
  {
    "path": "examples/trader/follow_ii_trader.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.api.utils import get_recent_report_date\nfrom zvt.contract import ActorType, AdjustType\nfrom zvt.domain import StockActorSummary, Stock1dKdata, Stock\nfrom zvt.trader import StockTrader\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import is_same_date, to_pd_timestamp\n\n\nclass FollowIITrader(StockTrader):\n    finish_date = None\n\n    def on_time(self, timestamp: pd.Timestamp):\n        recent_report_date = to_pd_timestamp(get_recent_report_date(timestamp))\n        if self.finish_date and is_same_date(recent_report_date, self.finish_date):\n            return\n        filters = [\n            StockActorSummary.actor_type == ActorType.raised_fund.value,\n            StockActorSummary.report_date == recent_report_date,\n        ]\n\n        if self.entity_ids:\n            filters = filters + [StockActorSummary.entity_id.in_(self.entity_ids)]\n\n        df = StockActorSummary.query_data(filters=filters)\n\n        if pd_is_not_null(df):\n            self.logger.info(f\"{df}\")\n            self.finish_date = recent_report_date\n\n        long_df = df[df[\"change_ratio\"] > 0.05]\n        short_df = df[df[\"change_ratio\"] < -0.5]\n        try:\n            long_targets = set(long_df[\"entity_id\"].to_list())\n            short_targets = set(short_df[\"entity_id\"].to_list())\n            if long_targets:\n                self.buy(timestamp=timestamp, entity_ids=long_targets)\n            if short_targets:\n                self.sell(timestamp=timestamp, entity_ids=short_targets)\n        except Exception as e:\n            self.logger.error(e)\n\n\nif __name__ == \"__main__\":\n    code = \"600519\"\n    Stock.record_data(provider=\"em\")\n    Stock1dKdata.record_data(code=code, provider=\"em\")\n    StockActorSummary.record_data(code=code, provider=\"em\")\n    FollowIITrader(\n        start_timestamp=\"2002-01-01\",\n        end_timestamp=\"2021-01-01\",\n        codes=[code],\n        provider=\"em\",\n        adjust_type=AdjustType.qfq,\n        profit_threshold=None,\n    ).run()\n"
  },
  {
    "path": "examples/trader/keep_run_trader.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom zvt.api.stats import get_top_fund_holding_stocks\nfrom zvt.api.stats import get_top_volume_entities\nfrom zvt.contract import IntervalLevel\nfrom zvt.factors.macd.macd_factor import BullFactor\nfrom zvt.trader import StockTrader\nfrom zvt.trader.trader_info_api import clear_trader\nfrom zvt.utils.time_utils import split_time_interval, date_time_by_interval\n\nlogger = logging.getLogger(__name__)\n\n\nclass MultipleLevelTrader(StockTrader):\n    def init_factors(\n        self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, adjust_type=None\n    ):\n        start_timestamp = date_time_by_interval(start_timestamp, -50)\n\n        return [\n            BullFactor(\n                entity_ids=entity_ids,\n                entity_schema=entity_schema,\n                exchanges=exchanges,\n                codes=codes,\n                start_timestamp=date_time_by_interval(start_timestamp, -200),\n                end_timestamp=end_timestamp,\n                provider=\"joinquant\",\n                level=IntervalLevel.LEVEL_1WEEK,\n            ),\n            GoldCrossFactor(\n                entity_ids=entity_ids,\n                entity_schema=entity_schema,\n                exchanges=exchanges,\n                codes=codes,\n                start_timestamp=start_timestamp,\n                end_timestamp=end_timestamp,\n                provider=\"joinquant\",\n                level=IntervalLevel.LEVEL_1DAY,\n            ),\n        ]\n\n\nif __name__ == \"__main__\":\n    start = \"2019-01-01\"\n    end = \"2021-01-01\"\n    trader_name = \"keep_run_trader\"\n    clear_trader(trader_name=trader_name)\n    for time_interval in split_time_interval(start=start, end=end, interval=40):\n        start_timestamp = time_interval[0]\n        end_timestamp = time_interval[-1]\n        # 成交量\n        vol_df = get_top_volume_entities(\n            entity_type=\"stock\",\n            start_timestamp=date_time_by_interval(start_timestamp, -50),\n            end_timestamp=start_timestamp,\n            pct=0.3,\n        )\n        # 机构重仓\n        ii_df = get_top_fund_holding_stocks(timestamp=start_timestamp, pct=0.3, by=\"trading\")\n\n        current_entity_pool = list(set(vol_df.index.tolist()) & set(ii_df.index.tolist()))\n\n        logger.info(f\"current_entity_pool({len(current_entity_pool)}):{current_entity_pool}\")\n\n        trader = MultipleLevelTrader(\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            entity_ids=current_entity_pool,\n            trader_name=trader_name,\n            keep_history=True,\n            draw_result=False,\n            rich_mode=False,\n        )\n        trader.run()\n"
  },
  {
    "path": "examples/trader/ma_trader.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.contract import IntervalLevel\nfrom zvt.factors.ma.ma_factor import CrossMaFactor\nfrom zvt.factors.macd.macd_factor import BullFactor\n\nfrom zvt.trader.trader import StockTrader\n\n\nclass MyMaTrader(StockTrader):\n    def init_factors(\n        self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, adjust_type=None\n    ):\n        return [\n            CrossMaFactor(\n                entity_ids=entity_ids,\n                entity_schema=entity_schema,\n                exchanges=exchanges,\n                codes=codes,\n                start_timestamp=start_timestamp,\n                end_timestamp=end_timestamp,\n                windows=[5, 10],\n                need_persist=False,\n            )\n        ]\n\n\nclass MyBullTrader(StockTrader):\n    def init_factors(\n        self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, adjust_type=None\n    ):\n        return [\n            BullFactor(\n                entity_ids=entity_ids,\n                entity_schema=entity_schema,\n                exchanges=exchanges,\n                codes=codes,\n                start_timestamp=start_timestamp,\n                end_timestamp=end_timestamp,\n                adjust_type=\"hfq\",\n            )\n        ]\n\n\nif __name__ == \"__main__\":\n    # single stock with cross ma factor\n    MyBullTrader(\n        codes=[\"000338\"],\n        level=IntervalLevel.LEVEL_1DAY,\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2019-06-30\",\n        trader_name=\"000338_ma_trader\",\n    ).run()\n\n    # single stock with bull factor\n    # MyBullTrader(codes=['000338'], level=IntervalLevel.LEVEL_1DAY, start_timestamp='2018-01-01',\n    #              end_timestamp='2019-06-30', trader_name='000338_bull_trader').run()\n\n    #  multiple stocks with cross ma factor\n    # MyMaTrader(codes=SAMPLE_STOCK_CODES, level=IntervalLevel.LEVEL_1DAY, start_timestamp='2018-01-01',\n    #            end_timestamp='2019-06-30', trader_name='sample_stocks_ma_trader').run()\n\n    # multiple stocks with bull factor\n    # MyBullTrader(codes=SAMPLE_STOCK_CODES, level=IntervalLevel.LEVEL_1DAY, start_timestamp='2018-01-01',\n    #              end_timestamp='2019-06-30', trader_name='sample_stocks_bull_trader').run()\n"
  },
  {
    "path": "examples/trader/macd_day_trader.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List, Tuple\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.factor import Factor\nfrom zvt.factors.macd.macd_factor import GoldCrossFactor\nfrom zvt.trader import TradingSignal\nfrom zvt.trader.trader import StockTrader\n\n# 依赖数据\n# data_schema: Stock1dHfqKdata\n# provider: joinquant\nfrom zvt.utils.time_utils import date_time_by_interval\n\n\nclass MacdDayTrader(StockTrader):\n    def init_factors(\n        self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, adjust_type=None\n    ):\n        # 日线策略\n        start_timestamp = date_time_by_interval(start_timestamp, -50)\n        return [\n            GoldCrossFactor(\n                entity_ids=entity_ids,\n                entity_schema=entity_schema,\n                exchanges=exchanges,\n                codes=codes,\n                start_timestamp=start_timestamp,\n                end_timestamp=end_timestamp,\n                provider=\"joinquant\",\n                level=IntervalLevel.LEVEL_1DAY,\n            )\n        ]\n\n    def on_profit_control(self):\n        # 覆盖该函数做止盈 止损\n        return super().on_profit_control()\n\n    def on_time(self, timestamp: pd.Timestamp):\n        # 对selectors选出的标的做进一步处理，或者不使用selector完全自己根据时间和数据生成交易信号\n        super().on_time(timestamp)\n\n    def on_trading_signals(self, trading_signals: List[TradingSignal]):\n        # 批量处理交易信号，比如连接交易接口，发邮件，微信推送等\n        super().on_trading_signals(trading_signals)\n\n    def on_trading_open(self, timestamp):\n        # 开盘自定义逻辑\n        super().on_trading_open(timestamp)\n\n    def on_trading_close(self, timestamp):\n        # 收盘自定义逻辑\n        super().on_trading_close(timestamp)\n\n    def on_trading_finish(self, timestamp):\n        # 策略退出自定义逻辑\n        super().on_trading_finish(timestamp)\n\n    def on_trading_error(self, timestamp, error):\n        # 出错处理\n        super().on_trading_error(timestamp, error)\n\n    def long_position_control(self):\n        # 多头仓位管理\n        return super().long_position_control()\n\n    def short_position_control(self):\n        # 空头仓位管理\n        return super().short_position_control()\n\n    def on_factor_targets_filtered(\n        self, timestamp, level, factor: Factor, long_targets: List[str], short_targets: List[str]\n    ) -> Tuple[List[str], List[str]]:\n        # 过滤某级别选出的 标的\n        return super().on_factor_targets_filtered(timestamp, level, factor, long_targets, short_targets)\n\n\nif __name__ == \"__main__\":\n    trader = MacdDayTrader(start_timestamp=\"2019-01-01\", end_timestamp=\"2020-01-01\")\n    trader.run()\n    # f = VolFactor(start_timestamp='2020-01-01', end_timestamp='2020-04-01')\n    # print(f.result_df)\n"
  },
  {
    "path": "examples/trader/macd_week_and_day_trader.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List, Tuple\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.factors.macd.macd_factor import GoldCrossFactor\nfrom zvt.trader.trader import StockTrader\n\n\n# 依赖数据\n# dataschema: Stock1dHfqKdata Stock1wkHfqKdata\n# provider: joinquant\nclass MultipleLevelTrader(StockTrader):\n    def init_factors(\n        self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, adjust_type=None\n    ):\n        # 同时使用周线和日线策略\n        return [\n            GoldCrossFactor(\n                entity_ids=entity_ids,\n                entity_schema=entity_schema,\n                exchanges=exchanges,\n                codes=codes,\n                start_timestamp=start_timestamp,\n                end_timestamp=end_timestamp,\n                provider=\"joinquant\",\n                level=IntervalLevel.LEVEL_1WEEK,\n            ),\n            GoldCrossFactor(\n                entity_ids=entity_ids,\n                entity_schema=entity_schema,\n                exchanges=exchanges,\n                codes=codes,\n                start_timestamp=start_timestamp,\n                end_timestamp=end_timestamp,\n                provider=\"joinquant\",\n                level=IntervalLevel.LEVEL_1DAY,\n            ),\n        ]\n\n    def on_targets_selected_from_levels(self, timestamp) -> Tuple[List[str], List[str]]:\n        # 过滤多级别做 多/空 的标的\n        return super().on_targets_selected_from_levels(timestamp)\n\n\nif __name__ == \"__main__\":\n    trader = MultipleLevelTrader(start_timestamp=\"2019-01-01\", end_timestamp=\"2020-01-01\")\n    trader.run()\n"
  },
  {
    "path": "examples/utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport json\nimport logging\nimport os\n\nimport pandas as pd\n\nfrom zvt.domain import StockNews, Stock, LimitUpInfo\nfrom zvt.utils.time_utils import date_time_by_interval, now_pd_timestamp\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_hot_words_config():\n    with open(os.path.join(os.path.dirname(__file__), \"hot.json\")) as f:\n        return json.load(f)\n\n\ndef count_hot_words(text: str):\n    text = text.upper()\n    hot_words_config = get_hot_words_config()\n    word_stats = {}\n    topic_stats = {}\n    for topic in hot_words_config:\n        topic_count = 0\n        for word in hot_words_config[topic]:\n            word_stats[word] = text.lower().count(word)\n            topic_count = topic_count + word_stats[word]\n        topic_stats[topic] = topic_count\n    return topic_stats, word_stats\n\n\ndef hot_stats(data: pd.Series):\n    pass\n\n\ndef group_stocks_by_topic(\n    keyword=None, entities=None, hot_words_config=None, start_timestamp=None, days_ago=60, threshold=3\n):\n    \"\"\"\n\n    :param keyword:\n    :param entities:\n    :param hot_words_config: hot words config为二重结构，即 主题:[分支1，分支2,...]的形式\n    比如一个有效的item：{\"华为\":[\"华为\", \"mate pro\", \"星闪\", \"问界\"]}\n    :param start_timestamp:\n    :param days_ago:\n    :param threshold:\n    :return:\n    \"\"\"\n    if not start_timestamp:\n        start_timestamp = date_time_by_interval(now_pd_timestamp(), -days_ago)\n    stock_map = {}\n\n    entity_ids = None\n    if entities:\n        entity_ids = [entity.entity_id for entity in entities]\n    else:\n        entities = Stock.query_data(provider=\"em\", return_type=\"domain\")\n\n    for entity in entities:\n        stock_map[entity.entity_id] = {\"code\": entity.code, \"name\": entity.name}\n\n    filters = None\n    if keyword:\n        filters = [StockNews.news_title.contains(keyword)]\n    df = StockNews.query_data(start_timestamp=start_timestamp, entity_ids=entity_ids, filters=filters)\n    df = df.groupby(\"entity_id\")[\"news_title\"].apply(\",\".join).reset_index()\n\n    if not hot_words_config:\n        hot_words_config = get_hot_words_config()\n\n    hot_stocks_map = {}\n    topic_count = {}\n    word_count = {}\n    for _, row in df[[\"entity_id\", \"news_title\"]].iterrows():\n        entity_id = row[\"entity_id\"]\n        text = row[\"news_title\"]\n\n        is_hot = False\n        for topic in hot_words_config:\n            topic_count.setdefault(topic, 0)\n            for words in hot_words_config[topic]:\n                hot_stocks_map.setdefault(words, [])\n                word_count.setdefault(words, 0)\n                count = 0\n                for word in words.split(\",\"):\n                    count = text.lower().count(word) + count\n                if count >= threshold:\n                    word_count[words] = word_count[words] + 1\n                    topic_count[topic] = topic_count[topic] + 1\n                    hot_stocks_map[words].append(\n                        (f\"{stock_map[entity_id]['code']}({stock_map[entity_id]['name']})\", count)\n                    )\n                    is_hot = True\n        if not is_hot:\n            hot_stocks_map.setdefault(\"其他\", [])\n            hot_stocks_map[\"其他\"].append((f\"{stock_map[entity_id]['code']}({stock_map[entity_id]['name']})\", 0))\n\n    sorted_topics = sorted(topic_count.items(), key=lambda item: item[1], reverse=True)\n    sorted_words = sorted(word_count.items(), key=lambda item: item[1], reverse=True)\n\n    result = []\n    for topic, count in sorted_topics:\n        topic_words = hot_words_config[topic]\n        topic_words_stocks = [\n            (f\"{words}({count})\", sorted(hot_stocks_map[words], key=lambda item: item[1], reverse=True))\n            for (words, count) in sorted_words\n            if words in topic_words\n        ]\n        result.append((f\"{topic}({count})\", topic_words_stocks))\n\n    result.append((\"其他\", [(\"其他\", hot_stocks_map.get(\"其他\", \"\"))]))\n\n    return result\n\n\ndef msg_group_stocks_by_topic(\n    keyword=None, entities=None, hot_words_config=None, start_timestamp=None, days_ago=60, threshold=3\n):\n    group_info = group_stocks_by_topic(\n        keyword=keyword,\n        entities=entities,\n        hot_words_config=hot_words_config,\n        start_timestamp=start_timestamp,\n        days_ago=days_ago,\n        threshold=threshold,\n    )\n    msg = \"\"\n    for group in group_info:\n        topic = group[0]\n        msg = msg + f\"^^^^^^ {topic} ^^^^^^\\n\"\n        for topic_word, stocks_count in group[1]:\n            msg = msg + f\"{topic_word}\\n\"\n            stocks = [f\"{stock_count[0]} {stock_count[1]}\" for stock_count in stocks_count]\n            msg = msg + \"\\n\".join(stocks) + \"\\n\"\n    return msg\n\n\ndef get_hot_topics(start_timestamp=None, days_ago=20, limit=15):\n    if not start_timestamp:\n        start_timestamp = date_time_by_interval(now_pd_timestamp(), -days_ago)\n    df = LimitUpInfo.query_data(start_timestamp=start_timestamp, columns=[\"reason\"])\n    df[\"reason\"] = df[\"reason\"].str.split(\"+\")\n    result = df[\"reason\"].tolist()\n    result = [item for sublist in result for item in sublist]\n    result = pd.Series(result)\n    result = result.value_counts()\n    result = result[:limit].to_dict()\n    return result\n\n\nif __name__ == \"__main__\":\n    # ids = get_top_performance_entities_by_periods(entity_provider=\"em\", data_provider=\"em\")\n    #\n    # entities = get_entities(provider=\"em\", entity_type=\"stock\", entity_ids=ids, return_type=\"domain\")\n    #\n    # print(msg_group_stocks_by_topic(entities=entities, threshold=1))\n    get_hot_topics(days_ago=10)\n"
  },
  {
    "path": "examples/z.sh",
    "content": "nohup python examples/data_runner/kdata_runner.py >/dev/null 2>&1 &\nnohup python examples/reports/report_tops.py >/dev/null 2>&1 &\nnohup python examples/reports/report_vol_up.py >/dev/null 2>&1 &"
  },
  {
    "path": "init_env.sh",
    "content": "#!/bin/bash -e\n\nplatform='unknown'\nunamestr=`uname`\nif [[ \"$unamestr\" == 'Linux' ]]; then\n   platform='linux'\nelif [[ \"$unamestr\" == 'Darwin' ]]; then\n   platform='mac'\nfi\n\ninstall_cmd='sudo apt-get install'\nif [[ \"$platform\" == 'mac' ]]; then\n    install_cmd='brew install'\nfi\n\nBASEDIR=`dirname $0`\n\nif ! which python > /dev/null; then\n   echo -e \"Python3 not found! Install? (y/n) \\c\"\n   read\n   if [ \"$REPLY\" = \"y\" ]; then\n      $install_cmd install python3\n   fi\nfi\n\npip_opt=''\n#pip_opt='-i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn'\n\nif ! which virtualenv > /dev/null; then\n   echo -e \"virtualenv not found! Install? (y/n) \\c\"\n   read\n   if [ \"$REPLY\" = \"y\" ]; then\n      pip3 install virtualenv $pip_opt\n   fi\nfi\n\nif [ ! -d \"$BASEDIR/ve\" ]; then\n    virtualenv -p python3 $BASEDIR/ve --no-download\n    echo \"Virtualenv created.\"\nfi\n\nsource $BASEDIR/ve/bin/activate\ncd $BASEDIR\nexport PYTHONPATH=$PYTHONPATH:./src\n\nif [ ! -f \"$BASEDIR/ve/updated\" -o $BASEDIR/requirements.txt -nt $BASEDIR/ve/updated ]; then\n    pip install -r $BASEDIR/requirements.txt $pip_opt\n    touch $BASEDIR/ve/updated\n    echo \"Requirements installed.\"\nfi\n\npip install ipython jupyterlab $pip_opt\n\necho \"env ok\""
  },
  {
    "path": "pyproject.toml",
    "content": "[tool.black]\nline-length = 120"
  },
  {
    "path": "requirements/dev.txt",
    "content": "# dev tools\npytest == 6.2.0\npre-commit == 2.15.0\nrst2pdf\n"
  },
  {
    "path": "requirements/docs.txt",
    "content": "sphinx==8.1.3\nsphinx_autodoc_typehints==3.0.1\nrst2pdf==0.103.1\nipython==8.29.0\nsphinx_rtd_theme==3.0.2\nreadthedocs-sphinx-search==0.3.2"
  },
  {
    "path": "requirements.txt",
    "content": "requests==2.32.0\nSQLAlchemy==2.0.36\npandas==2.2.3\npydantic==2.6.4\narrow==1.3.0\nopenpyxl==3.1.1\ndemjson3==3.0.6\nplotly==5.13.0\ndash==2.18.2\njqdatapy==0.1.8\ndash-bootstrap-components==1.3.1\ndash_daq==0.5.0\nscikit-learn==1.5.2\nfastapi==0.110.0\nfastapi-pagination==0.12.23\napscheduler==3.10.4\neastmoneypy==0.2.0\norjson==3.10.3\nnumpy==2.1.3"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\n\n# To use a consistent encoding\nfrom codecs import open\nfrom os import path\n\n# Always prefer setuptools over distutils\nfrom setuptools import setup, find_packages\n\nhere = path.abspath(path.dirname(__file__))\n\n# Get the long description from the README file\nwith open(path.join(here, \"README.md\"), encoding=\"utf-8\") as f:\n    long_description = f.read()\n\nsetup(\n    name=\"zvt\",\n    version=\"0.13.5\",\n    description=\"unified,modular quant framework for human beings \",\n    long_description=long_description,\n    url=\"https://github.com/zvtvz/zvt\",\n    author=\"foolcage\",\n    author_email=\"5533061@qq.com\",\n    classifiers=[  # Optional\n        \"Development Status :: 5 - Production/Stable\",\n        \"Intended Audience :: Developers\",\n        \"Intended Audience :: Customer Service\",\n        \"Intended Audience :: Education\",\n        \"Intended Audience :: Financial and Insurance Industry\",\n        \"Topic :: Software Development :: Build Tools\",\n        \"Topic :: Office/Business :: Financial :: Investment\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3.9\",\n        \"Programming Language :: Python :: 3.10\",\n        \"Programming Language :: Python :: 3.11\",\n        \"Programming Language :: Python :: 3.12\",\n    ],\n    keywords=\"quant stock finance fintech big-data zvt technical-analysis trading-platform pandas fundamental-analysis\",\n    package_dir={\"\": \"src\"},\n    packages=find_packages(where=\"src\"),\n    python_requires=\">=3.9, <4\",\n    include_package_data=True,\n    install_requires=[\n        \"requests==2.32.0\",\n        \"SQLAlchemy==2.0.36\",\n        \"pandas==2.2.3\",\n        \"pydantic==2.6.4\",\n        \"arrow==1.3.0\",\n        \"openpyxl==3.1.1\",\n        \"demjson3==3.0.6\",\n        \"plotly==5.13.0\",\n        \"dash==2.18.2\",\n        \"jqdatapy==0.1.8\",\n        \"dash-bootstrap-components==1.3.1\",\n        \"dash_daq==0.5.0\",\n        \"scikit-learn==1.5.2\",\n        \"fastapi==0.110.0\",\n        \"fastapi-pagination==0.12.23\",\n        \"apscheduler==3.10.4\",\n        \"eastmoneypy==0.2.0\",\n        \"orjson==3.10.3\",\n        \"numpy==2.1.3\",\n    ],\n    project_urls={  # Optional\n        \"Bug Reports\": \"https://github.com/zvtvz/zvt/issues\",\n        \"Funding\": \"https://www.foolcage.com/zvt\",\n        \"Say Thanks!\": \"https://saythanks.io/to/foolcage\",\n        \"Source\": \"https://github.com/zvtvz/zvt\",\n    },\n    long_description_content_type=\"text/markdown\",\n    entry_points={\n        \"console_scripts\": [\n            \"zvt = zvt.main:main\",\n            \"zvt_server = zvt.zvt_server:main\",\n            \"zvt_export = zvt.plugin:export\",\n        ],\n    },\n    license_file=\"LICENSE\",\n)\n"
  },
  {
    "path": "sql/change_indices.sql",
    "content": "-- 由于创建的table有不少使用了enum,导致有类似的约束\n-- \tconstraint provider\n-- \t\tcheck (provider IN ('eastmoney', 'sina', 'netease'))\n-- 当需要在原来的数据文件上做扩展时,可以去掉约束并做数据迁移\ncreate table indices1\n(\n  id          VARCHAR(128) not null,\n  provider    VARCHAR(9)   not null,\n  timestamp   DATETIME,\n  exchange    VARCHAR(32),\n  type        VARCHAR(5),\n  code        VARCHAR(32),\n  name        VARCHAR(32),\n  is_delisted BOOLEAN,\n  category    VARCHAR(8),\n  primary key (id, provider),\n  check (is_delisted IN (0, 1))\n);\n\nINSERT INTO indices1\nSELECT *\nFROM index;\n\n\nDROP TABLE index;\nALTER TABLE indices1\n  RENAME TO indices;\n\nupdate income_statement set report_period='season3' where report_period='seanson3'\nupdate finance_factor set report_period='season3' where report_period='seanson3'\nupdate balance_sheet set report_period='season3' where report_period='seanson3'\nupdate cash_flow_statement set report_period='season3' where report_period='seanson3'\n"
  },
  {
    "path": "sql/change_stock_index.sql",
    "content": "alter table index_stock\n    add report_period VARCHAR(32);\nalter table index_stock\n\tadd report_date DATETIME;\nalter table index_stock\n\tadd proportion FLOAT;\nalter table index_stock\n\tadd shares FLOAT;\nalter table index_stock\n\tadd market_cap FLOAT;"
  },
  {
    "path": "sql/reduce_size.sql",
    "content": "-- k线数据去除索引，方便传输原始数据，重跑zvt会重建\n-- 再压缩一下，大小为原来的1/10\ndrop index stock_1d_hfq_kdata_entity_id_index;\ndrop index stock_1d_hfq_kdata_code_index;\ndrop index stock_1d_hfq_kdata_timestamp_index;\nVACUUM;"
  },
  {
    "path": "sql/samples.sql",
    "content": "select * from stock_valuation where CAST(strftime('%s', timestamp)  AS  integer) ==CAST(strftime('%s', '2009-12-14')  AS  integer) ;\n"
  },
  {
    "path": "src/zvt/__init__.py",
    "content": "# -*- coding: utf-8 -*-\nimport importlib\nimport json\nimport logging\nimport os\nimport pkgutil\nimport pprint\nimport shutil\nfrom logging.handlers import RotatingFileHandler\nfrom typing import List\n\nimport pandas as pd\nimport pkg_resources\nfrom pkg_resources import get_distribution, DistributionNotFound\n\nfrom zvt.consts import DATA_SAMPLE_ZIP_PATH, ZVT_TEST_HOME, ZVT_HOME, ZVT_TEST_DATA_PATH, ZVT_TEST_ZIP_DATA_PATH\n\ntry:\n    dist_name = __name__\n    __version__ = get_distribution(dist_name).version\nexcept DistributionNotFound:\n    __version__ = \"unknown\"\nfinally:\n    del get_distribution, DistributionNotFound\n\nlogger = logging.getLogger(__name__)\n\n\ndef init_log(file_name=\"zvt.log\", log_dir=None, simple_formatter=True):\n    if not log_dir:\n        log_dir = zvt_env[\"log_path\"]\n\n    root_logger = logging.getLogger()\n\n    # reset the handlers\n    root_logger.handlers = []\n\n    root_logger.setLevel(logging.INFO)\n\n    file_name = os.path.join(log_dir, file_name)\n\n    file_log_handler = RotatingFileHandler(file_name, maxBytes=524288000, backupCount=10)\n\n    file_log_handler.setLevel(logging.INFO)\n\n    console_log_handler = logging.StreamHandler()\n    console_log_handler.setLevel(logging.INFO)\n\n    # create formatter and add it to the handlers\n    if simple_formatter:\n        formatter = logging.Formatter(\"%(asctime)s  %(levelname)s  %(threadName)s  %(message)s\")\n    else:\n        formatter = logging.Formatter(\n            \"%(asctime)s  %(levelname)s  %(threadName)s  %(name)s:%(filename)s:%(lineno)s  %(funcName)s  %(message)s\"\n        )\n    file_log_handler.setFormatter(formatter)\n    console_log_handler.setFormatter(formatter)\n\n    # add the handlers to the logger\n    root_logger.addHandler(file_log_handler)\n    root_logger.addHandler(console_log_handler)\n\n\nos.environ.setdefault(\"SQLALCHEMY_WARN_20\", \"1\")\npd.set_option(\"expand_frame_repr\", False)\npd.set_option(\"mode.chained_assignment\", \"raise\")\npd.set_option(\"display.max_rows\", None)\npd.set_option(\"display.max_columns\", None)\n\nzvt_env = {}\n\n# load default config\nwith open(pkg_resources.resource_filename(\"zvt\", \"config.json\")) as f:\n    zvt_config = json.load(f)\n\n_plugins = {}\n\n\ndef init_env(zvt_home: str, **kwargs) -> dict:\n    \"\"\"\n    init env\n\n    :param zvt_home: home path for zvt\n    \"\"\"\n    data_path = os.path.join(zvt_home, \"data\")\n    resource_path = os.path.join(zvt_home, \"resources\")\n    tmp_path = os.path.join(zvt_home, \"tmp\")\n    if not os.path.exists(data_path):\n        os.makedirs(data_path)\n\n    if not os.path.exists(resource_path):\n        os.makedirs(resource_path)\n\n    if not os.path.exists(tmp_path):\n        os.makedirs(tmp_path)\n\n    zvt_env[\"zvt_home\"] = zvt_home\n    zvt_env[\"data_path\"] = data_path\n    zvt_env[\"resource_path\"] = resource_path\n    zvt_env[\"tmp_path\"] = tmp_path\n\n    # path for storing ui results\n    zvt_env[\"ui_path\"] = os.path.join(zvt_home, \"ui\")\n    if not os.path.exists(zvt_env[\"ui_path\"]):\n        os.makedirs(zvt_env[\"ui_path\"])\n\n    # path for storing logs\n    zvt_env[\"log_path\"] = os.path.join(zvt_home, \"logs\")\n    if not os.path.exists(zvt_env[\"log_path\"]):\n        os.makedirs(zvt_env[\"log_path\"])\n\n    init_log()\n\n    pprint.pprint(zvt_env)\n\n    init_resources(resource_path=resource_path)\n    # init config\n    init_config(current_config=zvt_config, **kwargs)\n    # init plugin\n    # init_plugins()\n\n    return zvt_env\n\n\ndef init_resources(resource_path, force_overwrite=True):\n    package_name = \"zvt\"\n    package_dir = pkg_resources.resource_filename(package_name, \"resources\")\n    from zvt.utils.file_utils import list_all_files\n\n    files: List[str] = list_all_files(package_dir, ext=None)\n    for source_file in files:\n        dst_file = os.path.join(resource_path, source_file[len(package_dir) + 1 :])\n        if force_overwrite or not os.path.exists(dst_file):\n            shutil.copyfile(source_file, dst_file)\n\n\ndef init_config(pkg_name: str = None, current_config: dict = None, **kwargs) -> dict:\n    \"\"\"\n    init config\n    \"\"\"\n\n    # create default config.json if not exist\n    if pkg_name:\n        config_file = f\"{pkg_name}_config.json\"\n    else:\n        pkg_name = \"zvt\"\n        config_file = \"config.json\"\n\n    logger.info(f\"init config for {pkg_name}, current_config:{current_config}\")\n\n    config_path = os.path.join(zvt_env[\"zvt_home\"], config_file)\n    if not os.path.exists(config_path):\n        try:\n            sample_config = pkg_resources.resource_filename(pkg_name, \"config.json\")\n            if os.path.exists(sample_config):\n                shutil.copyfile(sample_config, config_path)\n        except Exception as e:\n            logger.warning(f\"could not load config.json from package {pkg_name}\")\n\n    if os.path.exists(config_path):\n        with open(config_path) as f:\n            config_json = json.load(f)\n            for k in config_json:\n                current_config[k] = config_json[k]\n\n    # set and save the config\n    for k in kwargs:\n        current_config[k] = kwargs[k]\n        with open(config_path, \"w+\") as outfile:\n            json.dump(current_config, outfile)\n\n    pprint.pprint(current_config)\n    logger.info(f\"current_config:{current_config}\")\n\n    return current_config\n\n\ndef init_plugins():\n    for finder, name, ispkg in pkgutil.iter_modules():\n        if name.startswith(\"zvt_\"):\n            try:\n                _plugins[name] = importlib.import_module(name)\n            except Exception as e:\n                logger.warning(f\"failed to load plugin {name}\", e)\n    logger.info(f\"loaded plugins:{_plugins}\")\n\n\ndef old_db_to_provider_dir(data_path):\n    files = os.listdir(data_path)\n    for file in files:\n        if file.endswith(\".db\"):\n            # Split the file name to extract the provider\n            provider = file.split(\"_\")[0]\n\n            # Define the destination directory\n            destination_dir = os.path.join(data_path, provider)\n\n            # Create the destination directory if it doesn't exist\n            if not os.path.exists(destination_dir):\n                os.makedirs(destination_dir)\n\n            # Define the source and destination paths\n            source_path = os.path.join(data_path, file)\n            destination_path = os.path.join(destination_dir, file)\n\n            # Move the file to the destination directory\n            if not os.path.exists(destination_path):\n                shutil.move(source_path, destination_path)\n                logger.info(f\"Moved {file} to {destination_dir}\")\n\n\nif os.getenv(\"TESTING_ZVT\"):\n    init_env(zvt_home=ZVT_TEST_HOME)\n\n    # init the sample data if need\n    same = False\n    if os.path.exists(ZVT_TEST_ZIP_DATA_PATH):\n        import filecmp\n\n        same = filecmp.cmp(ZVT_TEST_ZIP_DATA_PATH, DATA_SAMPLE_ZIP_PATH)\n\n    if not same:\n        from zvt.contract import *\n        from zvt.utils.zip_utils import unzip\n\n        shutil.copyfile(DATA_SAMPLE_ZIP_PATH, ZVT_TEST_ZIP_DATA_PATH)\n        unzip(ZVT_TEST_ZIP_DATA_PATH, ZVT_TEST_DATA_PATH)\nelse:\n    init_env(zvt_home=ZVT_HOME)\n\nold_db_to_provider_dir(zvt_env[\"data_path\"])\n\n# register to meta\nimport zvt.contract as zvt_contract\nimport zvt.recorders as zvt_recorders\nimport zvt.factors as zvt_factors\n\nimport platform\n\nif platform.system() == \"Windows\":\n    try:\n        import zvt.recorders.qmt as qmt_recorder\n    except Exception as e:\n        logger.error(\"QMT not work\", e)\nelse:\n    logger.warning(\"QMT need run in Windows!\")\n\n\n__all__ = [\"zvt_env\", \"zvt_config\", \"init_log\", \"init_env\", \"init_config\", \"__version__\"]\n"
  },
  {
    "path": "src/zvt/api/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/api/intent.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nimport pandas as pd\n\nfrom zvt.api.kdata import get_kdata_schema\nfrom zvt.contract.api import decode_entity_id\nfrom zvt.contract.drawer import Drawer, ChartType\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\ndef compare(\n    entity_ids=None,\n    codes=None,\n    schema=None,\n    columns=None,\n    schema_map_columns: dict = None,\n    chart_type: ChartType = ChartType.line,\n    start_timestamp=None,\n    scale_value: int = None,\n):\n    \"\"\"\n    compare indicators(columns) of entities\n\n    :param entity_ids:\n    :param codes:\n    :param schema:\n    :param columns:\n    :param schema_map_columns: key represents schema, value represents columns\n    :param chart_type: \"line\", \"area\", \"scatter\", default \"line\"\n    :param start_timestamp: \"\n    :param scale_value: compare with same value which scaled to scale_value\n    \"\"\"\n\n    dfs = []\n    # default compare kdata\n    if schema_map_columns is None and schema is None:\n        entity_type_map_ids = _group_entity_ids(entity_ids=entity_ids)\n        for entity_type in entity_type_map_ids:\n            schema = get_kdata_schema(entity_type=entity_type)\n            df = schema.query_data(entity_ids=entity_type_map_ids.get(entity_type), start_timestamp=start_timestamp)\n            dfs.append(df)\n        all_df = pd.concat(dfs)\n        drawer = Drawer(main_df=all_df, sub_df_list=[all_df[[\"entity_id\", \"timestamp\", \"turnover\"]].copy()])\n        drawer.draw_kline(show=True, scale_value=scale_value)\n    else:\n        if schema_map_columns:\n            for schema in schema_map_columns:\n                columns = [\"entity_id\", \"timestamp\"] + schema_map_columns.get(schema)\n                df = schema.query_data(\n                    entity_ids=entity_ids, codes=codes, columns=columns, start_timestamp=start_timestamp\n                )\n                dfs.append(df)\n        elif schema:\n            columns = [\"entity_id\", \"timestamp\"] + columns\n            df = schema.query_data(entity_ids=entity_ids, codes=codes, columns=columns, start_timestamp=start_timestamp)\n            dfs.append(df)\n\n        all_df = pd.concat(dfs)\n        drawer = Drawer(main_df=all_df)\n        drawer.draw(main_chart=chart_type, show=True, scale_value=scale_value)\n\n\ndef compare_df(df: pd.DataFrame, chart_type: ChartType = ChartType.line):\n    \"\"\"\n    compare indicators(columns) of entities in df\n\n    :param df: normal df\n    :param chart_type:\n    \"\"\"\n    drawer = Drawer(main_df=df)\n    drawer.draw(main_chart=chart_type, show=True)\n\n\ndef distribute(data_schema, columns, entity_ids=None, codes=None, histnorm=\"percent\", nbinsx=20, filters=None):\n    \"\"\"\n    distribute indicators(columns) of entities\n\n    :param data_schema:\n    :param columns:\n    :param entity_ids:\n    :param codes:\n    :param histnorm: \"percent\", \"probability\", default \"percent\"\n    :param nbinsx:\n    :param filters:\n    \"\"\"\n    columns = [\"entity_id\", \"timestamp\"] + columns\n    df = data_schema.query_data(entity_ids=entity_ids, codes=codes, columns=columns, filters=filters)\n    if not entity_ids or codes:\n        df[\"entity_id\"] = \"entity_x_distribute\"\n    distribute_df(df=df, histnorm=histnorm, nbinsx=nbinsx)\n\n\ndef distribute_df(df, histnorm=\"percent\", nbinsx=20):\n    \"\"\"\n    distribute indicators(columns) of entities in df\n\n    :param df: normal df\n    :param histnorm: \"percent\", \"probability\", default \"percent\"\n    :param nbinsx:\n    \"\"\"\n    drawer = Drawer(main_df=df)\n    drawer.draw_histogram(show=True, histnorm=histnorm, nbinsx=nbinsx)\n\n\ndef composite(entity_id, data_schema, columns, filters=None):\n    \"\"\"\n    composite indicators(columns) of entity\n\n    :param entity_id:\n    :param data_schema:\n    :param columns:\n    :param filters:\n    \"\"\"\n    columns = [\"entity_id\", \"timestamp\"] + columns\n    df = data_schema.query_data(entity_id=entity_id, columns=columns, filters=filters)\n    composite_df(df=df)\n\n\ndef composite_df(df):\n    \"\"\"\n    composite indicators(columns) of entity in df\n\n    :param df:\n    \"\"\"\n    drawer = Drawer(main_df=df)\n    drawer.draw_pie(show=True)\n\n\ndef composite_all(data_schema, column, timestamp, provider=None, entity_ids=None, filters=None):\n    if type(column) is not str:\n        column = column.name\n    if filters:\n        filters.append([data_schema.timestamp == to_pd_timestamp(timestamp)])\n    else:\n        filters = [data_schema.timestamp == to_pd_timestamp(timestamp)]\n    df = data_schema.query_data(\n        provider=provider,\n        entity_ids=entity_ids,\n        columns=[\"entity_id\", \"timestamp\", column],\n        filters=filters,\n        index=\"entity_id\",\n    )\n    entity_type, exchange, _ = decode_entity_id(df[\"entity_id\"].iloc[0])\n    pie_df = pd.DataFrame(columns=df.index, data=[df[column].tolist()])\n    pie_df[\"entity_id\"] = f\"{entity_type}_{exchange}_{column}\"\n    pie_df[\"timestamp\"] = timestamp\n\n    drawer = Drawer(main_df=pie_df)\n    drawer.draw_pie(show=True)\n\n\ndef _group_entity_ids(entity_ids):\n    entity_type_map_ids = {}\n    for entity_id in entity_ids:\n        entity_type, _, _ = decode_entity_id(entity_id)\n        ids: List = entity_type_map_ids.setdefault(entity_type, [])\n        ids.append(entity_id)\n    return entity_type_map_ids\n\n\nif __name__ == \"__main__\":\n    # from zvt.domain import Index1wkKdata\n    # from zvt.api.intent import compare\n    #\n    # Index1wkKdata.record_data(provider=\"em\", codes=[\"399370\", \"399371\"])\n    # df1 = Index1wkKdata.query_data(code=\"399371\", index=\"timestamp\")\n    # df2 = Index1wkKdata.query_data(code=\"399370\", index=\"timestamp\")\n    # se = df1[\"close\"] / (df2[\"close\"])\n    #\n    # compare(se)\n\n    from zvt.domain import CashFlowStatement\n\n    #\n    # composite(\n    #     entity_id=\"stock_sz_000338\",\n    #     data_schema=CashFlowStatement,\n    #     columns=[\n    #         CashFlowStatement.net_op_cash_flows,\n    #         CashFlowStatement.net_investing_cash_flows,\n    #         CashFlowStatement.net_financing_cash_flows,\n    #     ],\n    #     filters=[\n    #         CashFlowStatement.report_period == \"year\",\n    #         CashFlowStatement.report_date == to_pd_timestamp(\"2015-12-31\"),\n    #     ],\n    # )\n    df = CashFlowStatement.query_data(\n        entity_id=\"stock_sz_000338\",\n        columns=[\n            CashFlowStatement.net_op_cash_flows,\n            CashFlowStatement.net_investing_cash_flows,\n            CashFlowStatement.net_financing_cash_flows,\n        ],\n        filters=[\n            CashFlowStatement.report_period == \"year\",\n            CashFlowStatement.report_date == to_pd_timestamp(\"2015-12-31\"),\n        ],\n        index=\"timestamp\",\n    )\n    composite_df(df=df)\n\n\n# the __all__ is generated\n__all__ = [\"compare\", \"compare_df\", \"distribute\", \"distribute_df\", \"composite\", \"composite_df\", \"composite_all\"]\n"
  },
  {
    "path": "src/zvt/api/kdata.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom typing import Union\n\nimport numpy as np\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel, AdjustType, Mixin\nfrom zvt.contract.api import decode_entity_id, get_schema_by_name\nfrom zvt.domain import Index1dKdata, Indexus1dKdata, Indexhk1dKdata\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import (\n    to_date_time_str,\n    TIME_FORMAT_DAY,\n    TIME_FORMAT_ISO8601,\n    to_pd_timestamp,\n    date_time_by_interval,\n    current_date,\n)\n\n\ndef get_trade_dates(entity_type, start, end=None):\n    if entity_type == \"stockus\":\n        df = Indexus1dKdata.query_data(\n            entity_id=\"indexus_us_SPX\",\n            provider=\"em\",\n            columns=[\"timestamp\"],\n            start_timestamp=start,\n            end_timestamp=end,\n            order=Indexus1dKdata.timestamp.asc(),\n            return_type=\"df\",\n        )\n    elif entity_type == \"stockhk\":\n        df = Indexhk1dKdata.query_data(\n            entity_id=\"indexhk_hk_HSI\",\n            provider=\"em\",\n            columns=[\"timestamp\"],\n            start_timestamp=start,\n            end_timestamp=end,\n            order=Indexhk1dKdata.timestamp.asc(),\n            return_type=\"df\",\n        )\n\n    else:\n        df = Index1dKdata.query_data(\n            entity_id=\"index_sh_000001\",\n            provider=\"em\",\n            columns=[\"timestamp\"],\n            start_timestamp=start,\n            end_timestamp=end,\n            order=Index1dKdata.timestamp.asc(),\n            return_type=\"df\",\n        )\n\n    return df[\"timestamp\"].tolist()\n\n\ndef get_recent_trade_dates(entity_type, target_date=current_date(), days_count=5):\n    max_start = date_time_by_interval(target_date, -days_count - 15)\n    dates = get_trade_dates(entity_type=entity_type, start=max_start)\n    if days_count == 0:\n        return dates[-1:]\n    return dates[-days_count:]\n\n\ndef get_latest_kdata_date(\n    entity_type: str,\n    provider: str = None,\n    level: Union[IntervalLevel, str] = IntervalLevel.LEVEL_1DAY,\n    adjust_type: Union[AdjustType, str] = None,\n) -> pd.Timestamp:\n    data_schema: Mixin = get_kdata_schema(entity_type, level=level, adjust_type=adjust_type)\n\n    filters = None\n    if entity_type == \"indexus\":\n        filters = [data_schema.entity_id == \"indexus_us_SPX\"]\n    latest_data = data_schema.query_data(\n        provider=provider, filters=filters, order=data_schema.timestamp.desc(), limit=1, return_type=\"domain\"\n    )\n    return to_pd_timestamp(latest_data[0].timestamp)\n\n\ndef get_kdata_schema(\n    entity_type: str,\n    level: Union[IntervalLevel, str] = IntervalLevel.LEVEL_1DAY,\n    adjust_type: Union[AdjustType, str] = None,\n) -> Mixin:\n    if type(level) == str:\n        level = IntervalLevel(level)\n    if type(adjust_type) == str:\n        adjust_type = AdjustType(adjust_type)\n\n    # kdata schema rule\n    # name:{entity_type.capitalize()}{IntervalLevel.value.capitalize()}Kdata\n    if adjust_type and (adjust_type != AdjustType.qfq):\n        schema_str = \"{}{}{}Kdata\".format(\n            entity_type.capitalize(),\n            level.value.capitalize(),\n            adjust_type.value.capitalize(),\n        )\n    else:\n        schema_str = \"{}{}Kdata\".format(entity_type.capitalize(), level.value.capitalize())\n    return get_schema_by_name(schema_str)\n\n\ndef get_kdata(\n    entity_id=None,\n    entity_ids=None,\n    level=IntervalLevel.LEVEL_1DAY.value,\n    provider=None,\n    columns=None,\n    return_type=\"df\",\n    start_timestamp=None,\n    end_timestamp=None,\n    filters=None,\n    session=None,\n    order=None,\n    limit=None,\n    index=\"timestamp\",\n    drop_index_col=False,\n    adjust_type: AdjustType = None,\n):\n    assert not entity_id or not entity_ids\n    if entity_ids:\n        entity_id = entity_ids[0]\n    else:\n        entity_ids = [entity_id]\n\n    entity_type, exchange, code = decode_entity_id(entity_id)\n    data_schema: Mixin = get_kdata_schema(entity_type, level=level, adjust_type=adjust_type)\n\n    return data_schema.query_data(\n        entity_ids=entity_ids,\n        level=level,\n        provider=provider,\n        columns=columns,\n        return_type=return_type,\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        filters=filters,\n        session=session,\n        order=order,\n        limit=limit,\n        index=index,\n        drop_index_col=drop_index_col,\n    )\n\n\ndef default_adjust_type(entity_type: str) -> AdjustType:\n    \"\"\"\n    :type entity_type: entity type, e.g stock, stockhk, stockus\n    \"\"\"\n    if entity_type.lower().startswith(\"stock\"):\n        return AdjustType.hfq\n    return AdjustType.qfq\n\n\ndef generate_kdata_id(entity_id, timestamp, level):\n    if level >= IntervalLevel.LEVEL_1DAY:\n        return \"{}_{}\".format(entity_id, to_date_time_str(timestamp, fmt=TIME_FORMAT_DAY))\n    else:\n        return \"{}_{}\".format(entity_id, to_date_time_str(timestamp, fmt=TIME_FORMAT_ISO8601))\n\n\ndef to_high_level_kdata(kdata_df: pd.DataFrame, to_level: IntervalLevel):\n    def to_close(s):\n        if pd_is_not_null(s):\n            return s[-1]\n\n    def to_open(s):\n        if pd_is_not_null(s):\n            return s[0]\n\n    def to_high(s):\n        return np.max(s)\n\n    def to_low(s):\n        return np.min(s)\n\n    def to_sum(s):\n        return np.sum(s)\n\n    original_level = kdata_df[\"level\"][0]\n    entity_id = kdata_df[\"entity_id\"][0]\n    provider = kdata_df[\"provider\"][0]\n    name = kdata_df[\"name\"][0]\n    code = kdata_df[\"code\"][0]\n\n    entity_type, _, _ = decode_entity_id(entity_id=entity_id)\n\n    assert IntervalLevel(original_level) <= IntervalLevel.LEVEL_1DAY\n    assert IntervalLevel(original_level) < IntervalLevel(to_level)\n\n    df: pd.DataFrame = None\n    if to_level == IntervalLevel.LEVEL_1WEEK:\n        # loffset='-2'　用周五作为时间标签\n        if entity_type == \"stock\":\n            df = kdata_df.resample(\"W\", offset=pd.Timedelta(days=-2)).apply(\n                {\n                    \"close\": to_close,\n                    \"open\": to_open,\n                    \"high\": to_high,\n                    \"low\": to_low,\n                    \"volume\": to_sum,\n                    \"turnover\": to_sum,\n                }\n            )\n        else:\n            df = kdata_df.resample(\"W\", offset=pd.Timedelta(days=-2)).apply(\n                {\n                    \"close\": to_close,\n                    \"open\": to_open,\n                    \"high\": to_high,\n                    \"low\": to_low,\n                    \"volume\": to_sum,\n                    \"turnover\": to_sum,\n                }\n            )\n    df = df.dropna()\n    # id        entity_id  timestamp   provider    code  name level\n    df[\"entity_id\"] = entity_id\n    df[\"provider\"] = provider\n    df[\"code\"] = code\n    df[\"name\"] = name\n\n    return df\n\n\nif __name__ == \"__main__\":\n    print(get_trade_dates(entity_type=\"stockhk\", start=\"2025-07-01\"))\n\n\n# the __all__ is generated\n__all__ = [\n    \"get_trade_dates\",\n    \"get_recent_trade_dates\",\n    \"get_latest_kdata_date\",\n    \"get_kdata_schema\",\n    \"get_kdata\",\n    \"default_adjust_type\",\n    \"generate_kdata_id\",\n    \"to_high_level_kdata\",\n]\n"
  },
  {
    "path": "src/zvt/api/portfolio.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom typing import List\n\nimport pandas as pd\n\nfrom zvt.api.utils import get_recent_report_date\nfrom zvt.contract import PortfolioStockHistory\nfrom zvt.contract.api import get_schema_by_name\nfrom zvt.domain import ReportPeriod, Fund, Etf\nfrom zvt.utils.time_utils import to_pd_timestamp, now_pd_timestamp\n\n\ndef portfolio_relate_stock(df, portfolio):\n    df[\"entity_id\"] = portfolio.entity_id\n    df[\"entity_type\"] = portfolio.entity_type\n    df[\"exchange\"] = portfolio.exchange\n    df[\"code\"] = portfolio.code\n    df[\"name\"] = portfolio.name\n\n    return df\n\n\n# 季报只有前十大持仓，半年报和年报才有全量的持仓信息，故根据离timestamp最近的报表(年报 or 半年报)来确定持仓\ndef get_portfolio_stocks(\n    portfolio_entity=Fund,\n    code=None,\n    codes=None,\n    ids=None,\n    timestamp=now_pd_timestamp(),\n    provider=None,\n):\n    portfolio_stock = f\"{portfolio_entity.__name__}Stock\"\n    data_schema: PortfolioStockHistory = get_schema_by_name(portfolio_stock)\n    latests: List[PortfolioStockHistory] = data_schema.query_data(\n        provider=provider,\n        code=code,\n        end_timestamp=timestamp,\n        order=data_schema.timestamp.desc(),\n        limit=1,\n        return_type=\"domain\",\n    )\n    if latests:\n        latest_record = latests[0]\n        # 获取最新的报表\n        df = data_schema.query_data(\n            provider=provider,\n            code=code,\n            codes=codes,\n            ids=ids,\n            end_timestamp=timestamp,\n            filters=[data_schema.report_date == latest_record.report_date],\n        )\n        # 最新的为年报或者半年报\n        if latest_record.report_period == ReportPeriod.year or latest_record.report_period == ReportPeriod.half_year:\n            return df\n        # 季报，需要结合 年报或半年报 来算持仓\n        else:\n            step = 0\n            while step <= 20:\n                report_date = get_recent_report_date(latest_record.report_date, step=step)\n\n                pre_df = data_schema.query_data(\n                    provider=provider,\n                    code=code,\n                    codes=codes,\n                    ids=ids,\n                    end_timestamp=timestamp,\n                    filters=[data_schema.report_date == to_pd_timestamp(report_date)],\n                )\n                # df = df.append(pre_df)\n                df = pd.concat([df, pre_df])\n\n                # 半年报和年报\n                if (ReportPeriod.half_year.value in pre_df[\"report_period\"].tolist()) or (\n                    ReportPeriod.year.value in pre_df[\"report_period\"].tolist()\n                ):\n                    # 保留最新的持仓\n                    df = df.drop_duplicates(subset=[\"stock_code\"], keep=\"first\")\n                    return df\n                step = step + 1\n\n\ndef get_etf_stocks(code=None, codes=None, ids=None, timestamp=now_pd_timestamp(), provider=None):\n    return get_portfolio_stocks(\n        portfolio_entity=Etf,\n        code=code,\n        codes=codes,\n        ids=ids,\n        timestamp=timestamp,\n        provider=provider,\n    )\n\n\ndef get_fund_stocks(code=None, codes=None, ids=None, timestamp=now_pd_timestamp(), provider=None):\n    return get_portfolio_stocks(\n        portfolio_entity=Fund,\n        code=code,\n        codes=codes,\n        ids=ids,\n        timestamp=timestamp,\n        provider=provider,\n    )\n\n\n# the __all__ is generated\n__all__ = [\"portfolio_relate_stock\", \"get_portfolio_stocks\", \"get_etf_stocks\", \"get_fund_stocks\"]\n"
  },
  {
    "path": "src/zvt/api/selector.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nimport pandas as pd\nfrom sqlalchemy import or_, and_\n\nfrom zvt.api.kdata import default_adjust_type, get_kdata_schema, get_latest_kdata_date, get_recent_trade_dates\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.contract.api import get_entity_ids, get_entity_schema\nfrom zvt.domain import DragonAndTiger, Stock1dHfqKdata, Stock, LimitUpInfo, StockQuote, Stock1mQuote\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import (\n    to_pd_timestamp,\n    date_time_by_interval,\n    current_date,\n    next_date,\n    to_date_time_str,\n    TIME_FORMAT_MINUTE2,\n)\n\nlogger = logging.getLogger(__name__)\n\n# 500亿\nBIG_CAP = 50000000000\n# 150亿\nMIDDLE_CAP = 15000000000\n# 40亿\nSMALL_CAP = 4000000000\n\n# 买入榜单\nIN_DEPS = [\"dep1\", \"dep2\", \"dep3\", \"dep4\", \"dep5\"]\n# 卖出入榜单\nOUT_DEPS = [\"dep_1\", \"dep_2\", \"dep_3\", \"dep_4\", \"dep_5\"]\n\n\ndef get_entity_ids_by_filter(\n    provider=\"em\",\n    ignore_delist=True,\n    ignore_st=False,\n    ignore_new_stock=False,\n    target_date=None,\n    entity_type=\"stock\",\n    entity_ids=None,\n    ignore_bj=False,\n    exchange=None,\n):\n    entity_schema = get_entity_schema(entity_type=entity_type)\n\n    filters = []\n    if ignore_new_stock:\n        if not target_date:\n            target_date = current_date()\n        pre_year = date_time_by_interval(target_date, -365)\n        filters += [entity_schema.timestamp <= pre_year]\n    else:\n        if target_date:\n            filters += [entity_schema.timestamp <= target_date]\n    if ignore_delist:\n        filters += [\n            entity_schema.name.not_like(\"%退%\"),\n            entity_schema.name.not_like(\"%PT%\"),\n        ]\n\n    if ignore_st:\n        filters += [\n            entity_schema.name.not_like(\"%ST%\"),\n            entity_schema.name.not_like(\"%*ST%\"),\n        ]\n    if entity_schema == Stock and ignore_bj:\n        filters += [entity_schema.exchange != \"bj\"]\n\n    if exchange:\n        filters += [entity_schema.exchange == exchange]\n\n    return get_entity_ids(provider=provider, entity_schema=entity_schema, filters=filters, entity_ids=entity_ids)\n\n\ndef get_limit_up_stocks(timestamp):\n    df = LimitUpInfo.query_data(start_timestamp=timestamp, end_timestamp=timestamp, columns=[LimitUpInfo.entity_id])\n    if pd_is_not_null(df):\n        return df[\"entity_id\"].tolist()\n\n\ndef get_recent_trending_stocks(adjust_type=AdjustType.qfq, provider=\"em\", recent_days=20):\n    kdata_schema = get_kdata_schema(\"stock\", level=IntervalLevel.LEVEL_1DAY, adjust_type=adjust_type)\n    recent_dates = get_recent_trade_dates(entity_type=\"stock\", days_count=recent_days)\n    start_date = recent_dates[0]\n\n    df = kdata_schema.query_data(\n        provider=provider,\n        filters=[\n            kdata_schema.timestamp >= start_date,\n            kdata_schema.turnover >= 500000000,\n            kdata_schema.change_pct > 0.08,\n        ],\n        index=\"entity_id\",\n    )\n    entity_ids = list(set(df[\"entity_id\"].tolist()))\n\n    kdata_df = kdata_schema.query_data(\n        provider=provider,\n        entity_ids=entity_ids,\n        filters=[kdata_schema.timestamp >= date_time_by_interval(start_date, -400)],\n        index=[\"entity_id\", \"timestamp\"],\n    )\n\n    from zvt.factors.algorithm import MaTransformer\n\n    t = MaTransformer(windows=[10, 20, 30, 60, 120, 250])\n    ma_df = t.transform(kdata_df)\n\n    end_date = ma_df[\"timestamp\"].max()\n    result_df = ma_df[\n        (ma_df[\"timestamp\"] == end_date)\n        & (ma_df[\"turnover\"] >= 500000000)\n        & (ma_df[\"close\"] > ma_df[\"ma20\"])\n        & (ma_df[\"ma10\"] > ma_df[\"ma20\"])\n        & (ma_df[\"ma20\"] > ma_df[\"ma30\"])\n        & (ma_df[\"ma30\"] > ma_df[\"ma60\"])\n        & (ma_df[\"ma60\"] > ma_df[\"ma120\"])\n        & (ma_df[\"ma120\"] > ma_df[\"ma250\"])\n    ]\n    return result_df[\"entity_id\"].tolist()\n\n\ndef get_recent_active_stocks(adjust_type=AdjustType.qfq, provider=\"em\", recent_days=10):\n    kdata_schema = get_kdata_schema(\"stock\", level=IntervalLevel.LEVEL_1DAY, adjust_type=adjust_type)\n    recent_dates = get_recent_trade_dates(entity_type=\"stock\", days_count=recent_days)\n    start_date = recent_dates[0]\n\n    df = kdata_schema.query_data(\n        provider=provider,\n        filters=[kdata_schema.timestamp >= start_date, kdata_schema.is_limit_up == True],\n        index=\"entity_id\",\n    )\n    entity_ids = list(set(df[\"entity_id\"].tolist()))\n\n    kdata_df = kdata_schema.query_data(\n        provider=provider,\n        entity_ids=entity_ids,\n        filters=[kdata_schema.timestamp >= start_date],\n        index=[\"entity_id\", \"timestamp\"],\n    )\n\n    from zvt.factors.algorithm import MaTransformer\n\n    t = MaTransformer(windows=[10])\n    ma_df = t.transform(kdata_df)\n\n    end_date = ma_df[\"timestamp\"].max()\n    result_df = ma_df[\n        (ma_df[\"close\"] > ma_df[\"ma10\"]) & (ma_df[\"timestamp\"] == end_date) & (ma_df[\"turnover\"] >= 500000000)\n    ]\n    return result_df[\"entity_id\"].tolist()\n\n\ndef get_dragon_and_tigger_player(start_timestamp, end_timestamp=None, direction=\"in\"):\n    assert direction in (\"in\", \"out\")\n\n    filters = None\n    if direction == \"in\":\n        filters = [DragonAndTiger.change_pct > 0]\n        columns = [\"dep1\", \"dep2\", \"dep3\"]\n    elif direction == \"out\":\n        filters = [DragonAndTiger.change_pct > 0]\n        columns = [\"dep_1\", \"dep_2\", \"dep_3\"]\n\n    df = DragonAndTiger.query_data(start_timestamp=start_timestamp, end_timestamp=end_timestamp, filters=filters)\n    counts = []\n    for col in columns:\n        counts.append(df[[col, f\"{col}_rate\"]].groupby(col).count().sort_values(f\"{col}_rate\", ascending=False))\n    return counts\n\n\ndef get_big_players(start_timestamp, end_timestamp=None, count=40):\n    dep1, dep2, dep3 = get_dragon_and_tigger_player(start_timestamp=start_timestamp, end_timestamp=end_timestamp)\n    # 榜1前40\n    bang1 = dep1.index.tolist()[:count]\n\n    # 榜2前40\n    bang2 = dep2.index.tolist()[:count]\n\n    # 榜3前40\n    bang3 = dep3.index.tolist()[:count]\n\n    return list(set(bang1 + bang2 + bang3))\n\n\ndef get_player_performance(start_timestamp, end_timestamp=None, days=5, players=\"机构专用\", provider=\"em\", buy_rate=5):\n    filters = []\n    if isinstance(players, str):\n        players = [players]\n\n    if isinstance(players, list):\n        for player in players:\n            filters.append(\n                or_(\n                    and_(DragonAndTiger.dep1 == player, DragonAndTiger.dep1_rate >= buy_rate),\n                    and_(DragonAndTiger.dep2 == player, DragonAndTiger.dep2_rate >= buy_rate),\n                    and_(DragonAndTiger.dep3 == player, DragonAndTiger.dep3_rate >= buy_rate),\n                    and_(DragonAndTiger.dep4 == player, DragonAndTiger.dep4_rate >= buy_rate),\n                    and_(DragonAndTiger.dep5 == player, DragonAndTiger.dep5_rate >= buy_rate),\n                )\n            )\n    else:\n        raise AssertionError(\"players should be list or str type\")\n\n    df = DragonAndTiger.query_data(\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        filters=filters,\n        index=[\"entity_id\", \"timestamp\"],\n        provider=provider,\n    )\n    df = df[~df.index.duplicated(keep=\"first\")]\n    records = []\n    for entity_id, timestamp in df.index:\n        end_date = date_time_by_interval(timestamp, days + round(days + days * 2 / 5 + 30))\n        kdata = Stock1dHfqKdata.query_data(\n            entity_id=entity_id,\n            start_timestamp=timestamp,\n            end_timestamp=end_date,\n            provider=provider,\n            index=\"timestamp\",\n        )\n        if len(kdata) <= days:\n            logger.warning(f\"ignore {timestamp} -> end_timestamp: {end_date}\")\n            break\n        close = kdata[\"close\"]\n        change_pct = (close[days] - close[0]) / close[0]\n        records.append({\"entity_id\": entity_id, \"timestamp\": timestamp, f\"change_pct\": change_pct})\n    return pd.DataFrame.from_records(records)\n\n\ndef get_player_success_rate(\n    start_timestamp,\n    end_timestamp=None,\n    intervals=(3, 5, 10, 60),\n    players=(\"机构专用\", \"东方财富证券股份有限公司拉萨团结路第二证券营业部\"),\n    provider=\"em\",\n):\n    records = []\n    for player in players:\n        record = {\"player\": player}\n        for days in intervals:\n            df = get_player_performance(\n                start_timestamp=start_timestamp,\n                end_timestamp=end_timestamp,\n                days=days,\n                players=player,\n                provider=provider,\n            )\n            rate = len(df[df[\"change_pct\"] > 0]) / len(df)\n            record[f\"rate_{days}\"] = rate\n        records.append(record)\n    return pd.DataFrame.from_records(records, index=\"player\")\n\n\ndef get_players(entity_id, start_timestamp, end_timestamp, provider=\"em\", direction=\"in\", buy_rate=5):\n    columns = [\"entity_id\", \"timestamp\"]\n    if direction == \"in\":\n        for i in range(5):\n            columns.append(f\"dep{i + 1}\")\n            columns.append(f\"dep{i + 1}_rate\")\n    elif direction == \"out\":\n        for i in range(5):\n            columns.append(f\"dep_{i + 1}\")\n            columns.append(f\"dep_{i + 1}_rate\")\n\n    df = DragonAndTiger.query_data(\n        entity_id=entity_id,\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        provider=provider,\n        columns=columns,\n        index=[\"entity_id\", \"timestamp\"],\n    )\n    dfs = []\n    if direction == \"in\":\n        for i in range(5):\n            p_df = df[[f\"dep{i + 1}\", f\"dep{i + 1}_rate\"]].copy()\n            p_df.columns = [\"player\", \"buy_rate\"]\n            dfs.append(p_df)\n    elif direction == \"out\":\n        for i in range(5):\n            p_df = df[[f\"dep_{i + 1}\", f\"dep_{i + 1}_rate\"]].copy()\n            p_df.columns = [\"player\", \"buy_rate\"]\n            dfs.append(p_df)\n\n    player_df = pd.concat(dfs, sort=True)\n    return player_df.sort_index(level=[0, 1])\n\n\ndef get_good_players(timestamp=current_date(), recent_days=400, intervals=(3, 5, 10)):\n    end_timestamp = date_time_by_interval(timestamp, -intervals[-1] - 30)\n    # recent year\n    start_timestamp = date_time_by_interval(end_timestamp, -recent_days)\n    print(f\"{start_timestamp} to {end_timestamp}\")\n    # 最近一年牛x的营业部\n    players = get_big_players(start_timestamp=start_timestamp, end_timestamp=end_timestamp)\n    logger.info(players)\n    df = get_player_success_rate(\n        start_timestamp=start_timestamp, end_timestamp=end_timestamp, intervals=intervals, players=players\n    )\n    good_players = df[(df[\"rate_3\"] > 0.4) & (df[\"rate_5\"] > 0.3) & (df[\"rate_10\"] > 0.3)].index.tolist()\n    return good_players\n\n\ndef get_entity_list_by_cap(\n    timestamp, cap_start, cap_end, entity_type=\"stock\", provider=None, adjust_type=None, retry_times=20\n):\n    if not adjust_type:\n        adjust_type = default_adjust_type(entity_type=entity_type)\n\n    kdata_schema = get_kdata_schema(entity_type, level=IntervalLevel.LEVEL_1DAY, adjust_type=adjust_type)\n    df = kdata_schema.query_data(\n        provider=provider,\n        filters=[kdata_schema.timestamp == to_pd_timestamp(timestamp)],\n        index=\"entity_id\",\n    )\n    if pd_is_not_null(df):\n        df[\"cap\"] = df[\"turnover\"] / df[\"turnover_rate\"]\n        df_result = df.copy()\n        if cap_start:\n            df_result = df_result.loc[(df[\"cap\"] >= cap_start)]\n        if cap_end:\n            df_result = df_result.loc[(df[\"cap\"] <= cap_end)]\n        return df_result.index.tolist()\n    else:\n        if retry_times == 0:\n            return []\n        return get_entity_list_by_cap(\n            timestamp=next_date(timestamp),\n            cap_start=cap_start,\n            cap_end=cap_end,\n            entity_type=entity_type,\n            provider=provider,\n            adjust_type=adjust_type,\n            retry_times=retry_times - 1,\n        )\n\n\ndef get_big_cap_stock(timestamp, provider=\"em\", adjust_type=None):\n    return get_entity_list_by_cap(\n        timestamp=timestamp,\n        cap_start=BIG_CAP,\n        cap_end=None,\n        entity_type=\"stock\",\n        provider=provider,\n        adjust_type=adjust_type,\n    )\n\n\ndef get_middle_cap_stock(timestamp, provider=\"em\", adjust_type=None):\n    return get_entity_list_by_cap(\n        timestamp=timestamp,\n        cap_start=MIDDLE_CAP,\n        cap_end=BIG_CAP,\n        entity_type=\"stock\",\n        provider=provider,\n        adjust_type=adjust_type,\n    )\n\n\ndef get_small_cap_stock(timestamp, provider=\"em\", adjust_type=None):\n    return get_entity_list_by_cap(\n        timestamp=timestamp,\n        cap_start=SMALL_CAP,\n        cap_end=MIDDLE_CAP,\n        entity_type=\"stock\",\n        provider=provider,\n        adjust_type=adjust_type,\n    )\n\n\ndef get_mini_cap_stock(timestamp, provider=\"em\", adjust_type=None):\n    return get_entity_list_by_cap(\n        timestamp=timestamp,\n        cap_start=None,\n        cap_end=SMALL_CAP,\n        entity_type=\"stock\",\n        provider=provider,\n        adjust_type=adjust_type,\n    )\n\n\ndef get_mini_and_small_stock(timestamp, provider=\"em\", adjust_type=None):\n    return get_entity_list_by_cap(\n        timestamp=timestamp,\n        cap_start=None,\n        cap_end=MIDDLE_CAP,\n        entity_type=\"stock\",\n        provider=provider,\n        adjust_type=adjust_type,\n    )\n\n\ndef get_middle_and_big_stock(timestamp, provider=\"em\", adjust_type=None):\n    return get_entity_list_by_cap(\n        timestamp=timestamp,\n        cap_start=MIDDLE_CAP,\n        cap_end=None,\n        entity_type=\"stock\",\n        provider=provider,\n        adjust_type=adjust_type,\n    )\n\n\ndef get_limit_up_today():\n    df = StockQuote.query_data(filters=[StockQuote.is_limit_up], columns=[StockQuote.entity_id])\n    if pd_is_not_null(df):\n        return df[\"entity_id\"].to_list()\n\n\ndef get_top_up_today(n=100):\n    df = StockQuote.query_data(columns=[StockQuote.entity_id], order=StockQuote.change_pct.desc(), limit=n)\n    if pd_is_not_null(df):\n        return df[\"entity_id\"].to_list()\n\n\ndef get_shoot_today(up_change_pct=0.03, down_change_pct=-0.03, interval=1):\n    latest = Stock1mQuote.query_data(\n        columns=[Stock1mQuote.time], return_type=\"df\", limit=1, order=Stock1mQuote.time.desc()\n    )\n    latest_time = int(latest[\"time\"][0])\n\n    # interval minutes\n    start_time = latest_time - ((interval + 1) * 60 * 1000)\n\n    filters = [Stock1mQuote.time > start_time, Stock1mQuote.turnover_rate > 0.02]\n    df = Stock1mQuote.query_data(\n        filters=filters,\n        columns=[\n            Stock1mQuote.entity_id,\n            Stock1mQuote.is_limit_up,\n            Stock1mQuote.near_limit_up,\n            Stock1mQuote.time,\n            Stock1mQuote.price,\n        ],\n        return_type=\"df\",\n    )\n\n    if not pd_is_not_null(df):\n        logger.warning(\n            f\"no data found in Stock1mQuote from {to_date_time_str(start_time,fmt=TIME_FORMAT_MINUTE2)} to {to_date_time_str(latest_time,fmt=TIME_FORMAT_MINUTE2)}\"\n        )\n        return None, None\n\n    up_list = []\n    up_df = df[(~df[\"is_limit_up\"]) & df[\"near_limit_up\"]]\n    if pd_is_not_null(up_df):\n        up_list = up_df[\"entity_id\"]\n        up_list = list(set(up_list.to_list()))\n\n    df.sort_values(by=[\"entity_id\", \"time\"], inplace=True)\n\n    g_df = df.groupby(\"entity_id\").agg(\n        first_price=(\"price\", \"first\"),\n        last_price=(\"price\", \"last\"),\n        last_time=(\"time\", \"last\"),\n        change_pct=(\"price\", lambda x: (x.iloc[-1] - x.iloc[0]) / x.iloc[0]),\n    )\n    up = g_df[g_df[\"change_pct\"] >= up_change_pct]\n    down = g_df[g_df[\"change_pct\"] <= down_change_pct]\n\n    if up_list:\n        up_list = list(set(up_list + up.index.tolist()))\n    else:\n        up_list = up.index.tolist()\n\n    return up_list, down.index.tolist()\n\n\ndef get_top_vol(\n    entity_ids,\n    target_date=None,\n    limit=500,\n    provider=\"qmt\",\n):\n    if provider == \"qmt\":\n        df = StockQuote.query_data(\n            entity_ids=entity_ids,\n            columns=[StockQuote.entity_id],\n            order=StockQuote.turnover.desc(),\n            limit=limit,\n        )\n        return df[\"entity_id\"].to_list()\n    else:\n        if not target_date:\n            target_date = get_latest_kdata_date(provider=\"em\", entity_type=\"stock\", adjust_type=AdjustType.hfq)\n        df = Stock1dHfqKdata.query_data(\n            provider=\"em\",\n            filters=[Stock1dHfqKdata.timestamp == to_pd_timestamp(target_date)],\n            entity_ids=entity_ids,\n            columns=[Stock1dHfqKdata.entity_id],\n            order=Stock1dHfqKdata.turnover.desc(),\n            limit=limit,\n        )\n        return df[\"entity_id\"].to_list()\n\n\ndef get_top_down_today(n=100):\n    df = StockQuote.query_data(columns=[StockQuote.entity_id], order=StockQuote.change_pct.asc(), limit=n)\n    if pd_is_not_null(df):\n        return df[\"entity_id\"].to_list()\n\n\ndef get_limit_down_today():\n    df = StockQuote.query_data(filters=[StockQuote.is_limit_down], columns=[StockQuote.entity_id])\n    if pd_is_not_null(df):\n        return df[\"entity_id\"].to_list()\n\n\ndef get_high_days_count(entity_ids=None, target_date=current_date(), days_count=10, high_days_count=None):\n    recent_days = get_recent_trade_dates(entity_type=\"stock\", target_date=target_date, days_count=days_count)\n\n    if recent_days:\n        filters = [LimitUpInfo.timestamp >= recent_days[0]]\n    else:\n        filters = [LimitUpInfo.timestamp >= target_date]\n\n    if high_days_count:\n        filters = filters + [LimitUpInfo.high_days_count >= high_days_count]\n\n    df = LimitUpInfo.query_data(\n        entity_ids=entity_ids,\n        filters=filters,\n        columns=[LimitUpInfo.timestamp, LimitUpInfo.entity_id, LimitUpInfo.high_days, LimitUpInfo.high_days_count],\n    )\n    df_sorted = df.sort_values(by=[\"entity_id\", \"timestamp\"])\n    df_latest = df_sorted.drop_duplicates(subset=\"entity_id\", keep=\"last\").reset_index(drop=True)\n\n    entity_id_to_high_days_map = df_latest.set_index(\"entity_id\")[\"high_days\"].to_dict()\n    return entity_id_to_high_days_map\n\n\nif __name__ == \"__main__\":\n    # stocks = get_top_vol(entity_ids=None, provider=\"em\")\n    # assert len(stocks) == 500\n    # Index1dKdata.record_data(provider=\"em\",sleeping_time=0)\n    # print(get_recent_trade_dates(days_count=10))\n    # print(get_high_days_count(days_count=3, high_days_count=3))\n    print(get_shoot_today())\n\n# the __all__ is generated\n__all__ = [\n    \"get_entity_ids_by_filter\",\n    \"get_limit_up_stocks\",\n    \"get_dragon_and_tigger_player\",\n    \"get_big_players\",\n    \"get_player_performance\",\n    \"get_player_success_rate\",\n    \"get_players\",\n    \"get_good_players\",\n    \"get_entity_list_by_cap\",\n    \"get_big_cap_stock\",\n    \"get_middle_cap_stock\",\n    \"get_small_cap_stock\",\n    \"get_mini_cap_stock\",\n    \"get_mini_and_small_stock\",\n    \"get_middle_and_big_stock\",\n]\n"
  },
  {
    "path": "src/zvt/api/stats.py",
    "content": "# -*- coding: utf-8 -*-\nimport enum\nimport itertools\nimport logging\nfrom typing import Union\n\nimport pandas as pd\n\nfrom zvt.api.kdata import get_kdata_schema, default_adjust_type, get_latest_kdata_date, get_trade_dates\nfrom zvt.api.selector import get_entity_ids_by_filter\nfrom zvt.api.utils import get_recent_report_date\nfrom zvt.contract import Mixin, AdjustType\nfrom zvt.contract.api import decode_entity_id, get_entity_schema, get_entity_ids\nfrom zvt.contract.drawer import Drawer\nfrom zvt.domain import FundStock, StockValuation, BlockStock, Block\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import (\n    month_start_end_ranges,\n    to_date_time_str,\n    is_same_date,\n    now_pd_timestamp,\n    date_time_by_interval,\n)\n\nlogger = logging.getLogger(__name__)\n\n\nclass WindowMethod(enum.Enum):\n    change = \"change\"\n    avg = \"avg\"\n    sum = \"sum\"\n\n\nclass TopType(enum.Enum):\n    positive = \"positive\"\n    negative = \"negative\"\n\n\ndef get_top_performance_by_month(\n    entity_type=\"stock\",\n    start_timestamp=\"2015-01-01\",\n    end_timestamp=now_pd_timestamp(),\n    list_days=None,\n    data_provider=None,\n):\n    ranges = month_start_end_ranges(start_date=start_timestamp, end_date=end_timestamp)\n\n    for month_range in ranges:\n        start_timestamp = month_range[0]\n        end_timestamp = month_range[1]\n        top, _ = get_top_performance_entities(\n            entity_type=entity_type,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            list_days=list_days,\n            data_provider=data_provider,\n        )\n\n        yield start_timestamp, end_timestamp, top\n\n\ndef get_top_performance_entities_by_periods(\n    entity_provider,\n    data_provider,\n    target_date=None,\n    periods=None,\n    entity_ids=None,\n    ignore_new_stock=True,\n    ignore_st=True,\n    entity_type=\"stock\",\n    adjust_type=None,\n    top_count=50,\n    turnover_threshold=100000000,\n    turnover_rate_threshold=0.02,\n    return_type=TopType.positive,\n):\n    if periods is None:\n        periods = [*range(1, 21)]\n    if not adjust_type:\n        adjust_type = default_adjust_type(entity_type=entity_type)\n    kdata_schema = get_kdata_schema(entity_type=entity_type, adjust_type=adjust_type)\n    entity_schema = get_entity_schema(entity_type=entity_type)\n\n    if not target_date:\n        target_date = get_latest_kdata_date(provider=data_provider, entity_type=entity_type, adjust_type=adjust_type)\n\n    if not entity_ids:\n        filter_entity_ids = get_entity_ids_by_filter(\n            provider=entity_provider,\n            ignore_st=ignore_st,\n            ignore_new_stock=ignore_new_stock,\n            entity_schema=entity_schema,\n            target_date=target_date,\n        )\n    else:\n        filter_entity_ids = entity_ids\n\n    if not filter_entity_ids:\n        return []\n\n    filter_turnover_df = kdata_schema.query_data(\n        filters=[\n            kdata_schema.turnover >= turnover_threshold,\n            kdata_schema.turnover_rate >= turnover_rate_threshold,\n        ],\n        provider=data_provider,\n        start_timestamp=date_time_by_interval(target_date, -7),\n        end_timestamp=target_date,\n        index=\"entity_id\",\n        columns=[\"entity_id\", \"code\"],\n    )\n    if filter_entity_ids:\n        filter_entity_ids = set(filter_entity_ids) & set(filter_turnover_df.index.tolist())\n    else:\n        filter_entity_ids = filter_turnover_df.index.tolist()\n\n    if not filter_entity_ids:\n        return []\n\n    logger.info(f\"{entity_type} filter_entity_ids size: {len(filter_entity_ids)}\")\n    filters = [kdata_schema.entity_id.in_(filter_entity_ids)]\n    selected = []\n    current_start = None\n    real_period = 1\n    for i, period in enumerate(periods):\n        real_period = max(real_period, period)\n        while True:\n            start = date_time_by_interval(target_date, -real_period)\n            trade_days = get_trade_dates(entity_type=entity_type, start=start, end=target_date)\n            if not trade_days:\n                logger.info(f\"no trade days in: {start} to {target_date}\")\n                real_period = real_period + 1\n                continue\n            if current_start and is_same_date(current_start, trade_days[0]):\n                logger.info(\"ignore same trade days\")\n                real_period = real_period + 1\n                continue\n            break\n        current_start = trade_days[0]\n        current_end = trade_days[-1]\n\n        logger.info(f\"trade days in: {current_start} to {current_end}, real_period: {real_period} \")\n        positive_df, negative_df = get_top_performance_entities(\n            entity_type=entity_type,\n            start_timestamp=current_start,\n            end_timestamp=current_end,\n            kdata_filters=filters,\n            pct=1,\n            show_name=True,\n            entity_provider=entity_provider,\n            data_provider=data_provider,\n            adjust_type=adjust_type,\n            return_type=return_type,\n        )\n\n        if return_type == TopType.positive:\n            df = positive_df\n        else:\n            df = negative_df\n        if pd_is_not_null(df):\n            selected = selected + df.index[:top_count].tolist()\n            selected = list(dict.fromkeys(selected))\n    return selected, real_period\n\n\ndef get_top_performance_entities(\n    entity_type=\"stock\",\n    start_timestamp=None,\n    end_timestamp=None,\n    pct=0.1,\n    return_type=None,\n    adjust_type: Union[AdjustType, str] = None,\n    entity_filters=None,\n    kdata_filters=None,\n    show_name=False,\n    list_days=None,\n    entity_provider=None,\n    data_provider=None,\n):\n    if not adjust_type:\n        adjust_type = default_adjust_type(entity_type=entity_type)\n    data_schema = get_kdata_schema(entity_type=entity_type, adjust_type=adjust_type)\n\n    if not entity_filters:\n        entity_filters = []\n    if list_days:\n        entity_schema = get_entity_schema(entity_type=entity_type)\n        list_date = date_time_by_interval(start_timestamp, -list_days)\n        entity_filters += [entity_schema.list_date <= list_date]\n\n    filter_entities = get_entity_ids(\n        provider=entity_provider,\n        entity_type=entity_type,\n        filters=entity_filters,\n    )\n    if not filter_entities:\n        logger.warning(f\"no entities selected\")\n        return None, None\n\n    if not kdata_filters:\n        kdata_filters = []\n    kdata_filters = kdata_filters + [data_schema.entity_id.in_(filter_entities)]\n\n    return get_top_entities(\n        data_schema=data_schema,\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        column=\"close\",\n        pct=pct,\n        method=WindowMethod.change,\n        return_type=return_type,\n        kdata_filters=kdata_filters,\n        show_name=show_name,\n        data_provider=data_provider,\n    )\n\n\ndef get_top_fund_holding_stocks(timestamp=None, pct=0.3, by=None):\n    if not timestamp:\n        timestamp = now_pd_timestamp()\n    # 季报一般在report_date后1个月内公布，年报2个月内，年报4个月内\n    # 所以取时间点的最近的两个公布点，保证取到数据\n    # 所以，这是个滞后的数据，只是为了看个大概，毕竟模糊的正确better than 精确的错误\n    report_date = get_recent_report_date(timestamp, 1)\n    fund_cap_df = FundStock.query_data(\n        filters=[\n            FundStock.report_date >= report_date,\n            FundStock.timestamp <= timestamp,\n        ],\n        columns=[\"stock_id\", \"market_cap\"],\n    )\n    fund_cap_df = fund_cap_df.groupby(\"stock_id\")[\"market_cap\"].sum().sort_values(ascending=False)\n\n    # 直接根据持有市值返回\n    if not by:\n        s = fund_cap_df.iloc[: int(len(fund_cap_df) * pct)]\n\n        return s.to_frame()\n\n    # 按流通盘比例\n    if by == \"trading\":\n        columns = [\"entity_id\", \"circulating_market_cap\"]\n    # 按市值比例\n    elif by == \"all\":\n        columns = [\"entity_id\", \"market_cap\"]\n\n    entity_ids = fund_cap_df.index.tolist()\n    start_timestamp = date_time_by_interval(timestamp, -30)\n    cap_df = StockValuation.query_data(\n        entity_ids=entity_ids,\n        filters=[\n            StockValuation.timestamp >= start_timestamp,\n            StockValuation.timestamp <= timestamp,\n        ],\n        columns=columns,\n    )\n    if by == \"trading\":\n        cap_df = cap_df.rename(columns={\"circulating_market_cap\": \"cap\"})\n    elif by == \"all\":\n        cap_df = cap_df.rename(columns={\"market_cap\": \"cap\"})\n\n    cap_df = cap_df.groupby(\"entity_id\").mean()\n    result_df = pd.concat([cap_df, fund_cap_df], axis=1, join=\"inner\")\n    result_df[\"pct\"] = result_df[\"market_cap\"] / result_df[\"cap\"]\n\n    pct_df = result_df[\"pct\"].sort_values(ascending=False)\n\n    s = pct_df.iloc[: int(len(pct_df) * pct)]\n\n    return s.to_frame()\n\n\ndef get_performance(\n    entity_ids,\n    start_timestamp=None,\n    end_timestamp=None,\n    adjust_type: Union[AdjustType, str] = None,\n    data_provider=None,\n):\n    entity_type, _, _ = decode_entity_id(entity_ids[0])\n    if not adjust_type:\n        adjust_type = default_adjust_type(entity_type=entity_type)\n    data_schema = get_kdata_schema(entity_type=entity_type, adjust_type=adjust_type)\n\n    result, _ = get_top_entities(\n        data_schema=data_schema,\n        column=\"close\",\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        pct=1,\n        method=WindowMethod.change,\n        return_type=TopType.positive,\n        kdata_filters=[data_schema.entity_id.in_(entity_ids)],\n        data_provider=data_provider,\n    )\n    return result\n\n\ndef get_performance_stats_by_month(\n    entity_type=\"stock\",\n    start_timestamp=\"2015-01-01\",\n    end_timestamp=now_pd_timestamp(),\n    adjust_type: Union[AdjustType, str] = None,\n    data_provider=None,\n):\n    ranges = month_start_end_ranges(start_date=start_timestamp, end_date=end_timestamp)\n\n    month_stats = {}\n    for month_range in ranges:\n        start_timestamp = month_range[0]\n        end_timestamp = month_range[1]\n        logger.info(f\"calculate range [{start_timestamp}, {end_timestamp}]\")\n        stats = get_performance_stats(\n            entity_type=entity_type,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            adjust_type=adjust_type,\n            data_provider=data_provider,\n        )\n        if stats:\n            month_stats[f\"{to_date_time_str(start_timestamp)}\"] = stats\n\n    return pd.DataFrame.from_dict(data=month_stats, orient=\"index\")\n\n\ndef get_performance_stats(\n    entity_type=\"stock\",\n    start_timestamp=None,\n    end_timestamp=None,\n    adjust_type: Union[AdjustType, str] = None,\n    data_provider=None,\n    changes=((-1, -0.5), (-0.5, -0.2), (-0.2, 0), (0, 0.2), (0.2, 0.5), (0.5, 1), (1, 1000)),\n):\n    if not adjust_type:\n        adjust_type = default_adjust_type(entity_type=entity_type)\n    data_schema = get_kdata_schema(entity_type=entity_type, adjust_type=adjust_type)\n\n    score_df, _ = get_top_entities(\n        data_schema=data_schema,\n        column=\"close\",\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        pct=1,\n        method=WindowMethod.change,\n        return_type=TopType.positive,\n        data_provider=data_provider,\n    )\n\n    if pd_is_not_null(score_df):\n        result = {}\n        for change in changes:\n            range_start = change[0]\n            range_end = change[1]\n            key = f\"pct_{range_start}_{range_end}\"\n            df = score_df[(score_df[\"score\"] >= range_start) & (score_df[\"score\"] < range_end)]\n            result[key] = len(df)\n        return result\n\n\ndef get_top_volume_entities(\n    entity_type=\"stock\",\n    entity_ids=None,\n    start_timestamp=None,\n    end_timestamp=None,\n    pct=0.1,\n    return_type=TopType.positive,\n    adjust_type: Union[AdjustType, str] = None,\n    method=WindowMethod.avg,\n    data_provider=None,\n    threshold=None,\n):\n    if not adjust_type:\n        adjust_type = default_adjust_type(entity_type=entity_type)\n\n    data_schema = get_kdata_schema(entity_type=entity_type, adjust_type=adjust_type)\n\n    filters = []\n    if entity_ids:\n        filters.append(data_schema.entity_id.in_(entity_ids))\n    if threshold:\n        filters.append(data_schema.turnover >= threshold)\n\n    result, _ = get_top_entities(\n        data_schema=data_schema,\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        column=\"turnover\",\n        pct=pct,\n        method=method,\n        return_type=return_type,\n        kdata_filters=filters,\n        data_provider=data_provider,\n    )\n    return result\n\n\ndef get_top_turnover_rate_entities(\n    entity_type=\"stock\",\n    entity_ids=None,\n    start_timestamp=None,\n    end_timestamp=None,\n    pct=0.1,\n    return_type=TopType.positive,\n    adjust_type: Union[AdjustType, str] = None,\n    method=WindowMethod.avg,\n    data_provider=None,\n    threshold=None,\n):\n    if not adjust_type:\n        adjust_type = default_adjust_type(entity_type=entity_type)\n\n    data_schema = get_kdata_schema(entity_type=entity_type, adjust_type=adjust_type)\n\n    filters = []\n    if entity_ids:\n        filters.append(data_schema.entity_id.in_(entity_ids))\n    if threshold:\n        filters.append(data_schema.turnover_rate >= threshold)\n\n    result, _ = get_top_entities(\n        data_schema=data_schema,\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        column=\"turnover_rate\",\n        pct=pct,\n        method=method,\n        return_type=return_type,\n        kdata_filters=filters,\n        data_provider=data_provider,\n    )\n    return result\n\n\ndef get_top_entities(\n    data_schema: Mixin,\n    column: str,\n    start_timestamp=None,\n    end_timestamp=None,\n    pct=0.1,\n    method: WindowMethod = WindowMethod.change,\n    return_type: TopType = None,\n    kdata_filters=None,\n    show_name=False,\n    data_provider=None,\n):\n    \"\"\"\n    get top entities in specific domain between time range\n\n    :param data_schema: schema in domain\n    :param column: schema column\n    :param start_timestamp:\n    :param end_timestamp:\n    :param pct: range (0,1]\n    :param method:\n    :param return_type:\n    :param entity_filters:\n    :param kdata_filters:\n    :param show_name: show entity name\n    :return:\n    \"\"\"\n    if type(method) == str:\n        method = WindowMethod(method)\n\n    if type(return_type) == str:\n        return_type = TopType(return_type)\n\n    if show_name:\n        columns = [\"entity_id\", column, \"name\"]\n    else:\n        columns = [\"entity_id\", column]\n\n    all_df = data_schema.query_data(\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        columns=columns,\n        filters=kdata_filters,\n        provider=data_provider,\n    )\n    if not pd_is_not_null(all_df):\n        return None, None\n    g = all_df.groupby(\"entity_id\")\n    tops = {}\n    names = {}\n    for entity_id, df in g:\n        if method == WindowMethod.change:\n            start = df[column].iloc[0]\n            end = df[column].iloc[-1]\n            if start != 0:\n                change = (end - start) / abs(start)\n            else:\n                change = 0\n            tops[entity_id] = change\n        elif method == WindowMethod.avg:\n            tops[entity_id] = df[column].mean()\n        elif method == WindowMethod.sum:\n            tops[entity_id] = df[column].sum()\n\n        if show_name:\n            names[entity_id] = df[\"name\"].iloc[0]\n\n    positive_df = None\n    negative_df = None\n    top_index = int(len(tops) * pct)\n    if return_type is None or return_type == TopType.positive:\n        # from big to small\n        positive_tops = {k: v for k, v in sorted(tops.items(), key=lambda item: item[1], reverse=True)}\n        positive_tops = dict(itertools.islice(positive_tops.items(), top_index))\n        positive_df = pd.DataFrame.from_dict(positive_tops, orient=\"index\")\n\n        col = \"score\"\n        positive_df.columns = [col]\n        positive_df.sort_values(by=col, ascending=False)\n    if return_type is None or return_type == TopType.negative:\n        # from small to big\n        negative_tops = {k: v for k, v in sorted(tops.items(), key=lambda item: item[1])}\n        negative_tops = dict(itertools.islice(negative_tops.items(), top_index))\n        negative_df = pd.DataFrame.from_dict(negative_tops, orient=\"index\")\n\n        col = \"score\"\n        negative_df.columns = [col]\n        negative_df.sort_values(by=col)\n\n    if names:\n        if pd_is_not_null(positive_df):\n            positive_df[\"name\"] = positive_df.index.map(lambda x: names[x])\n        if pd_is_not_null(negative_df):\n            negative_df[\"name\"] = negative_df.index.map(lambda x: names[x])\n    return positive_df, negative_df\n\n\ndef show_month_performance():\n    dfs = []\n    for timestamp, df in get_top_performance_by_month(start_timestamp=\"2005-01-01\", list_days=250):\n        if pd_is_not_null(df):\n            df = df.reset_index(drop=True)\n            df[\"entity_id\"] = \"stock_cn_performance\"\n            df[\"timestamp\"] = timestamp\n            dfs.append(df)\n\n    all_df = pd.concat(dfs)\n    print(all_df)\n\n    drawer = Drawer(main_df=all_df)\n    drawer.draw_scatter(show=True)\n\n\ndef show_industry_composition(entity_ids, timestamp):\n    block_df = Block.query_data(provider=\"eastmoney\", filters=[Block.category == \"industry\"], index=\"entity_id\")\n    block_ids = block_df.index.tolist()\n\n    block_df = BlockStock.query_data(entity_ids=block_ids, filters=[BlockStock.stock_id.in_(entity_ids)])\n\n    s = block_df[\"name\"].value_counts()\n\n    cycle_df = pd.DataFrame(columns=s.index, data=[s.tolist()])\n    cycle_df[\"entity_id\"] = \"stock_cn_industry\"\n    cycle_df[\"timestamp\"] = timestamp\n    drawer = Drawer(main_df=cycle_df)\n    drawer.draw_pie(show=True)\n\n\ndef get_change_ratio(\n    entity_type=\"stock\",\n    start_timestamp=None,\n    end_timestamp=None,\n    adjust_type: Union[AdjustType, str] = None,\n    provider=\"em\",\n):\n    def cal_ratio(df):\n        positive = df[df[\"direction\"]]\n        other = df[~df[\"direction\"]]\n        return len(positive) / len(other)\n\n    if not adjust_type:\n        adjust_type = default_adjust_type(entity_type=entity_type)\n    data_schema = get_kdata_schema(entity_type=entity_type, adjust_type=adjust_type)\n    kdata_df = data_schema.query_data(provider=provider, start_timestamp=start_timestamp, end_timestamp=end_timestamp)\n    kdata_df[\"direction\"] = kdata_df[\"change_pct\"] > 0\n    ratio_df = kdata_df.groupby(\"timestamp\").apply(lambda df: cal_ratio(df))\n    return ratio_df\n\n\nif __name__ == \"__main__\":\n    normal_stock_ids = get_entity_ids_by_filter(\n        entity_type=\"stockus\", provider=\"em\", ignore_delist=True, ignore_st=False, ignore_new_stock=False\n    )\n\n    print(\n        get_top_performance_entities_by_periods(\n            entity_provider=\"em\",\n            data_provider=\"em\",\n            entity_ids=normal_stock_ids,\n            entity_type=\"stockus\",\n            adjust_type=AdjustType.qfq,\n            turnover_threshold=10000000,\n            turnover_rate_threshold=0,\n        )\n    )\n\n\n# the __all__ is generated\n__all__ = [\n    \"WindowMethod\",\n    \"TopType\",\n    \"get_top_performance_by_month\",\n    \"get_top_performance_entities_by_periods\",\n    \"get_top_performance_entities\",\n    \"get_top_fund_holding_stocks\",\n    \"get_performance\",\n    \"get_performance_stats_by_month\",\n    \"get_performance_stats\",\n    \"get_top_volume_entities\",\n    \"get_top_turnover_rate_entities\",\n    \"get_top_entities\",\n    \"show_month_performance\",\n    \"show_industry_composition\",\n    \"get_change_ratio\",\n]\n"
  },
  {
    "path": "src/zvt/api/utils.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import Type\n\nfrom zvt.contract import Mixin\nfrom zvt.domain import ReportPeriod\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_pd_timestamp, now_pd_timestamp\n\n\ndef to_report_period_type(report_date):\n    the_date = to_pd_timestamp(report_date)\n    if the_date.month == 3 and the_date.day == 31:\n        return ReportPeriod.season1.value\n    if the_date.month == 6 and the_date.day == 30:\n        return ReportPeriod.half_year.value\n    if the_date.month == 9 and the_date.day == 30:\n        return ReportPeriod.season3.value\n    if the_date.month == 12 and the_date.day == 31:\n        return ReportPeriod.year.value\n\n    return None\n\n\ndef get_recent_report_date(the_date=now_pd_timestamp(), step=0):\n    the_date = to_pd_timestamp(the_date)\n    assert step >= 0\n    if the_date.month >= 10:\n        recent = \"{}{}\".format(the_date.year, \"-09-30\")\n    elif the_date.month >= 7:\n        recent = \"{}{}\".format(the_date.year, \"-06-30\")\n    elif the_date.month >= 4:\n        recent = \"{}{}\".format(the_date.year, \"-03-31\")\n    else:\n        recent = \"{}{}\".format(the_date.year - 1, \"-12-31\")\n\n    if step == 0:\n        return recent\n    else:\n        step = step - 1\n        return get_recent_report_date(recent, step)\n\n\ndef get_recent_report_period(the_date=now_pd_timestamp(), step=0):\n    return to_report_period_type(get_recent_report_date(the_date, step=step))\n\n\ndef get_china_exchange(code):\n    code_ = int(code)\n    if 800000 >= code_ >= 600000:\n        return \"sh\"\n    elif code_ >= 400000:\n        return \"bj\"\n    else:\n        return \"sz\"\n\n\ndef china_stock_code_to_id(code):\n    return \"{}_{}_{}\".format(\"stock\", get_china_exchange(code), code)\n\n\ndef value_to_pct(value, default=0):\n    return value / 100 if value else default\n\n\ndef value_multiply(value, multiplier, default=0):\n    return value * multiplier if value else default\n\n\ndef float_to_pct_str(value):\n    return f\"{round(value * 100, 2)}%\"\n\n\ndef get_recent_report(data_schema: Type[Mixin], timestamp, entity_id=None, filters=None, max_step=2):\n    i = 0\n    while i < max_step:\n        report_date = get_recent_report_date(the_date=timestamp, step=i)\n        if filters:\n            filters = filters + [data_schema.report_date == to_pd_timestamp(report_date)]\n        else:\n            filters = [data_schema.report_date == to_pd_timestamp(report_date)]\n        df = data_schema.query_data(entity_id=entity_id, filters=filters)\n        if pd_is_not_null(df):\n            return df\n        i = i + 1\n\n\n# the __all__ is generated\n__all__ = [\n    \"to_report_period_type\",\n    \"get_recent_report_date\",\n    \"get_recent_report_period\",\n    \"get_china_exchange\",\n    \"china_stock_code_to_id\",\n    \"value_to_pct\",\n    \"value_multiply\",\n    \"float_to_pct_str\",\n    \"get_recent_report\",\n]\n"
  },
  {
    "path": "src/zvt/autocode/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule generator\nfrom .generator import *\nfrom .generator import __all__ as _generator_all\n\n__all__ += _generator_all\n\n# import all from submodule templates\nfrom .templates import *\nfrom .templates import __all__ as _templates_all\n\n__all__ += _templates_all\n"
  },
  {
    "path": "src/zvt/autocode/generator.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport os\nfrom typing import List\n\nfrom zvt.autocode.templates import all_tpls\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.utils.file_utils import list_all_files\nfrom zvt.utils.git_utils import get_git_user_name, get_git_user_email\nfrom zvt.utils.time_utils import now_pd_timestamp\n\nlogger = logging.getLogger(__name__)\n\n\ndef all_sub_modules(dir_path: str):\n    \"\"\"\n    list all module name in specific directory\n\n    :param dir_path:\n    :return:\n    \"\"\"\n    modules = []\n    for entry in os.scandir(dir_path):\n        if entry.is_dir() or (entry.path.endswith(\".py\") and not entry.path.endswith(\"__init__.py\")):\n            module_name = os.path.splitext(os.path.basename(entry.path))[0]\n            # ignore hidden\n            if module_name.startswith(\".\") or not module_name[0].isalpha():\n                continue\n            modules.append(module_name)\n    return modules\n\n\ndef _remove_start_end(line: str, start=\"class \", end=\"(\"):\n    if line.startswith(start) and (end in line):\n        start_index = len(start)\n        end_index = line.index(end)\n        return line[start_index:end_index]\n    if not start and (end in line):\n        end_index = line.index(end)\n        return line[:end_index]\n\n\ndef _get_interface_name(line: str):\n    \"\"\"\n    get interface name of the line\n\n    :param line: the line of the source\n    :return:\n    \"\"\"\n    if line.startswith(\"class \"):\n        return _remove_start_end(line, \"class \", \"(\")\n    elif line.startswith(\"def \"):\n        return _remove_start_end(line, \"def \", \"(\")\n\n\ndef _get_var_name(line: str):\n    \"\"\"\n    get var name of the line\n\n    :param line: the line of the source\n    :return:\n    \"\"\"\n    if not _get_interface_name(line):\n        words = line.split(\" \")\n        if len(words) >= 2 and words[1] == \"=\":\n            return words[0]\n\n\ndef all_sub_all(sub_module):\n    return \"\"\"\n\n# import all from submodule {0}\nfrom .{0} import *\nfrom .{0} import __all__ as _{0}_all\n\n__all__ += _{0}_all\"\"\".format(\n        sub_module\n    )\n\n\ndef fill_package_if_not_exist(dir_path: str):\n    fill_package(dir_path)\n    for entry in os.scandir(dir_path):\n        if entry.is_dir():\n            fill_package(entry.path)\n            fill_package_if_not_exist(entry.path)\n        elif entry.is_file():\n            pass\n\n\ndef fill_package(dir_path: str):\n    base_name = os.path.basename(dir_path)\n    if base_name[0].isalpha():\n        if os.path.isdir(dir_path):\n            pkg_file = os.path.join(dir_path, \"__init__.py\")\n            if not os.path.exists(pkg_file):\n                package_template = \"# -*- coding: utf-8 -*-\\n\"\n                with open(pkg_file, \"w\", encoding=\"utf-8\") as outfile:\n                    outfile.write(package_template)\n\n\ndef gen_exports(\n    dir_path=\"./domain\",\n    gen_flag=\"# the __all__ is generated\",\n    export_from_package=False,\n    exclude_modules=None,\n    export_modules=None,\n    excludes=None,\n    export_var=False,\n):\n    if not excludes:\n        excludes = [\"logger\"]\n    if os.path.isfile(dir_path):\n        files = [dir_path]\n    else:\n        fill_package_if_not_exist(dir_path=dir_path)\n        files = list_all_files(dir_path=dir_path)\n    for file in files:\n        exports = []\n        lines = []\n        # read and generate __all__\n        with open(file, encoding=\"utf-8\") as fp:\n            line = fp.readline()\n            while line:\n                if line.startswith(gen_flag):\n                    break\n                lines.append(line)\n                export = _get_interface_name(line)\n                if export_var and not export:\n                    export = _get_var_name(line)\n                if export and export[0].isalpha() and export not in excludes:\n                    exports.append(export)\n                line = fp.readline()\n        print(f\"{file}:{exports}\")\n        end_empty_lines_count = 0\n        for i in range(-1, -len(lines) - 1, -1):\n            if not lines[i].isspace():\n                break\n            end_empty_lines_count = end_empty_lines_count + 1\n        lines = lines[: len(lines) - end_empty_lines_count]\n\n        if not lines:\n            lines.append(\"# -*- coding: utf-8 -*-#\")\n\n        lines.append(\"\\n\\n\")\n        lines.append(gen_flag)\n        lines.append(\"\\n\")\n        exports_str = f\"__all__ = {exports}\"\n        exports_str = exports_str.replace(\"'\", '\"')\n        if len(exports_str) > 120:\n            exports_wrap = [f'\\n    \"{item}\",' for item in exports]\n            exports_str = \"__all__ = [\" + \"\".join(exports_wrap) + \"\\n]\"\n            exports_str = exports_str.replace(\"'\", '\"')\n        lines.append(exports_str)\n        lines.append(\"\\n\")\n\n        # the package module\n        if export_from_package:\n            basename = os.path.basename(file)\n            if basename == \"__init__.py\":\n                dir_path = os.path.dirname(file)\n                modules = all_sub_modules(dir_path)\n                if modules:\n                    if exclude_modules:\n                        modules = set(modules) - set(exclude_modules)\n                    if export_modules:\n                        modules = set(modules) & set(export_modules)\n                    lines.append(\n                        \"\"\"\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\"\"\"\n                    )\n                    for mod in modules:\n                        lines.append(all_sub_all(mod))\n                    lines.append(\"\\n\")\n\n        # write with __all__\n        with open(file, mode=\"w\", encoding=\"utf-8\") as fp:\n            fp.writelines(lines)\n\n\n# kdata schema rule\n# 1)name:{entity_type.capitalize()}{IntervalLevel.value.capitalize()}Kdata\n# 2)one db file for one schema\n\n\ndef gen_kdata_schema(\n    pkg: str,\n    entity_type: str,\n    levels: List[IntervalLevel],\n    adjust_types=None,\n    entity_in_submodule: bool = False,\n    kdata_module=\"quotes\",\n):\n    if adjust_types is None:\n        adjust_types = [None]\n    tables = []\n\n    base_path = \"./domain\"\n\n    if kdata_module:\n        base_path = os.path.join(base_path, kdata_module)\n    if entity_in_submodule:\n        base_path = os.path.join(base_path, entity_type)\n\n    if not os.path.exists(base_path):\n        logger.info(f\"create dir {base_path}\")\n        os.makedirs(base_path)\n\n    for level in levels:\n        for adjust_type in adjust_types:\n            level = IntervalLevel(level)\n\n            cap_entity_type = entity_type.capitalize()\n            cap_level = level.value.capitalize()\n\n            # you should define {EntityType}KdataCommon in kdata_module at first\n            kdata_common = f\"{cap_entity_type}KdataCommon\"\n\n            if adjust_type and (adjust_type != AdjustType.qfq):\n                class_name = f\"{cap_entity_type}{cap_level}{adjust_type.value.capitalize()}Kdata\"\n                table_name = f\"{entity_type}_{level.value}_{adjust_type.value.lower()}_kdata\"\n            else:\n                class_name = f\"{cap_entity_type}{cap_level}Kdata\"\n                table_name = f\"{entity_type}_{level.value}_kdata\"\n\n            tables.append(table_name)\n\n            schema_template = f\"\"\"# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom {pkg}.domain.{kdata_module} import {kdata_common}\n\nKdataBase = declarative_base()\n\n\nclass {class_name}(KdataBase, {kdata_common}):\n    __tablename__ = \"{table_name}\"\n\n\nregister_schema(db_name=\"{table_name}\", schema_base=KdataBase, entity_type=\"{entity_type}\")\n\n\"\"\"\n            # generate the schema\n            with open(os.path.join(base_path, f\"{table_name}.py\"), \"w\", encoding=\"utf-8\") as outfile:\n                outfile.write(schema_template)\n\n        # generate the package\n        pkg_file = os.path.join(base_path, \"__init__.py\")\n        if not os.path.exists(pkg_file):\n            package_template = \"\"\"# -*- coding: utf-8 -*-\n\"\"\"\n            with open(pkg_file, \"w\", encoding=\"utf-8\") as outfile:\n                outfile.write(package_template)\n\n    # generate exports\n    gen_exports(\"./domain\")\n\n\ndef gen_plugin_project(entity_type, prefix: str = \"zvt\", dir_path: str = \".\", providers=[\"joinquant\"]):\n    \"\"\"\n    generate a standard plugin project\n\n    :param entity_type: the entity type of the plugin project\n    :param prefix:  project prefix\n    :param dir_path: the root path for the project\n    :param providers: the supported providers\n    \"\"\"\n\n    # generate project files\n    project = f\"{prefix}_{entity_type}\"\n    entity_class = entity_type.capitalize()\n    project_path = os.path.join(dir_path, project)\n    if not os.path.exists(project_path):\n        os.makedirs(project_path)\n\n    current_time = now_pd_timestamp()\n    user_name = get_git_user_name()\n    user_email = get_git_user_email()\n\n    for tpl in all_tpls(project=project, entity_type=entity_type):\n        file_name = tpl[0]\n        tpl_content = tpl[1].safe_substitute(\n            project=project,\n            entity_type=entity_type,\n            entity_class=entity_class,\n            providers=providers,\n            provider=providers[0],\n            Provider=providers[0].capitalize(),\n            year=current_time.year,\n            user=user_name,\n            email=user_email,\n        )\n        file_path = os.path.join(project_path, file_name)\n\n        file_dir = os.path.dirname(file_path)\n        if not os.path.exists(file_dir):\n            os.makedirs(file_dir)\n\n        with open(file_path, \"w\", encoding=\"utf-8\") as fh:\n            fh.write(tpl_content)\n\n\n# the __all__ is generated\n__all__ = [\n    \"all_sub_modules\",\n    \"all_sub_all\",\n    \"fill_package_if_not_exist\",\n    \"fill_package\",\n    \"gen_exports\",\n    \"gen_kdata_schema\",\n    \"gen_plugin_project\",\n]\n"
  },
  {
    "path": "src/zvt/autocode/templates/.coveragerc.template",
    "content": "[run]\nomit = ${project}/recorders/*"
  },
  {
    "path": "src/zvt/autocode/templates/.gitignore.template",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n#idea\n.idea\n.vscode\n# Distribution / packaging\n.Python\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n.hypothesis/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# IPython Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# dotenv\n.env\n\n# virtualenv\nvenv/\nENV/\n/ve/\n\n# Spyder project settings\n.spyderproject\n\n# Rope project settings\n.ropeproject\n\n/zvt-home\n/zvt-test-home\n*.tar.gz\n\n.pytest_cache/\n\n# LibreOffice locks\n.~lock.*#\n\nnode_modules/\n\n*running.ipynb\n\n.DS_Store"
  },
  {
    "path": "src/zvt/autocode/templates/.travis.yml.template",
    "content": "language: python\ncache: pip\npython:\n  - '3.6'\ninstall:\n  - git fetch --tags --depth=500\n  - pip install 'pytest>=3.6' --force-reinstall\n  - pip install pytest-cov codecov\n  - pip install -r ./requirements.txt\nscript:\n  - pytest ./tests --cov-config=.coveragerc --cov-report term --cov=./${project} --ignore=tests/recorders/\nafter_success:\n  - codecov\ndeploy:\n  provider: pypi\n  user: ${user}\n  password:\n    secure: MvVCnUZUuTYxzs8R8kWTocOqZt2MGUX/W1hdKMxKJ9G9hFOYhzjEnbsF1Sc9yHB/0S1OY49068ScbN6iCuyKK2LZy/x3o7cKJgPnQt2LEjy6Yuu9MLxT6v8hJ0MTT3YCn0N8bNv4tOz7KkxxbZ8O/b5MgIKfdjBhVHEj92hhykYzyzlmG8mF+nxU/j0IGCAdxN9+IDioMIvCgnFqQhvkDwva4YbG6Uy+8YMVHFT3I+tSZRSmYxl/IwHJS+5tinI4TxX/ewrI5EznOe0HZvhF+eez+tGenS3pKF4hqF6t4RmKQX2kkdMuPFAvuveoMnPGiaSdoEMni1JPFnZL+3R4GVJPzk4F10v6AZPd10CARXqEwP23JCKAe0WvnbSBkV4iKpkvgxqPA59UwNQ90Jn4pDcTSao1WfRliAnBWCVj7S4x6xjEoNKPvOhXP3RPIYhgEFu4Ma4Cpihkof6VVtlg8VAJKH7j1vWmms3ShdddKXMeF2365sn5Owe671okYmMYMas/v47Y2Cz/0hwpVLuklNR5OYayXMXfUMIG2pH1pEFvg7y8v7ivy1EyCKGva+M/qEnoR1VGWNGSkypJHxG+w8dbtYwf3EYTA9fk/di3ygzepV7RrKJSDf8R+NwFrSfXeoEP07LonbtGN9iIz7Lp5PoB6rv99NHihlB47n1+PtM=\n  on:\n    tags: true\nnotifications:\n  email:\n    recipients:\n      - ${email}"
  },
  {
    "path": "src/zvt/autocode/templates/LICENSE.template",
    "content": "MIT License\n\nCopyright (c) ${year} zvtvz\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "src/zvt/autocode/templates/MANIFEST.in.template",
    "content": "include *.md\ninclude *.txt\ninclude LICENSE\nrecursive-include ${project}/* *"
  },
  {
    "path": "src/zvt/autocode/templates/README-en.md.template",
    "content": "[![github](https://img.shields.io/github/stars/zvtvz/${project}.svg)](https://github.com/zvtvz/${project})\n[![image](https://img.shields.io/pypi/v/${project}.svg)](https://pypi.org/project/${project}/)\n[![image](https://img.shields.io/pypi/l/${project}.svg)](https://pypi.org/project/${project}/)\n[![image](https://img.shields.io/pypi/pyversions/${project}.svg)](https://pypi.org/project/${project}/)\n[![Build Status](https://api.travis-ci.org/zvtvz/${project}.svg?branch=master)](https://travis-ci.org/zvtvz/${project})\n\n### introduction\n\n\n### feature\n\n### install\n```\npip install ${project}\n\npip show ${project}\n```\n\nupgrate to latest version\n```\npip install --upgrade ${project}\n```\n\n###  usage\n\n### contact\n\nwechat foolcage"
  },
  {
    "path": "src/zvt/autocode/templates/README.md.template",
    "content": "[![github](https://img.shields.io/github/stars/zvtvz/${project}.svg)](https://github.com/zvtvz/${project})\n[![image](https://img.shields.io/pypi/v/${project}.svg)](https://pypi.org/project/${project}/)\n[![image](https://img.shields.io/pypi/l/${project}.svg)](https://pypi.org/project/${project}/)\n[![image](https://img.shields.io/pypi/pyversions/${project}.svg)](https://pypi.org/project/${project}/)\n[![Build Status](https://api.travis-ci.org/zvtvz/${project}.svg?branch=master)](https://travis-ci.org/zvtvz/${project})\n\n### 说明\n\n\n### 特性\n\n### 安装\n```\npip install ${project}\n\npip show ${project}\n```\n\n更新到最新版本\n```\npip install --upgrade ${project}\n```\n\n###  使用\n\n### 联系\n\n微信 foolcage"
  },
  {
    "path": "src/zvt/autocode/templates/__init__.py",
    "content": "# -*- coding: utf-8 -*-\nimport os\nimport string\n\nfrom pkg_resources import resource_string\n\nfrom zvt.utils.file_utils import list_all_files\n\n\ndef all_tpls(project: str, entity_type: str):\n    \"\"\"\n    return list of templates(location,Template)\n\n    :param project:\n    :return:\n    \"\"\"\n    tpl_dir = os.path.join(os.path.dirname(__file__))\n    tpl_files = list_all_files(tpl_dir, ext=\"template\", return_base_name=True)\n    tpls = []\n    for tpl in tpl_files:\n        data = resource_string(__name__, tpl)\n        file_location = os.path.splitext(os.path.basename(tpl))[0]\n        # we assure that line endings are converted to '\\n' for all OS\n        data = data.decode(encoding=\"utf-8\").replace(os.linesep, \"\\n\")\n\n        # change path for specific file\n        # domain\n        if file_location == \"kdata_common.py\":\n            file_location = f\"{project}/domain/quotes/__init__.py\"\n        elif file_location == \"meta.py\":\n            file_location = f\"{project}/domain/{entity_type}_meta.py\"\n        # recorder\n        elif file_location == \"kdata_recorder.py\":\n            file_location = f\"{project}/recorders/{entity_type}_kdata_recorder.py\"\n        elif file_location == \"meta_recorder.py\":\n            file_location = f\"{project}/recorders/{entity_type}_meta_recorder.py\"\n        # fill script\n        elif file_location == \"fill_project.py\":\n            file_location = f\"{project}/fill_project.py\"\n        # tests\n        elif file_location == \"test_pass.py\":\n            file_location = f\"tests/test_pass.py\"\n        elif file_location == \"pkg_init.py\":\n            file_location = f\"{project}/__init__.py\"\n\n        tpls.append((file_location, string.Template(data)))\n    return tpls\n\n\n# the __all__ is generated\n__all__ = [\"all_tpls\"]\n"
  },
  {
    "path": "src/zvt/autocode/templates/fill_project.py.template",
    "content": "# script to auto generate some files\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.autocode.generator import gen_exports, gen_kdata_schema\n\nif __name__ == '__main__':\n    gen_kdata_schema(pkg='${project}', entity_type='${entity_type}',\n                 levels=[IntervalLevel.LEVEL_1DAY], adjust_types=[None])\n    gen_exports()\n"
  },
  {
    "path": "src/zvt/autocode/templates/kdata_common.py.template",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, Float\n\nfrom zvt.domain import KdataCommon\n\n\n# ${entity_type} common kdata\nclass ${entity_class}KdataCommon(KdataCommon):\n    pass\n"
  },
  {
    "path": "src/zvt/autocode/templates/kdata_recorder.py.template",
    "content": "# -*- coding: utf-8 -*-\n\n# -*- coding: utf-8 -*-\n\nfrom zvt import IntervalLevel\nfrom zvt.api.kdata import get_kdata_schema\nfrom zvt.contract.recorder import FixedCycleDataRecorder\n\nfrom ${project}.domain import ${entity_class}, ${entity_class}KdataCommon\n\n\nclass ${Provider}${entity_class}KdataRecorder(FixedCycleDataRecorder):\n    entity_provider = '${provider}'\n    entity_schema = ${entity_class}\n\n    provider = '${provider}'\n\n    # register the recorder to data_schema\n    data_schema = ${entity_class}KdataCommon\n\n    def __init__(self, entity_type='${entity_type}', exchanges=None, entity_ids=None, codes=None, day_data=False,\n                 force_update=True, sleeping_time=10, entity_filters=None, real_time=False, fix_duplicate_way='ignore',\n             start_timestamp=None, end_timestamp=None,  level=IntervalLevel.LEVEL_1DAY,\n             kdata_use_begin_time=False, one_day_trading_minutes=24 * 60) -> None:\n        level = IntervalLevel(level)\n        self.data_schema = get_kdata_schema(entity_type=entity_type, level=level, adjust_type=None)\n\n        super().__init__(entity_type, exchanges, entity_ids, codes, day_data, force_update, sleeping_time,\n                         entity_filters, real_time, fix_duplicate_way, start_timestamp, end_timestamp, close_hour,\n                         close_minute, level, kdata_use_begin_time, one_day_trading_minutes)\n\n    def record(self, entity, start, end, size, timestamps):\n        pass\n"
  },
  {
    "path": "src/zvt/autocode/templates/meta.py.template",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, DateTime, Boolean\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import EntityMixin\nfrom zvt.contract.register import register_schema, register_entity\n\n${entity_class}MetaBase = declarative_base()\n\n@register_entity(entity_type='${entity_type}')\nclass ${entity_class}(EntityMixin, ${entity_class}MetaBase):\n    __tablename__ = '${entity_type}'\n    # 上市日\n    list_date = Column(DateTime)\n    # 退市日\n    end_date = Column(DateTime)\n\n\nregister_schema(db_name='${entity_type}_meta', schema_base=${entity_class}MetaBase)\n\n"
  },
  {
    "path": "src/zvt/autocode/templates/meta_recorder.py.template",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.contract.recorder import Recorder\nfrom ${project}.domain import ${entity_class}\n\nclass ${Provider}${entity_class}Recorder(Recorder):\n    data_schema = ${entity_class}\n    provider = '${provider}'\n\n    def run(self):\n        pass\n"
  },
  {
    "path": "src/zvt/autocode/templates/pkg_init.py.template",
    "content": "# -*- coding: utf-8 -*-\nimport functools\n\nfrom zvt import init_config\n\n${project}_config = {}\n\nint_${project}_config = functools.partial(init_config, pkg_name='${project}', current_config=${project}_config)\n\nint_${project}_config()\n\n__all__ = ['int_${project}_config']\n"
  },
  {
    "path": "src/zvt/autocode/templates/requirements.txt.template",
    "content": "zvt"
  },
  {
    "path": "src/zvt/autocode/templates/setup.py.template",
    "content": "#!/usr/bin/env python\n\n# To use a consistent encoding\nfrom codecs import open\nfrom os import path\n\n# Always prefer setuptools over distutils\nfrom setuptools import setup, find_packages\n\ntry:\n    # for pip >= 10\n    from pip._internal.req import parse_requirements\nexcept ImportError:\n    # for pip <= 9.0.3\n    from pip.req import parse_requirements\n\nhere = path.abspath(path.dirname(__file__))\n\n# Get the long description from the README file\nwith open(path.join(here, \"README.md\"), encoding=\"utf-8\") as f:\n    long_description = f.read()\n\n# Arguments marked as \"Required\" below must be included for upload to PyPI.\n# Fields marked as \"Optional\" may be commented out.\n\ninstall_reqs = parse_requirements(\"requirements.txt\", session=False)\n\ntry:\n    requirements = [str(ir.req) for ir in install_reqs]\nexcept:\n    requirements = [str(ir.requirement) for ir in install_reqs]\n\nsetup(\n    name=\"${project}\",\n    version=\"0.0.1\",\n    description=\"unified,modular quant framework for human beings \",\n    long_description=long_description,\n    url=\"https://github.com/zvtvz/${project}\",\n    author=\"${user}\",\n    author_email=\"${email}\",\n    classifiers=[  # Optional\n        \"Development Status :: 5 - Production/Stable\",\n        \"Intended Audience :: Developers\",\n        \"Intended Audience :: Customer Service\",\n        \"Intended Audience :: Education\",\n        \"Intended Audience :: Financial and Insurance Industry\",\n        \"Topic :: Software Development :: Build Tools\",\n        \"Topic :: Office/Business :: Financial :: Investment\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n    ],\n    keywords=\"quant stock finance fintech big-data zvt ma-analysis trading-platform pandas fundamental-analysis\",\n    packages=find_packages(include=[\"${project}.*\", \"${project}\"]),\n    python_requires=\">=3.5, <4\",\n    include_package_data=True,\n    install_requires=requirements,\n    project_urls={\n        \"Bug Reports\": \"https://github.com/zvtvz/${project}/issues\",\n        \"Funding\": \"https://github.com/zvtvz/${project}\",\n        \"Say Thanks!\": \"https://saythanks.io/to/foolcage\",\n        \"Source\": \"https://github.com/zvtvz/${project}\",\n    },\n    long_description_content_type=\"text/markdown\",\n)\n"
  },
  {
    "path": "src/zvt/autocode/templates/test_pass.py.template",
    "content": "# -*- coding: utf-8 -*-\n\ndef test_pass():\n    pass\n# the __all__ is generated\n__all__ = ['test_pass']"
  },
  {
    "path": "src/zvt/broker/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/broker/qmt/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/broker/qmt/context.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import Optional\n\nfrom zvt import zvt_env\nfrom zvt.broker.qmt.qmt_account import QmtStockAccount\n\n\nclass QmtContext(object):\n    def __init__(self):\n        self.qmt_account: Optional[QmtStockAccount] = None\n\n\nqmt_context = QmtContext()\n\n\ndef init_qmt_account(qmt_mini_data_path=None, qmt_account_id=None):\n    if not qmt_mini_data_path:\n        qmt_mini_data_path = zvt_env[\"qmt_mini_data_path\"]\n    if not qmt_account_id:\n        qmt_account_id = zvt_env[\"qmt_account_id\"]\n    qmt_context.qmt_account = QmtStockAccount(\n        path=qmt_mini_data_path, account_id=qmt_account_id, trader_name=\"zvt\", session_id=None\n    )\n\n\ninit_qmt_account()\n\n\n# the __all__ is generated\n__all__ = [\"QmtContext\", \"init_qmt_account\"]\n"
  },
  {
    "path": "src/zvt/broker/qmt/errors.py",
    "content": "# -*- coding: utf-8 -*-\nclass TraderError(Exception):\n    \"\"\"Base class for exceptions in this module.\"\"\"\n\n    pass\n\n\nclass QmtError(TraderError):\n    def __init__(self, message=\"qmt error\"):\n        self.message = message\n\n\nclass PositionOverflowError(TraderError):\n    def __init__(self, message=\"超出仓位限制\"):\n        self.message = message\n\n\n# the __all__ is generated\n__all__ = [\"TraderError\", \"QmtError\", \"PositionOverflowError\"]\n"
  },
  {
    "path": "src/zvt/broker/qmt/qmt_account.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\nfrom typing import List\n\nfrom xtquant import xtconstant, xtdata\nfrom xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback\nfrom xtquant.xttype import StockAccount, XtPosition\n\nfrom zvt.broker.qmt.errors import QmtError, PositionOverflowError\nfrom zvt.broker.qmt.qmt_quote import _to_qmt_code\nfrom zvt.common.trading_models import BuyParameter, PositionType, SellParameter\nfrom zvt.trader import AccountService, TradingSignal, OrderType, trading_signal_type_to_order_type\nfrom zvt.utils.time_utils import now_pd_timestamp, to_pd_timestamp\n\nlogger = logging.getLogger(__name__)\n\n\ndef _to_qmt_order_type(order_type: OrderType):\n    if order_type == OrderType.order_long:\n        return xtconstant.STOCK_BUY\n    elif order_type == OrderType.order_close_long:\n        return xtconstant.STOCK_SELL\n\n\nclass MyXtQuantTraderCallback(XtQuantTraderCallback):\n    def on_connected(self):\n        logger.info(\"qmt on_connected\")\n\n    def on_smt_appointment_async_response(self, response):\n        logger.info(f\"qmt on_smt_appointment_async_response: {vars(response)}\")\n\n    def on_cancel_order_stock_async_response(self, response):\n        logger.info(f\"qmt on_cancel_order_stock_async_response: {vars(response)}\")\n\n    def on_disconnected(self):\n        \"\"\"\n        连接断开\n        :return:\n        \"\"\"\n        logger.info(f\"qmt on_disconnected\")\n\n    def on_stock_order(self, order):\n        \"\"\"\n        委托回报推送\n        :param order: XtOrder对象\n        :return:\n        \"\"\"\n        logger.info(f\"qmt on_stock_order: {vars(order)}\")\n\n    def on_stock_asset(self, asset):\n        \"\"\"\n        资金变动推送\n        :param asset: XtAsset对象\n        :return:\n        \"\"\"\n        logger.info(f\"qmt on_stock_asset: {vars(asset)}\")\n\n    def on_stock_trade(self, trade):\n        \"\"\"\n        成交变动推送\n        :param trade: XtTrade对象\n        :return:\n        \"\"\"\n        logger.info(f\"qmt on_stock_trade: {vars(trade)}\")\n\n    def on_stock_position(self, position):\n        \"\"\"\n        持仓变动推送\n        :param position: XtPosition对象\n        :return:\n        \"\"\"\n        logger.info(f\"qmt on_stock_position: {vars(position)}\")\n\n    def on_order_error(self, order_error):\n        \"\"\"\n        委托失败推送\n        :param order_error:XtOrderError 对象\n        :return:\n        \"\"\"\n        logger.info(f\"qmt on_order_error: {vars(order_error)}\")\n\n    def on_cancel_error(self, cancel_error):\n        \"\"\"\n        撤单失败推送\n        :param cancel_error: XtCancelError 对象\n        :return:\n        \"\"\"\n        logger.info(f\"qmt on_cancel_error: {vars(cancel_error)}\")\n\n    def on_order_stock_async_response(self, response):\n        \"\"\"\n        异步下单回报推送\n        :param response: XtOrderResponse 对象\n        :return:\n        \"\"\"\n        logger.info(f\"qmt on_order_stock_async_response: {vars(response)}\")\n\n    def on_account_status(self, status):\n        \"\"\"\n        :param response: XtAccountStatus 对象\n        :return:\n        \"\"\"\n        logger.info(status.account_id, status.account_type, status.status)\n\n\nclass QmtStockAccount(AccountService):\n    def __init__(self, path, account_id, trader_name, session_id=None) -> None:\n        if not session_id:\n            session_id = int(time.time())\n        self.trader_name = trader_name\n        logger.info(f\"path: {path}, account: {account_id}, trader_name: {trader_name}, session: {session_id}\")\n\n        self.xt_trader = XtQuantTrader(path=path, session=session_id)\n\n        # StockAccount可以用第二个参数指定账号类型，如沪港通传'HUGANGTONG'，深港通传'SHENGANGTONG'\n        self.account = StockAccount(account_id=account_id, account_type=\"STOCK\")\n\n        # 创建交易回调类对象，并声明接收回调\n        callback = MyXtQuantTraderCallback()\n        self.xt_trader.register_callback(callback)\n\n        # 启动交易线程\n        self.xt_trader.start()\n\n        # 建立交易连接，返回0表示连接成功\n        connect_result = self.xt_trader.connect()\n        if connect_result != 0:\n            logger.error(f\"qmt trader 连接失败: {connect_result}\")\n            raise QmtError(f\"qmt trader 连接失败: {connect_result}\")\n        logger.info(\"qmt trader 建立交易连接成功！\")\n\n        # 对交易回调进行订阅，订阅后可以收到交易主推，返回0表示订阅成功\n        subscribe_result = self.xt_trader.subscribe(self.account)\n\n        if subscribe_result != 0:\n            logger.error(f\"账号订阅失败: {subscribe_result}\")\n            raise QmtError(f\"账号订阅失败: {subscribe_result}\")\n        logger.info(\"账号订阅成功！\")\n\n    def get_positions(self):\n        positions: List[XtPosition] = self.xt_trader.query_stock_positions(self.account)\n        return positions\n\n    def get_current_position(self, entity_id, create_if_not_exist=False):\n        stock_code = _to_qmt_code(entity_id=entity_id)\n        # 根据股票代码查询对应持仓\n        return self.xt_trader.query_stock_position(self.account, stock_code)\n\n    def get_current_account(self):\n        asset = self.xt_trader.query_stock_asset(self.account)\n        return asset\n\n    def order_by_amount(self, entity_id, order_price, order_timestamp, order_type, order_amount):\n        stock_code = _to_qmt_code(entity_id=entity_id)\n        fix_result_order_id = self.xt_trader.order_stock(\n            account=self.account,\n            stock_code=stock_code,\n            order_type=_to_qmt_order_type(order_type=order_type),\n            order_volume=order_amount,\n            price_type=xtconstant.FIX_PRICE,\n            price=order_price,\n            strategy_name=self.trader_name,\n            order_remark=\"order from zvt\",\n        )\n        logger.info(f\"order result id: {fix_result_order_id}\")\n\n    def on_trading_signals(self, trading_signals: List[TradingSignal]):\n        for trading_signal in trading_signals:\n            try:\n                self.handle_trading_signal(trading_signal)\n            except Exception as e:\n                logger.exception(e)\n                self.on_trading_error(timestamp=trading_signal.happen_timestamp, error=e)\n\n    def handle_trading_signal(self, trading_signal: TradingSignal):\n        entity_id = trading_signal.entity_id\n        happen_timestamp = trading_signal.happen_timestamp\n        order_type = trading_signal_type_to_order_type(trading_signal.trading_signal_type)\n        trading_level = trading_signal.trading_level.value\n        # askPrice\t多档委卖价\n        # bidPrice\t多档委买价\n        # askVol\t多档委卖量\n        # bidVol\t多档委买量\n        if now_pd_timestamp() > to_pd_timestamp(trading_signal.due_timestamp):\n            logger.warning(\n                f\"the signal is expired, now {now_pd_timestamp()} is after due time: {trading_signal.due_timestamp}\"\n            )\n            return\n        quote = xtdata.get_l2_quote(stock_code=_to_qmt_code(entity_id=entity_id), start_time=happen_timestamp)\n        if order_type == OrderType.order_long:\n            price = quote[\"askPrice\"]\n        elif order_type == OrderType.order_close_long:\n            price = quote[\"bidPrice\"]\n        else:\n            assert False\n        self.order_by_amount(\n            entity_id=entity_id,\n            order_price=price,\n            order_timestamp=happen_timestamp,\n            order_type=order_type,\n            order_amount=trading_signal.order_amount,\n        )\n\n    def on_trading_open(self, timestamp):\n        pass\n\n    def on_trading_close(self, timestamp):\n        pass\n\n    def on_trading_finish(self, timestamp):\n        pass\n\n    def on_trading_error(self, timestamp, error):\n        pass\n\n    def sell(self, position_strategy: SellParameter):\n        # account_type\tint\t账号类型，参见数据字典\n        # account_id\tstr\t资金账号\n        # stock_code\tstr\t证券代码\n        # volume\tint\t持仓数量\n        # can_use_volume\tint\t可用数量\n        # open_price\tfloat\t开仓价\n        # market_value\tfloat\t市值\n        # frozen_volume\tint\t冻结数量\n        # on_road_volume\tint\t在途股份\n        # yesterday_volume\tint\t昨夜拥股\n        # avg_price\tfloat\t成本价\n        # direction\tint\t多空方向，股票不适用；参见数据字典\n        stock_codes = [_to_qmt_code(entity_id) for entity_id in position_strategy.entity_ids]\n        for i, stock_code in enumerate(stock_codes):\n            pct = position_strategy.sell_pcts[i]\n            position = self.xt_trader.query_stock_position(self.account, stock_code)\n            fix_result_order_id = self.xt_trader.order_stock(\n                account=self.account,\n                stock_code=stock_code,\n                order_type=xtconstant.STOCK_SELL,\n                order_volume=int(position.can_use_volume * pct),\n                price_type=xtconstant.MARKET_SH_CONVERT_5_CANCEL,\n                price=0,\n                strategy_name=self.trader_name,\n                order_remark=\"order from zvt\",\n            )\n            logger.info(f\"order result id: {fix_result_order_id}\")\n\n    def buy(self, buy_parameter: BuyParameter):\n        # account_type\tint\t账号类型，参见数据字典\n        # account_id\tstr\t资金账号\n        # cash\tfloat\t可用金额\n        # frozen_cash\tfloat\t冻结金额\n        # market_value\tfloat\t持仓市值\n        # total_asset\tfloat\t总资产\n        acc = self.get_current_account()\n\n        # 优先使用金额下单\n        if buy_parameter.money_to_use:\n            money_to_use = buy_parameter.money_to_use\n            if acc.cash < money_to_use:\n                raise QmtError(f\"可用余额不足 {acc.cash} < {money_to_use}\")\n        else:\n            # 检查仓位\n            if buy_parameter.position_type == PositionType.normal:\n                current_pct = round(acc.market_value / acc.total_asset, 2)\n                if current_pct >= buy_parameter.position_pct:\n                    raise PositionOverflowError(f\"目前仓位为{current_pct}, 已超过请求的仓位: {buy_parameter.position_pct}\")\n\n                money_to_use = acc.total_asset * (buy_parameter.position_pct - current_pct)\n            elif buy_parameter.position_type == PositionType.cash:\n                money_to_use = acc.cash * buy_parameter.position_pct\n            else:\n                assert False\n\n        stock_codes = [_to_qmt_code(entity_id) for entity_id in buy_parameter.entity_ids]\n        ticks = xtdata.get_full_tick(code_list=stock_codes)\n\n        if not buy_parameter.weights:\n            stocks_count = len(stock_codes)\n            money_for_stocks = [round(money_to_use / stocks_count)] * stocks_count\n        else:\n            weights_sum = sum(buy_parameter.weights)\n            money_for_stocks = [round(weight / weights_sum) for weight in buy_parameter.weights]\n\n        for i, stock_code in enumerate(stock_codes):\n            try_price = ticks[stock_code][\"askPrice\"][3]\n            volume = money_for_stocks[i] / try_price\n            fix_result_order_id = self.xt_trader.order_stock(\n                account=self.account,\n                stock_code=stock_code,\n                order_type=xtconstant.STOCK_BUY,\n                order_volume=volume,\n                price_type=xtconstant.MARKET_SH_CONVERT_5_CANCEL,\n                price=0,\n                strategy_name=self.trader_name,\n                order_remark=\"order from zvt\",\n            )\n            logger.info(f\"order result id: {fix_result_order_id}\")\n\n\nif __name__ == \"__main__\":\n    account = QmtStockAccount(path=r\"D:\\qmt\\userdata_mini\", account_id=\"\")\n    account.get_positions()\n\n\n# the __all__ is generated\n__all__ = [\"MyXtQuantTraderCallback\", \"QmtStockAccount\"]\n"
  },
  {
    "path": "src/zvt/broker/qmt/qmt_quote.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\n\nimport pandas as pd\nfrom xtquant import xtdata\n\nfrom zvt.api.kdata import get_recent_trade_dates\nfrom zvt.api.selector import get_entity_ids_by_filter\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.contract.api import decode_entity_id, df_to_db, get_db_session\nfrom zvt.domain import StockQuote, Stock, Stock1dKdata, StockQuoteLog, Stock1mQuote\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import (\n    to_date_time_str,\n    current_date,\n    to_pd_timestamp,\n    now_pd_timestamp,\n    date_time_by_interval,\n    now_timestamp_ms,\n    to_timestamp_ms,\n    date_and_time,\n    TIME_FORMAT_MINUTE,\n    is_same_date,\n)\n\n# https://dict.thinktrader.net/nativeApi/start_now.html?id=e2M5nZ\n\nlogger = logging.getLogger(__name__)\n\n\ndef _to_qmt_code(entity_id):\n    _, exchange, code = decode_entity_id(entity_id=entity_id)\n    return f\"{code}.{exchange.upper()}\"\n\n\ndef _to_zvt_entity_id(qmt_code):\n    code, exchange = qmt_code.split(\".\")\n    exchange = exchange.lower()\n    return f\"stock_{exchange}_{code}\"\n\n\ndef _to_qmt_dividend_type(adjust_type: AdjustType):\n    if adjust_type == AdjustType.qfq:\n        return \"front\"\n    elif adjust_type == AdjustType.hfq:\n        return \"back\"\n    else:\n        return \"none\"\n\n\ndef _qmt_instrument_detail_to_stock(stock_detail):\n    exchange = stock_detail[\"ExchangeID\"].lower()\n    code = stock_detail[\"InstrumentID\"]\n    name = stock_detail[\"InstrumentName\"]\n    list_date = to_pd_timestamp(stock_detail[\"OpenDate\"])\n    try:\n        end_date = to_pd_timestamp(stock_detail[\"ExpireDate\"])\n    except:\n        end_date = None\n\n    pre_close = stock_detail[\"PreClose\"]\n    limit_up_price = stock_detail[\"UpStopPrice\"]\n    limit_down_price = stock_detail[\"DownStopPrice\"]\n    float_volume = stock_detail[\"FloatVolume\"]\n    total_volume = stock_detail[\"TotalVolume\"]\n\n    entity_id = f\"stock_{exchange}_{code}\"\n\n    return {\n        \"id\": entity_id,\n        \"entity_id\": entity_id,\n        \"timestamp\": list_date,\n        \"entity_type\": \"stock\",\n        \"exchange\": exchange,\n        \"code\": code,\n        \"name\": name,\n        \"list_date\": list_date,\n        \"end_date\": end_date,\n        \"pre_close\": pre_close,\n        \"limit_up_price\": limit_up_price,\n        \"near_limit_up_price\": limit_up_price * 0.98,\n        \"limit_down_price\": limit_down_price,\n        \"float_volume\": float_volume,\n        \"total_volume\": total_volume,\n    }\n\n\ndef get_qmt_stocks(include_bj=True):\n    stock_list = xtdata.get_stock_list_in_sector(\"沪深A股\")\n\n    if include_bj:\n        bj_entity_ids = get_entity_ids_by_filter(\n            provider=\"em\", ignore_delist=True, ignore_st=False, entity_type=\"stock\", exchange=\"bj\"\n        )\n        bj_stock_list = map(lambda x: _to_qmt_code(x), bj_entity_ids)\n\n        stock_list += bj_stock_list\n    return stock_list\n\n\ndef _build_entity_list(qmt_stocks):\n    entity_list = []\n    for stock in qmt_stocks:\n        stock_detail = xtdata.get_instrument_detail(stock, False)\n        if stock_detail:\n            entity_list.append(_qmt_instrument_detail_to_stock(stock_detail))\n        else:\n            code, exchange = stock.split(\".\")\n            exchange = exchange.lower()\n            entity_id = f\"stock_{exchange}_{code}\"\n            # get from provider em\n            datas = Stock.query_data(provider=\"em\", entity_id=entity_id, return_type=\"dict\")\n            if datas:\n                entity = datas[0]\n            else:\n                entity = {\n                    \"id\": _to_zvt_entity_id(stock),\n                    \"entity_id\": entity_id,\n                    \"entity_type\": \"stock\",\n                    \"exchange\": exchange,\n                    \"code\": code,\n                    \"name\": \"未获取\",\n                }\n\n            # Do this in other task in days timely\n            # xtdata.download_financial_data(stock_list=[stock], table_list=[\"Capital\"])\n            capital_datas = xtdata.get_financial_data(\n                [stock],\n                table_list=[\"Capital\"],\n                report_type=\"report_time\",\n            )\n            df = capital_datas[stock][\"Capital\"]\n            if pd_is_not_null(df):\n                latest_data = df.iloc[-1]\n                entity[\"float_volume\"] = latest_data[\"circulating_capital\"]\n                entity[\"total_volume\"] = latest_data[\"total_capital\"]\n\n            tick = xtdata.get_full_tick(code_list=[stock])\n            if tick and tick[stock]:\n                if exchange == \"bj\":  # 北交所\n                    limit_up_price = tick[stock][\"lastClose\"] * 1.29\n                    limit_down_price = tick[stock][\"lastClose\"] * 0.71\n                elif code.startswith((\"30\", \"68\")):  # 创业板和科创板\n                    limit_up_price = tick[stock][\"lastClose\"] * 1.19\n                    limit_down_price = tick[stock][\"lastClose\"] * 0.81\n                else:\n                    limit_up_price = tick[stock][\"lastClose\"] * 1.1\n                    limit_down_price = tick[stock][\"lastClose\"] * 0.9\n                entity[\"limit_up_price\"] = round(limit_up_price, 2)\n                entity[\"near_limit_up_price\"] = round(limit_up_price * 0.98, 2)\n                entity[\"limit_down_price\"] = round(limit_down_price, 2)\n            entity_list.append(entity)\n\n    return pd.DataFrame.from_records(data=entity_list)\n\n\ndef get_entity_list(include_bj=True):\n    stocks = get_qmt_stocks(include_bj=include_bj)\n    return _build_entity_list(qmt_stocks=stocks)\n\n\ndef get_kdata(\n    entity_id,\n    start_timestamp,\n    end_timestamp,\n    level=IntervalLevel.LEVEL_1DAY,\n    adjust_type=AdjustType.qfq,\n    download_history=True,\n):\n    code = _to_qmt_code(entity_id=entity_id)\n    period = level.value\n    start_time = to_date_time_str(start_timestamp, fmt=\"YYYYMMDDHHmmss\")\n    end_time = to_date_time_str(end_timestamp, fmt=\"YYYYMMDDHHmmss\")\n    # download比较耗时，建议单独定时任务来做\n    if download_history:\n        print(f\"download from {start_time} to {end_time}\")\n        xtdata.download_history_data(stock_code=code, period=period, start_time=start_time, end_time=end_time)\n    records = xtdata.get_market_data(\n        stock_list=[code],\n        period=period,\n        start_time=to_date_time_str(start_timestamp, fmt=\"YYYYMMDDHHmmss\"),\n        end_time=to_date_time_str(end_timestamp, fmt=\"YYYYMMDDHHmmss\"),\n        dividend_type=_to_qmt_dividend_type(adjust_type=adjust_type),\n        fill_data=False,\n    )\n\n    dfs = []\n    for col in records:\n        df = records[col].T\n        df.columns = [col]\n        dfs.append(df)\n    df = pd.concat(dfs, axis=1)\n    df[\"volume\"] = df[\"volume\"] * 100\n    return df\n\n\ndef tick_to_quote(entity_df):\n    # def calculate_limit_up_amount(row):\n    #     if row[\"is_limit_up\"]:\n    #         return row[\"price\"] * row[\"bidVol\"][0] * 100\n    #     else:\n    #         return None\n    #\n    # def calculate_limit_down_amount(row):\n    #     if row[\"is_limit_down\"]:\n    #         return row[\"price\"] * row[\"askVol\"][0] * 100\n    #     else:\n    #         return None\n\n    def on_data(datas, stock_df=entity_df):\n        stock_finished = False\n        if not Stock.in_trading_time():\n            stock_finished = True\n        start_time = time.time()\n\n        time_tag = False\n        track_time = None\n\n        for code in datas:\n            tick_data = datas[code]\n            if \"timetag\" in tick_data:\n                time_tag = True\n                track_time = to_timestamp_ms(tick_data[\"timetag\"])\n            else:\n                track_time = datas[code][\"time\"]\n\n            delay = (now_timestamp_ms() - track_time) / 1000\n            logger.info(f\"stock {code} delay {delay} seconds\")\n            if delay < 60:\n                break\n            else:\n                logger.warning(f\"delay {delay} seconds, may need to restart this script or qmt client\")\n                break\n\n        tick_df = pd.DataFrame.from_records(data=[datas[code] for code in datas], index=list(datas.keys()))\n        if time_tag:\n            tick_df[\"time\"] = tick_df[\"timetag\"].apply(to_timestamp_ms)\n        # 过滤无效tick,一般是退市的\n        tick_df = tick_df[tick_df[\"lastPrice\"] != 0]\n        tick_df.index = tick_df.index.map(_to_zvt_entity_id)\n\n        # tick_df = tick_df[tick_df.index.isin(stock_df.index)]\n\n        stock_df = stock_df[~stock_df.index.duplicated(keep=\"first\")]  # 保留首次出现的行\n        tick_df = tick_df[~tick_df.index.duplicated(keep=\"first\")]\n        df = pd.concat(\n            [\n                stock_df,\n                tick_df,\n            ],\n            axis=1,\n            join=\"inner\",\n        )\n\n        df = df.rename(columns={\"lastPrice\": \"price\", \"amount\": \"turnover\"})\n        df[\"close\"] = df[\"price\"]\n\n        if stock_finished:\n            the_time = date_and_time(track_time, \"15:00\")\n            df[\"time\"] = to_timestamp_ms(the_time)\n\n        df[\"timestamp\"] = df[\"time\"].apply(to_pd_timestamp)\n\n        df[\"volume\"] = df[\"pvolume\"]\n        df[\"avg_price\"] = df[\"turnover\"] / df[\"volume\"]\n        # 换手率\n        # df[\"turnover_rate\"] = df[\"pvolume\"] / df[\"float_volume\"]\n        # 涨跌幅\n        df[\"change_pct\"] = (df[\"price\"] - df[\"lastClose\"]) / df[\"lastClose\"]\n        # # 盘口卖单金额\n        # df[\"ask_amount\"] = df.apply(\n        #     lambda row: np.sum(np.array(row[\"askPrice\"]) * (np.array(row[\"askVol\"]) * 100)), axis=1\n        # )\n        # # 盘口买单金额\n        # df[\"bid_amount\"] = df.apply(\n        #     lambda row: np.sum(np.array(row[\"bidPrice\"]) * (np.array(row[\"bidVol\"]) * 100)), axis=1\n        # )\n        # 涨停\n        df[\"is_limit_up\"] = (df[\"price\"] != 0) & (df[\"price\"] >= df[\"limit_up_price\"])\n        df[\"near_limit_up\"] = (df[\"price\"] != 0) & (df[\"price\"] >= df[\"near_limit_up_price\"])\n        # df[\"limit_up_amount\"] = df.apply(lambda row: calculate_limit_up_amount(row), axis=1)\n\n        # 跌停\n        df[\"is_limit_down\"] = (df[\"price\"] != 0) & (df[\"price\"] <= df[\"limit_down_price\"])\n        # df[\"limit_down_amount\"] = df.apply(lambda row: calculate_limit_down_amount(row), axis=1)\n\n        df[\"float_cap\"] = df[\"float_volume\"] * df[\"price\"]\n        df[\"total_cap\"] = df[\"total_volume\"] * df[\"price\"]\n\n        # 换手率\n        df[\"turnover_rate\"] = df[\"turnover\"] / df[\"float_cap\"]\n\n        df[\"provider\"] = \"qmt\"\n\n        # 实时行情统计，只保留最新\n        df[\"id\"] = df[[\"entity_id\", \"timestamp\"]].apply(\n            lambda se: \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"])), axis=1\n        )\n        df_to_db(df, data_schema=StockQuote, provider=\"qmt\", force_update=True, drop_duplicates=False)\n\n        # 历史记录\n        # df[\"id\"] = df[[\"entity_id\", \"timestamp\"]].apply(\n        #     lambda se: \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"], TIME_FORMAT_MINUTE2)), axis=1\n        # )\n        # df_to_db(df, data_schema=StockQuoteLog, provider=\"qmt\", force_update=False, drop_duplicates=False)\n\n        # 1分钟分时\n        df[\"id\"] = df[[\"entity_id\", \"timestamp\"]].apply(\n            lambda se: \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"], fmt=TIME_FORMAT_MINUTE)),\n            axis=1,\n        )\n        df_to_db(df, data_schema=Stock1mQuote, provider=\"qmt\", force_update=True, drop_duplicates=False)\n\n        # 日线行情\n        if stock_finished:\n            df[\"timestamp\"] = date_and_time(track_time, \"00:00\")\n            df[\"id\"] = df[[\"entity_id\", \"timestamp\"]].apply(\n                lambda se: \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"])), axis=1\n            )\n            df[\"level\"] = \"1d\"\n            df_to_db(df=df, data_schema=Stock1dKdata, provider=\"em\", force_update=True, drop_duplicates=False)\n\n        cost_time = time.time() - start_time\n        logger.info(f\"Quotes cost_time:{cost_time} for {len(datas.keys())} stocks\")\n\n        return track_time\n\n    return on_data\n\n\ndef clear_history_quote(target_date=current_date()):\n    session = get_db_session(\"qmt\", data_schema=StockQuote)\n    session.query(StockQuote).filter(StockQuote.timestamp < target_date).delete()\n    session.commit()\n    logger.info(f\"clear stock quote data before: {target_date}\")\n\n\ndef clear_history_1m_quote(target_date=current_date()):\n    session = get_db_session(\"qmt\", data_schema=StockQuote)\n    dates = get_recent_trade_dates(entity_type=\"stock\", target_date=target_date, days_count=5)\n    if dates:\n        start_date = dates[0]\n    else:\n        start_date = date_time_by_interval(target_date, -5)\n\n    session.query(Stock1mQuote).filter(Stock1mQuote.timestamp < start_date).delete()\n    session.commit()\n    logger.info(f\"clear stock 1m data before: {start_date}\")\n\n\ndef clear_history_quote_log(target_date):\n    dates = get_recent_trade_dates(entity_type=\"stock\", target_date=target_date, days_count=2)\n    if dates:\n        start_date = dates[0]\n    else:\n        start_date = date_time_by_interval(target_date, -2)\n\n    session = get_db_session(\"qmt\", data_schema=StockQuoteLog)\n    session.query(StockQuoteLog).filter(StockQuoteLog.timestamp < start_date).delete()\n    session.commit()\n    logger.info(f\"clear stock quote log data before: {target_date}\")\n\n\ndef record_stock_quote(subscribe=False):\n    clear_history_quote(target_date=current_date())\n    qmt_stocks = get_qmt_stocks()\n    entity_list = _build_entity_list(qmt_stocks=qmt_stocks)\n    entity_df = entity_list[\n        [\n            \"entity_id\",\n            \"code\",\n            \"name\",\n            \"limit_up_price\",\n            \"near_limit_up_price\",\n            \"limit_down_price\",\n            \"float_volume\",\n            \"total_volume\",\n        ]\n    ]\n    entity_df = entity_df.set_index(\"entity_id\", drop=False)\n    on_data_func = tick_to_quote(entity_df=entity_df)\n\n    if subscribe:\n        logger.info(f\"subscribe tick for {len(qmt_stocks)} stocks\")\n        sid = xtdata.subscribe_whole_quote(qmt_stocks, callback=on_data_func)\n\n        \"\"\"阻塞线程接收行情回调\"\"\"\n        import time\n\n        client = xtdata.get_client()\n        while True:\n            time.sleep(3)\n            if not client.is_connected():\n                raise Exception(\"行情服务连接断开\")\n            current_timestamp = now_pd_timestamp()\n            if not Stock.in_real_trading_time():\n                logger.info(f\"record tick finished at: {current_timestamp}\")\n                break\n        xtdata.unsubscribe_quote(sid)\n    else:\n        import time\n\n        first_time = True\n        last_time = False\n        while True:\n            if not first_time and Stock.in_trading_time() and not Stock.in_real_trading_time():\n                logger.info(\"Sleeping time......\")\n                time.sleep(30 * 1)\n                continue\n\n            if not Stock.in_trading_time():\n                logger.info(\"Not in trading time......\")\n                time.sleep(60 * 1)\n                last_time = True\n\n            datas = xtdata.get_full_tick(code_list=qmt_stocks)\n            track_time = on_data_func(datas=datas)\n            if first_time and not is_same_date(track_time, current_date()):\n                last_time = True\n\n            first_time = False\n\n            if last_time:\n                current_timestamp = now_pd_timestamp()\n                logger.info(f\"record tick finished at: {current_timestamp}\")\n                # clear_history_quote_log(target_date=current_date())\n                clear_history_1m_quote(target_date=current_date())\n                break\n\n\nif __name__ == \"__main__\":\n    from apscheduler.schedulers.background import BackgroundScheduler\n\n    sched = BackgroundScheduler()\n    record_stock_quote()\n    sched.add_job(func=record_stock_quote, trigger=\"cron\", hour=9, minute=22, day_of_week=\"mon-fri\")\n    sched.start()\n    sched._thread.join()\n\n# the __all__ is generated\n__all__ = [\n    \"get_qmt_stocks\",\n    \"get_entity_list\",\n    \"get_kdata\",\n    \"tick_to_quote\",\n    \"clear_history_quote\",\n]\n"
  },
  {
    "path": "src/zvt/common/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/common/query_models.py",
    "content": "# -*- coding: utf-8 -*-\nfrom datetime import datetime\nfrom enum import Enum\nfrom typing import Optional\n\nfrom pydantic import BaseModel, Field\n\n\nclass OrderByType(Enum):\n    asc = \"asc\"\n    desc = \"desc\"\n\n\nclass TimeUnit(Enum):\n    year = \"year\"\n    month = \"month\"\n    day = \"day\"\n    hour = \"hour\"\n    minute = \"minute\"\n    second = \"second\"\n\n\nclass AbsoluteTimeRange(BaseModel):\n    start_timestamp: datetime\n    end_timestamp: datetime\n\n\nclass RelativeTimeRage(BaseModel):\n    interval: int\n    time_unit: TimeUnit\n\n\nclass TimeRange(BaseModel):\n    absolute_time_range: Optional[AbsoluteTimeRange] = Field(default=None)\n    relative_time_range: Optional[RelativeTimeRage] = Field(default=None)\n\n\n# the __all__ is generated\n__all__ = [\"OrderByType\", \"TimeUnit\", \"AbsoluteTimeRange\", \"RelativeTimeRage\", \"TimeRange\"]\n"
  },
  {
    "path": "src/zvt/common/trading_models.py",
    "content": "# -*- coding: utf-8 -*-\nfrom enum import Enum\nfrom typing import List, Optional\n\nfrom pydantic import BaseModel, Field\n\n\nclass PositionType(Enum):\n    # 按整体仓位算\n    normal = \"normal\"\n\n    # 不管整体仓位多少\n    # 按现金算\n    cash = \"cash\"\n\n\nclass BuyParameter(BaseModel):\n    entity_ids: List[str]\n    position_type: PositionType = Field(default=PositionType.normal)\n    position_pct: Optional[float] = Field(default=None)\n    weights: Optional[List[float]] = Field(default=None)\n    money_to_use: Optional[float] = Field(default=None)\n\n\nclass SellParameter(BaseModel):\n    entity_ids: List[str]\n    sell_pcts: Optional[List[float]] = Field(default=None)\n\n\nclass TradingResult(BaseModel):\n    success_entity_ids: Optional[List[str]] = Field(default=None)\n    failed_entity_ids: Optional[List[str]] = Field(default=None)\n\n\n# the __all__ is generated\n__all__ = [\"PositionType\", \"BuyParameter\", \"SellParameter\", \"TradingResult\"]\n"
  },
  {
    "path": "src/zvt/config.json",
    "content": "{\n  \"jq_username\": \"\",\n  \"jq_password\": \"\",\n  \"http_proxy\": \"127.0.0.1:1087\",\n  \"https_proxy\": \"127.0.0.1:1087\",\n  \"smtp_host\": \"smtpdm.aliyun.com\",\n  \"smtp_port\": \"80\",\n  \"email_username\": \"\",\n  \"email_password\": \"\",\n  \"wechat_app_id\": \"\",\n  \"wechat_app_secrect\": \"\",\n  \"qiye_wechat_bot_token\": \"\",\n  \"qmt_mini_data_path\": \"D:\\\\qmt\\\\userdata_mini\",\n  \"qmt_account_id\": \"\",\n  \"moonshot_api_key\": \"\",\n  \"qwen_api_key\": \"\",\n  \"em_header\": \"\",\n  \"storage\": {\n    \"base_path\": null,\n    \"path_template\": null,\n    \"storage_routes\": {},\n    \"schema_providers\": {\n      \"zvt_info\": [\n        \"zvt\"\n      ],\n      \"trader_info\": [\n        \"zvt\"\n      ],\n      \"stock_tags\": [\n        \"zvt\"\n      ],\n      \"stock_trading\": [\n        \"zvt\"\n      ],\n      \"stock_1d_ma_factor\": [\n        \"zvt\"\n      ],\n      \"stock_1d_ma_stats_factor\": [\n        \"zvt\"\n      ],\n      \"stock_1d_zen_factor\": [\n        \"zvt\"\n      ],\n      \"stock_1wk_zen_factor\": [\n        \"zvt\"\n      ],\n      \"index_1d_zen_factor\": [\n        \"zvt\"\n      ],\n      \"top_stocks\": [\n        \"zvt\"\n      ],\n      \"stock_quote\": [\n        \"qmt\"\n      ],\n      \"stock_quote_log\": [\n        \"qmt\"\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/zvt/consts.py",
    "content": "# -*- coding: utf-8 -*-\nimport os\nfrom pathlib import Path\n\n# zvt home dir\nZVT_HOME = os.environ.get(\"ZVT_HOME\")\nif not ZVT_HOME:\n    ZVT_HOME = os.path.abspath(os.path.join(Path.home(), \"zvt-home\"))\n\n# data for testing\nZVT_TEST_HOME = os.path.abspath(os.path.join(Path.home(), \"zvt-test-home\"))\nZVT_TEST_ZIP_DATA_PATH = os.path.join(ZVT_TEST_HOME, \"data.zip\")\nZVT_TEST_DATA_PATH = os.path.join(ZVT_TEST_HOME, \"data\")\n\nDATA_SAMPLE_ZIP_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), \"samples\", \"data.zip\"))\n\n# ****** setting for stocks ****** #\nSAMPLE_STOCK_CODES = [\"000001\", \"000002\"]\n\n# 沪深300，证券，中证500，上证50，创业板，军工,传媒,资源\nSAMPLE_ETF_CODES = [\"510300\", \"512880\", \"510500\", \"510050\", \"159915\", \"512660\", \"512980\", \"510410\"]\n\n# 上证指数 上证50 沪深300 中证500 中证1000  科创50\n# 深证成指(399001) 创业板指(399006) 国证成长（399370）国证价值（399371）国证基金(399379) 国证ETF(399380)\nIMPORTANT_INDEX = [\n    \"000001\",\n    \"000016\",\n    \"000300\",\n    \"000905\",\n    \"000852\",\n    \"000688\",\n    \"399001\",\n    \"399006\",\n    \"399370\",\n    \"399371\",\n    \"399379\",\n    \"399380\",\n]\n"
  },
  {
    "path": "src/zvt/contract/__init__.py",
    "content": "# -*- coding: utf-8 -*-\nfrom enum import Enum\n\n\nclass IntervalLevel(Enum):\n    \"\"\"\n    Repeated fixed time interval, e.g, 5m, 1d.\n    \"\"\"\n\n    #: level l2 quote\n    LEVEL_L2_QUOTE = \"l2quote\"\n    #: level tick\n    LEVEL_TICK = \"tick\"\n    #: 1 minute\n    LEVEL_1MIN = \"1m\"\n    #: 5 minutes\n    LEVEL_5MIN = \"5m\"\n    #: 15 minutes\n    LEVEL_15MIN = \"15m\"\n    #: 30 minutes\n    LEVEL_30MIN = \"30m\"\n    #: 1 hour\n    LEVEL_1HOUR = \"1h\"\n    #: 4 hours\n    LEVEL_4HOUR = \"4h\"\n    #: 1 day\n    LEVEL_1DAY = \"1d\"\n    #: 1 week\n    LEVEL_1WEEK = \"1wk\"\n    #: 1 month\n    LEVEL_1MON = \"1mon\"\n\n    def to_pd_freq(self):\n        if self == IntervalLevel.LEVEL_1MIN:\n            return \"1min\"\n        if self == IntervalLevel.LEVEL_5MIN:\n            return \"5min\"\n        if self == IntervalLevel.LEVEL_15MIN:\n            return \"15min\"\n        if self == IntervalLevel.LEVEL_30MIN:\n            return \"30min\"\n        if self == IntervalLevel.LEVEL_1HOUR:\n            return \"1H\"\n        if self == IntervalLevel.LEVEL_4HOUR:\n            return \"4H\"\n        if self >= IntervalLevel.LEVEL_1DAY:\n            return \"1D\"\n\n    def floor_timestamp(self, pd_timestamp):\n        if self == IntervalLevel.LEVEL_1MIN:\n            return pd_timestamp.floor(\"1min\")\n        if self == IntervalLevel.LEVEL_5MIN:\n            return pd_timestamp.floor(\"5min\")\n        if self == IntervalLevel.LEVEL_15MIN:\n            return pd_timestamp.floor(\"15min\")\n        if self == IntervalLevel.LEVEL_30MIN:\n            return pd_timestamp.floor(\"30min\")\n        if self == IntervalLevel.LEVEL_1HOUR:\n            return pd_timestamp.floor(\"1h\")\n        if self == IntervalLevel.LEVEL_4HOUR:\n            return pd_timestamp.floor(\"4h\")\n        if self == IntervalLevel.LEVEL_1DAY:\n            return pd_timestamp.floor(\"1d\")\n\n    def to_minute(self):\n        return int(self.to_second() / 60)\n\n    def to_second(self):\n        return int(self.to_ms() / 1000)\n\n    def to_ms(self):\n        \"\"\"\n        To seconds count in the interval\n\n        :return: seconds count in the interval\n        \"\"\"\n        #: we treat tick intervals is 5s, you could change it\n        if self == IntervalLevel.LEVEL_TICK:\n            return 5 * 1000\n        if self == IntervalLevel.LEVEL_1MIN:\n            return 60 * 1000\n        if self == IntervalLevel.LEVEL_5MIN:\n            return 5 * 60 * 1000\n        if self == IntervalLevel.LEVEL_15MIN:\n            return 15 * 60 * 1000\n        if self == IntervalLevel.LEVEL_30MIN:\n            return 30 * 60 * 1000\n        if self == IntervalLevel.LEVEL_1HOUR:\n            return 60 * 60 * 1000\n        if self == IntervalLevel.LEVEL_4HOUR:\n            return 4 * 60 * 60 * 1000\n        if self == IntervalLevel.LEVEL_1DAY:\n            return 24 * 60 * 60 * 1000\n        if self == IntervalLevel.LEVEL_1WEEK:\n            return 7 * 24 * 60 * 60 * 1000\n        if self == IntervalLevel.LEVEL_1MON:\n            return 31 * 7 * 24 * 60 * 60 * 1000\n\n    def __ge__(self, other):\n        if self.__class__ is other.__class__:\n            return self.to_ms() >= other.to_ms()\n        return NotImplemented\n\n    def __gt__(self, other):\n\n        if self.__class__ is other.__class__:\n            return self.to_ms() > other.to_ms()\n        return NotImplemented\n\n    def __le__(self, other):\n        if self.__class__ is other.__class__:\n            return self.to_ms() <= other.to_ms()\n        return NotImplemented\n\n    def __lt__(self, other):\n        if self.__class__ is other.__class__:\n            return self.to_ms() < other.to_ms()\n        return NotImplemented\n\n\nclass AdjustType(Enum):\n    \"\"\"\n    split-adjusted type for :class:`~.zvt.contract.schema.TradableEntity` quotes\n\n    \"\"\"\n\n    #: not adjusted\n    #: 不复权\n    bfq = \"bfq\"\n    #: pre adjusted\n    #: 前复权\n    qfq = \"qfq\"\n    #: post adjusted\n    #: 后复权\n    hfq = \"hfq\"\n\n\nclass ActorType(Enum):\n    #: 个人\n    individual = \"individual\"\n    #: 公募基金\n    raised_fund = \"raised_fund\"\n    #: 社保\n    social_security = \"social_security\"\n    #: 保险\n    insurance = \"insurance\"\n    #: 外资\n    qfii = \"qfii\"\n    #: 信托\n    trust = \"trust\"\n    #: 券商\n    broker = \"qmt\"\n    #: 私募\n    private_equity = \"private_equity\"\n    #: 公司(可能包括私募)\n    corporation = \"corporation\"\n\n\nclass TradableType(Enum):\n    #: A股(中国)\n    #: China stock\n    stock = \"stock\"\n    #: 可转债(中国)\n    #: China convertible Bond\n    cbond = \"cbond\"\n    #: A股指数(中国)\n    #: China index\n    index = \"index\"\n    #: A股板块(中国)\n    #: China stock block\n    block = \"block\"\n    #: 美股\n    #: USA stock\n    stockus = \"stockus\"\n    #: 美股指数\n    #: USA index\n    indexus = \"indexus\"\n    #: USA block\n    blockus = \"blockus\"\n    #: 港股\n    #: Hongkong Stock\n    stockhk = \"stockhk\"\n    #: 港股指数\n    #: Hongkong Index\n    indexhk = \"indexhk\"\n    #: 期货(中国)\n    #: China future\n    future = \"future\"\n    #: 数字货币\n    #: Cryptocurrency\n    coin = \"coin\"\n    #: 期权(中国)\n    #: China option\n    option = \"option\"\n    #: 基金(中国)\n    #: China fund\n    fund = \"fund\"\n    #: 货币汇率\n    #: currency exchange rate\n    currency = \"currency\"\n\n\nclass Exchange(Enum):\n    #: 上证交易所\n    sh = \"sh\"\n    #: 深证交易所\n    sz = \"sz\"\n    #: 北交所\n    bj = \"bj\"\n\n    #: 对于中国的非交易所的 标的\n    cn = \"cn\"\n    #: 对于美国的非交易所的 标的\n    us = \"us\"\n\n    #: 纳斯达克\n    nasdaq = \"nasdaq\"\n\n    #: 纽交所\n    nyse = \"nyse\"\n\n    #: 港交所\n    hk = \"hk\"\n\n    #: 数字货币\n    binance = \"binance\"\n    huobipro = \"huobipro\"\n\n    #: 上海期货交易所\n    shfe = \"shfe\"\n    #: 大连商品交易所\n    dce = \"dce\"\n    #: 郑州商品交易所\n    czce = \"czce\"\n    #: 中国金融期货交易所\n    cffex = \"cffex\"\n    #: 上海国际能源交易中心\n    ine = \"ine\"\n\n    #: 广州期货所\n    gfex = \"gfex\"\n\n    #: 外汇交易所(虚拟)\n    #: currency exchange(virtual)\n    forex = \"forex\"\n    #: 人民币中间价\n\n\ntradable_type_map_exchanges = {\n    TradableType.block: [Exchange.cn],\n    TradableType.index: [Exchange.sh, Exchange.sz],\n    TradableType.stock: [Exchange.sh, Exchange.sz, Exchange.bj],\n    TradableType.cbond: [Exchange.sh, Exchange.sz],\n    TradableType.stockhk: [Exchange.hk],\n    TradableType.indexhk: [Exchange.hk],\n    TradableType.stockus: [Exchange.nasdaq, Exchange.nyse],\n    TradableType.blockus: [Exchange.us],\n    TradableType.indexus: [Exchange.us],\n    TradableType.future: [Exchange.shfe, Exchange.dce, Exchange.czce, Exchange.cffex, Exchange.ine],\n    TradableType.coin: [Exchange.binance, Exchange.huobipro],\n    TradableType.currency: [Exchange.forex],\n}\n\n\ndef get_entity_exchanges(entity_type):\n    entity_type = TradableType(entity_type)\n    return tradable_type_map_exchanges.get(entity_type)\n\n\nfrom .context import zvt_context\n\nzvt_context = zvt_context\n\n\n# the __all__ is generated\n__all__ = [\"IntervalLevel\", \"AdjustType\", \"ActorType\", \"TradableType\", \"Exchange\", \"get_entity_exchanges\"]\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule schema\nfrom .schema import *\nfrom .schema import __all__ as _schema_all\n\n__all__ += _schema_all\n"
  },
  {
    "path": "src/zvt/contract/api.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport platform\nfrom typing import List, Union, Type\n\nimport pandas as pd\nfrom sqlalchemy import func, exists, and_\nfrom sqlalchemy.engine import Engine\nfrom sqlalchemy.ext.declarative import DeclarativeMeta\nfrom sqlalchemy.orm import Query\nfrom sqlalchemy.orm import sessionmaker, Session\nfrom sqlalchemy.sql.expression import text\n\nfrom zvt import zvt_env\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract import zvt_context\nfrom zvt.contract.schema import Mixin, TradableEntity\nfrom zvt.contract.storage import get_storage_backend\nfrom zvt.contract.route_registry import get_route_registry\nfrom zvt.contract.register import ensure_schema_tables_and_indexes\n\n_initialized_storage_ids = set()\n\n\ndef _storage_backend():\n    return zvt_context.storage_backend or get_storage_backend()\n\n\ndef _route_registry():\n    return zvt_context.route_registry or get_route_registry()\nfrom zvt.utils.pd_utils import pd_is_not_null, index_df\nfrom zvt.utils.time_utils import to_pd_timestamp\n\nlogger = logging.getLogger(__name__)\n\n\ndef _get_db_name(data_schema: DeclarativeMeta) -> str:\n    \"\"\"\n    get db name of the domain schema\n\n    :param data_schema: the data schema\n    :return: db name\n    \"\"\"\n    for db_name, base in zvt_context.dbname_map_base.items():\n        if issubclass(data_schema, base):\n            return db_name\n\n\ndef get_db_engine(\n    provider: str, db_name: str = None, data_schema: object = None, data_path: str = None\n) -> Engine:\n    \"\"\"\n    get db engine from (provider,db_name) or (provider,data_schema).\n    Creates tables and indexes on first use (lazy init).\n    \"\"\"\n    if data_schema:\n        db_name = _get_db_name(data_schema=data_schema)\n\n    if data_path is None:\n        data_path = zvt_env.get(\"data_path\", \".\")\n\n    storage_id = _route_registry().get_storage_id(provider, db_name)\n    engine = _storage_backend().get_engine(storage_id, data_path)\n\n    if storage_id not in _initialized_storage_ids:\n        schema_base = zvt_context.dbname_map_base.get(db_name)\n        if schema_base:\n            ensure_schema_tables_and_indexes(engine, schema_base, db_name)\n        _initialized_storage_ids.add(storage_id)\n\n    return engine\n\n\ndef _ensure_schema_providers_loaded():\n    \"\"\"Populate zvt_context from config schema_providers for schemas without Recorders.\"\"\"\n    if getattr(_ensure_schema_providers_loaded, \"_done\", False):\n        return\n    try:\n        from zvt.contract.schema import _get_schema_providers\n        schema_providers = _get_schema_providers()\n        for db_name, providers in schema_providers.items():\n            for p in providers:\n                if p not in zvt_context.providers:\n                    zvt_context.providers.append(p)\n                if p not in zvt_context.provider_map_dbnames:\n                    zvt_context.provider_map_dbnames[p] = []\n                if db_name not in zvt_context.provider_map_dbnames[p]:\n                    zvt_context.provider_map_dbnames[p].append(db_name)\n    except Exception:\n        pass\n    _ensure_schema_providers_loaded._done = True\n\n\ndef get_providers() -> List[str]:\n    _ensure_schema_providers_loaded()\n    return zvt_context.providers\n\n\ndef get_schemas(provider: str) -> List[DeclarativeMeta]:\n    _ensure_schema_providers_loaded()\n    schemas = []\n    for provider1, dbs in zvt_context.provider_map_dbnames.items():\n        if provider == provider1:\n            for dbname in dbs:\n                schemas1 = zvt_context.dbname_map_schemas.get(dbname)\n                if schemas1:\n                    schemas += schemas1\n    return schemas\n\n\ndef get_db_session(provider: str, db_name: str = None, data_schema: object = None, force_new: bool = False) -> Session:\n    \"\"\"\n    get db session from (provider,db_name) or (provider,data_schema)\n\n    :param provider: data provider\n    :param db_name: db name\n    :param data_schema: data schema\n    :param force_new: True for new session, otherwise use global session\n    :return: db session\n    \"\"\"\n    _ensure_schema_providers_loaded()\n    if data_schema:\n        db_name = _get_db_name(data_schema=data_schema)\n\n    data_path = zvt_env.get(\"data_path\", \".\")\n    storage_id = _route_registry().get_storage_id(provider, db_name)\n    engine = get_db_engine(provider=provider, db_name=db_name)\n    session_fac = _storage_backend().get_session_factory(storage_id, data_path)\n    session_fac.configure(bind=engine)\n\n    if force_new:\n        return session_fac()\n\n    session_key = storage_id\n    session = zvt_context.sessions.get(session_key)\n    # FIXME: should not maintain global session\n    if not session:\n        session = session_fac()\n        zvt_context.sessions[session_key] = session\n    return session\n\n\ndef get_db_session_factory(provider: str, db_name: str = None, data_schema: object = None):\n    \"\"\"\n    get db session factory from (provider,db_name) or (provider,data_schema)\n    \"\"\"\n    if data_schema:\n        db_name = _get_db_name(data_schema=data_schema)\n\n    engine = get_db_engine(provider=provider, db_name=db_name)\n    data_path = zvt_env.get(\"data_path\", \".\")\n    storage_id = _route_registry().get_storage_id(provider, db_name)\n    session_fac = _storage_backend().get_session_factory(storage_id, data_path)\n    session_fac.configure(bind=engine)\n    return session_fac\n\n\nDBSession = get_db_session_factory\n\n\ndef get_entity_schema(entity_type: str) -> Type[TradableEntity]:\n    \"\"\"\n    get entity schema from name\n\n    :param entity_type: entity type, e.g. stock, stockus.\n    :return: the Schema of the entity\n    \"\"\"\n    return zvt_context.tradable_schema_map[entity_type]\n\n\ndef get_schema_by_name(name: str) -> DeclarativeMeta:\n    \"\"\"\n    get domain schema by the name\n\n    :param name: schema name\n    :return: schema\n    \"\"\"\n    for schema in zvt_context.schemas:\n        if schema.__name__ == name:\n            return schema\n\n\ndef get_schema_columns(schema: DeclarativeMeta) -> List[str]:\n    \"\"\"\n    get all columns of the domain schema\n\n    :param schema: data schema\n    :return: columns of the schema\n    \"\"\"\n    return schema.__table__.columns.keys()\n\n\ndef common_filter(\n    query: Query,\n    data_schema,\n    start_timestamp=None,\n    end_timestamp=None,\n    filters=None,\n    order=None,\n    limit=None,\n    distinct=None,\n    time_field=\"timestamp\",\n):\n    \"\"\"\n    build filter by the arguments\n\n    :param query: sql query\n    :param data_schema: data schema\n    :param start_timestamp: start timestamp\n    :param end_timestamp: end timestamp\n    :param filters: sql filters\n    :param order: sql order\n    :param limit: sql limit size\n    :param time_field: time field in columns\n    :return: result query\n    \"\"\"\n    assert data_schema is not None\n    time_col = eval(\"data_schema.{}\".format(time_field))\n\n    if start_timestamp:\n        query = query.filter(time_col >= to_pd_timestamp(start_timestamp))\n    if end_timestamp:\n        query = query.filter(time_col <= to_pd_timestamp(end_timestamp))\n\n    if filters:\n        for filter in filters:\n            query = query.filter(filter)\n    if order is not None:\n        query = query.order_by(order)\n    else:\n        query = query.order_by(time_col.asc())\n    if limit:\n        query = query.limit(limit)\n    if distinct:\n        query = query.distinct(distinct)\n\n    return query\n\n\ndef del_data(data_schema: Type[Mixin], filters: List = None, provider=None):\n    \"\"\"\n    delete data by filters\n\n    :param data_schema: data schema\n    :param filters: filters\n    :param provider: data provider\n    \"\"\"\n    if not provider:\n        provider = data_schema.get_providers()[0]\n\n    session = get_db_session(provider=provider, data_schema=data_schema)\n    query = session.query(data_schema)\n    if filters:\n        for f in filters:\n            query = query.filter(f)\n    query.delete()\n    session.commit()\n\n\ndef get_by_id(data_schema, id: str, provider: str = None, session: Session = None):\n    \"\"\"\n    get one record by id from data schema\n\n    :param data_schema: data schema\n    :param id: the record id\n    :param provider: data provider\n    :param session: db session\n    :return: the record of the id\n    \"\"\"\n    _ensure_schema_providers_loaded()\n    providers = data_schema.get_providers()\n    if not providers:\n        raise ValueError(f\"no provider registered for: {data_schema}\")\n    if not provider:\n        provider = providers[0]\n\n    if not session:\n        session = get_db_session(provider=provider, data_schema=data_schema)\n\n    return session.query(data_schema).get(id)\n\n\ndef _row2dict(row):\n    d = {}\n    for column in row.__table__.columns:\n        d[column.name] = getattr(row, column.name)\n    return d\n\n\ndef get_data(\n    data_schema: Type[Mixin],\n    ids: List[str] = None,\n    entity_ids: List[str] = None,\n    entity_id: str = None,\n    codes: List[str] = None,\n    code: str = None,\n    level: Union[IntervalLevel, str] = None,\n    provider: str = None,\n    columns: List = None,\n    col_label: dict = None,\n    return_type: str = \"df\",\n    start_timestamp: Union[pd.Timestamp, str] = None,\n    end_timestamp: Union[pd.Timestamp, str] = None,\n    filters: List = None,\n    session: Session = None,\n    order=None,\n    limit: int = None,\n    distinct=None,\n    index: Union[str, list] = None,\n    drop_index_col=False,\n    time_field: str = \"timestamp\",\n):\n    \"\"\"\n    query data by the arguments\n\n    :param data_schema:\n    :param ids:\n    :param entity_ids:\n    :param entity_id:\n    :param codes:\n    :param code:\n    :param level:\n    :param provider:\n    :param columns:\n    :param col_label: dict with key(column), value(label)\n    :param return_type: df, domain or dict. default is df\n    :param start_timestamp:\n    :param end_timestamp:\n    :param filters:\n    :param session:\n    :param order:\n    :param limit:\n    :param index: index field name, str for single index, str list for multiple index\n    :param drop_index_col: whether drop the col if it's in index, default False\n    :param time_field:\n    :return: results basing on return_type.\n    \"\"\"\n    _ensure_schema_providers_loaded()\n    providers = data_schema.get_providers()\n    if not providers:\n        raise ValueError(f\"no provider registered for: {data_schema}\")\n    if not provider:\n        provider = providers[0]\n\n    if not session:\n        session = get_db_session(provider=provider, data_schema=data_schema)\n\n    time_col = eval(\"data_schema.{}\".format(time_field))\n\n    if columns:\n        # support str\n        for i, col in enumerate(columns):\n            if isinstance(col, str):\n                columns[i] = eval(\"data_schema.{}\".format(col))\n\n        # make sure get timestamp\n        if time_col not in columns:\n            columns.append(time_col)\n\n        if col_label:\n            columns_ = []\n            for col in columns:\n                if col.name in col_label:\n                    columns_.append(col.label(col_label.get(col.name)))\n                else:\n                    columns_.append(col)\n            columns = columns_\n\n        query = session.query(*columns)\n    else:\n        query = session.query(data_schema)\n\n    if entity_id:\n        query = query.filter(data_schema.entity_id == entity_id)\n    if entity_ids:\n        query = query.filter(data_schema.entity_id.in_(entity_ids))\n    if code:\n        query = query.filter(data_schema.code == code)\n    if codes:\n        query = query.filter(data_schema.code.in_(codes))\n    if ids:\n        query = query.filter(data_schema.id.in_(ids))\n\n    # we always store different level in different schema,the level param is not useful now\n    if level:\n        try:\n            #: some schema has no level,just ignore it\n            data_schema.level\n            if type(level) == IntervalLevel:\n                level = level.value\n            query = query.filter(data_schema.level == level)\n        except Exception as e:\n            pass\n\n    query = common_filter(\n        query,\n        data_schema=data_schema,\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        filters=filters,\n        order=order,\n        limit=limit,\n        distinct=distinct,\n        time_field=time_field,\n    )\n\n    if return_type == \"df\":\n        df = pd.read_sql(query.statement, query.session.bind)\n        if pd_is_not_null(df):\n            if index:\n                df = index_df(df, index=index, drop=drop_index_col, time_field=time_field)\n        return df\n    elif return_type == \"domain\":\n        return query.all()\n    elif return_type == \"dict\":\n        domains = query.all()\n        return [_row2dict(item) for item in domains]\n    elif return_type == \"select\":\n        return query.selectable\n\n\ndef data_exist(session, schema, id):\n    \"\"\"\n    whether exist data of the id\n\n    :param session:\n    :param schema:\n    :param id:\n    :return:\n    \"\"\"\n    return session.query(exists().where(and_(schema.id == id))).scalar()\n\n\ndef get_data_count(data_schema, filters=None, provider=None, session=None):\n    \"\"\"\n    get record count basing on the filters\n\n    :param data_schema:\n    :param filters:\n    :param session:\n    :return:\n    \"\"\"\n    if not session:\n        session = get_db_session(provider=provider, data_schema=data_schema)\n\n    query = session.query(data_schema)\n    if filters:\n        for filter in filters:\n            query = query.filter(filter)\n\n    count_q = query.statement.with_only_columns(func.count(data_schema.id)).order_by(None)\n    count = session.execute(count_q).scalar()\n    return count\n\n\ndef get_group(provider, data_schema, column, group_func=func.count, session=None):\n    if not session:\n        session = get_db_session(provider=provider, data_schema=data_schema)\n    if group_func:\n        query = session.query(column, group_func(column)).group_by(column)\n    else:\n        query = session.query(column).group_by(column)\n    df = pd.read_sql(query.statement, session.bind)\n    return df\n\n\ndef decode_entity_id(entity_id: str):\n    \"\"\"\n    decode entity id to entity_type, exchange, code\n\n    :param entity_id:\n    :return: tuple with format (entity_type, exchange, code)\n    \"\"\"\n    result = entity_id.split(\"_\")\n    entity_type = result[0]\n    exchange = result[1]\n    code = \"\".join(result[2:])\n    return entity_type, exchange, code\n\n\ndef get_entity_type(entity_id: str):\n    \"\"\"\n    get entity type by entity id\n\n    :param entity_id:\n    :return: entity type\n    \"\"\"\n    entity_type, _, _ = decode_entity_id(entity_id)\n    return entity_type\n\n\ndef get_entity_exchange(entity_id: str):\n    \"\"\"\n    get exchange by entity id\n\n    :param entity_id:\n    :return: exchange\n    \"\"\"\n    _, exchange, _ = decode_entity_id(entity_id)\n    return exchange\n\n\ndef get_entity_code(entity_id: str):\n    \"\"\"\n    get code by entity id\n\n    :param entity_id:\n    :return: code\n    \"\"\"\n    _, _, code = decode_entity_id(entity_id)\n    return code\n\n\ndef df_to_db(\n    df: pd.DataFrame,\n    data_schema: DeclarativeMeta,\n    provider: str,\n    force_update: bool = False,\n    sub_size: int = 8000,\n    drop_duplicates: bool = True,\n    dtype=None,\n    session=None,\n    need_check=True,\n) -> object:\n    \"\"\"\n    store the df to db\n\n    :param df: data with columns of the schema\n    :param data_schema: data schema\n    :param provider: data provider\n    :param force_update: whether update the data with id existed\n    :param sub_size: update batch size\n    :param drop_duplicates: whether drop duplicates\n    :return:\n    \"\"\"\n    if not pd_is_not_null(df):\n        return 0\n\n    if drop_duplicates and df.duplicated(subset=\"id\").any():\n        logger.warning(f\"remove duplicated:{df[df.duplicated()]}\")\n        df = df.drop_duplicates(subset=\"id\", keep=\"last\")\n\n    schema_cols = get_schema_columns(data_schema)\n    cols = set(df.columns.tolist()) & set(schema_cols)\n\n    if not cols:\n        print(\"wrong cols\")\n        return 0\n\n    cols = list(cols)\n    df = df[cols]\n\n    size = len(df)\n\n    if platform.system() == \"Windows\":\n        sub_size = 900\n\n    if size >= sub_size:\n        step_size = int(size / sub_size)\n        if size % sub_size:\n            step_size = step_size + 1\n    else:\n        step_size = 1\n\n    saved = 0\n\n    if not session:\n        session = get_db_session(provider=provider, data_schema=data_schema)\n\n    for step in range(step_size):\n        df_current = df.iloc[sub_size * step : sub_size * (step + 1)]\n\n        if need_check:\n            if force_update:\n                ids = df_current[\"id\"].tolist()\n                if len(ids) == 1:\n                    sql = text(f'delete from `{data_schema.__tablename__}` where id = \"{ids[0]}\"')\n                else:\n                    sql = text(f\"delete from `{data_schema.__tablename__}` where id in {tuple(ids)}\")\n\n                session.execute(sql)\n            else:\n                current = get_data(\n                    session=session,\n                    data_schema=data_schema,\n                    columns=[data_schema.id],\n                    provider=provider,\n                    ids=df_current[\"id\"].tolist(),\n                )\n                if pd_is_not_null(current):\n                    df_current = df_current[~df_current[\"id\"].isin(current[\"id\"])]\n\n        if pd_is_not_null(df_current):\n            saved = saved + len(df_current)\n            df_current.to_sql(\n                data_schema.__tablename__, session.connection(), index=False, if_exists=\"append\", dtype=dtype\n            )\n        session.commit()\n    return saved\n\n\ndef get_entities(\n    entity_schema: Type[TradableEntity] = None,\n    entity_type: str = None,\n    exchanges: List[str] = None,\n    ids: List[str] = None,\n    entity_ids: List[str] = None,\n    entity_id: str = None,\n    codes: List[str] = None,\n    code: str = None,\n    provider: str = None,\n    columns: List = None,\n    col_label: dict = None,\n    return_type: str = \"df\",\n    start_timestamp: Union[pd.Timestamp, str] = None,\n    end_timestamp: Union[pd.Timestamp, str] = None,\n    filters: List = None,\n    session: Session = None,\n    order=None,\n    limit: int = None,\n    index: Union[str, list] = \"code\",\n) -> List:\n    \"\"\"\n    get entities by the arguments\n\n    :param entity_schema:\n    :param entity_type:\n    :param exchanges:\n    :param ids:\n    :param entity_ids:\n    :param entity_id:\n    :param codes:\n    :param code:\n    :param provider:\n    :param columns:\n    :param col_label:\n    :param return_type:\n    :param start_timestamp:\n    :param end_timestamp:\n    :param filters:\n    :param session:\n    :param order:\n    :param limit:\n    :param index:\n    :return:\n    \"\"\"\n    if not entity_schema:\n        entity_schema = zvt_context.tradable_schema_map[entity_type]\n\n    if not provider:\n        provider = entity_schema.get_providers()[0]\n\n    if not order:\n        order = entity_schema.code.asc()\n\n    if exchanges:\n        if filters:\n            filters.append(entity_schema.exchange.in_(exchanges))\n        else:\n            filters = [entity_schema.exchange.in_(exchanges)]\n\n    return get_data(\n        data_schema=entity_schema,\n        ids=ids,\n        entity_ids=entity_ids,\n        entity_id=entity_id,\n        codes=codes,\n        code=code,\n        level=None,\n        provider=provider,\n        columns=columns,\n        col_label=col_label,\n        return_type=return_type,\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        filters=filters,\n        session=session,\n        order=order,\n        limit=limit,\n        index=index,\n    )\n\n\ndef get_entity_ids(\n    entity_type=\"stock\",\n    entity_schema: TradableEntity = None,\n    exchanges=None,\n    codes=None,\n    provider=None,\n    filters=None,\n    entity_ids=None,\n):\n    \"\"\"\n    get entity ids by the arguments\n\n    :param entity_type:\n    :param entity_schema:\n    :param exchanges:\n    :param codes:\n    :param provider:\n    :param filters:\n    :param entity_ids:\n    :return:\n    \"\"\"\n    df = get_entities(\n        entity_type=entity_type,\n        entity_schema=entity_schema,\n        exchanges=exchanges,\n        codes=codes,\n        provider=provider,\n        filters=filters,\n        entity_ids=entity_ids,\n    )\n    if pd_is_not_null(df):\n        return df[\"entity_id\"].to_list()\n    return None\n\n\nif __name__ == \"__main__\":\n    print(get_entities(entity_type=\"block\"))\n\n\n# the __all__ is generated\n__all__ = [\n    \"get_db_engine\",\n    \"get_providers\",\n    \"get_schemas\",\n    \"get_db_session\",\n    \"get_db_session_factory\",\n    \"get_entity_schema\",\n    \"get_schema_by_name\",\n    \"get_schema_columns\",\n    \"common_filter\",\n    \"del_data\",\n    \"get_by_id\",\n    \"get_data\",\n    \"data_exist\",\n    \"get_data_count\",\n    \"get_group\",\n    \"decode_entity_id\",\n    \"get_entity_type\",\n    \"get_entity_exchange\",\n    \"get_entity_code\",\n    \"df_to_db\",\n    \"get_entities\",\n    \"get_entity_ids\",\n]\n"
  },
  {
    "path": "src/zvt/contract/base_service.py",
    "content": "# -*- coding: utf-8 -*-\nimport json\nfrom typing import Type, List\n\nfrom zvt.contract.api import del_data, get_db_session\nfrom zvt.contract.zvt_info import StateMixin\nfrom zvt.utils.str_utils import to_snake_str\n\n\nclass StatefulService(object):\n    \"\"\"\n    Base service with state could be stored in state_schema\n    \"\"\"\n\n    #: state schema\n    state_schema: Type[StateMixin] = None\n\n    #: name of the service, default name of class if not set manually\n    name = None\n\n    def __init__(self) -> None:\n        assert self.state_schema is not None\n        if self.name is None:\n            self.name = to_snake_str(type(self).__name__)\n        self.state_session = get_db_session(data_schema=self.state_schema, provider=\"zvt\")\n\n    def clear_state_data(self, entity_id=None):\n        \"\"\"\n        clear state of the entity\n\n        :param entity_id: entity id\n        \"\"\"\n        filters = [self.state_schema.state_name == self.name]\n        if entity_id:\n            filters = filters + [self.state_schema.entity_id == entity_id]\n        del_data(self.state_schema, filters=filters)\n\n    def decode_state(self, state: str):\n        \"\"\"\n        decode state\n\n        :param state:\n        :return:\n        \"\"\"\n\n        return json.loads(state, object_hook=self.state_object_hook())\n\n    def encode_state(self, state: object):\n        \"\"\"\n        encode state\n\n        :param state:\n        :return:\n        \"\"\"\n\n        return json.dumps(state, cls=self.state_encoder())\n\n    def state_object_hook(self):\n        return None\n\n    def state_encoder(self):\n        return None\n\n\nclass OneStateService(StatefulService):\n    \"\"\"\n    StatefulService which saving all states in one object\n    \"\"\"\n\n    def __init__(self) -> None:\n        super().__init__()\n        self.state_domain = self.state_schema.get_by_id(id=self.name)\n        if self.state_domain:\n            self.state: dict = self.decode_state(self.state_domain.state)\n        else:\n            self.state = None\n\n    def persist_state(self):\n        state_str = self.encode_state(self.state)\n        if not self.state_domain:\n            self.state_domain = self.state_schema(id=self.name, entity_id=self.name, state_name=self.name)\n        self.state_domain.state = state_str\n        self.state_session.add(self.state_domain)\n        self.state_session.commit()\n\n\nclass EntityStateService(StatefulService):\n    \"\"\"\n    StatefulService which saving one state one entity\n    \"\"\"\n\n    def __init__(self, entity_ids) -> None:\n        super().__init__()\n        self.entity_ids = entity_ids\n        state_domains: List[StateMixin] = self.state_schema.query_data(\n            filters=[self.state_schema.state_name == self.name], entity_ids=self.entity_ids, return_type=\"domain\"\n        )\n\n        #: entity_id:state\n        self.states: dict = {}\n        if state_domains:\n            for state in state_domains:\n                self.states[state.entity_id] = self.decode_state(state.state)\n\n    def persist_state(self, entity_id):\n        state = self.states.get(entity_id)\n        if state:\n            domain_id = f\"{self.name}_{entity_id}\"\n            state_domain = self.state_schema.get_by_id(domain_id)\n            state_str = self.encode_state(state)\n            if not state_domain:\n                state_domain = self.state_schema(id=domain_id, entity_id=entity_id, state_name=self.name)\n            state_domain.state = state_str\n            self.state_session.add(state_domain)\n            self.state_session.commit()\n\n\n# the __all__ is generated\n__all__ = [\"StatefulService\", \"OneStateService\", \"EntityStateService\"]\n"
  },
  {
    "path": "src/zvt/contract/context.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\nZVT global context. Phase 1: storage_backend and route_registry are optional\ninjections; api uses get_storage_backend()/get_route_registry() when not set.\n\"\"\"\nfrom typing import Optional, TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from zvt.contract.storage import StorageBackend\n    from zvt.contract.route_registry import RouteRegistry\n\n\nclass Registry(object):\n    \"\"\"\n    Class storing zvt registering meta\n    \"\"\"\n\n    def __init__(self) -> None:\n        #: all registered providers\n        self.providers = []\n\n        #: all registered entity types(str)\n        self.tradable_entity_types = []\n\n        #: all entity schemas\n        self.tradable_entity_schemas = []\n\n        #: all registered schemas\n        self.schemas = []\n\n        #: tradable entity  type -> schema\n        self.tradable_schema_map = {}\n\n        #: global sessions\n        self.sessions = {}\n\n        #: provider_dbname -> engine (deprecated: engines now in StorageBackend)\n        self.db_engine_map = {}\n\n        #: provider_dbname -> session factory (deprecated: now in StorageBackend)\n        self.db_session_map = {}\n\n        #: provider -> [db_name1,db_name2...]\n        self.provider_map_dbnames = {}\n\n        #: db_name -> [declarative_base1,declarative_base2...]\n        self.dbname_map_base = {}\n\n        #: db_name -> [declarative_meta1,declarative_meta2...]\n        self.dbname_map_schemas = {}\n\n        #: entity_type -> related schemas\n        self.entity_map_schemas = {}\n\n        #: factor class registry\n        self.factor_cls_registry = {}\n\n        #: db_names of internal schemas (business logic data, not tied to external data source)\n        self.internal_db_names = set()\n\n        #: Optional StorageBackend; if set, api uses it instead of get_storage_backend()\n        self.storage_backend: Optional[\"StorageBackend\"] = None\n\n        #: Optional RouteRegistry; if set, api uses it instead of get_route_registry()\n        self.route_registry: Optional[\"RouteRegistry\"] = None\n\n\n#: :class:`~.zvt.contract.context.Registry` instance\nzvt_context = Registry()\n\n\n# the __all__ is generated\n__all__ = [\"Registry\"]\n"
  },
  {
    "path": "src/zvt/contract/data_type.py",
    "content": "# -*- coding: utf-8 -*-\n\n\nclass Bean(object):\n    def __init__(self) -> None:\n        super().__init__()\n        self.__dict__\n\n    def dict(self):\n        return self.__dict__\n\n    def from_dct(self, dct: dict):\n        if dct:\n            for k in dct:\n                self.__dict__[k] = dct[k]\n\n\n# the __all__ is generated\n__all__ = [\"Bean\"]\n"
  },
  {
    "path": "src/zvt/contract/drawer.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nfrom enum import Enum\nfrom typing import List, Optional\n\nimport numpy as np\nimport pandas as pd\nimport plotly.graph_objs as go\nfrom plotly.subplots import make_subplots\n\nfrom zvt.contract.api import decode_entity_id\nfrom zvt.contract.data_type import Bean\nfrom zvt.contract.normal_data import NormalData\nfrom zvt.utils.decorator import to_string\nfrom zvt.utils.pd_utils import pd_is_not_null\n\nlogger = logging.getLogger(__name__)\n\n\nclass ChartType(Enum):\n    \"\"\"\n    Chart type enum\n    \"\"\"\n\n    #: candlestick chart\n    kline = \"kline\"\n    #: line chart\n    line = \"line\"\n    #: area chart\n    area = \"area\"\n    #: scatter chart\n    scatter = \"scatter\"\n    #: histogram chart\n    histogram = \"histogram\"\n    #: pie chart\n    pie = \"pie\"\n    #: bar chart\n    bar = \"bar\"\n\n\n_zvt_chart_type_map_scatter_mode = {ChartType.line: \"lines\", ChartType.area: \"none\", ChartType.scatter: \"markers\"}\n\n\n@to_string\nclass Rect(Bean):\n    \"\"\"\n    rect struct with left-bottom(x0, y0), right-top(x1, y1)\n    \"\"\"\n\n    def __init__(self, x0=None, y0=None, x1=None, y1=None) -> None:\n        #: left-bottom x0\n        self.x0 = x0\n        #: left-bottom y0\n        self.y0 = y0\n        #: right-top x1\n        self.x1 = x1\n        #: right-top y1\n        self.y1 = y1\n\n\nclass Draw(object):\n    def draw_kline(\n        self, width=None, height=None, title=None, keep_ui_state=True, show=False, scale_value=None, **kwargs\n    ):\n        return self.draw(\n            main_chart=ChartType.kline,\n            width=width,\n            height=height,\n            title=title,\n            keep_ui_state=keep_ui_state,\n            show=show,\n            scale_value=scale_value,\n            **kwargs,\n        )\n\n    def draw_line(\n        self, width=None, height=None, title=None, keep_ui_state=True, show=False, scale_value=None, **kwargs\n    ):\n        return self.draw(\n            main_chart=ChartType.line,\n            width=width,\n            height=height,\n            title=title,\n            keep_ui_state=keep_ui_state,\n            show=show,\n            scale_value=scale_value,\n            **kwargs,\n        )\n\n    def draw_area(\n        self, width=None, height=None, title=None, keep_ui_state=True, show=False, scale_value=None, **kwargs\n    ):\n        return self.draw(\n            main_chart=ChartType.area,\n            width=width,\n            height=height,\n            title=title,\n            keep_ui_state=keep_ui_state,\n            show=show,\n            scale_value=scale_value,\n            **kwargs,\n        )\n\n    def draw_scatter(\n        self, width=None, height=None, title=None, keep_ui_state=True, show=False, scale_value=None, **kwargs\n    ):\n        return self.draw(\n            main_chart=ChartType.scatter,\n            width=width,\n            height=height,\n            title=title,\n            keep_ui_state=keep_ui_state,\n            show=show,\n            scale_value=scale_value,\n            **kwargs,\n        )\n\n    def draw_histogram(\n        self, width=None, height=None, title=None, keep_ui_state=True, show=False, scale_value=None, **kwargs\n    ):\n        return self.draw(\n            ChartType.histogram,\n            width=width,\n            height=height,\n            title=title,\n            keep_ui_state=keep_ui_state,\n            show=show,\n            scale_value=scale_value,\n            **kwargs,\n        )\n\n    def draw_bar(self, width=None, height=None, title=None, keep_ui_state=True, show=False, scale_value=None, **kwargs):\n        return self.draw(\n            ChartType.bar,\n            width=width,\n            height=height,\n            title=title,\n            keep_ui_state=keep_ui_state,\n            show=show,\n            scale_value=scale_value,\n            **kwargs,\n        )\n\n    def draw_pie(self, width=None, height=None, title=None, keep_ui_state=True, show=False, scale_value=None, **kwargs):\n        return self.draw(\n            ChartType.pie,\n            width=width,\n            height=height,\n            title=title,\n            keep_ui_state=keep_ui_state,\n            show=show,\n            scale_value=scale_value,\n            **kwargs,\n        )\n\n    def draw(\n        self,\n        main_chart=ChartType.kline,\n        sub_chart=\"bar\",\n        width=None,\n        height=None,\n        title=None,\n        keep_ui_state=True,\n        show=False,\n        scale_value=None,\n        **kwargs,\n    ):\n\n        raise NotImplementedError()\n\n    def default_layout(self, main_chart=None, width=None, height=None, title=None, keep_ui_state=True, **layout_params):\n        if keep_ui_state:\n            uirevision = True\n        else:\n            uirevision = None\n\n        if main_chart == ChartType.histogram:\n            xaxis = None\n        else:\n            xaxis = dict(\n                linecolor=\"#BCCCDC\",\n                showgrid=False,\n                showspikes=True,  # Show spike line for X-axis\n                # Format spike\n                spikethickness=2,\n                spikedash=\"dot\",\n                spikecolor=\"#999999\",\n                spikemode=\"across\",\n                rangeselector=dict(\n                    buttons=list(\n                        [\n                            dict(count=1, label=\"1m\", step=\"month\", stepmode=\"backward\"),\n                            dict(count=3, label=\"3m\", step=\"month\", stepmode=\"backward\"),\n                            dict(count=6, label=\"6m\", step=\"month\", stepmode=\"backward\"),\n                            dict(count=1, label=\"YTD\", step=\"year\", stepmode=\"todate\"),\n                            dict(count=1, label=\"1y\", step=\"year\", stepmode=\"backward\"),\n                            dict(step=\"all\"),\n                        ]\n                    )\n                ),\n                rangeslider=dict(\n                    visible=True,\n                ),\n                type=\"date\",\n            )\n\n        return dict(\n            showlegend=True,\n            plot_bgcolor=\"#FFF\",\n            hovermode=\"x\",\n            hoverdistance=100,  # Distance to show hover label of data point\n            spikedistance=1000,  # Distance to show spike\n            uirevision=uirevision,\n            height=height,\n            width=width,\n            title=title,\n            yaxis=dict(\n                autorange=True,\n                fixedrange=False,\n                zeroline=False,\n                linecolor=\"#BCCCDC\",\n                showgrid=False,\n            ),\n            xaxis=xaxis,\n            legend_orientation=\"h\",\n            hoverlabel={\"namelength\": -1},\n            **layout_params,\n        )\n\n\nclass Drawable(object):\n    def drawer(self):\n        drawer = Drawer(\n            main_df=self.drawer_main_df(),\n            main_data=self.drawer_main_data(),\n            factor_df_list=self.drawer_factor_df_list(),\n            factor_data_list=self.drawer_factor_data_list(),\n            sub_df_list=self.drawer_sub_df_list(),\n            sub_data_list=self.drawer_sub_data_list(),\n            sub_col_chart=self.drawer_sub_col_chart(),\n            annotation_df=self.drawer_annotation_df(),\n            rects=self.drawer_rects(),\n        )\n        return drawer\n\n    def draw(\n        self,\n        main_chart=ChartType.kline,\n        width=None,\n        height=None,\n        title=None,\n        keep_ui_state=True,\n        show=False,\n        scale_value=None,\n        **kwargs,\n    ):\n        return self.drawer().draw(\n            main_chart=main_chart,\n            width=width,\n            height=height,\n            title=title,\n            keep_ui_state=keep_ui_state,\n            show=show,\n            scale_value=scale_value,\n            **kwargs,\n        )\n\n    def drawer_main_df(self) -> Optional[pd.DataFrame]:\n        return None\n\n    def drawer_main_data(self) -> Optional[NormalData]:\n        return None\n\n    def drawer_factor_df_list(self) -> Optional[List[pd.DataFrame]]:\n        return None\n\n    def drawer_factor_data_list(self) -> Optional[List[NormalData]]:\n        return None\n\n    def drawer_sub_df_list(self) -> Optional[List[pd.DataFrame]]:\n        return None\n\n    def drawer_sub_data_list(self) -> Optional[List[NormalData]]:\n        return None\n\n    def drawer_annotation_df(self) -> Optional[pd.DataFrame]:\n        return None\n\n    def drawer_rects(self) -> Optional[List[Rect]]:\n        return None\n\n    def drawer_sub_col_chart(self) -> Optional[dict]:\n        return None\n\n\nclass StackedDrawer(Draw):\n    def __init__(self, *drawers) -> None:\n        super().__init__()\n        assert len(drawers) > 1\n        self.drawers: List[Drawer] = drawers\n\n    def make_y_layout(self, index, total, start_index=1, domain_range=(0, 1)):\n        part = (domain_range[1] - domain_range[0]) / total\n\n        if index == 1:\n            yaxis = \"yaxis\"\n            y = \"y\"\n        else:\n            yaxis = f\"yaxis{index}\"\n            y = f\"y{index}\"\n\n        return (\n            yaxis,\n            y,\n            dict(\n                anchor=\"x\",\n                autorange=True,\n                fixedrange=False,\n                zeroline=False,\n                linecolor=\"#BCCCDC\",\n                showgrid=False,\n                domain=[\n                    domain_range[0] + part * (index - start_index),\n                    domain_range[0] + part * (index - start_index + 1),\n                ],\n            ),\n        )\n\n    def draw(\n        self,\n        main_chart=ChartType.kline,\n        sub_chart=\"bar\",\n        width=None,\n        height=None,\n        title=None,\n        keep_ui_state=True,\n        show=False,\n        scale_value=None,\n        **kwargs,\n    ):\n        stacked_fig = go.Figure()\n\n        total = len(self.drawers)\n        start = 1\n        domain_range = (0, 1)\n        for drawer in self.drawers:\n            if drawer.has_sub_plot():\n                domain_range = (0.2, 1)\n                start = 2\n                break\n        for index, drawer in enumerate(self.drawers, start=start):\n            traces, sub_traces = drawer.make_traces(\n                main_chart=main_chart, sub_chart=sub_chart, scale_value=scale_value, **kwargs\n            )\n\n            # fix sub traces as the bottom\n            if sub_traces:\n                yaxis, y, layout = self.make_y_layout(index=1, total=1, domain_range=(0, 0.2))\n                # update sub_traces with yaxis\n                for trace in sub_traces:\n                    trace.yaxis = y\n                stacked_fig.add_traces(sub_traces)\n                stacked_fig.layout[yaxis] = layout\n\n            # make y layouts\n            yaxis, y, layout = self.make_y_layout(\n                index=index, total=total, start_index=start, domain_range=domain_range\n            )\n\n            stacked_fig.layout[yaxis] = layout\n\n            # update traces with yaxis\n            for trace in traces:\n                trace.yaxis = y\n            stacked_fig.add_traces(traces)\n\n            # update shapes with yaxis\n            if drawer.rects:\n                for rect in drawer.rects:\n                    stacked_fig.add_shape(\n                        type=\"rect\",\n                        x0=rect.x0,\n                        y0=rect.y0,\n                        x1=rect.x1,\n                        y1=rect.y1,\n                        line=dict(color=\"RoyalBlue\", width=1),\n                        # fillcolor=\"LightSkyBlue\",\n                        yref=y,\n                    )\n\n            # annotations\n            if pd_is_not_null(drawer.annotation_df):\n                stacked_fig.layout[\"annotations\"] = annotations(drawer.annotation_df, yref=y)\n\n        stacked_fig.update_layout(\n            self.default_layout(\n                main_chart=main_chart, width=width, height=height, title=title, keep_ui_state=keep_ui_state\n            )\n        )\n\n        if show:\n            stacked_fig.show()\n        else:\n            return stacked_fig\n\n\nclass Drawer(Draw):\n    def __init__(\n        self,\n        main_df: pd.DataFrame = None,\n        factor_df_list: List[pd.DataFrame] = None,\n        sub_df_list: pd.DataFrame = None,\n        main_data: NormalData = None,\n        factor_data_list: List[NormalData] = None,\n        sub_data_list: NormalData = None,\n        sub_col_chart: Optional[dict] = None,\n        rects: List[Rect] = None,\n        annotation_df: pd.DataFrame = None,\n        scale_value: int = None,\n    ) -> None:\n        \"\"\"\n\n        :param main_df: df for main chart\n        :param factor_df_list: list of factor df on main chart\n        :param sub_df_list: df for sub chart under main chart\n        :param main_data: NormalData wrap main_df,use either\n        :param factor_data_list: list of NormalData wrap factor_df,use either\n        :param sub_data_list: NormalData wrap sub_df,use either\n        :param annotation_df:\n        \"\"\"\n\n        #: 主图数据\n        if main_data is None:\n            main_data = NormalData(main_df)\n        self.main_data: NormalData = main_data\n\n        #: 主图因子\n        if not factor_data_list and factor_df_list:\n            factor_data_list = []\n            for df in factor_df_list:\n                factor_data_list.append(NormalData(df))\n        #: 每一个df可能有多个column, 代表多个指标，对于连续型的，可以放在一个df里面\n        #: 对于离散型的，比如一些特定模式的连线，放在多个df里面较好，因为index不同\n        self.factor_data_list: List[NormalData] = factor_data_list\n\n        #: 副图数据\n        if not sub_data_list and sub_df_list:\n            sub_data_list = []\n            for df in sub_df_list:\n                sub_data_list.append(NormalData(df))\n        #: 每一个df可能有多个column, 代表多个指标，对于连续型的，可以放在一个df里面\n        #: 对于离散型的，比如一些特定模式的连线，放在多个df里面较好，因为index不同\n        self.sub_data_list: List[NormalData] = sub_data_list\n\n        #: 幅图col对应的图形，line or bar\n        self.sub_col_chart = sub_col_chart\n\n        #: 主图的标记数据\n        self.annotation_df = annotation_df\n\n        #: list of rect\n        self.rects = rects\n\n        self.scale_value = scale_value\n\n    def add_factor_df(self, df: pd.DataFrame):\n        self.add_factor_data(NormalData(df))\n\n    def add_factor_data(self, data: NormalData):\n        if not self.factor_data_list:\n            self.factor_data_list = []\n        self.factor_data_list.append(data)\n\n    def add_sub_df(self, df: pd.DataFrame):\n        self.add_sub_data(NormalData(df))\n\n    def add_sub_data(self, data: NormalData):\n        if not self.sub_data_list:\n            self.sub_data_list = []\n        self.sub_data_list.append(data)\n\n    def has_sub_plot(self):\n        return self.sub_data_list is not None and not self.sub_data_list[0].empty()\n\n    def make_traces(self, main_chart=ChartType.kline, sub_chart=\"bar\", yaxis=\"y\", scale_value=None, **kwargs):\n        traces = []\n        sub_traces = []\n\n        for entity_id, df in self.main_data.entity_map_df.items():\n            df = df.select_dtypes(np.number)\n            df = df.copy()\n            if scale_value:\n                for col in df.columns:\n                    first = None\n                    for i in range(0, len(df)):\n                        first = df[col][i]\n                        if first != 0:\n                            break\n                    if first == 0:\n                        continue\n                    scale = scale_value / first\n                    df[col] = df[col] * scale\n            code = entity_id\n            try:\n                _, _, code = decode_entity_id(entity_id)\n            except Exception:\n                pass\n\n            # 构造主图\n            if main_chart == ChartType.bar:\n                for col in df.columns:\n                    trace_name = \"{}_{}\".format(code, col)\n                    ydata = df[col].values.tolist()\n                    traces.append(go.Bar(x=df.index, y=ydata, name=trace_name, yaxis=yaxis, **kwargs))\n            elif main_chart == ChartType.kline:\n                trace_name = \"{}_kdata\".format(code)\n                trace = go.Candlestick(\n                    x=df.index,\n                    open=df[\"open\"],\n                    close=df[\"close\"],\n                    low=df[\"low\"],\n                    high=df[\"high\"],\n                    name=trace_name,\n                    yaxis=yaxis,\n                    **kwargs,\n                )\n                traces.append(trace)\n            elif main_chart in [ChartType.scatter, ChartType.line, ChartType.area]:\n                mode = _zvt_chart_type_map_scatter_mode.get(main_chart)\n                for col in df.columns:\n                    trace_name = \"{}_{}\".format(code, col)\n                    ydata = df[col].values.tolist()\n                    traces.append(go.Scatter(x=df.index, y=ydata, mode=mode, name=trace_name, yaxis=yaxis, **kwargs))\n            elif main_chart == ChartType.histogram:\n                for col in df.columns:\n                    trace_name = \"{}_{}\".format(code, col)\n                    x = df[col].tolist()\n                    trace = go.Histogram(x=x, name=trace_name, **kwargs)\n                    traces.append(trace)\n                    annotation = [\n                        dict(\n                            entity_id=entity_id,\n                            timestamp=x[-1],\n                            value=0,\n                            flag=f\"{trace_name}:{x[-1]}\",\n                        )\n                    ]\n                    annotation_df = pd.DataFrame.from_records(annotation, index=[\"entity_id\", \"timestamp\"])\n                    if pd_is_not_null(self.annotation_df):\n                        self.annotation_df = pd.concat([self.annotation_df, annotation_df])\n                    else:\n                        self.annotation_df = annotation_df\n            elif main_chart == ChartType.pie:\n                for _, row in df.iterrows():\n                    traces.append(go.Pie(name=entity_id, labels=df.columns.tolist(), values=row.tolist(), **kwargs))\n            else:\n                assert False\n\n            # 构造主图指标\n            if self.factor_data_list:\n                for factor_data in self.factor_data_list:\n                    if not factor_data.empty():\n                        factor_df = factor_data.entity_map_df.get(entity_id)\n                        factor_df = factor_df.select_dtypes(np.number)\n                        if pd_is_not_null(factor_df):\n                            for col in factor_df.columns:\n                                trace_name = \"{}_{}\".format(code, col)\n                                ydata = factor_df[col].values.tolist()\n\n                                line = go.Scatter(\n                                    x=factor_df.index, y=ydata, mode=\"lines\", name=trace_name, yaxis=yaxis, **kwargs\n                                )\n                                traces.append(line)\n\n            # 构造幅图\n            if self.has_sub_plot():\n                for sub_data in self.sub_data_list:\n                    sub_df = sub_data.entity_map_df.get(entity_id)\n                    if pd_is_not_null(sub_df):\n                        sub_df = sub_df.select_dtypes(np.number)\n                        for col in sub_df.columns:\n                            trace_name = \"{}_{}\".format(code, col)\n                            ydata = sub_df[col].values.tolist()\n\n                            def color(i):\n                                if i > 0:\n                                    return \"red\"\n                                else:\n                                    return \"green\"\n\n                            colors = [color(i) for i in ydata]\n\n                            the_sub_chart = None\n                            if self.sub_col_chart is not None:\n                                the_sub_chart = self.sub_col_chart.get(col)\n                            if not the_sub_chart:\n                                the_sub_chart = sub_chart\n\n                            if the_sub_chart == ChartType.line:\n                                sub_trace = go.Scatter(\n                                    x=sub_df.index, y=ydata, name=trace_name, yaxis=\"y2\", marker=dict(color=colors)\n                                )\n                            else:\n                                sub_trace = go.Bar(\n                                    x=sub_df.index, y=ydata, name=trace_name, yaxis=\"y2\", marker=dict(color=colors)\n                                )\n                            sub_traces.append(sub_trace)\n\n        return traces, sub_traces\n\n    def add_rects(self, fig, yaxis=\"y\"):\n        if self.rects:\n            for rect in self.rects:\n                fig.add_shape(\n                    type=\"rect\",\n                    x0=rect.x0,\n                    y0=rect.y0,\n                    x1=rect.x1,\n                    y1=rect.y1,\n                    line=dict(color=\"RoyalBlue\", width=1),\n                    # fillcolor=\"LightSkyBlue\"\n                )\n            fig.update_shapes(dict(xref=\"x\", yref=yaxis))\n\n    def draw(\n        self,\n        main_chart=ChartType.kline,\n        sub_chart=\"bar\",\n        width=None,\n        height=None,\n        title=None,\n        keep_ui_state=True,\n        show=False,\n        scale_value=None,\n        **kwargs,\n    ):\n        yaxis = \"y\"\n        traces, sub_traces = self.make_traces(\n            main_chart=main_chart, sub_chart=sub_chart, yaxis=yaxis, scale_value=scale_value, **kwargs\n        )\n\n        if sub_traces:\n            fig = make_subplots(rows=2, cols=1, row_heights=[0.8, 0.2], vertical_spacing=0.08, shared_xaxes=True)\n            fig.add_traces(traces, rows=[1] * len(traces), cols=[1] * len(traces))\n            fig.add_traces(sub_traces, rows=[2] * len(sub_traces), cols=[1] * len(sub_traces))\n        else:\n            fig = go.Figure()\n            fig.add_traces(traces)\n\n        # 绘制矩形\n        self.add_rects(fig, yaxis=yaxis)\n\n        fig.update_layout(\n            self.default_layout(\n                main_chart=main_chart, width=width, height=height, title=title, keep_ui_state=keep_ui_state\n            )\n        )\n\n        if sub_traces:\n            fig.update_layout(xaxis_rangeslider_visible=False)\n            fig.update_layout(xaxis2_rangeslider_visible=True, xaxis2_rangeslider_thickness=0.1)\n        # 绘制标志\n        if pd_is_not_null(self.annotation_df):\n            fig.layout[\"annotations\"] = annotations(self.annotation_df, yref=yaxis)\n\n        if show:\n            fig.show()\n        else:\n            return fig\n\n    def draw_table(self, width=None, height=None, title=None, keep_ui_state=True, **kwargs):\n        cols = self.main_data.data_df.index.names + self.main_data.data_df.columns.tolist()\n\n        index1 = self.main_data.data_df.index.get_level_values(0).tolist()\n        index2 = self.main_data.data_df.index.get_level_values(1).tolist()\n        values = [index1] + [index2] + [self.main_data.data_df[col] for col in self.main_data.data_df.columns]\n\n        data = go.Table(\n            header=dict(\n                values=cols,\n                fill_color=[\"#000080\", \"#000080\"] + [\"#0066cc\"] * len(self.main_data.data_df.columns),\n                align=\"left\",\n                font=dict(color=\"white\", size=13),\n            ),\n            cells=dict(values=values, fill=dict(color=\"#F5F8FF\"), align=\"left\"),\n            **kwargs,\n        )\n\n        fig = go.Figure()\n        fig.add_traces([data])\n        fig.update_layout(self.default_layout(width=width, height=height, title=title, keep_ui_state=keep_ui_state))\n\n        fig.show()\n\n\ndef annotations(annotation_df: pd.DataFrame, yref=\"y\"):\n    \"\"\"\n    annotation_df format::\n\n                                        value    flag    color\n        entity_id    timestamp\n\n    :param annotation_df:\n    :param yref: specific yaxis e.g, y,y2,y3\n    :return:\n    \"\"\"\n\n    if pd_is_not_null(annotation_df):\n        annotations = []\n        for trace_name, df in annotation_df.groupby(level=0):\n            if pd_is_not_null(df):\n                for (_, timestamp), item in df.iterrows():\n                    if \"color\" in item:\n                        color = item[\"color\"]\n                    else:\n                        color = \"#ec0000\"\n\n                    value = round(item[\"value\"], 2)\n                    annotations.append(\n                        dict(\n                            x=timestamp,\n                            y=value,\n                            xref=\"x\",\n                            yref=yref,\n                            text=item[\"flag\"],\n                            showarrow=True,\n                            align=\"center\",\n                            arrowhead=2,\n                            arrowsize=1,\n                            arrowwidth=2,\n                            # arrowcolor='#030813',\n                            ax=-10,\n                            ay=-30,\n                            bordercolor=\"#c7c7c7\",\n                            borderwidth=1,\n                            bgcolor=color,\n                            opacity=0.8,\n                        )\n                    )\n        return annotations\n    return None\n\n\n# the __all__ is generated\n__all__ = [\"ChartType\", \"Rect\", \"Draw\", \"Drawable\", \"StackedDrawer\", \"Drawer\", \"annotations\"]\n"
  },
  {
    "path": "src/zvt/contract/factor.py",
    "content": "# -*- coding: utf-8 -*-\nimport json\nimport logging\nimport time\nfrom enum import Enum\nfrom typing import List, Union, Optional, Type\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract import zvt_context\nfrom zvt.contract.api import get_data, df_to_db, del_data\nfrom zvt.contract.base_service import EntityStateService\nfrom zvt.contract.reader import DataReader, DataListener\nfrom zvt.contract.schema import Mixin, TradableEntity\nfrom zvt.contract.zvt_info import FactorState\nfrom zvt.utils.pd_utils import pd_is_not_null, drop_continue_duplicate, is_filter_result_df, is_score_result_df\nfrom zvt.utils.str_utils import to_snake_str\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\nclass TargetType(Enum):\n    positive = \"positive\"\n    negative = \"negative\"\n    keep = \"keep\"\n\n\nclass Indicator(object):\n    def __init__(self) -> None:\n        self.logger = logging.getLogger(self.__class__.__name__)\n        self.indicators = []\n\n\nclass Transformer(Indicator):\n    def __init__(self) -> None:\n        super().__init__()\n\n    def transform(self, input_df: pd.DataFrame) -> pd.DataFrame:\n        \"\"\"\n        input_df format::\n\n                                      col1    col2    col3    ...\n            entity_id    timestamp\n                                      1.2     0.5     0.3     ...\n                                      1.0     0.7     0.2     ...\n\n        the return result would change the columns and  keep the format\n\n        :param input_df:\n        :return:\n        \"\"\"\n        g = input_df.groupby(level=0)\n        if len(g.groups) == 1:\n            entity_id = input_df.index[0][0]\n\n            df = input_df.reset_index(level=0, drop=True)\n            ret_df = self.transform_one(entity_id=entity_id, df=df)\n            ret_df[\"entity_id\"] = entity_id\n\n            return ret_df.set_index(\"entity_id\", append=True).swaplevel(0, 1)\n        else:\n            return g.apply(lambda x: self.transform_one(x.index[0][0], x.reset_index(level=0, drop=True)))\n\n    def transform_one(self, entity_id: str, df: pd.DataFrame) -> pd.DataFrame:\n        \"\"\"\n        df format::\n\n                         col1    col2    col3    ...\n            timestamp\n                         1.2     0.5     0.3     ...\n                         1.0     0.7     0.2     ...\n\n        the return result would change the columns and  keep the format\n\n        :param entity_id:\n        :param df:\n        :return:\n        \"\"\"\n        return df\n\n\nclass Accumulator(Indicator):\n    def __init__(self, acc_window: int = 1) -> None:\n        \"\"\"\n\n        :param acc_window: the window size of acc for computing,default is 1\n        \"\"\"\n        super().__init__()\n        self.acc_window = acc_window\n\n    def acc(self, input_df: pd.DataFrame, acc_df: pd.DataFrame, states: dict) -> (pd.DataFrame, dict):\n        \"\"\"\n\n        :param input_df: new input\n        :param acc_df: previous result\n        :param states: current states of the entity\n        :return: new result and states\n        \"\"\"\n        g = input_df.groupby(level=0)\n        if len(g.groups) == 1:\n            entity_id = input_df.index[0][0]\n\n            df = input_df.reset_index(level=0, drop=True)\n            if pd_is_not_null(acc_df) and (entity_id == acc_df.index[0][0]):\n                acc_one_df = acc_df.reset_index(level=0, drop=True)\n            else:\n                acc_one_df = None\n            ret_df, state = self.acc_one(entity_id=entity_id, df=df, acc_df=acc_one_df, state=states.get(entity_id))\n            if pd_is_not_null(ret_df):\n                ret_df[\"entity_id\"] = entity_id\n                ret_df = ret_df.set_index(\"entity_id\", append=True).swaplevel(0, 1)\n                ret_df[\"entity_id\"] = entity_id\n                return ret_df, {entity_id: state}\n            return None, {entity_id: state}\n        else:\n            new_states = {}\n\n            def cal_acc(x):\n                entity_id = x.index[0][0]\n                if pd_is_not_null(acc_df):\n                    acc_g = acc_df.groupby(level=0)\n                    acc_one_df = None\n                    if entity_id in acc_g.groups:\n                        acc_one_df = acc_g.get_group(entity_id)\n                        if pd_is_not_null(acc_one_df):\n                            acc_one_df = acc_one_df.reset_index(level=0, drop=True)\n                else:\n                    acc_one_df = None\n\n                one_result, state = self.acc_one(\n                    entity_id=entity_id,\n                    df=x.reset_index(level=0, drop=True),\n                    acc_df=acc_one_df,\n                    state=states.get(x.index[0][0]),\n                )\n\n                new_states[entity_id] = state\n                return one_result\n\n            ret_df = g.apply(lambda x: cal_acc(x))\n            return ret_df, new_states\n\n    def acc_one(self, entity_id, df: pd.DataFrame, acc_df: pd.DataFrame, state: dict) -> (pd.DataFrame, dict):\n        \"\"\"\n        df format::\n\n                         col1    col2    col3    ...\n            timestamp\n                         1.2     0.5     0.3     ...\n                         1.0     0.7     0.2     ...\n\n        the new result and state\n\n        :param df: current input df\n        :param entity_id: current computing entity_id\n        :param acc_df: current result of the entity_id\n        :param state: current state of the entity_id\n        :return: new result and state of the entity_id\n        \"\"\"\n        return acc_df, state\n\n\nclass Scorer(object):\n    def __init__(self) -> None:\n        self.logger = logging.getLogger(self.__class__.__name__)\n\n    def score(self, input_df: pd.DataFrame) -> pd.DataFrame:\n        \"\"\"\n\n        :param input_df: current input df\n        :return: df with normal score\n        \"\"\"\n        return input_df\n\n\ndef _register_class(target_class):\n    if target_class.__name__ not in (\"Factor\", \"FilterFactor\", \"ScoreFactor\", \"StateFactor\"):\n        zvt_context.factor_cls_registry[target_class.__name__] = target_class\n\n\nclass FactorMeta(type):\n    def __new__(meta, name, bases, class_dict):\n        cls = type.__new__(meta, name, bases, class_dict)\n        _register_class(cls)\n        return cls\n\n\nclass Factor(DataReader, EntityStateService, DataListener):\n    #: Schema for storing states\n    state_schema = FactorState\n    #: define the schema for persist,its columns should be same as indicators in transformer or accumulator\n    factor_schema: Type[Mixin] = None\n\n    #: transformer for this factor if not passed as __init__ argument\n    transformer: Transformer = None\n    #: accumulator for this factor if not passed as __init__ argument\n    accumulator: Accumulator = None\n\n    def __init__(\n        self,\n        data_schema: Type[Mixin],\n        entity_schema: Type[TradableEntity] = None,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        transformer: Transformer = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n    ) -> None:\n        \"\"\"\n        :param keep_all_timestamp:\n        :param fill_method:\n        :param effective_number:\n        :param transformer:\n        :param accumulator:\n        :param need_persist: whether persist factor\n        :param only_compute_factor: only compute factor nor result\n        :param factor_name:\n        :param clear_state:\n        :param only_load_factor: only load factor and compute result\n        \"\"\"\n        self.only_load_factor = only_load_factor\n\n        #: define unique name of your factor if you want to keep factor state\n        #: the factor state is defined by factor_name and entity_id\n        if not factor_name:\n            self.name = to_snake_str(type(self).__name__)\n        else:\n            self.name = factor_name\n\n        DataReader.__init__(\n            self,\n            data_schema,\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n        )\n\n        EntityStateService.__init__(self, entity_ids=entity_ids)\n\n        self.clear_state = clear_state\n\n        self.keep_all_timestamp = keep_all_timestamp\n        self.fill_method = fill_method\n        self.effective_number = effective_number\n\n        if transformer:\n            self.transformer = transformer\n        else:\n            self.transformer = self.__class__.transformer\n\n        if accumulator:\n            self.accumulator = accumulator\n        else:\n            self.accumulator = self.__class__.accumulator\n\n        self.need_persist = need_persist\n        self.only_compute_factor = only_compute_factor\n\n        #: 中间结果，不持久化\n        #: data_df->pipe_df\n        self.pipe_df: pd.DataFrame = None\n\n        #: 计算因子的结果，可持久化,通过对pipe_df的计算得到\n        #: pipe_df->factor_df\n        self.factor_df: pd.DataFrame = None\n\n        #: result_df是用于选股的标准df,通过对factor_df的计算得到\n        #: factor_df->result_df\n        self.result_df: pd.DataFrame = None\n\n        if self.clear_state:\n            self.clear_state_data()\n        elif self.need_persist or self.only_load_factor:\n            self.load_factor()\n\n            #: 根据已经计算的factor_df和computing_window来保留data_df\n            #: 因为读取data_df的目的是为了计算factor_df,选股和回测只依赖factor_df\n            #: 所以如果有持久化的factor_df,只需保留需要用于计算的data_df即可\n            if pd_is_not_null(self.data_df) and self.computing_window:\n                dfs = []\n                for entity_id, df in self.data_df.groupby(level=0):\n                    latest_laved = get_data(\n                        provider=\"zvt\",\n                        data_schema=self.factor_schema,\n                        entity_id=entity_id,\n                        order=self.factor_schema.timestamp.desc(),\n                        limit=1,\n                        index=[self.category_field, self.time_field],\n                        return_type=\"domain\",\n                    )\n                    if latest_laved:\n                        df1 = df[df.timestamp < latest_laved[0].timestamp].iloc[-self.computing_window :]\n                        if pd_is_not_null(df1):\n                            df = df[df.timestamp >= df1.iloc[0].timestamp]\n                    dfs.append(df)\n\n                self.data_df = pd.concat(dfs)\n\n        self.register_data_listener(self)\n\n        #: the compute logic is not triggered from load data\n        #: for the case:1)load factor from db 2)compute the result\n        if self.only_load_factor:\n            self.compute()\n\n    def load_data(self):\n        if self.only_load_factor:\n            return\n        super().load_data()\n\n    def load_factor(self):\n        if self.only_compute_factor:\n            #: 如果只是为了计算因子，只需要读取acc_window的factor_df\n            if self.accumulator is not None:\n                self.factor_df = self.load_window_df(\n                    provider=\"zvt\", data_schema=self.factor_schema, window=self.accumulator.acc_window\n                )\n        else:\n            self.factor_df = get_data(\n                provider=\"zvt\",\n                data_schema=self.factor_schema,\n                start_timestamp=self.start_timestamp,\n                entity_ids=self.entity_ids,\n                end_timestamp=self.end_timestamp,\n                index=[self.category_field, self.time_field],\n            )\n\n        self.decode_factor_df(self.factor_df)\n\n    def decode_factor_df(self, df):\n        col_map_object_hook = self.factor_col_map_object_hook()\n        if pd_is_not_null(df) and col_map_object_hook:\n            for col in col_map_object_hook:\n                if col in df.columns:\n                    df[col] = df[col].apply(\n                        lambda x: json.loads(x, object_hook=col_map_object_hook.get(col)) if x else None\n                    )\n\n    def factor_col_map_object_hook(self) -> dict:\n        \"\"\"\n\n        :return:{col:object_hook}\n        \"\"\"\n        return {}\n\n    def clear_state_data(self, entity_id=None):\n        super().clear_state_data(entity_id=entity_id)\n        if entity_id:\n            del_data(self.factor_schema, filters=[self.factor_schema.entity_id == entity_id], provider=\"zvt\")\n        else:\n            del_data(self.factor_schema, provider=\"zvt\")\n\n    def pre_compute(self):\n        if not self.only_load_factor and not pd_is_not_null(self.pipe_df):\n            self.pipe_df = self.data_df\n\n    def do_compute(self):\n        self.logger.info(\"compute factor start\")\n        self.compute_factor()\n        self.logger.info(\"compute factor finish\")\n\n        self.logger.info(\"compute result start\")\n        self.compute_result()\n        self.logger.info(\"compute result finish\")\n\n    def compute_factor(self):\n        if self.only_load_factor:\n            return\n        #: 无状态的转换运算\n        if pd_is_not_null(self.data_df) and self.transformer:\n            self.pipe_df = self.transformer.transform(self.data_df)\n        else:\n            self.pipe_df = self.data_df\n\n        #: 有状态的累加运算\n        if pd_is_not_null(self.pipe_df) and self.accumulator:\n            self.factor_df, self.states = self.accumulator.acc(self.pipe_df, self.factor_df, self.states)\n        else:\n            self.factor_df = self.pipe_df\n\n    def compute_result(self):\n        if pd_is_not_null(self.factor_df):\n            cols = []\n            if is_filter_result_df(self.factor_df):\n                cols.append(\"filter_result\")\n            if is_score_result_df(self.factor_df):\n                cols.append(\"score_result\")\n\n            if cols:\n                self.result_df = self.factor_df[cols]\n\n    def after_compute(self):\n        if self.only_load_factor:\n            return\n        if self.keep_all_timestamp:\n            self.fill_gap()\n\n        if self.need_persist and pd_is_not_null(self.factor_df):\n            self.persist_factor()\n\n    def compute(self):\n        self.pre_compute()\n\n        self.logger.info(f\"[[[ ~~~~~~~~factor:{self.name} ~~~~~~~~]]]\")\n        self.logger.info(\"do_compute start\")\n        start_time = time.time()\n        self.do_compute()\n        cost_time = time.time() - start_time\n        self.logger.info(\"do_compute finished,cost_time:{}s\".format(cost_time))\n\n        self.logger.info(\"after_compute start\")\n        start_time = time.time()\n        self.after_compute()\n        cost_time = time.time() - start_time\n        self.logger.info(\"after_compute finished,cost_time:{}s\".format(cost_time))\n        self.logger.info(f\"[[[ ^^^^^^^^factor:{self.name} ^^^^^^^^]]]\")\n\n    def drawer_main_df(self) -> Optional[pd.DataFrame]:\n        if self.only_load_factor:\n            return self.factor_df\n        return self.data_df\n\n    def drawer_factor_df_list(self) -> Optional[List[pd.DataFrame]]:\n        if (self.transformer is not None or self.accumulator is not None) and pd_is_not_null(self.factor_df):\n            indicators = None\n            if self.transformer is not None:\n                indicators = self.transformer.indicators\n            elif self.accumulator is not None:\n                indicators = self.accumulator.indicators\n\n            if indicators:\n                return [self.factor_df[indicators]]\n            else:\n                return [self.factor_df]\n        return None\n\n    def drawer_sub_df_list(self) -> Optional[List[pd.DataFrame]]:\n        if (self.transformer is not None or self.accumulator is not None) and pd_is_not_null(self.result_df):\n            return [self.result_df]\n        return None\n\n    def drawer_annotation_df(self) -> Optional[pd.DataFrame]:\n        def order_type_flag(order_type):\n            if order_type is None:\n                return None\n            if order_type:\n                return \"B\"\n            if not order_type:\n                return \"S\"\n\n        def order_type_color(order_type):\n            if order_type:\n                return \"#ec0000\"\n            else:\n                return \"#00da3c\"\n\n        if is_filter_result_df(self.result_df):\n            annotation_df = self.result_df[[\"filter_result\"]].copy()\n            annotation_df = annotation_df[~annotation_df[\"filter_result\"].isna()]\n            annotation_df = drop_continue_duplicate(annotation_df, \"filter_result\")\n            annotation_df[\"value\"] = self.factor_df.loc[annotation_df.index][\"close\"]\n            annotation_df[\"flag\"] = annotation_df[\"filter_result\"].apply(lambda x: order_type_flag(x))\n            annotation_df[\"color\"] = annotation_df[\"filter_result\"].apply(lambda x: order_type_color(x))\n            return annotation_df\n\n    def fill_gap(self):\n        #: 该操作较慢，只适合做基本面的运算\n        idx = pd.date_range(self.start_timestamp, self.end_timestamp)\n        new_index = pd.MultiIndex.from_product(\n            [self.result_df.index.levels[0], idx], names=[self.category_field, self.time_field]\n        )\n        self.result_df = self.result_df.loc[~self.result_df.index.duplicated(keep=\"first\")]\n        self.result_df = self.result_df.reindex(new_index)\n        self.result_df = self.result_df.groupby(level=0).fillna(method=self.fill_method, limit=self.effective_number)\n\n    def add_entities(self, entity_ids):\n        if (self.entity_ids and entity_ids) and (set(self.entity_ids) == set(entity_ids)):\n            self.logger.info(f\"current: {self.entity_ids}\")\n            self.logger.info(f\"refresh: {entity_ids}\")\n            return\n        new_entity_ids = None\n        if entity_ids:\n            new_entity_ids = list(set(entity_ids) - set(self.entity_ids))\n            self.entity_ids = list(set(self.entity_ids + entity_ids))\n\n        if new_entity_ids:\n            self.logger.info(f\"added new entity: {new_entity_ids}\")\n            if not self.only_load_factor:\n                new_data_df = self.data_schema.query_data(\n                    entity_ids=new_entity_ids,\n                    provider=self.provider,\n                    columns=self.columns,\n                    start_timestamp=self.start_timestamp,\n                    end_timestamp=self.end_timestamp,\n                    filters=self.filters,\n                    order=self.order,\n                    limit=self.limit,\n                    level=self.level,\n                    index=[self.category_field, self.time_field],\n                    time_field=self.time_field,\n                )\n                self.data_df = pd.concat([self.data_df, new_data_df], sort=False)\n                self.data_df.sort_index(level=[0, 1], inplace=True)\n\n            new_factor_df = get_data(\n                provider=\"zvt\",\n                data_schema=self.factor_schema,\n                start_timestamp=self.start_timestamp,\n                entity_ids=new_entity_ids,\n                end_timestamp=self.end_timestamp,\n                index=[self.category_field, self.time_field],\n            )\n            self.decode_factor_df(new_factor_df)\n\n            self.factor_df = pd.concat([self.factor_df, new_factor_df], sort=False)\n            self.factor_df.sort_index(level=[0, 1], inplace=True)\n\n    def on_data_loaded(self, data: pd.DataFrame):\n        self.compute()\n\n    def on_data_changed(self, data: pd.DataFrame):\n        \"\"\"\n        overwrite it for computing after data added\n\n        :param data:\n        \"\"\"\n        self.compute()\n\n    def on_entity_data_changed(self, entity, added_data: pd.DataFrame):\n        \"\"\"\n        overwrite it for computing after entity data added\n\n        :param entity:\n        :param added_data:\n        \"\"\"\n        pass\n\n    def persist_factor(self):\n        df = self.factor_df.copy()\n        #: encode json columns\n        if pd_is_not_null(df) and self.factor_col_map_object_hook():\n            for col in self.factor_col_map_object_hook():\n                if col in df.columns:\n                    df[col] = df[col].apply(lambda x: json.dumps(x, cls=self.state_encoder()))\n\n        if self.states:\n            g = df.groupby(level=0)\n            for entity_id in self.states:\n                state = self.states[entity_id]\n                try:\n                    if state:\n                        self.persist_state(entity_id=entity_id)\n                    if entity_id in g.groups:\n                        df_to_db(\n                            df=df.loc[(entity_id,)], data_schema=self.factor_schema, provider=\"zvt\", force_update=False\n                        )\n                except Exception as e:\n                    self.logger.error(f\"{self.name} {entity_id} save state error\")\n                    self.logger.exception(e)\n                    #: clear them if error happen\n                    self.clear_state_data(entity_id)\n        else:\n            df_to_db(df=df, data_schema=self.factor_schema, provider=\"zvt\", force_update=False)\n\n    def get_filter_df(self):\n        if is_filter_result_df(self.result_df):\n            return self.result_df[[\"filter_result\"]]\n\n    def get_score_df(self):\n        if is_score_result_df(self.result_df):\n            return self.result_df[[\"score_result\"]]\n\n    def get_trading_signal_df(self):\n        df = self.result_df[[\"filter_result\"]].copy()\n        df = df[~df[\"filter_result\"].isna()]\n        df = drop_continue_duplicate(df, \"filter_result\")\n        return df\n\n    def get_targets(\n        self,\n        timestamp=None,\n        start_timestamp=None,\n        end_timestamp=None,\n        target_type: TargetType = TargetType.positive,\n        positive_threshold=0.8,\n        negative_threshold=-0.8,\n    ):\n        if timestamp and (start_timestamp or end_timestamp):\n            raise ValueError(\"Use timestamp or (start_timestamp, end_timestamp)\")\n        # select by filter\n        filter_df = self.get_filter_df()\n        selected_df = None\n        target_df = None\n        if pd_is_not_null(filter_df):\n            if target_type == TargetType.positive:\n                selected_df = filter_df[filter_df[\"filter_result\"] == True]\n            elif target_type == TargetType.negative:\n                selected_df = filter_df[filter_df[\"filter_result\"] == False]\n            else:\n                selected_df = filter_df[filter_df[\"filter_result\"].isna()]\n\n        # select by score\n        score_df = self.get_score_df()\n        if pd_is_not_null(score_df):\n            if pd_is_not_null(selected_df):\n                # filter at first\n                score_df = score_df.loc[selected_df.index, :]\n            if target_type == TargetType.positive:\n                selected_df = score_df[score_df[\"score_result\"] >= positive_threshold]\n            elif target_type == TargetType.negative:\n                selected_df = score_df[score_df[\"score_result\"] <= negative_threshold]\n            else:\n                selected_df = score_df[\n                    (score_df[\"score_result\"] > negative_threshold) & (score_df[\"score\"] < positive_threshold)\n                ]\n\n        self.logger.debug(selected_df)\n\n        if pd_is_not_null(selected_df):\n            selected_df = selected_df.reset_index(level=\"entity_id\")\n            if timestamp:\n                if to_pd_timestamp(timestamp) in selected_df.index:\n                    target_df = selected_df.loc[[to_pd_timestamp(timestamp)], [\"entity_id\"]]\n            else:\n                target_df = selected_df.loc[\n                    slice(to_pd_timestamp(start_timestamp), to_pd_timestamp(end_timestamp)), [\"entity_id\"]\n                ]\n\n        if pd_is_not_null(target_df):\n            return target_df[\"entity_id\"].tolist()\n        return []\n\n\nclass ScoreFactor(Factor):\n    scorer: Scorer = None\n\n    def compute_result(self):\n        super().compute_result()\n        if pd_is_not_null(self.factor_df) and self.scorer:\n            self.result_df = self.scorer.score(self.factor_df)\n\n\n# the __all__ is generated\n__all__ = [\"TargetType\", \"Indicator\", \"Transformer\", \"Accumulator\", \"Scorer\", \"FactorMeta\", \"Factor\", \"ScoreFactor\"]\n"
  },
  {
    "path": "src/zvt/contract/model.py",
    "content": "# -*- coding: utf-8 -*-\nfrom datetime import datetime\n\nfrom pydantic import BaseModel, ConfigDict\n\n\nclass CustomModel(BaseModel):\n    model_config = ConfigDict(from_attributes=True, allow_inf_nan=True)\n\n\nclass MixinModel(CustomModel):\n    id: str\n    entity_id: str\n    timestamp: datetime\n\n\n# the __all__ is generated\n__all__ = [\"CustomModel\", \"MixinModel\"]\n"
  },
  {
    "path": "src/zvt/contract/normal_data.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.utils.pd_utils import pd_is_not_null, fill_with_same_index, normal_index_df, is_normal_df\n\n\nclass NormalData(object):\n    table_type_sample = None\n\n    def __init__(self, df, category_field=\"entity_id\", time_field=\"timestamp\", fill_index: bool = False) -> None:\n        self.data_df = df\n        self.category_field = category_field\n        self.time_field = time_field\n        self.fill_index = fill_index\n\n        self.entity_ids = []\n        self.df_list = []\n        self.entity_map_df = {}\n\n        self.normalize()\n\n    def normalize(self):\n        \"\"\"\n        normalize data_df to::\n\n                                        col1    col2    col3\n            entity_id    timestamp\n\n        \"\"\"\n        if pd_is_not_null(self.data_df):\n            if not is_normal_df(self.data_df):\n                self.data_df = normal_index_df(self.data_df, self.category_field, self.time_field)\n\n            self.entity_ids = self.data_df.index.levels[0].to_list()\n\n            for entity_id in self.entity_ids:\n                df = self.data_df.loc[(entity_id,)]\n                self.df_list.append(df)\n                self.entity_map_df[entity_id] = df\n\n            if len(self.df_list) > 1 and self.fill_index:\n                self.df_list = fill_with_same_index(df_list=self.df_list)\n\n    def empty(self):\n        return not pd_is_not_null(self.data_df)\n\n\n# the __all__ is generated\n__all__ = [\"NormalData\"]\n"
  },
  {
    "path": "src/zvt/contract/reader.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\nfrom typing import List, Union, Type, Optional\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.api import get_entities\nfrom zvt.contract.drawer import Drawable\nfrom zvt.contract.schema import Mixin, TradableEntity\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_pd_timestamp, now_pd_timestamp\n\n\nclass DataListener(object):\n    def on_data_loaded(self, data: pd.DataFrame) -> object:\n        \"\"\"\n\n        :param data:\n        \"\"\"\n        raise NotImplementedError\n\n    def on_data_changed(self, data: pd.DataFrame) -> object:\n        \"\"\"\n\n        :param data:\n        \"\"\"\n        raise NotImplementedError\n\n    def on_entity_data_changed(self, entity: str, added_data: pd.DataFrame) -> object:\n        \"\"\"\n\n        :param entity: the entity\n        :param added_data: the data added for the entity\n        \"\"\"\n        pass\n\n\nclass DataReader(Drawable):\n    logger = logging.getLogger(__name__)\n\n    def __init__(\n        self,\n        data_schema: Type[Mixin],\n        entity_schema: Type[TradableEntity] = None,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = now_pd_timestamp(),\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: IntervalLevel = None,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n    ) -> None:\n        self.logger = logging.getLogger(self.__class__.__name__)\n\n        self.data_schema = data_schema\n        self.entity_schema = entity_schema\n        self.provider = provider\n        self.entity_provider = entity_provider\n        self.start_timestamp = start_timestamp\n        self.end_timestamp = end_timestamp\n        self.start_timestamp = to_pd_timestamp(self.start_timestamp)\n        self.end_timestamp = to_pd_timestamp(self.end_timestamp)\n        self.exchanges = exchanges\n        self.codes = codes\n        self.entity_ids = entity_ids\n\n        # 转换成标准entity_id\n        if entity_schema and not self.entity_ids:\n            df = get_entities(\n                entity_schema=entity_schema, provider=self.entity_provider, exchanges=self.exchanges, codes=self.codes\n            )\n            if pd_is_not_null(df):\n                self.entity_ids = df[\"entity_id\"].to_list()\n\n        self.filters = filters\n        self.order = order\n        self.limit = limit\n\n        if level:\n            self.level = IntervalLevel(level)\n        else:\n            self.level = level\n\n        self.category_field = category_field\n        self.time_field = time_field\n        self.computing_window = keep_window\n\n        self.category_col = eval(\"self.data_schema.{}\".format(self.category_field))\n        self.time_col = eval(\"self.data_schema.{}\".format(self.time_field))\n\n        self.columns = columns\n\n        if self.columns:\n            # always add category_column and time_field for normalizing\n            self.columns = list(set(self.columns) | {self.category_field, self.time_field})\n\n        self.data_listeners: List[DataListener] = []\n\n        self.data_df: pd.DataFrame = None\n\n        self.load_data()\n\n    def load_window_df(self, provider, data_schema, window):\n        window_df = None\n\n        dfs = []\n        for entity_id in self.entity_ids:\n            df = data_schema.query_data(\n                provider=provider,\n                index=[self.category_field, self.time_field],\n                order=data_schema.timestamp.desc(),\n                entity_id=entity_id,\n                limit=window,\n            )\n            if pd_is_not_null(df):\n                dfs.append(df)\n        if dfs:\n            window_df = pd.concat(dfs)\n            window_df = window_df.sort_index(level=[0, 1])\n        return window_df\n\n    def load_data(self):\n        self.logger.info(\"load_data start\")\n        start_time = time.time()\n        params = dict(\n            entity_size=len(self.entity_ids) if self.entity_ids != None else None,\n            provider=self.provider,\n            columns=self.columns,\n            start_timestamp=self.start_timestamp,\n            end_timestamp=self.end_timestamp,\n            filters=self.filters,\n            order=self.order,\n            limit=self.limit,\n            level=self.level,\n            index=[self.category_field, self.time_field],\n            time_field=self.time_field,\n        )\n        self.logger.info(f\"query_data params:{params}\")\n\n        self.data_df = self.data_schema.query_data(\n            entity_ids=self.entity_ids,\n            provider=self.provider,\n            columns=self.columns,\n            start_timestamp=self.start_timestamp,\n            end_timestamp=self.end_timestamp,\n            filters=self.filters,\n            order=self.order,\n            limit=self.limit,\n            level=self.level,\n            index=[self.category_field, self.time_field],\n            time_field=self.time_field,\n        )\n\n        cost_time = time.time() - start_time\n        self.logger.info(\"load_data finished, cost_time:{}\".format(cost_time))\n\n        for listener in self.data_listeners:\n            listener.on_data_loaded(self.data_df)\n\n    def move_on(self, to_timestamp: Union[str, pd.Timestamp] = None, timeout: int = 20) -> object:\n        \"\"\"\n        using continual fetching data in realtime\n        1)get the data happened before to_timestamp,if not set,get all the data which means to now\n        2)if computing_window set,the data_df would be cut for saving memory\n\n\n        :param to_timestamp:\n        :type to_timestamp:\n        :param timeout:\n        :type timeout: int\n        :return:\n        :rtype:\n        \"\"\"\n\n        if not pd_is_not_null(self.data_df):\n            self.load_data()\n            return\n\n        start_time = time.time()\n\n        has_got = []\n        dfs = []\n        changed = False\n        while True:\n            for entity_id, df in self.data_df.groupby(level=0):\n                if entity_id in has_got:\n                    continue\n\n                recorded_timestamp = df.index.levels[1].max()\n\n                #: move_on读取数据，表明之前的数据已经处理完毕，只需要保留computing_window的数据\n                if self.computing_window:\n                    df = df.iloc[-self.computing_window :]\n\n                added_filter = [self.category_col == entity_id, self.time_col > recorded_timestamp]\n                if self.filters:\n                    filters = self.filters + added_filter\n                else:\n                    filters = added_filter\n\n                added_df = self.data_schema.query_data(\n                    provider=self.provider,\n                    columns=self.columns,\n                    end_timestamp=to_timestamp,\n                    filters=filters,\n                    level=self.level,\n                    index=[self.category_field, self.time_field],\n                )\n\n                if pd_is_not_null(added_df):\n                    self.logger.info(f'got new data:{df.to_json(orient=\"records\", force_ascii=False)}')\n\n                    for listener in self.data_listeners:\n                        listener.on_entity_data_changed(entity=entity_id, added_data=added_df)\n                    #: if got data,just move to another entity_id\n                    changed = True\n                    has_got.append(entity_id)\n                    # df = df.append(added_df, sort=False)\n                    df = pd.concat([df, added_df], sort=False)\n                    dfs.append(df)\n                else:\n                    cost_time = time.time() - start_time\n                    if cost_time > timeout:\n                        #: if timeout,just add the old data\n                        has_got.append(entity_id)\n                        dfs.append(df)\n                        self.logger.warning(\n                            \"category:{} level:{} getting data timeout,to_timestamp:{},now:{}\".format(\n                                entity_id, self.level, to_timestamp, now_pd_timestamp()\n                            )\n                        )\n                        continue\n\n            if len(has_got) == len(self.data_df.index.levels[0]):\n                break\n\n        if dfs:\n            self.data_df = pd.concat(dfs, sort=False)\n            self.data_df.sort_index(level=[0, 1], inplace=True)\n\n            if changed:\n                for listener in self.data_listeners:\n                    listener.on_data_changed(self.data_df)\n\n    def register_data_listener(self, listener):\n        if listener not in self.data_listeners:\n            self.data_listeners.append(listener)\n\n        #: notify it once after registered\n        if pd_is_not_null(self.data_df):\n            listener.on_data_loaded(self.data_df)\n\n    def deregister_data_listener(self, listener):\n        if listener in self.data_listeners:\n            self.data_listeners.remove(listener)\n\n    def empty(self):\n        return not pd_is_not_null(self.data_df)\n\n    def drawer_main_df(self) -> Optional[pd.DataFrame]:\n        return self.data_df\n\n\nif __name__ == \"__main__\":\n    from zvt.domain import Stock1dKdata, Stock\n\n    data_reader = DataReader(\n        data_schema=Stock1dKdata,\n        entity_schema=Stock,\n        codes=[\"002572\", \"000338\"],\n        start_timestamp=\"2017-01-01\",\n        end_timestamp=\"2019-06-10\",\n    )\n\n    data_reader.draw(show=True)\n\n\n# the __all__ is generated\n__all__ = [\"DataListener\", \"DataReader\"]\n"
  },
  {
    "path": "src/zvt/contract/recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\nimport uuid\nfrom typing import List\n\nimport pandas as pd\nimport requests\nfrom sqlalchemy.orm import Session\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.api import get_db_session, get_schema_columns\nfrom zvt.contract.api import get_entities, get_data\nfrom zvt.contract.base_service import OneStateService\nfrom zvt.contract.schema import Mixin, TradableEntity\nfrom zvt.contract.utils import is_in_same_interval, evaluate_size_from_timestamp\nfrom zvt.contract.zvt_info import RecorderState\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import (\n    to_pd_timestamp,\n    TIME_FORMAT_DAY,\n    to_date_time_str,\n    now_pd_timestamp,\n    now_date_time_str,\n)\nfrom zvt.utils.utils import fill_domain_from_dict\n\n\ndef _register_kdata_schemas_for_recorder(cls):\n    \"\"\"\n    Pre-register concrete kdata schemas (Stock1dKdata, etc.) for recorders that define\n    supported_levels. KdataCommon is a placeholder; the real schemas are per (level, adjust_type).\n    Must have: entity_schema, data_schema (KdataCommon), supported_levels, provider.\n    \"\"\"\n    if not (\n        hasattr(cls, \"entity_schema\")\n        and cls.entity_schema\n        and hasattr(cls, \"data_schema\")\n        and cls.data_schema\n        and cls.data_schema.__name__.endswith(\"KdataCommon\")\n        and hasattr(cls, \"supported_levels\")\n        and cls.supported_levels\n        and hasattr(cls, \"provider\")\n        and cls.provider\n    ):\n        return False\n    from zvt.api.kdata import get_kdata_schema\n    from zvt.contract import zvt_context\n\n    entity_type = cls.entity_schema.__name__.lower()\n    adjust_types = getattr(cls, \"supported_adjust_types\", [None])\n    for level in cls.supported_levels:\n        for adjust_type in adjust_types:\n            try:\n                schema = get_kdata_schema(entity_type=entity_type, level=level, adjust_type=adjust_type)\n                if schema:\n                    schema.register_recorder_cls(cls.provider, cls)\n                    db_name = getattr(schema, \"_zvt_db_name\", None)\n                    if db_name:\n                        if cls.provider not in zvt_context.providers:\n                            zvt_context.providers.append(cls.provider)\n                        if not zvt_context.provider_map_dbnames.get(cls.provider):\n                            zvt_context.provider_map_dbnames[cls.provider] = []\n                        if db_name not in zvt_context.provider_map_dbnames[cls.provider]:\n                            zvt_context.provider_map_dbnames[cls.provider].append(db_name)\n            except Exception:\n                pass\n    return True\n\n\nclass Meta(type):\n    def __new__(meta, name, bases, class_dict):\n        cls = type.__new__(meta, name, bases, class_dict)\n        if hasattr(cls, \"data_schema\") and hasattr(cls, \"provider\"):\n            if cls.data_schema and issubclass(cls.data_schema, Mixin):\n                if _register_kdata_schemas_for_recorder(cls):\n                    pass\n                else:\n                    cls.data_schema.register_recorder_cls(cls.provider, cls)\n                    from zvt.contract import zvt_context\n                    if cls.provider not in zvt_context.providers:\n                        zvt_context.providers.append(cls.provider)\n                    if not zvt_context.provider_map_dbnames.get(cls.provider):\n                        zvt_context.provider_map_dbnames[cls.provider] = []\n                    db_name = getattr(cls.data_schema, \"_zvt_db_name\", None)\n                    if db_name and db_name not in zvt_context.provider_map_dbnames[cls.provider]:\n                        zvt_context.provider_map_dbnames[cls.provider].append(db_name)\n        return cls\n\n\nclass Recorder(OneStateService, metaclass=Meta):\n    #: overwrite them to set up the data you want to record\n    provider: str = None\n    data_schema: Mixin = None\n\n    #: original page url\n    original_page_url = None\n    #: request url\n    url = None\n\n    state_schema = RecorderState\n\n    def __init__(self, force_update: bool = False, sleeping_time: int = 10) -> None:\n        super().__init__()\n        self.logger = logging.getLogger(self.__class__.__name__)\n\n        assert self.provider is not None\n        assert self.data_schema is not None\n        # Register concrete schema for kdata Recorders that set data_schema in __init__\n        # (e.g. via get_kdata_schema); metaclass only runs at class-def time when data_schema may be None\n        self.data_schema.register_recorder_cls(self.provider, self.__class__)\n        from zvt.contract import zvt_context\n        if self.provider not in zvt_context.providers:\n            zvt_context.providers.append(self.provider)\n        if not zvt_context.provider_map_dbnames.get(self.provider):\n            zvt_context.provider_map_dbnames[self.provider] = []\n        db_name = getattr(self.data_schema, \"_zvt_db_name\", None)\n        if db_name and db_name not in zvt_context.provider_map_dbnames[self.provider]:\n            zvt_context.provider_map_dbnames[self.provider].append(db_name)\n        if self.provider not in self.data_schema.get_providers():\n            self.logger.error(\n                f\"provider: {self.provider} is not registered for {self.data_schema}({self.data_schema.get_providers()})\"\n            )\n            assert False\n\n        self.force_update = force_update\n        self.sleeping_time = sleeping_time\n\n        #: using to do db operations\n        self.session = get_db_session(provider=self.provider, data_schema=self.data_schema)\n        self.http_session = requests.Session()\n\n    def run(self):\n        raise NotImplementedError\n\n    def sleep(self, seconds=None):\n        if seconds:\n            sleeping_time = seconds\n        else:\n            sleeping_time = self.sleeping_time\n\n        if sleeping_time and sleeping_time > 0:\n            self.logger.info(f\"sleeping {sleeping_time} seconds\")\n            time.sleep(self.sleeping_time)\n\n\nclass EntityEventRecorder(Recorder):\n    #: overwrite them to fetch the entity list\n    entity_provider: str = None\n    entity_schema: TradableEntity = None\n\n    def __init__(\n        self,\n        force_update=False,\n        sleeping_time=10,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        end_timestamp=None,\n        entity_filters=None,\n        ignore_failed=True,\n        return_unfinished=False,\n    ) -> None:\n        \"\"\"\n        :param latest_trading_date:\n        :param code:\n        :param ignore_failed:\n        :param entity_filters:\n        :param exchanges:\n        :param entity_id: for record single entity\n        :param entity_ids: set entity_ids or (entity_type,exchanges,codes)\n        :param codes:\n        :param day_data: one record per day,set to True if you want skip recording it when data of today exist\n        :param force_update:\n        :param sleeping_time:\n        \"\"\"\n        super().__init__(force_update=force_update, sleeping_time=sleeping_time)\n\n        assert self.entity_provider is not None\n        assert self.entity_schema is not None\n\n        #: setup the entities you want to record\n        self.exchanges = exchanges\n        if codes is None and code is not None:\n            self.codes = [code]\n        else:\n            self.codes = codes\n        self.day_data = day_data\n        self.end_timestamp = end_timestamp\n\n        #: set entity_ids or (entity_type,exchanges,codes)\n        self.entity_ids = None\n        if entity_id:\n            self.entity_ids = [entity_id]\n        if entity_ids:\n            self.entity_ids = entity_ids\n        self.entity_filters = entity_filters\n        self.ignore_failed = ignore_failed\n        self.return_unfinished = return_unfinished\n\n        self.entity_session: Session = None\n        self.entities: List = None\n        self.init_entities()\n\n    def init_entities(self):\n        \"\"\"\n        init the entities which we would record data for\n\n        \"\"\"\n        if self.entity_provider == self.provider and self.entity_schema == self.data_schema:\n            self.entity_session = self.session\n        else:\n            self.entity_session = get_db_session(provider=self.entity_provider, data_schema=self.entity_schema)\n\n        if self.day_data:\n            if not self.end_timestamp:\n                self.end_timestamp = now_date_time_str()\n            df = self.data_schema.query_data(\n                start_timestamp=self.end_timestamp, columns=[\"entity_id\", \"timestamp\"], provider=self.provider\n            )\n            if pd_is_not_null(df):\n                entity_ids = df[\"entity_id\"].tolist()\n                self.logger.info(f\"ignore entity_ids:{entity_ids}\")\n                if self.entity_filters:\n                    self.entity_filters.append(self.entity_schema.entity_id.notin_(entity_ids))\n                else:\n                    self.entity_filters = [self.entity_schema.entity_id.notin_(entity_ids)]\n\n        #: init the entity list\n        self.entities = get_entities(\n            session=self.entity_session,\n            entity_schema=self.entity_schema,\n            exchanges=self.exchanges,\n            entity_ids=self.entity_ids,\n            codes=self.codes,\n            return_type=\"domain\",\n            provider=self.entity_provider,\n            filters=self.entity_filters,\n        )\n\n\nclass TimeSeriesDataRecorder(EntityEventRecorder):\n    default_size = 2000\n\n    def __init__(\n        self,\n        force_update=False,\n        sleeping_time=5,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"add\",\n        start_timestamp=None,\n        end_timestamp=None,\n        return_unfinished=False,\n    ) -> None:\n        self.start_timestamp = to_pd_timestamp(start_timestamp)\n        self.end_timestamp = to_pd_timestamp(end_timestamp)\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code=code,\n            codes=codes,\n            day_data=day_data,\n            end_timestamp=self.end_timestamp,\n            entity_filters=entity_filters,\n            ignore_failed=ignore_failed,\n            return_unfinished=return_unfinished,\n        )\n\n        self.real_time = real_time\n        self.close_hour, self.close_minute = self.entity_schema.get_close_hour_and_minute()\n        self.fix_duplicate_way = fix_duplicate_way\n\n    def get_latest_saved_record(self, entity):\n        order = eval(\"self.data_schema.{}.desc()\".format(self.get_evaluated_time_field()))\n\n        records = get_data(\n            entity_id=entity.id,\n            provider=self.provider,\n            data_schema=self.data_schema,\n            order=order,\n            limit=1,\n            return_type=\"domain\",\n            session=self.session,\n        )\n        if records:\n            return records[0]\n        return None\n\n    def evaluate_start_end_size_timestamps(self, entity):\n        #: not to list date yet\n        if entity.timestamp and (entity.timestamp >= now_pd_timestamp()):\n            self.logger.info(\"ignore entity: {} list date: {}\", entity.id, entity.timestamp)\n            return entity.timestamp, None, 0, None\n\n        latest_saved_record = self.get_latest_saved_record(entity=entity)\n\n        if latest_saved_record:\n            latest_timestamp = eval(\"latest_saved_record.{}\".format(self.get_evaluated_time_field()))\n        else:\n            latest_timestamp = entity.timestamp\n\n        if not latest_timestamp:\n            return self.start_timestamp, self.end_timestamp, self.default_size, None\n\n        if self.start_timestamp:\n            latest_timestamp = max(latest_timestamp, self.start_timestamp)\n\n        size = self.default_size\n        if self.end_timestamp:\n            if latest_timestamp > self.end_timestamp:\n                size = 0\n\n        return latest_timestamp, self.end_timestamp, size, None\n\n    def get_data_map(self):\n        \"\"\"\n        {'original_field':('domain_field',transform_func)}\n\n        \"\"\"\n        return {}\n\n    def record(self, entity, start, end, size, timestamps):\n        \"\"\"\n        implement the recording logic in this method, should return json or domain list\n\n        :param entity:\n        :type entity:\n        :param start:\n        :type start:\n        :param end:\n        :type end:\n        :param size:\n        :type size:\n        :param timestamps:\n        :type timestamps:\n        \"\"\"\n        raise NotImplementedError\n\n    def get_evaluated_time_field(self):\n        \"\"\"\n        the timestamp field for evaluating time range of recorder,used in get_latest_saved_record\n\n        \"\"\"\n        return \"timestamp\"\n\n    def get_original_time_field(self):\n        return \"timestamp\"\n\n    def generate_domain_id(self, entity, original_data, time_fmt=TIME_FORMAT_DAY):\n        \"\"\"\n        generate domain id from the entity and original data,the default id meaning:entity + event happen time\n\n        :param entity:\n        :type entity:\n        :param original_data:\n        :type original_data:\n        :param time_fmt:\n        :type time_fmt:\n        :return:\n        :rtype:\n        \"\"\"\n        timestamp = to_date_time_str(original_data[self.get_original_time_field()], fmt=time_fmt)\n        return \"{}_{}\".format(entity.id, timestamp)\n\n    def generate_domain(self, entity, original_data):\n        \"\"\"\n        generate the data_schema instance using entity and original_data,the original_data is from record result\n\n        :param entity:\n        :param original_data:\n        \"\"\"\n\n        got_new_data = False\n\n        #: if the domain is directly generated in record method, we just return it\n        if isinstance(original_data, self.data_schema):\n            got_new_data = True\n            return got_new_data, original_data\n\n        the_id = self.generate_domain_id(entity, original_data)\n\n        #: optional way\n        #: item = self.session.query(self.data_schema).get(the_id)\n\n        items = get_data(\n            data_schema=self.data_schema,\n            session=self.session,\n            provider=self.provider,\n            entity_id=entity.id,\n            filters=[self.data_schema.id == the_id],\n            return_type=\"domain\",\n        )\n\n        if items and not self.force_update:\n            self.logger.info(\"ignore the data {}:{} saved before\".format(self.data_schema, the_id))\n            return got_new_data, None\n\n        if not items:\n            timestamp_str = original_data[self.get_original_time_field()]\n            timestamp = None\n            try:\n                timestamp = to_pd_timestamp(timestamp_str)\n            except Exception as e:\n                self.logger.exception(e)\n\n            if \"name\" in get_schema_columns(self.data_schema):\n                domain_item = self.data_schema(\n                    id=the_id, code=entity.code, name=entity.name, entity_id=entity.id, timestamp=timestamp\n                )\n            else:\n                domain_item = self.data_schema(id=the_id, code=entity.code, entity_id=entity.id, timestamp=timestamp)\n            got_new_data = True\n        else:\n            domain_item = items[0]\n\n        fill_domain_from_dict(domain_item, original_data, self.get_data_map())\n        return got_new_data, domain_item\n\n    def persist(self, entity, domain_list):\n        \"\"\"\n        persist the domain list to db\n\n        :param entity:\n        :param domain_list:\n        \"\"\"\n        if domain_list:\n            try:\n                if domain_list[0].timestamp >= domain_list[-1].timestamp:\n                    first_timestamp = domain_list[-1].timestamp\n                    last_timestamp = domain_list[0].timestamp\n                else:\n                    first_timestamp = domain_list[0].timestamp\n                    last_timestamp = domain_list[-1].timestamp\n            except:\n                first_timestamp = domain_list[0].timestamp\n                last_timestamp = domain_list[-1].timestamp\n\n            self.logger.info(\n                \"persist {} for entity_id:{},time interval:[{},{}]\".format(\n                    self.data_schema, entity.id, first_timestamp, last_timestamp\n                )\n            )\n\n            self.session.add_all(domain_list)\n            self.session.commit()\n\n    def on_finish(self):\n        try:\n            if self.session:\n                self.session.close()\n\n            if self.entity_session:\n                self.entity_session.close()\n            if self.http_session:\n                self.http_session.close()\n        except Exception as e:\n            self.logger.error(e)\n\n    def on_finish_entity(self, entity):\n        pass\n\n    def run(self):\n        finished_items = []\n        unfinished_items = self.entities\n        raising_exception = None\n        while True:\n            count = len(unfinished_items)\n            for index, entity_item in enumerate(unfinished_items):\n                try:\n                    self.logger.info(f\"run to {index + 1}/{count}\")\n\n                    start_timestamp, end_timestamp, size, timestamps = self.evaluate_start_end_size_timestamps(\n                        entity_item\n                    )\n                    size = int(size)\n\n                    if timestamps:\n                        self.logger.info(\n                            \"entity_id:{},evaluate_start_end_size_timestamps result:{},{},{},{}-{}\".format(\n                                entity_item.id, start_timestamp, end_timestamp, size, timestamps[0], timestamps[-1]\n                            )\n                        )\n                    else:\n                        self.logger.info(\n                            \"entity_id:{},evaluate_start_end_size_timestamps result:{},{},{},{}\".format(\n                                entity_item.id, start_timestamp, end_timestamp, size, timestamps\n                            )\n                        )\n\n                    #: no more to record\n                    if size == 0:\n                        finished_items.append(entity_item)\n                        self.logger.info(\n                            \"finish recording {} for entity_id:{},latest_timestamp:{}\".format(\n                                self.data_schema, entity_item.id, start_timestamp\n                            )\n                        )\n                        self.on_finish_entity(entity_item)\n                        continue\n\n                    #: sleep for a while to next entity\n                    if index != 0:\n                        self.sleep()\n\n                    original_list = self.record(\n                        entity_item, start=start_timestamp, end=end_timestamp, size=size, timestamps=timestamps\n                    )\n\n                    all_duplicated = True\n\n                    if original_list:\n                        domain_list = []\n                        for original_item in original_list:\n                            got_new_data, domain_item = self.generate_domain(entity_item, original_item)\n\n                            if got_new_data:\n                                all_duplicated = False\n\n                            #: handle the case  generate_domain_id generate duplicate id\n                            if domain_item:\n                                duplicate = [item for item in domain_list if item.id == domain_item.id]\n                                if duplicate:\n                                    #: regenerate the id\n                                    if self.fix_duplicate_way == \"add\":\n                                        domain_item.id = \"{}_{}\".format(domain_item.id, uuid.uuid1())\n                                    #: ignore\n                                    else:\n                                        self.logger.info(f\"ignore original duplicate item:{domain_item.id}\")\n                                        continue\n\n                                domain_list.append(domain_item)\n\n                        if domain_list:\n                            self.persist(entity_item, domain_list)\n                        else:\n                            self.logger.info(\"just got {} duplicated data in this cycle\".format(len(original_list)))\n\n                    #: could not get more data\n                    entity_finished = False\n                    if not original_list or all_duplicated:\n                        #: not realtime\n                        if not self.real_time:\n                            entity_finished = True\n\n                        #: realtime and to the close time\n                        if self.real_time and (self.close_hour is not None) and (self.close_minute is not None):\n                            current_timestamp = pd.Timestamp.now()\n                            if current_timestamp.hour >= self.close_hour:\n                                if current_timestamp.minute - self.close_minute >= 5:\n                                    self.logger.info(\n                                        \"{} now is the close time:{}\".format(entity_item.id, current_timestamp)\n                                    )\n\n                                    entity_finished = True\n\n                    #: add finished entity to finished_items\n                    if entity_finished:\n                        finished_items.append(entity_item)\n\n                        latest_saved_record = self.get_latest_saved_record(entity=entity_item)\n                        if latest_saved_record:\n                            start_timestamp = eval(\"latest_saved_record.{}\".format(self.get_evaluated_time_field()))\n\n                        self.logger.info(\n                            \"finish recording {} for entity_id:{},latest_timestamp:{}\".format(\n                                self.data_schema, entity_item.id, start_timestamp\n                            )\n                        )\n                        self.on_finish_entity(entity_item)\n                        continue\n\n                except Exception as e:\n                    self.logger.exception(\n                        \"recording data for entity_id:{},{},error:{}\".format(entity_item.id, self.data_schema, e)\n                    )\n                    raising_exception = e\n                    if self.return_unfinished:\n                        self.on_finish()\n                        unfinished_items = set(unfinished_items) - set(finished_items)\n                        return [item.entity_id for item in unfinished_items]\n\n                    finished_items = unfinished_items\n                    break\n\n            unfinished_items = set(unfinished_items) - set(finished_items)\n\n            if len(unfinished_items) == 0:\n                break\n\n        self.on_finish()\n        if self.return_unfinished:\n            return []\n\n        if raising_exception:\n            raise raising_exception\n\n\nclass FixedCycleDataRecorder(TimeSeriesDataRecorder):\n    def __init__(\n        self,\n        force_update=True,\n        sleeping_time=10,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"ignore\",\n        start_timestamp=None,\n        end_timestamp=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        kdata_use_begin_time=False,\n        one_day_trading_minutes=24 * 60,\n        return_unfinished=False,\n    ) -> None:\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code=code,\n            codes=codes,\n            day_data=day_data,\n            entity_filters=entity_filters,\n            ignore_failed=ignore_failed,\n            real_time=real_time,\n            fix_duplicate_way=fix_duplicate_way,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            return_unfinished=return_unfinished,\n        )\n\n        self.level = IntervalLevel(level)\n        self.kdata_use_begin_time = kdata_use_begin_time\n        self.one_day_trading_minutes = one_day_trading_minutes\n\n    def get_latest_saved_record(self, entity):\n        order = eval(\"self.data_schema.{}.desc()\".format(self.get_evaluated_time_field()))\n\n        #: 对于k线这种数据，最后一个记录有可能是没完成的，所以取两个\n        #: 同一周期内只保留最新的一个数据\n        records = get_data(\n            entity_id=entity.id,\n            provider=self.provider,\n            data_schema=self.data_schema,\n            order=order,\n            limit=2,\n            return_type=\"domain\",\n            session=self.session,\n            level=self.level,\n        )\n        if records:\n            #: delete unfinished kdata\n            if len(records) == 2:\n                if is_in_same_interval(t1=records[0].timestamp, t2=records[1].timestamp, level=self.level):\n                    self.session.delete(records[1])\n                    self.session.flush()\n            return records[0]\n        return None\n\n    def evaluate_start_end_size_timestamps(self, entity):\n        #: not to list date yet\n        if entity.timestamp and (entity.timestamp >= now_pd_timestamp()):\n            return entity.timestamp, None, 0, None\n\n        #: get latest record\n        latest_saved_record = self.get_latest_saved_record(entity=entity)\n\n        if latest_saved_record:\n            #: the latest saved timestamp\n            latest_saved_timestamp = latest_saved_record.timestamp\n        else:\n            #: the list date\n            latest_saved_timestamp = entity.timestamp\n\n        if not latest_saved_timestamp:\n            return None, None, self.default_size, None\n\n        size = evaluate_size_from_timestamp(\n            start_timestamp=latest_saved_timestamp,\n            level=self.level,\n            one_day_trading_minutes=self.one_day_trading_minutes,\n        )\n\n        if self.start_timestamp:\n            start = max(self.start_timestamp, latest_saved_timestamp)\n        else:\n            start = latest_saved_timestamp\n\n        return start, None, size, None\n\n\nclass TimestampsDataRecorder(TimeSeriesDataRecorder):\n    def __init__(\n        self,\n        force_update=False,\n        sleeping_time=5,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"add\",\n        start_timestamp=None,\n        end_timestamp=None,\n    ) -> None:\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code=code,\n            codes=codes,\n            day_data=day_data,\n            entity_filters=entity_filters,\n            ignore_failed=ignore_failed,\n            real_time=real_time,\n            fix_duplicate_way=fix_duplicate_way,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n        )\n        self.security_timestamps_map = {}\n\n    def init_timestamps(self, entity_item) -> List[pd.Timestamp]:\n        raise NotImplementedError\n\n    def evaluate_start_end_size_timestamps(self, entity):\n        timestamps = self.security_timestamps_map.get(entity.id)\n        if not timestamps:\n            timestamps = self.init_timestamps(entity)\n            if self.start_timestamp:\n                timestamps = [t for t in timestamps if t >= self.start_timestamp]\n\n            if self.end_timestamp:\n                timestamps = [t for t in timestamps if t <= self.end_timestamp]\n\n            self.security_timestamps_map[entity.id] = timestamps\n\n        if not timestamps:\n            return None, None, 0, timestamps\n\n        timestamps.sort()\n\n        self.logger.info(\"entity_id:{},timestamps start:{},end:{}\".format(entity.id, timestamps[0], timestamps[-1]))\n\n        latest_record = self.get_latest_saved_record(entity=entity)\n\n        if latest_record:\n            self.logger.info(\"latest record timestamp:{}\".format(latest_record.timestamp))\n            timestamps = [t for t in timestamps if t >= latest_record.timestamp]\n\n            if timestamps:\n                return timestamps[0], timestamps[-1], len(timestamps), timestamps\n            return None, None, 0, None\n\n        return timestamps[0], timestamps[-1], len(timestamps), timestamps\n\n\n# the __all__ is generated\n__all__ = [\n    \"Meta\",\n    \"Recorder\",\n    \"EntityEventRecorder\",\n    \"TimeSeriesDataRecorder\",\n    \"FixedCycleDataRecorder\",\n    \"TimestampsDataRecorder\",\n]\n"
  },
  {
    "path": "src/zvt/contract/register.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\nPhase 3: providers removed from register_schema. Providers come from Recorder registration.\n\"\"\"\nimport logging\nfrom typing import List\n\nimport sqlalchemy\nfrom sqlalchemy import MetaData\nfrom sqlalchemy.ext.declarative import DeclarativeMeta\nfrom sqlalchemy.sql.ddl import CreateTable\nfrom sqlalchemy.sql.expression import text\n\nfrom zvt.contract import zvt_context\nfrom zvt.contract.schema import TradableEntity, Mixin\nfrom zvt.utils.utils import add_to_map_list\n\nlogger = logging.getLogger(__name__)\n\n\ndef ensure_schema_tables_and_indexes(engine, schema_base: DeclarativeMeta, db_name: str):\n    \"\"\"Create tables and indexes for schema. Used by lazy init.\"\"\"\n    schema_base.metadata.create_all(bind=engine)\n    for table_name, table in iter(schema_base.metadata.tables.items()):\n        db_meta = MetaData()\n        db_meta.reflect(bind=engine)\n        db_table = db_meta.tables[table_name]\n        existing_columns = [c.name for c in db_table.columns]\n        added_columns = [c for c in table.columns if c.name not in existing_columns]\n        index_list = []\n        with engine.connect() as con:\n            if db_name in (\"zvt_info\", \"stock_news\", \"stock_tags\", \"stock_quote\"):\n                con.execute(text(\"PRAGMA journal_mode=WAL;\"))\n                con.execute(text(\"PRAGMA journal_size_limit=1073741824;\"))\n            else:\n                con.execute(text(\"PRAGMA journal_mode=DELETE;\"))\n            rs = con.execute(text(\"PRAGMA INDEX_LIST('{}')\".format(table_name)))\n            for row in rs:\n                index_list.append(row[1])\n            try:\n                if added_columns:\n                    ddl_c = engine.dialect.ddl_compiler(engine.dialect, CreateTable(table))\n                    for added_column in added_columns:\n                        stmt = text(\n                            f\"ALTER TABLE {table_name} ADD COLUMN {ddl_c.get_column_specification(added_column)}\"\n                        )\n                        logger.info(f\"{engine.url} migrations:\\n {stmt}\")\n                        con.execute(stmt)\n                for col in [\n                    \"timestamp\", \"entity_id\", \"code\", \"report_period\",\n                    \"created_timestamp\", \"updated_timestamp\",\n                ]:\n                    if col in table.c:\n                        column = getattr(table.c, col)\n                        index_name = \"{}_{}_index\".format(table_name, col)\n                        if index_name not in index_list:\n                            sqlalchemy.schema.Index(index_name, column).create(engine)\n                for cols in [(\"timestamp\", \"entity_id\"), (\"timestamp\", \"code\")]:\n                    if cols[0] in table.c and cols[1] in table.c:\n                        column0 = getattr(table.c, cols[0])\n                        column1 = getattr(table.c, cols[1])\n                        index_name = \"{}_{}_{}_index\".format(table_name, cols[0], cols[1])\n                        if index_name not in index_list:\n                            sqlalchemy.schema.Index(index_name, column0, column1).create(engine)\n            except Exception as e:\n                logger.error(e)\n\n\ndef register_entity(entity_type: str = None):\n    \"\"\"\n    function for register entity type\n\n    :param entity_type:\n    :type entity_type:\n    :return:\n    :rtype:\n    \"\"\"\n\n    def register(cls):\n        # register the entity\n        if issubclass(cls, TradableEntity):\n            entity_type_ = entity_type\n            if not entity_type:\n                entity_type_ = cls.__name__.lower()\n\n            if entity_type_ not in zvt_context.tradable_entity_types:\n                zvt_context.tradable_entity_types.append(entity_type_)\n                zvt_context.tradable_entity_schemas.append(cls)\n            zvt_context.tradable_schema_map[entity_type_] = cls\n        return cls\n\n    return register\n\n\ndef register_schema(\n    db_name: str,\n    schema_base: DeclarativeMeta,\n    entity_type: str = None,\n    internal: bool = False,\n):\n    \"\"\"\n    Register schema. Providers come from Recorder registration (provider_map_recorder).\n    Tables/engines are created lazily on first get_db_engine(provider, db_name).\n    For schemas without Recorders, add to config: storage.schema_providers.\n\n    :param internal: If True, schema is business/internal data, not tied to external data source.\n                     Internal schemas only care about storage routing, do not participate in\n                     provider switching (e.g. zvt_info, trader_info, stock_tags).\n    \"\"\"\n    schemas = []\n    for item in schema_base.registry.mappers:\n        cls = item.class_\n        if type(cls) == DeclarativeMeta:\n            if zvt_context.dbname_map_schemas.get(db_name):\n                schemas = zvt_context.dbname_map_schemas[db_name]\n            zvt_context.schemas.append(cls)\n            if issubclass(cls, Mixin):\n                cls._zvt_db_name = db_name\n                cls._zvt_internal = internal  # internal=True: business data, no external data source\n            if entity_type:\n                add_to_map_list(the_map=zvt_context.entity_map_schemas, key=entity_type, value=cls)\n            schemas.append(cls)\n\n    zvt_context.dbname_map_schemas[db_name] = schemas\n    zvt_context.dbname_map_base[db_name] = schema_base\n    if internal:\n        zvt_context.internal_db_names.add(db_name)\n\n\n# the __all__ is generated\n__all__ = [\"register_entity\", \"register_schema\", \"ensure_schema_tables_and_indexes\"]\n"
  },
  {
    "path": "src/zvt/contract/route_registry.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\nRoute registry: maps (provider, db_name) -> storage_id.\nPhase 1: uses deterministic rule storage_id = \"{provider}_{db_name}\".\nPhase 2: supports config file overrides via zvt_config[\"storage\"][\"storage_routes\"].\n\"\"\"\nfrom typing import Optional, Type\n\n\ndef _get_db_name_from_context(data_schema) -> Optional[str]:\n    \"\"\"Resolve db_name for a schema from zvt_context.dbname_map_base.\"\"\"\n    from zvt.contract import zvt_context\n    for db_name, base in zvt_context.dbname_map_base.items():\n        if issubclass(data_schema, base):\n            return db_name\n    return None\n\n\ndef _get_storage_config() -> dict:\n    \"\"\"Load storage config from zvt_config. Lazy import to avoid cycles.\"\"\"\n    try:\n        from zvt import zvt_config\n        return zvt_config.get(\"storage\") or {}\n    except Exception:\n        return {}\n\n\nclass RouteRegistry:\n    \"\"\"\n    Maps (provider, db_name) or (provider, data_schema) to storage_id.\n    Default: storage_id = \"{provider}_{db_name}\".\n    Config override: zvt_config[\"storage\"][\"storage_routes\"][\"provider|db_name\"] = storage_id.\n    \"\"\"\n\n    def __init__(self) -> None:\n        self._route_overrides: dict = {}\n\n    def register_route(self, provider: str, db_name: str, storage_id: str) -> None:\n        \"\"\"Register explicit route override. Takes precedence over config.\"\"\"\n        key = f\"{provider}|{db_name}\"\n        self._route_overrides[key] = storage_id\n\n    def _get_route_overrides_from_config(self) -> dict:\n        \"\"\"Merge config storage_routes with programmatic overrides. Config is loaded once.\"\"\"\n        config = _get_storage_config()\n        routes = dict(config.get(\"storage_routes\") or {})\n        routes.update(self._route_overrides)\n        return routes\n\n    def get_storage_id(self, provider: str, db_name: str) -> str:\n        \"\"\"\n        Get storage_id for (provider, db_name).\n        Checks: 1) programmatic overrides 2) config storage_routes 3) default \"{provider}_{db_name}\".\n        \"\"\"\n        overrides = self._get_route_overrides_from_config()\n        key = f\"{provider}|{db_name}\"\n        if key in overrides:\n            return overrides[key]\n        return f\"{provider}_{db_name}\"\n\n    def get_storage_id_for_schema(\n        self, provider: str, data_schema: Type = None, db_name: str = None\n    ) -> str:\n        \"\"\"\n        Get storage_id for (provider, data_schema) or (provider, db_name).\n        :param provider: Data provider name.\n        :param data_schema: Schema class (used to resolve db_name if db_name is None).\n        :param db_name: DB name. If provided, data_schema is ignored.\n        :return: storage_id string.\n        \"\"\"\n        if db_name is None:\n            db_name = _get_db_name_from_context(data_schema)\n        if db_name is None:\n            raise ValueError(f\"Cannot resolve db_name for schema {data_schema}\")\n        return self.get_storage_id(provider, db_name)\n\n\n# Default instance; can be replaced for custom routing\n_default_route_registry: Optional[RouteRegistry] = None\n\n\ndef get_route_registry() -> RouteRegistry:\n    \"\"\"Get the default route registry.\"\"\"\n    global _default_route_registry\n    if _default_route_registry is None:\n        _default_route_registry = RouteRegistry()\n    return _default_route_registry\n\n\ndef set_route_registry(registry: RouteRegistry):\n    \"\"\"Replace default route registry. Useful for tests.\"\"\"\n    global _default_route_registry\n    _default_route_registry = registry\n\n\n__all__ = [\n    \"RouteRegistry\",\n    \"get_route_registry\",\n    \"set_route_registry\",\n]\n"
  },
  {
    "path": "src/zvt/contract/schema.py",
    "content": "# -*- coding: utf-8 -*-\nimport inspect\nimport json\nfrom datetime import timedelta\nfrom typing import Dict, List, Union\n\nimport pandas as pd\nimport pkg_resources\nfrom sqlalchemy import Column, String, DateTime, Float\nfrom sqlalchemy.orm import Session\n\nfrom zvt.contract import IntervalLevel\n\n\ndef _get_schema_providers() -> Dict[str, List[str]]:\n    \"\"\"Get schema_providers merged with package default (ensures defaults are available).\"\"\"\n    default = {}\n    try:\n        with open(pkg_resources.resource_filename(\"zvt\", \"config.json\")) as f:\n            default = (json.load(f).get(\"storage\") or {}).get(\"schema_providers\") or {}\n    except Exception:\n        pass\n    try:\n        from zvt import zvt_config\n        user = (zvt_config.get(\"storage\") or {}).get(\"schema_providers\") or {}\n        default = dict(default)\n        default.update(user)\n    except Exception:\n        pass\n    return default\nfrom zvt.utils.time_utils import date_and_time, is_same_date_time, now_pd_timestamp\n\n\nclass Mixin(object):\n    \"\"\"\n    Base class of schema.\n    \"\"\"\n\n    #: id\n    id = Column(String, primary_key=True)\n    #: entity id\n    entity_id = Column(String)\n\n    #: the meaning could be different for different case,most time it means 'happen time'\n    #: Need replace with your timezone\n    timestamp = Column(DateTime)\n\n    # unix epoch,same meaning with timestamp\n    # ts = Column(BIGINT)\n\n    @classmethod\n    def help(cls):\n        print(inspect.getsource(cls))\n\n    @classmethod\n    def important_cols(cls):\n        return []\n\n    @classmethod\n    def time_field(cls):\n        return \"timestamp\"\n\n    @classmethod\n    def register_recorder_cls(cls, provider, recorder_cls):\n        \"\"\"\n        register the recorder for the schema\n\n        :param provider:\n        :param recorder_cls:\n        \"\"\"\n        # don't make provider_map_recorder as class field,it should be created for the sub class as need\n        if not hasattr(cls, \"provider_map_recorder\"):\n            cls.provider_map_recorder = {}\n\n        if provider not in cls.provider_map_recorder:\n            cls.provider_map_recorder[provider] = recorder_cls\n\n    @classmethod\n    def register_provider(cls, provider):\n        \"\"\"(Deprecated) Providers now come from Recorder registration.\"\"\"\n        if not hasattr(cls, \"_zvt_providers_override\"):\n            cls._zvt_providers_override = []\n        if provider not in cls._zvt_providers_override:\n            cls._zvt_providers_override.append(provider)\n\n    @classmethod\n    def is_internal(cls) -> bool:\n        \"\"\"True if schema is internal/business data, not tied to external data source.\"\"\"\n        return getattr(cls, \"_zvt_internal\", False)\n\n    @classmethod\n    def get_providers(cls) -> List[str]:\n        \"\"\"\n        Providers: from provider_map_recorder (Recorder registration), or config storage.schema_providers.\n        Only use provider_map_recorder when set on this concrete schema class (cls.__dict__), not inherited\n        from a shared base like KdataCommon - otherwise StockQuote would wrongly get Stock1dKdata's providers.\n        \"\"\"\n        if \"provider_map_recorder\" in cls.__dict__ and cls.provider_map_recorder:\n            return list(cls.provider_map_recorder.keys())\n        db_name = getattr(cls, \"_zvt_db_name\", None)\n        if db_name:\n            schema_providers = _get_schema_providers()\n            if db_name in schema_providers:\n                return schema_providers[db_name]\n        return getattr(cls, \"_zvt_providers_override\", []) or []\n\n    @classmethod\n    def test_data_correctness(cls, provider, data_samples):\n        for data in data_samples:\n            item = cls.query_data(provider=provider, ids=[data[\"id\"]], return_type=\"dict\")\n            print(item)\n            for k in data:\n                if k == \"timestamp\":\n                    assert is_same_date_time(item[0][k], data[k])\n                else:\n                    assert item[0][k] == data[k]\n\n    @classmethod\n    def get_by_id(cls, id, provider_index: int = 0, provider: str = None):\n        from .api import get_by_id\n\n        if not provider:\n            provider = cls.get_providers()[provider_index]\n        return get_by_id(data_schema=cls, id=id, provider=provider)\n\n    @classmethod\n    def query_data(\n        cls,\n        provider_index: int = 0,\n        ids: List[str] = None,\n        entity_ids: List[str] = None,\n        entity_id: str = None,\n        codes: List[str] = None,\n        code: str = None,\n        level: Union[IntervalLevel, str] = None,\n        provider: str = None,\n        columns: List = None,\n        col_label: dict = None,\n        return_type: str = \"df\",\n        start_timestamp: Union[pd.Timestamp, str] = None,\n        end_timestamp: Union[pd.Timestamp, str] = None,\n        filters: List = None,\n        session: Session = None,\n        order=None,\n        limit: int = None,\n        distinct=None,\n        index: Union[str, list] = None,\n        drop_index_col=False,\n        time_field: str = \"timestamp\",\n    ):\n        \"\"\"\n        query data by the arguments\n\n        :param provider_index:\n        :param data_schema:\n        :param ids:\n        :param entity_ids:\n        :param entity_id:\n        :param codes:\n        :param code:\n        :param level:\n        :param provider:\n        :param columns:\n        :param col_label: dict with key(column), value(label)\n        :param return_type: df, domain or dict. default is df\n        :param start_timestamp:\n        :param end_timestamp:\n        :param filters:\n        :param session:\n        :param order:\n        :param limit:\n        :param index: index field name, str for single index, str list for multiple index\n        :param drop_index_col: whether drop the col if it's in index, default False\n        :param time_field:\n        :return: results basing on return_type.\n        \"\"\"\n        from .api import get_data\n\n        if not provider:\n            provider = cls.get_providers()[provider_index]\n        return get_data(\n            data_schema=cls,\n            ids=ids,\n            entity_ids=entity_ids,\n            entity_id=entity_id,\n            codes=codes,\n            code=code,\n            level=level,\n            provider=provider,\n            columns=columns,\n            col_label=col_label,\n            return_type=return_type,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            filters=filters,\n            session=session,\n            order=order,\n            limit=limit,\n            index=index,\n            distinct=distinct,\n            drop_index_col=drop_index_col,\n            time_field=time_field,\n        )\n\n    @classmethod\n    def get_storages(\n        cls,\n        provider: str = None,\n    ):\n        \"\"\"\n        get the storages info\n\n        :param provider: provider\n        :return: storages\n        \"\"\"\n        if not provider:\n            providers = cls.get_providers()\n        else:\n            providers = [provider]\n        from zvt.contract.api import get_db_engine\n\n        engines = []\n        for p in providers:\n            engines.append(get_db_engine(provider=p, data_schema=cls))\n        return engines\n\n    @classmethod\n    def record_data(\n        cls,\n        provider_index: int = 0,\n        provider: str = None,\n        force_update=None,\n        sleeping_time=None,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        real_time=None,\n        fix_duplicate_way=None,\n        start_timestamp=None,\n        end_timestamp=None,\n        one_day_trading_minutes=None,\n        **kwargs,\n    ):\n        \"\"\"\n        record data by the arguments\n\n        :param entity_id:\n        :param provider_index:\n        :param provider:\n        :param force_update:\n        :param sleeping_time:\n        :param exchanges:\n        :param entity_ids:\n        :param code:\n        :param codes:\n        :param real_time:\n        :param fix_duplicate_way:\n        :param start_timestamp:\n        :param end_timestamp:\n        :param one_day_trading_minutes:\n        :param kwargs:\n        :return:\n        \"\"\"\n        if cls.provider_map_recorder:\n            print(f\"{cls.__name__} registered recorders:{cls.provider_map_recorder}\")\n\n            if provider:\n                recorder_class = cls.provider_map_recorder[provider]\n            else:\n                recorder_class = cls.provider_map_recorder[cls.get_providers()[provider_index]]\n\n            # get args for specific recorder class\n            from zvt.contract.recorder import TimeSeriesDataRecorder\n\n            if issubclass(recorder_class, TimeSeriesDataRecorder):\n                args = [\n                    item\n                    for item in inspect.getfullargspec(cls.record_data).args\n                    if item not in (\"cls\", \"provider_index\", \"provider\")\n                ]\n            else:\n                args = [\"force_update\", \"sleeping_time\"]\n\n            #: just fill the None arg to kw,so we could use the recorder_class default args\n            kw = {}\n            for arg in args:\n                tmp = eval(arg)\n                if tmp is not None:\n                    kw[arg] = tmp\n\n            #: FixedCycleDataRecorder\n            from zvt.contract.recorder import FixedCycleDataRecorder\n\n            if issubclass(recorder_class, FixedCycleDataRecorder):\n                #: contract:\n                #: 1)use FixedCycleDataRecorder to record the data with IntervalLevel\n                #: 2)the table of schema with IntervalLevel format is {entity}_{level}_[adjust_type]_{event}\n                table: str = cls.__tablename__\n                try:\n                    items = table.split(\"_\")\n                    if len(items) == 4:\n                        adjust_type = items[2]\n                        kw[\"adjust_type\"] = adjust_type\n                    level = IntervalLevel(items[1])\n                except:\n                    #: for other schema not with normal format,but need to calculate size for remaining days\n                    level = IntervalLevel.LEVEL_1DAY\n\n                kw[\"level\"] = level\n\n                #: add other custom args\n                for k in kwargs:\n                    kw[k] = kwargs[k]\n\n                r = recorder_class(**kw)\n                return r.run()\n            else:\n                r = recorder_class(**kw)\n                return r.run()\n        else:\n            print(f\"no recorders for {cls.__name__}\")\n\n\nclass NormalMixin(Mixin):\n    #: the record created time in db\n    created_timestamp = Column(DateTime, default=pd.Timestamp.now())\n    #: the record updated time in db, some recorder would check it for whether need to refresh\n    updated_timestamp = Column(DateTime)\n\n\nclass Entity(Mixin):\n    #: 标的类型\n    entity_type = Column(String(length=64))\n    #: 所属交易所\n    exchange = Column(String(length=32))\n    #: 编码\n    code = Column(String(length=64))\n    #: 名字\n    name = Column(String(length=128))\n    #: 上市日\n    list_date = Column(DateTime)\n    #: 退市日\n    end_date = Column(DateTime)\n\n\nclass TradableEntity(Entity):\n    \"\"\"\n    tradable entity\n    \"\"\"\n\n    @classmethod\n    def get_timezone(cls):\n        \"\"\"\n        overwrite it to get the timezone of the entity\n\n        :return: pytz timezone\n        \"\"\"\n\n        return None\n\n    @classmethod\n    def get_trading_dates(cls, start_date=None, end_date=None):\n        \"\"\"\n        overwrite it to get the trading dates of the entity\n\n        :param start_date:\n        :param end_date:\n        :return: list of dates\n        \"\"\"\n        return pd.date_range(start_date, end_date, freq=\"B\")\n\n    @classmethod\n    def get_trading_intervals(cls, include_bidding_time=False):\n        \"\"\"\n        overwrite it to get the trading intervals of the entity\n\n        :return: list of time intervals, in format [(start,end)]\n        \"\"\"\n        if include_bidding_time:\n            return [(\"09:15\", \"11:30\"), (\"13:00\", \"15:00\")]\n        else:\n            return [(\"09:30\", \"11:30\"), (\"13:00\", \"15:00\")]\n\n    @classmethod\n    def in_real_trading_time(cls, timestamp=None, include_bidding_time=True):\n        if not timestamp:\n            timestamp = now_pd_timestamp(tz=cls.get_timezone())\n        else:\n            timestamp = pd.Timestamp(timestamp, tz=cls.get_timezone())\n        for open_close in cls.get_trading_intervals(include_bidding_time=include_bidding_time):\n            open_time = date_and_time(the_date=timestamp.date(), the_time=open_close[0], tz=cls.get_timezone())\n            close_time = date_and_time(the_date=timestamp.date(), the_time=open_close[1], tz=cls.get_timezone())\n            if open_time <= timestamp <= close_time:\n                return True\n            else:\n                continue\n        return False\n\n    @classmethod\n    def before_trading_time(cls, timestamp=None):\n        if not timestamp:\n            timestamp = now_pd_timestamp(tz=cls.get_timezone())\n        else:\n            timestamp = pd.Timestamp(timestamp, tz=cls.get_timezone())\n        open_time = date_and_time(\n            the_date=timestamp.date(),\n            the_time=cls.get_trading_intervals(include_bidding_time=True)[0][0],\n            tz=cls.get_timezone(),\n        )\n        return timestamp < open_time\n\n    @classmethod\n    def after_trading_time(cls, timestamp=None):\n        if not timestamp:\n            timestamp = now_pd_timestamp(tz=cls.get_timezone())\n        else:\n            timestamp = pd.Timestamp(timestamp, tz=cls.get_timezone())\n        close_time = date_and_time(\n            the_date=timestamp.date(),\n            the_time=cls.get_trading_intervals(include_bidding_time=True)[-1][1],\n            tz=cls.get_timezone(),\n        )\n        return timestamp > close_time\n\n    @classmethod\n    def in_trading_time(cls, timestamp=None):\n        if not timestamp:\n            timestamp = now_pd_timestamp(tz=cls.get_timezone())\n        else:\n            timestamp = pd.Timestamp(timestamp, tz=cls.get_timezone())\n        open_time = date_and_time(\n            the_date=timestamp.date(),\n            the_time=cls.get_trading_intervals(include_bidding_time=True)[0][0],\n            tz=cls.get_timezone(),\n        )\n        close_time = date_and_time(\n            the_date=timestamp.date(),\n            the_time=cls.get_trading_intervals(include_bidding_time=True)[-1][1],\n            tz=cls.get_timezone(),\n        )\n        return open_time <= timestamp <= close_time\n\n    @classmethod\n    def get_close_hour_and_minute(cls):\n        hour, minute = cls.get_trading_intervals()[-1][1].split(\":\")\n        return int(hour), int(minute)\n\n    @classmethod\n    def get_interval_timestamps(cls, start_date, end_date, level: IntervalLevel):\n        \"\"\"\n        generate the timestamps for the level\n\n        :param start_date:\n        :param end_date:\n        :param level:\n        \"\"\"\n\n        for current_date in cls.get_trading_dates(start_date=start_date, end_date=end_date):\n            if level == IntervalLevel.LEVEL_1DAY:\n                yield current_date\n            elif level == IntervalLevel.LEVEL_1WEEK:\n                if current_date.weekday() == 4:\n                    yield current_date\n            else:\n                start_end_list = cls.get_trading_intervals()\n\n                for start_end in start_end_list:\n                    start = start_end[0]\n                    end = start_end[1]\n\n                    current_timestamp = date_and_time(the_date=current_date, the_time=start)\n                    end_timestamp = date_and_time(the_date=current_date, the_time=end)\n\n                    while current_timestamp <= end_timestamp:\n                        yield current_timestamp\n                        current_timestamp = current_timestamp + timedelta(minutes=level.to_minute())\n\n    @classmethod\n    def is_open_timestamp(cls, timestamp):\n        timestamp = pd.Timestamp(timestamp)\n        return is_same_date_time(\n            timestamp,\n            date_and_time(the_date=timestamp.date(), the_time=cls.get_trading_intervals()[0][0]),\n        )\n\n    @classmethod\n    def is_close_timestamp(cls, timestamp):\n        timestamp = pd.Timestamp(timestamp)\n        return is_same_date_time(\n            timestamp,\n            date_and_time(the_date=timestamp.date(), the_time=cls.get_trading_intervals()[-1][1]),\n        )\n\n    @classmethod\n    def is_finished_kdata_timestamp(cls, timestamp: pd.Timestamp, level: IntervalLevel):\n        \"\"\"\n        :param timestamp: the timestamp could be recorded in kdata of the level\n        :type timestamp: pd.Timestamp\n        :param level:\n        :type level: zvt.domain.common.IntervalLevel\n        :return:\n        :rtype: bool\n        \"\"\"\n        timestamp = pd.Timestamp(timestamp)\n\n        for t in cls.get_interval_timestamps(timestamp.date(), timestamp.date(), level=level):\n            if is_same_date_time(t, timestamp):\n                return True\n\n        return False\n\n    @classmethod\n    def could_short(cls):\n        \"\"\"\n        whether could be shorted\n\n        :return:\n        \"\"\"\n        return False\n\n    @classmethod\n    def get_trading_t(cls):\n        \"\"\"\n        0 means t+0\n        1 means t+1\n\n        :return:\n        \"\"\"\n        return 1\n\n\nclass ActorEntity(Entity):\n    pass\n\n\nclass NormalEntityMixin(TradableEntity):\n    #: the record created time in db\n    created_timestamp = Column(DateTime, default=pd.Timestamp.now())\n    #: the record updated time in db, some recorder would check it for whether need to refresh\n    updated_timestamp = Column(DateTime)\n\n\nclass Portfolio(TradableEntity):\n    \"\"\"\n    composition of tradable entities\n    \"\"\"\n\n    @classmethod\n    def get_stocks(\n        cls,\n        code=None,\n        codes=None,\n        ids=None,\n        timestamp=now_pd_timestamp(),\n        provider=None,\n    ):\n        \"\"\"\n        the publishing policy of portfolio positions is different for different types,\n        overwrite this function for get the holding stocks in specific date\n\n        :param code: portfolio(etf/block/index...) code\n        :param codes: portfolio(etf/block/index...) codes\n        :param ids: portfolio(etf/block/index...) ids\n        :param timestamp: the date of the holding stocks\n        :param provider: the data provider\n        :return:\n        \"\"\"\n        from zvt.contract.api import get_schema_by_name\n\n        schema_str = f\"{cls.__name__}Stock\"\n        portfolio_stock = get_schema_by_name(schema_str)\n        return portfolio_stock.query_data(provider=provider, code=code, codes=codes, timestamp=timestamp, ids=ids)\n\n\n#: 组合(Fund,Etf,Index,Block等)和个股(Stock)的关系 应该继承自该类\n#: 该基础类可以这样理解:\n#: entity为组合本身,其包含了stock这种entity,timestamp为持仓日期,从py的\"你知道你在干啥\"的哲学出发，不加任何约束\nclass PortfolioStock(Mixin):\n    #: portfolio标的类型\n    entity_type = Column(String(length=64))\n    #: portfolio所属交易所\n    exchange = Column(String(length=32))\n    #: portfolio编码\n    code = Column(String(length=64))\n    #: portfolio名字\n    name = Column(String(length=128))\n\n    stock_id = Column(String)\n    stock_code = Column(String(length=64))\n    stock_name = Column(String(length=128))\n\n\n#: 支持时间变化,报告期标的调整\nclass PortfolioStockHistory(PortfolioStock):\n    #: 报告期,season1,half_year,season3,year\n    report_period = Column(String(length=32))\n    #: 3-31,6-30,9-30,12-31\n    report_date = Column(DateTime)\n\n    #: 占净值比例\n    proportion = Column(Float)\n    #: 持有股票的数量\n    shares = Column(Float)\n    #: 持有股票的市值\n    market_cap = Column(Float)\n\n\n#: 交易标的和参与者的关系应该继承自该类, meet,遇见,恰如其分的诠释参与者和交易标的的关系\n#: 市场就是参与者与交易标的的关系，类的命名规范为{Entity}{relation}{Entity}，entity_id代表\"所\"为的entity,\"受\"者entity以具体类别的id命名\n#: 比如StockTopTenHolder:TradableMeetActor中entity_id和actor_id,分别代表股票和股东\nclass TradableMeetActor(Mixin):\n    #: tradable code\n    code = Column(String(length=64))\n    #: tradable name\n    name = Column(String(length=128))\n\n    actor_id = Column(String)\n    actor_type = Column(String)\n    actor_code = Column(String(length=64))\n    actor_name = Column(String(length=128))\n\n\n#: 也可以\"所\"为参与者，\"受\"为标的\nclass ActorMeetTradable(Mixin):\n    #: actor code\n    code = Column(String(length=64))\n    #: actor name\n    name = Column(String(length=128))\n\n    tradable_id = Column(String)\n    tradable_type = Column(String)\n    tradable_code = Column(String(length=64))\n    tradable_name = Column(String(length=128))\n\n\n# the __all__ is generated\n__all__ = [\n    \"Mixin\",\n    \"NormalMixin\",\n    \"Entity\",\n    \"TradableEntity\",\n    \"ActorEntity\",\n    \"NormalEntityMixin\",\n    \"Portfolio\",\n    \"PortfolioStock\",\n    \"PortfolioStockHistory\",\n    \"TradableMeetActor\",\n    \"ActorMeetTradable\",\n]\n"
  },
  {
    "path": "src/zvt/contract/storage.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\nStorage abstraction for ZVT.\nPhase 1: StorageBackend interface + SqliteStorageBackend with existing path logic.\nPhase 2: path_template and base_path from config.\n\"\"\"\nimport json\nimport logging\nimport os\nfrom abc import ABC, abstractmethod\nfrom typing import Optional\n\nfrom sqlalchemy import create_engine\nfrom sqlalchemy.engine import Engine\nfrom sqlalchemy.orm import sessionmaker\n\nlogger = logging.getLogger(__name__)\n\n\ndef _default_data_path() -> str:\n    \"\"\"Get data path from zvt_env, with fallback for tests.\"\"\"\n    try:\n        from zvt import zvt_env\n        return zvt_env.get(\"data_path\", \".\")\n    except Exception:\n        return \".\"\n\n\ndef _get_storage_config() -> dict:\n    \"\"\"Load storage config from zvt_config. Lazy import to avoid cycles.\"\"\"\n    try:\n        from zvt import zvt_config\n        return zvt_config.get(\"storage\") or {}\n    except Exception:\n        return {}\n\n\nclass StorageBackend(ABC):\n    \"\"\"\n    Abstract storage backend. Decouples physical storage from domain/read/record logic.\n    \"\"\"\n\n    @abstractmethod\n    def get_engine(self, storage_id: str, data_path: Optional[str] = None) -> Engine:\n        \"\"\"\n        Get or create SQLAlchemy engine for the given storage_id.\n        :param storage_id: Logical storage identifier (e.g. \"em_stock_meta\").\n        :param data_path: Base path for data files. If None, uses zvt_env[\"data_path\"].\n        :return: Engine instance.\n        \"\"\"\n        raise NotImplementedError\n\n    @abstractmethod\n    def get_session_factory(self, storage_id: str, data_path: Optional[str] = None):\n        \"\"\"\n        Get or create sessionmaker for the given storage_id.\n        Must be configured with engine before use (caller's responsibility, e.g. in register_schema).\n        :param storage_id: Logical storage identifier.\n        :param data_path: Base path for data files.\n        :return: sessionmaker instance.\n        \"\"\"\n        raise NotImplementedError\n\n\nclass SqliteStorageBackend(StorageBackend):\n    \"\"\"\n    SQLite storage backend. Default path: {data_path}/{provider}/{provider}_{db_name}.db\n    Config (zvt_config[\"storage\"]):\n      - base_path: override data path\n      - path_template: format string with {base_path}, {provider}, {db_name}, {storage_id}\n    \"\"\"\n\n    def __init__(self, path_template: Optional[str] = None, base_path: Optional[str] = None):\n        \"\"\"\n        :param path_template: Optional. Overrides config. Placeholders: {base_path}, {provider}, {db_name}, {storage_id}\n        :param base_path: Optional. Overrides config.\n        \"\"\"\n        self._path_template_override = path_template\n        self._base_path_override = base_path\n        self._engine_map = {}\n        self._session_factory_map = {}\n\n    def _get_path_template(self) -> Optional[str]:\n        \"\"\"Path template: constructor override > config > None (use default).\"\"\"\n        if self._path_template_override is not None:\n            return self._path_template_override\n        config = _get_storage_config()\n        return config.get(\"path_template\")\n\n    def _get_base_path(self, data_path: Optional[str]) -> str:\n        \"\"\"Base path: param > constructor override > config > zvt_env.\"\"\"\n        if data_path is not None:\n            return data_path\n        if self._base_path_override is not None:\n            return self._base_path_override\n        config = _get_storage_config()\n        cfg_path = config.get(\"base_path\")\n        if cfg_path is not None:\n            return cfg_path\n        return _default_data_path()\n\n    def _storage_id_to_path(self, storage_id: str, data_path: str) -> str:\n        \"\"\"\n        Compute SQLite file path from storage_id.\n        storage_id format: \"{provider}_{db_name}\" (e.g. \"em_stock_meta\").\n        Default path: {data_path}/{provider}/{provider}_{db_name}.db\n        \"\"\"\n        if \"_\" not in storage_id:\n            raise ValueError(f\"Invalid storage_id: {storage_id}, expected '{{provider}}_{{db_name}}'\")\n        provider, db_name = storage_id.split(\"_\", 1)\n        path_template = self._get_path_template()\n        if path_template:\n            return path_template.format(\n                base_path=data_path, provider=provider, db_name=db_name, storage_id=storage_id\n            )\n        provider_dir = os.path.join(data_path, provider)\n        if not os.path.exists(provider_dir):\n            os.makedirs(provider_dir)\n        return os.path.join(provider_dir, f\"{provider}_{db_name}.db?check_same_thread=False\")\n\n    def get_engine(self, storage_id: str, data_path: Optional[str] = None) -> Engine:\n        data_path = self._get_base_path(data_path)\n        if storage_id in self._engine_map:\n            return self._engine_map[storage_id]\n        db_path = self._storage_id_to_path(storage_id, data_path)\n        url = \"sqlite:///\" + db_path\n        engine = create_engine(\n            url, echo=False, json_serializer=lambda obj: json.dumps(obj, ensure_ascii=False)\n        )\n        self._engine_map[storage_id] = engine\n        return engine\n\n    def get_session_factory(self, storage_id: str, data_path: Optional[str] = None):\n        data_path = self._get_base_path(data_path)\n        if storage_id in self._session_factory_map:\n            return self._session_factory_map[storage_id]\n        fac = sessionmaker()\n        self._session_factory_map[storage_id] = fac\n        return fac\n\n# Default instance; can be replaced for testing or custom backends\n_default_storage_backend: Optional[StorageBackend] = None\n\n\ndef get_storage_backend() -> StorageBackend:\n    \"\"\"Get the default storage backend. Creates SqliteStorageBackend on first use.\"\"\"\n    global _default_storage_backend\n    if _default_storage_backend is None:\n        _default_storage_backend = SqliteStorageBackend()\n    return _default_storage_backend\n\n\ndef set_storage_backend(backend: StorageBackend):\n    \"\"\"Replace default storage backend. Useful for tests.\"\"\"\n    global _default_storage_backend\n    _default_storage_backend = backend\n\n\n__all__ = [\n    \"StorageBackend\",\n    \"SqliteStorageBackend\",\n    \"get_storage_backend\",\n    \"set_storage_backend\",\n]\n"
  },
  {
    "path": "src/zvt/contract/utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport math\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\ndef is_in_same_interval(t1: pd.Timestamp, t2: pd.Timestamp, level: IntervalLevel):\n    t1 = to_pd_timestamp(t1)\n    t2 = to_pd_timestamp(t2)\n    if level == IntervalLevel.LEVEL_1WEEK:\n        return t1.week == t2.week\n    if level == IntervalLevel.LEVEL_1MON:\n        return t1.month == t2.month\n\n    return level.floor_timestamp(t1) == level.floor_timestamp(t2)\n\n\ndef evaluate_size_from_timestamp(\n    start_timestamp, level: IntervalLevel, one_day_trading_minutes, end_timestamp: pd.Timestamp = None\n):\n    \"\"\"\n    given from timestamp,level,one_day_trading_minutes,this func evaluate size of kdata to current.\n    it maybe a little bigger than the real size for fetching all the kdata.\n\n    :param start_timestamp:\n    :type start_timestamp: pd.Timestamp\n    :param level:\n    :type level: IntervalLevel\n    :param one_day_trading_minutes:\n    :type one_day_trading_minutes: int\n    \"\"\"\n    if not end_timestamp:\n        end_timestamp = pd.Timestamp.now()\n    else:\n        end_timestamp = to_pd_timestamp(end_timestamp)\n\n    time_delta = end_timestamp - to_pd_timestamp(start_timestamp)\n\n    one_day_trading_seconds = one_day_trading_minutes * 60\n\n    if level == IntervalLevel.LEVEL_1DAY:\n        return time_delta.days + 1\n\n    if level == IntervalLevel.LEVEL_1WEEK:\n        return int(math.ceil(time_delta.days / 7)) + 1\n\n    if level == IntervalLevel.LEVEL_1MON:\n        return int(math.ceil(time_delta.days / 30)) + 1\n\n    if time_delta.days > 0:\n        seconds = (time_delta.days + 1) * one_day_trading_seconds\n        return int(math.ceil(seconds / level.to_second())) + 1\n    else:\n        seconds = time_delta.total_seconds()\n        return min(int(math.ceil(seconds / level.to_second())) + 1, one_day_trading_seconds / level.to_second() + 1)\n\n\ndef next_timestamp_on_level(current_timestamp: pd.Timestamp, level: IntervalLevel) -> pd.Timestamp:\n    current_timestamp = to_pd_timestamp(current_timestamp)\n    return current_timestamp + pd.Timedelta(seconds=level.to_second())\n\n\ndef is_finished_kdata_timestamp(timestamp, level: IntervalLevel):\n    timestamp = to_pd_timestamp(timestamp)\n    if level.floor_timestamp(timestamp) == timestamp:\n        return True\n    return False\n"
  },
  {
    "path": "src/zvt/contract/zvt_info.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, Text\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.contract.schema import Mixin\n\nZvtInfoBase = declarative_base()\n\n\nclass StateMixin(Mixin):\n    #: the unique name of the service, e.g. recorder,factor,tag\n    state_name = Column(String(length=128))\n\n    #: json string\n    state = Column(Text())\n\n\nclass RecorderState(ZvtInfoBase, StateMixin):\n    \"\"\"\n    Schema for storing recorder state\n    \"\"\"\n\n    __tablename__ = \"recorder_state\"\n\n\nclass TaggerState(ZvtInfoBase, StateMixin):\n    \"\"\"\n    Schema for storing tagger state\n    \"\"\"\n\n    __tablename__ = \"tagger_state\"\n\n\nclass FactorState(ZvtInfoBase, StateMixin):\n    \"\"\"\n    Schema for storing factor state\n    \"\"\"\n\n    __tablename__ = \"factor_state\"\n\n\nregister_schema(db_name=\"zvt_info\", schema_base=ZvtInfoBase, internal=True)\n\n\n# the __all__ is generated\n__all__ = [\"StateMixin\", \"RecorderState\", \"TaggerState\", \"FactorState\"]\n"
  },
  {
    "path": "src/zvt/domain/__init__.py",
    "content": "# -*- coding: utf-8 -*-\nimport enum\n\n\nclass BlockCategory(enum.Enum):\n    #: 行业版块\n    industry = \"industry\"\n    #: 概念版块\n    concept = \"concept\"\n    #: 区域版块\n    area = \"area\"\n\n\nclass IndexCategory(enum.Enum):\n    #: 中国指数提供商：\n    #: 中证指数公司 http://www.csindex.com.cn/zh-CN\n    #: 上证指数(上交所标的) 中证指数(沪深)\n\n    #: 国证指数公司 http://www.cnindex.com.cn/index.html\n    #: 深证指数(深交所标的) 国证指数(沪深)\n\n    #: 规模指数\n    #: 常见的上证指数，深证指数等\n    scope = \"scope\"\n    #: 行业指数\n    industry = \"industry\"\n    #: 风格指数\n    style = \"style\"\n    #: 主题指数\n    #\n    #: 策略指数\n    #\n    #: 综合指数\n    #\n    #: 债券指数\n    #\n    #: 基金指数\n    fund = \"fund\"\n    #: 定制指数\n    #\n    #: 人民币指数\n    #\n    #: 跨境指数\n    #\n    #: 其他指数\n\n\nclass ReportPeriod(enum.Enum):\n    # 有些基金的2，4季报只有10大持仓，半年报和年报有详细持仓，需要区别对待\n    season1 = \"season1\"\n    season2 = \"season2\"\n    season3 = \"season3\"\n    season4 = \"season4\"\n    half_year = \"half_year\"\n    year = \"year\"\n\n\n# 用于区分不同的财务指标\nclass CompanyType(enum.Enum):\n    qiye = \"qiye\"\n    baoxian = \"baoxian\"\n    yinhang = \"yinhang\"\n    quanshang = \"quanshang\"\n\n\nCHINA_FUTURE_CODE_MAP_NAME = {\n    \"I\": \"铁矿石\",\n    \"RB\": \"螺纹钢\",\n    \"HC\": \"热轧卷板\",\n    \"SS\": \"不锈钢\",\n    \"SF\": \"硅铁\",\n    \"SM\": \"锰硅\",\n    \"WR\": \"线材\",\n    \"CU\": \"沪铜\",\n    \"AL\": \"沪铝\",\n    \"ZN\": \"沪锌\",\n    \"PB\": \"沪铅\",\n    \"NI\": \"沪镍\",\n    \"SN\": \"沪锡\",\n    \"BC\": \"国际铜\",\n    \"AU\": \"沪金\",\n    \"AG\": \"沪银\",\n    \"A\": \"豆一\",\n    \"B\": \"豆二\",\n    \"Y\": \"豆油\",\n    \"M\": \"豆粕\",\n    \"RS\": \"菜籽\",\n    \"OI\": \"菜油\",\n    \"RM\": \"菜粕\",\n    \"P\": \"棕榈油\",\n    \"C\": \"玉米\",\n    \"CS\": \"玉米淀粉\",\n    \"JD\": \"鸡蛋\",\n    \"CF\": \"一号棉花\",\n    \"CY\": \"棉纱\",\n    \"SR\": \"白糖\",\n    \"AP\": \"苹果\",\n    \"CJ\": \"红枣\",\n    \"PK\": \"花生\",\n    \"PM\": \"普麦\",\n    \"WH\": \"强麦\",\n    \"RR\": \"粳米\",\n    \"JR\": \"粳稻\",\n    \"RI\": \"早籼稻\",\n    \"LR\": \"晚籼稻\",\n    \"LH\": \"生猪\",\n    \"SC\": \"原油\",\n    \"FU\": \"燃油\",\n    \"PG\": \"LPG\",\n    \"LU\": \"低硫燃油\",\n    \"BU\": \"石油沥青\",\n    \"MA\": \"甲醇\",\n    \"EG\": \"乙二醇\",\n    \"L\": \"聚乙烯\",\n    \"TA\": \"PTA\",\n    \"V\": \"聚氯乙烯\",\n    \"PP\": \"聚丙烯\",\n    \"EB\": \"苯乙烯\",\n    \"SA\": \"纯碱\",\n    \"FG\": \"玻璃\",\n    \"UR\": \"尿素\",\n    \"RU\": \"橡胶\",\n    \"NR\": \"20号胶\",\n    \"SP\": \"纸浆\",\n    \"FB\": \"纤维板\",\n    \"BB\": \"胶合板\",\n    \"PF\": \"短纤\",\n    \"JM\": \"焦煤\",\n    \"J\": \"焦炭\",\n    \"ZC\": \"动力煤\",\n    \"IC\": \"中证500指数\",\n    \"IF\": \"沪深300指数\",\n    \"IH\": \"上证50指数\",\n    \"T\": \"10年期国债期货\",\n    \"TF\": \"5年期国债期货\",\n    \"TS\": \"2年期国债期货\",\n}\n\n\ndef get_future_name(code):\n    simple_code = code[:-4]\n    return f\"{CHINA_FUTURE_CODE_MAP_NAME[simple_code]}{code[-4:]}\"\n\n\n# the __all__ is generated\n__all__ = [\"BlockCategory\", \"IndexCategory\", \"ReportPeriod\", \"CompanyType\", \"get_future_name\"]\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule misc\nfrom .misc import *\nfrom .misc import __all__ as _misc_all\n\n__all__ += _misc_all\n\n# import all from submodule quotes\nfrom .quotes import *\nfrom .quotes import __all__ as _quotes_all\n\n__all__ += _quotes_all\n\n# import all from submodule meta\nfrom .meta import *\nfrom .meta import __all__ as _meta_all\n\n__all__ += _meta_all\n\n# import all from submodule fundamental\nfrom .fundamental import *\nfrom .fundamental import __all__ as _fundamental_all\n\n__all__ += _fundamental_all\n\n# import all from submodule macro\nfrom .macro import *\nfrom .macro import __all__ as _macro_all\n\n__all__ += _macro_all\n\n# import all from submodule actor\nfrom .actor import *\nfrom .actor import __all__ as _actor_all\n\n__all__ += _actor_all\n\n# import all from submodule emotion\nfrom .emotion import *\nfrom .emotion import __all__ as _emotion_all\n\n__all__ += _emotion_all\n"
  },
  {
    "path": "src/zvt/domain/actor/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule stock_actor\nfrom .stock_actor import *\nfrom .stock_actor import __all__ as _stock_actor_all\n\n__all__ += _stock_actor_all\n\n# import all from submodule actor_meta\nfrom .actor_meta import *\nfrom .actor_meta import __all__ as _actor_meta_all\n\n__all__ += _actor_meta_all\n"
  },
  {
    "path": "src/zvt/domain/actor/actor_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.contract.schema import ActorEntity\n\nActorMetaBase = declarative_base()\n\n\n#: 参与者\nclass ActorMeta(ActorMetaBase, ActorEntity):\n    __tablename__ = \"actor_meta\"\n\n\nregister_schema(db_name=\"actor_meta\", schema_base=ActorMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"ActorMeta\"]\n"
  },
  {
    "path": "src/zvt/domain/actor/stock_actor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, DateTime, Float, Boolean, Integer\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.contract.schema import TradableMeetActor\n\nStockActorBase = declarative_base()\n\n\nclass StockTopTenFreeHolder(StockActorBase, TradableMeetActor):\n    __tablename__ = \"stock_top_ten_free_holder\"\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n\n    #: 持股数\n    holding_numbers = Column(Float)\n    #: 持股比例\n    holding_ratio = Column(Float)\n    #: 持股市值\n    holding_values = Column(Float)\n\n\nclass StockTopTenHolder(StockActorBase, TradableMeetActor):\n    __tablename__ = \"stock_top_ten_holder\"\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n\n    #: 持股数\n    holding_numbers = Column(Float)\n    #: 持股比例\n    holding_ratio = Column(Float)\n    #: 持股市值\n    holding_values = Column(Float)\n\n\nclass StockInstitutionalInvestorHolder(StockActorBase, TradableMeetActor):\n    __tablename__ = \"stock_institutional_investor_holder\"\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n\n    #: 持股数\n    holding_numbers = Column(Float)\n    #: 持股比例\n    holding_ratio = Column(Float)\n    #: 持股市值\n    holding_values = Column(Float)\n\n\nclass StockActorSummary(StockActorBase, TradableMeetActor):\n    __tablename__ = \"stock_actor_summary\"\n    #: tradable code\n    code = Column(String(length=64))\n    #: tradable name\n    name = Column(String(length=128))\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n\n    #: 变动比例\n    change_ratio = Column(Float)\n    #: 是否完成\n    is_complete = Column(Boolean)\n    #: 持股市值\n    actor_type = Column(String)\n    actor_count = Column(Integer)\n\n    #: 持股数\n    holding_numbers = Column(Float)\n    #: 持股比例\n    holding_ratio = Column(Float)\n    #: 持股市值\n    holding_values = Column(Float)\n\n\nregister_schema(db_name=\"stock_actor\", schema_base=StockActorBase, entity_type=\"stock\")\n\n\n# the __all__ is generated\n__all__ = [\"StockTopTenFreeHolder\", \"StockTopTenHolder\", \"StockInstitutionalInvestorHolder\", \"StockActorSummary\"]\n"
  },
  {
    "path": "src/zvt/domain/emotion/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule emotion\nfrom .emotion import *\nfrom .emotion import __all__ as _emotion_all\n\n__all__ += _emotion_all\n"
  },
  {
    "path": "src/zvt/domain/emotion/emotion.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, Integer, DateTime, Boolean, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nEmotionBase = declarative_base()\n\n\nclass LimitUpInfo(EmotionBase, Mixin):\n    __tablename__ = \"limit_up_info\"\n\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n    #: 是否新股\n    is_new = Column(Boolean)\n    #: 是否回封，是就是打开过，否相反\n    is_again_limit = Column(Boolean)\n    #: 涨停打开次数,0代表封住就没开板\n    open_count = Column(Integer)\n    #: 首次封板时间\n    first_limit_up_time = Column(DateTime)\n    #: 最后封板时间\n    last_limit_up_time = Column(DateTime)\n    #: 涨停类型:换手板，一字板\n    limit_up_type = Column(String)\n    #: 封单金额\n    order_amount = Column(String)\n    #: 最近一年封板成功率\n    success_rate = Column(Float)\n    #: 流通市值\n    currency_value = Column(Float)\n    #: 涨幅\n    change_pct = Column(Float)\n    #: 换手率\n    turnover_rate = Column(Float)\n    #: 涨停原因\n    reason = Column(String)\n    #: 几天几板\n    high_days = Column(String)\n    #: 最近几板，不一定是连板\n    high_days_count = Column(Integer)\n\n\nclass LimitDownInfo(EmotionBase, Mixin):\n    __tablename__ = \"limit_down_info\"\n\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n    #: 是否新股\n    is_new = Column(Boolean)\n    #: 是否回封，是就是打开过，否相反\n    is_again_limit = Column(Boolean)\n    #: 流通市值\n    currency_value = Column(Float)\n    #: 涨幅\n    change_pct = Column(Float)\n    #: 换手率\n    turnover_rate = Column(Float)\n\n\nclass Emotion(EmotionBase, Mixin):\n    __tablename__ = \"emotion\"\n    #: 涨停数量\n    limit_up_count = Column(Integer)\n    #: 炸板数\n    limit_up_open_count = Column(Integer)\n    #: 涨停封板成功率\n    limit_up_success_rate = Column(Float)\n\n    #: 连板高度\n    max_height = Column(Integer)\n    #: 连板数x个数 相加\n    continuous_power = Column(Integer)\n\n    #: 跌停数量\n    limit_down_count = Column(Integer)\n    #: 跌停打开\n    limit_down_open_count = Column(Integer)\n    #: 跌停封板成功率\n    limit_down_success_rate = Column(Float)\n\n\nregister_schema(db_name=\"emotion\", schema_base=EmotionBase)\n\n\n# the __all__ is generated\n__all__ = [\"LimitUpInfo\", \"LimitDownInfo\", \"Emotion\"]\n"
  },
  {
    "path": "src/zvt/domain/fundamental/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule dividend_financing\nfrom .dividend_financing import *\nfrom .dividend_financing import __all__ as _dividend_financing_all\n\n__all__ += _dividend_financing_all\n\n# import all from submodule finance\nfrom .finance import *\nfrom .finance import __all__ as _finance_all\n\n__all__ += _finance_all\n\n# import all from submodule trading\nfrom .trading import *\nfrom .trading import __all__ as _trading_all\n\n__all__ += _trading_all\n\n# import all from submodule valuation\nfrom .valuation import *\nfrom .valuation import __all__ as _valuation_all\n\n__all__ += _valuation_all\n"
  },
  {
    "path": "src/zvt/domain/fundamental/dividend_financing.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, DateTime, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nDividendFinancingBase = declarative_base()\n\n\nclass DividendFinancing(DividendFinancingBase, Mixin):\n    __tablename__ = \"dividend_financing\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    #: 分红总额\n    dividend_money = Column(Float)\n\n    #: 新股\n    ipo_issues = Column(Float)\n    ipo_raising_fund = Column(Float)\n\n    #: 增发\n    spo_issues = Column(Float)\n    spo_raising_fund = Column(Float)\n    #: 配股\n    rights_issues = Column(Float)\n    rights_raising_fund = Column(Float)\n\n\nclass DividendDetail(DividendFinancingBase, Mixin):\n    __tablename__ = \"dividend_detail\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    #: 公告日\n    announce_date = Column(DateTime)\n    #: 股权登记日\n    record_date = Column(DateTime)\n    #: 除权除息日\n    dividend_date = Column(DateTime)\n\n    #: 方案\n    dividend = Column(String(length=128))\n\n\nclass SpoDetail(DividendFinancingBase, Mixin):\n    __tablename__ = \"spo_detail\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    spo_issues = Column(Float)\n    spo_price = Column(Float)\n    spo_raising_fund = Column(Float)\n\n\nclass RightsIssueDetail(DividendFinancingBase, Mixin):\n    __tablename__ = \"rights_issue_detail\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    #: 配股\n    rights_issues = Column(Float)\n    rights_issue_price = Column(Float)\n    rights_raising_fund = Column(Float)\n\n\nregister_schema(\n    db_name=\"dividend_financing\", schema_base=DividendFinancingBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"DividendFinancing\", \"DividendDetail\", \"SpoDetail\", \"RightsIssueDetail\"]\n"
  },
  {
    "path": "src/zvt/domain/fundamental/finance.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, DateTime, Float, Integer\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nFinanceBase = declarative_base()\n\n\nclass BalanceSheet(FinanceBase, Mixin):\n    @classmethod\n    def important_cols(cls):\n        return [\n            \"total_assets\",\n            \"total_liabilities\",\n            \"equity\",\n            \"cash_and_cash_equivalents\",\n            \"accounts_receivable\",\n            \"inventories\",\n            \"goodwill\",\n        ]\n\n    __tablename__ = \"balance_sheet\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n\n    #: 流动资产\n    #\n    #: 货币资金\n    cash_and_cash_equivalents = Column(Float)\n    #: 应收票据\n    note_receivable = Column(Float)\n    #: 应收账款\n    accounts_receivable = Column(Float)\n    #: 预付款项\n    advances_to_suppliers = Column(Float)\n    #: 其他应收款\n    other_receivables = Column(Float)\n    #: 存货\n    inventories = Column(Float)\n    #: 一年内到期的非流动资产\n    current_portion_of_non_current_assets = Column(Float)\n    #: 其他流动资产\n    other_current_assets = Column(Float)\n    #: 流动资产合计\n    total_current_assets = Column(Float)\n    #: 非流动资产\n    #\n    #: 可供出售金融资产\n    fi_assets_saleable = Column(Float)\n    #: 长期应收款\n    long_term_receivables = Column(Float)\n    #: 长期股权投资\n    long_term_equity_investment = Column(Float)\n    #: 投资性房地产\n    real_estate_investment = Column(Float)\n    #: 固定资产\n    fixed_assets = Column(Float)\n    #: 在建工程\n    construction_in_process = Column(Float)\n    #: 无形资产\n    intangible_assets = Column(Float)\n    #: 商誉\n    goodwill = Column(Float)\n    #: 长期待摊费用\n    long_term_prepaid_expenses = Column(Float)\n    #: 递延所得税资产\n    deferred_tax_assets = Column(Float)\n    #: 其他非流动资产\n    other_non_current_assets = Column(Float)\n    #: 非流动资产合计\n    total_non_current_assets = Column(Float)\n    #: 资产总计\n    total_assets = Column(Float)\n    #: 流动负债\n    #\n    #: 短期借款\n    short_term_borrowing = Column(Float)\n    #: 吸收存款及同业存放\n    accept_money_deposits = Column(Float)\n    #: 应付账款\n    accounts_payable = Column(Float)\n    #: 预收款项\n    advances_from_customers = Column(Float)\n    #: 应付职工薪酬\n    employee_benefits_payable = Column(Float)\n    #: 应交税费\n    taxes_payable = Column(Float)\n    #: 应付利息\n    interest_payable = Column(Float)\n    #: 其他应付款\n    other_payable = Column(Float)\n    #: 一年内到期的非流动负债\n    current_portion_of_non_current_liabilities = Column(Float)\n    #: 其他流动负债\n    other_current_liabilities = Column(Float)\n    #: 流动负债合计\n    total_current_liabilities = Column(Float)\n    #: 非流动负债\n    #\n    #: 长期借款\n    long_term_borrowing = Column(Float)\n    #: 长期应付款\n    long_term_payable = Column(Float)\n    #: 递延收益\n    deferred_revenue = Column(Float)\n    #: 递延所得税负债\n    deferred_tax_liabilities = Column(Float)\n    #: 其他非流动负债\n    other_non_current_liabilities = Column(Float)\n    #: 非流动负债合计\n    total_non_current_liabilities = Column(Float)\n    #: 负债合计\n    total_liabilities = Column(Float)\n    #: 所有者权益(或股东权益)\n    #\n    #: 实收资本（或股本）\n    capital = Column(Float)\n    #: 资本公积\n    capital_reserve = Column(Float)\n    #: 专项储备\n    special_reserve = Column(Float)\n    #: 盈余公积\n    surplus_reserve = Column(Float)\n    #: 未分配利润\n    undistributed_profits = Column(Float)\n    #: 归属于母公司股东权益合计\n    equity = Column(Float)\n    #: 少数股东权益\n    equity_as_minority_interest = Column(Float)\n    #: 股东权益合计\n    total_equity = Column(Float)\n    #: 负债和股东权益合计\n    total_liabilities_and_equity = Column(Float)\n\n    #: 银行相关\n    #: 资产\n    #: 现金及存放中央银行款项\n    fi_cash_and_deposit_in_central_bank = Column(Float)\n    #: 存放同业款项\n    fi_deposit_in_other_fi = Column(Float)\n    #: 贵金属\n    fi_expensive_metals = Column(Float)\n    #: 拆出资金\n    fi_lending_to_other_fi = Column(Float)\n    #: 以公允价值计量且其变动计入当期损益的金融资产\n    fi_financial_assets_effect_current_income = Column(Float)\n    #: 衍生金融资产\n    fi_financial_derivative_asset = Column(Float)\n    #: 买入返售金融资产\n    fi_buying_sell_back_fi__asset = Column(Float)\n    #: 应收账款\n    #\n    #: 应收利息\n    fi_interest_receivable = Column(Float)\n    #: 发放贷款及垫款\n    fi_disbursing_loans_and_advances = Column(Float)\n    #: 可供出售金融资产\n    #\n    #: 持有至到期投资\n    fi_held_to_maturity_investment = Column(Float)\n    #: 应收款项类投资\n    fi_account_receivable_investment = Column(Float)\n    #: 投资性房地产\n    #\n    #: 固定资产\n    #\n    #: 无形资产\n    #\n    #: 商誉\n    #\n    #: 递延所得税资产\n    #\n    #: 其他资产\n    fi_other_asset = Column(Float)\n    #: 资产总计\n    #\n\n    #: 负债\n    #\n    #: 向中央银行借款\n    fi_borrowings_from_central_bank = Column(Float)\n    #: 同业和其他金融机构存放款项\n    fi_deposit_from_other_fi = Column(Float)\n    #: 拆入资金\n    fi_borrowings_from_fi = Column(Float)\n    #: 以公允价值计量且其变动计入当期损益的金融负债\n    fi_financial_liability_effect_current_income = Column(Float)\n    #: 衍生金融负债\n    fi_financial_derivative_liability = Column(Float)\n    #: 卖出回购金融资产款\n    fi_sell_buy_back_fi_asset = Column(Float)\n    #: 吸收存款\n    fi_savings_absorption = Column(Float)\n    #: 存款证及应付票据\n    fi_notes_payable = Column(Float)\n    #: 应付职工薪酬\n    #\n    #: 应交税费\n    #\n    #: 应付利息\n    #\n    #: 预计负债\n    fi_estimated_liabilities = Column(Float)\n    #: 应付债券\n    fi_bond_payable = Column(Float)\n    #: 其他负债\n    fi_other_liability = Column(Float)\n    #: 负债合计\n    #\n\n    #: 所有者权益(或股东权益)\n    #: 股本\n    fi_capital = Column(Float)\n    #: 其他权益工具\n    fi_other_equity_instruments = Column(Float)\n    #: 其中:优先股\n    fi_preferred_stock = Column(Float)\n    #: 资本公积\n    #\n    #: 盈余公积\n    #\n    #: 一般风险准备\n    fi_generic_risk_reserve = Column(Float)\n    #: 未分配利润\n    #\n    #: 归属于母公司股东权益合计\n    #\n    #: 股东权益合计\n    #\n    #: 负债及股东权益总计\n\n    #: 券商相关\n    #: 资产\n    #\n    #: 货币资金\n    #\n    #: 其中: 客户资金存款\n    fi_client_fund = Column(Float)\n    #: 结算备付金\n    fi_deposit_reservation_for_balance = Column(Float)\n    #: 其中: 客户备付金\n    fi_client_deposit_reservation_for_balance = Column(Float)\n    #: 融出资金\n    fi_margin_out_fund = Column(Float)\n    #: 以公允价值计量且其变动计入当期损益的金融资产\n    #\n    #: 衍生金融资产\n    #\n    #: 买入返售金融资产\n    #\n    #: 应收利息\n    #\n    #: 应收款项\n    fi_receivables = Column(Float)\n    #: 存出保证金\n    fi_deposit_for_recognizance = Column(Float)\n    #: 可供出售金融资产\n    #\n    #: 持有至到期投资\n    #\n    #: 长期股权投资\n    #\n    #: 固定资产\n    #\n    #: 在建工程\n    #\n    #: 无形资产\n    #\n    #: 商誉\n    #\n    #: 递延所得税资产\n    #\n    #: 其他资产\n    #\n    #: 资产总计\n    #\n    #: 负债\n    #\n    #: 短期借款\n    #\n    #: 拆入资金\n    #\n    #: 以公允价值计量且其变动计入当期损益的金融负债\n    #\n    #: 衍生金融负债\n    #\n    #: 卖出回购金融资产款\n    #\n    #: 代理买卖证券款\n    fi_receiving_as_agent = Column(Float)\n    #: 应付账款\n    #\n    #: 应付职工薪酬\n    #\n    #: 应交税费\n    #\n    #: 应付利息\n    #\n    #: 应付短期融资款\n    fi_short_financing_payable = Column(Float)\n    #: 预计负债\n    #\n    #: 应付债券\n    #\n    #: 递延所得税负债\n    #\n    #: 其他负债\n    #\n    #: 负债合计\n    #\n    #: 所有者权益(或股东权益)\n    #\n    #: 股本\n    #\n    #: 资本公积\n    #\n    #: 其他权益工具\n    #\n    #: 盈余公积\n    #\n    #: 一般风险准备\n    #\n    #: 交易风险准备\n    fi_trade_risk_reserve = Column(Float)\n    #: 未分配利润\n    #\n    #: 归属于母公司股东权益合计\n    #\n    #: 少数股东权益\n    #\n    #: 股东权益合计\n    #\n    #: 负债和股东权益总计\n\n    #: 保险相关\n\n    #: 资产\n    #: 应收保费\n    fi_premiums_receivable = Column(Float)\n    #: 应收分保账款\n    fi_reinsurance_premium_receivable = Column(Float)\n    #: 应收分保合同准备金\n    fi_reinsurance_contract_reserve = Column(Float)\n    #: 保户质押贷款\n    fi_policy_pledge_loans = Column(Float)\n    #: 发放贷款及垫款\n    #: 定期存款\n    fi_time_deposit = Column(Float)\n    #: 可供出售金融资产\n    #\n    #: 持有至到期投资\n    #\n    #: 应收款项类投资\n    #\n    #: 应收账款\n    #\n    #: 长期股权投资\n    #\n    #: 存出资本保证金\n    fi_deposit_for_capital_recognizance = Column(Float)\n    #: 投资性房地产\n    #\n    #: 固定资产\n    #\n    #: 无形资产\n    #\n    #: 商誉\n    #\n    #: 递延所得税资产\n    #\n    #: 其他资产\n    #\n    #: 独立账户资产\n    fi_capital_in_independent_accounts = Column(Float)\n    #: 资产总计\n    #\n    #: 负债\n    #\n    #: 短期借款\n    #\n    #: 同业及其他金融机构存放款项\n    #\n    #: 拆入资金\n    #\n    #: 以公允价值计量且其变动计入当期损益的金融负债\n    #\n    #: 衍生金融负债\n    #\n    #: 卖出回购金融资产款\n    #\n    #: 吸收存款\n    #\n    #: 代理买卖证券款\n    #\n    #: 应付账款\n    #\n    #: 预收账款\n    fi_advance_from_customers = Column(Float)\n    #: 预收保费\n    fi_advance_premium = Column(Float)\n    #: 应付手续费及佣金\n    fi_fees_and_commissions_payable = Column(Float)\n    #: 应付分保账款\n    fi_dividend_payable_for_reinsurance = Column(Float)\n    #: 应付职工薪酬\n    #\n    #: 应交税费\n    #\n    #: 应付利息\n    #\n    #: 预计负债\n    #\n    #: 应付赔付款\n    fi_claims_payable = Column(Float)\n    #: 应付保单红利\n    fi_policy_holder_dividend_payable = Column(Float)\n    #: 保户储金及投资款\n    fi_policy_holder_deposits_and_investment_funds = Column(Float)\n    #: 保险合同准备金\n    fi_contract_reserve = Column(Float)\n    #: 长期借款\n    #\n    #: 应付债券\n    #\n    #: 递延所得税负债\n    #\n    #: 其他负债\n    #\n    #: 独立账户负债\n    fi_independent_liability = Column(Float)\n    #: 负债合计\n    #\n    #: 所有者权益(或股东权益)\n    #\n    #: 股本\n    #\n    #: 资本公积\n    #\n    #: 盈余公积\n    #\n    #: 一般风险准备\n    #\n    #: 未分配利润\n    #\n    #: 归属于母公司股东权益总计\n    #\n    #: 少数股东权益\n    #\n    #: 股东权益合计\n    #\n    #: 负债和股东权益总计\n\n\nclass IncomeStatement(FinanceBase, Mixin):\n    @classmethod\n    def important_cols(cls):\n        return [\n            \"operating_income\",\n            \"investment_income\",\n            \"total_operating_costs\",\n            \"total_profits\",\n            \"sales_costs\",\n            \"managing_costs\",\n            \"financing_costs\",\n        ]\n\n    __tablename__ = \"income_statement\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n\n    #: 营业总收入\n    #\n    #: 营业收入\n    operating_income = Column(Float)\n    #: 营业总成本\n    total_operating_costs = Column(Float)\n    #: 营业成本\n    operating_costs = Column(Float)\n    #: 研发费用\n    rd_costs = Column(Float)\n    #: 提取保险合同准备金净额\n    net_change_in_insurance_contract_reserves = Column(Float)\n    #: 营业税金及附加\n    business_taxes_and_surcharges = Column(Float)\n    #: 销售费用\n    sales_costs = Column(Float)\n    #: 管理费用\n    managing_costs = Column(Float)\n    #: 财务费用\n    financing_costs = Column(Float)\n    #: 资产减值损失\n    assets_devaluation = Column(Float)\n    #: 其他经营收益\n    #\n    #: 加: 投资收益\n    investment_income = Column(Float)\n    #: 其中: 对联营企业和合营企业的投资收益\n    investment_income_from_related_enterprise = Column(Float)\n    #: 营业利润\n    operating_profit = Column(Float)\n    #: 加: 营业外收入\n    non_operating_income = Column(Float)\n    #: 减: 营业外支出\n    non_operating_costs = Column(Float)\n    #: 其中: 非流动资产处置净损失\n    loss_on_disposal_non_current_asset = Column(Float)\n\n    #: 利润总额\n    total_profits = Column(Float)\n    #: 减: 所得税费用\n    tax_expense = Column(Float)\n    #: 净利润\n    net_profit = Column(Float)\n    #: 其中: 归属于母公司股东的净利润\n    net_profit_as_parent = Column(Float)\n    #: 少数股东损益\n    net_profit_as_minority_interest = Column(Float)\n    #: 扣除非经常性损益后的净利润\n    deducted_net_profit = Column(Float)\n    #: 每股收益\n    #: 基本每股收益\n    eps = Column(Float)\n    #: 稀释每股收益\n    diluted_eps = Column(Float)\n    #: 其他综合收益\n    other_comprehensive_income = Column(Float)\n    #: 归属于母公司股东的其他综合收益\n    other_comprehensive_income_as_parent = Column(Float)\n    #: 归属于少数股东的其他综合收益\n    other_comprehensive_income_as_minority_interest = Column(Float)\n    #: 综合收益总额\n    total_comprehensive_income = Column(Float)\n    #: 归属于母公司所有者的综合收益总额\n    total_comprehensive_income_as_parent = Column(Float)\n    #: 归属于少数股东的综合收益总额\n    total_comprehensive_income_as_minority_interest = Column(Float)\n\n    #: 银行相关\n    #: 利息净收入\n    fi_net_interest_income = Column(Float)\n    #: 其中:利息收入\n    fi_interest_income = Column(Float)\n    #: 利息支出\n    fi_interest_expenses = Column(Float)\n    #: 手续费及佣金净收入\n    fi_net_incomes_from_fees_and_commissions = Column(Float)\n    #: 其中:手续费及佣金收入\n    fi_incomes_from_fees_and_commissions = Column(Float)\n    #: 手续费及佣金支出\n    fi_expenses_for_fees_and_commissions = Column(Float)\n    #: 公允价值变动收益\n    fi_income_from_fair_value_change = Column(Float)\n    #: 汇兑收益\n    fi_income_from_exchange = Column(Float)\n    #: 其他业务收入\n    fi_other_income = Column(Float)\n    #: 业务及管理费\n    fi_operate_and_manage_expenses = Column(Float)\n\n    #: 保险相关\n    #: 已赚保费\n    fi_net_income_from_premium = Column(Float)\n    #: 其中:保险业务收入\n    fi_income_from_premium = Column(Float)\n    #: 分保费收入\n    fi_income_from_reinsurance_premium = Column(Float)\n    #: 减:分出保费\n    fi_reinsurance_premium = Column(Float)\n    #: 提取未到期责任准备金\n    fi_undue_duty_reserve = Column(Float)\n    #: 银行业务利息净收入\n    fi_net_income_from_bank_interest = Column(Float)\n    #: 其中:银行业务利息收入\n    fi_income_from_bank_interest = Column(Float)\n    #: 银行业务利息支出\n    fi_expenses_for_bank_interest = Column(Float)\n    #: 非保险业务手续费及佣金净收入\n    fi_net_incomes_from_fees_and_commissions_of_non_insurance = Column(Float)\n    #: 非保险业务手续费及佣金收入\n    fi_incomes_from_fees_and_commissions_of_non_insurance = Column(Float)\n    #: 非保险业务手续费及佣金支出\n    fi_expenses_for_fees_and_commissions_of_non_insurance = Column(Float)\n    #: 退保金\n    fi_insurance_surrender_costs = Column(Float)\n    #: 赔付支出\n    fi_insurance_claims_expenses = Column(Float)\n    #: 减:摊回赔付支出\n    fi_amortized_insurance_claims_expenses = Column(Float)\n    #: 提取保险责任准备金\n    fi_insurance_duty_reserve = Column(Float)\n    #: 减:摊回保险责任准备金\n    fi_amortized_insurance_duty_reserve = Column(Float)\n    #: 保单红利支出\n    fi_dividend_expenses_to_insured = Column(Float)\n    #: 分保费用\n    fi_reinsurance_expenses = Column(Float)\n    #: 减:摊回分保费用\n    fi_amortized_reinsurance_expenses = Column(Float)\n    #: 其他业务成本\n    fi_other_op_expenses = Column(Float)\n\n    #: 券商相关\n    #: 手续费及佣金净收入\n    #\n    #: 其中:代理买卖证券业务净收入\n    fi_net_incomes_from_trading_agent = Column(Float)\n    #: 证券承销业务净收入\n    fi_net_incomes_from_underwriting = Column(Float)\n    #: 受托客户资产管理业务净收入\n    fi_net_incomes_from_customer_asset_management = Column(Float)\n    #: 手续费及佣金净收入其他项目\n    fi_fees_from_other = Column(Float)\n    #: 公允价值变动收益\n    #\n    #: 其中:可供出售金融资产公允价值变动损益\n    fi_income_from_fair_value_change_of_fi_salable = Column(Float)\n\n\nclass CashFlowStatement(FinanceBase, Mixin):\n    @classmethod\n    def important_cols(cls):\n        return [\"net_op_cash_flows\", \"net_investing_cash_flows\", \"net_financing_cash_flows\", \"cash\"]\n\n    __tablename__ = \"cash_flow_statement\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n    #: 经营活动产生的现金流量\n    #\n    #: 销售商品、提供劳务收到的现金\n    cash_from_selling = Column(Float)\n\n    #: 收到的税费返还\n    tax_refund = Column(Float)\n\n    #: 收到其他与经营活动有关的现金\n    cash_from_other_op = Column(Float)\n\n    #: 经营活动现金流入小计\n    total_op_cash_inflows = Column(Float)\n\n    #: 购买商品、接受劳务支付的现金\n    cash_to_goods_services = Column(Float)\n    #: 支付给职工以及为职工支付的现金\n    cash_to_employees = Column(Float)\n    #: 支付的各项税费\n    taxes_and_surcharges = Column(Float)\n    #: 支付其他与经营活动有关的现金\n    cash_to_other_related_op = Column(Float)\n    #: 经营活动现金流出小计\n    total_op_cash_outflows = Column(Float)\n\n    #: 经营活动产生的现金流量净额\n    net_op_cash_flows = Column(Float)\n\n    #: 投资活动产生的现金流量\n\n    #: 收回投资收到的现金\n    cash_from_disposal_of_investments = Column(Float)\n    #: 取得投资收益收到的现金\n    cash_from_returns_on_investments = Column(Float)\n    #: 处置固定资产、无形资产和其他长期资产收回的现金净额\n    cash_from_disposal_fixed_intangible_assets = Column(Float)\n    #: 处置子公司及其他营业单位收到的现金净额\n    cash_from_disposal_subsidiaries = Column(Float)\n\n    #: 收到其他与投资活动有关的现金\n    cash_from_other_investing = Column(Float)\n\n    #: 投资活动现金流入小计\n    total_investing_cash_inflows = Column(Float)\n\n    #: 购建固定资产、无形资产和其他长期资产支付的现金\n    cash_to_acquire_fixed_intangible_assets = Column(Float)\n    #: 投资支付的现金\n    cash_to_investments = Column(Float)\n\n    #: 取得子公司及其他营业单位支付的现金净额\n    cash_to_acquire_subsidiaries = Column(Float)\n\n    #: 支付其他与投资活动有关的现金\n    cash_to_other_investing = Column(Float)\n\n    #: 投资活动现金流出小计\n    total_investing_cash_outflows = Column(Float)\n\n    #: 投资活动产生的现金流量净额\n    net_investing_cash_flows = Column(Float)\n\n    #: 筹资活动产生的现金流量\n    #\n    #: 吸收投资收到的现金\n    cash_from_accepting_investment = Column(Float)\n    #: 子公司吸收少数股东投资收到的现金\n    cash_from_subsidiaries_accepting_minority_interest = Column(Float)\n\n    #: 取得借款收到的现金\n    cash_from_borrowings = Column(Float)\n    #: 发行债券收到的现金\n    cash_from_issuing_bonds = Column(Float)\n    #: 收到其他与筹资活动有关的现金\n    cash_from_other_financing = Column(Float)\n\n    #: 筹资活动现金流入小计\n    total_financing_cash_inflows = Column(Float)\n\n    #: 偿还债务支付的现金\n    cash_to_repay_borrowings = Column(Float)\n\n    #: 分配股利、利润或偿付利息支付的现金\n    cash_to_pay_interest_dividend = Column(Float)\n\n    #: 子公司支付给少数股东的股利、利润\n    cash_to_pay_subsidiaries_minority_interest = Column(Float)\n\n    #: 支付其他与筹资活动有关的现金\n    cash_to_other_financing = Column(Float)\n    #: 筹资活动现金流出小计\n    total_financing_cash_outflows = Column(Float)\n\n    #: 筹资活动产生的现金流量净额\n    net_financing_cash_flows = Column(Float)\n    #: 汇率变动对现金及现金等价物的影响\n    foreign_exchange_rate_effect = Column(Float)\n    #: 现金及现金等价物净增加额\n    net_cash_increase = Column(Float)\n    #: 加: 期初现金及现金等价物余额\n    cash_at_beginning = Column(Float)\n    #: 期末现金及现金等价物余额\n    cash = Column(Float)\n\n    #: 银行相关\n    #: 客户存款和同业及其他金融机构存放款项净增加额\n    fi_deposit_increase = Column(Float)\n    #: 向中央银行借款净增加额\n    fi_borrow_from_central_bank_increase = Column(Float)\n    #: 存放中央银行和同业款项及其他金融机构净减少额\n    fi_deposit_in_others_decrease = Column(Float)\n    #: 拆入资金及卖出回购金融资产款净增加额\n    fi_borrowing_and_sell_repurchase_increase = Column(Float)\n    #: 其中:卖出回购金融资产款净增加额\n    fi_sell_repurchase_increase = Column(Float)\n    #: 拆出资金及买入返售金融资产净减少额\n    fi_lending_and_buy_repurchase_decrease = Column(Float)\n    #: 其中:拆出资金净减少额\n    fi_lending_decrease = Column(Float)\n    #: 买入返售金融资产净减少额\n    fi_buy_repurchase_decrease = Column(Float)\n    #: 收取的利息、手续费及佣金的现金\n    fi_cash_from_interest_commission = Column(Float)\n    #: 客户贷款及垫款净增加额\n    fi_loan_advance_increase = Column(Float)\n    #: 存放中央银行和同业及其他金融机构款项净增加额\n    fi_deposit_in_others_increase = Column(Float)\n    #: 拆出资金及买入返售金融资产净增加额\n    fi_lending_and_buy_repurchase_increase = Column(Float)\n    #: 其中:拆出资金净增加额\n    fi_lending_increase = Column(Float)\n    #: 拆入资金及卖出回购金融资产款净减少额\n    fi_borrowing_and_sell_repurchase_decrease = Column(Float)\n    #: 其中:拆入资金净减少额\n    fi_borrowing_decrease = Column(Float)\n    #: 卖出回购金融资产净减少额\n    fi_sell_repurchase_decrease = Column(Float)\n    #: 支付利息、手续费及佣金的现金\n    fi_cash_to_interest_commission = Column(Float)\n    #: 应收账款净增加额\n    fi_account_receivable_increase = Column(Float)\n    #: 偿付债券利息支付的现金\n    fi_cash_to_pay_interest = Column(Float)\n\n    #: 保险相关\n    #: 收到原保险合同保费取得的现金\n    fi_cash_from_premium_of_original = Column(Float)\n    #: 保户储金及投资款净增加额\n    fi_insured_deposit_increase = Column(Float)\n    #: 银行及证券业务卖出回购资金净增加额\n    fi_bank_broker_sell_repurchase_increase = Column(Float)\n    #: 银行及证券业务买入返售资金净减少额\n    fi_bank_broker_buy_repurchase_decrease = Column(Float)\n    #: 支付原保险合同赔付等款项的现金\n    fi_cash_to_insurance_claim = Column(Float)\n    #: 支付再保险业务现金净额\n    fi_cash_to_reinsurance = Column(Float)\n    #: 银行业务及证券业务拆借资金净减少额\n    fi_lending_decrease = Column(Float)\n    #: 银行业务及证券业务卖出回购资金净减少额\n    fi_bank_broker_sell_repurchase_decrease = Column(Float)\n    #: 支付保单红利的现金\n    fi_cash_to_dividends = Column(Float)\n    #: 保户质押贷款净增加额\n    fi_insured_pledge_loans_increase = Column(Float)\n    #: 收购子公司及其他营业单位支付的现金净额\n    fi_cash_to_acquire_subsidiaries = Column(Float)\n    #: 处置子公司及其他营业单位流出的现金净额\n    fi_cash_to_disposal_subsidiaries = Column(Float)\n    #: 支付卖出回购金融资产款现金净额\n    fi_cash_to_sell_repurchase = Column(Float)\n\n    #: 券商相关\n    #: 拆入资金净增加额\n    fi_borrowing_increase = Column(Float)\n    #: 代理买卖证券收到的现金净额\n    fi_cash_from_trading_agent = Column(Float)\n    #: 回购业务资金净增加额\n    fi_cash_from_repurchase_increase = Column(Float)\n    #: 处置交易性金融资产的净减少额\n    fi_disposal_trade_asset_decrease = Column(Float)\n    #: 回购业务资金净减少额\n    fi_repurchase_decrease = Column(Float)\n    #: 代理买卖证券支付的现金净额（净减少额）\n    fi_cash_to_agent_trade = Column(Float)\n\n\n#: 主要财务指标\n\n\nclass FinanceFactor(FinanceBase, Mixin):\n    @classmethod\n    def important_cols(cls):\n        return [\n            \"basic_eps\",\n            \"total_op_income\",\n            \"net_profit\",\n            \"op_income_growth_yoy\",\n            \"net_profit_growth_yoy\",\n            \"roe\",\n            \"rota\",\n            \"gross_profit_margin\",\n            \"net_margin\",\n        ]\n\n    __tablename__ = \"finance_factor\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n    #: 每股指标\n    #\n    #: 基本每股收益(元)\n    basic_eps = Column(Float)\n    #: 扣非每股收益(元)\n    deducted_eps = Column(Float)\n    #: 稀释每股收益(元)\n    diluted_eps = Column(Float)\n    #: 每股净资产(元)\n    bps = Column(Float)\n    #: 每股资本公积(元)\n    capital_reserve_ps = Column(Float)\n    #: 每股未分配利润(元)\n    undistributed_profit_ps = Column(Float)\n    #: 每股经营现金流(元)\n    op_cash_flow_ps = Column(Float)\n    #: 成长能力指标\n    #\n    #: 营业总收入(元)\n    total_op_income = Column(Float)\n    #: 毛利润(元)\n    gross_profit = Column(Float)\n    #: 归属净利润(元)\n    net_profit = Column(Float)\n    #: 扣非净利润(元)\n    deducted_net_profit = Column(Float)\n    #: 营业总收入同比增长\n    op_income_growth_yoy = Column(Float)\n    #: 归属净利润同比增长\n    net_profit_growth_yoy = Column(Float)\n    #: 扣非净利润同比增长\n    deducted_net_profit_growth_yoy = Column(Float)\n    #: 营业总收入滚动环比增长\n    op_income_growth_qoq = Column(Float)\n    #: 归属净利润滚动环比增长\n    net_profit_growth_qoq = Column(Float)\n    #: 扣非净利润滚动环比增长\n    deducted_net_profit_growth_qoq = Column(Float)\n    #: 盈利能力指标\n    #\n    #: 净资产收益率(加权)\n    roe = Column(Float)\n    #: 净资产收益率(扣非/加权)\n    deducted_roe = Column(Float)\n    #: 总资产收益率(加权)\n    rota = Column(Float)\n    #: 毛利率\n    gross_profit_margin = Column(Float)\n    #: 净利率\n    net_margin = Column(Float)\n    #: 收益质量指标\n    #\n    #: 预收账款/营业收入\n    advance_receipts_per_op_income = Column(Float)\n    #: 销售净现金流/营业收入\n    sales_net_cash_flow_per_op_income = Column(Float)\n    #: 经营净现金流/营业收入\n    op_net_cash_flow_per_op_income = Column(Float)\n    #: 实际税率\n    actual_tax_rate = Column(Float)\n    #: 财务风险指标\n    #\n    #: 流动比率\n    current_ratio = Column(Float)\n    #: 速动比率\n    quick_ratio = Column(Float)\n    #: 现金流量比率\n    cash_flow_ratio = Column(Float)\n    #: 资产负债率\n    debt_asset_ratio = Column(Float)\n    #: 权益乘数\n    em = Column(Float)\n    #: 产权比率\n    equity_ratio = Column(Float)\n    #: 营运能力指标(一般企业)\n    #\n    #: 总资产周转天数(天)\n    total_assets_turnover_days = Column(Integer)\n    #: 存货周转天数(天)\n    inventory_turnover_days = Column(Integer)\n    #: 应收账款周转天数(天)\n    receivables_turnover_days = Column(Integer)\n    #: 总资产周转率(次)\n    total_assets_turnover = Column(Float)\n    #: 存货周转率(次)\n    inventory_turnover = Column(Float)\n    #: 应收账款周转率(次)\n    receivables_turnover = Column(Float)\n\n    #: 专项指标(银行)\n    #\n    #: 存款总额\n    fi_total_deposit = Column(Float)\n    #: 贷款总额\n    fi_total_loan = Column(Float)\n    #: 存贷款比例\n    fi_loan_deposit_ratio = Column(Float)\n    #: 资本充足率\n    fi_capital_adequacy_ratio = Column(Float)\n    #: 核心资本充足率\n    fi_core_capital_adequacy_ratio = Column(Float)\n    #: 不良贷款率\n    fi_npl_ratio = Column(Float)\n    #: 不良贷款拨备覆盖率\n    fi_npl_provision_coverage = Column(Float)\n    #: 资本净额\n    fi_net_capital = Column(Float)\n    #: 专项指标(保险)\n    #\n    #: 总投资收益率\n    insurance_roi = Column(Float)\n    #: 净投资收益率\n    insurance_net_investment_yield = Column(Float)\n    #: 已赚保费\n    insurance_earned_premium = Column(Float)\n    #: 赔付支出\n    insurance_payout = Column(Float)\n    #: 退保率\n    insurance_surrender_rate = Column(Float)\n    #: 偿付能力充足率\n    insurance_solvency_adequacy_ratio = Column(Float)\n    #: 专项指标(券商)\n    #\n    #: 净资本\n    broker_net_capital = Column(Float)\n    #: 净资产\n    broker_net_assets = Column(Float)\n    #: 净资本/净资产\n    broker_net_capital_assets_ratio = Column(Float)\n    #: 自营固定收益类证券规模/净资本\n    broker_self_operated_fixed_income_securities_net_capital_ratio = Column(Float)\n\n\nregister_schema(db_name=\"finance\", schema_base=FinanceBase, entity_type=\"stock\")\n\n\n# the __all__ is generated\n__all__ = [\"BalanceSheet\", \"IncomeStatement\", \"CashFlowStatement\", \"FinanceFactor\"]\n"
  },
  {
    "path": "src/zvt/domain/fundamental/trading.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nTradingBase = declarative_base()\n\n\nclass ManagerTrading(TradingBase, Mixin):\n    __tablename__ = \"manager_trading\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n    #: 日期 变动人 变动数量(股) 交易均价(元) 结存股票(股) 交易方式 董监高管 高管职位 与高管关系\n    #: 2017-08-11 韦春 200 9.16 -- 竞价交易 刘韬 高管 兄弟姐妹\n\n    #: 变动人\n    trading_person = Column(String(length=32))\n    #: 变动数量\n    volume = Column(Float)\n    #: 交易均价\n    price = Column(Float)\n    #: 结存股票\n    holding = Column(Float)\n    #: 交易方式\n    trading_way = Column(String(length=32))\n    #: 董监高管\n    manager = Column(String(length=32))\n    #: 高管职位\n    manager_position = Column(String(length=32))\n    #: 与高管关系\n    relationship_with_manager = Column(String(length=32))\n\n\nclass HolderTrading(TradingBase, Mixin):\n    __tablename__ = \"holder_trading\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    #: 股东名称\n    holder_name = Column(String(length=32))\n    #: 变动数量\n    volume = Column(Float)\n    #: 变动比例\n    change_pct = Column(Float)\n    #: 变动后持股比例\n    holding_pct = Column(Float)\n\n\nclass BigDealTrading(TradingBase, Mixin):\n    __tablename__ = \"big_deal_trading\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    #: 成交额\n    turnover = Column(Float)\n    #: 成交价\n    price = Column(Float)\n    #: 卖出营业部\n    sell_broker = Column(String(length=128))\n    #: 买入营业部\n    buy_broker = Column(String(length=128))\n    #: 折/溢价率\n    compare_rate = Column(Float)\n\n\nclass MarginTrading(TradingBase, Mixin):\n    __tablename__ = \"margin_trading\"\n    code = Column(String(length=32))\n\n    #: 融资余额(元）\n    fin_value = Column(Float)\n    #: 融资买入额（元）\n    fin_buy_value = Column(Float)\n    #: 融资偿还额（元）\n    fin_refund_value = Column(Float)\n    #: 融券余量（股）\n    sec_value = Column(Float)\n    #: 融券卖出量（股）\n    sec_sell_value = Column(Float)\n    #: 融券偿还量（股）\n    sec_refund_value = Column(Float)\n    #: 融资融券余额（元）\n    fin_sec_value = Column(Float)\n\n\nclass DragonAndTiger(TradingBase, Mixin):\n    __tablename__ = \"dragon_and_tiger\"\n\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    #: 异动原因\n    reason = Column(String(length=128))\n    #: 成交额\n    turnover = Column(Float)\n    #: 涨幅\n    change_pct = Column(Float)\n    #: 净买入\n    net_in = Column(Float)\n\n    #: 买入营业部\n    dep1 = Column(String(length=128))\n    dep1_in = Column(Float)\n    dep1_out = Column(Float)\n    dep1_rate = Column(Float)\n\n    dep2 = Column(String(length=128))\n    dep2_in = Column(Float)\n    dep2_out = Column(Float)\n    dep2_rate = Column(Float)\n\n    dep3 = Column(String(length=128))\n    dep3_in = Column(Float)\n    dep3_out = Column(Float)\n    dep3_rate = Column(Float)\n\n    dep4 = Column(String(length=128))\n    dep4_in = Column(Float)\n    dep4_out = Column(Float)\n    dep4_rate = Column(Float)\n\n    dep5 = Column(String(length=128))\n    dep5_in = Column(Float)\n    dep5_out = Column(Float)\n    dep5_rate = Column(Float)\n\n    #: 卖出营业部\n    dep_1 = Column(String(length=128))\n    dep_1_in = Column(Float)\n    dep_1_out = Column(Float)\n    dep_1_rate = Column(Float)\n\n    dep_2 = Column(String(length=128))\n    dep_2_in = Column(Float)\n    dep_2_out = Column(Float)\n    dep_2_rate = Column(Float)\n\n    dep_3 = Column(String(length=128))\n    dep_3_in = Column(Float)\n    dep_3_out = Column(Float)\n    dep_3_rate = Column(Float)\n\n    dep_4 = Column(String(length=128))\n    dep_4_in = Column(Float)\n    dep_4_out = Column(Float)\n    dep_4_rate = Column(Float)\n\n    dep_5 = Column(String(length=128))\n    dep_5_in = Column(Float)\n    dep_5_out = Column(Float)\n    dep_5_rate = Column(Float)\n\n\nregister_schema(\n    db_name=\"trading\", schema_base=TradingBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"ManagerTrading\", \"HolderTrading\", \"BigDealTrading\", \"MarginTrading\", \"DragonAndTiger\"]\n"
  },
  {
    "path": "src/zvt/domain/fundamental/valuation.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nValuationBase = declarative_base()\n\n\nclass StockValuation(ValuationBase, Mixin):\n    __tablename__ = \"stock_valuation\"\n\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n    #: 总股本(股)\n    capitalization = Column(Float)\n    #: 公司已发行的普通股股份总数(包含A股，B股和H股的总股本)\n    circulating_cap = Column(Float)\n    #: 市值\n    market_cap = Column(Float)\n    #: 流通市值\n    circulating_market_cap = Column(Float)\n    #: 换手率\n    turnover_ratio = Column(Float)\n    #: 静态pe\n    pe = Column(Float)\n    #: 动态pe\n    pe_ttm = Column(Float)\n    #: 市净率\n    pb = Column(Float)\n    #: 市销率\n    ps = Column(Float)\n    #: 市现率\n    pcf = Column(Float)\n\n\nclass EtfValuation(ValuationBase, Mixin):\n    __tablename__ = \"etf_valuation\"\n\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n    #: 静态pe\n    pe = Column(Float)\n    #: 加权\n    pe1 = Column(Float)\n    #: 动态pe\n    pe_ttm = Column(Float)\n    #: 加权\n    pe_ttm1 = Column(Float)\n    #: 市净率\n    pb = Column(Float)\n    #: 加权\n    pb1 = Column(Float)\n    #: 市销率\n    ps = Column(Float)\n    #: 加权\n    ps1 = Column(Float)\n    #: 市现率\n    pcf = Column(Float)\n    #: 加权\n    pcf1 = Column(Float)\n\n\nregister_schema(db_name=\"valuation\", schema_base=ValuationBase, entity_type=\"stock\")\n\n\n# the __all__ is generated\n__all__ = [\"StockValuation\", \"EtfValuation\"]\n"
  },
  {
    "path": "src/zvt/domain/macro/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule monetary\nfrom .monetary import *\nfrom .monetary import __all__ as _monetary_all\n\n__all__ += _monetary_all\n\n# import all from submodule macro\nfrom .macro import *\nfrom .macro import __all__ as _macro_all\n\n__all__ += _macro_all\n"
  },
  {
    "path": "src/zvt/domain/macro/macro.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, Float, BIGINT\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nMacroBase = declarative_base()\n\n\nclass Economy(MacroBase, Mixin):\n    # https://datatopics.worldbank.org/world-development-indicators//themes/economy.html\n    __tablename__ = \"economy\"\n\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n    population = Column(BIGINT)\n\n    gdp = Column(Float)\n    gdp_per_capita = Column(Float)\n    gdp_per_employed = Column(Float)\n    gdp_growth = Column(Float)\n    agriculture_growth = Column(Float)\n    industry_growth = Column(Float)\n    manufacturing_growth = Column(Float)\n    service_growth = Column(Float)\n    consumption_growth = Column(Float)\n    capital_growth = Column(Float)\n    exports_growth = Column(Float)\n    imports_growth = Column(Float)\n\n    gni = Column(Float)\n    gni_per_capita = Column(Float)\n\n    gross_saving = Column(Float)\n    cpi = Column(Float)\n    unemployment_rate = Column(Float)\n    fdi_of_gdp = Column(Float)\n\n\nregister_schema(db_name=\"macro\", schema_base=MacroBase)\n\n\n# the __all__ is generated\n__all__ = [\"Economy\"]\n"
  },
  {
    "path": "src/zvt/domain/macro/monetary.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nMonetaryBase = declarative_base()\n\n\nclass TreasuryYield(MonetaryBase, Mixin):\n    __tablename__ = \"treasury_yield\"\n\n    code = Column(String(length=32))\n\n    # 2年期\n    yield_2 = Column(Float)\n    # 5年期\n    yield_5 = Column(Float)\n    # 10年期\n    yield_10 = Column(Float)\n    # 30年期\n    yield_30 = Column(Float)\n\n\nregister_schema(db_name=\"monetary\", schema_base=MonetaryBase)\n\n\n# the __all__ is generated\n__all__ = [\"TreasuryYield\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule blockus_meta\nfrom .blockus_meta import *\nfrom .blockus_meta import __all__ as _blockus_meta_all\n\n__all__ += _blockus_meta_all\n\n# import all from submodule stockus_meta\nfrom .stockus_meta import *\nfrom .stockus_meta import __all__ as _stockus_meta_all\n\n__all__ += _stockus_meta_all\n\n# import all from submodule indexhk_meta\nfrom .indexhk_meta import *\nfrom .indexhk_meta import __all__ as _indexhk_meta_all\n\n__all__ += _indexhk_meta_all\n\n# import all from submodule stockhk_meta\nfrom .stockhk_meta import *\nfrom .stockhk_meta import __all__ as _stockhk_meta_all\n\n__all__ += _stockhk_meta_all\n\n# import all from submodule indexus_meta\nfrom .indexus_meta import *\nfrom .indexus_meta import __all__ as _indexus_meta_all\n\n__all__ += _indexus_meta_all\n\n# import all from submodule country_meta\nfrom .country_meta import *\nfrom .country_meta import __all__ as _country_meta_all\n\n__all__ += _country_meta_all\n\n# import all from submodule cbond_meta\nfrom .cbond_meta import *\nfrom .cbond_meta import __all__ as _cbond_meta_all\n\n__all__ += _cbond_meta_all\n\n# import all from submodule index_meta\nfrom .index_meta import *\nfrom .index_meta import __all__ as _index_meta_all\n\n__all__ += _index_meta_all\n\n# import all from submodule future_meta\nfrom .future_meta import *\nfrom .future_meta import __all__ as _future_meta_all\n\n__all__ += _future_meta_all\n\n# import all from submodule etf_meta\nfrom .etf_meta import *\nfrom .etf_meta import __all__ as _etf_meta_all\n\n__all__ += _etf_meta_all\n\n# import all from submodule currency_meta\nfrom .currency_meta import *\nfrom .currency_meta import __all__ as _currency_meta_all\n\n__all__ += _currency_meta_all\n\n# import all from submodule stock_meta\nfrom .stock_meta import *\nfrom .stock_meta import __all__ as _stock_meta_all\n\n__all__ += _stock_meta_all\n\n# import all from submodule block_meta\nfrom .block_meta import *\nfrom .block_meta import __all__ as _block_meta_all\n\n__all__ += _block_meta_all\n\n# import all from submodule fund_meta\nfrom .fund_meta import *\nfrom .fund_meta import __all__ as _fund_meta_all\n\n__all__ += _fund_meta_all\n"
  },
  {
    "path": "src/zvt/domain/meta/block_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy import Column, String\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Portfolio, PortfolioStock\nfrom zvt.contract.register import register_schema, register_entity\n\nBlockMetaBase = declarative_base()\n\n\n#: 板块\n@register_entity(entity_type=\"block\")\nclass Block(BlockMetaBase, Portfolio):\n    __tablename__ = \"block\"\n\n    #: 板块类型，行业(industry),概念(concept)\n    category = Column(String(length=64))\n\n\nclass BlockStock(BlockMetaBase, PortfolioStock):\n    __tablename__ = \"block_stock\"\n\n\nregister_schema(db_name=\"block_meta\", schema_base=BlockMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Block\", \"BlockStock\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/blockus_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy import Column, String\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Portfolio, PortfolioStock\nfrom zvt.contract.register import register_schema, register_entity\n\nBlockusMetaBase = declarative_base()\n\n\n#: 板块\n@register_entity(entity_type=\"blockus\")\nclass Blockus(BlockusMetaBase, Portfolio):\n    __tablename__ = \"block\"\n\n    #: 板块类型，行业(industry),概念(concept)\n    category = Column(String(length=64))\n\n\nclass BlockusStockus(BlockusMetaBase, PortfolioStock):\n    __tablename__ = \"blockus_stockus\"\n\n\nregister_schema(db_name=\"blockus_meta\", schema_base=BlockusMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Blockus\", \"BlockusStockus\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/cbond_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import TradableEntity\nfrom zvt.contract.register import register_schema, register_entity\n\nCBondBase = declarative_base()\n\n\n#: 美股\n@register_entity(entity_type=\"cbond\")\nclass CBond(CBondBase, TradableEntity):\n    __tablename__ = \"cbond\"\n\n\nregister_schema(db_name=\"cbond_meta\", schema_base=CBondBase)\n\n\n# the __all__ is generated\n__all__ = [\"CBond\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/country_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy import Column, String, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema, register_entity\nfrom zvt.contract.schema import TradableEntity\n\nCountryMetaBase = declarative_base()\n\n\n@register_entity(entity_type=\"country\")\nclass Country(CountryMetaBase, TradableEntity):\n    __tablename__ = \"country\"\n\n    #: 区域\n    #: region\n    region = Column(String(length=128))\n    #: 首都\n    #: capital city\n    capital_city = Column(String(length=128))\n    #: 收入水平\n    #: income level\n    income_level = Column(String(length=64))\n    #: 贷款类型\n    #: lending type\n    lending_type = Column(String(length=64))\n    #: 经度\n    #: longitude\n    longitude = Column(Float)\n    #: 纬度\n    #: latitude\n    latitude = Column(Float)\n\n\nregister_schema(db_name=\"country_meta\", schema_base=CountryMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Country\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/currency_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema, register_entity\nfrom zvt.contract.schema import TradableEntity\n\nCurrencyMetaBase = declarative_base()\n\n\n@register_entity(entity_type=\"currency\")\nclass Currency(CurrencyMetaBase, TradableEntity):\n    __tablename__ = \"currency\"\n\n\nregister_schema(db_name=\"currency_meta\", schema_base=CurrencyMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Currency\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/etf_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy import Column, String\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Portfolio, PortfolioStockHistory\nfrom zvt.contract.register import register_schema, register_entity\nfrom zvt.utils.time_utils import now_pd_timestamp\n\nEtfMetaBase = declarative_base()\n\n\n#: etf\n@register_entity(entity_type=\"etf\")\nclass Etf(EtfMetaBase, Portfolio):\n    __tablename__ = \"etf\"\n    category = Column(String(length=64))\n\n    @classmethod\n    def get_stocks(cls, code=None, codes=None, ids=None, timestamp=now_pd_timestamp(), provider=None):\n        from zvt.api.portfolio import get_etf_stocks\n\n        return get_etf_stocks(code=code, codes=codes, ids=ids, timestamp=timestamp, provider=provider)\n\n\nclass EtfStock(EtfMetaBase, PortfolioStockHistory):\n    __tablename__ = \"etf_stock\"\n\n\nregister_schema(db_name=\"etf_meta\", schema_base=EtfMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Etf\", \"EtfStock\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/fund_meta.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, Integer\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Portfolio, PortfolioStockHistory\nfrom zvt.contract.register import register_entity, register_schema\nfrom zvt.utils.time_utils import now_pd_timestamp\n\nFundMetaBase = declarative_base()\n\n\n#: 个股\n@register_entity(entity_type=\"fund\")\nclass Fund(FundMetaBase, Portfolio):\n    __tablename__ = \"fund\"\n    #: 基金管理人\n    advisor = Column(String(length=100))\n    #: 基金托管人\n    trustee = Column(String(length=100))\n\n    #: 编码\t基金运作方式\n    #: 401001\t开放式基金\n    #: 401002\t封闭式基金\n    #: 401003\tQDII\n    #: 401004\tFOF\n    #: 401005\tETF\n    #: 401006\tLOF\n    #: 基金运作方式编码\n    operate_mode_id = Column(Integer)\n    #: 基金运作方式\n    operate_mode = Column(String(length=32))\n\n    #: 编码\t基金类别\n    #: 402001\t股票型\n    #: 402002\t货币型\n    #: 402003\t债券型\n    #: 402004\t混合型\n    #: 402005\t基金型\n    #: 402006\t贵金属\n    #: 402007\t封闭式\n    #: 投资标的类型编码\n    underlying_asset_type_id = Column(Integer)\n    #: 投资标的类型\n    underlying_asset_type = Column(String(length=32))\n\n    @classmethod\n    def get_stocks(cls, code=None, codes=None, ids=None, timestamp=now_pd_timestamp(), provider=None):\n        from zvt.api.portfolio import get_fund_stocks\n\n        return get_fund_stocks(code=code, codes=codes, ids=ids, timestamp=timestamp, provider=provider)\n\n\nclass FundStock(FundMetaBase, PortfolioStockHistory):\n    __tablename__ = \"fund_stock\"\n\n\nregister_schema(db_name=\"fund_meta\", schema_base=FundMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Fund\", \"FundStock\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/future_meta.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema, register_entity\nfrom zvt.contract.schema import TradableEntity\n\nFutureMetaBase = declarative_base()\n\n\n@register_entity(entity_type=\"future\")\nclass Future(FutureMetaBase, TradableEntity):\n    __tablename__ = \"future\"\n\n\nregister_schema(db_name=\"future_meta\", schema_base=FutureMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Future\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/index_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy import Column, String, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Portfolio, PortfolioStockHistory\nfrom zvt.contract.register import register_schema, register_entity\n\nIndexMetaBase = declarative_base()\n\n\n#: 指数\n@register_entity(entity_type=\"index\")\nclass Index(IndexMetaBase, Portfolio):\n    __tablename__ = \"index\"\n\n    #: 发布商\n    publisher = Column(String(length=64))\n    #: 类别\n    #: see IndexCategory\n    category = Column(String(length=64))\n    #: 基准点数\n    base_point = Column(Float)\n\n\nclass IndexStock(IndexMetaBase, PortfolioStockHistory):\n    __tablename__ = \"index_stock\"\n\n\nregister_schema(db_name=\"index_meta\", schema_base=IndexMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Index\", \"IndexStock\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/indexhk_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy import Column, String, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Portfolio\nfrom zvt.contract.register import register_schema, register_entity\n\nIndexhkMetaBase = declarative_base()\n\n\n#: 港股指数\n@register_entity(entity_type=\"indexhk\")\nclass Indexhk(IndexhkMetaBase, Portfolio):\n    __tablename__ = \"indexhk\"\n\n    #: 发布商\n    publisher = Column(String(length=64))\n    #: 类别\n    #: see IndexCategory\n    category = Column(String(length=64))\n    #: 基准点数\n    base_point = Column(Float)\n\n\nregister_schema(db_name=\"indexhk_meta\", schema_base=IndexhkMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Indexhk\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/indexus_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy import Column, String, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Portfolio\nfrom zvt.contract.register import register_schema, register_entity\n\nIndexusMetaBase = declarative_base()\n\n\n#: 美股指数\n@register_entity(entity_type=\"indexus\")\nclass Indexus(IndexusMetaBase, Portfolio):\n    __tablename__ = \"indexus\"\n\n    #: 发布商\n    publisher = Column(String(length=64))\n    #: 类别\n    #: see IndexCategory\n    category = Column(String(length=64))\n    #: 基准点数\n    base_point = Column(Float)\n\n\nregister_schema(db_name=\"indexus_meta\", schema_base=IndexusMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Indexus\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/stock_meta.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy import Column, String, DateTime, BigInteger, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import TradableEntity\nfrom zvt.contract.register import register_schema, register_entity\n\nStockMetaBase = declarative_base()\n\n\n#: 个股\n@register_entity(entity_type=\"stock\")\nclass Stock(StockMetaBase, TradableEntity):\n    __tablename__ = \"stock\"\n    #: 流通市值\n    float_cap = Column(Float)\n    #: 总市值\n    total_cap = Column(Float)\n\n    #: 股东上次更新时间\n    holder_modified_date = Column(DateTime)\n    #: 控股股东\n    controlling_holder = Column(String)\n    #: 实际控制人\n    controlling_holder_parent = Column(String)\n    #: 前十大股东占比\n    top_ten_ratio = Column(Float)\n\n\n#: 个股详情\nclass StockDetail(StockMetaBase, TradableEntity):\n    __tablename__ = \"stock_detail\"\n\n    #: 所属行业\n    industries = Column(String)\n    #: 行业指数\n    industry_indices = Column(String)\n    #: 所属板块\n    concept_indices = Column(String)\n    #: 所属区域\n    area_indices = Column(String)\n\n    #: 成立日期\n    date_of_establishment = Column(DateTime)\n    #: 公司简介\n    profile = Column(String(length=1024))\n    #: 主营业务\n    main_business = Column(String(length=512))\n    #: 发行量(股)\n    issues = Column(BigInteger)\n    #: 发行价格\n    price = Column(Float)\n    #: 募资净额(元)\n    raising_fund = Column(Float)\n    #: 发行市盈率\n    issue_pe = Column(Float)\n    #: 网上中签率\n    net_winning_rate = Column(Float)\n\n\nregister_schema(\n    db_name=\"stock_meta\", schema_base=StockMetaBase\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock\", \"StockDetail\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/stockhk_meta.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, Boolean, Float, String\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import TradableEntity\nfrom zvt.contract.register import register_schema, register_entity\n\nStockhkMetaBase = declarative_base()\n\n\n#: 港股\n@register_entity(entity_type=\"stockhk\")\nclass Stockhk(StockhkMetaBase, TradableEntity):\n    __tablename__ = \"stockhk\"\n    #: 是否属于港股通\n    south = Column(Boolean)\n    #: 流通市值\n    float_cap = Column(Float)\n    #: 总市值\n    total_cap = Column(Float)\n    #: 所属行业\n    industry = Column(String)\n\n    @classmethod\n    def get_trading_t(cls):\n        \"\"\"\n        0 means t+0\n        1 means t+1\n\n        :return:\n        \"\"\"\n        return 0\n\n    @classmethod\n    def get_trading_intervals(cls, include_bidding_time=False):\n        \"\"\"\n        overwrite it to get the trading intervals of the entity\n\n        :return: list of time intervals, in format [(start,end)]\n        \"\"\"\n        if include_bidding_time:\n            return [(\"09:15\", \"12:00\"), (\"13:00\", \"16:00\")]\n        else:\n            return [(\"09:30\", \"12:00\"), (\"13:00\", \"16:00\")]\n\n\nregister_schema(db_name=\"stockhk_meta\", schema_base=StockhkMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Stockhk\"]\n"
  },
  {
    "path": "src/zvt/domain/meta/stockus_meta.py",
    "content": "# -*- coding: utf-8 -*-\nimport pytz\nfrom sqlalchemy import Column, Float, String\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import TradableEntity\nfrom zvt.contract.register import register_schema, register_entity\n\nStockusMetaBase = declarative_base()\n\n\n#: 美股\n@register_entity(entity_type=\"stockus\")\nclass Stockus(StockusMetaBase, TradableEntity):\n    __tablename__ = \"stockus\"\n\n    #: 流通市值\n    float_cap = Column(Float)\n    #: 总市值\n    total_cap = Column(Float)\n    #: 所属行业\n    industry = Column(String)\n\n    @classmethod\n    def get_timezone(cls):\n        return pytz.timezone(\"America/New_York\")\n\n    @classmethod\n    def get_trading_t(cls):\n        \"\"\"\n        0 means t+0\n        1 means t+1\n\n        :return:\n        \"\"\"\n        return 0\n\n    @classmethod\n    def get_trading_intervals(cls, include_bidding_time=False):\n        \"\"\"\n        overwrite it to get the trading intervals of the entity\n\n        :return: list of time intervals, in format [(start,end)]\n        \"\"\"\n        return [(\"09:30\", \"16:00\")]\n\n\nregister_schema(db_name=\"stockus_meta\", schema_base=StockusMetaBase)\n\n\n# the __all__ is generated\n__all__ = [\"Stockus\"]\n"
  },
  {
    "path": "src/zvt/domain/misc/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule overall\nfrom .overall import *\nfrom .overall import __all__ as _overall_all\n\n__all__ += _overall_all\n\n# import all from submodule money_flow\nfrom .money_flow import *\nfrom .money_flow import __all__ as _money_flow_all\n\n__all__ += _money_flow_all\n\n# import all from submodule holder\nfrom .holder import *\nfrom .holder import __all__ as _holder_all\n\n__all__ += _holder_all\n\n# import all from submodule stock_news\nfrom .stock_news import *\nfrom .stock_news import __all__ as _stock_news_all\n\n__all__ += _stock_news_all\n"
  },
  {
    "path": "src/zvt/domain/misc/holder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, DateTime, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nHolderBase = declarative_base()\n\n\nclass HkHolder(HolderBase, Mixin):\n    __tablename__ = \"hk_holder\"\n    #: 股票代码\n    code = Column(String(length=32))\n    #: 股票名称\n    name = Column(String(length=32))\n\n    #: 市场通编码\t三种类型：310001-沪股通，310002-深股通，310005-港股通\n    holder_code = Column(String(length=32))\n    #: 市场通名称\t三种类型：沪股通，深股通，港股通\n    holder_name = Column(String(length=32))\n\n    #: 持股数量\n    share_number = Column(Float)\n    #: 持股比例\n    share_ratio = Column(Float)\n\n\nclass TopTenTradableHolder(HolderBase, Mixin):\n    __tablename__ = \"top_ten_tradable_holder\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n\n    #: 股东代码\n    holder_code = Column(String(length=32))\n    #: 股东名称\n    holder_name = Column(String(length=32))\n    #: 持股数\n    shareholding_numbers = Column(Float)\n    #: 持股比例\n    shareholding_ratio = Column(Float)\n    #: 变动\n    change = Column(Float)\n    #: 变动比例\n    change_ratio = Column(Float)\n\n\nclass TopTenHolder(HolderBase, Mixin):\n    __tablename__ = \"top_ten_holder\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n\n    #: 股东代码\n    holder_code = Column(String(length=32))\n    #: 股东名称\n    holder_name = Column(String(length=32))\n    #: 持股数\n    shareholding_numbers = Column(Float)\n    #: 持股比例\n    shareholding_ratio = Column(Float)\n    #: 变动\n    change = Column(Float)\n    #: 变动比例\n    change_ratio = Column(Float)\n\n\nclass InstitutionalInvestorHolder(HolderBase, Mixin):\n    __tablename__ = \"institutional_investor_holder\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n\n    report_period = Column(String(length=32))\n    report_date = Column(DateTime)\n\n    #: 机构类型\n    institutional_investor_type = Column(String(length=64))\n    #: 股东代码\n    holder_code = Column(String(length=32))\n    #: 股东名称\n    holder_name = Column(String(length=32))\n    #: 持股数\n    shareholding_numbers = Column(Float)\n    #: 持股比例\n    shareholding_ratio = Column(Float)\n\n\nregister_schema(db_name=\"holder\", schema_base=HolderBase, entity_type=\"stock\")\n\n\n# the __all__ is generated\n__all__ = [\"HkHolder\", \"TopTenTradableHolder\", \"TopTenHolder\", \"InstitutionalInvestorHolder\"]\n"
  },
  {
    "path": "src/zvt/domain/misc/money_flow.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nMoneyFlowBase = declarative_base()\n\n\n#: 板块资金流向\n\n\nclass BlockMoneyFlow(MoneyFlowBase, Mixin):\n    __tablename__ = \"block_money_flow\"\n\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    #: 收盘价\n    close = Column(Float)\n    change_pct = Column(Float)\n    turnover_rate = Column(Float)\n\n    #: 净流入\n    net_inflows = Column(Float)\n    #: 净流入率\n    net_inflow_rate = Column(Float)\n\n    #: 主力=超大单+大单\n    net_main_inflows = Column(Float)\n    net_main_inflow_rate = Column(Float)\n    #: 超大单\n    net_huge_inflows = Column(Float)\n    net_huge_inflow_rate = Column(Float)\n    #: 大单\n    net_big_inflows = Column(Float)\n    net_big_inflow_rate = Column(Float)\n\n    #: 中单\n    net_medium_inflows = Column(Float)\n    net_medium_inflow_rate = Column(Float)\n    #: 小单\n    net_small_inflows = Column(Float)\n    net_small_inflow_rate = Column(Float)\n\n\nclass StockMoneyFlow(MoneyFlowBase, Mixin):\n    __tablename__ = \"stock_money_flow\"\n\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    #: 收盘价\n    close = Column(Float)\n    change_pct = Column(Float)\n    turnover_rate = Column(Float)\n\n    #: 净流入\n    net_inflows = Column(Float)\n    #: 净流入率\n    net_inflow_rate = Column(Float)\n\n    #: 主力=超大单+大单\n    net_main_inflows = Column(Float)\n    net_main_inflow_rate = Column(Float)\n    #: 超大单\n    net_huge_inflows = Column(Float)\n    net_huge_inflow_rate = Column(Float)\n    #: 大单\n    net_big_inflows = Column(Float)\n    net_big_inflow_rate = Column(Float)\n\n    #: 中单\n    net_medium_inflows = Column(Float)\n    net_medium_inflow_rate = Column(Float)\n    #: 小单\n    net_small_inflows = Column(Float)\n    net_small_inflow_rate = Column(Float)\n\n\nclass IndexMoneyFlow(MoneyFlowBase, Mixin):\n    __tablename__ = \"index_money_flow\"\n\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    #: 净流入\n    net_inflows = Column(Float)\n    #: 净流入率\n    net_inflow_rate = Column(Float)\n\n    #: 主力=超大单+大单\n    net_main_inflows = Column(Float)\n    net_main_inflow_rate = Column(Float)\n    #: 超大单\n    net_huge_inflows = Column(Float)\n    net_huge_inflow_rate = Column(Float)\n    #: 大单\n    net_big_inflows = Column(Float)\n    net_big_inflow_rate = Column(Float)\n\n    #: 中单\n    net_medium_inflows = Column(Float)\n    net_medium_inflow_rate = Column(Float)\n    #: 小单\n    net_small_inflows = Column(Float)\n    net_small_inflow_rate = Column(Float)\n\n\nregister_schema(db_name=\"money_flow\", schema_base=MoneyFlowBase, entity_type=\"stock\")\n\n\n# the __all__ is generated\n__all__ = [\"BlockMoneyFlow\", \"StockMoneyFlow\", \"IndexMoneyFlow\"]\n"
  },
  {
    "path": "src/zvt/domain/misc/overall.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, Float\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nOverallBase = declarative_base()\n\n\n#: 市场整体估值\n\n\nclass StockSummary(OverallBase, Mixin):\n    __tablename__ = \"stock_summary\"\n\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    total_value = Column(Float)\n    total_tradable_vaule = Column(Float)\n    pe = Column(Float)\n    pb = Column(Float)\n    volume = Column(Float)\n    turnover = Column(Float)\n    turnover_rate = Column(Float)\n\n\n#: 融资融券概况\n\n\nclass MarginTradingSummary(OverallBase, Mixin):\n    __tablename__ = \"margin_trading_summary\"\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    #: 融资余额\n    margin_value = Column(Float)\n    #: 买入额\n    margin_buy = Column(Float)\n\n    #: 融券余额\n    short_value = Column(Float)\n    #: 卖出量\n    short_volume = Column(Float)\n\n    #: 融资融券余额\n    total_value = Column(Float)\n\n\n#: 北向/南向成交概况\n\n\nclass CrossMarketSummary(OverallBase, Mixin):\n    __tablename__ = \"cross_market_summary\"\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    buy_amount = Column(Float)\n    buy_volume = Column(Float)\n    sell_amount = Column(Float)\n    sell_volume = Column(Float)\n    quota_daily = Column(Float)\n    quota_daily_balance = Column(Float)\n\n\nregister_schema(db_name=\"overall\", schema_base=OverallBase, entity_type=\"stock\")\n\n\n# the __all__ is generated\n__all__ = [\"StockSummary\", \"MarginTradingSummary\", \"CrossMarketSummary\"]\n"
  },
  {
    "path": "src/zvt/domain/misc/stock_news.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, JSON, Boolean, DateTime, Integer\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nNewsBase = declarative_base()\n\n\nclass StockNews(NewsBase, Mixin):\n    __tablename__ = \"stock_news\"\n\n    #: 新闻编号\n    news_code = Column(String)\n    #: 新闻地址\n    news_url = Column(String)\n    #: 新闻标题\n    news_title = Column(String)\n    #: 新闻内容\n    news_content = Column(String)\n    #: 新闻解读\n    news_analysis = Column(JSON)\n    #: 用户设置为忽略\n    ignore_by_user = Column(Boolean, default=False)\n\n\nclass StockHotTopic(NewsBase, Mixin):\n    __tablename__ = \"stock_hot_topic\"\n\n    #: 出现时间\n    created_timestamp = Column(DateTime)\n    #: 热度排行\n    position = Column(Integer)\n    #: 相关标的\n    entity_ids = Column(JSON)\n\n    #: 新闻编号\n    news_code = Column(String)\n    #: 新闻标题\n    news_title = Column(String)\n    #: 新闻内容\n    news_content = Column(String)\n    #: 新闻解读\n    news_analysis = Column(JSON)\n\n\nregister_schema(db_name=\"stock_news\", schema_base=NewsBase, entity_type=\"stock\")\n\n\n# the __all__ is generated\n__all__ = [\"StockNews\", \"StockHotTopic\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/__init__.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import String, Column, Float, Integer, JSON, Boolean\n\nfrom zvt.contract import Mixin\n\n\nclass KdataCommon(Mixin):\n    provider = Column(String(length=32))\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n    # Enum constraint is not extendable\n    # level = Column(Enum(IntervalLevel, values_callable=enum_value))\n    level = Column(String(length=32))\n\n    # 开盘价\n    open = Column(Float)\n    # 收盘价\n    close = Column(Float)\n    # 最高价\n    high = Column(Float)\n    # 最低价\n    low = Column(Float)\n    # 成交量\n    volume = Column(Float)\n    # 成交金额\n    turnover = Column(Float)\n    # 涨跌幅\n    change_pct = Column(Float)\n    # 换手率\n    turnover_rate = Column(Float)\n\n\nclass TickCommon(Mixin):\n    #: UNIX时间戳\n    time = Column(Integer)\n    #: 开盘价\n    open = Column(Float)\n    #: 收盘价/当前价格\n    close = Column(Float)\n    #: 最高价\n    high = Column(Float)\n    #: 最低价\n    low = Column(Float)\n    #: 成交量\n    volume = Column(Float)\n    #: 成交金额\n    turnover = Column(Float)\n    #: 委卖价\n    ask_price = Column(Float)\n    #: 委买价\n    bid_price = Column(Float)\n    #: 委卖量\n    ask_vol = Column(JSON)\n    #: 委买量\n    bid_vol = Column(JSON)\n    #: 成交笔数\n    transaction_num = Column(Integer)\n\n\nclass BlockKdataCommon(KdataCommon):\n    pass\n\n\nclass IndexKdataCommon(KdataCommon):\n    pass\n\n\nclass IndexhkKdataCommon(KdataCommon):\n    pass\n\n\nclass IndexusKdataCommon(KdataCommon):\n    pass\n\n\nclass EtfKdataCommon(KdataCommon):\n    turnover_rate = Column(Float)\n\n    # ETF 累计净值（货币 ETF 为七日年化)\n    cumulative_net_value = Column(Float)\n\n\nclass StockKdataCommon(KdataCommon):\n    #: 是否涨停\n    is_limit_up = Column(Boolean)\n    #: 是否跌停\n    is_limit_down = Column(Boolean)\n\n\nclass StockusKdataCommon(KdataCommon):\n    #: 是否涨停\n    is_limit_up = Column(Boolean)\n    #: 是否跌停\n    is_limit_down = Column(Boolean)\n\n\nclass StockhkKdataCommon(KdataCommon):\n    pass\n\n\n# future common kdata\nclass FutureKdataCommon(KdataCommon):\n    #: 持仓量\n    interest = Column(Float)\n    #: 结算价\n    settlement = Column(Float)\n    #: 涨跌幅(按收盘价)\n    # change_pct = Column(Float)\n    #: 涨跌幅(按结算价)\n    change_pct1 = Column(Float)\n\n\nclass CurrencyKdataCommon(KdataCommon):\n    #: 持仓量\n    interest = Column(Float)\n    #: 结算价\n    settlement = Column(Float)\n    #: 涨跌幅(按收盘价)\n    # change_pct = Column(Float)\n    #: 涨跌幅(按结算价)\n    change_pct1 = Column(Float)\n\n\n# the __all__ is generated\n__all__ = [\n    \"KdataCommon\",\n    \"TickCommon\",\n    \"BlockKdataCommon\",\n    \"IndexKdataCommon\",\n    \"IndexhkKdataCommon\",\n    \"IndexusKdataCommon\",\n    \"EtfKdataCommon\",\n    \"StockKdataCommon\",\n    \"StockusKdataCommon\",\n    \"StockhkKdataCommon\",\n    \"FutureKdataCommon\",\n    \"CurrencyKdataCommon\",\n]\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule indexhk\nfrom .indexhk import *\nfrom .indexhk import __all__ as _indexhk_all\n\n__all__ += _indexhk_all\n\n# import all from submodule trade_day\nfrom .trade_day import *\nfrom .trade_day import __all__ as _trade_day_all\n\n__all__ += _trade_day_all\n\n# import all from submodule indexus\nfrom .indexus import *\nfrom .indexus import __all__ as _indexus_all\n\n__all__ += _indexus_all\n\n# import all from submodule stockhk\nfrom .stockhk import *\nfrom .stockhk import __all__ as _stockhk_all\n\n__all__ += _stockhk_all\n\n# import all from submodule stockus\nfrom .stockus import *\nfrom .stockus import __all__ as _stockus_all\n\n__all__ += _stockus_all\n\n# import all from submodule index\nfrom .index import *\nfrom .index import __all__ as _index_all\n\n__all__ += _index_all\n\n# import all from submodule etf\nfrom .etf import *\nfrom .etf import __all__ as _etf_all\n\n__all__ += _etf_all\n\n# import all from submodule stock\nfrom .stock import *\nfrom .stock import __all__ as _stock_all\n\n__all__ += _stock_all\n\n# import all from submodule currency\nfrom .currency import *\nfrom .currency import __all__ as _currency_all\n\n__all__ += _currency_all\n\n# import all from submodule future\nfrom .future import *\nfrom .future import __all__ as _future_all\n\n__all__ += _future_all\n\n# import all from submodule block\nfrom .block import *\nfrom .block import __all__ as _block_all\n\n__all__ += _block_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/block/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule block_1d_kdata\nfrom .block_1d_kdata import *\nfrom .block_1d_kdata import __all__ as _block_1d_kdata_all\n\n__all__ += _block_1d_kdata_all\n\n# import all from submodule block_1wk_kdata\nfrom .block_1wk_kdata import *\nfrom .block_1wk_kdata import __all__ as _block_1wk_kdata_all\n\n__all__ += _block_1wk_kdata_all\n\n# import all from submodule block_1mon_kdata\nfrom .block_1mon_kdata import *\nfrom .block_1mon_kdata import __all__ as _block_1mon_kdata_all\n\n__all__ += _block_1mon_kdata_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/block/block_1d_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import BlockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Block1dKdata(KdataBase, BlockKdataCommon):\n    __tablename__ = \"block_1d_kdata\"\n\n\nregister_schema(db_name=\"block_1d_kdata\", schema_base=KdataBase, entity_type=\"block\")\n\n\n# the __all__ is generated\n__all__ = [\"Block1dKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/block/block_1mon_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import BlockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Block1monKdata(KdataBase, BlockKdataCommon):\n    __tablename__ = \"block_1mon_kdata\"\n\n\nregister_schema(db_name=\"block_1mon_kdata\", schema_base=KdataBase, entity_type=\"block\")\n\n\n# the __all__ is generated\n__all__ = [\"Block1monKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/block/block_1wk_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import BlockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Block1wkKdata(KdataBase, BlockKdataCommon):\n    __tablename__ = \"block_1wk_kdata\"\n\n\nregister_schema(db_name=\"block_1wk_kdata\", schema_base=KdataBase, entity_type=\"block\")\n\n\n# the __all__ is generated\n__all__ = [\"Block1wkKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/currency/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule currency_1d_kdata\nfrom .currency_1d_kdata import *\nfrom .currency_1d_kdata import __all__ as _currency_1d_kdata_all\n\n__all__ += _currency_1d_kdata_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/currency/currency_1d_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import CurrencyKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Currency1dKdata(KdataBase, CurrencyKdataCommon):\n    __tablename__ = \"currency_1d_kdata\"\n\n\nregister_schema(db_name=\"currency_1d_kdata\", schema_base=KdataBase, entity_type=\"currency\")\n\n\n# the __all__ is generated\n__all__ = [\"Currency1dKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/etf/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule etf_1d_kdata\nfrom .etf_1d_kdata import *\nfrom .etf_1d_kdata import __all__ as _etf_1d_kdata_all\n\n__all__ += _etf_1d_kdata_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/etf/etf_1d_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import EtfKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Etf1dKdata(KdataBase, EtfKdataCommon):\n    __tablename__ = \"etf_1d_kdata\"\n\n\nregister_schema(db_name=\"etf_1d_kdata\", schema_base=KdataBase, entity_type=\"etf\")\n\n\n# the __all__ is generated\n__all__ = [\"Etf1dKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/future/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule future_1d_kdata\nfrom .future_1d_kdata import *\nfrom .future_1d_kdata import __all__ as _future_1d_kdata_all\n\n__all__ += _future_1d_kdata_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/future/future_1d_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import FutureKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Future1dKdata(KdataBase, FutureKdataCommon):\n    __tablename__ = \"future_1d_kdata\"\n\n\nregister_schema(db_name=\"future_1d_kdata\", schema_base=KdataBase, entity_type=\"future\")\n\n\n# the __all__ is generated\n__all__ = [\"Future1dKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/index/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule index_1d_kdata\nfrom .index_1d_kdata import *\nfrom .index_1d_kdata import __all__ as _index_1d_kdata_all\n\n__all__ += _index_1d_kdata_all\n\n# import all from submodule index_1m_kdata\nfrom .index_1m_kdata import *\nfrom .index_1m_kdata import __all__ as _index_1m_kdata_all\n\n__all__ += _index_1m_kdata_all\n\n# import all from submodule index_1wk_kdata\nfrom .index_1wk_kdata import *\nfrom .index_1wk_kdata import __all__ as _index_1wk_kdata_all\n\n__all__ += _index_1wk_kdata_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/index/index_1d_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import IndexKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Index1dKdata(KdataBase, IndexKdataCommon):\n    __tablename__ = \"index_1d_kdata\"\n\n\nregister_schema(db_name=\"index_1d_kdata\", schema_base=KdataBase, entity_type=\"index\")\n\n\n# the __all__ is generated\n__all__ = [\"Index1dKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/index/index_1m_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import TradableEntity\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import IndexKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Index1mKdata(KdataBase, IndexKdataCommon, TradableEntity):\n    __tablename__ = \"index_1m_kdata\"\n\n\nregister_schema(db_name=\"index_1m_kdata\", schema_base=KdataBase, entity_type=\"index\")\n\n\n# the __all__ is generated\n__all__ = [\"Index1mKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/index/index_1wk_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import IndexKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Index1wkKdata(KdataBase, IndexKdataCommon):\n    __tablename__ = \"index_1wk_kdata\"\n\n\nregister_schema(db_name=\"index_1wk_kdata\", schema_base=KdataBase, entity_type=\"index\")\n\n\n# the __all__ is generated\n__all__ = [\"Index1wkKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/indexhk/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule indexhk_1d_kdata\nfrom .indexhk_1d_kdata import *\nfrom .indexhk_1d_kdata import __all__ as _indexhk_1d_kdata_all\n\n__all__ += _indexhk_1d_kdata_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/indexhk/indexhk_1d_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import IndexhkKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Indexhk1dKdata(KdataBase, IndexhkKdataCommon):\n    __tablename__ = \"indexhk_1d_kdata\"\n\n\nregister_schema(db_name=\"indexhk_1d_kdata\", schema_base=KdataBase, entity_type=\"indexhk\")\n\n\n# the __all__ is generated\n__all__ = [\"Indexhk1dKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/indexus/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule indexus_1d_kdata\nfrom .indexus_1d_kdata import *\nfrom .indexus_1d_kdata import __all__ as _indexus_1d_kdata_all\n\n__all__ += _indexus_1d_kdata_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/indexus/indexus_1d_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import IndexusKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Indexus1dKdata(KdataBase, IndexusKdataCommon):\n    __tablename__ = \"indexus_1d_kdata\"\n\n\nregister_schema(db_name=\"indexus_1d_kdata\", schema_base=KdataBase, entity_type=\"indexus\")\n\n\n# the __all__ is generated\n__all__ = [\"Indexus1dKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule stock_1h_kdata\nfrom .stock_1h_kdata import *\nfrom .stock_1h_kdata import __all__ as _stock_1h_kdata_all\n\n__all__ += _stock_1h_kdata_all\n\n# import all from submodule stock_15m_hfq_kdata\nfrom .stock_15m_hfq_kdata import *\nfrom .stock_15m_hfq_kdata import __all__ as _stock_15m_hfq_kdata_all\n\n__all__ += _stock_15m_hfq_kdata_all\n\n# import all from submodule stock_1wk_kdata\nfrom .stock_1wk_kdata import *\nfrom .stock_1wk_kdata import __all__ as _stock_1wk_kdata_all\n\n__all__ += _stock_1wk_kdata_all\n\n# import all from submodule stock_15m_kdata\nfrom .stock_15m_kdata import *\nfrom .stock_15m_kdata import __all__ as _stock_15m_kdata_all\n\n__all__ += _stock_15m_kdata_all\n\n# import all from submodule stock_1m_hfq_kdata\nfrom .stock_1m_hfq_kdata import *\nfrom .stock_1m_hfq_kdata import __all__ as _stock_1m_hfq_kdata_all\n\n__all__ += _stock_1m_hfq_kdata_all\n\n# import all from submodule stock_4h_hfq_kdata\nfrom .stock_4h_hfq_kdata import *\nfrom .stock_4h_hfq_kdata import __all__ as _stock_4h_hfq_kdata_all\n\n__all__ += _stock_4h_hfq_kdata_all\n\n# import all from submodule stock_5m_hfq_kdata\nfrom .stock_5m_hfq_kdata import *\nfrom .stock_5m_hfq_kdata import __all__ as _stock_5m_hfq_kdata_all\n\n__all__ += _stock_5m_hfq_kdata_all\n\n# import all from submodule stock_5m_kdata\nfrom .stock_5m_kdata import *\nfrom .stock_5m_kdata import __all__ as _stock_5m_kdata_all\n\n__all__ += _stock_5m_kdata_all\n\n# import all from submodule stock_1d_kdata\nfrom .stock_1d_kdata import *\nfrom .stock_1d_kdata import __all__ as _stock_1d_kdata_all\n\n__all__ += _stock_1d_kdata_all\n\n# import all from submodule stock_30m_hfq_kdata\nfrom .stock_30m_hfq_kdata import *\nfrom .stock_30m_hfq_kdata import __all__ as _stock_30m_hfq_kdata_all\n\n__all__ += _stock_30m_hfq_kdata_all\n\n# import all from submodule stock_1mon_hfq_kdata\nfrom .stock_1mon_hfq_kdata import *\nfrom .stock_1mon_hfq_kdata import __all__ as _stock_1mon_hfq_kdata_all\n\n__all__ += _stock_1mon_hfq_kdata_all\n\n# import all from submodule stock_1wk_hfq_kdata\nfrom .stock_1wk_hfq_kdata import *\nfrom .stock_1wk_hfq_kdata import __all__ as _stock_1wk_hfq_kdata_all\n\n__all__ += _stock_1wk_hfq_kdata_all\n\n# import all from submodule stock_1mon_kdata\nfrom .stock_1mon_kdata import *\nfrom .stock_1mon_kdata import __all__ as _stock_1mon_kdata_all\n\n__all__ += _stock_1mon_kdata_all\n\n# import all from submodule stock_1h_hfq_kdata\nfrom .stock_1h_hfq_kdata import *\nfrom .stock_1h_hfq_kdata import __all__ as _stock_1h_hfq_kdata_all\n\n__all__ += _stock_1h_hfq_kdata_all\n\n# import all from submodule stock_1m_kdata\nfrom .stock_1m_kdata import *\nfrom .stock_1m_kdata import __all__ as _stock_1m_kdata_all\n\n__all__ += _stock_1m_kdata_all\n\n# import all from submodule stock_4h_kdata\nfrom .stock_4h_kdata import *\nfrom .stock_4h_kdata import __all__ as _stock_4h_kdata_all\n\n__all__ += _stock_4h_kdata_all\n\n# import all from submodule stock_1d_hfq_kdata\nfrom .stock_1d_hfq_kdata import *\nfrom .stock_1d_hfq_kdata import __all__ as _stock_1d_hfq_kdata_all\n\n__all__ += _stock_1d_hfq_kdata_all\n\n# import all from submodule stock_quote\nfrom .stock_quote import *\nfrom .stock_quote import __all__ as _stock_quote_all\n\n__all__ += _stock_quote_all\n\n# import all from submodule stock_30m_kdata\nfrom .stock_30m_kdata import *\nfrom .stock_30m_kdata import __all__ as _stock_30m_kdata_all\n\n__all__ += _stock_30m_kdata_all\n\n# import all from submodule stock_quote_log\nfrom .stock_quote_log import *\nfrom .stock_quote_log import __all__ as _stock_quote_log_all\n\n__all__ += _stock_quote_log_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_15m_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock15mHfqKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_15m_hfq_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_15m_hfq_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock15mHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_15m_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock15mKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_15m_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_15m_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock15mKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_1d_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock1dHfqKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_1d_hfq_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_1d_hfq_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1dHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_1d_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock1dKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_1d_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_1d_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1dKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_1h_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock1hHfqKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_1h_hfq_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_1h_hfq_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1hHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_1h_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock1hKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_1h_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_1h_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1hKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_1m_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock1mHfqKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_1m_hfq_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_1m_hfq_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1mHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_1m_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock1mKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_1m_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_1m_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1mKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_1mon_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock1monHfqKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_1mon_hfq_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_1mon_hfq_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1monHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_1mon_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock1monKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_1mon_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_1mon_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1monKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_1wk_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock1wkHfqKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_1wk_hfq_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_1wk_hfq_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1wkHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_1wk_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock1wkKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_1wk_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_1wk_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1wkKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_30m_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock30mHfqKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_30m_hfq_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_30m_hfq_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock30mHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_30m_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock30mKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_30m_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_30m_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock30mKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_4h_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock4hHfqKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_4h_hfq_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_4h_hfq_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock4hHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_4h_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock4hKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_4h_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_4h_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock4hKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_5m_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock5mHfqKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_5m_hfq_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_5m_hfq_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock5mHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_5m_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stock5mKdata(KdataBase, StockKdataCommon):\n    __tablename__ = \"stock_5m_kdata\"\n\n\nregister_schema(\n    db_name=\"stock_5m_kdata\", schema_base=KdataBase, entity_type=\"stock\"\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock5mKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_quote.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import String, Column, Float, Integer, Boolean\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nStockQuoteBase = declarative_base()\n\n\nclass StockQuote(StockQuoteBase, StockKdataCommon):\n    __tablename__ = \"stock_quote\"\n    #: UNIX时间戳\n    time = Column(Integer)\n    #: 最新价\n    price = Column(Float)\n    #: 封涨停金额\n    # limit_up_amount = Column(Float)\n    #: 封跌停金额\n    # limit_down_amount = Column(Float)\n    #: 5挡卖单金额\n    # ask_amount = Column(Float)\n    #: 5挡买单金额\n    # bid_amount = Column(Float)\n    #: 流通市值\n    float_cap = Column(Float)\n    #: 总市值\n    total_cap = Column(Float)\n\n\nclass Stock1mQuote(StockQuoteBase, Mixin):\n    __tablename__ = \"stock_1m_quote\"\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    #: UNIX时间戳\n    time = Column(Integer)\n    #: 最新价\n    price = Column(Float)\n    #: 均价\n    avg_price = Column(Float)\n    # 涨跌幅\n    change_pct = Column(Float)\n    # 成交量\n    volume = Column(Float)\n    # 成交金额\n    turnover = Column(Float)\n    # 换手率\n    turnover_rate = Column(Float)\n    #: 是否涨停\n    is_limit_up = Column(Boolean)\n    #: 冲涨停\n    near_limit_up = Column(Boolean)\n    #: 是否跌停\n    is_limit_down = Column(Boolean)\n\n\nregister_schema(db_name=\"stock_quote\", schema_base=StockQuoteBase, entity_type=\"stock\")\n\n\n# the __all__ is generated\n__all__ = [\"StockQuote\", \"Stock1mQuote\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stock/stock_quote_log.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, Float, Integer, Boolean\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockKdataCommon\n\nStockQuoteLogBase = declarative_base()\n\n\nclass StockQuoteLog(StockQuoteLogBase, StockKdataCommon):\n    __tablename__ = \"stock_quote_log\"\n    #: UNIX时间戳\n    time = Column(Integer)\n    #: 最新价\n    price = Column(Float)\n    #: 冲涨停\n    near_limit_up = Column(Boolean)\n    #: 封涨停金额\n    # limit_up_amount = Column(Float)\n    #: 封跌停金额\n    # limit_down_amount = Column(Float)\n    #: 5挡卖单金额\n    # ask_amount = Column(Float)\n    #: 5挡买单金额\n    # bid_amount = Column(Float)\n    #: 流通市值\n    float_cap = Column(Float)\n    #: 总市值\n    total_cap = Column(Float)\n\n\nregister_schema(db_name=\"stock_quote_log\", schema_base=StockQuoteLogBase, entity_type=\"stock\")\n\n\n# the __all__ is generated\n__all__ = [\"StockQuoteLog\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stockhk/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule stockhk_1d_kdata\nfrom .stockhk_1d_kdata import *\nfrom .stockhk_1d_kdata import __all__ as _stockhk_1d_kdata_all\n\n__all__ += _stockhk_1d_kdata_all\n\n# import all from submodule stockhk_1d_hfq_kdata\nfrom .stockhk_1d_hfq_kdata import *\nfrom .stockhk_1d_hfq_kdata import __all__ as _stockhk_1d_hfq_kdata_all\n\n__all__ += _stockhk_1d_hfq_kdata_all\n\n# import all from submodule stockhk_quote\nfrom .stockhk_quote import *\nfrom .stockhk_quote import __all__ as _stockhk_quote_all\n\n__all__ += _stockhk_quote_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/stockhk/stockhk_1d_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockhkKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stockhk1dHfqKdata(KdataBase, StockhkKdataCommon):\n    __tablename__ = \"stockhk_1d_hfq_kdata\"\n\n\nregister_schema(db_name=\"stockhk_1d_hfq_kdata\", schema_base=KdataBase, entity_type=\"stockhk\")\n\n\n# the __all__ is generated\n__all__ = [\"Stockhk1dHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stockhk/stockhk_1d_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockhkKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stockhk1dKdata(KdataBase, StockhkKdataCommon):\n    __tablename__ = \"stockhk_1d_kdata\"\n\n\nregister_schema(db_name=\"stockhk_1d_kdata\", schema_base=KdataBase, entity_type=\"stockhk\")\n\n\n# the __all__ is generated\n__all__ = [\"Stockhk1dKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stockhk/stockhk_quote.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import String, Column, Float, Integer, Boolean\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockhkKdataCommon\n\nStockhkQuoteBase = declarative_base()\n\n\nclass StockhkQuote(StockhkQuoteBase, StockhkKdataCommon):\n    __tablename__ = \"stockhk_quote\"\n    #: UNIX时间戳\n    time = Column(Integer)\n    #: 最新价\n    price = Column(Float)\n    #: 是否涨停\n    is_limit_up = Column(Boolean)\n    #: 封涨停金额\n    limit_up_amount = Column(Float)\n    #: 是否跌停\n    is_limit_down = Column(Boolean)\n    #: 封跌停金额\n    limit_down_amount = Column(Float)\n    #: 5挡卖单金额\n    ask_amount = Column(Float)\n    #: 5挡买单金额\n    bid_amount = Column(Float)\n    #: 流通市值\n    float_cap = Column(Float)\n    #: 总市值\n    total_cap = Column(Float)\n\n\nclass Stockhk1mQuote(StockhkQuoteBase, Mixin):\n    __tablename__ = \"stockhk_1m_quote\"\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    #: UNIX时间戳\n    time = Column(Integer)\n    #: 最新价\n    price = Column(Float)\n    #: 均价\n    avg_price = Column(Float)\n    # 涨跌幅\n    change_pct = Column(Float)\n    # 成交量\n    volume = Column(Float)\n    # 成交金额\n    turnover = Column(Float)\n    # 换手率\n    turnover_rate = Column(Float)\n    #: 是否涨停\n    is_limit_up = Column(Boolean)\n    #: 是否跌停\n    is_limit_down = Column(Boolean)\n\n\nregister_schema(db_name=\"stockhk_quote\", schema_base=StockhkQuoteBase, entity_type=\"stockhk\")\n\n\n# the __all__ is generated\n__all__ = [\"StockhkQuote\", \"Stockhk1mQuote\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stockus/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule stockus_1d_kdata\nfrom .stockus_1d_kdata import *\nfrom .stockus_1d_kdata import __all__ as _stockus_1d_kdata_all\n\n__all__ += _stockus_1d_kdata_all\n\n# import all from submodule stockus_quote\nfrom .stockus_quote import *\nfrom .stockus_quote import __all__ as _stockus_quote_all\n\n__all__ += _stockus_quote_all\n\n# import all from submodule stockus_1d_hfq_kdata\nfrom .stockus_1d_hfq_kdata import *\nfrom .stockus_1d_hfq_kdata import __all__ as _stockus_1d_hfq_kdata_all\n\n__all__ += _stockus_1d_hfq_kdata_all\n"
  },
  {
    "path": "src/zvt/domain/quotes/stockus/stockus_1d_hfq_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockusKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stockus1dHfqKdata(KdataBase, StockusKdataCommon):\n    __tablename__ = \"stockus_1d_hfq_kdata\"\n\n\nregister_schema(db_name=\"stockus_1d_hfq_kdata\", schema_base=KdataBase, entity_type=\"stockus\")\n\n\n# the __all__ is generated\n__all__ = [\"Stockus1dHfqKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stockus/stockus_1d_kdata.py",
    "content": "# -*- coding: utf-8 -*-\n# this file is generated by gen_kdata_schema function, dont't change it\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockusKdataCommon\n\nKdataBase = declarative_base()\n\n\nclass Stockus1dKdata(KdataBase, StockusKdataCommon):\n    __tablename__ = \"stockus_1d_kdata\"\n\n\nregister_schema(db_name=\"stockus_1d_kdata\", schema_base=KdataBase, entity_type=\"stockus\")\n\n\n# the __all__ is generated\n__all__ = [\"Stockus1dKdata\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/stockus/stockus_quote.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import String, Column, Float, Integer, Boolean\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\nfrom zvt.domain.quotes import StockusKdataCommon\n\nStockusQuoteBase = declarative_base()\n\n\nclass StockusQuote(StockusQuoteBase, StockusKdataCommon):\n    __tablename__ = \"stockus_quote\"\n    #: UNIX时间戳\n    time = Column(Integer)\n    #: 最新价\n    price = Column(Float)\n    #: 是否涨停\n    is_limit_up = Column(Boolean)\n    #: 封涨停金额\n    limit_up_amount = Column(Float)\n    #: 是否跌停\n    is_limit_down = Column(Boolean)\n    #: 封跌停金额\n    limit_down_amount = Column(Float)\n    #: 5挡卖单金额\n    ask_amount = Column(Float)\n    #: 5挡买单金额\n    bid_amount = Column(Float)\n    #: 流通市值\n    float_cap = Column(Float)\n    #: 总市值\n    total_cap = Column(Float)\n\n\nclass Stockus1mQuote(StockusQuoteBase, Mixin):\n    __tablename__ = \"stockus_1m_quote\"\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    #: UNIX时间戳\n    time = Column(Integer)\n    #: 最新价\n    price = Column(Float)\n    #: 均价\n    avg_price = Column(Float)\n    # 涨跌幅\n    change_pct = Column(Float)\n    # 成交量\n    volume = Column(Float)\n    # 成交金额\n    turnover = Column(Float)\n    # 换手率\n    turnover_rate = Column(Float)\n    #: 是否涨停\n    is_limit_up = Column(Boolean)\n    #: 是否跌停\n    is_limit_down = Column(Boolean)\n\n\nregister_schema(db_name=\"stockus_quote\", schema_base=StockusQuoteBase, entity_type=\"stockus\")\n\n\n# the __all__ is generated\n__all__ = [\"StockusQuote\", \"Stockus1mQuote\"]\n"
  },
  {
    "path": "src/zvt/domain/quotes/trade_day.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nTradeDayBase = declarative_base()\n\n\nclass StockTradeDay(TradeDayBase, Mixin):\n    __tablename__ = \"stock_trade_day\"\n\n\nregister_schema(db_name=\"trade_day\", schema_base=TradeDayBase)\n\n\n# the __all__ is generated\n__all__ = [\"StockTradeDay\"]\n"
  },
  {
    "path": "src/zvt/factors/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule algorithm\nfrom .algorithm import *\nfrom .algorithm import __all__ as _algorithm_all\n\n__all__ += _algorithm_all\n\n# import all from submodule top_stocks\nfrom .top_stocks import *\nfrom .top_stocks import __all__ as _top_stocks_all\n\n__all__ += _top_stocks_all\n\n# import all from submodule ma\nfrom .ma import *\nfrom .ma import __all__ as _ma_all\n\n__all__ += _ma_all\n\n# import all from submodule transformers\nfrom .transformers import *\nfrom .transformers import __all__ as _transformers_all\n\n__all__ += _transformers_all\n\n# import all from submodule macd\nfrom .macd import *\nfrom .macd import __all__ as _macd_all\n\n__all__ += _macd_all\n\n# import all from submodule zen\nfrom .zen import *\nfrom .zen import __all__ as _zen_all\n\n__all__ += _zen_all\n\n# import all from submodule technical_factor\nfrom .technical_factor import *\nfrom .technical_factor import __all__ as _technical_factor_all\n\n__all__ += _technical_factor_all\n\n# import all from submodule fundamental\nfrom .fundamental import *\nfrom .fundamental import __all__ as _fundamental_all\n\n__all__ += _fundamental_all\n\n# import all from submodule factor_service\nfrom .factor_service import *\nfrom .factor_service import __all__ as _factor_service_all\n\n__all__ += _factor_service_all\n\n# import all from submodule factor_models\nfrom .factor_models import *\nfrom .factor_models import __all__ as _factor_models_all\n\n__all__ += _factor_models_all\n\n# import all from submodule target_selector\nfrom .target_selector import *\nfrom .target_selector import __all__ as _target_selector_all\n\n__all__ += _target_selector_all\n\n# import all from submodule shape\nfrom .shape import *\nfrom .shape import __all__ as _shape_all\n\n__all__ += _shape_all\n"
  },
  {
    "path": "src/zvt/factors/algorithm.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport pandas as pd\n\nfrom zvt.contract.factor import Scorer, Transformer\nfrom zvt.utils.pd_utils import normal_index_df, group_by_entity_id, normalize_group_compute_result\n\n\ndef ma(s: pd.Series, window: int = 5) -> pd.Series:\n    \"\"\"\n\n    :param s:\n    :param window:\n    :return:\n    \"\"\"\n    return s.rolling(window=window, min_periods=window).mean()\n\n\ndef ema(s: pd.Series, window: int = 12) -> pd.Series:\n    return s.ewm(span=window, adjust=False, min_periods=window).mean()\n\n\ndef live_or_dead(x):\n    if x:\n        return 1\n    else:\n        return -1\n\n\ndef macd(\n    s: pd.Series,\n    slow: int = 26,\n    fast: int = 12,\n    n: int = 9,\n    return_type: str = \"df\",\n    normal: bool = False,\n    count_live_dead: bool = False,\n):\n    # 短期均线\n    ema_fast = ema(s, window=fast)\n    # 长期均线\n    ema_slow = ema(s, window=slow)\n\n    # 短期均线 - 长期均线 = 趋势的力度\n    diff: pd.Series = ema_fast - ema_slow\n    # 力度均线\n    dea: pd.Series = diff.ewm(span=n, adjust=False).mean()\n\n    # 力度 的变化\n    m: pd.Series = (diff - dea) * 2\n\n    # normal it\n    if normal:\n        diff = diff / s\n        dea = dea / s\n        m = m / s\n\n    if count_live_dead:\n        live = (diff > dea).apply(lambda x: live_or_dead(x))\n        bull = (diff > 0) & (dea > 0)\n        live_count = live * (live.groupby((live != live.shift()).cumsum()).cumcount() + 1)\n\n    if return_type == \"se\":\n        if count_live_dead:\n            return diff, dea, m, live, bull, live_count\n        return diff, dea, m\n    else:\n        if count_live_dead:\n            return pd.DataFrame(\n                {\"diff\": diff, \"dea\": dea, \"macd\": m, \"live\": live, \"bull\": bull, \"live_count\": live_count}\n            )\n        return pd.DataFrame({\"diff\": diff, \"dea\": dea, \"macd\": m})\n\n\ndef point_in_range(point: float, range: tuple):\n    \"\"\"\n\n    :param point: one point\n    :param range: (start,end)\n    :return:\n    \"\"\"\n    return range[0] <= point <= range[1]\n\n\ndef intersect_ranges(range_list):\n    if len(range_list) == 1:\n        return range_list[0]\n\n    result = intersect(range_list[0], range_list[1])\n    for range_i in range_list[2:]:\n        result = intersect(result, range_i)\n    return result\n\n\ndef combine(range_a, range_b):\n    if intersect(range_a, range_b):\n        return min(range_a[0], range_b[0]), max(range_a[1], range_b[1])\n    return None\n\n\ndef distance(range_a, range_b, use_max=False):\n    if use_max:\n        # 上升\n        if range_b[0] >= range_a[1]:\n            return (range_b[1] - range_a[0]) / range_a[0]\n\n        # 下降\n        if range_b[1] <= range_a[0]:\n            return (range_b[0] - range_a[1]) / range_a[1]\n    else:\n        middle_start = (range_a[0] + range_a[1]) / 2\n        middle_end = (range_b[0] + range_b[1]) / 2\n\n        return (middle_end - middle_start) / middle_start\n\n\ndef intersect(range_a, range_b):\n    \"\"\"\n    range_a and range_b with format (start,end) in y axis\n\n    :param range_a:\n    :param range_b:\n    :return:\n    \"\"\"\n    if not range_a or not range_b:\n        return None\n    # 包含\n    if point_in_range(range_a[0], range_b) and point_in_range(range_a[1], range_b):\n        return range_a\n    if point_in_range(range_b[0], range_a) and point_in_range(range_b[1], range_a):\n        return range_b\n\n    if point_in_range(range_a[0], range_b):\n        return range_a[0], range_b[1]\n\n    if point_in_range(range_b[0], range_a):\n        return range_b[0], range_a[1]\n    return None\n\n\nclass RankScorer(Scorer):\n    def __init__(self, ascending=True) -> None:\n        self.ascending = ascending\n\n    def score(self, input_df) -> pd.DataFrame:\n        result_df = input_df.groupby(level=1).rank(ascending=self.ascending, pct=True)\n        return result_df\n\n\nclass MaTransformer(Transformer):\n    def __init__(self, windows=None, cal_change_pct=False) -> None:\n        super().__init__()\n        if windows is None:\n            windows = [5, 10]\n        self.windows = windows\n        self.cal_change_pct = cal_change_pct\n\n    def transform(self, input_df: pd.DataFrame) -> pd.DataFrame:\n        if self.cal_change_pct:\n            group_pct = group_by_entity_id(input_df[\"close\"]).pct_change()\n            input_df[\"change_pct\"] = normalize_group_compute_result(group_pct)\n\n        for window in self.windows:\n            col = \"ma{}\".format(window)\n            self.indicators.append(col)\n\n            group_ma = group_by_entity_id(input_df[\"close\"]).rolling(window=window, min_periods=window).mean()\n            input_df[col] = normalize_group_compute_result(group_ma)\n\n        return input_df\n\n    def transform_one(self, entity_id, df: pd.DataFrame) -> pd.DataFrame:\n        \"\"\"\n        transform_one would not take effects if transform was implemented.\n        Just show how to implement it here, most of time you should overwrite transform directly for performance.\n\n        :param entity_id:\n        :param df:\n        :return:\n        \"\"\"\n        if self.cal_change_pct:\n            df[\"change_pct\"] = df[\"close\"].pct_change()\n\n        for window in self.windows:\n            col = \"ma{}\".format(window)\n            self.indicators.append(col)\n\n            df[col] = df[\"close\"].rolling(window=window, min_periods=window).mean()\n\n        return df\n\n\nclass IntersectTransformer(Transformer):\n    def __init__(self, kdata_overlap=0) -> None:\n        super().__init__()\n        self.kdata_overlap = kdata_overlap\n\n    def transform(self, input_df) -> pd.DataFrame:\n        \"\"\"\n\n        :param input_df:\n        :return:\n        \"\"\"\n        if self.kdata_overlap > 0:\n            # 没有重叠，区间就是(0,0)\n            input_df[\"overlap\"] = [(0, 0)] * len(input_df.index)\n\n            def cal_overlap(s):\n                high = input_df.loc[s.index, \"high\"]\n                low = input_df.loc[s.index, \"low\"]\n                intersection = intersect_ranges(list(zip(low.to_list(), high.to_list())))\n                if intersection:\n                    # 设置column overlap为intersection,即重叠区间\n                    input_df.at[s.index[-1], \"overlap\"] = intersection\n                return 0\n\n            input_df[[\"high\", \"low\"]].groupby(level=0).rolling(\n                window=self.kdata_overlap, min_periods=self.kdata_overlap\n            ).apply(cal_overlap, raw=False)\n\n        return input_df\n\n\nclass MaAndVolumeTransformer(Transformer):\n    def __init__(self, windows=None, vol_windows=None, kdata_overlap=0) -> None:\n        super().__init__()\n        if vol_windows is None:\n            vol_windows = [30]\n        if windows is None:\n            windows = [5, 10]\n        self.windows = windows\n        self.vol_windows = vol_windows\n        self.kdata_overlap = kdata_overlap\n\n    def transform(self, input_df) -> pd.DataFrame:\n        for window in self.windows:\n            col = f\"ma{window}\"\n            self.indicators.append(col)\n\n            ma_df = input_df[\"close\"].groupby(level=0).rolling(window=window, min_periods=window).mean()\n            ma_df = ma_df.reset_index(level=0, drop=True)\n            input_df[col] = ma_df\n\n        for vol_window in self.vol_windows:\n            col = \"vol_ma{}\".format(vol_window)\n\n            vol_ma_df = input_df[\"volume\"].groupby(level=0).rolling(window=vol_window, min_periods=vol_window).mean()\n            vol_ma_df = vol_ma_df.reset_index(level=0, drop=True)\n            input_df[col] = vol_ma_df\n\n        if self.kdata_overlap > 0:\n            input_df[\"overlap\"] = [(0, 0)] * len(input_df.index)\n\n            def cal_overlap(s):\n                high = input_df.loc[s.index, \"high\"]\n                low = input_df.loc[s.index, \"low\"]\n                intersection = intersect_ranges(list(zip(low.to_list(), high.to_list())))\n                if intersection:\n                    input_df.at[s.index[-1], \"overlap\"] = intersection\n                return 0\n\n            input_df[[\"high\", \"low\"]].groupby(level=0).rolling(\n                window=self.kdata_overlap, min_periods=self.kdata_overlap\n            ).apply(cal_overlap, raw=False)\n\n        return input_df\n\n\nclass MacdTransformer(Transformer):\n    def __init__(self, slow=26, fast=12, n=9, normal=False, count_live_dead=False) -> None:\n        super().__init__()\n        self.slow = slow\n        self.fast = fast\n        self.n = n\n        self.normal = normal\n        self.count_live_dead = count_live_dead\n\n        self.indicators.append(\"diff\")\n        self.indicators.append(\"dea\")\n        self.indicators.append(\"macd\")\n\n    def transform(self, input_df) -> pd.DataFrame:\n        macd_df = input_df.groupby(level=0)[\"close\"].apply(\n            lambda x: macd(\n                x,\n                slow=self.slow,\n                fast=self.fast,\n                n=self.n,\n                return_type=\"df\",\n                normal=self.normal,\n                count_live_dead=self.count_live_dead,\n            )\n        )\n        macd_df = macd_df.reset_index(level=0, drop=True)\n        input_df = pd.concat([input_df, macd_df], axis=1, sort=False, verify_integrity=True)\n        return input_df\n\n    def transform_one(self, entity_id, df: pd.DataFrame) -> pd.DataFrame:\n        print(f\"transform_one {entity_id} {df}\")\n        return macd(\n            df[\"close\"],\n            slow=self.slow,\n            fast=self.fast,\n            n=self.n,\n            return_type=\"df\",\n            normal=self.normal,\n            count_live_dead=self.count_live_dead,\n        )\n\n\nclass QuantileScorer(Scorer):\n    def __init__(self, score_levels=[0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0]) -> None:\n        self.score_levels = score_levels\n\n    def score(self, input_df):\n        self.score_levels.sort(reverse=True)\n\n        quantile_df = input_df.groupby(level=1).quantile(self.score_levels)\n        quantile_df.index.names = [self.time_field, \"score_result\"]\n\n        self.logger.info(\"factor:{},quantile:\\n{}\".format(self.factor_name, quantile_df))\n\n        result_df = input_df.copy()\n        result_df.reset_index(inplace=True, level=\"entity_id\")\n        result_df[\"quantile\"] = None\n        for timestamp in quantile_df.index.levels[0]:\n            length = len(result_df.loc[result_df.index == timestamp, \"quantile\"])\n            result_df.loc[result_df.index == timestamp, \"quantile\"] = [quantile_df.loc[timestamp].to_dict()] * length\n\n        self.logger.info(\"factor:{},df with quantile:\\n{}\".format(self.factor_name, result_df))\n\n        # result_df = result_df.set_index(['entity_id'], append=True)\n        # result_df = result_df.sort_index(level=[0, 1])\n        #\n        # self.logger.info(result_df)\n        #\n        def calculate_score(df, factor_name, quantile):\n            original_value = df[factor_name]\n            score_map = quantile.get(factor_name)\n            min_score = self.score_levels[-1]\n\n            if original_value < score_map.get(min_score):\n                return 0\n\n            for score in self.score_levels[:-1]:\n                if original_value >= score_map.get(score):\n                    return score\n\n        for factor in input_df.columns.to_list():\n            result_df[factor] = result_df.apply(lambda x: calculate_score(x, factor, x[\"quantile\"]), axis=1)\n\n        result_df = result_df.reset_index()\n        result_df = normal_index_df(result_df)\n        result_df = result_df.loc[:, self.factors]\n\n        result_df = result_df.loc[~result_df.index.duplicated(keep=\"first\")]\n\n        self.logger.info(\"factor:{},df:\\n{}\".format(self.factor_name, result_df))\n\n        return result_df\n\n\n# the __all__ is generated\n__all__ = [\n    \"ma\",\n    \"ema\",\n    \"live_or_dead\",\n    \"macd\",\n    \"point_in_range\",\n    \"intersect_ranges\",\n    \"combine\",\n    \"distance\",\n    \"intersect\",\n    \"RankScorer\",\n    \"MaTransformer\",\n    \"IntersectTransformer\",\n    \"MaAndVolumeTransformer\",\n    \"MacdTransformer\",\n    \"QuantileScorer\",\n]\n"
  },
  {
    "path": "src/zvt/factors/factor_models.py",
    "content": "# -*- coding: utf-8 -*-\nfrom datetime import datetime\nfrom typing import List, Optional\n\nfrom pydantic import BaseModel, Field\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.trader import TradingSignalType\nfrom zvt.utils.time_utils import date_time_by_interval, current_date\n\n\nclass FactorRequestModel(BaseModel):\n    factor_name: str\n    entity_ids: Optional[List[str]]\n    data_provider: str = Field(default=\"em\")\n    start_timestamp: datetime = Field(default=date_time_by_interval(current_date(), -365))\n    level: IntervalLevel = Field(default=IntervalLevel.LEVEL_1DAY)\n\n\nclass TradingSignalModel(BaseModel):\n    entity_id: str\n    happen_timestamp: datetime\n    due_timestamp: datetime\n    trading_level: IntervalLevel = Field(default=IntervalLevel.LEVEL_1DAY)\n    trading_signal_type: TradingSignalType\n    position_pct: Optional[float] = Field(default=0.2)\n    order_amount: Optional[float] = Field(default=None)\n    order_money: Optional[float] = Field(default=None)\n\n\nclass FactorResultModel(BaseModel):\n    entity_ids: Optional[List[str]]\n    tag_reason: str\n\n\n# the __all__ is generated\n__all__ = [\"FactorRequestModel\", \"TradingSignalModel\", \"FactorResultModel\"]\n"
  },
  {
    "path": "src/zvt/factors/factor_service.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.contract import zvt_context\nfrom zvt.domain import Stock\nfrom zvt.factors.factor_models import FactorRequestModel\nfrom zvt.factors.technical_factor import TechnicalFactor\nfrom zvt.trader import TradingSignalType\n\n\ndef query_factor_result(factor_request_model: FactorRequestModel):\n    factor_name = factor_request_model.factor_name\n    entity_ids = factor_request_model.entity_ids\n    level = factor_request_model.level\n\n    factor: TechnicalFactor = zvt_context.factor_cls_registry[factor_name](\n        provider=\"em\",\n        entity_provider=\"em\",\n        entity_schema=Stock,\n        entity_ids=entity_ids,\n        level=level,\n        start_timestamp=factor_request_model.start_timestamp,\n    )\n    df = factor.get_trading_signal_df()\n    df = df.reset_index(drop=False)\n\n    def to_trading_signal(order_type):\n        if order_type is None:\n            return None\n        if order_type:\n            return TradingSignalType.open_long\n        if not order_type:\n            return TradingSignalType.close_long\n\n    df = df.rename(columns={\"timestamp\": \"happen_timestamp\"})\n    df[\"due_timestamp\"] = df[\"happen_timestamp\"] + pd.Timedelta(seconds=level.to_second())\n    df[\"trading_signal_type\"] = df[\"filter_result\"].apply(lambda x: to_trading_signal(x))\n\n    print(df)\n    return df.to_dict(orient=\"records\")\n\n\n# the __all__ is generated\n__all__ = [\"query_factor_result\"]\n"
  },
  {
    "path": "src/zvt/factors/fundamental/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule finance_factor\nfrom .finance_factor import *\nfrom .finance_factor import __all__ as _finance_factor_all\n\n__all__ += _finance_factor_all\n"
  },
  {
    "path": "src/zvt/factors/fundamental/finance_factor.py",
    "content": "# -*- coding: utf-8 -*-\nimport operator\nfrom itertools import accumulate\nfrom typing import List, Union, Type\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel, Mixin, TradableEntity\nfrom zvt.contract.factor import Factor, Transformer, Accumulator\nfrom zvt.domain import FinanceFactor, BalanceSheet, Stock\n\n\nclass FinanceBaseFactor(Factor):\n    def __init__(\n        self,\n        data_schema: Type[Mixin] = FinanceFactor,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = None,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        transformer: Transformer = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n    ) -> None:\n        if not columns:\n            columns = data_schema.important_cols()\n        super().__init__(\n            data_schema,\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n        )\n\n\nclass GoodCompanyFactor(FinanceBaseFactor):\n    def __init__(\n        self,\n        data_schema: Type[Mixin] = FinanceFactor,\n        entity_schema: TradableEntity = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = (\n            FinanceFactor.roe,\n            FinanceFactor.op_income_growth_yoy,\n            FinanceFactor.net_profit_growth_yoy,\n            FinanceFactor.report_period,\n            FinanceFactor.op_net_cash_flow_per_op_income,\n            FinanceFactor.sales_net_cash_flow_per_op_income,\n            FinanceFactor.current_ratio,\n            FinanceFactor.debt_asset_ratio,\n        ),\n        filters: List = (\n            FinanceFactor.roe >= 0.02,\n            FinanceFactor.op_income_growth_yoy >= 0.05,\n            FinanceFactor.net_profit_growth_yoy >= 0.05,\n            FinanceFactor.op_net_cash_flow_per_op_income >= 0.1,\n            FinanceFactor.sales_net_cash_flow_per_op_income >= 0.3,\n            FinanceFactor.current_ratio >= 1,\n            FinanceFactor.debt_asset_ratio <= 0.5,\n        ),\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = True,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        transformer: Transformer = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n        window=\"1095d\",\n        count=8,\n        col_period_threshold={\"roe\": 0.02},\n    ) -> None:\n        self.window = window\n        self.count = count\n\n        # 对于根据年度计算才有意义的指标，比如roe,我们会对不同季度的值区别处理,传入的参数为季度值\n        self.col_period_threshold = col_period_threshold\n        if self.col_period_threshold:\n            if \"report_period\" not in columns and (data_schema.report_period not in columns):\n                columns.append(data_schema.report_period)\n\n        self.logger.info(f\"using data_schema:{data_schema.__name__}\")\n\n        super().__init__(\n            data_schema,\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n        )\n\n    def compute_factor(self):\n        def filter_df(df):\n            se = pd.Series(index=df.index)\n            for index, row in df.iterrows():\n\n                if row.report_period == \"year\":\n                    mul = 4\n                elif row.report_period == \"season3\":\n                    mul = 3\n                elif row.report_period == \"half_year\":\n                    mul = 2\n                else:\n                    mul = 1\n\n                filters = []\n                for col in self.col_period_threshold:\n                    col_se = eval(f\"row.{col}\")\n                    filters.append(col_se >= mul * self.col_period_threshold[col])\n                se[index] = list(accumulate(filters, func=operator.__and__))[-1]\n\n            return se\n\n        if self.col_period_threshold:\n            self.factor_df = self.data_df.loc[lambda df: filter_df(df), :]\n\n        self.factor_df = pd.DataFrame(index=self.data_df.index, columns=[\"count\"], data=1)\n\n        self.factor_df = self.factor_df.reset_index(level=1)\n\n        self.factor_df = self.factor_df.groupby(level=0).rolling(window=self.window, on=self.time_field).count()\n\n        self.factor_df = self.factor_df.reset_index(level=0, drop=True)\n        self.factor_df = self.factor_df.set_index(self.time_field, append=True)\n\n        self.factor_df = self.factor_df.loc[(slice(None), slice(self.start_timestamp, self.end_timestamp)), :]\n\n        self.logger.info(\"factor:{},factor_df:\\n{}\".format(self.name, self.factor_df))\n\n    def compute_result(self):\n        self.result_df = self.factor_df.apply(lambda x: x >= self.count)\n        self.result_df.columns = [\"filter_score\"]\n\n        self.logger.info(\"factor:{},result_df:\\n{}\".format(self.name, self.result_df))\n\n\nif __name__ == \"__main__\":\n    # f1 = GoodCompanyFactor(keep_all_timestamp=False)\n    # print(f1.result_df)\n\n    # 高股息 低应收\n    factor2 = GoodCompanyFactor(\n        data_schema=BalanceSheet,\n        columns=[BalanceSheet.accounts_receivable],\n        filters=[BalanceSheet.accounts_receivable <= 0.2 * BalanceSheet.total_current_assets],\n        keep_all_timestamp=False,\n        col_period_threshold=None,\n    )\n    print(factor2.result_df)\n\n\n# the __all__ is generated\n__all__ = [\"FinanceBaseFactor\", \"GoodCompanyFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/ma/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule ma_stats_factor\nfrom .ma_stats_factor import *\nfrom .ma_stats_factor import __all__ as _ma_stats_factor_all\n\n__all__ += _ma_stats_factor_all\n\n# import all from submodule top_bottom_factor\nfrom .top_bottom_factor import *\nfrom .top_bottom_factor import __all__ as _top_bottom_factor_all\n\n__all__ += _top_bottom_factor_all\n\n# import all from submodule ma_factor\nfrom .ma_factor import *\nfrom .ma_factor import __all__ as _ma_factor_all\n\n__all__ += _ma_factor_all\n\n# import all from submodule domain\nfrom .domain import *\nfrom .domain import __all__ as _domain_all\n\n__all__ += _domain_all\n"
  },
  {
    "path": "src/zvt/factors/ma/domain/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule stock_1d_ma_stats_factor\nfrom .stock_1d_ma_stats_factor import *\nfrom .stock_1d_ma_stats_factor import __all__ as _stock_1d_ma_stats_factor_all\n\n__all__ += _stock_1d_ma_stats_factor_all\n\n# import all from submodule stock_1d_ma_factor\nfrom .stock_1d_ma_factor import *\nfrom .stock_1d_ma_factor import __all__ as _stock_1d_ma_factor_all\n\n__all__ += _stock_1d_ma_factor_all\n\n# import all from submodule common\nfrom .common import *\nfrom .common import __all__ as _common_all\n\n__all__ += _common_all\n"
  },
  {
    "path": "src/zvt/factors/ma/domain/common.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, Float, Integer\n\nfrom zvt.contract import Mixin\n\n\nclass MaStatsFactorCommon(Mixin):\n    open = Column(Float)\n    close = Column(Float)\n    high = Column(Float)\n    low = Column(Float)\n    turnover = Column(Float)\n\n    ma5 = Column(Float)\n    ma10 = Column(Float)\n\n    ma34 = Column(Float)\n    ma55 = Column(Float)\n    ma89 = Column(Float)\n    ma144 = Column(Float)\n\n    ma120 = Column(Float)\n    ma250 = Column(Float)\n\n    vol_ma30 = Column(Float)\n\n    live = Column(Integer)\n    count = Column(Integer)\n    distance = Column(Float)\n    area = Column(Float)\n\n\n# the __all__ is generated\n__all__ = [\"MaStatsFactorCommon\"]\n"
  },
  {
    "path": "src/zvt/factors/ma/domain/stock_1d_ma_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, Float, String\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nStock1dMaFactorBase = declarative_base()\n\n\nclass Stock1dMaFactor(Stock1dMaFactorBase, Mixin):\n    __tablename__ = \"Stock1dMaFactor\"\n\n    level = Column(String(length=32))\n    code = Column(String(length=32))\n    name = Column(String(length=32))\n\n    open = Column(Float)\n    close = Column(Float)\n    high = Column(Float)\n    low = Column(Float)\n\n    ma5 = Column(Float)\n    ma10 = Column(Float)\n\n    ma34 = Column(Float)\n    ma55 = Column(Float)\n    ma89 = Column(Float)\n    ma144 = Column(Float)\n\n    ma120 = Column(Float)\n    ma250 = Column(Float)\n\n\nregister_schema(db_name=\"stock_1d_ma_factor\", schema_base=Stock1dMaFactorBase, internal=True)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1dMaFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/ma/domain/stock_1d_ma_stats_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.factors.ma.domain.common import MaStatsFactorCommon\n\nStock1dMaStatsFactorBase = declarative_base()\n\n\nclass Stock1dMaStatsFactor(Stock1dMaStatsFactorBase, MaStatsFactorCommon):\n    __tablename__ = \"stock_1d_ma_stats_factor\"\n\n\nregister_schema(db_name=\"stock_1d_ma_stats_factor\", schema_base=Stock1dMaStatsFactorBase, internal=True)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1dMaStatsFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/ma/ma_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List, Union, Type\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel, TradableEntity, AdjustType\nfrom zvt.contract.api import get_schema_by_name\nfrom zvt.contract.factor import Accumulator\nfrom zvt.contract.factor import Transformer\nfrom zvt.domain import Stock\nfrom zvt.factors.algorithm import MaTransformer, MaAndVolumeTransformer\nfrom zvt.factors.technical_factor import TechnicalFactor\n\n\ndef get_ma_factor_schema(entity_type: str, level: Union[IntervalLevel, str] = IntervalLevel.LEVEL_1DAY):\n    if type(level) == str:\n        level = IntervalLevel(level)\n\n    schema_str = \"{}{}MaFactor\".format(entity_type.capitalize(), level.value.capitalize())\n\n    return get_schema_by_name(schema_str)\n\n\nclass MaFactor(TechnicalFactor):\n    def __init__(\n        self,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n        adjust_type: Union[AdjustType, str] = None,\n        windows=None,\n    ) -> None:\n        if need_persist:\n            self.factor_schema = get_ma_factor_schema(entity_type=entity_schema.__name__, level=level)\n\n        if not windows:\n            windows = [5, 10, 34, 55, 89, 144, 120, 250]\n        self.windows = windows\n        transformer: Transformer = MaTransformer(windows=windows)\n\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            None,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n        )\n\n\nclass CrossMaFactor(MaFactor):\n    def compute_result(self):\n        super().compute_result()\n        cols = [f\"ma{window}\" for window in self.windows]\n        s = self.factor_df[cols[0]] > self.factor_df[cols[1]]\n        current_col = cols[1]\n        for col in cols[2:]:\n            s = s & (self.factor_df[current_col] > self.factor_df[col])\n            current_col = col\n\n        print(self.factor_df[s])\n        self.result_df = s.to_frame(name=\"filter_result\")\n\n\nclass VolumeUpMaFactor(TechnicalFactor):\n    def __init__(\n        self,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n        adjust_type: Union[AdjustType, str] = None,\n        windows=None,\n        vol_windows=None,\n        turnover_threshold=300000000,\n        turnover_rate_threshold=0.02,\n        up_intervals=40,\n        over_mode=\"and\",\n    ) -> None:\n        if not windows:\n            windows = [250]\n        if not vol_windows:\n            vol_windows = [30]\n\n        self.windows = windows\n        self.vol_windows = vol_windows\n        self.turnover_threshold = turnover_threshold\n        self.turnover_rate_threshold = turnover_rate_threshold\n        self.up_intervals = up_intervals\n        self.over_mode = over_mode\n\n        transformer: Transformer = MaAndVolumeTransformer(windows=windows, vol_windows=vol_windows)\n\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n        )\n\n    def compute_result(self):\n        super().compute_result()\n\n        # 价格刚上均线\n        cols = [f\"ma{window}\" for window in self.windows]\n        filter_up = (self.factor_df[\"close\"] > self.factor_df[cols[0]]) & (\n            self.factor_df[\"close\"] < 1.15 * self.factor_df[cols[0]]\n        )\n        for col in cols[1:]:\n            if self.over_mode == \"and\":\n                filter_up = filter_up & (\n                    (self.factor_df[\"close\"] > self.factor_df[col])\n                    & (self.factor_df[\"close\"] < 1.1 * self.factor_df[col])\n                )\n            else:\n                filter_up = filter_up | (\n                    (self.factor_df[\"close\"] > self.factor_df[col])\n                    & (self.factor_df[\"close\"] < 1.1 * self.factor_df[col])\n                )\n        # 放量\n        if self.vol_windows:\n            vol_cols = [f\"vol_ma{window}\" for window in self.vol_windows]\n            filter_vol = self.factor_df[\"volume\"] > 2 * self.factor_df[vol_cols[0]]\n            for col in vol_cols[1:]:\n                filter_vol = filter_vol & (self.factor_df[\"volume\"] > 2 * self.factor_df[col])\n\n        # 成交额，换手率过滤\n        filter_turnover = (self.factor_df[\"turnover\"] > self.turnover_threshold) & (\n            self.factor_df[\"turnover_rate\"] > self.turnover_rate_threshold\n        )\n        s = filter_up & filter_vol & filter_turnover\n\n        # 突破后的时间周期 up_intervals\n        s[s == False] = None\n        s = s.groupby(level=0).fillna(method=\"ffill\", limit=self.up_intervals)\n        s[s.isna()] = False\n\n        # 还在均线附近\n        # 1)刚突破\n        # 2)突破后，回调到附近\n        filter_result = filter_up & s & filter_turnover\n\n        self.result_df = filter_result.to_frame(name=\"filter_result\")\n        # self.result_df = self.result_df.replace(False, None)\n\n\nclass CrossMaVolumeFactor(VolumeUpMaFactor):\n    def __init__(\n        self,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n        adjust_type: Union[AdjustType, str] = None,\n        windows=[5, 10, 250],\n        vol_windows=None,\n        turnover_threshold=300000000,\n        turnover_rate_threshold=0.02,\n        up_intervals=40,\n        over_mode=\"and\",\n    ) -> None:\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n            windows,\n            vol_windows,\n            turnover_threshold,\n            turnover_rate_threshold,\n            up_intervals,\n            over_mode,\n        )\n\n    def compute_result(self):\n        # 均线多头排列\n        cols = [f\"ma{window}\" for window in self.windows]\n        filter_se = self.factor_df[cols[0]] > self.factor_df[cols[1]]\n        current_col = cols[1]\n        for col in cols[2:]:\n            filter_se = filter_se & (self.factor_df[current_col] > self.factor_df[col])\n            current_col = col\n\n        filter_se = filter_se & (self.factor_df[\"turnover\"] > self.turnover_threshold)\n        self.result_df = filter_se.to_frame(name=\"filter_result\")\n        # self.result_df = self.result_df.replace(False, None)\n\n\nif __name__ == \"__main__\":\n    provider = \"em\"\n    target_date = get_latest_kdata_date(entity_type=\"stock\", provider=provider, adjust_type=AdjustType.qfq)\n    factor = VolumeUpMaFactor(\n        entity_schema=Stock,\n        entity_provider=provider,\n        provider=provider,\n        entity_ids=None,\n        start_timestamp=date_time_by_interval(target_date, -600),\n        end_timestamp=target_date,\n        adjust_type=AdjustType.qfq,\n        windows=[120, 250],\n        over_mode=\"or\",\n        up_intervals=60,\n        turnover_threshold=300000000,\n        turnover_rate_threshold=0.02,\n    )\n\n    stocks = factor.get_targets(timestamp=target_date, target_type=TargetType.positive)  # factor = CrossMaVolumeFactor(\n    print(stocks)\n    #     entity_provider=\"em\",\n    #     provider=\"em\",\n    #     entity_ids=[\"stock_sz_000338\"],\n    #     start_timestamp=\"2020-01-01\",\n    #     end_timestamp=now_pd_timestamp(),\n    #     need_persist=False,\n    # )\n    # factor.drawer().draw(show=True)\n\n\n# the __all__ is generated\n__all__ = [\"get_ma_factor_schema\", \"MaFactor\", \"CrossMaFactor\", \"VolumeUpMaFactor\", \"CrossMaVolumeFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/ma/ma_stats_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List, Union, Type, Optional\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel, TradableEntity, AdjustType\nfrom zvt.contract.api import get_schema_by_name\nfrom zvt.contract.factor import Accumulator\nfrom zvt.domain import Stock\nfrom zvt.factors.algorithm import live_or_dead\nfrom zvt.factors.technical_factor import TechnicalFactor\nfrom zvt.utils.pd_utils import pd_is_not_null\n\n\ndef get_ma_stats_factor_schema(entity_type: str, level: Union[IntervalLevel, str] = IntervalLevel.LEVEL_1DAY):\n    if type(level) == str:\n        level = IntervalLevel(level)\n\n    schema_str = \"{}{}MaStatsFactor\".format(entity_type.capitalize(), level.value.capitalize())\n\n    return get_schema_by_name(schema_str)\n\n\nclass MaStatsAccumulator(Accumulator):\n    def __init__(self, acc_window: int = 250, windows=None, vol_windows=None) -> None:\n        super().__init__(acc_window)\n        self.windows = windows\n        self.vol_windows = vol_windows\n\n    def acc_one(self, entity_id, df: pd.DataFrame, acc_df: pd.DataFrame, state: dict) -> (pd.DataFrame, dict):\n        self.logger.info(f\"acc_one:{entity_id}\")\n        if pd_is_not_null(acc_df):\n            df = df[df.index > acc_df.index[-1]]\n            if pd_is_not_null(df):\n                self.logger.info(f'compute from {df.iloc[0][\"timestamp\"]}')\n                acc_df = pd.concat([acc_df, df])\n            else:\n                self.logger.info(\"no need to compute\")\n                return acc_df, state\n        else:\n            acc_df = df\n\n        for window in self.windows:\n            col = \"ma{}\".format(window)\n            self.indicators.append(col)\n\n            ma_df = acc_df[\"close\"].rolling(window=window, min_periods=window).mean()\n            acc_df[col] = ma_df\n\n        acc_df[\"live\"] = (acc_df[\"ma5\"] > acc_df[\"ma10\"]).apply(lambda x: live_or_dead(x))\n        acc_df[\"distance\"] = (acc_df[\"ma5\"] - acc_df[\"ma10\"]) / acc_df[\"close\"]\n\n        live = acc_df[\"live\"]\n        acc_df[\"count\"] = live * (live.groupby((live != live.shift()).cumsum()).cumcount() + 1)\n\n        acc_df[\"bulk\"] = (live != live.shift()).cumsum()\n        area_df = acc_df[[\"distance\", \"bulk\"]]\n        acc_df[\"area\"] = area_df.groupby(\"bulk\").cumsum()\n\n        for vol_window in self.vol_windows:\n            col = \"vol_ma{}\".format(vol_window)\n            self.indicators.append(col)\n\n            vol_ma_df = acc_df[\"turnover\"].rolling(window=vol_window, min_periods=vol_window).mean()\n            acc_df[col] = vol_ma_df\n\n        acc_df = acc_df.set_index(\"timestamp\", drop=False)\n        return acc_df, state\n\n\nclass MaStatsFactor(TechnicalFactor):\n    def __init__(\n        self,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        need_persist: bool = True,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n        adjust_type: Union[AdjustType, str] = None,\n        windows=None,\n        vol_windows=None,\n    ) -> None:\n        if need_persist:\n            self.factor_schema = get_ma_stats_factor_schema(entity_type=entity_schema.__name__, level=level)\n\n        if not windows:\n            windows = [5, 10, 34, 55, 89, 144, 120, 250]\n        self.windows = windows\n\n        if not vol_windows:\n            vol_windows = [30]\n        self.vol_windows = vol_windows\n\n        columns: List = [\"id\", \"entity_id\", \"timestamp\", \"level\", \"open\", \"close\", \"high\", \"low\", \"turnover\"]\n\n        accumulator: Accumulator = MaStatsAccumulator(windows=self.windows, vol_windows=self.vol_windows)\n\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            None,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n        )\n\n\nclass TFactor(MaStatsFactor):\n    def __init__(\n        self,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        need_persist: bool = True,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = True,\n        adjust_type: Union[AdjustType, str] = None,\n        windows=None,\n        vol_windows=None,\n    ) -> None:\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n            windows,\n            vol_windows,\n        )\n\n    def drawer_sub_df_list(self) -> Optional[List[pd.DataFrame]]:\n        return [self.factor_df[[\"area\"]]]\n\n    def drawer_factor_df_list(self) -> Optional[List[pd.DataFrame]]:\n        return [self.factor_df[[\"ma5\", \"ma10\"]]]\n\n\nif __name__ == \"__main__\":\n    codes = [\"000338\"]\n\n    f = TFactor(codes=codes, only_load_factor=False)\n\n    # distribute(f.factor_df[['area']],'area')\n    f.draw(show=True)\n\n\n# the __all__ is generated\n__all__ = [\"get_ma_stats_factor_schema\", \"MaStatsAccumulator\", \"MaStatsFactor\", \"TFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/ma/top_bottom_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List, Union\n\nimport pandas as pd\n\nfrom zvt.contract import AdjustType\nfrom zvt.contract import IntervalLevel, TradableEntity\nfrom zvt.contract.drawer import Drawer\nfrom zvt.contract.factor import Accumulator\nfrom zvt.contract.factor import Transformer\nfrom zvt.contract.reader import DataReader\nfrom zvt.domain import Stock, Stock1dKdata\nfrom zvt.factors.technical_factor import TechnicalFactor\nfrom zvt.utils.time_utils import now_pd_timestamp\n\n\nclass TopBottomTransformer(Transformer):\n    def __init__(self, window=20) -> None:\n        super().__init__()\n        self.window = window\n\n    def transform(self, input_df) -> pd.DataFrame:\n        top_df = input_df[\"high\"].groupby(level=0).rolling(window=self.window, min_periods=self.window).max()\n        top_df = top_df.reset_index(level=0, drop=True)\n        input_df[\"top\"] = top_df\n\n        bottom_df = input_df[\"low\"].groupby(level=0).rolling(window=self.window, min_periods=self.window).min()\n        bottom_df = bottom_df.reset_index(level=0, drop=True)\n        input_df[\"bottom\"] = bottom_df\n\n        return input_df\n\n\nclass TopBottomFactor(TechnicalFactor):\n    def __init__(\n        self,\n        entity_schema: TradableEntity = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = [\"id\", \"entity_id\", \"timestamp\", \"level\", \"open\", \"close\", \"high\", \"low\"],\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n        adjust_type: Union[AdjustType, str] = None,\n        window=30,\n    ) -> None:\n\n        transformer = TopBottomTransformer(window=window)\n\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n        )\n\n\nif __name__ == \"__main__\":\n    factor = TopBottomFactor(\n        codes=[\"601318\"],\n        start_timestamp=\"2005-01-01\",\n        end_timestamp=now_pd_timestamp(),\n        level=IntervalLevel.LEVEL_1DAY,\n        window=120,\n    )\n    print(factor.factor_df)\n\n    data_reader1 = DataReader(data_schema=Stock1dKdata, entity_schema=Stock, codes=[\"601318\"])\n\n    drawer = Drawer(main_df=data_reader1.data_df, factor_df_list=[factor.factor_df[[\"top\", \"bottom\"]]])\n    drawer.draw_kline(show=True)\n\n\n# the __all__ is generated\n__all__ = [\"TopBottomTransformer\", \"TopBottomFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/macd/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule macd_factor\nfrom .macd_factor import *\nfrom .macd_factor import __all__ as _macd_factor_all\n\n__all__ += _macd_factor_all\n"
  },
  {
    "path": "src/zvt/factors/macd/macd_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List, Optional\n\nimport numpy as np\nimport pandas as pd\n\nfrom zvt.factors.algorithm import MacdTransformer\nfrom zvt.factors.technical_factor import TechnicalFactor\n\n\nclass MacdFactor(TechnicalFactor):\n    transformer = MacdTransformer(count_live_dead=True)\n\n    def drawer_factor_df_list(self) -> Optional[List[pd.DataFrame]]:\n        return None\n\n    def drawer_sub_df_list(self) -> Optional[List[pd.DataFrame]]:\n        return [self.factor_df[[\"diff\", \"dea\", \"macd\"]]]\n\n    def drawer_sub_col_chart(self) -> Optional[dict]:\n        return {\"diff\": \"line\", \"dea\": \"line\", \"macd\": \"bar\"}\n\n\nclass BullFactor(MacdFactor):\n    def compute_result(self):\n        super().compute_result()\n        self.result_df = self.factor_df[\"bull\"].to_frame(name=\"filter_result\")\n\n\nclass KeepBullFactor(BullFactor):\n    keep_window = 10\n\n    def compute_result(self):\n        super().compute_result()\n        df = (\n            self.result_df[\"filter_result\"]\n            .groupby(level=0)\n            .rolling(window=self.keep_window, min_periods=self.keep_window)\n            .apply(lambda x: np.logical_and.reduce(x))\n        )\n        df = df.reset_index(level=0, drop=True)\n        self.result_df[\"filter_result\"] = df\n\n\n# 金叉 死叉 持续时间 切换点\nclass LiveOrDeadFactor(MacdFactor):\n    pattern = [-5, 1]\n\n    def compute_result(self):\n        super().compute_result()\n        self.factor_df[\"pre\"] = self.factor_df[\"live_count\"].shift()\n        s = (self.factor_df[\"pre\"] <= self.pattern[0]) & (self.factor_df[\"live_count\"] >= self.pattern[1])\n        self.result_df = s.to_frame(name=\"filter_result\")\n\n\nclass GoldCrossFactor(MacdFactor):\n    def compute_result(self):\n        super().compute_result()\n        s = self.factor_df[\"live\"] == 1\n        self.result_df = s.to_frame(name=\"filter_result\")\n\n\nif __name__ == \"__main__\":\n    f = GoldCrossFactor(provider=\"em\", entity_provider=\"em\", entity_ids=[\"stock_sz_000338\"])\n    f.drawer().draw(show=True)\n\n\n# the __all__ is generated\n__all__ = [\"MacdFactor\", \"BullFactor\", \"KeepBullFactor\", \"LiveOrDeadFactor\", \"GoldCrossFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/shape.py",
    "content": "# -*- coding: utf-8 -*-\nimport json\nimport logging\nfrom enum import Enum\nfrom typing import List\n\nimport pandas as pd\n\nfrom zvt.contract.data_type import Bean\nfrom zvt.contract.drawer import Rect\nfrom zvt.factors.algorithm import intersect\nfrom zvt.utils.time_utils import TIME_FORMAT_ISO8601, to_date_time_str\n\nlogger = logging.getLogger(__name__)\n\n\nclass Direction(Enum):\n    up = \"up\"\n    down = \"down\"\n\n    def opposite(self):\n        if self == Direction.up:\n            return Direction.down\n        if self == Direction.down:\n            return Direction.up\n\n\nclass Fenxing(Bean):\n    def __init__(self, state, kdata, index) -> None:\n        self.state = state\n        self.kdata = kdata\n        self.index = index\n\n\ndef fenxing_power(left, middle, right, fenxing=\"tmp_ding\"):\n    if fenxing == \"tmp_ding\":\n        a = middle[\"high\"] - middle[\"close\"]\n        b = middle[\"high\"] - left[\"high\"]\n        c = middle[\"high\"] - right[\"high\"]\n        return -(a + b + c) / middle[\"close\"]\n    if fenxing == \"tmp_di\":\n        a = abs(middle[\"low\"] - middle[\"close\"])\n        b = abs(middle[\"low\"] - left[\"low\"])\n        c = abs(middle[\"low\"] - right[\"low\"])\n        return (a + b + c) / middle[\"close\"]\n\n\ndef a_include_b(a: pd.Series, b: pd.Series) -> bool:\n    \"\"\"\n    kdata a includes kdata b\n\n    :param a:\n    :param b:\n    :return:\n    \"\"\"\n    return (a[\"high\"] >= b[\"high\"]) and (a[\"low\"] <= b[\"low\"])\n\n\ndef get_direction(kdata, pre_kdata, current=Direction.up) -> Direction:\n    if is_up(kdata, pre_kdata):\n        return Direction.up\n    if is_down(kdata, pre_kdata):\n        return Direction.down\n\n    return current\n\n\ndef is_up(kdata, pre_kdata):\n    return kdata[\"high\"] > pre_kdata[\"high\"]\n\n\ndef is_down(kdata, pre_kdata):\n    return kdata[\"low\"] < pre_kdata[\"low\"]\n\n\ndef handle_first_fenxing(one_df, step=11):\n    if step >= len(one_df):\n        logger.info(f\"coult not get fenxing by step {step}, len {len(one_df)}\")\n        return None, None, None, None\n\n    logger.info(f\"try to get first fenxing by step {step}\")\n\n    df = one_df.iloc[:step]\n    ding_kdata = df[df[\"high\"].max() == df[\"high\"]]\n    ding_index = int(ding_kdata.index[-1])\n\n    di_kdata = df[df[\"low\"].min() == df[\"low\"]]\n    di_index = int(di_kdata.index[-1])\n\n    # 确定第一个分型\n    if abs(ding_index - di_index) >= 4:\n        if ding_index > di_index:\n            fenxing = \"bi_di\"\n            fenxing_index = di_index\n            one_df.loc[di_index, \"bi_di\"] = True\n            # 确定第一个分型后，开始遍历的位置\n            start_index = ding_index\n            # 目前的笔的方向，up代表寻找 can_ding;down代表寻找can_di\n            direction = Direction.up\n            interval = ding_index - di_index\n        else:\n            fenxing = \"bi_ding\"\n            fenxing_index = ding_index\n            one_df.loc[ding_index, \"bi_ding\"] = True\n            start_index = di_index\n            direction = Direction.down\n            interval = di_index - ding_index\n        return (\n            Fenxing(\n                state=fenxing,\n                index=fenxing_index,\n                kdata={\n                    \"low\": float(one_df.loc[fenxing_index][\"low\"]),\n                    \"high\": float(one_df.loc[fenxing_index][\"high\"]),\n                },\n            ),\n            start_index,\n            direction,\n            interval,\n        )\n    else:\n        logger.info(\"need add step\")\n        return handle_first_fenxing(one_df, step=step + 1)\n\n\ndef handle_zhongshu(points: list, acc_df, end_index, zhongshu_col=\"zhongshu\", zhongshu_change_col=\"zhongshu_change\"):\n    zhongshu = None\n    zhongshu_change = None\n    interval = None\n\n    if len(points) == 4:\n        x1 = points[0][0]\n        x2 = points[3][0]\n\n        interval = points[3][2] - points[0][2]\n\n        if points[0][1] < points[1][1]:\n            # 向下段\n            range = intersect((points[0][1], points[1][1]), (points[2][1], points[3][1]))\n            if range:\n                y1, y2 = range\n                # 记录中枢\n                zhongshu = Rect(x0=x1, x1=x2, y0=y1, y1=y2)\n                zhongshu_change = abs(y1 - y2) / y1\n                acc_df.loc[end_index, zhongshu_col] = zhongshu\n                acc_df.loc[end_index, zhongshu_change_col] = zhongshu_change\n                points = points[-1:]\n            else:\n                points = points[1:]\n        else:\n            # 向上段\n            range = intersect((points[1][1], points[0][1]), (points[3][1], points[2][1]))\n            if range:\n                y1, y2 = range\n                # 记录中枢\n                zhongshu = Rect(x0=x1, x1=x2, y0=y1, y1=y2)\n                zhongshu_change = abs(y1 - y2) / y1\n\n                acc_df.loc[end_index, zhongshu_col] = zhongshu\n                acc_df.loc[end_index, zhongshu_change_col] = zhongshu_change\n                points = points[-1:]\n            else:\n                points = points[1:]\n    return points, zhongshu, zhongshu_change, interval\n\n\ndef handle_duan(fenxing_list: List[Fenxing], pre_duan_state=\"yi\"):\n    state = fenxing_list[0].state\n    # 1笔区间\n    bi1_start = fenxing_list[0].kdata\n    bi1_end = fenxing_list[1].kdata\n    # 3笔区间\n    bi3_start = fenxing_list[2].kdata\n    bi3_end = fenxing_list[3].kdata\n\n    if state == \"bi_ding\":\n        # 向下段,下-上-下\n\n        # 第一笔区间\n        range1 = (bi1_end[\"low\"], bi1_start[\"high\"])\n        # 第三笔区间\n        range3 = (bi3_end[\"low\"], bi3_start[\"high\"])\n\n        # 1,3有重叠，认为第一个段出现\n        if intersect(range1, range3):\n            return \"down\"\n\n    else:\n        # 向上段，上-下-上\n\n        # 第一笔区间\n        range1 = (bi1_start[\"low\"], bi1_end[\"high\"])\n        # 第三笔区间\n        range3 = (bi3_start[\"low\"], bi3_end[\"high\"])\n\n        # 1,3有重叠，认为第一个段出现\n        if intersect(range1, range3):\n            return \"up\"\n\n    return pre_duan_state\n\n\ndef handle_including(one_df, index, kdata, pre_index, pre_kdata, tmp_direction: Direction):\n    # 改kdata\n    if a_include_b(kdata, pre_kdata):\n        # 长的kdata变短\n        if tmp_direction == Direction.up:\n            one_df.loc[index, \"low\"] = pre_kdata[\"low\"]\n        else:\n            one_df.loc[index, \"high\"] = pre_kdata[\"high\"]\n    # 改pre_kdata\n    elif a_include_b(pre_kdata, kdata):\n        # 长的pre_kdata变短\n        if tmp_direction == Direction.down:\n            one_df.loc[pre_index, \"low\"] = kdata[\"low\"]\n        else:\n            one_df.loc[pre_index, \"high\"] = kdata[\"high\"]\n\n\nclass FactorStateEncoder(json.JSONEncoder):\n    def default(self, object):\n        if isinstance(object, pd.Series):\n            return object.to_dict()\n        elif isinstance(object, pd.Timestamp):\n            return to_date_time_str(object, fmt=TIME_FORMAT_ISO8601)\n        elif isinstance(object, Enum):\n            return object.value\n        elif isinstance(object, Bean):\n            return object.dict()\n        else:\n            return super().default(object)\n\n\ndef decode_rect(dct):\n    return Rect(x0=dct[\"x0\"], y0=dct[\"y0\"], x1=dct[\"x1\"], y1=dct[\"y1\"])\n\n\ndef decode_fenxing(dct):\n    return Fenxing(state=dct[\"state\"], kdata=dct[\"kdata\"], index=dct[\"index\"])\n\n\n# the __all__ is generated\n__all__ = [\n    \"Direction\",\n    \"Fenxing\",\n    \"fenxing_power\",\n    \"a_include_b\",\n    \"get_direction\",\n    \"is_up\",\n    \"is_down\",\n    \"handle_first_fenxing\",\n    \"handle_zhongshu\",\n    \"handle_duan\",\n    \"handle_including\",\n    \"FactorStateEncoder\",\n    \"decode_rect\",\n    \"decode_fenxing\",\n]\n"
  },
  {
    "path": "src/zvt/factors/target_selector.py",
    "content": "import operator\nfrom enum import Enum\nfrom itertools import accumulate\nfrom typing import List, Optional\n\nimport pandas as pd\nfrom pandas import DataFrame\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.factor import Factor\nfrom zvt.domain.meta.stock_meta import Stock\nfrom zvt.utils.pd_utils import index_df, pd_is_not_null, is_filter_result_df, is_score_result_df\nfrom zvt.utils.time_utils import to_pd_timestamp, now_pd_timestamp\n\n\nclass TradeType(Enum):\n    # open_long 代表开多，并应该平掉相应标的的空单\n    open_long = \"open_long\"\n    # open_short 代表开空，并应该平掉相应标的的多单\n    open_short = \"open_short\"\n    # keep 代表保持现状，跟主动开仓有区别，有时有仓位是可以保持的，但不适合开新的仓\n    keep = \"keep\"\n\n\nclass SelectMode(Enum):\n    condition_and = \"condition_and\"\n    condition_or = \"condition_or\"\n\n\nclass TargetSelector(object):\n    def __init__(\n        self,\n        entity_ids=None,\n        entity_schema=Stock,\n        exchanges=None,\n        codes=None,\n        start_timestamp=None,\n        end_timestamp=None,\n        long_threshold=0.8,\n        short_threshold=0.2,\n        level=IntervalLevel.LEVEL_1DAY,\n        provider=None,\n        select_mode: SelectMode = SelectMode.condition_and,\n    ) -> None:\n        self.entity_ids = entity_ids\n        self.entity_schema = entity_schema\n        self.exchanges = exchanges\n        self.codes = codes\n        self.provider = provider\n        self.select_mode = select_mode\n\n        if start_timestamp:\n            self.start_timestamp = to_pd_timestamp(start_timestamp)\n        if end_timestamp:\n            self.end_timestamp = to_pd_timestamp(end_timestamp)\n        else:\n            self.end_timestamp = now_pd_timestamp()\n\n        self.long_threshold = long_threshold\n        self.short_threshold = short_threshold\n        self.level = level\n\n        self.factors: List[Factor] = []\n        self.filter_result = None\n        self.score_result = None\n\n        self.open_long_df: Optional[DataFrame] = None\n        self.open_short_df: Optional[DataFrame] = None\n        self.keep_df: Optional[DataFrame] = None\n\n        self.init_factors(\n            entity_ids=entity_ids,\n            entity_schema=entity_schema,\n            exchanges=exchanges,\n            codes=codes,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            level=self.level,\n        )\n\n    def init_factors(self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, level):\n        pass\n\n    def add_factor(self, factor: Factor):\n        self.check_factor(factor)\n        self.factors.append(factor)\n        return self\n\n    def check_factor(self, factor: Factor):\n        assert factor.level == self.level\n\n    def move_on(self, to_timestamp=None, kdata_use_begin_time=False, timeout=20):\n        if self.factors:\n            for factor in self.factors:\n                factor.move_on(to_timestamp, timeout=timeout)\n\n        self.run()\n\n    def run(self):\n        \"\"\" \"\"\"\n        if self.factors:\n            filters = []\n            scores = []\n            for factor in self.factors:\n                if is_filter_result_df(factor.result_df):\n                    df = factor.result_df[[\"filter_result\"]]\n                    if pd_is_not_null(df):\n                        df.columns = [\"score\"]\n                        filters.append(df)\n                    else:\n                        raise Exception(\"no data for factor:{},{}\".format(factor.name, factor))\n                if is_score_result_df(factor.result_df):\n                    df = factor.result_df[[\"score_result\"]]\n                    if pd_is_not_null(df):\n                        df.columns = [\"score\"]\n                        scores.append(df)\n                    else:\n                        raise Exception(\"no data for factor:{},{}\".format(factor.name, factor))\n\n            if filters:\n                if self.select_mode == SelectMode.condition_and:\n                    self.filter_result = list(accumulate(filters, func=operator.__and__))[-1]\n                else:\n                    self.filter_result = list(accumulate(filters, func=operator.__or__))[-1]\n\n            if scores:\n                self.score_result = list(accumulate(scores, func=operator.__add__))[-1] / len(scores)\n\n        self.generate_targets()\n\n    def get_targets(self, timestamp, trade_type: TradeType = TradeType.open_long) -> List[str]:\n        if trade_type == TradeType.open_long:\n            df = self.open_long_df\n        elif trade_type == TradeType.open_short:\n            df = self.open_short_df\n        elif trade_type == TradeType.keep:\n            df = self.keep_df\n        else:\n            assert False\n\n        if pd_is_not_null(df):\n            if timestamp in df.index:\n                target_df = df.loc[[to_pd_timestamp(timestamp)], :]\n                return target_df[\"entity_id\"].tolist()\n        return []\n\n    def get_targets_between(\n        self, start_timestamp, end_timestamp, trade_type: TradeType = TradeType.open_long\n    ) -> List[str]:\n        if trade_type == TradeType.open_long:\n            df = self.open_long_df\n        elif trade_type == TradeType.open_short:\n            df = self.open_short_df\n        elif trade_type == TradeType.keep:\n            df = self.keep_df\n        else:\n            assert False\n\n        if pd_is_not_null(df):\n            index = pd.date_range(start_timestamp, end_timestamp, freq=self.level.to_pd_freq())\n            return list(set(df.loc[df.index & index][\"entity_id\"].tolist()))\n        return []\n\n    def get_open_long_targets(self, timestamp):\n        return self.get_targets(timestamp=timestamp, trade_type=TradeType.open_long)\n\n    def get_open_short_targets(self, timestamp):\n        return self.get_targets(timestamp=timestamp, trade_type=TradeType.open_short)\n\n    # overwrite it to generate targets\n    def generate_targets(self):\n        keep_result = pd.DataFrame()\n        long_result = pd.DataFrame()\n        short_result = pd.DataFrame()\n\n        if pd_is_not_null(self.filter_result):\n            keep_result = self.filter_result[self.filter_result[\"score\"].isna()]\n            long_result = self.filter_result[self.filter_result[\"score\"] == True]\n            short_result = self.filter_result[self.filter_result[\"score\"] == False]\n\n        if pd_is_not_null(self.score_result):\n            score_keep_result = self.score_result[\n                (self.score_result[\"score\"] > self.short_threshold) & (self.score_result[\"score\"] < self.long_threshold)\n            ]\n            if pd_is_not_null(keep_result):\n                keep_result = score_keep_result.loc[keep_result.index, :]\n            else:\n                keep_result = score_keep_result\n\n            score_long_result = self.score_result[self.score_result[\"score\"] >= self.long_threshold]\n            if pd_is_not_null(long_result):\n                long_result = score_long_result.loc[long_result.index, :]\n            else:\n                long_result = score_long_result\n\n            score_short_result = self.score_result[self.score_result[\"score\"] <= self.short_threshold]\n            if pd_is_not_null(short_result):\n                short_result = score_short_result.loc[short_result.index, :]\n            else:\n                short_result = score_short_result\n\n        self.keep_df = self.normalize_result_df(keep_result)\n        self.open_long_df = self.normalize_result_df(long_result)\n        self.open_short_df = self.normalize_result_df(short_result)\n\n    def get_result_df(self):\n        return self.open_long_df\n\n    def normalize_result_df(self, df):\n        if pd_is_not_null(df):\n            df = df.reset_index()\n            df = index_df(df)\n            df = df.sort_values(by=[\"score\", \"entity_id\"])\n        return df\n\n\n# the __all__ is generated\n__all__ = [\"TradeType\", \"SelectMode\", \"TargetSelector\"]\n"
  },
  {
    "path": "src/zvt/factors/technical_factor.py",
    "content": "from typing import List, Union, Type, Optional\n\nimport pandas as pd\n\nfrom zvt.api.kdata import get_kdata_schema, default_adjust_type\nfrom zvt.contract import IntervalLevel, TradableEntity, AdjustType\nfrom zvt.contract.factor import Factor, Transformer, Accumulator, FactorMeta\nfrom zvt.domain import Stock\n\n\nclass TechnicalFactor(Factor, metaclass=FactorMeta):\n    def __init__(\n        self,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        transformer: Transformer = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n        adjust_type: Union[AdjustType, str] = None,\n    ) -> None:\n        if columns is None:\n            columns = [\n                \"id\",\n                \"entity_id\",\n                \"timestamp\",\n                \"level\",\n                \"open\",\n                \"close\",\n                \"high\",\n                \"low\",\n                \"volume\",\n                \"turnover\",\n                \"turnover_rate\",\n            ]\n\n        # 股票默认使用后复权\n        if not adjust_type:\n            adjust_type = default_adjust_type(entity_type=entity_schema.__name__)\n\n        self.adjust_type = adjust_type\n        self.data_schema = get_kdata_schema(entity_schema.__name__, level=level, adjust_type=adjust_type)\n\n        if not factor_name:\n            if type(level) == str:\n                factor_name = f\"{type(self).__name__.lower()}_{level}\"\n            else:\n                factor_name = f\"{type(self).__name__.lower()}_{level.value}\"\n\n        super().__init__(\n            self.data_schema,\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n        )\n\n    def drawer_sub_df_list(self) -> Optional[List[pd.DataFrame]]:\n        return [self.factor_df[[\"volume\"]]]\n\n\n# the __all__ is generated\n__all__ = [\"TechnicalFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/top_stocks.py",
    "content": "# -*- coding: utf-8 -*-\nimport json\nimport logging\nfrom typing import List\n\nfrom sqlalchemy import Column, String, Integer\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.api.kdata import get_trade_dates, get_kdata_schema\nfrom zvt.api.selector import (\n    get_entity_ids_by_filter,\n    get_limit_up_stocks,\n    get_mini_and_small_stock,\n    get_middle_and_big_stock,\n    get_recent_active_stocks,\n    get_recent_trending_stocks,\n)\nfrom zvt.api.stats import get_top_performance_entities_by_periods, TopType\nfrom zvt.contract import Mixin, AdjustType\nfrom zvt.contract.api import get_db_session\nfrom zvt.contract.factor import TargetType\nfrom zvt.contract.register import register_schema\nfrom zvt.domain import Stock\nfrom zvt.factors.ma.ma_factor import VolumeUpMaFactor\nfrom zvt.utils.time_utils import (\n    date_time_by_interval,\n    to_pd_timestamp,\n    to_date_time_str,\n    TIME_FORMAT_DAY,\n    next_date,\n)\n\nlogger = logging.getLogger(__name__)\n\nTopStocksBase = declarative_base()\n\n\nclass TopStocks(TopStocksBase, Mixin):\n    __tablename__ = \"top_stocks\"\n    entity_type = Column(String(length=64))\n\n    short_count = Column(Integer)\n    short_stocks = Column(String(length=2048))\n\n    long_count = Column(Integer)\n    long_stocks = Column(String(length=2048))\n\n    small_vol_up_count = Column(Integer)\n    small_vol_up_stocks = Column(String(length=2048))\n\n    big_vol_up_count = Column(Integer)\n    big_vol_up_stocks = Column(String(length=2048))\n\n    all_stocks_count = Column(Integer)\n\n\nregister_schema(db_name=\"top_stocks\", schema_base=TopStocksBase, internal=True)\n\n\ndef compute_vol_up_stocks(target_date, provider=\"em\", adjust_type=AdjustType.qfq, stock_type=\"small\", entity_ids=None):\n    if stock_type == \"small\":\n        current_entity_pool = get_mini_and_small_stock(\n            timestamp=target_date, provider=provider, adjust_type=adjust_type\n        )\n        turnover_threshold = 500000000\n        turnover_rate_threshold = 0.02\n    elif stock_type == \"big\":\n        current_entity_pool = get_middle_and_big_stock(\n            timestamp=target_date, provider=provider, adjust_type=adjust_type\n        )\n        turnover_threshold = 500000000\n        turnover_rate_threshold = 0.01\n    else:\n        assert False\n\n    if entity_ids:\n        current_entity_pool = set(current_entity_pool) & set(entity_ids)\n\n    kdata_schema = get_kdata_schema(entity_type=\"stock\", level=\"1d\", adjust_type=adjust_type)\n    filters = [\n        kdata_schema.timestamp == to_pd_timestamp(target_date),\n        kdata_schema.turnover >= turnover_threshold,\n        kdata_schema.turnover_rate >= turnover_rate_threshold,\n    ]\n    kdata_df = kdata_schema.query_data(\n        provider=provider, filters=filters, columns=[\"entity_id\", \"timestamp\"], index=\"entity_id\"\n    )\n    if current_entity_pool:\n        current_entity_pool = set(current_entity_pool) & set(kdata_df.index.tolist())\n    else:\n        current_entity_pool = kdata_df.index.tolist()\n\n    factor = VolumeUpMaFactor(\n        entity_schema=Stock,\n        entity_provider=provider,\n        provider=provider,\n        entity_ids=current_entity_pool,\n        start_timestamp=date_time_by_interval(target_date, -600),\n        end_timestamp=target_date,\n        adjust_type=adjust_type,\n        windows=[120, 250],\n        over_mode=\"or\",\n        up_intervals=60,\n        turnover_threshold=turnover_threshold,\n        turnover_rate_threshold=turnover_rate_threshold,\n    )\n\n    stocks = factor.get_targets(timestamp=target_date, target_type=TargetType.positive)\n    return stocks\n\n\ndef compute_top_stocks(start_date, entity_type=\"stock\", provider=\"em\", force_update=False, adjust_type=AdjustType.qfq):\n    session = get_db_session(provider=\"zvt\", data_schema=TopStocks)\n    if force_update:\n        session.query(TopStocks).filter(TopStocks.timestamp >= start_date).filter(\n            TopStocks.entity_type == entity_type\n        ).delete()\n    else:\n        latest = TopStocks.query_data(\n            limit=1,\n            filters=[TopStocks.entity_type == entity_type],\n            order=TopStocks.timestamp.desc(),\n            return_type=\"domain\",\n        )\n        if latest:\n            start_date = next_date(the_time=to_date_time_str(latest[0].timestamp, fmt=TIME_FORMAT_DAY))\n\n    trade_days = get_trade_dates(entity_type=entity_type, start=start_date)\n\n    for target_date in trade_days:\n        top_stocks = TopStocks(\n            entity_type=entity_type,\n            entity_id=f\"{entity_type}_top\",\n            id=f\"{entity_type}_top_{target_date}\",\n            timestamp=target_date,\n        )\n\n        entity_ids = get_entity_ids_by_filter(\n            entity_type=entity_type,\n            target_date=target_date,\n            provider=\"em\",\n        )\n\n        turnover_threshold = 0\n        if entity_type == \"stockus\" or entity_type == \"stockhk\":\n            turnover_threshold = 10000000\n\n        short_selected, short_period = get_top_performance_entities_by_periods(\n            entity_provider=\"em\",\n            data_provider=provider,\n            target_date=target_date,\n            periods=[*range(1, 20)],\n            ignore_new_stock=False,\n            ignore_st=False,\n            entity_ids=entity_ids,\n            entity_type=entity_type,\n            adjust_type=adjust_type,\n            top_count=30,\n            turnover_threshold=turnover_threshold,\n            turnover_rate_threshold=0,\n            return_type=TopType.positive,\n        )\n        if entity_type == \"stock\":\n            limit_up_stocks = get_limit_up_stocks(timestamp=target_date)\n            logger.info(f\"limit_up_stocks got: {len(limit_up_stocks)}\")\n\n            recent_active_stocks = get_recent_active_stocks(adjust_type=adjust_type, provider=provider, recent_days=10)\n            logger.info(f\"recent_active_stocks got: {len(recent_active_stocks)}\")\n\n            recent_trending_stocks = get_recent_trending_stocks(\n                adjust_type=adjust_type, provider=provider, recent_days=20\n            )\n            logger.info(f\"recent_trending_stocks got: {len(recent_trending_stocks)}\")\n\n            short_selected = list(set(short_selected + limit_up_stocks + recent_active_stocks + recent_trending_stocks))\n        top_stocks.short_count = len(short_selected)\n        top_stocks.short_stocks = json.dumps(short_selected, ensure_ascii=False)\n\n        long_period_start = short_period + 1\n        long_selected, long_period = get_top_performance_entities_by_periods(\n            entity_provider=\"em\",\n            data_provider=provider,\n            target_date=target_date,\n            periods=[*range(long_period_start, long_period_start + 30)],\n            ignore_new_stock=False,\n            ignore_st=False,\n            entity_ids=entity_ids,\n            entity_type=entity_type,\n            adjust_type=adjust_type,\n            top_count=30,\n            turnover_threshold=turnover_threshold,\n            turnover_rate_threshold=0,\n            return_type=TopType.positive,\n        )\n        top_stocks.long_count = len(long_selected)\n        top_stocks.long_stocks = json.dumps(long_selected, ensure_ascii=False)\n\n        if entity_type == \"stock\":\n            small_vol_up_stocks = compute_vol_up_stocks(\n                target_date=target_date, provider=provider, stock_type=\"small\", entity_ids=entity_ids\n            )\n            top_stocks.small_vol_up_count = len(small_vol_up_stocks)\n            top_stocks.small_vol_up_stocks = json.dumps(small_vol_up_stocks, ensure_ascii=False)\n\n            big_vol_up_stocks = compute_vol_up_stocks(\n                target_date=target_date, provider=provider, stock_type=\"big\", entity_ids=entity_ids\n            )\n            top_stocks.big_vol_up_count = len(big_vol_up_stocks)\n            top_stocks.big_vol_up_stocks = json.dumps(big_vol_up_stocks, ensure_ascii=False)\n\n        top_stocks.all_stocks_count = len(entity_ids)\n\n        session.add(top_stocks)\n        session.commit()\n\n\ndef get_top_stocks(target_date, entity_type=\"stock\", return_type=\"short\"):\n    datas: List[TopStocks] = TopStocks.query_data(\n        filters=[TopStocks.entity_type == entity_type, TopStocks.timestamp == to_pd_timestamp(target_date)],\n        return_type=\"domain\",\n    )\n    stocks = []\n    if datas:\n        assert len(datas) == 1\n        top_stock = datas[0]\n        if return_type == \"all\":\n            short_stocks = json.loads(top_stock.short_stocks) if top_stock.short_stocks else []\n            long_stocks = json.loads(top_stock.long_stocks) if top_stock.long_stocks else []\n            small_vol_up_stocks = json.loads(top_stock.small_vol_up_stocks) if top_stock.small_vol_up_stocks else []\n            big_vol_up_stocks = json.loads(top_stock.big_vol_up_stocks) if top_stock.big_vol_up_stocks else []\n            all_stocks = list(set(short_stocks + long_stocks + small_vol_up_stocks + big_vol_up_stocks))\n            return all_stocks\n        elif return_type == \"short\":\n            stocks = json.loads(top_stock.short_stocks) if top_stock.short_stocks else []\n        elif return_type == \"long\":\n            stocks = json.loads(top_stock.long_stocks) if top_stock.long_stocks else []\n        elif return_type == \"small_vol_up\":\n            stocks = json.loads(top_stock.small_vol_up_stocks) if top_stock.small_vol_up_stocks else []\n        elif return_type == \"big_vol_up\":\n            stocks = json.loads(top_stock.big_vol_up_stocks) if top_stock.big_vol_up_stocks else []\n        else:\n            assert False\n    return stocks\n\n\nif __name__ == \"__main__\":\n    print(get_top_stocks(entity_type=\"stockus\", target_date=\"2025-07-25\", return_type=\"all\"))\n\n# the __all__ is generated\n__all__ = [\n    \"TopStocks\",\n    \"compute_vol_up_stocks\",\n    \"compute_top_stocks\",\n    \"get_top_stocks\",\n]\n"
  },
  {
    "path": "src/zvt/factors/transformers.py",
    "content": "# -*- coding: utf-8 -*-\nimport numpy as np\nimport pandas as pd\n\nfrom zvt.contract.factor import Transformer\nfrom zvt.factors.algorithm import MaTransformer\nfrom zvt.factors.technical_factor import TechnicalFactor\nfrom zvt.utils.pd_utils import group_by_entity_id, normalize_group_compute_result, merge_filter_result\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\ndef _cal_state(s, df, pre, interval, col):\n    assert len(s) == pre + interval\n    s = df.loc[s.index, :]\n    pre_df: pd.DataFrame = s.iloc[:pre, :]\n    recent_df: pd.DataFrame = s.iloc[-interval:, :]\n    if pre_df.isnull().values.any() or recent_df.isnull().values.any():\n        return np.nan\n    pre_result = np.logical_and.reduce(pre_df[\"close\"] > pre_df[col])\n    recent_result = np.logical_and.reduce(recent_df[\"close\"] < recent_df[col])\n    if pre_result and recent_result:\n        return True\n    return np.nan\n\n\nclass CrossMaTransformer(MaTransformer):\n    def __init__(self, windows=None, cal_change_pct=False) -> None:\n        super().__init__(windows, cal_change_pct)\n\n    def transform(self, input_df: pd.DataFrame) -> pd.DataFrame:\n        input_df = super().transform(input_df)\n        cols = [f\"ma{window}\" for window in self.windows]\n        s = input_df[cols[0]] > input_df[cols[1]]\n        current_col = cols[1]\n        for col in cols[2:]:\n            s = s & (input_df[current_col] > input_df[col])\n            current_col = col\n        input_df[\"filter_result\"] = s\n        return input_df\n\n\nclass SpecificTransformer(Transformer):\n    def __init__(self, buy_timestamp, sell_timestamp) -> None:\n        self.buy_timestamp = to_pd_timestamp(buy_timestamp)\n        self.sell_timestamp = to_pd_timestamp(sell_timestamp)\n\n    def transform(self, input_df: pd.DataFrame) -> pd.DataFrame:\n        s = input_df[input_df.get_level_values[\"timestamp\"] == self.buy_timestamp]\n        s[s == False] = None\n        s[input_df.get_level_values[\"timestamp\"] == self.sell_timestamp] = False\n        input_df[\"filter_result\"] = s\n        return input_df\n\n\nclass FallBelowTransformer(Transformer):\n    def __init__(self, window=10, interval=3) -> None:\n        super().__init__()\n        self.window = window\n        self.interval = interval\n\n    def transform(self, input_df: pd.DataFrame) -> pd.DataFrame:\n        col = f\"ma{self.window}\"\n        if col not in input_df.columns:\n            group_result = (\n                group_by_entity_id(input_df[\"close\"]).rolling(window=self.window, min_periods=self.window).mean()\n            )\n            group_result = normalize_group_compute_result(group_result=group_result)\n            input_df[col] = group_result\n\n        # 连续3(interval)日收在10(window)日线下\n        s = input_df[\"close\"] < input_df[col]\n        s = (\n            group_by_entity_id(s)\n            .rolling(window=self.interval, min_periods=self.interval)\n            .apply(lambda x: np.logical_and.reduce(x))\n        )\n        s = normalize_group_compute_result(group_result=s)\n        # 构造卖点\n        s[s == False] = None\n        s[s == True] = False\n        input_df = merge_filter_result(input_df=input_df, filter_result=s)\n\n        return input_df\n\n\nif __name__ == \"__main__\":\n    # df = Stock1dHfqKdata.query_data(codes=[\"000338\"], index=[\"entity_id\", \"timestamp\"])\n    # df = FallBelowTransformer().transform(df)\n    # print(df[\"filter_result\"])\n    TechnicalFactor(transformer=SpecificTransformer(timestamp=\"2020-03-01\"))\n\n\n# the __all__ is generated\n__all__ = [\"CrossMaTransformer\", \"SpecificTransformer\", \"FallBelowTransformer\"]\n"
  },
  {
    "path": "src/zvt/factors/zen/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule zen_factor\nfrom .zen_factor import *\nfrom .zen_factor import __all__ as _zen_factor_all\n\n__all__ += _zen_factor_all\n\n# import all from submodule base_factor\nfrom .base_factor import *\nfrom .base_factor import __all__ as _base_factor_all\n\n__all__ += _base_factor_all\n\n# import all from submodule domain\nfrom .domain import *\nfrom .domain import __all__ as _domain_all\n\n__all__ += _domain_all\n"
  },
  {
    "path": "src/zvt/factors/zen/base_factor.py",
    "content": "# -*- coding: utf-8 -*-\nimport json\nimport logging\nfrom enum import Enum\nfrom typing import List\nfrom typing import Union, Optional, Type\n\nimport numpy as np\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.contract import TradableEntity\nfrom zvt.contract.api import get_schema_by_name\nfrom zvt.contract.data_type import Bean\nfrom zvt.contract.drawer import Rect\nfrom zvt.contract.factor import Accumulator\nfrom zvt.contract.factor import Transformer\nfrom zvt.domain import Stock, Index, Index1dKdata\nfrom zvt.factors.algorithm import intersect, combine\nfrom zvt.factors.shape import (\n    Fenxing,\n    Direction,\n    handle_first_fenxing,\n    decode_rect,\n    get_direction,\n    handle_including,\n    fenxing_power,\n    handle_duan,\n)\nfrom zvt.factors.technical_factor import TechnicalFactor\nfrom zvt.utils.decorator import to_string\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import TIME_FORMAT_ISO8601, to_date_time_str\n\nlogger = logging.getLogger(__name__)\n\n\nclass FactorStateEncoder(json.JSONEncoder):\n    def default(self, object):\n        if isinstance(object, pd.Series):\n            return object.to_dict()\n        elif isinstance(object, pd.Timestamp):\n            return to_date_time_str(object, fmt=TIME_FORMAT_ISO8601)\n        elif isinstance(object, Enum):\n            return object.value\n        elif isinstance(object, Bean):\n            return object.dict()\n        else:\n            return super().default(object)\n\n\ndef get_zen_factor_schema(entity_type: str, level: Union[IntervalLevel, str] = IntervalLevel.LEVEL_1DAY):\n    if type(level) == str:\n        level = IntervalLevel(level)\n\n    # z factor schema rule\n    # 1)name:{SecurityType.value.capitalize()}{IntervalLevel.value.upper()}ZFactor\n    schema_str = \"{}{}ZenFactor\".format(entity_type.capitalize(), level.value.capitalize())\n\n    return get_schema_by_name(schema_str)\n\n\n@to_string\nclass ZenState(Bean):\n    def __init__(self, state: dict = None) -> None:\n        super().__init__()\n\n        if not state:\n            state = dict()\n\n        # 用于计算未完成段的分型\n        self.fenxing_list = state.get(\"fenxing_list\", [])\n        fenxing_list = [Fenxing(item[\"state\"], item[\"kdata\"], item[\"index\"]) for item in self.fenxing_list]\n        self.fenxing_list = fenxing_list\n\n        # 目前的方向\n        if state.get(\"direction\"):\n            self.direction = Direction(state.get(\"direction\"))\n        else:\n            self.direction = None\n\n        # 候选分型(candidate)\n        self.can_fenxing = state.get(\"can_fenxing\")\n        self.can_fenxing_index = state.get(\"can_fenxing_index\")\n        # 反方向count\n        self.opposite_count = state.get(\"opposite_count\", 0)\n        # 目前段的方向\n        self.current_duan_state = state.get(\"current_duan_state\", \"yi\")\n\n        # 记录用于计算中枢的段\n        # list of (timestamp,value)\n        self.duans = state.get(\"duans\", [])\n        self.bis = state.get(\"bis\", [])\n\n        # 前一个点\n        self.pre_bi = state.get(\"pre_bi\")\n        self.pre_duan = state.get(\"pre_duan\")\n\n        # 目前的merge_zhongshu\n        self.merge_zhongshu = state.get(\"merge_zhongshu\")\n        self.merge_zhongshu_level = state.get(\"merge_zhongshu_level\")\n        self.merge_zhongshu_interval = state.get(\"merge_zhongshu_interval\")\n\n\ndef handle_zhongshu(\n    points: list,\n    acc_df,\n    end_index,\n    zhongshu_col=\"zhongshu\",\n    zhongshu_change_col=\"zhongshu_change\",\n):\n    zhongshu = None\n    zhongshu_change = None\n    interval = None\n\n    if len(points) == 4:\n        x1 = points[0][0]\n        x2 = points[3][0]\n\n        interval = points[3][2] - points[0][2]\n\n        if points[0][1] < points[1][1]:\n            # 向下段\n            range = intersect((points[0][1], points[1][1]), (points[2][1], points[3][1]))\n            if range:\n                y1, y2 = range\n                # 记录中枢\n                zhongshu = Rect(x0=x1, x1=x2, y0=y1, y1=y2)\n                zhongshu_change = abs(y1 - y2) / abs(y1)\n                acc_df.loc[end_index, zhongshu_col] = zhongshu\n                acc_df.loc[end_index, zhongshu_change_col] = zhongshu_change\n                points = points[-1:]\n            else:\n                points = points[1:]\n        else:\n            # 向上段\n            range = intersect((points[1][1], points[0][1]), (points[3][1], points[2][1]))\n            if range:\n                y1, y2 = range\n                # 记录中枢\n                zhongshu = Rect(x0=x1, x1=x2, y0=y1, y1=y2)\n                zhongshu_change = abs(y1 - y2) / abs(y1)\n\n                acc_df.loc[end_index, zhongshu_col] = zhongshu\n                acc_df.loc[end_index, zhongshu_change_col] = zhongshu_change\n                points = points[-1:]\n            else:\n                points = points[1:]\n    return points, zhongshu, zhongshu_change, interval\n\n\nclass ZenAccumulator(Accumulator):\n    def __init__(self, acc_window: int = 1) -> None:\n        \"\"\"\n        算法和概念\n        <实体> 某种状态的k线\n        [实体] 连续实体排列\n\n        两k线的关系有三种: 上涨，下跌，包含\n        上涨: k线高点比之前高，低点比之前高\n        下跌: k线低点比之前低，高点比之前低\n        包含: k线高点比之前高，低点比之前低;反方向，即被包含\n        处理包含关系，长的k线缩短，上涨时，低点取max(low1,low2)；下跌时，高点取min(high1,high2)\n\n        第一个顶(底)分型: 出现连续4根下跌(上涨)k线\n        之后开始寻找 候选底(顶)分型，寻找的过程中有以下状态\n\n        <临时顶>: 中间k线比两边的高点高,是一条特定的k线\n        <临时底>: 中间k线比两边的高点高,是一条特定的k线\n\n        <候选顶分型>: 连续的<临时顶>取最大\n        <候选底分型>:  连续的<临时底>取最小\n        任何时刻只能有一个候选，其之前是一个确定的分型\n\n        <上升k线>:\n        <下降k线>:\n        <连接k线>: 分型之间的k线都可以认为是连接k线，以上为演化过程的中间态\n        distance(<候选顶分型>, <连接k线>)>=4 则 <候选顶分型> 变成顶分型\n        distance(<候选底分型>, <连接k线>)>=4 则 <候选底分型> 变成底分型\n\n        <顶分型><连接k线><候选底分型>\n        <底分型><连接k线><候选顶分型>\n        \"\"\"\n        super().__init__(acc_window)\n\n    def acc_one(self, entity_id, df: pd.DataFrame, acc_df: pd.DataFrame, state: dict) -> (pd.DataFrame, dict):\n        self.logger.info(f\"acc_one:{entity_id}\")\n        if pd_is_not_null(acc_df):\n            df = df[df.index > acc_df.index[-1]]\n            if pd_is_not_null(df):\n                self.logger.info(f'compute from {df.iloc[0][\"timestamp\"]}')\n                # 遍历的开始位置\n                start_index = len(acc_df)\n\n                acc_df = pd.concat([acc_df, df])\n\n                zen_state = ZenState(state)\n\n                acc_df = acc_df.reset_index(drop=True)\n                current_interval = acc_df.iloc[start_index - 1][\"current_interval\"]\n            else:\n                self.logger.info(\"no need to compute\")\n                return acc_df, state\n        else:\n            acc_df = df\n            # 笔的底\n            acc_df[\"bi_di\"] = False\n            # 笔的顶\n            acc_df[\"bi_ding\"] = False\n            # 记录笔顶/底分型的值，bi_di取low,bi_ding取high,其他为None,绘图时取有值的连线即为 笔\n            acc_df[\"bi_value\"] = np.nan\n            # 笔的变化\n            acc_df[\"bi_change\"] = np.nan\n            # 笔的斜率\n            acc_df[\"bi_slope\"] = np.nan\n            # 持续的周期\n            acc_df[\"bi_interval\"] = np.nan\n\n            # 记录临时分型，不变\n            acc_df[\"tmp_ding\"] = False\n            acc_df[\"tmp_di\"] = False\n            # 分型的力度\n            acc_df[\"fenxing_power\"] = np.nan\n\n            # 目前分型确定的方向\n            acc_df[\"current_direction\"] = None\n            acc_df[\"current_change\"] = np.nan\n            acc_df[\"current_interval\"] = np.nan\n            acc_df[\"current_slope\"] = np.nan\n            # 最近的一个笔中枢\n            # acc_df['current_zhongshu'] = np.nan\n            acc_df[\"current_zhongshu_change\"] = np.nan\n            acc_df[\"current_zhongshu_y0\"] = np.nan\n            acc_df[\"current_zhongshu_y1\"] = np.nan\n\n            acc_df[\"current_merge_zhongshu_change\"] = np.nan\n            acc_df[\"current_merge_zhongshu_y0\"] = np.nan\n            acc_df[\"current_merge_zhongshu_y1\"] = np.nan\n            acc_df[\"current_merge_zhongshu_level\"] = np.nan\n            acc_df[\"current_merge_zhongshu_interval\"] = np.nan\n\n            # 目前走势的临时方向 其跟direction的的关系 确定了下一个分型\n            acc_df[\"tmp_direction\"] = None\n            acc_df[\"opposite_change\"] = np.nan\n            acc_df[\"opposite_interval\"] = np.nan\n            acc_df[\"opposite_slope\"] = np.nan\n\n            acc_df[\"duan_state\"] = \"yi\"\n\n            # 段的底\n            acc_df[\"duan_di\"] = False\n            # 段的顶\n            acc_df[\"duan_ding\"] = False\n            # 记录段顶/底的值，为duan_di时取low,为duan_ding时取high,其他为None,绘图时取有值的连线即为 段\n            acc_df[\"duan_value\"] = np.nan\n            # 段的变化\n            acc_df[\"duan_change\"] = np.nan\n            # 段的斜率\n            acc_df[\"duan_slope\"] = np.nan\n            # 持续的周期\n            acc_df[\"duan_interval\"] = np.nan\n\n            # 记录在确定中枢的最后一个段的终点x1，值为Rect(x0,y0,x1,y1)\n            acc_df[\"zhongshu\"] = None\n            acc_df[\"zhongshu_change\"] = np.nan\n\n            acc_df[\"bi_zhongshu\"] = None\n            acc_df[\"bi_zhongshu_change\"] = np.nan\n\n            acc_df[\"merge_zhongshu\"] = None\n            acc_df[\"merge_zhongshu_change\"] = np.nan\n            acc_df[\"merge_zhongshu_level\"] = np.nan\n            acc_df[\"merge_zhongshu_interval\"] = np.nan\n\n            acc_df = acc_df.reset_index(drop=True)\n\n            zen_state = ZenState(\n                dict(\n                    fenxing_list=[],\n                    direction=None,\n                    can_fenxing=None,\n                    can_fenxing_index=None,\n                    opposite_count=0,\n                    current_duan_state=\"yi\",\n                    duans=[],\n                    pre_bi=None,\n                    pre_duan=None,\n                    merge_zhongshu=None,\n                )\n            )\n\n            zen_state.fenxing_list: List[Fenxing] = []\n\n            # 取前11条k线，至多出现一个顶分型+底分型\n            # 注:只是一种方便的确定第一个分型的办法，有了第一个分型，后面的处理就比较统一\n            # start_index 为遍历开始的位置\n            # direction为一个确定分型后的方向，即顶分型后为:down，底分型后为:up\n            fenxing, start_index, direction, current_interval = handle_first_fenxing(acc_df, step=11)\n            if not fenxing:\n                return None, None\n\n            zen_state.fenxing_list.append(fenxing)\n            zen_state.direction = direction\n\n            # list of (timestamp,value)\n            zen_state.duans = []\n            zen_state.bis = []\n\n        pre_kdata = acc_df.iloc[start_index - 1]\n        pre_index = start_index - 1\n\n        tmp_direction = zen_state.direction\n        current_merge_zhongshu = decode_rect(zen_state.merge_zhongshu) if zen_state.merge_zhongshu else None\n        current_merge_zhongshu_change = None\n        current_merge_zhongshu_interval = zen_state.merge_zhongshu_interval\n        current_merge_zhongshu_level = zen_state.merge_zhongshu_level\n\n        current_zhongshu = None\n        current_zhongshu_change = None\n        for index, kdata in acc_df.iloc[start_index:].iterrows():\n            # print(f'timestamp: {kdata.timestamp}')\n            # 临时方向\n            tmp_direction = get_direction(kdata, pre_kdata, current=tmp_direction)\n\n            # current states\n            current_interval = current_interval + 1\n            if zen_state.direction == Direction.up:\n                pre_value = acc_df.loc[zen_state.fenxing_list[0].index, \"low\"]\n                current_value = kdata[\"high\"]\n            else:\n                pre_value = acc_df.loc[zen_state.fenxing_list[0].index, \"high\"]\n                current_value = kdata[\"low\"]\n            acc_df.loc[index, \"current_direction\"] = zen_state.direction.value\n            acc_df.loc[index, \"current_interval\"] = current_interval\n            change = (current_value - pre_value) / abs(pre_value)\n            acc_df.loc[index, \"current_change\"] = change\n            acc_df.loc[index, \"current_slope\"] = change / current_interval\n            if current_zhongshu:\n                # acc_df.loc[index, 'current_zhongshu'] = current_zhongshu\n                acc_df.loc[index, \"current_zhongshu_y0\"] = current_zhongshu.y0\n                acc_df.loc[index, \"current_zhongshu_y1\"] = current_zhongshu.y1\n                acc_df.loc[index, \"current_zhongshu_change\"] = current_zhongshu_change\n            else:\n                # acc_df.loc[index, 'current_zhongshu'] = acc_df.loc[index - 1, 'current_zhongshu']\n                acc_df.loc[index, \"current_zhongshu_y0\"] = acc_df.loc[index - 1, \"current_zhongshu_y0\"]\n                acc_df.loc[index, \"current_zhongshu_y1\"] = acc_df.loc[index - 1, \"current_zhongshu_y1\"]\n                acc_df.loc[index, \"current_zhongshu_change\"] = acc_df.loc[index - 1, \"current_zhongshu_change\"]\n\n            if current_merge_zhongshu:\n                # acc_df.loc[index, 'current_merge_zhongshu'] = current_merge_zhongshu\n                acc_df.loc[index, \"current_merge_zhongshu_y0\"] = current_merge_zhongshu.y0\n                acc_df.loc[index, \"current_merge_zhongshu_y1\"] = current_merge_zhongshu.y1\n                acc_df.loc[index, \"current_merge_zhongshu_change\"] = current_merge_zhongshu_change\n                acc_df.loc[index, \"current_merge_zhongshu_level\"] = current_merge_zhongshu_level\n                acc_df.loc[index, \"current_merge_zhongshu_interval\"] = current_merge_zhongshu_interval\n            else:\n                # acc_df.loc[index, 'current_merge_zhongshu'] = acc_df.loc[index - 1, 'current_merge_zhongshu']\n                acc_df.loc[index, \"current_merge_zhongshu_y0\"] = acc_df.loc[index - 1, \"current_merge_zhongshu_y0\"]\n                acc_df.loc[index, \"current_merge_zhongshu_y1\"] = acc_df.loc[index - 1, \"current_merge_zhongshu_y1\"]\n                acc_df.loc[index, \"current_merge_zhongshu_change\"] = acc_df.loc[\n                    index - 1, \"current_merge_zhongshu_change\"\n                ]\n                acc_df.loc[index, \"current_merge_zhongshu_level\"] = acc_df.loc[\n                    index - 1, \"current_merge_zhongshu_level\"\n                ]\n                acc_df.loc[index, \"current_merge_zhongshu_interval\"] = acc_df.loc[\n                    index - 1, \"current_merge_zhongshu_interval\"\n                ]\n\n            # 处理包含关系\n            handle_including(\n                one_df=acc_df,\n                index=index,\n                kdata=kdata,\n                pre_index=pre_index,\n                pre_kdata=pre_kdata,\n                tmp_direction=tmp_direction,\n            )\n\n            # 根据方向，寻找对应的分型 和 段\n            if zen_state.direction == Direction.up:\n                tmp_fenxing_col = \"tmp_ding\"\n                fenxing_col = \"bi_ding\"\n            else:\n                tmp_fenxing_col = \"tmp_di\"\n                fenxing_col = \"bi_di\"\n\n            # 方向一致，延续中\n            if tmp_direction == zen_state.direction:\n                zen_state.opposite_count = 0\n            # 反向，寻找反 分型\n            else:\n                zen_state.opposite_count = zen_state.opposite_count + 1\n\n                # opposite states\n                current_interval = zen_state.opposite_count\n                if tmp_direction == Direction.up:\n                    pre_value = acc_df.loc[index - zen_state.opposite_count, \"low\"]\n                    current_value = kdata[\"high\"]\n                else:\n                    pre_value = acc_df.loc[index - zen_state.opposite_count, \"high\"]\n                    current_value = kdata[\"low\"]\n                acc_df.loc[index, \"tmp_direction\"] = tmp_direction.value\n                acc_df.loc[index, \"opposite_interval\"] = current_interval\n                change = (current_value - pre_value) / abs(pre_value)\n                acc_df.loc[index, \"opposite_change\"] = change\n                acc_df.loc[index, \"opposite_slope\"] = change / current_interval\n\n                # 第一次反向\n                if zen_state.opposite_count == 1:\n                    acc_df.loc[pre_index, tmp_fenxing_col] = True\n                    acc_df.loc[pre_index, \"fenxing_power\"] = fenxing_power(\n                        acc_df.loc[pre_index - 1],\n                        pre_kdata,\n                        kdata,\n                        fenxing=tmp_fenxing_col,\n                    )\n\n                    if zen_state.can_fenxing is not None:\n                        # 候选底分型\n                        if tmp_direction == Direction.up:\n                            # 取小的\n                            if pre_kdata[\"low\"] <= zen_state.can_fenxing[\"low\"]:\n                                zen_state.can_fenxing = pre_kdata[[\"low\", \"high\"]]\n                                zen_state.can_fenxing_index = pre_index\n\n                        # 候选顶分型\n                        else:\n                            # 取大的\n                            if pre_kdata[\"high\"] >= zen_state.can_fenxing[\"high\"]:\n                                zen_state.can_fenxing = pre_kdata[[\"low\", \"high\"]]\n                                zen_state.can_fenxing_index = pre_index\n                    else:\n                        zen_state.can_fenxing = pre_kdata[[\"low\", \"high\"]]\n                        zen_state.can_fenxing_index = pre_index\n\n                # 分型确立\n                if zen_state.can_fenxing is not None:\n                    if zen_state.opposite_count >= 4 or (index - zen_state.can_fenxing_index >= 8):\n                        acc_df.loc[zen_state.can_fenxing_index, fenxing_col] = True\n\n                        # 记录笔的值\n                        if fenxing_col == \"bi_ding\":\n                            bi_value = acc_df.loc[zen_state.can_fenxing_index, \"high\"]\n                        else:\n                            bi_value = acc_df.loc[zen_state.can_fenxing_index, \"low\"]\n                        acc_df.loc[zen_state.can_fenxing_index, \"bi_value\"] = bi_value\n\n                        # 计算笔斜率\n                        if zen_state.pre_bi:\n                            change = (bi_value - zen_state.pre_bi[1]) / abs(zen_state.pre_bi[1])\n                            interval = zen_state.can_fenxing_index - zen_state.pre_bi[0]\n                            bi_slope = change / interval\n                            acc_df.loc[zen_state.can_fenxing_index, \"bi_change\"] = change\n                            acc_df.loc[zen_state.can_fenxing_index, \"bi_slope\"] = bi_slope\n                            acc_df.loc[zen_state.can_fenxing_index, \"bi_interval\"] = interval\n\n                        # 记录用于计算笔中枢的笔\n                        zen_state.bis.append(\n                            (\n                                acc_df.loc[zen_state.can_fenxing_index, \"timestamp\"],\n                                bi_value,\n                                zen_state.can_fenxing_index,\n                            )\n                        )\n\n                        # 计算笔中枢，当下来说这个 中枢 是确定的，并且是不可变的\n                        # 但标记的点为 过去，注意在回测时最近的一个中枢可能用到未来函数，前一个才是 已知的\n                        # 所以记了一个 current_zhongshu_y0 current_zhongshu_y1 这个是可直接使用的\n                        end_index = zen_state.can_fenxing_index\n\n                        (\n                            zen_state.bis,\n                            current_zhongshu,\n                            current_zhongshu_change,\n                            current_zhongshu_interval,\n                        ) = handle_zhongshu(\n                            points=zen_state.bis,\n                            acc_df=acc_df,\n                            end_index=end_index,\n                            zhongshu_col=\"bi_zhongshu\",\n                            zhongshu_change_col=\"bi_zhongshu_change\",\n                        )\n\n                        if not current_merge_zhongshu:\n                            current_merge_zhongshu = current_zhongshu\n                            current_merge_zhongshu_change = current_zhongshu_change\n                            current_merge_zhongshu_level = 1\n                            current_merge_zhongshu_interval = current_zhongshu_interval\n                        else:\n                            if current_zhongshu:\n                                range_a = (\n                                    current_merge_zhongshu.y0,\n                                    current_merge_zhongshu.y1,\n                                )\n                                range_b = (current_zhongshu.y0, current_zhongshu.y1)\n                                combine_range = combine(range_a, range_b)\n                                if combine_range:\n                                    y0 = combine_range[0]\n                                    y1 = combine_range[1]\n                                    current_merge_zhongshu = Rect(\n                                        x0=current_merge_zhongshu.x0,\n                                        x1=current_zhongshu.x1,\n                                        y0=y0,\n                                        y1=y1,\n                                    )\n                                    current_merge_zhongshu_change = abs(y0 - y1) / abs(y0)\n                                    current_merge_zhongshu_level = current_merge_zhongshu_level + 1\n                                    current_merge_zhongshu_interval = (\n                                        current_merge_zhongshu_interval + current_zhongshu_interval\n                                    )\n                                else:\n                                    current_merge_zhongshu = current_zhongshu\n                                    current_merge_zhongshu_change = current_zhongshu_change\n                                    current_merge_zhongshu_level = 1\n                                    current_merge_zhongshu_interval = current_zhongshu_interval\n\n                                acc_df.loc[end_index, \"merge_zhongshu\"] = current_merge_zhongshu\n                                acc_df.loc[end_index, \"merge_zhongshu_change\"] = current_merge_zhongshu_change\n                                acc_df.loc[end_index, \"merge_zhongshu_level\"] = current_merge_zhongshu_level\n                                acc_df.loc[end_index, \"merge_zhongshu_interval\"] = current_merge_zhongshu_interval\n\n                        zen_state.merge_zhongshu = current_merge_zhongshu\n                        zen_state.merge_zhongshu_interval = current_merge_zhongshu_interval\n                        zen_state.merge_zhongshu_level = current_merge_zhongshu_level\n\n                        zen_state.pre_bi = (zen_state.can_fenxing_index, bi_value)\n\n                        zen_state.opposite_count = 0\n                        zen_state.direction = zen_state.direction.opposite()\n                        zen_state.can_fenxing = None\n\n                        # 确定第一个段\n                        if zen_state.fenxing_list != None:\n                            zen_state.fenxing_list.append(\n                                Fenxing(\n                                    state=fenxing_col,\n                                    kdata={\n                                        \"low\": float(acc_df.loc[zen_state.can_fenxing_index][\"low\"]),\n                                        \"high\": float(acc_df.loc[zen_state.can_fenxing_index][\"high\"]),\n                                    },\n                                    index=zen_state.can_fenxing_index,\n                                )\n                            )\n\n                            if len(zen_state.fenxing_list) == 4:\n                                duan_state = handle_duan(\n                                    fenxing_list=zen_state.fenxing_list,\n                                    pre_duan_state=zen_state.current_duan_state,\n                                )\n\n                                change = duan_state != zen_state.current_duan_state\n\n                                if change:\n                                    zen_state.current_duan_state = duan_state\n\n                                    # 确定状态\n                                    acc_df.loc[\n                                        zen_state.fenxing_list[0].index : zen_state.fenxing_list[-1].index,\n                                        \"duan_state\",\n                                    ] = zen_state.current_duan_state\n\n                                    duan_index = zen_state.fenxing_list[0].index\n                                    if zen_state.current_duan_state == \"up\":\n                                        acc_df.loc[duan_index, \"duan_di\"] = True\n                                        duan_value = acc_df.loc[duan_index, \"low\"]\n                                    else:\n                                        duan_index = zen_state.fenxing_list[0].index\n                                        acc_df.loc[duan_index, \"duan_ding\"] = True\n                                        duan_value = acc_df.loc[duan_index, \"high\"]\n                                    # 记录段的值\n                                    acc_df.loc[duan_index, \"duan_value\"] = duan_value\n\n                                    # 计算段斜率\n                                    if zen_state.pre_duan:\n                                        change = (duan_value - zen_state.pre_duan[1]) / abs(zen_state.pre_duan[1])\n                                        interval = duan_index - zen_state.pre_duan[0]\n                                        duan_slope = change / interval\n                                        acc_df.loc[duan_index, \"duan_change\"] = change\n                                        acc_df.loc[duan_index, \"duan_slope\"] = duan_slope\n                                        acc_df.loc[duan_index, \"duan_interval\"] = interval\n\n                                    zen_state.pre_duan = (duan_index, duan_value)\n\n                                    # 记录用于计算中枢的段\n                                    zen_state.duans.append(\n                                        (\n                                            acc_df.loc[duan_index, \"timestamp\"],\n                                            duan_value,\n                                            duan_index,\n                                        )\n                                    )\n\n                                    # 计算中枢\n                                    zen_state.duans, _, _, _ = handle_zhongshu(\n                                        points=zen_state.duans,\n                                        acc_df=acc_df,\n                                        end_index=duan_index,\n                                        zhongshu_col=\"zhongshu\",\n                                        zhongshu_change_col=\"zhongshu_change\",\n                                    )\n\n                                    # 只留最后一个\n                                    zen_state.fenxing_list = zen_state.fenxing_list[-1:]\n                                else:\n                                    # 保持之前的状态并踢出候选\n                                    acc_df.loc[zen_state.fenxing_list[0].index, \"duan_state\"] = (\n                                        zen_state.current_duan_state\n                                    )\n                                    zen_state.fenxing_list = zen_state.fenxing_list[1:]\n\n            pre_kdata = kdata\n            pre_index = index\n\n        acc_df = acc_df.set_index(\"timestamp\", drop=False)\n        return acc_df, zen_state\n\n\nclass ZenFactor(TechnicalFactor):\n    accumulator = ZenAccumulator()\n\n    def __init__(\n        self,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        transformer: Transformer = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = False,\n        adjust_type: Union[AdjustType, str] = None,\n    ) -> None:\n        self.factor_schema = get_zen_factor_schema(entity_type=entity_schema.__name__, level=level)\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n        )\n\n    def factor_col_map_object_hook(self) -> dict:\n        return {\n            \"zhongshu\": decode_rect,\n            \"bi_zhongshu\": decode_rect,\n            \"merge_zhongshu\": decode_rect,\n        }\n\n    def state_encoder(self):\n        return FactorStateEncoder\n\n    def drawer_factor_df_list(self) -> Optional[List[pd.DataFrame]]:\n        bi_value = self.factor_df[[\"bi_value\"]].dropna()\n        # duan_value = self.factor_df[['duan_value']].dropna()\n        return [bi_value]\n\n    def drawer_rects(self) -> List[Rect]:\n        df1 = self.factor_df[[\"merge_zhongshu\"]].dropna()\n        return df1[\"merge_zhongshu\"].tolist()\n\n    def drawer_sub_df_list(self) -> Optional[List[pd.DataFrame]]:\n        # bi_slope = self.factor_df[['bi_slope']].dropna()\n        # duan_slope = self.factor_df[['duan_slope']].dropna()\n        # power = self.factor_df[['fenxing_power']].dropna()\n        # zhongshu_change = self.factor_df[['zhongshu_change']].dropna()\n        # return [bi_slope, duan_slope, power, zhongshu_change]\n        # change1 = self.factor_df[['current_merge_zhongshu_level']].dropna()\n        # change2 = self.factor_df[['opposite_change']].dropna()\n        current_slope = self.factor_df[[\"current_slope\"]].dropna()\n        return [current_slope]\n\n\nif __name__ == \"__main__\":\n    entity_ids = [\"index_sh_000001\"]\n    Index1dKdata.record_data(entity_ids=entity_ids)\n\n    f = ZenFactor(\n        entity_schema=Index,\n        entity_ids=entity_ids,\n        need_persist=False,\n        provider=\"em\",\n        entity_provider=\"exchange\",\n    )\n    f.draw(show=True)\n\n\n# the __all__ is generated\n__all__ = [\"FactorStateEncoder\", \"get_zen_factor_schema\", \"ZenState\", \"handle_zhongshu\", \"ZenAccumulator\", \"ZenFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/zen/domain/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule index_1d_zen_factor\nfrom .index_1d_zen_factor import *\nfrom .index_1d_zen_factor import __all__ as _index_1d_zen_factor_all\n\n__all__ += _index_1d_zen_factor_all\n\n# import all from submodule stock_1wk_zen_factor\nfrom .stock_1wk_zen_factor import *\nfrom .stock_1wk_zen_factor import __all__ as _stock_1wk_zen_factor_all\n\n__all__ += _stock_1wk_zen_factor_all\n\n# import all from submodule common\nfrom .common import *\nfrom .common import __all__ as _common_all\n\n__all__ += _common_all\n\n# import all from submodule stock_1d_zen_factor\nfrom .stock_1d_zen_factor import *\nfrom .stock_1d_zen_factor import __all__ as _stock_1d_zen_factor_all\n\n__all__ += _stock_1d_zen_factor_all\n"
  },
  {
    "path": "src/zvt/factors/zen/domain/common.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, Float, String, Boolean, Integer\n\nfrom zvt.contract import Mixin\n\n\nclass ZenFactorCommon(Mixin):\n    level = Column(String(length=32))\n    # 开盘价\n    open = Column(Float)\n    # 收盘价\n    close = Column(Float)\n    # 最高价\n    high = Column(Float)\n    # 最低价\n    low = Column(Float)\n    # 成交量\n    volume = Column(Float)\n    # 成交金额\n    turnover = Column(Float)\n\n    # 笔的底\n    bi_di = Column(Boolean)\n    # 笔的顶\n    bi_ding = Column(Boolean)\n    # 记录笔顶/底分型的值，bi_di取low,bi_ding取high,其他为None,绘图时取有值的连线即为 笔\n    bi_value = Column(Float)\n    # 笔的变化\n    bi_change = Column(Float)\n    # 笔的斜率\n    bi_slope = Column(Float)\n    # 持续的周期\n    bi_interval = Column(Integer)\n\n    # 记录临时分型，不变\n    tmp_ding = Column(Boolean)\n    tmp_di = Column(Boolean)\n    # 分型的力度\n    fenxing_power = Column(Float)\n\n    # 目前分型确定的方向\n    current_direction = Column(String(length=16))\n    current_change = Column(Float)\n    current_interval = Column(Integer)\n    current_slope = Column(Float)\n    # 最近的一个笔中枢\n    # current_zhongshu = Column(String(length=512))\n    current_zhongshu_y0 = Column(Float)\n    current_zhongshu_y1 = Column(Float)\n    current_zhongshu_change = Column(Float)\n\n    current_merge_zhongshu_y0 = Column(Float)\n    current_merge_zhongshu_y1 = Column(Float)\n    current_merge_zhongshu_change = Column(Float)\n    current_merge_zhongshu_level = Column(Integer)\n    current_merge_zhongshu_interval = Column(Integer)\n\n    # 目前走势的临时方向 其跟direction的的关系 确定了下一个分型\n    tmp_direction = Column(String(length=16))\n    # 已经确定分型，目前反向才有值\n    opposite_change = Column(Float)\n    opposite_slope = Column(Float)\n    opposite_interval = Column(Integer)\n\n    duan_state = Column(String(length=32))\n\n    # 段的底\n    duan_di = Column(Boolean)\n    # 段的顶\n    duan_ding = Column(Boolean)\n    # 记录段顶/底的值，为duan_di时取low,为duan_ding时取high,其他为None,绘图时取有值的连线即为 段\n    duan_value = Column(Float)\n    # 段的变化\n    duan_change = Column(Float)\n    # 段的斜率\n    duan_slope = Column(Float)\n    # 持续的周期\n    duan_interval = Column(Integer)\n\n    # 记录在确定中枢的最后一个段的终点x1，值为Rect(x0,y0,x1,y1)\n    zhongshu = Column(String(length=512))\n    zhongshu_change = Column(Float)\n\n    # 记录在确定中枢的最后一个笔的终点x1，值为Rect(x0,y0,x1,y1)\n    bi_zhongshu = Column(String(length=512))\n    bi_zhongshu_change = Column(Float)\n\n    # 从前往后，合并相邻的有重叠的笔中枢\n    merge_zhongshu = Column(String(length=512))\n    merge_zhongshu_change = Column(Float)\n    merge_zhongshu_level = Column(Integer)\n    merge_zhongshu_interval = Column(Integer)\n\n\n# the __all__ is generated\n__all__ = [\"ZenFactorCommon\"]\n"
  },
  {
    "path": "src/zvt/factors/zen/domain/index_1d_zen_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy.ext.declarative import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.factors.zen.domain.common import ZenFactorCommon\n\nIndex1dZenFactorBase = declarative_base()\n\n\nclass Index1dZenFactor(Index1dZenFactorBase, ZenFactorCommon):\n    __tablename__ = \"index_1d_zen_factor\"\n\n\nregister_schema(db_name=\"index_1d_zen_factor\", schema_base=Index1dZenFactorBase, entity_type=\"index\", internal=True)\n\n\n# the __all__ is generated\n__all__ = [\"Index1dZenFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/zen/domain/stock_1d_zen_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy.ext.declarative import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.factors.zen.domain.common import ZenFactorCommon\n\nStock1dZenFactorBase = declarative_base()\n\n\nclass Stock1dZenFactor(Stock1dZenFactorBase, ZenFactorCommon):\n    __tablename__ = \"stock_1d_zen_factor\"\n\n\nregister_schema(db_name=\"stock_1d_zen_factor\", schema_base=Stock1dZenFactorBase, entity_type=\"stock\", internal=True)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1dZenFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/zen/domain/stock_1wk_zen_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy.ext.declarative import declarative_base\n\nfrom zvt.contract.register import register_schema\nfrom zvt.factors.zen.domain.common import ZenFactorCommon\n\nStock1wkZenFactorBase = declarative_base()\n\n\nclass Stock1wkZenFactor(Stock1wkZenFactorBase, ZenFactorCommon):\n    __tablename__ = \"stock_1wk_zen_factor\"\n\n\nregister_schema(\n    db_name=\"stock_1wk_zen_factor\", schema_base=Stock1wkZenFactorBase, entity_type=\"stock\", internal=True\n)\n\n\n# the __all__ is generated\n__all__ = [\"Stock1wkZenFactor\"]\n"
  },
  {
    "path": "src/zvt/factors/zen/zen_factor.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport math\nfrom enum import Enum\nfrom typing import List, Optional\nfrom typing import Union, Type\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.contract import TradableEntity\nfrom zvt.contract.data_type import Bean\nfrom zvt.contract.drawer import Rect\nfrom zvt.contract.factor import Accumulator\nfrom zvt.contract.factor import Transformer\nfrom zvt.domain import Stock\nfrom zvt.factors.algorithm import distance, intersect\nfrom zvt.factors.zen.base_factor import ZenFactor\nfrom zvt.utils.pd_utils import (\n    group_by_entity_id,\n    normalize_group_compute_result,\n    pd_is_not_null,\n)\n\nlogger = logging.getLogger(__name__)\n\n\nclass ZhongshuRange(Enum):\n    # <=0.4\n    small = \"small\"\n    # >0.4\n    big = \"big\"\n\n    @classmethod\n    def of(cls, change):\n        if change <= 0.4:\n            return ZhongshuRange.small\n        else:\n            return ZhongshuRange.big\n\n\nclass ZhongshuLevel(Enum):\n    # level <= 3\n    level1 = \"level1\"\n    # 3 < level <=7\n    level2 = \"level2\"\n    # level > 7\n    level3 = \"level3\"\n\n    @classmethod\n    def of(cls, level):\n        if level <= 3:\n            return ZhongshuLevel.level1\n        elif level <= 7:\n            return ZhongshuLevel.level2\n        else:\n            return ZhongshuLevel.level3\n\n\nclass ZhongshuDistance(Enum):\n    big_up = \"big_up\"\n    big_down = \"big_down\"\n    small_up = \"small_up\"\n    small_down = \"small_down\"\n\n    @classmethod\n    def of(cls, d):\n        if d is None or math.isnan(d) or d == 0:\n            zhongshu_distance = None\n        elif d <= -0.5:\n            zhongshu_distance = ZhongshuDistance.big_down\n        elif d < 0:\n            zhongshu_distance = ZhongshuDistance.small_down\n        elif d <= 0.5:\n            zhongshu_distance = ZhongshuDistance.small_up\n        else:\n            zhongshu_distance = ZhongshuDistance.big_up\n        return zhongshu_distance\n\n\nclass Zhongshu(object):\n    def __str__(self) -> str:\n        if self.zhongshu_distance:\n            d = self.zhongshu_distance.value\n        else:\n            d = None\n        return f\"{self.zhongshu_range.value},{self.zhongshu_level.value},{d}\"\n\n    def __eq__(self, o: object) -> bool:\n        if isinstance(o, self.__class__):\n            return (\n                self.zhongshu_range == o.zhongshu_range\n                and self.zhongshu_level == o.zhongshu_level\n                and self.zhongshu_distance == o.zhongshu_distance\n            )\n        return False\n\n    def __init__(\n        self,\n        zhongshu_range: ZhongshuRange,\n        zhongshu_level: ZhongshuLevel,\n        zhongshu_distance: ZhongshuDistance,\n    ) -> None:\n        self.zhongshu_range = zhongshu_range\n        self.zhongshu_level = zhongshu_level\n        self.zhongshu_distance = zhongshu_distance\n\n\ndef category_zen_state():\n    all_states = []\n\n    for zhongshu_range in ZhongshuRange:\n        for zhongshu_level in ZhongshuLevel:\n            for distance in ZhongshuDistance:\n                pass\n\n\nclass ZenState(Bean):\n    def __eq__(self, o: object) -> bool:\n        if isinstance(o, self.__class__):\n            return self.zhongshu_list == o.zhongshu_list\n\n    def __str__(self) -> str:\n        return \",\".join([f\"{elem}\" for elem in self.zhongshu_list])\n\n    def __init__(self, zhongshu_state_list: List) -> None:\n        self.zhongshu_list: List[Zhongshu] = []\n        self.zhongshu_state_list = zhongshu_state_list\n\n        pre_range = None\n        for zhongshu_state in zhongshu_state_list:\n            current_range = (zhongshu_state[0], zhongshu_state[1])\n            d = None\n            if pre_range is None:\n                pre_range = current_range\n            else:\n                d = distance(pre_range, current_range)\n                pre_range = current_range\n            change = zhongshu_state[2]\n            level = zhongshu_state[3]\n\n            zhongshu_range = ZhongshuRange.of(change=change)\n            zhongshu_level = ZhongshuLevel.of(level=level)\n            zhongshu_distance = ZhongshuDistance.of(d=d)\n\n            zhongshu = Zhongshu(\n                zhongshu_range=zhongshu_range,\n                zhongshu_level=zhongshu_level,\n                zhongshu_distance=zhongshu_distance,\n            )\n\n            self.zhongshu_list.append(zhongshu)\n\n\ndef cal_distance(s):\n    d_list = []\n    current_range = None\n    print(s)\n    for idx, row in s.items():\n        d = None\n        if row is not None:\n            if current_range is None:\n                current_range = row\n            else:\n                d = distance((current_range.y0, current_range.y1), (row.y0, row.y1))\n                current_range = row\n        d_list.append(d)\n    return pd.Series(index=s.index, data=d_list)\n\n\ndef cal_zen_state(s):\n    zen_states = []\n    zhongshu_state_list = []\n    current_zhongshu_state = None\n    for idx, row in s.items():\n        # row\n        # 0 current_merge_zhongshu_y0\n        # 1 current_merge_zhongshu_y1\n        # 2 current_merge_zhongshu_change\n        # 3 current_merge_zhongshu_level\n        # 4 current_merge_zhongshu_interval\n        if row[0] is not None and not math.isnan(row[0]):\n            if current_zhongshu_state != row:\n                # 相同的中枢，保留最近的(包含关系时产生)\n                if current_zhongshu_state != None and intersect(\n                    (current_zhongshu_state[0], current_zhongshu_state[1]),\n                    (row[0], row[1]),\n                ):\n                    zhongshu_state_list = zhongshu_state_list[:-1]\n\n                # 最多保留最近5个\n                zhongshu_state_list = zhongshu_state_list[-4:] + [row]\n                current_zhongshu_state = row\n\n        if len(zhongshu_state_list) == 5:\n            zen_states.append(ZenState(zhongshu_state_list))\n        else:\n            zen_states.append(None)\n    return pd.Series(index=s.index, data=zen_states)\n\n\ndef good_state(zen_state: ZenState):\n    if zen_state:\n        zhongshu0 = zen_state.zhongshu_list[0]\n        zhongshu1 = zen_state.zhongshu_list[1]\n        zhongshu2 = zen_state.zhongshu_list[2]\n        zhongshu3 = zen_state.zhongshu_list[3]\n        zhongshu4 = zen_state.zhongshu_list[4]\n\n        # 没大涨过\n        if ZhongshuDistance.big_up not in (\n            zhongshu1.zhongshu_distance,\n            zhongshu2.zhongshu_distance,\n            zhongshu3.zhongshu_distance,\n            zhongshu4.zhongshu_distance,\n        ):\n            if ZhongshuRange.big not in (\n                zhongshu3.zhongshu_range,\n                zhongshu4.zhongshu_range,\n            ):\n                # 最近一个窄幅震荡\n                if ZhongshuRange.small == zhongshu4.zhongshu_range and ZhongshuLevel.level1 != zhongshu4.zhongshu_level:\n                    return True\n\n    return False\n\n\ndef trending_state(zen_state: ZenState):\n    if zen_state:\n        zhongshu0 = zen_state.zhongshu_list[0]\n        zhongshu1 = zen_state.zhongshu_list[1]\n        zhongshu2 = zen_state.zhongshu_list[2]\n        zhongshu3 = zen_state.zhongshu_list[3]\n        zhongshu4 = zen_state.zhongshu_list[4]\n\n        # 没大涨过\n        if ZhongshuDistance.big_up not in (\n            zhongshu1.zhongshu_distance,\n            zhongshu2.zhongshu_distance,\n            zhongshu3.zhongshu_distance,\n        ):\n            if ZhongshuRange.big not in (\n                zhongshu3.zhongshu_range,\n                zhongshu4.zhongshu_range,\n            ):\n                # 最近一个窄幅震荡\n                if ZhongshuRange.small == zhongshu4.zhongshu_range and ZhongshuLevel.level1 == zhongshu4.zhongshu_level:\n                    return True\n\n    return False\n\n\nclass TrendingFactor(ZenFactor):\n    def __init__(\n        self,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        transformer: Transformer = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = True,\n        adjust_type: Union[AdjustType, str] = None,\n    ) -> None:\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n        )\n\n    def compute_result(self):\n        super().compute_result()\n        if pd_is_not_null(self.factor_df):\n            df = self.factor_df.apply(\n                lambda x: (\n                    x[\"current_merge_zhongshu_y0\"],\n                    x[\"current_merge_zhongshu_y1\"],\n                    x[\"current_merge_zhongshu_change\"],\n                    x[\"current_merge_zhongshu_level\"],\n                    x[\"current_merge_zhongshu_interval\"],\n                ),\n                axis=1,\n            )\n\n            state_df = group_by_entity_id(df).apply(cal_zen_state)\n            print(self.factor_df)\n            print(state_df)\n            self.factor_df[\"zen_state\"] = normalize_group_compute_result(state_df)\n            self.factor_df[\"good_state\"] = self.factor_df[\"zen_state\"].apply(good_state)\n\n            s = self.factor_df[\"good_state\"]\n            self.result_df = s.to_frame(name=\"filter_result\")\n\n\nclass ShakingFactor(ZenFactor):\n    # 震荡区间\n    shaking_range = 0.5\n\n    def __init__(\n        self,\n        entity_schema: Type[TradableEntity] = Stock,\n        provider: str = None,\n        entity_provider: str = None,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        limit: int = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        category_field: str = \"entity_id\",\n        time_field: str = \"timestamp\",\n        keep_window: int = None,\n        keep_all_timestamp: bool = False,\n        fill_method: str = \"ffill\",\n        effective_number: int = None,\n        transformer: Transformer = None,\n        accumulator: Accumulator = None,\n        need_persist: bool = False,\n        only_compute_factor: bool = False,\n        factor_name: str = None,\n        clear_state: bool = False,\n        only_load_factor: bool = True,\n        adjust_type: Union[AdjustType, str] = None,\n    ) -> None:\n        super().__init__(\n            entity_schema,\n            provider,\n            entity_provider,\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            filters,\n            order,\n            limit,\n            level,\n            category_field,\n            time_field,\n            keep_window,\n            keep_all_timestamp,\n            fill_method,\n            effective_number,\n            transformer,\n            accumulator,\n            need_persist,\n            only_compute_factor,\n            factor_name,\n            clear_state,\n            only_load_factor,\n            adjust_type,\n        )\n\n    def drawer_sub_df_list(self) -> Optional[List[pd.DataFrame]]:\n        df1 = self.factor_df[[\"current_merge_zhongshu_y1\"]].dropna()\n        df2 = self.factor_df[[\"current_merge_zhongshu_y0\"]].dropna()\n        return [df1, df2]\n\n    def drawer_rects(self) -> List[Rect]:\n        return super().drawer_rects()\n\n    def compute_result(self):\n        super().compute_result()\n        # 窄幅震荡\n        s1 = self.factor_df[\"current_merge_zhongshu_change\"] <= self.shaking_range\n        # 中枢级别\n        s2 = self.factor_df[\"current_merge_zhongshu_level\"] >= 2\n        s3 = self.factor_df[\"current_merge_zhongshu_interval\"] >= 120\n\n        # 中枢上缘\n        s4 = self.factor_df[\"close\"] <= 1.1 * self.factor_df[\"current_merge_zhongshu_y1\"]\n        s5 = self.factor_df[\"close\"] >= 0.9 * self.factor_df[\"current_merge_zhongshu_y1\"]\n\n        # 中枢下缘\n        s6 = self.factor_df[\"close\"] <= 1.1 * self.factor_df[\"current_merge_zhongshu_y0\"]\n        s7 = self.factor_df[\"close\"] >= 0.9 * self.factor_df[\"current_merge_zhongshu_y0\"]\n\n        s = s1 & s2 & s3 & ((s4 & s5) | (s6 & s7))\n        # s = s.groupby(level=0).apply(drop_continue_duplicate)\n        if s.index.nlevels == 3:\n            s = s.reset_index(level=0, drop=True)\n\n        self.result_df = s.to_frame(name=\"filter_result\")\n        print(self.result_df)\n\n\nif __name__ == \"__main__\":\n    entity_ids = [\"stock_sz_000338\"]\n\n    f = ZenFactor(\n        provider=\"em\",\n        entity_schema=Stock,\n        entity_ids=entity_ids,\n        need_persist=True,\n    )\n    f.draw(show=True)\n\n\n# the __all__ is generated\n__all__ = [\n    \"ZhongshuRange\",\n    \"ZhongshuLevel\",\n    \"ZhongshuDistance\",\n    \"Zhongshu\",\n    \"category_zen_state\",\n    \"ZenState\",\n    \"cal_distance\",\n    \"cal_zen_state\",\n    \"good_state\",\n    \"trending_state\",\n    \"TrendingFactor\",\n    \"ShakingFactor\",\n]\n"
  },
  {
    "path": "src/zvt/fill_project.py",
    "content": "# script to auto generate some files\nfrom zvt.autocode.generator import gen_kdata_schema, gen_exports\nfrom zvt.contract import AdjustType\nfrom zvt.contract import IntervalLevel\n\n\ndef gen_kdata_schemas():\n    \"\"\"\n    generate kdata(OHLC) schemas for tradable entity\n\n    \"\"\"\n    # A股行情\n    gen_kdata_schema(\n        pkg=\"zvt\",\n        entity_type=\"stock\",\n        levels=[\n            level for level in IntervalLevel if level not in (IntervalLevel.LEVEL_L2_QUOTE, IntervalLevel.LEVEL_TICK)\n        ],\n        adjust_types=[None, AdjustType.hfq],\n        entity_in_submodule=True,\n    )\n    # 中国期货\n    gen_kdata_schema(\n        pkg=\"zvt\",\n        entity_type=\"future\",\n        levels=[IntervalLevel.LEVEL_1DAY],\n        entity_in_submodule=True,\n    )\n\n    # 美股\n    gen_kdata_schema(\n        pkg=\"zvt\",\n        entity_type=\"stockus\",\n        levels=[IntervalLevel.LEVEL_1DAY],\n        adjust_types=[None, AdjustType.hfq],\n        entity_in_submodule=True,\n    )\n    # 美指\n    gen_kdata_schema(\n        pkg=\"zvt\",\n        entity_type=\"indexus\",\n        levels=[IntervalLevel.LEVEL_1DAY],\n        entity_in_submodule=True,\n    )\n\n    # 港股\n    gen_kdata_schema(\n        pkg=\"zvt\",\n        entity_type=\"stockhk\",\n        levels=[IntervalLevel.LEVEL_1DAY],\n        adjust_types=[None, AdjustType.hfq],\n        entity_in_submodule=True,\n    )\n\n    # 板块行情\n    gen_kdata_schema(\n        pkg=\"zvt\",\n        entity_type=\"block\",\n        levels=[IntervalLevel.LEVEL_1DAY, IntervalLevel.LEVEL_1WEEK, IntervalLevel.LEVEL_1MON],\n        entity_in_submodule=True,\n    )\n\n    # A股指数行情\n    gen_kdata_schema(\n        pkg=\"zvt\",\n        entity_type=\"index\",\n        levels=[IntervalLevel.LEVEL_1DAY, IntervalLevel.LEVEL_1WEEK],\n        entity_in_submodule=True,\n    )\n\n    # etf行情\n    gen_kdata_schema(\n        pkg=\"zvt\", entity_type=\"etf\", levels=[IntervalLevel.LEVEL_1DAY], entity_in_submodule=True\n    )\n\n    # currency行情\n    gen_kdata_schema(\n        pkg=\"zvt\", entity_type=\"currency\", levels=[IntervalLevel.LEVEL_1DAY], entity_in_submodule=True\n    )\n\n\nif __name__ == \"__main__\":\n    # gen_exports(\"api\")\n    # gen_exports(\"broker\")\n    # gen_exports(\"common\")\n    # gen_exports(\"contract\", export_from_package=True, export_modules=[\"schema\"])\n    gen_exports(\"domain\", export_from_package=True)\n    # gen_exports(\"factors\", export_from_package=True)\n    # gen_exports(\"trading\")\n\n    # gen_exports(\"ml\")\n    # gen_exports(\"utils\", export_from_package=False, export_var=True)\n    # gen_exports('informer')\n    # gen_exports('trader')\n    # gen_exports('autocode')\n    # gen_exports(\"zhdate\")\n    gen_exports(\"recorders\", export_from_package=True, exclude_modules=[\"qmt\"])\n    # gen_exports(\"tag\", export_from_package=False)\n    # gen_exports(\"sso\", export_from_package=False)\n    # gen_kdata_schemas()\n    # zip_dir(ZVT_TEST_DATA_PATH, zip_file_name=DATA_SAMPLE_ZIP_PATH)\n"
  },
  {
    "path": "src/zvt/informer/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule informer\nfrom .informer import *\nfrom .informer import __all__ as _informer_all\n\n__all__ += _informer_all\n"
  },
  {
    "path": "src/zvt/informer/inform_utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport eastmoneypy\nimport requests\n\nfrom zvt import zvt_config\nfrom zvt.contract.api import get_entities\nfrom zvt.informer import EmailInformer\n\n\ndef inform_email(entity_ids, entity_type, target_date, title, provider):\n    msg = \"no targets\"\n    if entity_ids:\n        entities = get_entities(provider=provider, entity_type=entity_type, entity_ids=entity_ids, return_type=\"domain\")\n        assert len(entities) == len(entity_ids)\n\n        infos = [f\"{entity.name}({entity.code})\" for entity in entities]\n        msg = \"\\n\".join(infos) + \"\\n\"\n\n        EmailInformer().send_message(zvt_config[\"email_username\"], f\"{target_date} {title}\", msg)\n\n\ndef add_to_eastmoney(codes, group, entity_type=\"stock\", over_write=True, headers_list=None):\n    if headers_list is None:\n        headers_list = [None]\n\n    for headers in headers_list:\n        with requests.Session() as session:\n            group_id = eastmoneypy.get_group_id(group, session=session, headers=headers)\n\n            need_create_group = False\n\n            if not group_id:\n                need_create_group = True\n\n            if group_id and over_write:\n                eastmoneypy.del_group(group_name=group, session=session, headers=headers)\n                need_create_group = True\n\n            codes = set(codes)\n            if need_create_group:\n                result = eastmoneypy.create_group(group_name=group, session=session, headers=headers)\n                group_id = result[\"gid\"]\n            else:\n                current_codes = eastmoneypy.list_entities(group_id=group_id, session=session, headers=headers)\n                if current_codes:\n                    codes = codes - set(current_codes)\n\n            for code in codes:\n                eastmoneypy.add_to_group(\n                    code=code, entity_type=entity_type, group_id=group_id, session=session, headers=headers\n                )\n\n\ndef clean_eastmoney_groups(keep, headers_list=None):\n    if headers_list is None:\n        headers_list = [None]\n\n    for headers in headers_list:\n        if keep is None:\n            keep = [\"自选股\"]\n        with requests.Session() as session:\n            groups = eastmoneypy.get_groups(session=session, headers=headers)\n            groups_to_clean = [group[\"gid\"] for group in groups if group[\"gname\"] not in keep]\n            for gid in groups_to_clean:\n                eastmoneypy.del_group(group_id=gid, session=session, headers=headers)\n\n\ndef delete_eastmoney_group(group_name, headers_list=None):\n    if headers_list is None:\n        headers_list = [None]\n    for headers in headers_list:\n        with requests.Session() as session:\n            eastmoneypy.del_group(group_name=group_name, session=session, headers=headers)\n\n\n# the __all__ is generated\n__all__ = [\"inform_email\", \"add_to_eastmoney\", \"clean_eastmoney_groups\", \"delete_eastmoney_group\"]\n"
  },
  {
    "path": "src/zvt/informer/informer.py",
    "content": "# -*- coding: utf-8 -*-\nimport email\nimport json\nimport logging\nimport smtplib\nfrom email.header import Header\nfrom email.mime.multipart import MIMEMultipart\nfrom email.mime.text import MIMEText\n\nimport requests\n\nfrom zvt import zvt_config\n\nlogger = logging.getLogger(__name__)\n\n\nclass Informer(object):\n    def send_message(self, to_user, title, body, **kwargs):\n        pass\n\n\nclass EmailInformer(Informer):\n    def __init__(self, ssl=True) -> None:\n        super().__init__()\n        self.ssl = ssl\n\n    def send_message_(self, to_user, title, body, **kwargs):\n        if (\n            not zvt_config[\"smtp_host\"]\n            or not zvt_config[\"smtp_port\"]\n            or not zvt_config[\"email_username\"]\n            or not zvt_config[\"email_password\"]\n        ):\n            logger.warning(f\"Please set smtp_host/smtp_port/email_username/email_password in ~/zvt-home/config.json\")\n            return\n        host = zvt_config[\"smtp_host\"]\n        port = zvt_config[\"smtp_port\"]\n\n        smtp_client = None\n        try:\n            if self.ssl:\n                try:\n                    smtp_client = smtplib.SMTP_SSL(host=host, port=port)\n                except:\n                    smtp_client = smtplib.SMTP_SSL()\n            else:\n                try:\n                    smtp_client = smtplib.SMTP(host=host, port=port)\n                except:\n                    smtp_client = smtplib.SMTP()\n\n            smtp_client.connect(host=host, port=port)\n            smtp_client.login(zvt_config[\"email_username\"], zvt_config[\"email_password\"])\n            msg = MIMEMultipart(\"alternative\")\n            msg[\"Subject\"] = Header(title).encode()\n            msg[\"From\"] = \"{} <{}>\".format(Header(\"zvt\").encode(), zvt_config[\"email_username\"])\n            if type(to_user) is list:\n                msg[\"To\"] = \", \".join(to_user)\n            else:\n                msg[\"To\"] = to_user\n            msg[\"Message-id\"] = email.utils.make_msgid()\n            msg[\"Date\"] = email.utils.formatdate()\n\n            plain_text = MIMEText(body, _subtype=\"plain\", _charset=\"UTF-8\")\n            msg.attach(plain_text)\n            smtp_client.sendmail(zvt_config[\"email_username\"], to_user, msg.as_string())\n        except Exception as e:\n            logger.exception(\"send email failed\", e)\n        finally:\n            if smtp_client:\n                try:\n                    smtp_client.quit()\n                except Exception as e:\n                    logger.exception(\"smtp_client quit failed\", e)\n\n    def send_message(self, to_user, title, body, sub_size=20, with_sender=True, **kwargs):\n        if type(to_user) is list and sub_size:\n            size = len(to_user)\n            if size >= sub_size:\n                step_size = int(size / sub_size)\n                if size % sub_size:\n                    step_size = step_size + 1\n            else:\n                step_size = 1\n\n            for step in range(step_size):\n                sub_to_user = to_user[sub_size * step : sub_size * (step + 1)]\n                if with_sender:\n                    sub_to_user.append(zvt_config[\"email_username\"])\n                self.send_message_(sub_to_user, title, body, **kwargs)\n        else:\n            self.send_message_(to_user, title, body, **kwargs)\n\n\nclass WechatInformer(Informer):\n    GET_TOKEN_URL = \"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}\".format(\n        zvt_config[\"wechat_app_id\"], zvt_config[\"wechat_app_secrect\"]\n    )\n\n    GET_TEMPLATE_URL = \"https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token={}\"\n    SEND_MSG_URL = \"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={}\"\n\n    token = None\n\n    def __init__(self) -> None:\n        self.refresh_token()\n\n    def refresh_token(self):\n        resp = requests.get(self.GET_TOKEN_URL)\n        logger.info(\"refresh_token resp.status_code:{}, resp.text:{}\".format(resp.status_code, resp.text))\n\n        if resp.status_code == 200 and resp.json() and \"access_token\" in resp.json():\n            self.token = resp.json()[\"access_token\"]\n        else:\n            logger.exception(\"could not refresh_token\")\n\n    def send_price_notification(self, to_user, security_name, current_price, change_pct):\n        the_json = self._format_price_notification(to_user, security_name, current_price, change_pct)\n        the_data = json.dumps(the_json, ensure_ascii=False).encode(\"utf-8\")\n\n        resp = requests.post(self.SEND_MSG_URL.format(self.token), the_data)\n\n        logger.info(\"send_price_notification resp:{}\".format(resp.text))\n\n        if resp.json() and resp.json()[\"errcode\"] == 0:\n            logger.info(\"send_price_notification to user:{} data:{} success\".format(to_user, the_json))\n\n    def _format_price_notification(self, to_user, security_name, current_price, change_pct):\n        if change_pct > 0:\n            title = \"吃肉喝汤\"\n        else:\n            title = \"关灯吃面\"\n\n        # 先固定一个template\n\n        # {\n        #     \"template_id\": \"mkqi-L1h56mH637vLXiuS_ulLTs1byDYYgLBbSXQ65U\",\n        #     \"title\": \"涨跌幅提醒\",\n        #     \"primary_industry\": \"金融业\",\n        #     \"deputy_industry\": \"证券|基金|理财|信托\",\n        #     \"content\": \"{{first.DATA}}\\n股票名：{{keyword1.DATA}}\\n最新价：{{keyword2.DATA}}\\n涨跌幅：{{keyword3.DATA}}\\n{{remark.DATA}}\",\n        #     \"example\": \"您好，腾新控股最新价130.50元，上涨达到设置的3.2%\\r\\n股票名：腾讯控股（00700）\\r\\n最新价：130.50元\\r\\n涨跌幅：+3.2%\\r\\n点击查看最新实时行情。\"\n        # }\n\n        template_id = \"mkqi-L1h56mH637vLXiuS_ulLTs1byDYYgLBbSXQ65U\"\n        the_json = {\n            \"touser\": to_user,\n            \"template_id\": template_id,\n            \"url\": \"http://www.foolcage.com\",\n            \"data\": {\n                \"first\": {\"value\": title, \"color\": \"#173177\"},\n                \"keyword1\": {\"value\": security_name, \"color\": \"#173177\"},\n                \"keyword2\": {\"value\": current_price, \"color\": \"#173177\"},\n                \"keyword3\": {\"value\": \"{:.2%}\".format(change_pct), \"color\": \"#173177\"},\n                \"remark\": {\"value\": \"会所嫩模 Or 下海干活?\", \"color\": \"#173177\"},\n            },\n        }\n\n        return the_json\n\n\nclass QiyeWechatBot(Informer):\n    def __init__(self, token=None) -> None:\n        self.token = token\n\n    def send_message(self, content):\n        if not self.token:\n            if not zvt_config[\"qiye_wechat_bot_token\"]:\n                logger.warning(f\"Please set qiye_wechat_bot_token in ~/zvt-home/config.json\")\n                return\n            self.token = zvt_config[\"qiye_wechat_bot_token\"]\n\n        msg = {\n            \"msgtype\": \"text\",\n            \"text\": {\"content\": content},\n        }\n        requests.post(f\"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={self.token}\", json=msg)\n\n\nif __name__ == \"__main__\":\n    # weixin_action = WechatInformer()\n    # weixin_action.send_price_notification(to_user='oRvNP0XIb9G3g6a-2fAX9RHX5--Q', security_name='BTC/USDT',\n    #                                       current_price=1000, change_pct='0.5%')\n    bot = QiyeWechatBot()\n    bot.send_message(content=\"test\")\n# the __all__ is generated\n__all__ = [\"Informer\", \"EmailInformer\", \"WechatInformer\"]\n"
  },
  {
    "path": "src/zvt/main.py",
    "content": "import dash_bootstrap_components as dbc\nfrom dash import html\nfrom dash.dependencies import Input, Output\n\nfrom zvt.ui import zvt_app\nfrom zvt.ui.apps import factor_app\n\n\ndef serve_layout():\n    layout = html.Div(\n        children=[\n            # banner\n            html.Div(className=\"zvt-banner\", children=html.H2(className=\"h2-title\", children=\"ZVT\")),\n            dbc.CardHeader(\n                dbc.Tabs(\n                    [dbc.Tab(label=\"factor\", tab_id=\"tab-factor\", label_style={}, tab_style={\"width\": \"100px\"})],\n                    id=\"card-tabs\",\n                    active_tab=\"tab-factor\",\n                )\n            ),\n            dbc.CardBody(html.P(id=\"card-content\", className=\"card-text\")),\n        ]\n    )\n\n    return layout\n\n\n@zvt_app.callback(Output(\"card-content\", \"children\"), [Input(\"card-tabs\", \"active_tab\")])\ndef tab_content(active_tab):\n    if \"tab-factor\" == active_tab:\n        return factor_app.factor_layout()\n\n\nzvt_app.layout = serve_layout\n\n\ndef main():\n    # init_plugins()\n    zvt_app.run_server(debug=True, host=\"0.0.0.0\")\n    # zvt_app.run_server()\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "src/zvt/misc/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/misc/constants.py",
    "content": "# -*- coding: utf-8 -*-\nCHINESEYEARCODE = [\n    19416,\n    19168,\n    42352,\n    21717,\n    53856,\n    55632,\n    91476,\n    22176,\n    39632,\n    21970,\n    19168,\n    42422,\n    42192,\n    53840,\n    119381,\n    46400,\n    54944,\n    44450,\n    38320,\n    84343,\n    18800,\n    42160,\n    46261,\n    27216,\n    27968,\n    109396,\n    11104,\n    38256,\n    21234,\n    18800,\n    25958,\n    54432,\n    59984,\n    92821,\n    23248,\n    11104,\n    100067,\n    37600,\n    116951,\n    51536,\n    54432,\n    120998,\n    46416,\n    22176,\n    107956,\n    9680,\n    37584,\n    53938,\n    43344,\n    46423,\n    27808,\n    46416,\n    86869,\n    19872,\n    42416,\n    83315,\n    21168,\n    43432,\n    59728,\n    27296,\n    44710,\n    43856,\n    19296,\n    43748,\n    42352,\n    21088,\n    62051,\n    55632,\n    23383,\n    22176,\n    38608,\n    19925,\n    19152,\n    42192,\n    54484,\n    53840,\n    54616,\n    46400,\n    46752,\n    103846,\n    38320,\n    18864,\n    43380,\n    42160,\n    45690,\n    27216,\n    27968,\n    44870,\n    43872,\n    38256,\n    19189,\n    18800,\n    25776,\n    29859,\n    59984,\n    27480,\n    23232,\n    43872,\n    38613,\n    37600,\n    51552,\n    55636,\n    54432,\n    55888,\n    30034,\n    22176,\n    43959,\n    9680,\n    37584,\n    51893,\n    43344,\n    46240,\n    47780,\n    44368,\n    21977,\n    19360,\n    42416,\n    86390,\n    21168,\n    43312,\n    31060,\n    27296,\n    44368,\n    23378,\n    19296,\n    42726,\n    42208,\n    53856,\n    60005,\n    54576,\n    23200,\n    30371,\n    38608,\n    19195,\n    19152,\n    42192,\n    118966,\n    53840,\n    54560,\n    56645,\n    46496,\n    22224,\n    21938,\n    18864,\n    42359,\n    42160,\n    43600,\n    111189,\n    27936,\n    44448,\n    84835,\n    37744,\n    18936,\n    18800,\n    25776,\n    92326,\n    59984,\n    27296,\n    108228,\n    43744,\n    37600,\n    53987,\n    51552,\n    54615,\n    54432,\n    55888,\n    23893,\n    22176,\n    42704,\n    21972,\n    21200,\n    43448,\n    43344,\n    46240,\n    46758,\n    44368,\n    21920,\n    43940,\n    42416,\n    21168,\n    45683,\n    26928,\n    29495,\n    27296,\n    44368,\n    84821,\n    19296,\n    42352,\n    21732,\n    53600,\n    59752,\n    54560,\n    55968,\n    92838,\n    22224,\n    19168,\n    43476,\n    41680,\n    53584,\n    62034,\n    54560,\n]\n\"\"\"\n从1900年到2100年的农历月份数据代码 20位二进制代码表示一个年份的数据。\n\n前四位0:表示闰月为29天，1:表示闰月为30天\n中间12位：从左起表示1-12月每月的大小，1为30天，0为29天\n最后四位：表示闰月的月份，0表示当年无闰月\n\n前四位和最后四位应该结合使用，如果最后四位为0，则不考虑前四位\n例： \n1901年代码为 19168，转成二进制为 0b100101011100000, 最后四位为0，当年无闰月，月份数据为 010010101110 分别代表12月的大小情况\n1903年代码为 21717，转成二进制为 0b101010011010101，最后四位为5，当年为闰五月，首四位为0，闰月为29天，月份数据为 010101001101 分别代表12月的大小情况\n\n\"\"\"\n\nCHINESENEWYEAR = [\n    \"19000131\",\n    \"19010219\",\n    \"19020208\",\n    \"19030129\",\n    \"19040216\",\n    \"19050204\",\n    \"19060125\",\n    \"19070213\",\n    \"19080202\",\n    \"19090122\",\n    \"19100210\",\n    \"19110130\",\n    \"19120218\",\n    \"19130206\",\n    \"19140126\",\n    \"19150214\",\n    \"19160203\",\n    \"19170123\",\n    \"19180211\",\n    \"19190201\",\n    \"19200220\",\n    \"19210208\",\n    \"19220128\",\n    \"19230216\",\n    \"19240205\",\n    \"19250124\",\n    \"19260213\",\n    \"19270202\",\n    \"19280123\",\n    \"19290210\",\n    \"19300130\",\n    \"19310217\",\n    \"19320206\",\n    \"19330126\",\n    \"19340214\",\n    \"19350204\",\n    \"19360124\",\n    \"19370211\",\n    \"19380131\",\n    \"19390219\",\n    \"19400208\",\n    \"19410127\",\n    \"19420215\",\n    \"19430205\",\n    \"19440125\",\n    \"19450213\",\n    \"19460202\",\n    \"19470122\",\n    \"19480210\",\n    \"19490129\",\n    \"19500217\",\n    \"19510206\",\n    \"19520127\",\n    \"19530214\",\n    \"19540203\",\n    \"19550124\",\n    \"19560212\",\n    \"19570131\",\n    \"19580218\",\n    \"19590208\",\n    \"19600128\",\n    \"19610215\",\n    \"19620205\",\n    \"19630125\",\n    \"19640213\",\n    \"19650202\",\n    \"19660121\",\n    \"19670209\",\n    \"19680130\",\n    \"19690217\",\n    \"19700206\",\n    \"19710127\",\n    \"19720215\",\n    \"19730203\",\n    \"19740123\",\n    \"19750211\",\n    \"19760131\",\n    \"19770218\",\n    \"19780207\",\n    \"19790128\",\n    \"19800216\",\n    \"19810205\",\n    \"19820125\",\n    \"19830213\",\n    \"19840202\",\n    \"19850220\",\n    \"19860209\",\n    \"19870129\",\n    \"19880217\",\n    \"19890206\",\n    \"19900127\",\n    \"19910215\",\n    \"19920204\",\n    \"19930123\",\n    \"19940210\",\n    \"19950131\",\n    \"19960219\",\n    \"19970207\",\n    \"19980128\",\n    \"19990216\",\n    \"20000205\",\n    \"20010124\",\n    \"20020212\",\n    \"20030201\",\n    \"20040122\",\n    \"20050209\",\n    \"20060129\",\n    \"20070218\",\n    \"20080207\",\n    \"20090126\",\n    \"20100214\",\n    \"20110203\",\n    \"20120123\",\n    \"20130210\",\n    \"20140131\",\n    \"20150219\",\n    \"20160208\",\n    \"20170128\",\n    \"20180216\",\n    \"20190205\",\n    \"20200125\",\n    \"20210212\",\n    \"20220201\",\n    \"20230122\",\n    \"20240210\",\n    \"20250129\",\n    \"20260217\",\n    \"20270206\",\n    \"20280126\",\n    \"20290213\",\n    \"20300203\",\n    \"20310123\",\n    \"20320211\",\n    \"20330131\",\n    \"20340219\",\n    \"20350208\",\n    \"20360128\",\n    \"20370215\",\n    \"20380204\",\n    \"20390124\",\n    \"20400212\",\n    \"20410201\",\n    \"20420122\",\n    \"20430210\",\n    \"20440130\",\n    \"20450217\",\n    \"20460206\",\n    \"20470126\",\n    \"20480214\",\n    \"20490202\",\n    \"20500123\",\n    \"20510211\",\n    \"20520201\",\n    \"20530219\",\n    \"20540208\",\n    \"20550128\",\n    \"20560215\",\n    \"20570204\",\n    \"20580124\",\n    \"20590212\",\n    \"20600202\",\n    \"20610121\",\n    \"20620209\",\n    \"20630129\",\n    \"20640217\",\n    \"20650205\",\n    \"20660126\",\n    \"20670214\",\n    \"20680203\",\n    \"20690123\",\n    \"20700211\",\n    \"20710131\",\n    \"20720219\",\n    \"20730207\",\n    \"20740127\",\n    \"20750215\",\n    \"20760205\",\n    \"20770124\",\n    \"20780212\",\n    \"20790202\",\n    \"20800122\",\n    \"20810209\",\n    \"20820129\",\n    \"20830217\",\n    \"20840206\",\n    \"20850126\",\n    \"20860214\",\n    \"20870203\",\n    \"20880124\",\n    \"20890210\",\n    \"20900130\",\n    \"20910218\",\n    \"20920207\",\n    \"20930127\",\n    \"20940215\",\n    \"20950205\",\n    \"20960125\",\n    \"20970212\",\n    \"20980201\",\n    \"20990121\",\n    \"21000209\",\n]\n\"\"\"\n从1900年，至2100年每年的农历春节的公历日期\n\"\"\"\n\n\n# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/misc/misc_models.py",
    "content": "# -*- coding: utf-8 -*-\nfrom datetime import datetime\n\nfrom zvt.contract.model import CustomModel\n\n\nclass TimeMessage(CustomModel):\n    # 时间\n    timestamp: datetime\n    # 信息\n    message: str\n\n\n# the __all__ is generated\n__all__ = [\"TimeMessage\"]\n"
  },
  {
    "path": "src/zvt/misc/misc_service.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.misc.zhdate import ZhDate\nfrom zvt.utils.time_utils import to_pd_timestamp, current_date, count_interval\n\n\ndef holiday_distance(timestamp=None, consider_future_days=15):\n    if not timestamp:\n        the_date = current_date()\n    else:\n        the_date = to_pd_timestamp(timestamp)\n\n    # 业绩预告\n    month = the_date.month\n\n    infos = [f\"今天是{the_date.date()}\"]\n    if month == 12:\n        infos.append(\"业绩预告期，注意排雷\")\n\n        # 元旦\n        new_year = to_pd_timestamp(f\"{the_date.year + 1}-01-01\")\n        distance = count_interval(the_date, new_year)\n        if 0 < distance < consider_future_days:\n            infos.append(f\"距离元旦还有{distance}天\")\n    if month in (1, 2):\n        # 春节\n        zh_date = ZhDate(lunar_year=the_date.year, lunar_month=1, lunar_day=1)\n        spring_date = zh_date.newyear\n        distance = count_interval(the_date, spring_date)\n        if 0 < distance < consider_future_days:\n            infos.append(f\"距离春节还有{distance}天\")\n\n        # 两会\n        # 三月初\n        lianghui = to_pd_timestamp(f\"{the_date.year}-03-01\")\n        distance = count_interval(the_date, lianghui)\n        if 0 < distance < consider_future_days:\n            infos.append(f\"距离两会还有{distance}天\")\n\n    # 年报发布\n    if month in (3, 4):\n        infos.append(\"年报发布期，注意排雷\")\n\n    # 五一\n    if month == 4:\n        wuyi = to_pd_timestamp(f\"{the_date.year}-05-01\")\n        distance = count_interval(the_date, wuyi)\n        if 0 < distance < consider_future_days:\n            infos.append(f\"距离五一还有{distance}天\")\n\n    # 业绩发布\n    if month in (7, 8):\n        infos.append(\"半年报发布期，注意排雷\")\n\n    if month == 9:\n        # 国庆\n        shiyi = to_pd_timestamp(f\"{the_date.year}-10-01\")\n        distance = count_interval(the_date, shiyi)\n        if 0 < distance < consider_future_days:\n            infos.append(f\"距离国庆还有{distance}天\")\n\n    msg = \"，\".join(infos)\n    return msg\n\n\ndef get_time_message():\n    return {\"timestamp\": current_date(), \"message\": holiday_distance()}\n\n\nif __name__ == \"__main__\":\n    print(get_time_message())\n\n# the __all__ is generated\n__all__ = [\"holiday_distance\", \"get_time_message\"]\n"
  },
  {
    "path": "src/zvt/misc/zhdate.py",
    "content": "\"\"\"\n-*- coding: utf-8 -*-\nthanks to https://github.com/CutePandaSh/zhdate\n\"\"\"\nfrom datetime import datetime, timedelta\nfrom itertools import accumulate\n\nfrom zvt.misc.constants import CHINESEYEARCODE, CHINESENEWYEAR\n\n\nclass ZhDate:\n    def __init__(self, lunar_year, lunar_month, lunar_day, leap_month=False):\n        \"\"\"初始化函数\n\n        Arguments:\n            lunar_year {int} -- 农历年\n            lunar_month {int} -- 农历月份\n            lunar_day {int} -- 农历日\n\n        Keyword Arguments:\n            leap_month {bool} -- 是否是在农历闰月中 (default: {False})\n        \"\"\"\n        self.lunar_year = lunar_year\n        self.lunar_month = lunar_month\n        self.lunar_day = lunar_day\n        self.leap_month = leap_month\n        self.year_code = CHINESEYEARCODE[self.lunar_year - 1900]\n        self.newyear = datetime.strptime(CHINESENEWYEAR[self.lunar_year - 1900], \"%Y%m%d\")\n        if not ZhDate.validate(lunar_year, lunar_month, lunar_day, leap_month):\n            raise TypeError(\"农历日期不支持所谓“{}”，超出农历1900年1月1日至2100年12月29日，或日期不存在\".format(self))\n\n    def to_datetime(self):\n        \"\"\"农历日期转换称公历日期\n\n        Returns:\n            datetime -- 当前农历对应的公历日期\n        \"\"\"\n        return self.newyear + timedelta(days=self.__days_passed())\n\n    @staticmethod\n    def from_datetime(dt):\n        \"\"\"静态方法，从公历日期生成农历日期\n\n        Arguments:\n            dt {datetime} -- 公历的日期\n\n        Returns:\n            ZhDate -- 生成的农历日期对象\n        \"\"\"\n        lunar_year = dt.year\n        # 如果还没有到农历正月初一 农历年份减去1\n        lunar_year -= (datetime.strptime(CHINESENEWYEAR[lunar_year - 1900], \"%Y%m%d\") - dt).total_seconds() > 0\n        # 当时农历新年时的日期对象\n        newyear_dt = datetime.strptime(CHINESENEWYEAR[lunar_year - 1900], \"%Y%m%d\")\n        # 查询日期距离当年的春节差了多久\n        days_passed = (dt - newyear_dt).days\n        # 被查询日期的年份码\n        year_code = CHINESEYEARCODE[lunar_year - 1900]\n        # 取得本年的月份列表\n        month_days = ZhDate.decode(year_code)\n\n        for pos, days in enumerate(accumulate(month_days)):\n            if days_passed + 1 <= days:\n                month = pos + 1\n                lunar_day = month_days[pos] - (days - days_passed) + 1\n                break\n\n        leap_month = False\n        if (year_code & 0xF) == 0 or month <= (year_code & 0xF):\n            lunar_month = month\n        else:\n            lunar_month = month - 1\n\n        if (year_code & 0xF) != 0 and month == (year_code & 0xF) + 1:\n            leap_month = True\n\n        return ZhDate(lunar_year, lunar_month, lunar_day, leap_month)\n\n    @staticmethod\n    def today():\n        return ZhDate.from_datetime(datetime.now())\n\n    def __days_passed(self):\n        \"\"\"私有方法，计算当前农历日期和当年农历新年之间的天数差值\n\n        Returns:\n            int -- 差值天数\n        \"\"\"\n        month_days = ZhDate.decode(self.year_code)\n        # 当前农历年的闰月，为0表示无润叶\n        month_leap = self.year_code & 0xF\n\n        # 当年无闰月，或者有闰月但是当前月小于闰月\n        if (month_leap == 0) or (self.lunar_month < month_leap):\n            days_passed_month = sum(month_days[: self.lunar_month - 1])\n        # 当前不是闰月，并且当前月份和闰月相同\n        elif (not self.leap_month) and (self.lunar_month == month_leap):\n            days_passed_month = sum(month_days[: self.lunar_month - 1])\n        else:\n            days_passed_month = sum(month_days[: self.lunar_month])\n\n        return days_passed_month + self.lunar_day - 1\n\n    def chinese(self):\n        ZHNUMS = \"〇一二三四五六七八九十\"\n        zh_year = \"\"\n        for i in range(0, 4):\n            zh_year += ZHNUMS[int(str(self.lunar_year)[i])]\n\n        if self.leap_month:\n            zh_month = \"闰\"\n        else:\n            zh_month = \"\"\n\n        if self.lunar_month == 1:\n            zh_month += \"正\"\n        elif self.lunar_month == 12:\n            zh_month += \"腊\"\n        elif self.lunar_month <= 10:\n            zh_month += ZHNUMS[self.lunar_month]\n        else:\n            zh_month += \"十{}\".format(ZHNUMS[self.lunar_month - 10])\n\n        if self.lunar_day <= 10:\n            zh_day = \"初{}\".format(ZHNUMS[self.lunar_day])\n        elif self.lunar_day < 20:\n            zh_day = \"十{}\".format(ZHNUMS[self.lunar_day - 10])\n        elif self.lunar_day == 20:\n            zh_day = \"二十\"\n        elif self.lunar_day < 30:\n            zh_day = \"廿{}\".format(ZHNUMS[self.lunar_day - 20])\n        else:\n            zh_day = \"三十\"\n\n        year_tiandi = ZhDate.__tiandi(self.lunar_year - 1900 + 36)\n\n        shengxiao = \"鼠牛虎兔龙蛇马羊猴鸡狗猪\"\n\n        return \"{}年{}月{} {}{}年\".format(zh_year, zh_month, zh_day, year_tiandi, shengxiao[(self.lunar_year - 1900) % 12])\n\n    def __str__(self):\n        \"\"\"打印字符串的方法\n\n        Returns:\n            str -- 标准格式农历日期字符串\n        \"\"\"\n        return \"农历{}年{}{}月{}日\".format(self.lunar_year, \"闰\" if self.leap_month else \"\", self.lunar_month, self.lunar_day)\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __eq__(self, another):\n        if not isinstance(another, ZhDate):\n            raise TypeError(\"比较必须都是ZhDate类型\")\n        cond1 = self.lunar_year == another.lunar_year\n        cond2 = self.lunar_month == another.lunar_month\n        cond3 = self.lunar_day == another.lunar_day\n        cond4 = self.leap_month == another.leap_month\n        return cond1 and cond2 and cond3 and cond4\n\n    def __add__(self, another):\n        if not isinstance(another, int):\n            raise TypeError(\"加法只支持整数天数相加\")\n        return ZhDate.from_datetime(self.to_datetime() + timedelta(days=another))\n\n    def __sub__(self, another):\n        if isinstance(another, int):\n            return ZhDate.from_datetime(self.to_datetime() - timedelta(days=another))\n        elif isinstance(another, ZhDate):\n            return (self.to_datetime() - another.to_datetime()).days\n        elif isinstance(another, datetime):\n            return (self.to_datetime() - another).days\n        else:\n            raise TypeError(\"减法只支持整数，ZhDate, Datetime类型\")\n\n    \"\"\"\n    以下为帮助函数\n    \"\"\"\n\n    @staticmethod\n    def __tiandi(anum):\n        tian = \"甲乙丙丁戊己庚辛壬癸\"\n        di = \"子丑寅卯辰巳午未申酉戌亥\"\n        return \"{}{}\".format(tian[anum % 10], di[anum % 12])\n\n    @staticmethod\n    def validate(year, month, day, leap):\n        \"\"\"农历日期校验\n\n        Arguments:\n            year {int} -- 农历年份\n            month {int} -- 农历月份\n            day {int} -- 农历日期\n            leap {bool} -- 农历是否为闰月日期\n\n        Returns:\n            bool -- 校验是否通过\n        \"\"\"\n        # 年份低于1900，大于2100，或者月份不属于 1-12，或者日期不属于 1-30，返回校验失败\n        if not (1900 <= year <= 2100 and 1 <= month <= 12 and 1 <= day <= 30):\n            return False\n\n        year_code = CHINESEYEARCODE[year - 1900]\n\n        # 有闰月标志\n        if leap:\n            if (year_code & 0xF) != month:  # 年度闰月和校验闰月不一致的话，返回校验失败\n                return False\n            elif day == 30:  # 如果日期是30的话，直接返回年度代码首位是否为1，即闰月是否为大月\n                return (year_code >> 16) == 1\n            else:  # 年度闰月和当前月份相同，日期不为30的情况，返回通过\n                return True\n        elif day <= 29:  # 非闰月，并且日期小于等于29，返回通过\n            return True\n        else:  # 非闰月日期为30，返回年度代码中的月份位是否为1，即是否为大月\n            return ((year_code >> (12 - month) + 4) & 1) == 1\n\n    @staticmethod\n    def decode(year_code):\n        \"\"\"解析年度农历代码函数\n\n        Arguments:\n            year_code {int} -- 从年度代码数组中获取的代码整数\n\n        Returns:\n            list[int, ] -- 当前年度代码解析以后形成的每月天数数组，已将闰月嵌入对应位置，即有闰月的年份返回的列表长度为13，否则为12\n        \"\"\"\n        # 请问您为什么不在这么重要的地方写注释？\n        month_days = []\n        for i in range(4, 16):\n            # 向右移动相应的位数\n            # 1 这个数只有一位，与任何数进行 按位与 都只能获得其\n            # 从后往前第一位，对！是获得这一位\n            month_days.insert(0, 30 if (year_code >> i) & 1 else 29)\n\n        # 0xf 即 15 即二进制的 1111\n        # 所以 1111 与任何数进行 按位与\n        # 都将获得其最后四位，对！是获得这最后四位\n        # 后四位非0则表示有闰月（多一月），则插入一次月份\n        # 而首四位表示闰月的天数\n        if year_code & 0xF:\n            month_days.insert((year_code & 0xF), 30 if year_code >> 16 else 29)\n\n        # 返回一个列表\n        return month_days\n\n    @staticmethod\n    def month_days(year):\n        \"\"\"根据年份返回当前农历月份天数list\n\n        Arguments:\n            year {int} -- 1900到2100的之间的整数\n\n        Returns:\n            [int] -- 农历年份所对应的农历月份天数列表\n        \"\"\"\n        return ZhDate.decode(CHINESEYEARCODE[year - 1900])\n\n\n# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/ml/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule lables\nfrom .lables import *\nfrom .lables import __all__ as _lables_all\n\n__all__ += _lables_all\n\n# import all from submodule ml\nfrom .ml import *\nfrom .ml import __all__ as _ml_all\n\n__all__ += _ml_all\n"
  },
  {
    "path": "src/zvt/ml/lables.py",
    "content": "# -*- coding: utf-8 -*-\nfrom enum import Enum\n\n\nclass BehaviorCategory(Enum):\n    # 上涨\n    up = 1\n    # 下跌\n    down = -1\n\n\nclass RelativePerformance(Enum):\n    # 表现比90%好\n    best = 0.9\n    ordinary = 0.5\n    poor = 0\n\n\n# the __all__ is generated\n__all__ = [\"BehaviorCategory\", \"RelativePerformance\"]\n"
  },
  {
    "path": "src/zvt/ml/ml.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nfrom typing import Union, Type, List\n\nimport pandas as pd\nfrom sklearn.linear_model import LinearRegression, SGDRegressor\nfrom sklearn.pipeline import make_pipeline\nfrom sklearn.preprocessing import StandardScaler\n\nfrom zvt.api.kdata import default_adjust_type, get_kdata\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.contract import TradableEntity\nfrom zvt.contract.drawer import Drawer\nfrom zvt.domain import Stock\nfrom zvt.factors.transformers import MaTransformer\nfrom zvt.ml.lables import RelativePerformance, BehaviorCategory\nfrom zvt.utils.pd_utils import group_by_entity_id, normalize_group_compute_result, pd_is_not_null\nfrom zvt.utils.time_utils import to_pd_timestamp\n\nlogger = logging.getLogger(__name__)\n\n\ndef cal_change(s: pd.Series, predict_range):\n    return s.pct_change(periods=-predict_range)\n\n\ndef cal_behavior_cls(s: pd.Series, predict_range):\n    return s.pct_change(periods=-predict_range).apply(\n        lambda x: BehaviorCategory.up.value if x > 0 else BehaviorCategory.down.value\n    )\n\n\ndef cal_predict(s: pd.Series, predict_range):\n    return s.shift(periods=-predict_range)\n\n\ndef cal_relative_performance(s: pd.Series):\n    if s >= RelativePerformance.best.value:\n        return RelativePerformance.best\n    if s >= RelativePerformance.ordinary.value:\n        return RelativePerformance.ordinary\n    if s >= RelativePerformance.poor.value:\n        return RelativePerformance.poor\n\n\nclass MLMachine(object):\n    entity_schema: Type[TradableEntity] = None\n\n    def __init__(\n        self,\n        entity_ids: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = \"2015-01-01\",\n        end_timestamp: Union[str, pd.Timestamp] = \"2021-12-01\",\n        predict_start_timestamp: Union[str, pd.Timestamp] = \"2021-06-01\",\n        predict_steps: int = 20,\n        level: Union[IntervalLevel, str] = IntervalLevel.LEVEL_1DAY,\n        adjust_type: Union[AdjustType, str] = None,\n        data_provider: str = None,\n        label_method: str = \"raw\",\n    ) -> None:\n        \"\"\"\n\n        :param entity_ids:\n        :param start_timestamp:\n        :param end_timestamp:\n        :param predict_start_timestamp:\n        :param predict_steps:\n        :param level:\n        :param adjust_type:\n        :param data_provider:\n        :param label_method: raw, change, or behavior_cls\n        \"\"\"\n        super().__init__()\n        self.entity_ids = entity_ids\n        self.start_timestamp = to_pd_timestamp(start_timestamp)\n        self.end_timestamp = to_pd_timestamp(end_timestamp)\n        self.predict_start_timestamp = to_pd_timestamp(predict_start_timestamp)\n        assert self.start_timestamp < self.predict_start_timestamp < self.end_timestamp\n        self.predict_steps = predict_steps\n\n        self.level = level\n        if not adjust_type:\n            adjust_type = default_adjust_type(entity_type=self.entity_schema.__name__)\n        self.adjust_type = adjust_type\n\n        self.data_provider = data_provider\n        self.label_method = label_method\n\n        self.kdata_df = self.build_kdata()\n        if not pd_is_not_null(self.kdata_df):\n            logger.error(\"not kdta\")\n            assert False\n\n        self.feature_df = self.build_feature(self.entity_ids, self.start_timestamp, self.end_timestamp)\n        # drop na in feature\n        self.feature_df = self.feature_df.dropna()\n        self.feature_names = list(set(self.feature_df.columns) - {\"entity_id\", \"timestamp\"})\n        self.feature_df = self.feature_df.loc[:, self.feature_names]\n\n        self.label_ser = self.build_label()\n        # keep same index with feature df\n        self.label_ser = self.label_ser.loc[self.feature_df.index]\n        self.label_name = self.label_ser.name\n\n        self.training_X, self.training_y, self.testing_X, self.testing_y = self.split_data()\n\n        logger.info(self.training_X)\n        logger.info(self.training_y)\n\n        self.model = None\n        self.pred_y = None\n\n    def split_data(self):\n        training_x = self.feature_df[self.feature_df.index.get_level_values(\"timestamp\") < self.predict_start_timestamp]\n        training_y = self.label_ser[self.label_ser.index.get_level_values(\"timestamp\") < self.predict_start_timestamp]\n\n        testing_x = self.feature_df[self.feature_df.index.get_level_values(\"timestamp\") >= self.predict_start_timestamp]\n        testing_y = self.label_ser[self.label_ser.index.get_level_values(\"timestamp\") >= self.predict_start_timestamp]\n        return training_x, training_y, testing_x, testing_y\n\n    def build_kdata(self):\n        columns = [\"entity_id\", \"timestamp\", \"close\"]\n        return get_kdata(\n            entity_ids=self.entity_ids,\n            start_timestamp=self.start_timestamp,\n            end_timestamp=self.end_timestamp,\n            columns=columns,\n            level=self.level,\n            adjust_type=self.adjust_type,\n            provider=self.data_provider,\n            index=[\"entity_id\", \"timestamp\"],\n            drop_index_col=True,\n        )\n\n    def build_label(self):\n        label_name = f\"y_{self.predict_steps}\"\n        if self.label_method == \"raw\":\n            y = (\n                group_by_entity_id(self.kdata_df[\"close\"])\n                .apply(lambda x: cal_predict(x, self.predict_steps))\n                .rename(label_name)\n            )\n        elif self.label_method == \"change\":\n            y = (\n                group_by_entity_id(self.kdata_df[\"close\"])\n                .apply(lambda x: cal_change(x, self.predict_steps))\n                .rename(label_name)\n            )\n        elif self.label_method == \"behavior_cls\":\n            y = (\n                group_by_entity_id(self.kdata_df[\"close\"])\n                .apply(lambda x: cal_behavior_cls(x, self.predict_steps))\n                .rename(label_name)\n            )\n        else:\n            assert False\n        y = normalize_group_compute_result(y)\n\n        return y\n\n    def train(self, model=LinearRegression(), **params):\n        self.model = model.fit(self.training_X, self.training_y, **params)\n        return self.model\n\n    def draw_result(self, entity_id):\n        if self.label_method == \"raw\":\n            df = self.kdata_df.loc[[entity_id], [\"close\"]].copy()\n\n            pred_df = self.pred_y.to_frame(name=\"pred_close\")\n            pred_df = pred_df.loc[[entity_id], :].shift(self.predict_steps)\n\n            drawer = Drawer(\n                main_df=df,\n                factor_df_list=[pred_df],\n            )\n            drawer.draw_line(show=True)\n        else:\n            pred_df = self.pred_y.to_frame(name=\"pred_result\").loc[[entity_id], :]\n            df = self.testing_y.to_frame(name=\"real_result\").loc[[entity_id], :].join(pred_df, how=\"outer\")\n\n            drawer = Drawer(main_df=df)\n            drawer.draw_table()\n\n    def predict(self):\n        predictions = self.model.predict(self.testing_X)\n        self.pred_y = pd.Series(data=predictions, index=self.testing_y.index)\n        # explained_variance_score(self.testing_y, self.pred_y)\n        # mean_squared_error(self.testing_y, self.pred_y)\n\n    def build_feature(\n        self, entity_ids: List[str], start_timestamp: pd.Timestamp, end_timestamp: pd.Timestamp\n    ) -> pd.DataFrame:\n        \"\"\"\n        result df format\n                                  col1    col2    col3    ...\n        entity_id    timestamp\n                                  1.2     0.5     0.3     ...\n                                  1.0     0.7     0.2     ...\n\n        :param entity_ids: entity id list\n        :param start_timestamp:\n        :param end_timestamp:\n        :rtype: pd.DataFrame\n        \"\"\"\n        raise NotImplementedError\n\n\nclass StockMLMachine(MLMachine):\n    entity_schema = Stock\n\n\nclass MaStockMLMachine(StockMLMachine):\n    def build_feature(\n        self, entity_ids: List[str], start_timestamp: pd.Timestamp, end_timestamp: pd.Timestamp\n    ) -> pd.DataFrame:\n        \"\"\"\n\n        :param entity_ids:\n        :param start_timestamp:\n        :param end_timestamp:\n        :return:\n        \"\"\"\n        t = MaTransformer(windows=[5, 10, 120, 250])\n        df = t.transform(self.kdata_df)\n        return df\n\n\nif __name__ == \"__main__\":\n    machine = MaStockMLMachine(entity_ids=[\"stock_sz_000001\"])\n    reg = make_pipeline(StandardScaler(), SGDRegressor(max_iter=1000, tol=1e-3))\n    machine.train(model=reg)\n    machine.predict()\n    machine.draw_result(entity_id=\"stock_sz_000001\")\n\n# the __all__ is generated\n__all__ = [\n    \"cal_change\",\n    \"cal_behavior_cls\",\n    \"cal_predict\",\n    \"cal_relative_performance\",\n    \"MLMachine\",\n    \"StockMLMachine\",\n    \"MaStockMLMachine\",\n]\n"
  },
  {
    "path": "src/zvt/plugin.py",
    "content": "# -*- coding: utf-8 -*-\nimport argparse\n\nfrom zvt.autocode import gen_exports\nfrom zvt.autocode.generator import gen_plugin_project\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--entity\", help=\"entity name\", default=\"future\")\n    parser.add_argument(\"--prefix\", help=\"project prefix\", default=\"zvt\")\n    parser.add_argument(\"--dir\", help=\"project directory\", default=\".\")\n    parser.add_argument(\"--providers\", help=\"providers\", default=[\"joinquant\"], nargs=\"+\")\n\n    args = parser.parse_args()\n\n    dir_path = args.dir\n    entity = args.entity\n    providers = args.providers\n    prefix = args.prefix\n    gen_plugin_project(prefix=prefix, dir_path=dir_path, entity_type=entity, providers=providers)\n\n\ndef export():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--dir\", help=\"export directory\", default=\".\")\n    args = parser.parse_args()\n    dir_path = args.dir\n    gen_exports(dir_path=dir_path)\n\n\nif __name__ == \"__main__\":\n    gen_plugin_project(dir_path=\"../../../\", entity_type=\"macro\", providers=[\"zvt\"])\n    main()\n"
  },
  {
    "path": "src/zvt/recorders/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\nCHINA_STOCK_MAIN_INDEX = [\n    # # 聚宽编码\n    # # 市场通编码\t市场通名称\n    # # 310001\t沪股通\n    # # 310002\t深股通\n    # # 310003\t港股通（沪）\n    # # 310004\t港股通（深）\n    {\n        \"id\": \"index_cn_310001\",\n        \"entity_id\": \"index_cn_310001\",\n        \"code\": \"310001\",\n        \"name\": \"沪股通\",\n        \"timestamp\": \"2014-11-17\",\n        \"exchange\": \"cn\",\n        \"entity_type\": \"index\",\n        \"category\": \"other\",\n    },\n    {\n        \"id\": \"index_cn_310002\",\n        \"entity_id\": \"index_cn_310002\",\n        \"code\": \"310002\",\n        \"name\": \"深股通\",\n        \"timestamp\": \"2014-11-17\",\n        \"exchange\": \"cn\",\n        \"entity_type\": \"index\",\n        \"category\": \"other\",\n    },\n    {\n        \"id\": \"index_cn_310003\",\n        \"entity_id\": \"index_cn_310003\",\n        \"code\": \"310003\",\n        \"name\": \"港股通（沪）\",\n        \"timestamp\": \"2014-11-17\",\n        \"exchange\": \"cn\",\n        \"entity_type\": \"index\",\n        \"category\": \"other\",\n    },\n    {\n        \"id\": \"index_cn_310004\",\n        \"entity_id\": \"index_cn_310004\",\n        \"code\": \"310004\",\n        \"name\": \"港股通（深）\",\n        \"timestamp\": \"2014-11-17\",\n        \"exchange\": \"cn\",\n        \"entity_type\": \"index\",\n        \"category\": \"other\",\n    },\n]\n\n\ndef init_main_index(provider=\"exchange\"):\n    from zvt.utils.time_utils import to_pd_timestamp\n    import pandas as pd\n    from zvt.contract.api import df_to_db\n    from zvt.domain.meta import Index\n\n    for item in CHINA_STOCK_MAIN_INDEX:\n        item[\"timestamp\"] = to_pd_timestamp(item[\"timestamp\"])\n    df = pd.DataFrame(CHINA_STOCK_MAIN_INDEX)\n    # print(df)\n    df_to_db(df=df, data_schema=Index, provider=provider, force_update=False)\n\n\n# the __all__ is generated\n__all__ = [\"init_main_index\"]\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule consts\nfrom .consts import *\nfrom .consts import __all__ as _consts_all\n\n__all__ += _consts_all\n\n# import all from submodule jqka\nfrom .jqka import *\nfrom .jqka import __all__ as _jqka_all\n\n__all__ += _jqka_all\n\n# import all from submodule eastmoney\nfrom .eastmoney import *\nfrom .eastmoney import __all__ as _eastmoney_all\n\n__all__ += _eastmoney_all\n\n# import all from submodule em\nfrom .em import *\nfrom .em import __all__ as _em_all\n\n__all__ += _em_all\n\n# import all from submodule exchange\nfrom .exchange import *\nfrom .exchange import __all__ as _exchange_all\n\n__all__ += _exchange_all\n\n# import all from submodule wb\nfrom .wb import *\nfrom .wb import __all__ as _wb_all\n\n__all__ += _wb_all\n\n# import all from submodule joinquant\nfrom .joinquant import *\nfrom .joinquant import __all__ as _joinquant_all\n\n__all__ += _joinquant_all\n\n# import all from submodule sina\nfrom .sina import *\nfrom .sina import __all__ as _sina_all\n\n__all__ += _sina_all\n\n# Recorders must be imported before schema use (providers derived from Recorder registration)\ninit_main_index(provider=\"exchange\")\n"
  },
  {
    "path": "src/zvt/recorders/consts.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.utils.utils import chrome_copy_header_to_dict\n\nSSE_KDATA_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nHost: yunhq.sse.com.cn:32041\nConnection: keep-alive\nPragma: no-cache\nCache-Control: no-cache\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\nAccept: */*\nReferer: http://www.sse.com.cn/market/price/trends/\nAccept-Encoding: gzip, deflate\nAccept-Language: zh-CN,zh;q=0.8,en;q=0.6\nCookie: yfx_c_g_u_id_10000042=_ck17072000172016360411059933357; yfx_f_l_v_t_10000042=f_t_1500481040618__r_t_1507560823182__v_t_1507561607501__r_c_1; VISITED_MENU=%5B%228451%22%2C%228453%22%5D\n\"\"\"\n)\n\nDEFAULT_SH_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nAccept: */*\nAccept-Encoding: gzip, deflate\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\nConnection: keep-alive\nCookie: yfx_c_g_u_id_10000042=_ck21052517524516575117884257529; yfx_f_l_v_t_10000042=f_t_1621936365643__r_t_1621936365643__v_t_1621936365643__r_c_0; VISITED_MENU=%5B%228466%22%2C%228528%22%5D; JSESSIONID=3C970EA11443D2B7F3844558174C2CDB\nHost: query.sse.com.cn\nReferer: http://www.sse.com.cn/\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36\n\"\"\"\n)\n\nDEFAULT_SZ_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nAccept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\nAccept-Encoding:gzip, deflate, sdch\nAccept-Language:zh-CN,zh;q=0.8,en;q=0.6\nConnection:keep-alive\nHost:www.szse.cn\nReferer:http://www.szse.cn/main/marketdata/jypz/colist/\nUpgrade-Insecure-Requests:1\nUser-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36\n\"\"\"\n)\n\nDEFAULT_TICK_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nHost: market.finance.sina.com.cn\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\nAccept-Encoding: gzip, deflate, sdch\nAccept-Language: zh-CN,zh;q=0.8,en;q=0.6\nCookie: U_TRS1=000000b3.fffc2f53.566f784b.2900c0a6; UOR=www.baidu.com,blog.sina.com.cn,; SINAGLOBAL=182.139.185.185_1450145868.176138; vjuids=-fde6fe2f2.151a36dec26.0.113c0d8; SGUID=1450145869010_36029656; Apache=182.139.185.132_1457087046.693218; ULV=1457087047846:3:2:2:182.139.185.132_1457087046.693218:1457087047557; U_TRS2=0000009f.95a9161b.57038c9e.11b3a69f; SessionID=e2ajs2t3k4emag633adfjho861; SINA_FINANCE_SELECT_TYPE=stock; SCF=AhFAeZgjMw_ozKbe2MXZtX2fG8ZrLemvX24bd57wciL98hfpOY_4hN8aYqvvaxZDbINdQq3XqZSrS-ubJo-5VUI.; sso_info=v02m6alo5qztbaYloWum6akpp2WpaSPk4S1jpOYsYyjlLONg5DA; SUP=cv%3D1%26bt%3D1470117772%26et%3D1470204172%26d%3D40c3%26i%3De2a8%26us%3D0%26vf%3D0%26vt%3D0%26ac%3D2%26st%3D0%26lt%3D7%26uid%3D1596125344%26user%3Djackofxuan%2540gmail.com%26ag%3D4%26name%3Djackofxuan%2540gmail.com%26nick%3Dvaanni%26sex%3D%26ps%3D0%26email%3D%26dob%3D%26ln%3D%26os%3D%26fmp%3D%26lcp%3D2011-12-26%252017%253A00%253A23; __utma=269849203.900451459.1468839849.1468839849.1471431141.2; __utmc=269849203; __utmz=269849203.1471431141.2.2.utmcsr=finance.sina.com.cn|utmccn=(referral)|utmcmd=referral|utmcct=/; SINABLOGNUINFO=1596125344.5f22f0a0.vannii; SINA_FINANCE=%3A1596125344%3A4; hk_visited_stocks=02338; WEB2_APACHE2_GD=1401fbd37fe42dfbc571af9dacb5e317; _s_upa=25; lxlrtst=1475225080_o; SR_SEL=1_511; SINA_PORTFOLIO=sh601600%2Csh600089%2Csz000528%2Csh600150%2Csz000338%2Csh600031%2Csh600199%2Csh000001%2Csz399001%2Csz000778%2Csh600315%2Csh600570%2Csh601608%2Csz000099%2Csh600439%2Csz000521%2Csz300276%2Csz000783%2Csh600396%2Csz300336%2Csz000751%2Csz000848%2Csh600711%2Csh600880%2Csh600362%2Csh600060%2Csz000680%2Csz002051; ArtiFSize=14; visited_uss=gb_goog%7Cgb_sina%7Cgb_.dji; FINA_DMHQ=0; hqEtagMode=1; VISITED_FUTURE=hf_CL_0%2Chf_GC_0%2Chf_SI_0%2Chf_CAD_0%2CCF1609_1%2CRB0_1%2Chf_DJS_0%2CCFF_RE_IF1606_2%2CCFF_RE_IF1510_2%2CCFF_RE_IC1606_2%2CRM0_1%2CRB1610_1; lxlrttp=1476092678; FINA_V5_HQ=0; SUB=_2A251BPWoDeTxGedL4lQQ8ivPzziIHXVWcGBgrDV_PUNbm9BeLXLzkW8iLp81bNoV5si_9RFc9X4XY1wBcg..; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9W5J7vXaOMNYJQ0d27EZPWIF5NHD95QpSK.ceKzfe0BXWs4Dqcj.i--fi-zRiKnEi--fiKLhi-iWi--Xi-isi-88i--Ri-2piKyh; ALF=1507965304; FIN_ALL_VISITED=sz000338%2Csh000001%2Csz000680%2Csz399006%2Csh600028%2Csh600315%2Csh600031%2Csh601608%2Csh601038%2Csh601628%2CCL%2Csh600150%2Csz300369%2Csz300079%2CAGTD%2Csz300336%2Csz300276%2Cgoog%2Csina%2CGC%2CSI%2Csh600004%2Csh600000%2Csz300374%2Csh600199%2Csh600439%2Csz000783%2Csz000912%2CCAD%2Csz002628%2Csh600362%2Csz002051%2Csz000930%2Csz000528%2Csh603568%2Csh600060%2Csh600635%2Csz150266%2Csz000651%2Csz000099%2Csh600570%2Csz000002%2Csh600048%2Csz000839%2Csz002223%2Csh600880%2Csz000521%2Csz000878%2Csz399001%2Csh600396%2Csz000751%2Csh600016%2Csz000915%2Csh600779%2Csh600497%2Csh601600%2Csh600111%2Csh600550%2Csz000426%2Csz002237; FINA_LV2_S_2=1596125344; FINA_LV2_S_2_B=0; rotatecount=1; vjlast=1476433318; FINA_V_S_2=sz000338,sh000001,sz000680,sz399006,sh600028,sh600315,sh600031,sh601608,sh601038,sh601628,sh600150,sz300369,sz300079,sz300336,sz300276,sh600004,sh600000,sz300374,sh600199,sh600439\nReferer:market.finance.sina.com.cn\nUpgrade-Insecure-Requests: 1\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36\n\"\"\"\n)\n\nDEFAULT_KDATA_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nAccept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\nAccept-Encoding:gzip, deflate, sdch\nAccept-Language:zh-CN,zh;q=0.8,en;q=0.6\nCache-Control:max-age=0\nConnection:keep-alive\nHost:vip.stock.finance.sina.com.cn\nReferer:vip.stock.finance.sina.com.cn\nUpgrade-Insecure-Requests:1\nUser-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36\n\"\"\"\n)\n\nTONGHUASHUN_GN_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nAccept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\nAccept-Encoding:gzip, deflate, sdch\nAccept-Language:zh-CN,zh;q=0.8,en;q=0.6\nCache-Control:max-age=0\nConnection:keep-alive\nCookie:Hm_lvt_ab89213e83c551bf095446c08bf64988=1477116434; Hm_lpvt_ab89213e83c551bf095446c08bf64988=1477116434; historystock=600126; spversion=20130314; Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1477105757,1477116434; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1477362626\nHost:q.10jqka.com.cn\nUpgrade-Insecure-Requests:1\nUser-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36\n\"\"\"\n)\n\nTONGHUASHUN_KDATA_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nAccept-Encoding: gzip, deflate\nAccept-Language: zh-CN,zh;q=0.8,en;q=0.6\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\nAccept: */*\nReferer: http://stockpage.10jqka.com.cn/HQ_v4.html\nCookie: __utma=156575163.1843700306.1488352720.1499234323.1502172029.4; __utmc=156575163; __utmz=156575163.1488352720.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); spversion=20130314; Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1507300869; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1508464591; historystock=603189%7C*%7C300295%7C*%7C600839%7C*%7C000338%7C*%7C002194; log=; v=AREjaxfGLPcJoUDd5wHRp1QiKRaufoSAL_MpD_OlDDL35T_CO86VwL9CPa2D\nConnection: keep-alive\n\"\"\"\n)\n\nDEFAULT_BALANCE_SHEET_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nHost: money.finance.sina.com.cn\nUser-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\nAccept-Language: en-US,en;q=0.5\nAccept-Encoding: gzip, deflate\nReferer: http://vip.stock.finance.sina.com.cn/corp/go.php/vFD_CashFlow/stockid/000338/ctrl/part/displaytype/4.phtml\nCookie: U_TRS1=000000be.c95848c3.59817e10.a54886e2; U_TRS2=000000be.c96a48c3.59817e10.a91795e2; UOR=,vip.stock.finance.sina.com.cn,; ULV=1501658645426:2:2:2:182.148.114.190_1501658642.469995:1501658642409; SINAGLOBAL=182.148.114.190_1501658642.469991; Apache=182.148.114.190_1501658642.469995; _s_upa=1; SUB=_2A250hQ5nDeRhGedL4lQQ8ivPzziIHXVX83ivrDV_PUNbm9BeLXigkW8-niaOks2yNkw8lYo-TvoqGk6nRA..; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9W5J7vXaOMNYJQ0d27EZPWIF5NHD95QpSK.ceKzfe0BXWs4Dqcj.i--fi-zRiKnEi--fiKLhi-iWi--Xi-isi-88i--Ri-2piKyh; SCF=At4whqZZyjTBTvcLfR0tyqIpfHUX2VOK-qvBVHkbyahiCVcr4-8NjJQGHwCaTtkQJ0SPmrzvZARwtEkL1I_46z8.; ALF=1533194679; sso_info=v02m6alo5qztbaYloWum6akpp2WpaSPk4S1jpOYsYyjlLONg5DA; FINANCE2=f7634b1d12920e2763ffc0dc463ef6bb\nConnection: keep-alive\nUpgrade-Insecure-Requests: 1\n\"\"\"\n)\n\nDEFAULT_SH_SUMMARY_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nHost: query.sse.com.cn\nConnection: keep-alive\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\nAccept: */*\nReferer: http://www.sse.com.cn/market/stockdata/overview/day/\nAccept-Encoding: gzip, deflate\nAccept-Language: zh-CN,zh;q=0.8,en;q=0.6\nCookie: yfx_c_g_u_id_10000042=_ck17122009304714819234313401740; VISITED_COMPANY_CODE=%5B%22000016%22%5D; VISITED_INDEX_CODE=%5B%22000016%22%5D; yfx_f_l_v_t_10000042=f_t_1513733447386__r_t_1515716891222__v_t_1515721033042__r_c_3; VISITED_MENU=%5B%228464%22%2C%229666%22%2C%229668%22%2C%229669%22%2C%228454%22%2C%228460%22%2C%229665%22%2C%228459%22%2C%229692%22%2C%228451%22%2C%228466%22%5D\n\"\"\"\n)\n\nDEFAULT_SH_ETF_LIST_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nHost: query.sse.com.cn\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36\nAccept: */*\nReferer: http://www.sse.com.cn/assortment/fund/etf/list/\nAccept-Encoding: gzip, deflate\nAccept-Language: zh-CN,zh;q=0.9\nCookie: yfx_c_g_u_id_10000042=_ck19062609443812815766114343798; VISITED_COMPANY_CODE=%5B%22510300%22%5D; VISITED_FUND_CODE=%5B%22510300%22%5D; VISITED_MENU=%5B%228307%22%2C%228823%22%2C%228547%22%2C%228556%22%2C%228549%22%2C%2210848%22%2C%228550%22%5D; yfx_f_l_v_t_10000042=f_t_1561513478278__r_t_1561692626758__v_t_1561695738302__r_c_1\nConnection: keep-alive\n\"\"\"\n)\n\nEASTMONEY_ETF_NET_VALUE_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36\nReferer: http://fund.eastmoney.com/\n\"\"\"\n)\n\nDEFAULT_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36\n\"\"\"\n)\n\n\n# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule common\nfrom .common import *\nfrom .common import __all__ as _common_all\n\n__all__ += _common_all\n\n# import all from submodule trading\nfrom .trading import *\nfrom .trading import __all__ as _trading_all\n\n__all__ += _trading_all\n\n# import all from submodule holder\nfrom .holder import *\nfrom .holder import __all__ as _holder_all\n\n__all__ += _holder_all\n\n# import all from submodule finance\nfrom .finance import *\nfrom .finance import __all__ as _finance_all\n\n__all__ += _finance_all\n\n# import all from submodule meta\nfrom .meta import *\nfrom .meta import __all__ as _meta_all\n\n__all__ += _meta_all\n\n# import all from submodule dividend_financing\nfrom .dividend_financing import *\nfrom .dividend_financing import __all__ as _dividend_financing_all\n\n__all__ += _dividend_financing_all\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/common.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nimport requests\n\nfrom zvt.contract.api import get_data_count, get_data\nfrom zvt.contract.recorder import TimestampsDataRecorder, TimeSeriesDataRecorder\nfrom zvt.domain import CompanyType\nfrom zvt.domain.meta.stock_meta import StockDetail\nfrom zvt.utils.time_utils import to_pd_timestamp\n\nlogger = logging.getLogger(__name__)\n\n\nclass ApiWrapper(object):\n    def request(self, url=None, method=\"post\", param=None, path_fields=None):\n        raise NotImplementedError\n\n\ndef get_fc(security_item):\n    if security_item.exchange == \"sh\":\n        fc = \"{}01\".format(security_item.code)\n    if security_item.exchange == \"sz\":\n        fc = \"{}02\".format(security_item.code)\n\n    return fc\n\n\ndef get_company_type(stock_domain: StockDetail):\n    industries = stock_domain.industries.split(\",\")\n    if (\"银行\" in industries) or (\"信托\" in industries):\n        return CompanyType.yinhang\n    if \"保险\" in industries:\n        return CompanyType.baoxian\n    if \"证券\" in industries:\n        return CompanyType.quanshang\n    return CompanyType.qiye\n\n\ndef company_type_flag(security_item):\n    try:\n        company_type = get_company_type(security_item)\n\n        if company_type == CompanyType.qiye:\n            return \"4\"\n        if company_type == CompanyType.quanshang:\n            return \"1\"\n        if company_type == CompanyType.baoxian:\n            return \"2\"\n        if company_type == CompanyType.yinhang:\n            return \"3\"\n    except Exception as e:\n        logger.warning(e)\n\n    param = {\"color\": \"w\", \"fc\": get_fc(security_item)}\n\n    resp = requests.post(\"https://emh5.eastmoney.com/api/CaiWuFenXi/GetCompanyType\", json=param)\n\n    ct = resp.json().get(\"Result\").get(\"CompanyType\")\n\n    logger.warning(\"{} not catching company type:{}\".format(security_item, ct))\n\n    return ct\n\n\ndef call_eastmoney_api(url=None, method=\"post\", param=None, path_fields=None):\n    if method == \"post\":\n        resp = requests.post(url, json=param)\n\n    resp.encoding = \"utf-8\"\n\n    try:\n        origin_result = resp.json().get(\"Result\")\n    except Exception as e:\n        logger.exception(\"code:{},content:{}\".format(resp.status_code, resp.text))\n        raise e\n\n    if path_fields:\n        the_data = get_from_path_fields(origin_result, path_fields)\n        if not the_data:\n            logger.warning(\n                \"url:{},param:{},origin_result:{},could not get data for nested_fields:{}\".format(\n                    url, param, origin_result, path_fields\n                )\n            )\n        return the_data\n\n    return origin_result\n\n\ndef get_from_path_fields(the_json, path_fields):\n    the_data = the_json.get(path_fields[0])\n    if the_data:\n        for field in path_fields[1:]:\n            the_data = the_data.get(field)\n            if not the_data:\n                return None\n    return the_data\n\n\nclass EastmoneyApiWrapper(ApiWrapper):\n    def request(self, url=None, method=\"post\", param=None, path_fields=None):\n        return call_eastmoney_api(url=url, method=method, param=param, path_fields=path_fields)\n\n\nclass BaseEastmoneyRecorder(object):\n    request_method = \"post\"\n    path_fields = None\n    api_wrapper = EastmoneyApiWrapper()\n\n    def generate_request_param(self, security_item, start, end, size, timestamp):\n        raise NotImplementedError\n\n    def record(self, entity_item, start, end, size, timestamps):\n        if timestamps:\n            original_list = []\n            for the_timestamp in timestamps:\n                param = self.generate_request_param(entity_item, start, end, size, the_timestamp)\n                tmp_list = self.api_wrapper.request(\n                    url=self.url, param=param, method=self.request_method, path_fields=self.path_fields\n                )\n                self.logger.info(\n                    \"record {} for entity_id:{},timestamp:{}\".format(self.data_schema, entity_item.id, the_timestamp)\n                )\n                # fill timestamp field\n                for tmp in tmp_list:\n                    tmp[self.get_evaluated_time_field()] = the_timestamp\n                original_list += tmp_list\n                if len(original_list) == 50:\n                    break\n            return original_list\n\n        else:\n            param = self.generate_request_param(entity_item, start, end, size, None)\n            return self.api_wrapper.request(\n                url=self.url, param=param, method=self.request_method, path_fields=self.path_fields\n            )\n\n\nclass EastmoneyTimestampsDataRecorder(BaseEastmoneyRecorder, TimestampsDataRecorder):\n    entity_provider = \"eastmoney\"\n    entity_schema = StockDetail\n\n    provider = \"eastmoney\"\n\n    timestamps_fetching_url = None\n    timestamp_list_path_fields = None\n    timestamp_path_fields = None\n\n    def init_timestamps(self, entity):\n        param = {\"color\": \"w\", \"fc\": get_fc(entity)}\n\n        timestamp_json_list = call_eastmoney_api(\n            url=self.timestamps_fetching_url, path_fields=self.timestamp_list_path_fields, param=param\n        )\n\n        if self.timestamp_path_fields and timestamp_json_list:\n            timestamps = [get_from_path_fields(data, self.timestamp_path_fields) for data in timestamp_json_list]\n            return [to_pd_timestamp(t) for t in timestamps]\n        return []\n\n\nclass EastmoneyPageabeDataRecorder(BaseEastmoneyRecorder, TimeSeriesDataRecorder):\n    entity_provider = \"eastmoney\"\n    entity_schema = StockDetail\n\n    provider = \"eastmoney\"\n\n    page_url = None\n\n    def get_remote_count(self, security_item):\n        param = {\"color\": \"w\", \"fc\": get_fc(security_item), \"pageNum\": 1, \"pageSize\": 1}\n        return call_eastmoney_api(self.page_url, param=param, path_fields=[\"TotalCount\"])\n\n    def evaluate_start_end_size_timestamps(self, entity):\n        remote_count = self.get_remote_count(entity)\n\n        if remote_count == 0:\n            return None, None, 0, None\n\n        # get local count\n        local_count = get_data_count(\n            data_schema=self.data_schema, session=self.session, filters=[self.data_schema.entity_id == entity.id]\n        )\n        # FIXME:the > case\n        if local_count >= remote_count:\n            return None, None, 0, None\n\n        return None, None, remote_count - local_count, None\n\n    def generate_request_param(self, security_item, start, end, size, timestamp):\n        return {\n            \"color\": \"w\",\n            \"fc\": get_fc(security_item),\n            \"pageNum\": 1,\n            # just get more for some fixed data\n            \"pageSize\": size + 10,\n        }\n\n\nclass EastmoneyMoreDataRecorder(BaseEastmoneyRecorder, TimeSeriesDataRecorder):\n    entity_provider = \"eastmoney\"\n    entity_schema = StockDetail\n\n    provider = \"eastmoney\"\n\n    def get_remote_latest_record(self, security_item):\n        param = {\"color\": \"w\", \"fc\": get_fc(security_item), \"pageNum\": 1, \"pageSize\": 1}\n        results = call_eastmoney_api(self.url, param=param, path_fields=self.path_fields)\n        _, result = self.generate_domain(security_item, results[0])\n        return result\n\n    def evaluate_start_end_size_timestamps(self, entity):\n        # get latest record\n        latest_record = get_data(\n            entity_id=entity.id,\n            provider=self.provider,\n            data_schema=self.data_schema,\n            order=self.data_schema.timestamp.desc(),\n            limit=1,\n            return_type=\"domain\",\n            session=self.session,\n        )\n        if latest_record:\n            remote_record = self.get_remote_latest_record(entity)\n            if not remote_record or (latest_record[0].id == remote_record.id):\n                return None, None, 0, None\n            else:\n                return None, None, 10, None\n\n        return None, None, 1000, None\n\n    def generate_request_param(self, security_item, start, end, size, timestamp):\n        return {\"color\": \"w\", \"fc\": get_fc(security_item), \"pageNum\": 1, \"pageSize\": size}\n\n\n# the __all__ is generated\n__all__ = [\n    \"ApiWrapper\",\n    \"get_fc\",\n    \"get_company_type\",\n    \"company_type_flag\",\n    \"call_eastmoney_api\",\n    \"get_from_path_fields\",\n    \"EastmoneyApiWrapper\",\n    \"BaseEastmoneyRecorder\",\n    \"EastmoneyTimestampsDataRecorder\",\n    \"EastmoneyPageabeDataRecorder\",\n    \"EastmoneyMoreDataRecorder\",\n]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/dividend_financing/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule eastmoney_dividend_detail_recorder\nfrom .eastmoney_dividend_detail_recorder import *\nfrom .eastmoney_dividend_detail_recorder import __all__ as _eastmoney_dividend_detail_recorder_all\n\n__all__ += _eastmoney_dividend_detail_recorder_all\n\n# import all from submodule eastmoney_rights_issue_detail_recorder\nfrom .eastmoney_rights_issue_detail_recorder import *\nfrom .eastmoney_rights_issue_detail_recorder import __all__ as _eastmoney_rights_issue_detail_recorder_all\n\n__all__ += _eastmoney_rights_issue_detail_recorder_all\n\n# import all from submodule eastmoney_dividend_financing_recorder\nfrom .eastmoney_dividend_financing_recorder import *\nfrom .eastmoney_dividend_financing_recorder import __all__ as _eastmoney_dividend_financing_recorder_all\n\n__all__ += _eastmoney_dividend_financing_recorder_all\n\n# import all from submodule eastmoney_spo_detail_recorder\nfrom .eastmoney_spo_detail_recorder import *\nfrom .eastmoney_spo_detail_recorder import __all__ as _eastmoney_spo_detail_recorder_all\n\n__all__ += _eastmoney_spo_detail_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/dividend_financing/eastmoney_dividend_detail_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain import DividendDetail\nfrom zvt.recorders.eastmoney.common import EastmoneyPageabeDataRecorder\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\nclass DividendDetailRecorder(EastmoneyPageabeDataRecorder):\n    data_schema = DividendDetail\n\n    url = \"https://emh5.eastmoney.com/api/FenHongRongZi/GetFenHongSongZhuanList\"\n    page_url = url\n    path_fields = [\"FenHongSongZhuanList\"]\n\n    def get_original_time_field(self):\n        return \"GongGaoRiQi\"\n\n    def get_data_map(self):\n        return {\n            # 公告日\n            \"announce_date\": (\"GongGaoRiQi\", to_pd_timestamp),\n            # 股权登记日\n            \"record_date\": (\"GuQuanDengJiRi\", to_pd_timestamp),\n            # 除权除息日\n            \"dividend_date\": (\"ChuQuanChuXiRi\", to_pd_timestamp),\n            # 方案\n            \"dividend\": (\"FengHongFangAn\", str),\n        }\n\n\nif __name__ == \"__main__\":\n    # init_log('dividend_detail.log')\n\n    recorder = DividendDetailRecorder(codes=[\"601318\"])\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"DividendDetailRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/dividend_financing/eastmoney_dividend_financing_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain.fundamental.dividend_financing import DividendFinancing\nfrom zvt.recorders.eastmoney.common import EastmoneyPageabeDataRecorder\nfrom zvt.utils.utils import second_item_to_float\n\n\nclass DividendFinancingRecorder(EastmoneyPageabeDataRecorder):\n    data_schema = DividendFinancing\n\n    url = \"https://emh5.eastmoney.com/api/FenHongRongZi/GetLiNianFenHongRongZiList\"\n    page_url = url\n    path_fields = [\"LiNianFenHongRongZiList\"]\n\n    def get_original_time_field(self):\n        return \"ShiJian\"\n\n    def get_data_map(self):\n        return {\n            # 分红总额\n            \"dividend_money\": (\"FenHongZongE\", second_item_to_float),\n            # 新股\n            \"ipo_issues\": (\"XinGu\", second_item_to_float),\n            # 增发\n            \"spo_issues\": (\"ZengFa\", second_item_to_float),\n            # 配股\n            \"rights_issues\": (\"PeiFa\", second_item_to_float),\n        }\n\n    def on_finish(self):\n        try:\n            code_security = {}\n            for item in self.entities:\n                code_security[item.code] = item\n\n                need_fill_items = DividendFinancing.query_data(\n                    provider=self.provider,\n                    codes=list(code_security.keys()),\n                    return_type=\"domain\",\n                    session=self.session,\n                    filters=[DividendFinancing.ipo_raising_fund.is_(None), DividendFinancing.ipo_issues != 0],\n                )\n\n                for need_fill_item in need_fill_items:\n                    if need_fill_item:\n                        need_fill_item.ipo_raising_fund = code_security[item.code].raising_fund\n                        self.session.commit()\n        except Exception as e:\n            self.logger.exception(e)\n\n        super().on_finish()\n\n\nif __name__ == \"__main__\":\n    # init_log('dividend_financing.log')\n\n    recorder = DividendFinancingRecorder(codes=[\"000999\"])\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"DividendFinancingRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/dividend_financing/eastmoney_rights_issue_detail_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.consts import SAMPLE_STOCK_CODES\nfrom zvt.domain import RightsIssueDetail, DividendFinancing\nfrom zvt.recorders.eastmoney.common import EastmoneyPageabeDataRecorder\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import now_pd_timestamp\nfrom zvt.utils.utils import to_float\n\n\nclass RightsIssueDetailRecorder(EastmoneyPageabeDataRecorder):\n    data_schema = RightsIssueDetail\n\n    url = \"https://emh5.eastmoney.com/api/FenHongRongZi/GetPeiGuMingXiList\"\n    page_url = url\n    path_fields = [\"PeiGuMingXiList\"]\n\n    def get_original_time_field(self):\n        return \"PeiGuGongGaoRi\"\n\n    def get_data_map(self):\n        return {\n            \"rights_issues\": (\"ShiJiPeiGu\", to_float),\n            \"rights_issue_price\": (\"PeiGuJiaGe\", to_float),\n            \"rights_raising_fund\": (\"ShiJiMuJi\", to_float),\n        }\n\n    def on_finish(self):\n        last_year = str(now_pd_timestamp().year)\n        codes = [item.code for item in self.entities]\n        need_filleds = DividendFinancing.query_data(\n            provider=self.provider,\n            codes=codes,\n            return_type=\"domain\",\n            session=self.session,\n            filters=[DividendFinancing.rights_raising_fund.is_(None)],\n            end_timestamp=last_year,\n        )\n\n        for item in need_filleds:\n            df = RightsIssueDetail.query_data(\n                provider=self.provider,\n                entity_id=item.entity_id,\n                columns=[RightsIssueDetail.timestamp, RightsIssueDetail.rights_raising_fund],\n                start_timestamp=item.timestamp,\n                end_timestamp=\"{}-12-31\".format(item.timestamp.year),\n            )\n            if pd_is_not_null(df):\n                item.rights_raising_fund = df[\"rights_raising_fund\"].sum()\n                self.session.commit()\n\n        super().on_finish()\n\n\nif __name__ == \"__main__\":\n    # init_log('rights_issue.log')\n\n    recorder = RightsIssueDetailRecorder(codes=SAMPLE_STOCK_CODES)\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"RightsIssueDetailRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/dividend_financing/eastmoney_spo_detail_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain import SpoDetail, DividendFinancing\nfrom zvt.recorders.eastmoney.common import EastmoneyPageabeDataRecorder\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import now_pd_timestamp\nfrom zvt.utils.utils import to_float\n\n\nclass SPODetailRecorder(EastmoneyPageabeDataRecorder):\n    data_schema = SpoDetail\n\n    url = \"https://emh5.eastmoney.com/api/FenHongRongZi/GetZengFaMingXiList\"\n    page_url = url\n    path_fields = [\"ZengFaMingXiList\"]\n\n    def get_original_time_field(self):\n        return \"ZengFaShiJian\"\n\n    def get_data_map(self):\n        return {\n            \"spo_issues\": (\"ShiJiZengFa\", to_float),\n            \"spo_price\": (\"ZengFaJiaGe\", to_float),\n            \"spo_raising_fund\": (\"ShiJiMuJi\", to_float),\n        }\n\n    def on_finish(self):\n        last_year = str(now_pd_timestamp().year)\n        codes = [item.code for item in self.entities]\n        need_filleds = DividendFinancing.query_data(\n            provider=self.provider,\n            codes=codes,\n            return_type=\"domain\",\n            session=self.session,\n            filters=[DividendFinancing.spo_raising_fund.is_(None)],\n            end_timestamp=last_year,\n        )\n\n        for item in need_filleds:\n            df = SpoDetail.query_data(\n                provider=self.provider,\n                entity_id=item.entity_id,\n                columns=[SpoDetail.timestamp, SpoDetail.spo_raising_fund],\n                start_timestamp=item.timestamp,\n                end_timestamp=\"{}-12-31\".format(item.timestamp.year),\n            )\n            if pd_is_not_null(df):\n                item.spo_raising_fund = df[\"spo_raising_fund\"].sum()\n                self.session.commit()\n        super().on_finish()\n\n\nif __name__ == \"__main__\":\n    # init_log('spo_detail.log')\n\n    recorder = SPODetailRecorder(codes=[\"000999\"])\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"SPODetailRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/finance/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule eastmoney_cash_flow_recorder\nfrom .eastmoney_cash_flow_recorder import *\nfrom .eastmoney_cash_flow_recorder import __all__ as _eastmoney_cash_flow_recorder_all\n\n__all__ += _eastmoney_cash_flow_recorder_all\n\n# import all from submodule eastmoney_income_statement_recorder\nfrom .eastmoney_income_statement_recorder import *\nfrom .eastmoney_income_statement_recorder import __all__ as _eastmoney_income_statement_recorder_all\n\n__all__ += _eastmoney_income_statement_recorder_all\n\n# import all from submodule eastmoney_finance_factor_recorder\nfrom .eastmoney_finance_factor_recorder import *\nfrom .eastmoney_finance_factor_recorder import __all__ as _eastmoney_finance_factor_recorder_all\n\n__all__ += _eastmoney_finance_factor_recorder_all\n\n# import all from submodule base_china_stock_finance_recorder\nfrom .base_china_stock_finance_recorder import *\nfrom .base_china_stock_finance_recorder import __all__ as _base_china_stock_finance_recorder_all\n\n__all__ += _base_china_stock_finance_recorder_all\n\n# import all from submodule eastmoney_balance_sheet_recorder\nfrom .eastmoney_balance_sheet_recorder import *\nfrom .eastmoney_balance_sheet_recorder import __all__ as _eastmoney_balance_sheet_recorder_all\n\n__all__ += _eastmoney_balance_sheet_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/finance/base_china_stock_finance_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom jqdatapy.api import get_fundamentals, get_query_count\n\nfrom zvt.api.utils import to_report_period_type\nfrom zvt.contract.api import get_data\nfrom zvt.domain import FinanceFactor, ReportPeriod\nfrom zvt.recorders.eastmoney.common import (\n    company_type_flag,\n    get_fc,\n    EastmoneyTimestampsDataRecorder,\n    call_eastmoney_api,\n    get_from_path_fields,\n)\nfrom zvt.recorders.joinquant.common import to_jq_entity_id\nfrom zvt.utils.pd_utils import index_df\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_date_time_str, to_pd_timestamp\n\n\ndef to_jq_report_period(timestamp):\n    the_date = to_pd_timestamp(timestamp)\n    report_period = to_report_period_type(timestamp)\n    if report_period == ReportPeriod.year.value:\n        return \"{}\".format(the_date.year)\n    if report_period == ReportPeriod.season1.value:\n        return \"{}q1\".format(the_date.year)\n    if report_period == ReportPeriod.half_year.value:\n        return \"{}q2\".format(the_date.year)\n    if report_period == ReportPeriod.season3.value:\n        return \"{}q3\".format(the_date.year)\n\n    assert False\n\n\nclass BaseChinaStockFinanceRecorder(EastmoneyTimestampsDataRecorder):\n    finance_report_type = None\n    data_type = 1\n\n    timestamps_fetching_url = \"https://emh5.eastmoney.com/api/CaiWuFenXi/GetCompanyReportDateList\"\n    timestamp_list_path_fields = [\"CompanyReportDateList\"]\n    timestamp_path_fields = [\"ReportDate\"]\n\n    def __init__(\n        self,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        force_update=False,\n        sleeping_time=5,\n        real_time=False,\n        fix_duplicate_way=\"add\",\n        start_timestamp=None,\n        end_timestamp=None,\n    ) -> None:\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            codes,\n            day_data,\n            real_time=real_time,\n            fix_duplicate_way=fix_duplicate_way,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n        )\n\n        try:\n            self.logger.info(f\"joinquant query count:{get_query_count()}\")\n            self.fetch_jq_timestamp = True\n        except Exception as e:\n            self.fetch_jq_timestamp = False\n            self.logger.warning(\n                f\"joinquant account not ok,the timestamp(publish date) for finance would be not correct. {e}\"\n            )\n\n    def init_timestamps(self, entity):\n        param = {\"color\": \"w\", \"fc\": get_fc(entity), \"DataType\": self.data_type}\n\n        if self.finance_report_type == \"LiRunBiaoList\" or self.finance_report_type == \"XianJinLiuLiangBiaoList\":\n            param[\"ReportType\"] = 1\n\n        timestamp_json_list = call_eastmoney_api(\n            url=self.timestamps_fetching_url, path_fields=self.timestamp_list_path_fields, param=param\n        )\n\n        if self.timestamp_path_fields:\n            timestamps = [get_from_path_fields(data, self.timestamp_path_fields) for data in timestamp_json_list]\n\n        return [to_pd_timestamp(t) for t in timestamps]\n\n    def generate_request_param(self, security_item, start, end, size, timestamps):\n        if len(timestamps) <= 10:\n            param = {\n                \"color\": \"w\",\n                \"fc\": get_fc(security_item),\n                \"corpType\": company_type_flag(security_item),\n                # 0 means get all types\n                \"reportDateType\": 0,\n                \"endDate\": \"\",\n                \"latestCount\": size,\n            }\n        else:\n            param = {\n                \"color\": \"w\",\n                \"fc\": get_fc(security_item),\n                \"corpType\": company_type_flag(security_item),\n                # 0 means get all types\n                \"reportDateType\": 0,\n                \"endDate\": to_date_time_str(timestamps[10]),\n                \"latestCount\": 10,\n            }\n\n        if self.finance_report_type == \"LiRunBiaoList\" or self.finance_report_type == \"XianJinLiuLiangBiaoList\":\n            param[\"reportType\"] = 1\n\n        return param\n\n    def generate_path_fields(self, security_item):\n        comp_type = company_type_flag(security_item)\n\n        if comp_type == \"3\":\n            return [\"{}_YinHang\".format(self.finance_report_type)]\n        elif comp_type == \"2\":\n            return [\"{}_BaoXian\".format(self.finance_report_type)]\n        elif comp_type == \"1\":\n            return [\"{}_QuanShang\".format(self.finance_report_type)]\n        elif comp_type == \"4\":\n            return [\"{}_QiYe\".format(self.finance_report_type)]\n\n    def record(self, entity, start, end, size, timestamps):\n        # different with the default timestamps handling\n        param = self.generate_request_param(entity, start, end, size, timestamps)\n        self.logger.info(\"request param:{}\".format(param))\n\n        return self.api_wrapper.request(\n            url=self.url, param=param, method=self.request_method, path_fields=self.generate_path_fields(entity)\n        )\n\n    def get_original_time_field(self):\n        return \"ReportDate\"\n\n    def fill_timestamp_with_jq(self, security_item, the_data):\n        # get report published date from jq\n        try:\n            df = get_fundamentals(\n                table=\"indicator\",\n                code=to_jq_entity_id(security_item),\n                columns=\"pubDate\",\n                date=to_jq_report_period(the_data.report_date),\n                count=None,\n                parse_dates=[\"pubDate\"],\n            )\n            if pd_is_not_null(df):\n                the_data.timestamp = to_pd_timestamp(df[\"pubDate\"][0])\n                self.logger.info(\n                    \"jq fill {} {} timestamp:{} for report_date:{}\".format(\n                        self.data_schema, security_item.id, the_data.timestamp, the_data.report_date\n                    )\n                )\n                self.session.commit()\n        except Exception as e:\n            self.logger.error(f\"Failed to fill timestamp(publish date) for finance data from joinquant {e}\")\n\n    def on_finish_entity(self, entity):\n        super().on_finish_entity(entity)\n\n        if not self.fetch_jq_timestamp:\n            return\n\n        # fill the timestamp for report published date\n        the_data_list = get_data(\n            data_schema=self.data_schema,\n            provider=self.provider,\n            entity_id=entity.id,\n            order=self.data_schema.timestamp.asc(),\n            return_type=\"domain\",\n            session=self.session,\n            filters=[\n                self.data_schema.timestamp == self.data_schema.report_date,\n                self.data_schema.timestamp >= to_pd_timestamp(\"2005-01-01\"),\n            ],\n        )\n        if the_data_list:\n            if self.data_schema == FinanceFactor:\n                for the_data in the_data_list:\n                    self.fill_timestamp_with_jq(entity, the_data)\n            else:\n                df = FinanceFactor.query_data(\n                    entity_id=entity.id,\n                    columns=[FinanceFactor.timestamp, FinanceFactor.report_date, FinanceFactor.id],\n                    filters=[\n                        FinanceFactor.timestamp != FinanceFactor.report_date,\n                        FinanceFactor.timestamp >= to_pd_timestamp(\"2005-01-01\"),\n                        FinanceFactor.report_date >= the_data_list[0].report_date,\n                        FinanceFactor.report_date <= the_data_list[-1].report_date,\n                    ],\n                )\n\n                if pd_is_not_null(df):\n                    index_df(df, index=\"report_date\", time_field=\"report_date\")\n\n                for the_data in the_data_list:\n                    if (df is not None) and (not df.empty) and the_data.report_date in df.index:\n                        the_data.timestamp = df.at[the_data.report_date, \"timestamp\"]\n                        self.logger.info(\n                            \"db fill {} {} timestamp:{} for report_date:{}\".format(\n                                self.data_schema, entity.id, the_data.timestamp, the_data.report_date\n                            )\n                        )\n                        self.session.commit()\n                    else:\n                        # self.logger.info(\n                        #     'waiting jq fill {} {} timestamp:{} for report_date:{}'.format(self.data_schema,\n                        #                                                                    security_item.id,\n                        #                                                                    the_data.timestamp,\n                        #                                                                    the_data.report_date))\n\n                        self.fill_timestamp_with_jq(entity, the_data)\n\n\n# the __all__ is generated\n__all__ = [\"to_jq_report_period\", \"BaseChinaStockFinanceRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/finance/eastmoney_balance_sheet_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.utils import to_report_period_type\nfrom zvt.domain import BalanceSheet\nfrom zvt.recorders.eastmoney.finance.base_china_stock_finance_recorder import BaseChinaStockFinanceRecorder\nfrom zvt.utils.time_utils import to_pd_timestamp\nfrom zvt.utils.utils import add_func_to_value, first_item_to_float\n\nbalance_sheet_map = {\n    # 流动资产\n    #\n    # 货币资金\n    \"cash_and_cash_equivalents\": \"Monetaryfund\",\n    # 应收票据\n    \"note_receivable\": \"Billrec\",\n    # 应收账款\n    \"accounts_receivable\": \"Accountrec\",\n    # 预付款项\n    \"advances_to_suppliers\": \"Advancepay\",\n    # 其他应收款\n    \"other_receivables\": \"Otherrec\",\n    # 存货\n    \"inventories\": \"Inventory\",\n    # 一年内到期的非流动资产\n    \"current_portion_of_non_current_assets\": \"Nonlassetoneyear\",\n    # 其他流动资产\n    \"other_current_assets\": \"Otherlasset\",\n    # 流动资产合计\n    \"total_current_assets\": \"Sumlasset\",\n    # 非流动资产\n    #\n    # 可供出售金融资产\n    \"fi_assets_saleable\": \"Saleablefasset\",\n    # 长期应收款\n    \"long_term_receivables\": \"Ltrec\",\n    # 长期股权投资\n    \"long_term_equity_investment\": \"Ltequityinv\",\n    # 投资性房地产\n    \"real_estate_investment\": \"Estateinvest\",\n    # 固定资产\n    \"fixed_assets\": \"Fixedasset\",\n    # 在建工程\n    \"construction_in_process\": \"Constructionprogress\",\n    # 无形资产\n    \"intangible_assets\": \"Intangibleasset\",\n    # 商誉\n    \"goodwill\": \"Goodwill\",\n    # 长期待摊费用\n    \"long_term_prepaid_expenses\": \"Ltdeferasset\",\n    # 递延所得税资产\n    \"deferred_tax_assets\": \"Deferincometaxasset\",\n    # 其他非流动资产\n    \"other_non_current_assets\": \"Othernonlasset\",\n    # 非流动资产合计\n    \"total_non_current_assets\": \"Sumnonlasset\",\n    # 资产总计\n    \"total_assets\": \"Sumasset\",\n    # 流动负债\n    #\n    # 短期借款\n    \"short_term_borrowing\": \"Stborrow\",\n    # 吸收存款及同业存放\n    \"accept_money_deposits\": \"Deposit\",\n    # 应付账款\n    \"accounts_payable\": \"Accountpay\",\n    # 预收款项\n    \"advances_from_customers\": \"Advancereceive\",\n    # 应付职工薪酬\n    \"employee_benefits_payable\": \"Salarypay\",\n    # 应交税费\n    \"taxes_payable\": \"Taxpay\",\n    # 应付利息\n    \"interest_payable\": \"Interestpay\",\n    # 其他应付款\n    \"other_payable\": \"Otherpay\",\n    # 一年内到期的非流动负债\n    \"current_portion_of_non_current_liabilities\": \"Nonlliaboneyear\",\n    # 其他流动负债\n    \"other_current_liabilities\": \"Otherlliab\",\n    # 流动负债合计\n    \"total_current_liabilities\": \"Sumlliab\",\n    # 非流动负债\n    #\n    # 长期借款\n    \"long_term_borrowing\": \"Ltborrow\",\n    # 长期应付款\n    \"long_term_payable\": \"Ltaccountpay\",\n    # 递延收益\n    \"deferred_revenue\": \"Deferincome\",\n    # 递延所得税负债\n    \"deferred_tax_liabilities\": \"Deferincometaxliab\",\n    # 其他非流动负债\n    \"other_non_current_liabilities\": \"Othernonlliab\",\n    # 非流动负债合计\n    \"total_non_current_liabilities\": \"Sumnonlliab\",\n    # 负债合计\n    \"total_liabilities\": \"Sumliab\",\n    # 所有者权益(或股东权益)\n    #\n    # 实收资本（或股本）\n    \"capital\": \"Sharecapital\",\n    # 资本公积\n    \"capital_reserve\": \"Capitalreserve\",\n    # 专项储备\n    \"special_reserve\": \"Specialreserve\",\n    # 盈余公积\n    \"surplus_reserve\": \"Surplusreserve\",\n    # 未分配利润\n    \"undistributed_profits\": \"Retainedearning\",\n    # 归属于母公司股东权益合计\n    \"equity\": \"Sumparentequity\",\n    # 少数股东权益\n    \"equity_as_minority_interest\": \"Minorityequity\",\n    # 股东权益合计\n    \"total_equity\": \"Sumshequity\",\n    # 负债和股东权益合计\n    \"total_liabilities_and_equity\": \"Sumliabshequity\",\n    # 银行相关\n    # 资产\n    # 现金及存放中央银行款项\n    \"fi_cash_and_deposit_in_central_bank\": \"Cashanddepositcbank\",\n    # 存放同业款项\n    \"fi_deposit_in_other_fi\": \"Depositinfi\",\n    # 贵金属\n    \"fi_expensive_metals\": \"Preciousmetal\",\n    # 拆出资金\n    \"fi_lending_to_other_fi\": \"Lendfund\",\n    # 以公允价值计量且其变动计入当期损益的金融资产\n    \"fi_financial_assets_effect_current_income\": \"Fvaluefasset\",\n    # 衍生金融资产\n    \"fi_financial_derivative_asset\": \"Derivefasset\",\n    # 买入返售金融资产\n    \"fi_buying_sell_back_fi__asset\": \"Buysellbackfasset\",\n    # 应收账款\n    #\n    # 应收利息\n    \"fi_interest_receivable\": \"Interestrec\",\n    # 发放贷款及垫款\n    \"fi_disbursing_loans_and_advances\": \"Loanadvances\",\n    # 可供出售金融资产\n    #\n    # 持有至到期投资\n    \"fi_held_to_maturity_investment\": \"Heldmaturityinv\",\n    # 应收款项类投资\n    \"fi_account_receivable_investment\": \"Investrec\",\n    # 投资性房地产\n    #\n    # 固定资产\n    #\n    # 无形资产\n    #\n    # 商誉\n    #\n    # 递延所得税资产\n    #\n    # 其他资产\n    \"fi_other_asset\": \"Otherasset\",\n    # 资产总计\n    #\n    # 负债\n    #\n    # 向中央银行借款\n    \"fi_borrowings_from_central_bank\": \"Borrowfromcbank\",\n    # 同业和其他金融机构存放款项\n    \"fi_deposit_from_other_fi\": \"Fideposit\",\n    # 拆入资金\n    \"fi_borrowings_from_fi\": \"Borrowfund\",\n    # 以公允价值计量且其变动计入当期损益的金融负债\n    \"fi_financial_liability_effect_current_income\": \"Fvaluefliab\",\n    # 衍生金融负债\n    \"fi_financial_derivative_liability\": \"Derivefliab\",\n    # 卖出回购金融资产款\n    \"fi_sell_buy_back_fi_asset\": \"Sellbuybackfasset\",\n    # 吸收存款\n    \"fi_savings_absorption\": \"Acceptdeposit\",\n    # 存款证及应付票据\n    \"fi_notes_payable\": \"Cdandbillrec\",\n    # 应付职工薪酬\n    #\n    # 应交税费\n    #\n    # 应付利息\n    #\n    # 预计负债\n    \"fi_estimated_liabilities\": \"Anticipateliab\",\n    # 应付债券\n    \"fi_bond_payable\": \"Bondpay\",\n    # 其他负债\n    \"fi_other_liability\": \"Otherliab\",\n    # 负债合计\n    #\n    # 所有者权益(或股东权益)\n    # 股本\n    \"fi_capital\": \"Shequity\",\n    # 其他权益工具\n    \"fi_other_equity_instruments\": \"Otherequity\",\n    # 其中:优先股\n    \"fi_preferred_stock\": \"Preferredstock\",\n    # 资本公积\n    #\n    # 盈余公积\n    #\n    # 一般风险准备\n    \"fi_generic_risk_reserve\": \"Generalriskprepare\",\n    # 未分配利润\n    #\n    # 归属于母公司股东权益合计\n    #\n    # 股东权益合计\n    #\n    # 负债及股东权益总计\n    # 券商相关\n    # 资产\n    #\n    # 货币资金\n    #\n    # 其中: 客户资金存款\n    \"fi_client_fund\": \"Clientfund\",\n    # 结算备付金\n    \"fi_deposit_reservation_for_balance\": \"Settlementprovision\",\n    # 其中: 客户备付金\n    \"fi_client_deposit_reservation_for_balance\": \"Clientprovision\",\n    # 融出资金\n    \"fi_margin_out_fund\": \"Marginoutfund\",\n    # 以公允价值计量且其变动计入当期损益的金融资产\n    #\n    # 衍生金融资产\n    #\n    # 买入返售金融资产\n    #\n    # 应收利息\n    #\n    # 应收款项\n    \"fi_receivables\": \"Receivables\",\n    # 存出保证金\n    \"fi_deposit_for_recognizance\": \"Gdepositpay\",\n    # 可供出售金融资产\n    #\n    # 持有至到期投资\n    #\n    # 长期股权投资\n    #\n    # 固定资产\n    #\n    # 在建工程\n    #\n    # 无形资产\n    #\n    # 商誉\n    #\n    # 递延所得税资产\n    #\n    # 其他资产\n    #\n    # 资产总计\n    #\n    # 负债\n    #\n    # 短期借款\n    #\n    # 拆入资金\n    #\n    # 以公允价值计量且其变动计入当期损益的金融负债\n    #\n    # 衍生金融负债\n    #\n    # 卖出回购金融资产款\n    #\n    # 代理买卖证券款\n    \"fi_receiving_as_agent\": \"Agenttradesecurity\",\n    # 应付账款\n    #\n    # 应付职工薪酬\n    #\n    # 应交税费\n    #\n    # 应付利息\n    #\n    # 应付短期融资款\n    \"fi_short_financing_payable\": \"Shortfinancing\",\n    # 预计负债\n    #\n    # 应付债券\n    #\n    # 递延所得税负债\n    #\n    # 其他负债\n    #\n    # 负债合计\n    #\n    # 所有者权益(或股东权益)\n    #\n    # 股本\n    #\n    # 资本公积\n    #\n    # 其他权益工具\n    #\n    # 盈余公积\n    #\n    # 一般风险准备\n    #\n    # 交易风险准备\n    \"fi_trade_risk_reserve\": \"Traderiskprepare\",\n    # 未分配利润\n    #\n    # 归属于母公司股东权益合计\n    #\n    # 少数股东权益\n    #\n    # 股东权益合计\n    #\n    # 负债和股东权益总计\n    # 保险相关\n    # 应收保费\n    \"fi_premiums_receivable\": \"Premiumrec\",\n    \"fi_reinsurance_premium_receivable\": \"Rirec\",\n    # 应收分保合同准备金\n    \"fi_reinsurance_contract_reserve\": \"Ricontactreserverec\",\n    # 保户质押贷款\n    \"fi_policy_pledge_loans\": \"Insuredpledgeloan\",\n    # 定期存款\n    \"fi_time_deposit\": \"Tdeposit\",\n    # 可供出售金融资产\n    #\n    # 持有至到期投资\n    #\n    # 应收款项类投资\n    #\n    # 应收账款\n    #\n    # 长期股权投资\n    #\n    # 存出资本保证金\n    \"fi_deposit_for_capital_recognizance\": \"Capitalgdepositpay\",\n    # 投资性房地产\n    #\n    # 固定资产\n    #\n    # 无形资产\n    #\n    # 商誉\n    #\n    # 递延所得税资产\n    #\n    # 其他资产\n    #\n    # 独立账户资产\n    \"fi_capital_in_independent_accounts\": \"Independentasset\",\n    # 资产总计\n    #\n    # 负债\n    #\n    # 短期借款\n    #\n    # 同业及其他金融机构存放款项\n    #\n    # 拆入资金\n    #\n    # 以公允价值计量且其变动计入当期损益的金融负债\n    #\n    # 衍生金融负债\n    #\n    # 卖出回购金融资产款\n    #\n    # 吸收存款\n    #\n    # 代理买卖证券款\n    #\n    # 应付账款\n    #\n    # 预收账款\n    \"fi_advance_from_customers\": \"Advancerec\",\n    # 预收保费\n    \"fi_advance_premium\": \"Premiumadvance\",\n    # 应付手续费及佣金\n    \"fi_fees_and_commissions_payable\": \"Commpay\",\n    # 应付分保账款\n    \"fi_dividend_payable_for_reinsurance\": \"Ripay\",\n    # 应付职工薪酬\n    #\n    # 应交税费\n    #\n    # 应付利息\n    #\n    # 预计负债\n    #\n    # 应付赔付款\n    \"fi_claims_payable\": \"Claimpay\",\n    # 应付保单红利\n    \"fi_policy_holder_dividend_payable\": \"Policydivipay\",\n    # 保户储金及投资款\n    \"fi_policy_holder_deposits_and_investment_funds\": \"Insureddepositinv\",\n    # 保险合同准备金\n    \"fi_contract_reserve\": \"Contactreserve\",\n    # 长期借款\n    #\n    # 应付债券\n    #\n    # 递延所得税负债\n    #\n    # 其他负债\n    #\n    # 独立账户负债\n    \"fi_independent_liability\": \"Independentliab\",\n    # 负债合计\n    #\n    # 所有者权益(或股东权益)\n    #\n    # 股本\n    #\n    # 资本公积\n    #\n    # 盈余公积\n    #\n    # 一般风险准备\n    #\n    # 未分配利润\n    #\n    # 归属于母公司股东权益总计\n    #\n    # 少数股东权益\n    #\n    # 股东权益合计\n    #\n    # 负债和股东权益总计\n}\n\nadd_func_to_value(balance_sheet_map, first_item_to_float)\nbalance_sheet_map[\"report_period\"] = (\"ReportDate\", to_report_period_type)\nbalance_sheet_map[\"report_date\"] = (\"ReportDate\", to_pd_timestamp)\n\n\nclass ChinaStockBalanceSheetRecorder(BaseChinaStockFinanceRecorder):\n    data_schema = BalanceSheet\n\n    url = \"https://emh5.eastmoney.com/api/CaiWuFenXi/GetZiChanFuZhaiBiaoList\"\n    finance_report_type = \"ZiChanFuZhaiBiaoList\"\n    data_type = 3\n\n    def get_data_map(self):\n        return balance_sheet_map\n\n\nif __name__ == \"__main__\":\n    # init_log('blance_sheet.log')\n    recorder = ChinaStockBalanceSheetRecorder(codes=[\"002572\"])\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"ChinaStockBalanceSheetRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/finance/eastmoney_cash_flow_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.utils import to_report_period_type\nfrom zvt.domain import CashFlowStatement\nfrom zvt.recorders.eastmoney.finance.base_china_stock_finance_recorder import BaseChinaStockFinanceRecorder\nfrom zvt.utils.time_utils import to_pd_timestamp\nfrom zvt.utils.utils import add_func_to_value, first_item_to_float\n\ncash_flow_map = {\n    # 经营活动产生的现金流量\n    #\n    # 销售商品、提供劳务收到的现金\n    \"cash_from_selling\": \"Salegoodsservicerec\",\n    # 收到的税费返还\n    \"tax_refund\": \"Taxreturnrec\",\n    # 收到其他与经营活动有关的现金\n    \"cash_from_other_op\": \"Otheroperaterec\",\n    # 经营活动现金流入小计\n    \"total_op_cash_inflows\": \"Sumoperateflowin\",\n    # 购买商品、接受劳务支付的现金\n    \"cash_to_goods_services\": \"Buygoodsservicepay\",\n    # 支付给职工以及为职工支付的现金\n    \"cash_to_employees\": \"Employeepay\",\n    # 支付的各项税费\n    \"taxes_and_surcharges\": \"Taxpay\",\n    # 支付其他与经营活动有关的现金\n    \"cash_to_other_related_op\": \"Otheroperatepay\",\n    # 经营活动现金流出小计\n    \"total_op_cash_outflows\": \"Sumoperateflowout\",\n    # 经营活动产生的现金流量净额\n    \"net_op_cash_flows\": \"Netoperatecashflow\",\n    # 投资活动产生的现金流量\n    # 收回投资收到的现金\n    \"cash_from_disposal_of_investments\": \"Disposalinvrec\",\n    # 取得投资收益收到的现金\n    \"cash_from_returns_on_investments\": \"Invincomerec\",\n    # 处置固定资产、无形资产和其他长期资产收回的现金净额\n    \"cash_from_disposal_fixed_intangible_assets\": \"Dispfilassetrec\",\n    # 处置子公司及其他营业单位收到的现金净额\n    \"cash_from_disposal_subsidiaries\": \"Dispsubsidiaryrec\",\n    # 收到其他与投资活动有关的现金\n    \"cash_from_other_investing\": \"Otherinvrec\",\n    # 投资活动现金流入小计\n    \"total_investing_cash_inflows\": \"Suminvflowin\",\n    # 购建固定资产、无形资产和其他长期资产支付的现金\n    \"cash_to_acquire_fixed_intangible_assets\": \"Buyfilassetpay\",\n    # 投资支付的现金\n    \"cash_to_investments\": \"Invpay\",\n    # 取得子公司及其他营业单位支付的现金净额\n    \"cash_to_acquire_subsidiaries\": \"Getsubsidiarypay\",\n    # 支付其他与投资活动有关的现金\n    \"cash_to_other_investing\": \"Otherinvpay\",\n    # 投资活动现金流出小计\n    \"total_investing_cash_outflows\": \"Suminvflowout\",\n    # 投资活动产生的现金流量净额\n    \"net_investing_cash_flows\": \"Netinvcashflow\",\n    # 筹资活动产生的现金流量\n    #\n    # 吸收投资收到的现金\n    \"cash_from_accepting_investment\": \"Acceptinvrec\",\n    # 子公司吸收少数股东投资收到的现金\n    \"cash_from_subsidiaries_accepting_minority_interest\": \"Subsidiaryaccept\",\n    # 取得借款收到的现金\n    \"cash_from_borrowings\": \"Loanrec\",\n    # 发行债券收到的现金\n    \"cash_from_issuing_bonds\": \"Issuebondrec\",\n    # 收到其他与筹资活动有关的现金\n    \"cash_from_other_financing\": \"Otherfinarec\",\n    # 筹资活动现金流入小计\n    \"total_financing_cash_inflows\": \"Sumfinaflowin\",\n    # 偿还债务支付的现金\n    \"cash_to_repay_borrowings\": \"Repaydebtpay\",\n    # 分配股利、利润或偿付利息支付的现金\n    \"cash_to_pay_interest_dividend\": \"Diviprofitorintpay\",\n    # 子公司支付给少数股东的股利、利润\n    \"cash_to_pay_subsidiaries_minority_interest\": \"Subsidiarypay\",\n    # 支付其他与筹资活动有关的现金\n    \"cash_to_other_financing\": \"Otherfinapay\",\n    # 筹资活动现金流出小计\n    \"total_financing_cash_outflows\": \"Sumfinaflowout\",\n    # 筹资活动产生的现金流量净额\n    \"net_financing_cash_flows\": \"Netfinacashflow\",\n    # 汇率变动对现金及现金等价物的影响\n    \"foreign_exchange_rate_effect\": \"Effectexchangerate\",\n    # 现金及现金等价物净增加额\n    \"net_cash_increase\": \"Nicashequi\",\n    # 加: 期初现金及现金等价物余额\n    \"cash_at_beginning\": \"Cashequibeginning\",\n    # 期末现金及现金等价物余额\n    \"cash\": \"Cashequiending\",\n    # 银行相关\n    # 客户存款和同业及其他金融机构存放款项净增加额\n    \"fi_deposit_increase\": \"Nideposit\",\n    # 向中央银行借款净增加额\n    \"fi_borrow_from_central_bank_increase\": \"Niborrowfromcbank\",\n    # 存放中央银行和同业款项及其他金融机构净减少额\n    \"fi_deposit_in_others_decrease\": \"Nddepositincbankfi\",\n    # 拆入资金及卖出回购金融资产款净增加额\n    \"fi_borrowing_and_sell_repurchase_increase\": \"Niborrowsellbuyback\",\n    # 其中:卖出回购金融资产款净增加额\n    \"fi_sell_repurchase_increase\": \"Nisellbuybackfasset\",\n    # 拆出资金及买入返售金融资产净减少额\n    \"fi_lending_and_buy_repurchase_decrease\": \"Ndlendbuysellback\",\n    # 其中:拆出资金净减少额\n    \"fi_lending_decrease\": \"Ndlendfund\",\n    # 买入返售金融资产净减少额\n    \"fi_buy_repurchase_decrease\": \"Ndbuysellbackfasset\",\n    # 收取的利息、手续费及佣金的现金\n    \"fi_cash_from_interest_commission\": \"Intandcommrec\",\n    # 客户贷款及垫款净增加额\n    \"fi_loan_advance_increase\": \"Niloanadvances\",\n    # 存放中央银行和同业及其他金融机构款项净增加额\n    \"fi_deposit_in_others_increase\": \"Nidepositincbankfi\",\n    # 拆出资金及买入返售金融资产净增加额\n    \"fi_lending_and_buy_repurchase_increase\": \"Nilendsellbuyback\",\n    # 其中:拆出资金净增加额\n    \"fi_lending_increase\": \"Nilendfund\",\n    # 拆入资金及卖出回购金融资产款净减少额\n    \"fi_borrowing_and_sell_repurchase_decrease\": \"Ndborrowsellbuyback\",\n    # 其中:拆入资金净减少额\n    \"fi_borrowing_decrease\": \"Ndborrowfund\",\n    # 卖出回购金融资产净减少额\n    \"fi_sell_repurchase_decrease\": \"Ndsellbuybackfasset\",\n    # 支付利息、手续费及佣金的现金\n    \"fi_cash_to_interest_commission\": \"Intandcommpay\",\n    # 应收账款净增加额\n    \"fi_account_receivable_increase\": \"Niaccountrec\",\n    # 偿付债券利息支付的现金\n    \"fi_cash_to_pay_interest\": \"Bondintpay\",\n    # 保险相关\n    # 收到原保险合同保费取得的现金\n    \"fi_cash_from_premium_of_original\": \"Premiumrec\",\n    # 保户储金及投资款净增加额\n    \"fi_insured_deposit_increase\": \"Niinsureddepositinv\",\n    # 银行及证券业务卖出回购资金净增加额\n    \"fi_bank_broker_sell_repurchase_increase\": \"Nisellbuyback\",\n    # 银行及证券业务买入返售资金净减少额\n    \"fi_bank_broker_buy_repurchase_decrease\": \"Ndbuysellback\",\n    # 支付原保险合同赔付等款项的现金\n    \"fi_cash_to_insurance_claim\": \"Indemnitypay\",\n    # 支付再保险业务现金净额\n    \"fi_cash_to_reinsurance\": \"Netripay\",\n    # 银行业务及证券业务拆借资金净减少额\n    \"fi_lending_decrease\": \"Ndlendfund\",\n    # 银行业务及证券业务卖出回购资金净减少额\n    \"fi_bank_broker_sell_repurchase_decrease\": \"Ndsellbuyback\",\n    # 支付保单红利的现金\n    \"fi_cash_to_dividends\": \"Divipay\",\n    # 保户质押贷款净增加额\n    \"fi_insured_pledge_loans_increase\": \"Niinsuredpledgeloan\",\n    # 收购子公司及其他营业单位支付的现金净额\n    \"fi_cash_to_acquire_subsidiaries\": \"Buysubsidiarypay\",\n    # 处置子公司及其他营业单位流出的现金净额\n    \"fi_cash_to_disposal_subsidiaries\": \"Dispsubsidiarypay\",\n    # 支付卖出回购金融资产款现金净额\n    \"fi_cash_to_sell_repurchase\": \"Netsellbuybackfassetpay\",\n    # 券商相关\n    # 拆入资金净增加额\n    \"fi_borrowing_increase\": \"Niborrowfund\",\n    # 代理买卖证券收到的现金净额\n    \"fi_cash_from_trading_agent\": \"Agenttradesecurityrec\",\n    # 回购业务资金净增加额\n    \"fi_cash_from_repurchase_increase\": \"Nibuybackfund\",\n    # 处置交易性金融资产的净减少额\n    \"fi_disposal_trade_asset_decrease\": \"Nddisptradefasset\",\n    # 回购业务资金净减少额\n    \"fi_repurchase_decrease\": \"Ndbuybackfund\",\n    # 代理买卖证券支付的现金净额（净减少额）\n    \"fi_cash_to_agent_trade\": \"Agenttradesecuritypay\",\n}\n\nadd_func_to_value(cash_flow_map, first_item_to_float)\ncash_flow_map[\"report_period\"] = (\"ReportDate\", to_report_period_type)\ncash_flow_map[\"report_date\"] = (\"ReportDate\", to_pd_timestamp)\n\n\nclass ChinaStockCashFlowRecorder(BaseChinaStockFinanceRecorder):\n    data_schema = CashFlowStatement\n\n    url = \"https://emh5.eastmoney.com/api/CaiWuFenXi/GetXianJinLiuLiangBiaoList\"\n    finance_report_type = \"XianJinLiuLiangBiaoList\"\n    data_type = 4\n\n    def get_data_map(self):\n        return cash_flow_map\n\n\nif __name__ == \"__main__\":\n    # init_log('cash_flow.log')\n    recorder = ChinaStockCashFlowRecorder(codes=[\"002572\"])\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"ChinaStockCashFlowRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/finance/eastmoney_finance_factor_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.utils import to_report_period_type\nfrom zvt.domain import FinanceFactor\nfrom zvt.recorders.eastmoney.finance.base_china_stock_finance_recorder import BaseChinaStockFinanceRecorder\nfrom zvt.utils.time_utils import to_pd_timestamp\nfrom zvt.utils.utils import add_func_to_value, to_float\n\nfinance_factor_map = {\n    # 基本每股收益(元)\n    \"basic_eps\": \"Epsjb\",\n    # 扣非每股收益(元)\n    \"deducted_eps\": \"Epskcjb\",\n    # 稀释每股收益(元)\n    \"diluted_eps\": \"Epsxs\",\n    # 每股净资产(元)\n    \"bps\": \"Bps\",\n    # 每股资本公积(元)\n    \"capital_reserve_ps\": \"Mgzbgj\",\n    # 每股未分配利润(元)\n    \"undistributed_profit_ps\": \"Mgwfplr\",\n    # 每股经营现金流(元)\n    \"op_cash_flow_ps\": \"Mgjyxjje\",\n    # 成长能力指标\n    #\n    # 营业总收入(元)\n    \"total_op_income\": \"Totalincome\",\n    # 毛利润(元)\n    \"gross_profit\": \"Grossprofit\",\n    # 归属净利润(元)\n    \"net_profit\": \"Parentnetprofit\",\n    # 扣非净利润(元)\n    \"deducted_net_profit\": \"Bucklenetprofit\",\n    # 营业总收入同比增长\n    \"op_income_growth_yoy\": \"Totalincomeyoy\",\n    # 归属净利润同比增长\n    \"net_profit_growth_yoy \": \"Parentnetprofityoy\",\n    # 扣非净利润同比增长\n    \"deducted_net_profit_growth_yoy\": \"Bucklenetprofityoy\",\n    # 营业总收入滚动环比增长\n    \"op_income_growth_qoq\": \"Totalincomerelativeratio\",\n    # 归属净利润滚动环比增长\n    \"net_profit_growth_qoq\": \"Parentnetprofitrelativeratio\",\n    # 扣非净利润滚动环比增长\n    \"deducted_net_profit_growth_qoq\": \"Bucklenetprofitrelativeratio\",\n    # 盈利能力指标\n    #\n    # 净资产收益率(加权)\n    \"roe\": \"Roejq\",\n    # 净资产收益率(扣非/加权)\n    \"deducted_roe\": \"Roekcjq\",\n    # 总资产收益率(加权)\n    \"rota\": \"Allcapitalearningsrate\",\n    # 毛利率\n    \"gross_profit_margin\": \"Grossmargin\",\n    # 净利率\n    \"net_margin\": \"Netinterest\",\n    # 收益质量指标\n    #\n    # 预收账款/营业收入\n    \"advance_receipts_per_op_income\": \"Accountsrate\",\n    # 销售净现金流/营业收入\n    \"sales_net_cash_flow_per_op_income\": \"Salesrate\",\n    # 经营净现金流/营业收入\n    \"op_net_cash_flow_per_op_income\": \"Operatingrate\",\n    # 实际税率\n    \"actual_tax_rate\": \"Taxrate\",\n    # 财务风险指标\n    #\n    # 流动比率\n    \"current_ratio\": \"Liquidityratio\",\n    # 速动比率\n    \"quick_ratio\": \"Quickratio\",\n    # 现金流量比率\n    \"cash_flow_ratio\": \"Cashflowratio\",\n    # 资产负债率\n    \"debt_asset_ratio\": \"Assetliabilityratio\",\n    # 权益乘数\n    \"em\": \"Equitymultiplier\",\n    # 产权比率\n    \"equity_ratio\": \"Equityratio\",\n    # 营运能力指标(一般企业)\n    #\n    # 总资产周转天数(天)\n    \"total_assets_turnover_days\": \"Totalassetsdays\",\n    # 存货周转天数(天)\n    \"inventory_turnover_days\": \"Inventorydays\",\n    # 应收账款周转天数(天)\n    \"receivables_turnover_days\": \"Accountsreceivabledays\",\n    # 总资产周转率(次)\n    \"total_assets_turnover\": \"Totalassetrate\",\n    # 存货周转率(次)\n    \"inventory_turnover\": \"Inventoryrate\",\n    # 应收账款周转率(次)\n    \"receivables_turnover\": \"Accountsreceiveablerate\",\n    # 专项指标(银行)\n    #\n    # 存款总额\n    \"fi_total_deposit\": \"Totaldeposit\",\n    # 贷款总额\n    \"fi_total_loan\": \"Totalloan\",\n    # 存贷款比例\n    \"fi_loan_deposit_ratio\": \"Depositloanratio\",\n    # 资本充足率\n    \"fi_capital_adequacy_ratio\": \"Capitaladequacyratio\",\n    # 核心资本充足率\n    \"fi_core_capital_adequacy_ratio\": \"Corecapitaladequacyratio\",\n    # 不良贷款率\n    \"fi_npl_ratio\": \"Nplratio\",\n    # 不良贷款拨备覆盖率\n    \"fi_npl_provision_coverage\": \"Nplprovisioncoverage\",\n    # 资本净额\n    \"fi_net_capital\": \"Netcapital_b\",\n    # 专项指标(保险)\n    #\n    # 总投资收益率\n    \"insurance_roi\": \"Tror\",\n    # 净投资收益率\n    \"insurance_net_investment_yield\": \"Nror\",\n    # 已赚保费\n    \"insurance_earned_premium\": \"Eapre\",\n    # 赔付支出\n    \"insurance_payout\": \"Comexpend\",\n    # 退保率\n    \"insurance_surrender_rate\": \"Surrate\",\n    # 偿付能力充足率\n    \"insurance_solvency_adequacy_ratio\": \"Solvenra\",\n    # 专项指标(券商)\n    #\n    # 净资本\n    \"broker_net_capital\": \"Netcapital\",\n    # 净资产\n    \"broker_net_assets\": \"Netassets\",\n    # 净资本/净资产\n    \"broker_net_capital_assets_ratio\": \"Captialrate\",\n    # 自营固定收益类证券规模/净资本\n    \"broker_self_operated_fixed_income_securities_net_capital_ratio\": \"Incomesizerate\",\n}\n\nadd_func_to_value(finance_factor_map, to_float)\nfinance_factor_map[\"report_period\"] = (\"ReportDate\", to_report_period_type)\nfinance_factor_map[\"report_date\"] = (\"ReportDate\", to_pd_timestamp)\n\n\nclass ChinaStockFinanceFactorRecorder(BaseChinaStockFinanceRecorder):\n    url = \"https://emh5.eastmoney.com/api/CaiWuFenXi/GetZhuYaoZhiBiaoList\"\n    finance_report_type = \"ZhuYaoZhiBiaoList\"\n\n    data_schema = FinanceFactor\n    data_type = 1\n\n    def get_data_map(self):\n        return finance_factor_map\n\n\nif __name__ == \"__main__\":\n    # init_log('finance_factor.log')\n    recorder = ChinaStockFinanceFactorRecorder(codes=[\"000001\"])\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"ChinaStockFinanceFactorRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/finance/eastmoney_income_statement_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.utils import to_report_period_type\nfrom zvt.domain import IncomeStatement\nfrom zvt.recorders.eastmoney.finance.base_china_stock_finance_recorder import BaseChinaStockFinanceRecorder\nfrom zvt.utils.time_utils import to_pd_timestamp\nfrom zvt.utils.utils import add_func_to_value, first_item_to_float\n\nincome_statement_map = {\n    # 营业总收入\n    #\n    # 营业收入\n    \"operating_income\": \"Operatereve\",\n    # 营业总成本\n    \"total_operating_costs\": \"Totaloperateexp\",\n    # 营业成本\n    \"operating_costs\": \"Operateexp\",\n    # 研发费用\n    \"rd_costs\": \"Rdexp\",\n    # 提取保险合同准备金净额\n    \"net_change_in_insurance_contract_reserves\": \"Netcontactreserve\",\n    # 营业税金及附加\n    \"business_taxes_and_surcharges\": \"Operatetax\",\n    # 销售费用\n    \"sales_costs\": \"Saleexp\",\n    # 管理费用\n    \"managing_costs\": \"Manageexp\",\n    # 财务费用\n    \"financing_costs\": \"Financeexp\",\n    # 资产减值损失\n    \"assets_devaluation\": \"Assetdevalueloss\",\n    # 其他经营收益\n    #\n    # 加: 投资收益\n    \"investment_income\": \"Investincome\",\n    # 其中: 对联营企业和合营企业的投资收益\n    \"investment_income_from_related_enterprise\": \"Investjointincome\",\n    # 营业利润\n    \"operating_profit\": \"Operateprofit\",\n    # 加: 营业外收入\n    \"non_operating_income\": \"Nonoperatereve\",\n    # 减: 营业外支出\n    \"non_operating_costs\": \"Nonoperateexp\",\n    # 其中: 非流动资产处置净损失\n    \"loss_on_disposal_non_current_asset\": \"Nonlassetnetloss\",\n    # 利润总额\n    \"total_profits\": \"Sumprofit\",\n    # 减: 所得税费用\n    \"tax_expense\": \"Incometax\",\n    # 净利润\n    \"net_profit\": \"Netprofit\",\n    # 其中: 归属于母公司股东的净利润\n    \"net_profit_as_parent\": \"Parentnetprofit\",\n    # 少数股东损益\n    \"net_profit_as_minority_interest\": \"Minorityincome\",\n    # 扣除非经常性损益后的净利润\n    \"deducted_net_profit\": \"Kcfjcxsyjlr\",\n    # 每股收益\n    # 基本每股收益\n    \"eps\": \"Basiceps\",\n    # 稀释每股收益\n    \"diluted_eps\": \"Dilutedeps\",\n    # 其他综合收益\n    \"other_comprehensive_income\": \"Othercincome\",\n    # 归属于母公司股东的其他综合收益\n    \"other_comprehensive_income_as_parent\": \"Parentothercincome\",\n    # 归属于少数股东的其他综合收益\n    \"other_comprehensive_income_as_minority_interest\": \"Minorityothercincome\",\n    # 综合收益总额\n    \"total_comprehensive_income\": \"Sumcincome\",\n    # 归属于母公司所有者的综合收益总额\n    \"total_comprehensive_income_as_parent\": \"Parentcincome\",\n    # 归属于少数股东的综合收益总额\n    \"total_comprehensive_income_as_minority_interest\": \"Minoritycincome\",\n    # 银行相关\n    # 利息净收入\n    \"fi_net_interest_income\": \"Intnreve\",\n    # 其中:利息收入\n    \"fi_interest_income\": \"Intreve\",\n    # 利息支出\n    \"fi_interest_expenses\": \"Intexp\",\n    # 手续费及佣金净收入\n    \"fi_net_incomes_from_fees_and_commissions\": \"Commnreve\",\n    # 其中:手续费及佣金收入\n    \"fi_incomes_from_fees_and_commissions\": \"Commreve\",\n    # 手续费及佣金支出\n    \"fi_expenses_for_fees_and_commissions\": \"Commexp\",\n    # 公允价值变动收益\n    \"fi_income_from_fair_value_change\": \"Fvalueincome\",\n    # 汇兑收益\n    \"fi_income_from_exchange\": \"Exchangeincome\",\n    # 其他业务收入\n    \"fi_other_income\": \"Otherreve\",\n    # 业务及管理费\n    \"fi_operate_and_manage_expenses\": \"Operatemanageexp\",\n    # 保险相关\n    # 已赚保费\n    \"fi_net_income_from_premium\": \"Premiumearned\",\n    # 其中:保险业务收入\n    \"fi_income_from_premium\": \"Insurreve\",\n    # 分保费收入\n    \"fi_income_from_reinsurance_premium\": \"Rireve\",\n    # 减:分出保费\n    \"fi_reinsurance_premium\": \"Ripremium\",\n    # 提取未到期责任准备金\n    \"fi_undue_duty_reserve\": \"Unduereserve\",\n    # 银行业务利息净收入\n    \"fi_net_income_from_bank_interest\": \"Bankintnreve\",\n    # 其中:银行业务利息收入\n    \"fi_income_from_bank_interest\": \"Bankintreve\",\n    # 银行业务利息支出\n    \"fi_expenses_for_bank_interest\": \"Bankintexp\",\n    # 非保险业务手续费及佣金净收入\n    \"fi_net_incomes_from_fees_and_commissions_of_non_insurance\": \"Ninsurcommnreve\",\n    # 非保险业务手续费及佣金收入\n    \"fi_incomes_from_fees_and_commissions_of_non_insurance\": \"Ninsurcommreve\",\n    # 非保险业务手续费及佣金支出\n    \"fi_expenses_for_fees_and_commissions_of_non_insurance\": \"Ninsurcommexp\",\n    # 退保金\n    \"fi_insurance_surrender_costs\": \"Surrenderpremium\",\n    # 赔付支出\n    \"fi_insurance_claims_expenses\": \"Indemnityexp\",\n    # 减:摊回赔付支出\n    \"fi_amortized_insurance_claims_expenses\": \"Amortiseindemnityexp\",\n    # 提取保险责任准备金\n    \"fi_insurance_duty_reserve\": \"Dutyreserve\",\n    # 减:摊回保险责任准备金\n    \"fi_amortized_insurance_duty_reserve\": \"Amortisedutyreserve\",\n    # 保单红利支出\n    \"fi_dividend_expenses_to_insured\": \"Policydiviexp\",\n    # 分保费用\n    \"fi_reinsurance_expenses\": \"Riexp\",\n    # 减:摊回分保费用\n    \"fi_amortized_reinsurance_expenses\": \"Amortiseriexp\",\n    # 其他业务成本\n    \"fi_other_op_expenses\": \"Otherexp\",\n    # 券商相关\n    # 手续费及佣金净收入\n    #\n    # 其中:代理买卖证券业务净收入\n    \"fi_net_incomes_from_trading_agent\": \"Agenttradesecurity\",\n    # 证券承销业务净收入\n    \"fi_net_incomes_from_underwriting\": \"Securityuw\",\n    # 受托客户资产管理业务净收入\n    \"fi_net_incomes_from_customer_asset_management\": \"Clientassetmanage\",\n    # 手续费及佣金净收入其他项目\n    \"fi_fees_from_other\": \"Commnreveother\",\n    # 公允价值变动收益\n    #\n    # 其中:可供出售金融资产公允价值变动损益\n    \"fi_income_from_fair_value_change_of_fi_salable\": \"Fvalueosalable\",\n}\n\nadd_func_to_value(income_statement_map, first_item_to_float)\nincome_statement_map[\"report_period\"] = (\"ReportDate\", to_report_period_type)\nincome_statement_map[\"report_date\"] = (\"ReportDate\", to_pd_timestamp)\n\n\nclass ChinaStockIncomeStatementRecorder(BaseChinaStockFinanceRecorder):\n    data_schema = IncomeStatement\n\n    url = \"https://emh5.eastmoney.com/api/CaiWuFenXi/GetLiRunBiaoList\"\n    finance_report_type = \"LiRunBiaoList\"\n\n    data_type = 2\n\n    def get_data_map(self):\n        return income_statement_map\n\n\nif __name__ == \"__main__\":\n    # init_log('income_statement.log')\n    recorder = ChinaStockIncomeStatementRecorder(codes=[\"002572\"])\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"ChinaStockIncomeStatementRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/holder/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule eastmoney_top_ten_tradable_holder_recorder\nfrom .eastmoney_top_ten_tradable_holder_recorder import *\nfrom .eastmoney_top_ten_tradable_holder_recorder import __all__ as _eastmoney_top_ten_tradable_holder_recorder_all\n\n__all__ += _eastmoney_top_ten_tradable_holder_recorder_all\n\n# import all from submodule eastmoney_stock_actor_recorder\nfrom .eastmoney_stock_actor_recorder import *\nfrom .eastmoney_stock_actor_recorder import __all__ as _eastmoney_stock_actor_recorder_all\n\n__all__ += _eastmoney_stock_actor_recorder_all\n\n# import all from submodule eastmoney_top_ten_holder_recorder\nfrom .eastmoney_top_ten_holder_recorder import *\nfrom .eastmoney_top_ten_holder_recorder import __all__ as _eastmoney_top_ten_holder_recorder_all\n\n__all__ += _eastmoney_top_ten_holder_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/holder/eastmoney_stock_actor_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport requests\n\nfrom zvt.api.utils import get_recent_report_date\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain.actor.actor_meta import ActorMeta\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\nclass EastmoneyActorRecorder(Recorder):\n    name = \"eastmoney_actor_recorder\"\n    provider = \"eastmoney\"\n    data_schema = ActorMeta\n\n    url = \"https://datacenter.eastmoney.com/securities/api/data/v1/get?reportName=RPT_FREEHOLDERS_BASIC_INFO&columns=HOLDER_NAME,END_DATE,HOLDER_NEW,HOLDER_NUM,HOLDER_CODE&quoteColumns=&filter=(END_DATE='{}')&pageNumber={}&pageSize={}&sortTypes=-1,-1&sortColumns=HOLDER_NUM,HOLDER_NAME&source=SECURITIES&client=SW\"\n\n    start = \"2016-03-31\"\n\n    def get_data(self, end_date, pn, ps):\n        resp = requests.get(url=self.url.format(end_date, pn, ps))\n        return resp.json()\n\n    def run(self):\n        current_date = get_recent_report_date()\n        pn = 1\n        ps = 2000\n\n        while to_pd_timestamp(current_date) >= to_pd_timestamp(self.start):\n            if not self.state:\n                current_date = get_recent_report_date()\n                result = self.get_data(end_date=current_date, pn=pn, ps=ps)\n                print(result)\n                self.state = {\"end_date\": current_date, \"pages\": result[\"result\"][\"pages\"], \"pn\": pn, \"ps\": ps}\n                self.persist_state(\"stock_sz_000001\", self.state)\n            else:\n                if self.state[\"pn\"] >= self.state[\"pages\"]:\n                    current_date = get_recent_report_date(the_date=self.state[\"end_date\"], step=1)\n                    pn = pn\n                    ps = ps\n                else:\n                    pn = self.state[\"pn\"] + 1\n                    ps = self.state[\"ps\"]\n                    current_date = self.state[\"end_date\"]\n\n                result = self.get_data(end_date=current_date, pn=pn, ps=ps)\n                print(result)\n                self.state = {\"end_date\": current_date, \"pages\": result[\"result\"][\"pages\"], \"pn\": pn, \"ps\": ps}\n                self.persist_state(\"stock_sz_000001\", self.state)\n\n\nif __name__ == \"__main__\":\n    EastmoneyActorRecorder().run()\n\n\n# the __all__ is generated\n__all__ = [\"EastmoneyActorRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/holder/eastmoney_top_ten_holder_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.utils import to_report_period_type\nfrom zvt.domain.misc.holder import TopTenHolder\nfrom zvt.recorders.eastmoney.common import EastmoneyTimestampsDataRecorder, get_fc\nfrom zvt.utils.time_utils import to_date_time_str, to_pd_timestamp\nfrom zvt.utils.utils import to_float\n\n\nclass TopTenHolderRecorder(EastmoneyTimestampsDataRecorder):\n    provider = \"eastmoney\"\n    data_schema = TopTenHolder\n\n    url = \"https://emh5.eastmoney.com/api/GuBenGuDong/GetShiDaGuDong\"\n    path_fields = [\"ShiDaGuDongList\"]\n\n    timestamps_fetching_url = \"https://emh5.eastmoney.com/api/GuBenGuDong/GetFirstRequest2Data\"\n    timestamp_list_path_fields = [\"SDGDBGQ\", \"ShiDaGuDongBaoGaoQiList\"]\n    timestamp_path_fields = [\"BaoGaoQi\"]\n\n    def get_data_map(self):\n        return {\n            \"report_period\": (\"timestamp\", to_report_period_type),\n            \"report_date\": (\"timestamp\", to_pd_timestamp),\n            # 股东代码\n            \"holder_code\": (\"GuDongDaiMa\", str),\n            # 股东名称\n            \"holder_name\": (\"GuDongMingCheng\", str),\n            # 持股数\n            \"shareholding_numbers\": (\"ChiGuShu\", to_float),\n            # 持股比例\n            \"shareholding_ratio\": (\"ChiGuBiLi\", to_float),\n            # 变动\n            \"change\": (\"ZengJian\", to_float),\n            # 变动比例\n            \"change_ratio\": (\"BianDongBiLi\", to_float),\n        }\n\n    def generate_request_param(self, security_item, start, end, size, timestamp):\n        return {\"color\": \"w\", \"fc\": get_fc(security_item), \"BaoGaoQi\": to_date_time_str(timestamp)}\n\n    def generate_domain_id(self, entity, original_data):\n        the_name = original_data.get(\"GuDongMingCheng\")\n        timestamp = original_data[self.get_original_time_field()]\n        the_id = \"{}_{}_{}\".format(entity.id, timestamp, the_name)\n        return the_id\n\n\nif __name__ == \"__main__\":\n    # init_log('top_ten_holder.log')\n\n    TopTenHolderRecorder(codes=[\"002572\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"TopTenHolderRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/holder/eastmoney_top_ten_tradable_holder_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain import TopTenTradableHolder\nfrom zvt.recorders.eastmoney.holder.eastmoney_top_ten_holder_recorder import TopTenHolderRecorder\n\n\nclass TopTenTradableHolderRecorder(TopTenHolderRecorder):\n    provider = \"eastmoney\"\n    data_schema = TopTenTradableHolder\n\n    url = \"https://emh5.eastmoney.com/api/GuBenGuDong/GetShiDaLiuTongGuDong\"\n    path_fields = [\"ShiDaLiuTongGuDongList\"]\n    timestamps_fetching_url = \"https://emh5.eastmoney.com/api/GuBenGuDong/GetFirstRequest2Data\"\n    timestamp_list_path_fields = [\"SDLTGDBGQ\", \"ShiDaLiuTongGuDongBaoGaoQiList\"]\n    timestamp_path_fields = [\"BaoGaoQi\"]\n\n\nif __name__ == \"__main__\":\n    # init_log('top_ten_tradable_holder.log')\n\n    TopTenTradableHolderRecorder(codes=[\"002572\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"TopTenTradableHolderRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/meta/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule eastmoney_block_meta_recorder\nfrom .eastmoney_block_meta_recorder import *\nfrom .eastmoney_block_meta_recorder import __all__ as _eastmoney_block_meta_recorder_all\n\n__all__ += _eastmoney_block_meta_recorder_all\n\n# import all from submodule eastmoney_stock_meta_recorder\nfrom .eastmoney_stock_meta_recorder import *\nfrom .eastmoney_stock_meta_recorder import __all__ as _eastmoney_stock_meta_recorder_all\n\n__all__ += _eastmoney_stock_meta_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/meta/eastmoney_block_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\nimport requests\n\nfrom zvt.api.utils import china_stock_code_to_id\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder, TimeSeriesDataRecorder\nfrom zvt.domain import BlockStock, BlockCategory, Block\nfrom zvt.recorders.consts import DEFAULT_HEADER\nfrom zvt.utils.time_utils import now_pd_timestamp\nfrom zvt.utils.utils import json_callback_param\n\n\nclass EastmoneyBlockRecorder(Recorder):\n    provider = \"eastmoney\"\n    data_schema = Block\n\n    # 用于抓取行业/概念/地域列表\n    category_map_url = {\n        BlockCategory.industry: \"https://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx?type=CT&cmd=C._BKHY&sty=DCRRBKCPAL&st=(ChangePercent)&sr=-1&p=1&ps=200&lvl=&cb=jsonp_F1A61014DE5E45B7A50068EA290BC918&token=4f1862fc3b5e77c150a2b985b12db0fd&_=08766\",\n        BlockCategory.concept: \"https://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx?type=CT&cmd=C._BKGN&sty=DCRRBKCPAL&st=(ChangePercent)&sr=-1&p=1&ps=300&lvl=&cb=jsonp_3071689CC1E6486A80027D69E8B33F26&token=4f1862fc3b5e77c150a2b985b12db0fd&_=08251\",\n        # BlockCategory.area: 'https://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx?type=CT&cmd=C._BKDY&sty=DCRRBKCPAL&st=(ChangePercent)&sr=-1&p=1&ps=200&lvl=&cb=jsonp_A597D4867B3D4659A203AADE5B3B3AD5&token=4f1862fc3b5e77c150a2b985b12db0fd&_=02443'\n    }\n\n    def run(self):\n        for category, url in self.category_map_url.items():\n            resp = requests.get(url, headers=DEFAULT_HEADER)\n            results = json_callback_param(resp.text)\n            the_list = []\n            for result in results:\n                items = result.split(\",\")\n                code = items[1]\n                name = items[2]\n                entity_id = f\"block_cn_{code}\"\n                the_list.append(\n                    {\n                        \"id\": entity_id,\n                        \"entity_id\": entity_id,\n                        \"entity_type\": \"block\",\n                        \"exchange\": \"cn\",\n                        \"code\": code,\n                        \"name\": name,\n                        \"category\": category.value,\n                    }\n                )\n            if the_list:\n                df = pd.DataFrame.from_records(the_list)\n                df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=self.force_update)\n            self.logger.info(f\"finish record eastmoney blocks:{category.value}\")\n\n\nclass EastmoneyBlockStockRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"eastmoney\"\n    entity_schema = Block\n\n    provider = \"eastmoney\"\n    data_schema = BlockStock\n\n    # 用于抓取行业包含的股票\n    category_stocks_url = \"https://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx?type=CT&cmd=C.{}{}&sty=SFCOO&st=(Close)&sr=-1&p=1&ps=300&cb=jsonp_B66B5BAA1C1B47B5BB9778045845B947&token=7bc05d0d4c3c22ef9fca8c2a912d779c\"\n\n    def record(self, entity, start, end, size, timestamps):\n        resp = requests.get(self.category_stocks_url.format(entity.code, \"1\"), headers=DEFAULT_HEADER)\n        try:\n            results = json_callback_param(resp.text)\n            the_list = []\n            for result in results:\n                items = result.split(\",\")\n                stock_code = items[1]\n                stock_id = china_stock_code_to_id(stock_code)\n                block_id = entity.id\n\n                the_list.append(\n                    {\n                        \"id\": \"{}_{}\".format(block_id, stock_id),\n                        \"entity_id\": block_id,\n                        \"entity_type\": \"block\",\n                        \"exchange\": entity.exchange,\n                        \"code\": entity.code,\n                        \"name\": entity.name,\n                        \"timestamp\": now_pd_timestamp(),\n                        \"stock_id\": stock_id,\n                        \"stock_code\": stock_code,\n                        \"stock_name\": items[2],\n                    }\n                )\n            if the_list:\n                df = pd.DataFrame.from_records(the_list)\n                df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n\n            self.logger.info(\"finish recording block:{},{}\".format(entity.category, entity.name))\n\n        except Exception as e:\n            self.logger.error(\"error:,resp.text:\", e, resp.text)\n        self.sleep()\n\n\nif __name__ == \"__main__\":\n    # init_log('china_stock_category.log')\n    EastmoneyBlockRecorder().run()\n\n    recorder = EastmoneyBlockStockRecorder(code=\"BK1144\")\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EastmoneyBlockRecorder\", \"EastmoneyBlockStockRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/meta/eastmoney_stock_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport requests\n\nfrom zvt.contract.api import get_entities\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain.meta.stock_meta import StockDetail, Stock\nfrom zvt.recorders.exchange.exchange_stock_meta_recorder import ExchangeStockMetaRecorder\nfrom zvt.utils.time_utils import to_pd_timestamp\nfrom zvt.utils.utils import to_float, pct_to_float\n\n\nclass EastmoneyStockRecorder(ExchangeStockMetaRecorder):\n    data_schema = Stock\n    provider = \"eastmoney\"\n\n\nclass EastmoneyStockDetailRecorder(Recorder):\n    provider = \"eastmoney\"\n    data_schema = StockDetail\n\n    def __init__(self, force_update=False, sleeping_time=5, code=None, codes=None) -> None:\n        super().__init__(force_update, sleeping_time)\n\n        # get list at first\n        EastmoneyStockRecorder().run()\n\n        if codes is None and code is not None:\n            self.codes = [code]\n        else:\n            self.codes = codes\n        filters = None\n        if not self.force_update:\n            filters = [StockDetail.profile.is_(None)]\n        self.entities = get_entities(\n            session=self.session,\n            entity_schema=StockDetail,\n            exchanges=None,\n            codes=self.codes,\n            filters=filters,\n            return_type=\"domain\",\n            provider=self.provider,\n        )\n\n    def run(self):\n        for security_item in self.entities:\n            assert isinstance(security_item, StockDetail)\n\n            if security_item.exchange == \"sh\":\n                fc = \"{}01\".format(security_item.code)\n            if security_item.exchange == \"sz\":\n                fc = \"{}02\".format(security_item.code)\n\n            # 基本资料\n            # param = {\"color\": \"w\", \"fc\": fc, \"SecurityCode\": \"SZ300059\"}\n\n            securities_code = f\"{security_item.code}.{security_item.exchange.upper()}\"\n            param = {\n                \"type\": \"RPT_F10_ORG_BASICINFO\",\n                \"sty\": \"ORG_PROFIE,MAIN_BUSINESS,FOUND_DATE,EM2016,BLGAINIAN,REGIONBK\",\n                \"filter\": f\"(SECUCODE=\\\"{securities_code}\\\")\",\n                \"client\": \"app\",\n                \"source\": \"SECURITIES\",\n                \"pageNumber\": 1,\n                \"pageSize\": 1\n            }\n            resp = requests.get(\"https://datacenter.eastmoney.com/securities/api/data/get\", params=param)\n            resp.encoding = \"utf8\"\n\n            resp_json = resp.json()[\"result\"][\"data\"][0]\n\n            security_item.profile = resp_json[\"ORG_PROFIE\"]\n            security_item.main_business = resp_json[\"MAIN_BUSINESS\"]\n            security_item.date_of_establishment = to_pd_timestamp(resp_json[\"FOUND_DATE\"])\n\n            # 关联行业\n            industries = \",\".join(resp_json[\"EM2016\"].split(\"-\"))\n            security_item.industries = industries\n\n            # 关联概念\n            security_item.concept_indices = resp_json[\"BLGAINIAN\"]\n\n            # 关联地区\n            security_item.area_indices = resp_json[\"REGIONBK\"]\n\n            self.sleep()\n\n            # 发行相关\n            param = {\n                \"reportName\": \"RPT_F10_ORG_ISSUEINFO\",\n                \"columns\": \"AFTER_ISSUE_PE,ISSUE_PRICE,TOTAL_ISSUE_NUM,NET_RAISE_FUNDS,ONLINE_ISSUE_LWR\",\n                \"filter\": f\"(SECUCODE=\\\"{securities_code}\\\")(TYPENEW=\\\"4\\\")\",\n                \"client\": \"app\",\n                \"source\": \"SECURITIES\",\n                \"pageNumber\": 1,\n                \"pageSize\": 1\n            }\n            resp = requests.get(\"https://datacenter.eastmoney.com/securities/api/data/v1/get\", params=param)\n            resp.encoding = \"utf8\"\n\n            resp_json = resp.json()[\"result\"][\"data\"][0]\n\n            security_item.issue_pe = resp_json[\"AFTER_ISSUE_PE\"]\n            security_item.price = resp_json[\"ISSUE_PRICE\"]\n            security_item.issues = resp_json[\"TOTAL_ISSUE_NUM\"]\n            security_item.raising_fund = resp_json.get(\"NET_RAISE_FUNDS\")\n            security_item.net_winning_rate = resp_json[\"ONLINE_ISSUE_LWR\"]\n\n            self.session.commit()\n\n            self.logger.info(\"finish recording stock meta for:{}\".format(security_item.code))\n\n            self.sleep()\n\n\nif __name__ == \"__main__\":\n    # init_log('china_stock_meta.log')\n\n    recorder = EastmoneyStockRecorder()\n    recorder.run()\n    StockDetail.record_data(codes=[\"000338\", \"000777\"], provider=\"eastmoney\")\n\n\n# the __all__ is generated\n__all__ = [\"EastmoneyStockRecorder\", \"EastmoneyStockDetailRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/trading/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule eastmoney_manager_trading_recorder\nfrom .eastmoney_manager_trading_recorder import *\nfrom .eastmoney_manager_trading_recorder import __all__ as _eastmoney_manager_trading_recorder_all\n\n__all__ += _eastmoney_manager_trading_recorder_all\n\n# import all from submodule eastmoney_holder_trading_recorder\nfrom .eastmoney_holder_trading_recorder import *\nfrom .eastmoney_holder_trading_recorder import __all__ as _eastmoney_holder_trading_recorder_all\n\n__all__ += _eastmoney_holder_trading_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/trading/eastmoney_holder_trading_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain import HolderTrading\nfrom zvt.recorders.eastmoney.common import EastmoneyMoreDataRecorder\nfrom zvt.utils.utils import to_float\n\n\nclass HolderTradingRecorder(EastmoneyMoreDataRecorder):\n    data_schema = HolderTrading\n\n    url = \"https://emh5.eastmoney.com/api/JiaoYiShuJu/GetGuDongZengJian\"\n    path_fields = [\"GuDongZengJianList\"]\n\n    def get_original_time_field(self):\n        return \"RiQi\"\n\n    def get_data_map(self):\n        return {\n            \"holder_name\": (\"GuDongMingCheng\", str),\n            \"volume\": (\"BianDongShuLiang\", to_float),\n            \"change_pct\": (\"BianDongBiLi\", to_float),\n            \"holding_pct\": (\"BianDongHouChiGuBiLi\", to_float),\n        }\n\n    def generate_domain_id(self, entity, original_data):\n        the_name = original_data.get(\"GuDongMingCheng\")\n        timestamp = original_data[self.get_original_time_field()]\n        the_id = \"{}_{}_{}\".format(entity.id, timestamp, the_name)\n        return the_id\n\n\nif __name__ == \"__main__\":\n    # init_log('holder_trading.log')\n\n    recorder = HolderTradingRecorder(codes=[\"002572\"])\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"HolderTradingRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/eastmoney/trading/eastmoney_manager_trading_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain import ManagerTrading\nfrom zvt.recorders.eastmoney.common import EastmoneyMoreDataRecorder\nfrom zvt.utils.utils import to_float\n\n\nclass ManagerTradingRecorder(EastmoneyMoreDataRecorder):\n    data_schema = ManagerTrading\n\n    url = \"https://emh5.eastmoney.com/api/JiaoYiShuJu/GetGaoGuanZengJian\"\n    path_fields = [\"GaoGuanZengJianList\"]\n\n    def get_original_time_field(self):\n        return \"RiQi\"\n\n    def get_data_map(self):\n        return {\n            \"trading_person\": (\"BianDongRen\", str),\n            \"volume\": (\"BianDongShuLiang\", to_float),\n            \"price\": (\"JiaoYiJunJia\", to_float),\n            \"holding\": (\"BianDongHouShuLiang\", to_float),\n            \"trading_way\": (\"JiaoYiTuJing\", str),\n            \"manager\": (\"GaoGuanMingCheng\", str),\n            \"manager_position\": (\"GaoGuanZhiWei\", str),\n            \"relationship_with_manager\": (\"GaoGuanGuanXi\", str),\n        }\n\n    def generate_domain_id(self, entity, original_data):\n        the_name = original_data.get(\"BianDongRen\")\n        timestamp = original_data[self.get_original_time_field()]\n        the_id = \"{}_{}_{}\".format(entity.id, timestamp, the_name)\n        return the_id\n\n\nif __name__ == \"__main__\":\n    # init_log('manager_trading.log')\n\n    recorder = ManagerTradingRecorder(codes=[\"002572\"])\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"ManagerTradingRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule quotes\nfrom .quotes import *\nfrom .quotes import __all__ as _quotes_all\n\n__all__ += _quotes_all\n\n# import all from submodule trading\nfrom .trading import *\nfrom .trading import __all__ as _trading_all\n\n__all__ += _trading_all\n\n# import all from submodule actor\nfrom .actor import *\nfrom .actor import __all__ as _actor_all\n\n__all__ += _actor_all\n\n# import all from submodule em_api\nfrom .em_api import *\nfrom .em_api import __all__ as _em_api_all\n\n__all__ += _em_api_all\n\n# import all from submodule macro\nfrom .macro import *\nfrom .macro import __all__ as _macro_all\n\n__all__ += _macro_all\n\n# import all from submodule meta\nfrom .meta import *\nfrom .meta import __all__ as _meta_all\n\n__all__ += _meta_all\n\n# import all from submodule misc\nfrom .misc import *\nfrom .misc import __all__ as _misc_all\n\n__all__ += _misc_all\n"
  },
  {
    "path": "src/zvt/recorders/em/actor/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule em_stock_top_ten_recorder\nfrom .em_stock_top_ten_recorder import *\nfrom .em_stock_top_ten_recorder import __all__ as _em_stock_top_ten_recorder_all\n\n__all__ += _em_stock_top_ten_recorder_all\n\n# import all from submodule em_stock_top_ten_free_recorder\nfrom .em_stock_top_ten_free_recorder import *\nfrom .em_stock_top_ten_free_recorder import __all__ as _em_stock_top_ten_free_recorder_all\n\n__all__ += _em_stock_top_ten_free_recorder_all\n\n# import all from submodule em_stock_actor_summary_recorder\nfrom .em_stock_actor_summary_recorder import *\nfrom .em_stock_actor_summary_recorder import __all__ as _em_stock_actor_summary_recorder_all\n\n__all__ += _em_stock_actor_summary_recorder_all\n\n# import all from submodule em_stock_ii_recorder\nfrom .em_stock_ii_recorder import *\nfrom .em_stock_ii_recorder import __all__ as _em_stock_ii_recorder_all\n\n__all__ += _em_stock_ii_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/em/actor/em_stock_actor_summary_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nimport pandas as pd\n\nfrom zvt.api.utils import to_report_period_type, value_to_pct\nfrom zvt.contract import ActorType\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import TimestampsDataRecorder\nfrom zvt.domain import Stock\nfrom zvt.domain.actor.stock_actor import StockActorSummary\nfrom zvt.recorders.em.em_api import get_ii_holder_report_dates, actor_type_to_org_type, get_ii_summary\nfrom zvt.utils.time_utils import to_pd_timestamp, to_date_time_str\n\n\n# [{'CHANGE_RATIO': -1.045966694333,\n#   'IS_COMPLETE': '1',\n#   'ORG_TYPE': '07',\n#   'REPORT_DATE': '2021-03-31 00:00:00',\n#   'SECUCODE': '000338.SZ',\n#   'SECURITY_CODE': '000338',\n#   'TOTAL_FREE_SHARES': 2598718411,\n#   'TOTAL_MARKET_CAP': 49999342227.64,\n#   'TOTAL_ORG_NUM': 5,\n#   'TOTAL_SHARES_RATIO': 29.51742666}]\n\n\nclass EMStockActorSummaryRecorder(TimestampsDataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Stock\n\n    provider = \"em\"\n    data_schema = StockActorSummary\n\n    def init_timestamps(self, entity_item) -> List[pd.Timestamp]:\n        result = get_ii_holder_report_dates(code=entity_item.code)\n        if result:\n            return [to_pd_timestamp(item[\"REPORT_DATE\"]) for item in result]\n\n    def record(self, entity, start, end, size, timestamps):\n        for timestamp in timestamps:\n            the_date = to_date_time_str(timestamp)\n            self.logger.info(f\"to {entity.code} {the_date}\")\n            for actor_type in ActorType:\n                if actor_type == ActorType.private_equity or actor_type == ActorType.individual:\n                    continue\n                result = get_ii_summary(\n                    code=entity.code, report_date=the_date, org_type=actor_type_to_org_type(actor_type)\n                )\n                if result:\n                    summary_list = [\n                        {\n                            \"id\": f\"{entity.entity_id}_{the_date}_{actor_type.value}\",\n                            \"entity_id\": entity.entity_id,\n                            \"timestamp\": timestamp,\n                            \"code\": entity.code,\n                            \"name\": entity.name,\n                            \"actor_type\": actor_type.value,\n                            \"actor_count\": item[\"TOTAL_ORG_NUM\"],\n                            \"report_date\": timestamp,\n                            \"report_period\": to_report_period_type(timestamp),\n                            \"change_ratio\": value_to_pct(item[\"CHANGE_RATIO\"], default=1),\n                            \"is_complete\": item[\"IS_COMPLETE\"],\n                            \"holding_numbers\": item[\"TOTAL_FREE_SHARES\"],\n                            \"holding_ratio\": value_to_pct(item[\"TOTAL_SHARES_RATIO\"], default=0),\n                            \"holding_values\": item[\"TOTAL_MARKET_CAP\"],\n                        }\n                        for item in result\n                    ]\n                    df = pd.DataFrame.from_records(summary_list)\n                    df_to_db(\n                        data_schema=self.data_schema,\n                        df=df,\n                        provider=self.provider,\n                        force_update=True,\n                        drop_duplicates=True,\n                    )\n\n\nif __name__ == \"__main__\":\n    EMStockActorSummaryRecorder(codes=[\"000338\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"EMStockActorSummaryRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/actor/em_stock_ii_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nimport pandas as pd\n\nfrom zvt.api.utils import to_report_period_type, value_to_pct\nfrom zvt.contract import ActorType\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import TimestampsDataRecorder\nfrom zvt.domain import Stock, ActorMeta\nfrom zvt.domain.actor.stock_actor import StockInstitutionalInvestorHolder\nfrom zvt.recorders.em.em_api import get_ii_holder_report_dates, get_ii_holder, actor_type_to_org_type\nfrom zvt.utils.time_utils import to_pd_timestamp, to_date_time_str\n\n\n# {'END_DATE': '2021-03-31 00:00:00',\n#   'HOLDER_CODE': '10015776',\n#   'HOLDER_CODE_OLD': '80010104',\n#   'HOLDER_NAME': '香港中央结算代理人有限公司',\n#   'HOLDER_RANK': 1,\n#   'HOLD_NUM': 1938664086,\n#   'HOLD_NUM_RATIO': 24.44,\n#   'HOLD_RATIO_QOQ': '0.04093328',\n#   'IS_HOLDORG': '1',\n#   'SECUCODE': '000338.SZ'}\n\n#  {'END_DATE': '2021-03-31 00:00:00',\n#   'FREE_HOLDNUM_RATIO': 0.631949916991,\n#   'FREE_RATIO_QOQ': '-5.33046217',\n#   'HOLDER_CODE': '161606',\n#   'HOLDER_CODE_OLD': '161606',\n#   'HOLDER_NAME': '交通银行-融通行业景气证券投资基金',\n#   'HOLDER_RANK': 10,\n#   'HOLD_NUM': 39100990,\n#   'IS_HOLDORG': '1',\n#   'SECUCODE': '000338.SZ'}\n\n\nclass EMStockIIRecorder(TimestampsDataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Stock\n\n    provider = \"em\"\n    data_schema = StockInstitutionalInvestorHolder\n\n    def init_timestamps(self, entity_item) -> List[pd.Timestamp]:\n        result = get_ii_holder_report_dates(code=entity_item.code)\n        if result:\n            return [to_pd_timestamp(item[\"REPORT_DATE\"]) for item in result]\n\n    def record(self, entity, start, end, size, timestamps):\n        for timestamp in timestamps:\n            the_date = to_date_time_str(timestamp)\n            self.logger.info(f\"to {entity.code} {the_date}\")\n            for actor_type in ActorType:\n                if actor_type == ActorType.private_equity or actor_type == ActorType.individual:\n                    continue\n                result = get_ii_holder(\n                    code=entity.code, report_date=the_date, org_type=actor_type_to_org_type(actor_type)\n                )\n                if result:\n                    holders = [\n                        {\n                            \"id\": f'{entity.entity_id}_{the_date}_{actor_type.value}_cn_{item[\"HOLDER_CODE\"]}',\n                            \"entity_id\": entity.entity_id,\n                            \"timestamp\": timestamp,\n                            \"code\": entity.code,\n                            \"name\": entity.name,\n                            \"actor_id\": f'{actor_type.value}_cn_{item[\"HOLDER_CODE\"]}',\n                            \"actor_type\": actor_type.value,\n                            \"actor_code\": item[\"HOLDER_CODE\"],\n                            \"actor_name\": f'{item[\"HOLDER_NAME\"]}',\n                            \"report_date\": timestamp,\n                            \"report_period\": to_report_period_type(timestamp),\n                            \"holding_numbers\": item[\"TOTAL_SHARES\"],\n                            \"holding_ratio\": value_to_pct(item[\"FREESHARES_RATIO\"], 0),\n                            \"holding_values\": item[\"HOLD_VALUE\"],\n                        }\n                        for item in result\n                    ]\n                    df = pd.DataFrame.from_records(holders)\n                    df_to_db(\n                        data_schema=self.data_schema,\n                        df=df,\n                        provider=self.provider,\n                        force_update=True,\n                        drop_duplicates=True,\n                    )\n\n                    # save the actors\n                    actors = [\n                        {\n                            \"id\": f'{actor_type.value}_cn_{item[\"HOLDER_CODE\"]}',\n                            \"entity_id\": f'{actor_type.value}_cn_{item[\"HOLDER_CODE\"]}',\n                            \"entity_type\": actor_type.value,\n                            \"exchange\": \"cn\",\n                            \"code\": item[\"HOLDER_CODE\"],\n                            \"name\": f'{item[\"HOLDER_NAME\"]}',\n                        }\n                        for item in result\n                    ]\n                    df1 = pd.DataFrame.from_records(actors)\n                    df_to_db(\n                        data_schema=ActorMeta, df=df1, provider=self.provider, force_update=False, drop_duplicates=True\n                    )\n\n\nif __name__ == \"__main__\":\n    EMStockIIRecorder(codes=[\"000562\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"EMStockIIRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/actor/em_stock_top_ten_free_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nimport pandas as pd\n\nfrom zvt.api.utils import to_report_period_type, value_to_pct\nfrom zvt.contract import ActorType\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import TimestampsDataRecorder\nfrom zvt.domain import Stock, ActorMeta\nfrom zvt.domain.actor.stock_actor import StockTopTenFreeHolder, StockInstitutionalInvestorHolder\nfrom zvt.recorders.em.em_api import get_holder_report_dates, get_free_holders\nfrom zvt.utils.time_utils import to_pd_timestamp, to_date_time_str\n\n\nclass EMStockTopTenFreeRecorder(TimestampsDataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Stock\n\n    provider = \"em\"\n    data_schema = StockTopTenFreeHolder\n\n    def init_timestamps(self, entity_item) -> List[pd.Timestamp]:\n        result = get_holder_report_dates(code=entity_item.code)\n        if result:\n            return [to_pd_timestamp(item[\"END_DATE\"]) for item in result]\n\n    def on_finish_entity(self, entity):\n        super().on_finish_entity(entity)\n        holders = StockTopTenFreeHolder.query_data(\n            entity_id=entity.id,\n            filters=[StockTopTenFreeHolder.holding_values == None],\n            session=self.session,\n            return_type=\"domain\",\n        )\n        for holder in holders:\n            ii = StockInstitutionalInvestorHolder.query_data(\n                entity_id=entity.id,\n                filters=[\n                    StockInstitutionalInvestorHolder.holding_values > 1,\n                    StockInstitutionalInvestorHolder.holding_ratio > 0.01,\n                    StockInstitutionalInvestorHolder.timestamp == holder.timestamp,\n                ],\n                limit=1,\n                return_type=\"domain\",\n            )\n            if ii:\n                holder.holding_values = holder.holding_ratio * ii[0].holding_values / ii[0].holding_ratio\n        self.session.commit()\n\n    def record(self, entity, start, end, size, timestamps):\n        for timestamp in timestamps:\n            the_date = to_date_time_str(timestamp)\n            result = get_free_holders(code=entity.code, end_date=the_date)\n            if result:\n                holders = []\n                new_actors = []\n                for item in result:\n                    # {'END_DATE': '2021-03-31 00:00:00',\n                    #   'FREE_HOLDNUM_RATIO': 0.631949916991,\n                    #   'FREE_RATIO_QOQ': '-5.33046217',\n                    #   'HOLDER_CODE': '161606',\n                    #   'HOLDER_CODE_OLD': '161606',\n                    #   'HOLDER_NAME': '交通银行-融通行业景气证券投资基金',\n                    #   'HOLDER_RANK': 10,\n                    #   'HOLD_NUM': 39100990,\n                    #   'IS_HOLDORG': '1',\n                    #   'SECUCODE': '000338.SZ'}\n                    # 机构\n                    if item[\"IS_HOLDORG\"] == \"1\":\n                        domains: List[ActorMeta] = ActorMeta.query_data(\n                            filters=[ActorMeta.code == item[\"HOLDER_CODE\"]], return_type=\"domain\"\n                        )\n                        if not domains:\n                            actor_type = ActorType.corporation.value\n                            actor = ActorMeta(\n                                entity_id=f'{actor_type}_cn_{item[\"HOLDER_CODE\"]}',\n                                id=f'{actor_type}_cn_{item[\"HOLDER_CODE\"]}',\n                                entity_type=actor_type,\n                                exchange=\"cn\",\n                                code=item[\"HOLDER_CODE\"],\n                                name=item[\"HOLDER_NAME\"],\n                            )\n                        else:\n                            actor = domains[0]\n                    else:\n                        actor_type = ActorType.individual.value\n                        actor = ActorMeta(\n                            entity_id=f'{actor_type}_cn_{item[\"HOLDER_NAME\"]}',\n                            id=f'{actor_type}_cn_{item[\"HOLDER_NAME\"]}',\n                            entity_type=actor_type,\n                            exchange=\"cn\",\n                            code=item[\"HOLDER_NAME\"],\n                            name=item[\"HOLDER_NAME\"],\n                        )\n                        new_actors.append(actor.__dict__)\n                    holder = {\n                        \"id\": f\"{entity.entity_id}_{the_date}_{actor.entity_id}\",\n                        \"entity_id\": entity.entity_id,\n                        \"timestamp\": timestamp,\n                        \"code\": entity.code,\n                        \"name\": entity.name,\n                        \"actor_id\": actor.entity_id,\n                        \"actor_type\": actor.entity_type,\n                        \"actor_code\": actor.code,\n                        \"actor_name\": actor.name,\n                        \"report_date\": timestamp,\n                        \"report_period\": to_report_period_type(timestamp),\n                        \"holding_numbers\": item[\"HOLD_NUM\"],\n                        \"holding_ratio\": value_to_pct(item[\"FREE_HOLDNUM_RATIO\"], 0),\n                    }\n                    holders.append(holder)\n                if holders:\n                    df = pd.DataFrame.from_records(holders)\n                    df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n                if new_actors:\n                    df = pd.DataFrame.from_records(new_actors)\n                    df_to_db(data_schema=ActorMeta, df=df, provider=self.provider, force_update=False)\n\n\nif __name__ == \"__main__\":\n    EMStockTopTenFreeRecorder(codes=[\"000338\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"EMStockTopTenFreeRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/actor/em_stock_top_ten_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nimport pandas as pd\n\nfrom zvt.api.utils import to_report_period_type, value_to_pct\nfrom zvt.contract import ActorType\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import TimestampsDataRecorder\nfrom zvt.domain import Stock, ActorMeta\nfrom zvt.domain.actor.stock_actor import StockTopTenHolder, StockInstitutionalInvestorHolder\nfrom zvt.recorders.em.em_api import get_holder_report_dates, get_holders\nfrom zvt.utils.time_utils import to_pd_timestamp, to_date_time_str\n\n\nclass EMStockTopTenRecorder(TimestampsDataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Stock\n\n    provider = \"em\"\n    data_schema = StockTopTenHolder\n\n    def init_timestamps(self, entity_item) -> List[pd.Timestamp]:\n        result = get_holder_report_dates(code=entity_item.code)\n        if result:\n            return [to_pd_timestamp(item[\"END_DATE\"]) for item in result]\n\n    def on_finish_entity(self, entity):\n        super().on_finish_entity(entity)\n        holders = StockTopTenHolder.query_data(\n            entity_id=entity.id,\n            filters=[StockTopTenHolder.holding_values == None],\n            session=self.session,\n            return_type=\"domain\",\n        )\n        for holder in holders:\n            ii = StockInstitutionalInvestorHolder.query_data(\n                entity_id=entity.id,\n                filters=[\n                    StockInstitutionalInvestorHolder.holding_values > 1,\n                    StockInstitutionalInvestorHolder.holding_ratio > 0.01,\n                    StockInstitutionalInvestorHolder.timestamp == holder.timestamp,\n                ],\n                limit=1,\n                return_type=\"domain\",\n            )\n            if ii:\n                holder.holding_values = holder.holding_ratio * ii[0].holding_values / ii[0].holding_ratio\n        self.session.commit()\n\n    def record(self, entity, start, end, size, timestamps):\n        for timestamp in timestamps:\n            the_date = to_date_time_str(timestamp)\n            result = get_holders(code=entity.code, end_date=the_date)\n            if result:\n                holders = []\n                new_actors = []\n                for item in result:\n                    # 机构\n                    if item[\"IS_HOLDORG\"] == \"1\":\n                        domains: List[ActorMeta] = ActorMeta.query_data(\n                            filters=[ActorMeta.code == item[\"HOLDER_CODE\"]], return_type=\"domain\"\n                        )\n                        if not domains:\n                            actor_type = ActorType.corporation.value\n                            actor = ActorMeta(\n                                entity_id=f'{actor_type}_cn_{item[\"HOLDER_CODE\"]}',\n                                id=f'{actor_type}_cn_{item[\"HOLDER_CODE\"]}',\n                                entity_type=actor_type,\n                                exchange=\"cn\",\n                                code=item[\"HOLDER_CODE\"],\n                                name=item[\"HOLDER_NAME\"],\n                            )\n                        else:\n                            actor = domains[0]\n                    else:\n                        actor_type = ActorType.individual.value\n                        actor = ActorMeta(\n                            entity_id=f'{actor_type}_cn_{item[\"HOLDER_NAME\"]}',\n                            id=f'{actor_type}_cn_{item[\"HOLDER_NAME\"]}',\n                            entity_type=actor_type,\n                            exchange=\"cn\",\n                            code=item[\"HOLDER_NAME\"],\n                            name=item[\"HOLDER_NAME\"],\n                        )\n                        new_actors.append(actor.__dict__)\n                    holder = {\n                        \"id\": f\"{entity.entity_id}_{the_date}_{actor.entity_id}\",\n                        \"entity_id\": entity.entity_id,\n                        \"timestamp\": timestamp,\n                        \"code\": entity.code,\n                        \"name\": entity.name,\n                        \"actor_id\": actor.entity_id,\n                        \"actor_type\": actor.entity_type,\n                        \"actor_code\": actor.code,\n                        \"actor_name\": actor.name,\n                        \"report_date\": timestamp,\n                        \"report_period\": to_report_period_type(timestamp),\n                        \"holding_numbers\": item[\"HOLD_NUM\"],\n                        \"holding_ratio\": value_to_pct(item[\"HOLD_NUM_RATIO\"], default=0),\n                    }\n                    holders.append(holder)\n                if holders:\n                    df = pd.DataFrame.from_records(holders)\n                    df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n                if new_actors:\n                    df = pd.DataFrame.from_records(new_actors)\n                    df_to_db(data_schema=ActorMeta, df=df, provider=self.provider, force_update=False)\n\n\nif __name__ == \"__main__\":\n    EMStockTopTenRecorder(codes=[\"000002\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"EMStockTopTenRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/em_api.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport random\nimport time\nfrom typing import Union\n\nimport demjson3\nimport pandas as pd\nimport requests\nimport sqlalchemy\nfrom requests import Session\n\nfrom zvt import zvt_config\nfrom zvt.api.kdata import generate_kdata_id\nfrom zvt.api.utils import value_to_pct, china_stock_code_to_id\nfrom zvt.contract import (\n    ActorType,\n    AdjustType,\n    IntervalLevel,\n    Exchange,\n    TradableType,\n    get_entity_exchanges,\n    tradable_type_map_exchanges,\n)\nfrom zvt.contract.api import decode_entity_id, df_to_db\nfrom zvt.domain import BlockCategory, StockHotTopic\nfrom zvt.recorders.consts import DEFAULT_HEADER\nfrom zvt.utils.time_utils import (\n    to_pd_timestamp,\n    now_timestamp_ms,\n    to_date_time_str,\n    current_date,\n    now_pd_timestamp,\n)\nfrom zvt.utils.utils import to_float, json_callback_param, chrome_copy_header_to_dict\n\nlogger = logging.getLogger(__name__)\n\nEM_TOKEN_HEADER = None\nif zvt_config[\"em_header\"]:\n    try:\n        EM_TOKEN_HEADER = chrome_copy_header_to_dict(zvt_config[\"em_header\"])\n    except Exception as e:\n        logger.error(e)\n\nif not EM_TOKEN_HEADER:\n    EM_TOKEN_HEADER = DEFAULT_HEADER\n\n\n# 获取中美国债收益率\ndef get_treasury_yield(pn=1, ps=2000, fetch_all=True):\n    results = get_em_data(\n        request_type=\"RPTA_WEB_TREASURYYIELD\",\n        source=None,\n        fields=\"ALL\",\n        sort_by=\"SOLAR_DATE\",\n        sort=\"desc\",\n        pn=pn,\n        ps=ps,\n        fetch_all=fetch_all,\n    )\n    yields = []\n    for item in results:\n        date = item[\"SOLAR_DATE\"]\n        # 中国\n        yields.append(\n            {\n                \"id\": f\"country_galaxy_CN_{to_date_time_str(date)}\",\n                \"entity_id\": \"country_galaxy_CN\",\n                \"timestamp\": to_pd_timestamp(date),\n                \"code\": \"CN\",\n                \"yield_2\": item.get(\"EMM00588704\"),\n                \"yield_5\": item.get(\"EMM00166462\"),\n                \"yield_10\": item.get(\"EMM00166466\"),\n                \"yield_30\": item.get(\"EMM00166469\"),\n            }\n        )\n        yields.append(\n            {\n                \"id\": f\"country_galaxy_US_{to_date_time_str(date)}\",\n                \"entity_id\": \"country_galaxy_US\",\n                \"timestamp\": to_pd_timestamp(date),\n                \"code\": \"US\",\n                \"yield_2\": item.get(\"EMG00001306\"),\n                \"yield_5\": item.get(\"EMG00001308\"),\n                \"yield_10\": item.get(\"EMG00001310\"),\n                \"yield_30\": item.get(\"EMG00001312\"),\n            }\n        )\n    return yields\n\n\n# 机构持仓日期\ndef get_ii_holder_report_dates(code):\n    return get_em_data(\n        request_type=\"RPT_F10_MAIN_ORGHOLD\",\n        fields=\"REPORT_DATE,IS_COMPLETE\",\n        filters=generate_filters(code=code),\n        sort_by=\"REPORT_DATE\",\n        sort=\"desc\",\n    )\n\n\ndef get_dragon_and_tiger_list(start_date, end_date=None):\n    start_date = to_date_time_str(start_date)\n    if not end_date:\n        end_date = now_timestamp_ms()\n    end_date = to_date_time_str(end_date)\n    return get_em_data(\n        request_type=\"RPT_DAILYBILLBOARD_DETAILS\",\n        fields=\"ALL\",\n        source=\"DataCenter\",\n        filters=f\"(TRADE_DATE>='{start_date}')(TRADE_DATE<='{end_date}')\",\n        sort_by=\"TRADE_DATE,SECURITY_CODE\",\n        sort=\"asc,asc\",\n    )\n\n\n# 龙虎榜\ndef get_dragon_and_tiger(code, start_date=None):\n    return get_em_data(\n        request_type=\"RPT_OPERATEDEPT_TRADE\",\n        fields=\"TRADE_ID,TRADE_DATE,EXPLANATION,SECUCODE,SECURITY_CODE,SECURITY_NAME_ABBR,ACCUM_AMOUNT,CHANGE_RATE,NET_BUY,BUY_BUY_TOTAL,BUY_SELL_TOTAL,BUY_RATIO_TOTAL,SELL_BUY_TOTAL,SELL_SELL_TOTAL,SELL_RATIO_TOTAL,TRADE_DIRECTION,RANK,OPERATEDEPT_NAME,BUY_AMT_REAL,SELL_AMT_REAL,BUY_RATIO,SELL_RATIO,BUY_TOTAL,SELL_TOTAL,BUY_TOTAL_NET,SELL_TOTAL_NET,NET\",\n        filters=generate_filters(code=code, trade_date=start_date, field_op={\"trade_date\": \">=\"}),\n        params='(groupField=TRADE_ID)(groupedFields=TRADE_DIRECTION,RANK,OPERATEDEPT_NAME,BUY_AMT_REAL,SELL_AMT_REAL,BUY_RATIO,SELL_RATIO,NET\")(groupListName=\"LIST\")',\n        sort_by=\"TRADE_DATE,RANK\",\n        sort=\"asc,asc\",\n    )\n\n\n# 十大股东持仓日期\ndef get_holder_report_dates(code):\n    return get_em_data(\n        request_type=\"RPT_F10_EH_HOLDERSDATE\",\n        fields=\"END_DATE,IS_DEFAULT,IS_REPORTDATE\",\n        filters=generate_filters(code=code),\n        sort_by=\"END_DATE\",\n        sort=\"desc\",\n    )\n\n\n# 十大流通股东日期\ndef get_free_holder_report_dates(code):\n    return get_em_data(\n        request_type=\"RPT_F10_EH_FREEHOLDERSDATE\",\n        fields=\"END_DATE,IS_DEFAULT,IS_REPORTDATE\",\n        filters=generate_filters(code=code),\n        sort_by=\"END_DATE\",\n        sort=\"desc\",\n    )\n\n\n# https://datacenter.eastmoney.com/securities/api/data/get?type=RPT_F10_EH_RELATION&sty=SECUCODE%2CHOLDER_NAME%2CRELATED_RELATION%2CHOLD_RATIO&filter=(SECUCODE%3D%22601162.SH%22)&client=APP&source=SECURITIES&p=1&ps=200&rdm=rnd_01BE6995104944ED99B70EEB7FFC0353&v=012649539724458458\n# https://datacenter.eastmoney.com/securities/api/data/get?type=RPT_F10_FREE_TOTALHOLDNUM&sty=SECUCODE%2CSECURITY_CODE%2CEND_DATE%2CHOLD_NUM_COUNT%2CHOLD_RATIO_COUNT%2CHOLD_RATIO_CHANGE&filter=(SECUCODE%3D%22601162.SH%22)(END_DATE%3D%272024-09-30%27)&client=APP&source=SECURITIES&p=1&ps=200&sr=1&st=&rdm=rnd_FA1943FA30474E3AA0CCF206EA1B5749&v=032098454407366983\ndef get_controlling_shareholder(code):\n    return get_em_data(\n        request_type=\"RPT_F10_EH_RELATION\",\n        fields=\"SECUCODE,CHOLDER_NAME,CRELATED_RELATION,CHOLD_RATIO\",\n        filters=generate_filters(code=code),\n    )\n\n\n# 机构持仓\ndef get_ii_holder(code, report_date, org_type):\n    return get_em_data(\n        request_type=\"RPT_MAIN_ORGHOLDDETAIL\",\n        fields=\"SECURITY_CODE,REPORT_DATE,HOLDER_CODE,HOLDER_NAME,TOTAL_SHARES,HOLD_VALUE,FREESHARES_RATIO,ORG_TYPE,SECUCODE,FUND_DERIVECODE\",\n        filters=generate_filters(code=code, report_date=report_date, org_type=org_type),\n    )\n\n\n# 机构持仓汇总\ndef get_ii_summary(code, report_date, org_type):\n    return get_em_data(\n        request_type=\"RPT_F10_MAIN_ORGHOLDDETAILS\",\n        fields=\"SECURITY_CODE,SECUCODE,REPORT_DATE,ORG_TYPE,TOTAL_ORG_NUM,TOTAL_FREE_SHARES,TOTAL_MARKET_CAP,TOTAL_SHARES_RATIO,CHANGE_RATIO,IS_COMPLETE\",\n        filters=generate_filters(code=code, report_date=report_date, org_type=org_type),\n    )\n\n\ndef get_free_holders(code, end_date):\n    return get_em_data(\n        request_type=\"RPT_F10_EH_FREEHOLDERS\",\n        fields=\"SECUCODE,END_DATE,HOLDER_NAME,HOLDER_CODE,HOLDER_CODE_OLD,HOLD_NUM,FREE_HOLDNUM_RATIO,FREE_RATIO_QOQ,IS_HOLDORG,HOLDER_RANK\",\n        filters=generate_filters(code=code, end_date=end_date),\n        sort_by=\"HOLDER_RANK\",\n    )\n\n\ndef get_top_ten_free_holder_stats(code):\n    datas = get_holder_report_dates(code=code)\n    if datas:\n        end_date = to_date_time_str(datas[0][\"END_DATE\"])\n        holders = get_em_data(\n            request_type=\"RPT_F10_FREE_TOTALHOLDNUM\",\n            fields=\"SECUCODE,SECURITY_CODE,END_DATE,HOLD_NUM_COUNT,HOLD_RATIO_COUNT,HOLD_RATIO_CHANGE,\",\n            filters=generate_filters(code=code, end_date=end_date),\n        )\n        if holders:\n            holder = holders[0]\n            ratio = 0\n            change = 0\n            try:\n                if holder[\"HOLD_RATIO_COUNT\"]:\n                    ratio = holder[\"HOLD_RATIO_COUNT\"] / 100\n                if holder[\"HOLD_RATIO_CHANGE\"]:\n                    change = holder[\"HOLD_RATIO_CHANGE\"] / 100\n            except Exception as e:\n                logger.warning(f\"Wrong holder {holder}\", e)\n\n            return {\n                \"code\": code,\n                \"timestamp\": end_date,\n                \"ratio\": ratio,\n                \"change\": change,\n            }\n\n\ndef get_controlling_shareholder(code):\n    holders = get_em_data(\n        request_type=\"RPT_F10_EH_RELATION\",\n        fields=\"SECUCODE,HOLDER_NAME,RELATED_RELATION,HOLD_RATIO\",\n        filters=generate_filters(code=code),\n    )\n\n    if holders:\n        control = {\"ratio\": 0}\n\n        for holder in holders:\n            if holder[\"RELATED_RELATION\"] == \"控股股东\":\n                control[\"holder\"] = holder[\"HOLDER_NAME\"]\n            elif holder[\"RELATED_RELATION\"] == \"实际控制人\":\n                control[\"parent\"] = holder[\"HOLDER_NAME\"]\n            if holder[\"HOLD_RATIO\"]:\n                control[\"ratio\"] = control[\"ratio\"] + holder[\"HOLD_RATIO\"]\n        return control\n\n\ndef get_holders(code, end_date):\n    return get_em_data(\n        request_type=\"RPT_F10_EH_HOLDERS\",\n        fields=\"SECUCODE,END_DATE,HOLDER_NAME,HOLDER_CODE,HOLDER_CODE_OLD,HOLD_NUM,HOLD_NUM_RATIO,HOLD_RATIO_QOQ,HOLDER_RANK,IS_HOLDORG\",\n        filters=generate_filters(code=code, end_date=end_date),\n        sort_by=\"HOLDER_RANK\",\n    )\n\n\ndef get_basic_info(entity_id, session=None):\n    entity_type, exchange, code = decode_entity_id(entity_id)\n    if entity_type == \"stock\":\n        request_type = \"RPT_F10_ORG_BASICINFO\"\n        fields = \"SECUCODE,LISTING_DATE,SECURITY_CODE,SECURITY_NAME_ABBR,ORG_NAME,FORMERNAME,CSRC_INDUSTRY_NAME,STR_CODEH,STR_NAMEH,STR_CODEA,STR_NAMEA,STR_CODEB,STR_NAMEB,REGIONBK,EM2016,BLGAINIAN,CHAIRMAN,LEGAL_PERSON,PRESIDENT,SECRETARY,FOUND_DATE,REG_CAPITAL,TOTAL_NUM,TATOLNUMBER,ORG_TEL,ORG_EMAIL,ORG_WEB,ADDRESS,REG_ADDRESS,ORG_PROFIE,MAIN_BUSINESS,SECURITY_TYPE_CODE,CURRENCY,ACCOUNT_FIRM,LEGAL_ADVISER,EXPAND_NAME_ABBR,ORG_PROFILE\"\n        filters = generate_filters(code=code, exchange=exchange.upper())\n    elif entity_type == \"stockhk\":\n        request_type = \"RPT_HKF10_INFO_ORGPROFILE;RPT_HKF10_INFO_SECURITYINFO\"\n        fields = \"SECUCODE,SECURITY_CODE,SECURITY_NAME_ABBR,ORG_NAME,CHAIRMAN,MAIN_BUSINESS,BELONG_INDUSTRY,BELONG_INDUSTRY,REG_PLACE,REG_CAPITAL,FOUND_DATE,LISTING_DATE,@SECURITY_INNER_CODE;@SECURITY_INNER_CODE,TRADE_UNIT,PAR_VALUE,ISSUE_PRICE,ISSUE_NUM\"\n        filters = generate_filters(code=code, exchange=\"HK\")\n    elif entity_type == \"stockus\":\n        if exchange == \"nasdaq\":\n            exchange_code = \"O\"\n        elif exchange == \"nyse\":\n            exchange_code = \"N\"\n        else:\n            raise Exception(f\"unknow exchange: {exchange}\")\n\n        request_type = \"RPT_USF10_INFO_SECURITYINFO;RPT_USF10_INFO_ORGPROFILE\"\n        fields = \"SECUCODE,SECURITY_CODE,SECURITY_TYPE,LISTING_DATE,TRADE_MARKET,ISSUE_PRICE,ISSUE_NUM,@SECUCODE;@SECUCODE,ORG_NAME,ORG_EN_ABBR,BELONG_INDUSTRY,FOUND_DATE,CHAIRMAN,ADDRESS,ORG_WEB\"\n        filters = generate_filters(code=code, exchange=exchange_code)\n    else:\n        raise Exception(f\"unknow entity_type: {entity_type}\")\n\n    datas = get_em_data(\n        session=session,\n        request_type=request_type,\n        fields=fields,\n        filters=filters,\n    )\n    if datas:\n        return datas[0]\n    return None\n\n\ndef _order_param(order: str):\n    if order:\n        orders = order.split(\",\")\n        return \",\".join([\"1\" if item == \"asc\" else \"-1\" for item in orders])\n    return order\n\n\ndef get_url(\n    request_type=None,\n    fields=None,\n    request_type_param_name=\"type\",\n    fields_param_name=\"sty\",\n    source=\"SECURITIES\",\n    filters=None,\n    order_by=\"\",\n    order=\"asc\",\n    pn=1,\n    ps=2000,\n    pn_param_name=\"p\",\n    ps_param_name=\"ps\",\n    params=None,\n):\n    # 根据 url 映射如下\n    # type=RPT_F10_MAIN_ORGHOLDDETAILS\n    # sty=SECURITY_CODE,SECUCODE,REPORT_DATE,ORG_TYPE,TOTAL_ORG_NUM,TOTAL_FREE_SHARES,TOTAL_MARKET_CAP,TOTAL_SHARES_RATIO,CHANGE_RATIO,IS_COMPLETE\n    # filter=(SECUCODE=\"000338.SZ\")(REPORT_DATE=\\'2021-03-31\\')(ORG_TYPE=\"01\")\n    # sr=1\n    # st=\n    sr = _order_param(order=order)\n    v = random.randint(1000000000000000, 9000000000000000)\n\n    if filters or source:\n        url = f\"https://datacenter.eastmoney.com/securities/api/data/get?{request_type_param_name}={request_type}&{fields_param_name}={fields}&filter={filters}&client=APP&source={source}&{pn_param_name}={pn}&{ps_param_name}={ps}&sr={sr}&st={order_by}&v=0{v}\"\n    else:\n        url = f\"https://datacenter.eastmoney.com/api/data/get?{request_type_param_name}={request_type}&{fields_param_name}={fields}&st={order_by}&sr={sr}&{pn_param_name}={pn}&{ps_param_name}={ps}&_={now_timestamp_ms()}\"\n\n    if params:\n        url = url + f\"&params={params}\"\n\n    return url\n\n\ndef get_exchange(code):\n    code_ = int(code)\n    if 800000 >= code_ >= 600000:\n        return \"SH\"\n    elif code_ >= 400000:\n        return \"BJ\"\n    else:\n        return \"SZ\"\n\n\ndef actor_type_to_org_type(actor_type: ActorType):\n    if actor_type == ActorType.raised_fund:\n        return \"01\"\n    if actor_type == ActorType.qfii:\n        return \"02\"\n    if actor_type == ActorType.social_security:\n        return \"03\"\n    if actor_type == ActorType.broker:\n        return \"04\"\n    if actor_type == ActorType.insurance:\n        return \"05\"\n    if actor_type == ActorType.trust:\n        return \"06\"\n    if actor_type == ActorType.corporation:\n        return \"07\"\n    assert False\n\n\ndef generate_filters(\n    code=None, exchange=None, trade_date=None, report_date=None, end_date=None, org_type=None, field_op: dict = None\n):\n    args = [\n        item for item in locals().items() if item[1] and (item[0] not in (\"code\", \"exchange\", \"org_type\", \"field_op\"))\n    ]\n\n    result = \"\"\n    if code:\n        if exchange:\n            result += f'(SECUCODE=\"{code}.{exchange}\")'\n        else:\n            result += f'(SECUCODE=\"{code}.{get_exchange(code)}\")'\n    if org_type:\n        result += f'(ORG_TYPE=\"{org_type}\")'\n\n    for arg in args:\n        field = arg[0]\n        value = arg[1]\n        if field_op:\n            op = field_op.get(field, \"=\")\n        else:\n            op = \"=\"\n        result += f\"({field.upper()}{op}'{value}')\"\n\n    return result\n\n\ndef get_em_data(\n    request_type,\n    fields,\n    request_type_param_name=\"type\",\n    fields_param_name=\"sty\",\n    session=None,\n    source=\"SECURITIES\",\n    filters=None,\n    sort_by=\"\",\n    sort=\"asc\",\n    pn=1,\n    ps=2000,\n    pn_param_name=\"p\",\n    ps_param_name=\"ps\",\n    fetch_all=True,\n    fetch_count=1,\n    params=None,\n):\n    url = get_url(\n        request_type=request_type,\n        fields=fields,\n        request_type_param_name=request_type_param_name,\n        fields_param_name=fields_param_name,\n        source=source,\n        filters=filters,\n        order_by=sort_by,\n        order=sort,\n        pn=pn,\n        ps=ps,\n        pn_param_name=pn_param_name,\n        ps_param_name=ps_param_name,\n        params=params,\n    )\n    if session:\n        resp = session.get(url)\n    else:\n        resp = requests.get(url)\n    if resp.status_code == 200:\n        json_result = resp.json()\n        resp.close()\n\n        if json_result:\n            if json_result.get(\"result\"):\n                data: list = json_result[\"result\"][\"data\"]\n                need_next = pn < json_result[\"result\"][\"pages\"]\n            elif json_result.get(\"data\"):\n                data: list = json_result[\"data\"]\n                need_next = json_result[\"hasNext\"] == 1\n            else:\n                data = []\n                need_next = False\n            if fetch_all or fetch_count - 1 > 0:\n                if need_next:\n                    next_data = get_em_data(\n                        session=session,\n                        request_type=request_type,\n                        fields=fields,\n                        request_type_param_name=request_type_param_name,\n                        fields_param_name=fields_param_name,\n                        source=source,\n                        filters=filters,\n                        sort_by=sort_by,\n                        sort=sort,\n                        pn=pn + 1,\n                        ps=ps,\n                        pn_param_name=pn_param_name,\n                        ps_param_name=ps_param_name,\n                        fetch_all=fetch_all,\n                        fetch_count=fetch_count - 1,\n                        params=params,\n                    )\n                    if next_data:\n                        data = data + next_data\n                        return data\n                    else:\n                        return data\n                else:\n                    return data\n            else:\n                return data\n        return None\n    raise RuntimeError(f\"request em data code: {resp.status_code}, error: {resp.text}\")\n\n\n# quote\n# url = 'https://push2his.eastmoney.com/api/qt/stock/kline/get?'\n# 日线      klt=101\n# 周线      klt=102\n# 月线      klt=103\n#\n# limit    lmt=2000\n#\n# 结束时间   end=20500000\n#\n# 复权      fqt 0 不复权 1 前复权 2 后复权\n#          iscca\n#\n# 字段\n# f51,f52,f53,f54,f55,\n# timestamp,open,close,high,low\n# f56,f57,f58,f59,f60,f61,f62,f63,f64\n# volume,turnover,震幅,change_pct,change,turnover_rate\n# 深圳\n# secid=0.399001&klt=101&fqt=1&lmt=66&end=20500000&iscca=1&fields1=f1,f2,f3,f4,f5,f6,f7,f8&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64&ut=f057cbcbce2a86e2866ab8877db1d059&forcect=1\n# secid=0.399001&klt=102&fqt=1&lmt=66&end=20500000&iscca=1&fields1=f1,f2,f3,f4,f5,f6,f7,f8&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64&ut=f057cbcbce2a86e2866ab8877db1d059&forcect=1\n# secid=0.000338&klt=101&fqt=1&lmt=66&end=20500000&iscca=1&fields1=f1,f2,f3,f4,f5,f6,f7,f8&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64&ut=f057cbcbce2a86e2866ab8877db1d059&forcect=1\n#\n# 港股\n# secid=116.01024&klt=102&fqt=1&lmt=66&end=20500000&iscca=1&fields1=f1,f2,f3,f4,f5,f6,f7,f8&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64&ut=f057cbcbce2a86e2866ab8877db1d059&forcect=1\n# 美股\n# secid=106.BABA&klt=102&fqt=1&lmt=66&end=20500000&iscca=1&fields1=f1,f2,f3,f4,f5,f6,f7,f8&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64&ut=f057cbcbce2a86e2866ab8877db1d059&forcect=1\n#\n# 上海\n# secid=1.512660&klt=101&fqt=1&lmt=66&end=20500000&iscca=1&fields1=f1,f2,f3,f4,f5,f6,f7,f8&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64&ut=f057cbcbce2a86e2866ab8877db1d059&forcect=1\ndef get_kdata(entity_id, session=None, level=IntervalLevel.LEVEL_1DAY, adjust_type=AdjustType.qfq, limit=10000):\n    entity_type, exchange, code = decode_entity_id(entity_id)\n    level = IntervalLevel(level)\n\n    sec_id = to_em_sec_id(entity_id)\n    fq_flag = to_em_fq_flag(adjust_type)\n    level_flag = to_em_level_flag(level)\n    # f131 结算价\n    # f133 持仓\n    # 目前未获取\n    url = f\"https://push2his.eastmoney.com/api/qt/stock/kline/get?secid={sec_id}&klt={level_flag}&fqt={fq_flag}&lmt={limit}&end=20500000&iscca=1&fields1=f1,f2,f3,f4,f5,f6,f7,f8&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63,f64&ut=f057cbcbce2a86e2866ab8877db1d059&forcect=1\"\n\n    if session:\n        resp = session.get(url, headers=DEFAULT_HEADER)\n    else:\n        resp = requests.get(url, headers=DEFAULT_HEADER)\n    resp.raise_for_status()\n    results = resp.json()\n    resp.close()\n    data = results[\"data\"]\n\n    kdatas = []\n\n    if data:\n        klines = data[\"klines\"]\n        name = data[\"name\"]\n\n        for result in klines:\n            # \"2000-01-28,1005.26,1012.56,1173.12,982.13,3023326,3075552000.00\"\n            # \"2021-08-27,19.39,20.30,20.30,19.25,1688497,3370240912.00,5.48,6.01,1.15,3.98,0,0,0\"\n            # time,open,close,high,low,volume,turnover\n            # \"2022-04-13,10708,10664,10790,10638,402712,43124771328,1.43,0.57,60,0.00,4667112399583576064,4690067230254170112,1169270784\"\n            fields = result.split(\",\")\n            the_timestamp = to_pd_timestamp(fields[0])\n\n            the_id = generate_kdata_id(entity_id=entity_id, timestamp=the_timestamp, level=level)\n\n            open = to_float(fields[1])\n            close = to_float(fields[2])\n            high = to_float(fields[3])\n            low = to_float(fields[4])\n            volume = to_float(fields[5])\n            turnover = to_float(fields[6])\n            # 7 振幅\n            change_pct = value_to_pct(to_float(fields[8]))\n            # 9 变动\n            turnover_rate = value_to_pct(to_float(fields[10]))\n\n            kdatas.append(\n                dict(\n                    id=the_id,\n                    timestamp=the_timestamp,\n                    entity_id=entity_id,\n                    provider=\"em\",\n                    code=code,\n                    name=name,\n                    level=level.value,\n                    open=open,\n                    close=close,\n                    high=high,\n                    low=low,\n                    volume=volume,\n                    turnover=turnover,\n                    turnover_rate=turnover_rate,\n                    change_pct=change_pct,\n                )\n            )\n    if kdatas:\n        df = pd.DataFrame.from_records(kdatas)\n        return df\n\n\ndef get_basic_info1(entity_id):\n    entity_type, exchange, code = decode_entity_id(entity_id)\n    if entity_type == \"stock\":\n        url = \"https://emh5.eastmoney.com/api/GongSiGaiKuang/GetJiBenZiLiao\"\n        result_field = \"JiBenZiLiao\"\n    elif entity_type == \"stockus\":\n        url = \"https://emh5.eastmoney.com/api/MeiGu/GaiKuang/GetZhengQuanZiLiao\"\n        result_field = \"ZhengQuanZiLiao\"\n    elif entity_type == \"stockhk\":\n        url = \"https://emh5.eastmoney.com/api/GangGu/GaiKuang/GetZhengQuanZiLiao\"\n        result_field = \"ZhengQuanZiLiao\"\n    else:\n        assert False\n\n    data = {\"fc\": to_em_fc(entity_id=entity_id), \"color\": \"w\"}\n    resp = requests.post(url=url, json=data, headers=DEFAULT_HEADER)\n    resp.encoding = \"utf-8\"\n    resp.raise_for_status()\n    resp.close()\n\n    return resp.json()[\"Result\"][result_field]\n\n\ndef get_future_list():\n    # 主连\n    url = f\"https://futsseapi.eastmoney.com/list/filter/2?fid=sp_all&mktid=0&typeid=0&pageSize=1000&pageIndex=0&callbackName=jQuery34106875017735118845_1649736551642&sort=asc&orderBy=idx&_={now_timestamp_ms()}\"\n    resp = requests.get(url, headers=DEFAULT_HEADER)\n    resp.raise_for_status()\n    result = json_callback_param(resp.text)\n    resp.close()\n    # [['DCE', 'im'], ['SHFE', 'rbm'], ['SHFE', 'hcm'], ['SHFE', 'ssm'], ['CZCE', 'SFM'], ['CZCE', 'SMM'], ['SHFE', 'wrm'], ['SHFE', 'cum'], ['SHFE', 'alm'], ['SHFE', 'znm'], ['SHFE', 'pbm'], ['SHFE', 'nim'], ['SHFE', 'snm'], ['INE', 'bcm'], ['SHFE', 'aum'], ['SHFE', 'agm'], ['DCE', 'am'], ['DCE', 'bm'], ['DCE', 'ym'], ['DCE', 'mm'], ['CZCE', 'RSM'], ['CZCE', 'OIM'], ['CZCE', 'RMM'], ['DCE', 'pm'], ['DCE', 'cm'], ['DCE', 'csm'], ['DCE', 'jdm'], ['CZCE', 'CFM'], ['CZCE', 'CYM'], ['CZCE', 'SRM'], ['CZCE', 'APM'], ['CZCE', 'CJM'], ['CZCE', 'PKM'], ['CZCE', 'PMM'], ['CZCE', 'WHM'], ['DCE', 'rrm'], ['CZCE', 'JRM'], ['CZCE', 'RIM'], ['CZCE', 'LRM'], ['DCE', 'lhm'], ['INE', 'scm'], ['SHFE', 'fum'], ['DCE', 'pgm'], ['INE', 'lum'], ['SHFE', 'bum'], ['CZCE', 'MAM'], ['DCE', 'egm'], ['DCE', 'lm'], ['CZCE', 'TAM'], ['DCE', 'vm'], ['DCE', 'ppm'], ['DCE', 'ebm'], ['CZCE', 'SAM'], ['CZCE', 'FGM'], ['CZCE', 'URM'], ['SHFE', 'rum'], ['INE', 'nrm'], ['SHFE', 'spm'], ['DCE', 'fbm'], ['DCE', 'bbm'], ['CZCE', 'PFM'], ['DCE', 'jmm'], ['DCE', 'jm'], ['CZCE', 'ZCM'], ['8', '060120'], ['8', '040120'], ['8', '070120'], ['8', '110120'], ['8', '050120'], ['8', '130120']]\n    futures = []\n    for item in result[\"list\"]:\n        entity = {}\n        entity[\"exchange\"], entity[\"code\"] = item[\"uid\"].split(\"|\")\n\n        # {'8', 'CZCE', 'DCE', 'INE', 'SHFE'}\n        if entity[\"exchange\"] == \"8\":\n            entity[\"exchange\"] = \"cffex\"\n            entity[\"code\"] = to_zvt_code(entity[\"code\"])\n        else:\n            try:\n                entity[\"exchange\"] = Exchange(entity[\"exchange\"].lower()).value\n                if entity[\"code\"][-1].lower() == \"m\":\n                    entity[\"code\"] = entity[\"code\"][:-1]\n                else:\n                    assert False\n                entity[\"code\"] = entity[\"code\"].upper()\n            except Exception as e:\n                logger.error(f\"wrong item: {item}\", e)\n                continue\n\n        entity[\"entity_type\"] = \"future\"\n        entity[\"name\"] = item[\"name\"]\n        entity[\"id\"] = f\"future_{entity['exchange']}_{entity['code']}\"\n        entity[\"entity_id\"] = entity[\"id\"]\n        futures.append(entity)\n    df = pd.DataFrame.from_records(data=futures)\n    return df\n\n\ndef _calculate_limit(row):\n    code = row[\"code\"]\n    change_pct = row[\"change_pct\"]\n    if code.startswith((\"83\", \"87\", \"88\", \"889\", \"82\", \"920\")):\n        return change_pct >= 0.29, change_pct <= -0.29\n    elif code.startswith(\"300\") or code.startswith(\"301\") or code.startswith(\"688\"):\n        return change_pct >= 0.19, change_pct <= -0.19\n    else:\n        return change_pct > 0.09, change_pct < -0.09\n\n\ndef get_stock_turnover():\n    sz_url = \"https://push2his.eastmoney.com/api/qt/stock/trends2/get?fields1=f1,f2&fields2=f51,f57&ut=fa5fd1943c7b386f172d6893dbfba10b&iscr=0&iscca=0&secid=0.399001&time=0&ndays=2\"\n    resp = requests.get(sz_url, headers=DEFAULT_HEADER)\n\n    resp.raise_for_status()\n\n    data = resp.json()[\"data\"][\"trends\"]\n    resp.close()\n    return data\n\n\ndef get_top_tradable_list(entity_type, fields, limit, entity_flag, pn=1, exchange=None, return_quote=False):\n    logger.info(f\"get_top_tradable_list {entity_type} exchange: {exchange} return_quote: {return_quote} to pn: {pn}\")\n    url = f\"https://push2.eastmoney.com/api/qt/clist/get?np=1&fltt=2&invt=2&fields={fields}&pn={pn}&pz={limit}&fid=f3&po=1&{entity_flag}&ut=f057cbcbce2a86e2866ab8877db1d059&forcect=1&cb=cbCallbackMore&&callback=jQuery34109676853980006124_{now_timestamp_ms() - 1}&_={now_timestamp_ms()}\"\n    resp = requests.get(url, headers=EM_TOKEN_HEADER)\n\n    resp.raise_for_status()\n\n    result = json_callback_param(resp.text)\n    resp.close()\n\n    if not result[\"data\"]:\n        return None\n\n    total = result[\"data\"][\"total\"]\n    data = result[\"data\"][\"diff\"]\n\n    if pn != 1:\n        return data\n\n    data_size = len(data)\n\n    if total != data_size and limit > data_size:\n        pn_size = int(total / data_size)\n        if total % data_size:\n            pn_size = pn_size + 1\n\n        while pn < pn_size:\n            pn = pn + 1\n            logger.info(f\"sleep 3 seconds to request {pn}/{pn_size}\")\n            time.sleep(3)\n\n            append_data = get_top_tradable_list(\n                entity_type=entity_type,\n                fields=fields,\n                limit=limit,\n                entity_flag=entity_flag,\n                pn=pn,\n                exchange=exchange,\n                return_quote=return_quote,\n            )\n            if append_data:\n                data = data + append_data\n\n    df = pd.DataFrame.from_records(data=data)\n\n    if return_quote:\n        df = df[[\"f12\", \"f13\", \"f14\", \"f2\", \"f3\", \"f5\", \"f8\", \"f6\", \"f15\", \"f16\", \"f17\", \"f20\", \"f21\"]]\n        df.columns = [\n            \"code\",\n            \"market_code\",\n            \"name\",\n            \"price\",\n            \"change_pct\",\n            \"volume\",\n            \"turnover_rate\",\n            \"turnover\",\n            \"high\",\n            \"low\",\n            \"open\",\n            \"total_cap\",\n            \"float_cap\",\n        ]\n\n        df = df.dropna()\n        df = df[df.change_pct != \"-\"]\n        df = df[df.turnover_rate != \"-\"]\n        df = df[df.turnover != \"-\"]\n        df = df[df.turnover != 0]\n\n        df = df.astype({\"change_pct\": \"float\", \"turnover_rate\": \"float\", \"turnover\": \"float\", \"volume\": \"float\"})\n\n        df[\"change_pct\"] = df[\"change_pct\"] / 100\n        df[\"turnover_rate\"] = df[\"turnover_rate\"] / 100\n\n        df[[\"is_limit_up\", \"is_limit_down\"]] = df.apply(lambda row: _calculate_limit(row), axis=1, result_type=\"expand\")\n        df[\"entity_id\"] = df[[\"market_code\", \"code\"]].apply(\n            lambda row: market_code_to_entity_id(market=row[\"market_code\"], code=row[\"code\"]), axis=1\n        )\n    else:\n        if entity_type in (TradableType.stock, TradableType.stockhk, TradableType.stockus):\n            df = df[[\"f12\", \"f13\", \"f14\", \"f20\", \"f21\", \"f9\", \"f23\"]]\n            df.columns = [\"code\", \"exchange\", \"name\", \"total_cap\", \"float_cap\", \"pe\", \"pb\"]\n            df[[\"total_cap\", \"float_cap\", \"pe\", \"pb\"]] = df[[\"total_cap\", \"float_cap\", \"pe\", \"pb\"]].apply(\n                pd.to_numeric, errors=\"coerce\"\n            )\n        else:\n            df = df[[\"f12\", \"f13\", \"f14\"]]\n            df.columns = [\"code\", \"exchange\", \"name\"]\n        if exchange:\n            df[\"exchange\"] = exchange.value\n        df[\"entity_type\"] = entity_type.value\n        df[\"id\"] = df[[\"entity_type\", \"exchange\", \"code\"]].apply(lambda x: \"_\".join(x.astype(str)), axis=1)\n        df[\"entity_id\"] = df[\"id\"]\n\n    return df\n\n\ndef get_top_stocks(limit=100):\n    # 沪深和北交所\n    entity_flag = \"fs=m:0+t:6+f:!2,m:0+t:13+f:!2,m:0+t:80+f:!2,m:1+t:2+f:!2,m:1+t:23+f:!2,m:0+t:81+s:2048\"\n\n    fields = \"f2,f3,f5,f6,f8,f12,f13,f14,f15,f16,f17,f20,f21\"\n    return get_top_tradable_list(\n        entity_type=TradableType.stock, fields=fields, limit=limit, entity_flag=entity_flag, return_quote=True\n    )\n\n\ndef get_top_stockhks(limit=20, hk_south=False):\n    if hk_south:\n        entity_flag = \"fs=b:DLMK0144,b:DLMK0146\"\n    else:\n        entity_flag = f\"fs=m:116+t:3,m:116+t:4\"\n    fields = \"f2,f3,f5,f6,f8,f12,f13,f14,f15,f16,f17,f20,f21\"\n    return get_top_tradable_list(\n        entity_type=TradableType.stockhk, fields=fields, limit=limit, entity_flag=entity_flag, return_quote=True\n    )\n\n\ndef get_top_stockuss(limit=50):\n    entity_flag = \"fs=m:105,m:106\"\n    fields = \"f2,f3,f5,f6,f8,f12,f13,f14,f15,f16,f17,f20,f21\"\n    return get_top_tradable_list(\n        entity_type=TradableType.stockus, fields=fields, limit=limit, entity_flag=entity_flag, return_quote=True\n    )\n\n\ndef get_tradable_list(\n    entity_type: Union[TradableType, str] = \"stock\",\n    exchange: Union[Exchange, str] = None,\n    limit: int = 10000,\n    hk_south=False,\n    block_category=BlockCategory.concept,\n):\n    entity_type = TradableType(entity_type)\n    if entity_type == TradableType.future:\n        return get_future_list()\n\n    exchanges = get_entity_exchanges(entity_type=entity_type)\n\n    if exchange is not None:\n        assert exchange in exchanges\n        exchanges = [exchange]\n\n    dfs = []\n    for exchange in exchanges:\n        exchange = Exchange(exchange)\n        ex_flag = to_em_entity_flag(exchange=exchange)\n        entity_flag = f\"fs=m:{ex_flag}\"\n\n        if entity_type == TradableType.index:\n            if exchange == Exchange.sh:\n                entity_flag = \"fs=i:1.000001,i:1.000002,i:1.000003,i:1.000009,i:1.000010,i:1.000011,i:1.000012,i:1.000016,i:1.000300,i:1.000903,i:1.000905,i:1.000906,i:1.000688,i:1.000852,i:2.932000\"\n            if exchange == Exchange.sz:\n                entity_flag = \"fs=i:0.399001,i:0.399002,i:0.399003,i:0.399004,i:0.399005,i:0.399006,i:0.399100,i:0.399106,i:0.399305,i:0.399550\"\n        elif entity_type == TradableType.currency:\n            entity_flag = \"fs=m:119,m:120\"\n        elif entity_type == TradableType.indexus:\n            # 纳斯达克，道琼斯，标普500，美元指数\n            entity_flag = \"fs=i:100.NDX,i:100.DJIA,i:100.SPX,i:100.UDI\"\n        elif entity_type == TradableType.blockus:\n            # 美股板块\n            entity_flag = \"fs=m:202\"\n        elif entity_type == TradableType.cbond:\n            if exchange == Exchange.sz:\n                entity_flag = \"fs=m:0+e:11\"\n            elif exchange == Exchange.sh:\n                entity_flag = \"fs=m:1+e:11\"\n            else:\n                assert False\n        elif entity_type == TradableType.indexhk:\n            entity_flag = \"fs=i:305.HSI\"\n        # m为交易所代码，t为交易类型\n        elif entity_type in [TradableType.block, TradableType.stock, TradableType.stockus, TradableType.stockhk]:\n            if exchange == Exchange.sh:\n                # t=2 主板\n                # t=23 科创板\n                entity_flag = \"fs=m:1+t:2,m:1+t:23\"\n            if exchange == Exchange.sz:\n                # t=6 主板\n                # t=80 创业板\n                entity_flag = \"fs=m:0+t:6,m:0+t:13,m:0+t:80\"\n            if exchange == Exchange.bj:\n                entity_flag = \"fs=m:0+t:81+s:2048\"\n            if exchange == Exchange.hk:\n                if hk_south:\n                    # 港股通\n                    entity_flag = \"fs=b:DLMK0144,b:DLMK0146\"\n                else:\n                    # t=3 主板\n                    # t=4 创业板\n                    entity_flag = f\"fs=m:116+t:3,m:116+t:4\"\n            if exchange == Exchange.nasdaq:\n                # t=1\n                # t=3 中概股\n                entity_flag = f\"fs=m:105+t:1,m:105+t:3\"\n            if exchange == Exchange.nyse:\n                # t=1\n                # t=3 中概股\n                entity_flag = f\"fs=m:106+t:1,m:105+t:3\"\n            if exchange == Exchange.cn:\n                if block_category == BlockCategory.industry:\n                    entity_flag = entity_flag + \"+t:2\"\n                elif block_category == BlockCategory.concept:\n                    entity_flag = entity_flag + \"+t:3\"\n                else:\n                    assert False\n\n        # f2, f3, f4, f12, f13, f14, f19, f111, f148\n        fields = \"f1,f2,f3,f4,f12,f13,f14\"\n        if entity_type in (TradableType.stock, TradableType.stockhk, TradableType.stockus):\n            # 市值,流通市值,pe,pb\n            fields = fields + \",f20,f21,f9,f23\"\n\n        df = get_top_tradable_list(\n            entity_type=entity_type, fields=fields, limit=limit, entity_flag=entity_flag, exchange=exchange\n        )\n        if entity_type == TradableType.block:\n            df[\"category\"] = block_category.value\n\n        dfs.append(df)\n\n    return pd.concat(dfs)\n\n\ndef get_block_stocks(block_id, name=\"\", session=None):\n    entity_type, exchange, code = decode_entity_id(block_id)\n    category_stocks_url = f\"http://48.push2.eastmoney.com/api/qt/clist/get?cb=jQuery11240710111145777397_{now_timestamp_ms() - 1}&pn=1&pz=1000&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&wbp2u=4668014655929990|0|1|0|web&fid=f3&fs=b:{code}+f:!50&fields=f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f13,f14,f15,f16,f17,f18,f20,f21,f23,f24,f25,f22,f11,f62,f128,f136,f115,f152,f45&_={now_timestamp_ms()}\"\n    if session:\n        resp = session.get(category_stocks_url, headers=DEFAULT_HEADER)\n    else:\n        resp = requests.get(category_stocks_url, headers=DEFAULT_HEADER)\n\n    data = json_callback_param(resp.text)[\"data\"]\n    the_list = []\n    if data:\n        results = data[\"diff\"]\n        for result in results:\n            stock_code = result[\"f12\"]\n            stock_name = result[\"f14\"]\n            stock_id = china_stock_code_to_id(stock_code)\n\n            the_list.append(\n                {\n                    \"id\": \"{}_{}\".format(block_id, stock_id),\n                    \"entity_id\": block_id,\n                    \"entity_type\": \"block\",\n                    \"exchange\": exchange,\n                    \"code\": code,\n                    \"name\": name,\n                    \"timestamp\": current_date(),\n                    \"stock_id\": stock_id,\n                    \"stock_code\": stock_code,\n                    \"stock_name\": stock_name,\n                }\n            )\n    return the_list\n\n\ndef market_code_to_entity_id(market, code):\n    if market in (0, 1):\n        return china_stock_code_to_id(code)\n    elif market == 105:\n        return f\"stockus_nasdaq_{code}\"\n    elif market == 106:\n        return f\"stockus_nyse_{code}\"\n    elif market == 116:\n        return f\"stockhk_hk_{code}\"\n    else:\n        for exchange, flag in exchange_map_em_flag.items():\n            if flag == market:\n                for entity_type, exchanges in tradable_type_map_exchanges.items():\n                    if exchange in exchanges:\n                        return f\"{entity_type.value}_{exchange.value}_{code}\"\n    return code\n\n\ndef get_hot_topic(session: Session = None):\n    url = \"https://emcreative.eastmoney.com/FortuneApi/GuBaApi/common\"\n    data = {\n        \"url\": \"newctopic/api/Topic/HomeTopicRead?deviceid=IPHONE&version=10001000&product=Guba&plat=Iphone&p=1&ps=20&needPkPost=true\",\n        \"type\": \"get\",\n        \"parm\": \"\",\n    }\n    logger.debug(f\"get hot topic from: {url}\")\n    if session:\n        resp = session.post(url=url, json=data, headers=DEFAULT_HEADER)\n    else:\n        resp = requests.post(url=url, json=data, headers=DEFAULT_HEADER)\n\n    if resp.status_code == 200:\n        data_list = resp.json().get(\"re\")\n        if data_list:\n            hot_topics = []\n            for position, data in enumerate(data_list):\n                if data[\"stockList\"]:\n                    entity_ids = [\n                        market_code_to_entity_id(market=stock[\"qMarket\"], code=stock[\"qCode\"])\n                        for stock in data[\"stockList\"]\n                    ]\n                else:\n                    entity_ids = []\n                topic_id = data[\"topicid\"]\n                entity_id = f\"hot_topic_{topic_id}\"\n                hot_topics.append(\n                    {\n                        \"id\": entity_id,\n                        \"entity_id\": entity_id,\n                        \"timestamp\": now_pd_timestamp(),\n                        \"created_timestamp\": to_pd_timestamp(data[\"cTime\"]),\n                        \"position\": position,\n                        \"entity_ids\": entity_ids,\n                        \"news_code\": topic_id,\n                        \"news_title\": data[\"name\"],\n                        \"news_content\": data[\"summary\"],\n                    }\n                )\n            return hot_topics\n\n    logger.error(f\"request em data code: {resp.status_code}, error: {resp.text}\")\n\n\ndef record_hot_topic():\n    hot_topics = get_hot_topic()\n    logger.debug(hot_topics)\n    if hot_topics:\n        df = pd.DataFrame.from_records(hot_topics)\n        df_to_db(\n            df=df, data_schema=StockHotTopic, provider=\"em\", force_update=True, dtype={\"entity_ids\": sqlalchemy.JSON}\n        )\n\n\ndef get_news(entity_id, ps=200, index=1, start_timestamp=None, session=None, latest_code=None):\n    sec_id = to_em_sec_id(entity_id=entity_id)\n    url = f\"https://np-listapi.eastmoney.com/comm/wap/getListInfo?cb=callback&client=wap&type=1&mTypeAndCode={sec_id}&pageSize={ps}&pageIndex={index}&callback=jQuery1830017478247906740352_{now_timestamp_ms() - 1}&_={now_timestamp_ms()}\"\n    logger.debug(f\"get news from: {url}\")\n    if session:\n        resp = session.get(url)\n    else:\n        resp = requests.get(url)\n    # {\n    #     \"Art_ShowTime\": \"2022-02-11 14:29:25\",\n    #     \"Art_Image\": \"\",\n    #     \"Art_MediaName\": \"每日经济新闻\",\n    #     \"Art_Code\": \"202202112274017262\",\n    #     \"Art_Title\": \"潍柴动力：巴拉德和锡里斯不纳入合并财务报表范围\",\n    #     \"Art_SortStart\": \"1644560965017262\",\n    #     \"Art_VideoCount\": 0,\n    #     \"Art_OriginUrl\": \"http://finance.eastmoney.com/news/1354,202202112274017262.html\",\n    #     \"Art_Url\": \"http://finance.eastmoney.com/a/202202112274017262.html\",\n    # }\n    if resp.status_code == 200:\n        json_text = resp.text[resp.text.index(\"(\") + 1 : resp.text.rindex(\")\")]\n        if \"list\" in demjson3.decode(json_text)[\"data\"]:\n            json_result = demjson3.decode(json_text)[\"data\"][\"list\"]\n            resp.close()\n            if json_result:\n                news = [\n                    {\n                        \"id\": f'{entity_id}_{item.get(\"Art_ShowTime\", \"\")}',\n                        \"entity_id\": entity_id,\n                        \"timestamp\": to_pd_timestamp(item.get(\"Art_ShowTime\", \"\")),\n                        \"news_code\": item.get(\"Art_Code\", \"\"),\n                        \"news_url\": item.get(\"Art_Url\", \"\"),\n                        \"news_title\": item.get(\"Art_Title\", \"\"),\n                        \"ignore_by_user\": False,\n                    }\n                    for index, item in enumerate(json_result)\n                    if not start_timestamp\n                    or (\n                        (to_pd_timestamp(item[\"Art_ShowTime\"]) >= start_timestamp)\n                        and (item.get(\"Art_Code\", \"\") != latest_code)\n                    )\n                ]\n                if len(news) < len(json_result):\n                    return news\n                next_data = get_news(entity_id=entity_id, ps=ps, index=index + 1)\n                if next_data:\n                    return news + next_data\n                else:\n                    return news\n        else:\n            return None\n\n    logger.error(f\"request em data code: {resp.status_code}, error: {resp.text}\")\n\n\n# utils to transform zvt entity to em entity\ndef to_em_fc(entity_id):\n    entity_type, exchange, code = decode_entity_id(entity_id)\n    if entity_type == \"stock\":\n        if exchange == \"sh\":\n            return f\"{code}01\"\n        if exchange == \"sz\":\n            return f\"{code}02\"\n\n    if entity_type == \"stockhk\":\n        return code\n\n    if entity_type == \"stockus\":\n        if exchange == \"nyse\":\n            return f\"{code}.N\"\n        if exchange == \"nasdaq\":\n            return f\"{code}.O\"\n\n\nexchange_map_em_flag = {\n    #: 深证交易所\n    Exchange.sz: 0,\n    #: 上证交易所\n    Exchange.sh: 1,\n    #: 北交所\n    Exchange.bj: 0,\n    #: 纳斯达克\n    Exchange.nasdaq: 105,\n    #: 纽交所\n    Exchange.nyse: 106,\n    #: 中国金融期货交易所\n    Exchange.cffex: 8,\n    #: 上海期货交易所\n    Exchange.shfe: 113,\n    #: 大连商品交易所\n    Exchange.dce: 114,\n    #: 郑州商品交易所\n    Exchange.czce: 115,\n    #: 上海国际能源交易中心\n    Exchange.ine: 142,\n    #: 港交所\n    Exchange.hk: 116,\n    #: 中国行业/概念板块\n    Exchange.cn: 90,\n    #: 美国指数\n    Exchange.us: 100,\n    #: 汇率\n    Exchange.forex: 119,\n}\n\n\ndef to_em_entity_flag(exchange: Union[Exchange, str]):\n    exchange = Exchange(exchange)\n    return exchange_map_em_flag.get(exchange, exchange)\n\n\ndef to_em_fq_flag(adjust_type: AdjustType):\n    adjust_type = AdjustType(adjust_type)\n    if adjust_type == AdjustType.bfq:\n        return 0\n    if adjust_type == AdjustType.qfq:\n        return 1\n    if adjust_type == AdjustType.hfq:\n        return 2\n\n\ndef to_em_level_flag(level: IntervalLevel):\n    level = IntervalLevel(level)\n    if level == IntervalLevel.LEVEL_1MIN:\n        return 1\n    elif level == IntervalLevel.LEVEL_5MIN:\n        return 5\n    elif level == IntervalLevel.LEVEL_15MIN:\n        return 15\n    elif level == IntervalLevel.LEVEL_30MIN:\n        return 30\n    elif level == IntervalLevel.LEVEL_1HOUR:\n        return 60\n    elif level == IntervalLevel.LEVEL_1DAY:\n        return 101\n    elif level == IntervalLevel.LEVEL_1WEEK:\n        return 102\n    elif level == IntervalLevel.LEVEL_1MON:\n        return 103\n\n    assert False\n\n\ndef to_em_sec_id(entity_id):\n    entity_type, exchange, code = decode_entity_id(entity_id)\n    # 主力合约\n    if entity_type == \"future\" and code[-1].isalpha():\n        code = code + \"m\"\n    if entity_type == \"currency\" and \"CNYC\" in code:\n        return f\"120.{code}\"\n    if entity_type == \"indexhk\":\n        return f\"100.{code}\"\n    return f\"{to_em_entity_flag(exchange)}.{code}\"\n\n\ndef to_zvt_code(code):\n    #  ('中证当月连续', '8|060120'),\n    #  ('沪深当月连续', '8|040120'),\n    #  ('上证当月连续', '8|070120'),\n    #  ('十债当季连续', '8|110120'),\n    #  ('五债当季连续', '8|050120'),\n    #  ('二债当季连续', '8|130120')]\n    if code == \"060120\":\n        return \"IC\"\n    elif code == \"040120\":\n        return \"IF\"\n    elif code == \"070120\":\n        return \"IH\"\n    elif code == \"110120\":\n        return \"T\"\n    elif code == \"050120\":\n        return \"TF\"\n    elif code == \"130120\":\n        return \"TS\"\n    return code\n\n\nif __name__ == \"__main__\":\n    print(get_tradable_list(entity_type=\"stockhk\"))\n\n\n# the __all__ is generated\n__all__ = [\n    \"get_treasury_yield\",\n    \"get_ii_holder_report_dates\",\n    \"get_dragon_and_tiger_list\",\n    \"get_dragon_and_tiger\",\n    \"get_holder_report_dates\",\n    \"get_free_holder_report_dates\",\n    \"get_controlling_shareholder\",\n    \"get_ii_holder\",\n    \"get_ii_summary\",\n    \"get_free_holders\",\n    \"get_top_ten_free_holder_stats\",\n    \"get_controlling_shareholder\",\n    \"get_holders\",\n    \"get_basic_info\",\n    \"get_url\",\n    \"get_exchange\",\n    \"actor_type_to_org_type\",\n    \"generate_filters\",\n    \"get_em_data\",\n    \"get_kdata\",\n    \"get_basic_info1\",\n    \"get_future_list\",\n    \"get_stock_turnover\",\n    \"get_top_tradable_list\",\n    \"get_top_stocks\",\n    \"get_top_stockhks\",\n    \"get_top_stockuss\",\n    \"get_tradable_list\",\n    \"get_block_stocks\",\n    \"market_code_to_entity_id\",\n    \"get_hot_topic\",\n    \"record_hot_topic\",\n    \"get_news\",\n    \"to_em_fc\",\n    \"to_em_entity_flag\",\n    \"to_em_fq_flag\",\n    \"to_em_level_flag\",\n    \"to_em_sec_id\",\n    \"to_zvt_code\",\n]\n"
  },
  {
    "path": "src/zvt/recorders/em/macro/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule em_treasury_yield_recorder\nfrom .em_treasury_yield_recorder import *\nfrom .em_treasury_yield_recorder import __all__ as _em_treasury_yield_recorder_all\n\n__all__ += _em_treasury_yield_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/em/macro/em_treasury_yield_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import Country\nfrom zvt.domain.macro.monetary import TreasuryYield\nfrom zvt.recorders.em import em_api\n\n\nclass EMTreasuryYieldRecorder(FixedCycleDataRecorder):\n    entity_schema = Country\n    data_schema = TreasuryYield\n    entity_provider = \"wb\"\n    provider = \"em\"\n\n    def __init__(\n        self,\n        force_update=True,\n        sleeping_time=10,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"ignore\",\n        start_timestamp=None,\n        end_timestamp=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        kdata_use_begin_time=False,\n        one_day_trading_minutes=24 * 60,\n        return_unfinished=False,\n    ) -> None:\n        super().__init__(\n            force_update,\n            sleeping_time,\n            None,\n            None,\n            None,\n            None,\n            [\"CN\"],\n            True,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n            level,\n            kdata_use_begin_time,\n            one_day_trading_minutes,\n            return_unfinished,\n        )\n\n    def record(self, entity, start, end, size, timestamps):\n        # record before\n        if start:\n            result = em_api.get_treasury_yield(pn=1, ps=size, fetch_all=False)\n        else:\n            result = em_api.get_treasury_yield(fetch_all=True)\n        if result:\n            df = pd.DataFrame.from_records(result)\n            df_to_db(\n                data_schema=self.data_schema,\n                df=df,\n                provider=self.provider,\n                force_update=True,\n                drop_duplicates=True,\n            )\n\n\nif __name__ == \"__main__\":\n    r = EMTreasuryYieldRecorder()\n    r.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMTreasuryYieldRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule em_index_meta_recorder\nfrom .em_index_meta_recorder import *\nfrom .em_index_meta_recorder import __all__ as _em_index_meta_recorder_all\n\n__all__ += _em_index_meta_recorder_all\n\n# import all from submodule em_future_meta_recorder\nfrom .em_future_meta_recorder import *\nfrom .em_future_meta_recorder import __all__ as _em_future_meta_recorder_all\n\n__all__ += _em_future_meta_recorder_all\n\n# import all from submodule em_indexus_meta_recorder\nfrom .em_indexus_meta_recorder import *\nfrom .em_indexus_meta_recorder import __all__ as _em_indexus_meta_recorder_all\n\n__all__ += _em_indexus_meta_recorder_all\n\n# import all from submodule em_indexhk_meta_recorder\nfrom .em_indexhk_meta_recorder import *\nfrom .em_indexhk_meta_recorder import __all__ as _em_indexhk_meta_recorder_all\n\n__all__ += _em_indexhk_meta_recorder_all\n\n# import all from submodule em_cbond_meta_recorder\nfrom .em_cbond_meta_recorder import *\nfrom .em_cbond_meta_recorder import __all__ as _em_cbond_meta_recorder_all\n\n__all__ += _em_cbond_meta_recorder_all\n\n# import all from submodule em_stockhk_meta_recorder\nfrom .em_stockhk_meta_recorder import *\nfrom .em_stockhk_meta_recorder import __all__ as _em_stockhk_meta_recorder_all\n\n__all__ += _em_stockhk_meta_recorder_all\n\n# import all from submodule em_stockus_meta_recorder\nfrom .em_stockus_meta_recorder import *\nfrom .em_stockus_meta_recorder import __all__ as _em_stockus_meta_recorder_all\n\n__all__ += _em_stockus_meta_recorder_all\n\n# import all from submodule em_stock_meta_recorder\nfrom .em_stock_meta_recorder import *\nfrom .em_stock_meta_recorder import __all__ as _em_stock_meta_recorder_all\n\n__all__ += _em_stock_meta_recorder_all\n\n# import all from submodule em_currency_meta_recorder\nfrom .em_currency_meta_recorder import *\nfrom .em_currency_meta_recorder import __all__ as _em_currency_meta_recorder_all\n\n__all__ += _em_currency_meta_recorder_all\n\n# import all from submodule em_block_meta_recorder\nfrom .em_block_meta_recorder import *\nfrom .em_block_meta_recorder import __all__ as _em_block_meta_recorder_all\n\n__all__ += _em_block_meta_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/em_block_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder, TimeSeriesDataRecorder\nfrom zvt.domain import Block, BlockCategory, BlockStock\nfrom zvt.recorders.em import em_api\n\n\nclass EMBlockRecorder(Recorder):\n    provider = \"em\"\n    data_schema = Block\n\n    def run(self):\n        for block_category in [BlockCategory.concept, BlockCategory.industry]:\n            df = em_api.get_tradable_list(entity_type=\"block\", block_category=block_category, limit=100)\n            self.logger.info(df)\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nclass EMBlockStockRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Block\n\n    provider = \"em\"\n    data_schema = BlockStock\n\n    def record(self, entity, start, end, size, timestamps):\n        the_list = em_api.get_block_stocks(entity.id, entity.name)\n        if the_list:\n            df = pd.DataFrame.from_records(the_list)\n            df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n            self.logger.info(\"finish recording block:{},{}\".format(entity.category, entity.name))\n            self.sleep()\n\n\nif __name__ == \"__main__\":\n    recorder = EMBlockStockRecorder(day_data=True, sleeping_time=0)\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMBlockRecorder\", \"EMBlockStockRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/em_cbond_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain.meta.cbond_meta import CBond\nfrom zvt.recorders.em import em_api\n\n\nclass EMCBondRecorder(Recorder):\n    provider = \"em\"\n    data_schema = CBond\n\n    def run(self):\n        df = em_api.get_tradable_list(entity_type=\"cbond\")\n        self.logger.info(df)\n        df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nif __name__ == \"__main__\":\n    recorder = EMCBondRecorder()\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMCBondRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/em_currency_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain.meta.currency_meta import Currency\nfrom zvt.recorders.em import em_api\n\n\nclass EMCurrencyRecorder(Recorder):\n    provider = \"em\"\n    data_schema = Currency\n\n    def run(self):\n        df = em_api.get_tradable_list(entity_type=\"currency\")\n        self.logger.info(df)\n        df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nif __name__ == \"__main__\":\n    recorder = EMCurrencyRecorder(force_update=True)\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMCurrencyRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/em_future_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain import Future\nfrom zvt.recorders.em import em_api\n\n\nclass EMFutureRecorder(Recorder):\n    provider = \"em\"\n    data_schema = Future\n\n    def run(self):\n        df = em_api.get_tradable_list(entity_type=\"future\")\n        self.logger.info(df)\n        df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nif __name__ == \"__main__\":\n    recorder = EMFutureRecorder(force_update=True)\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMFutureRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/em_index_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain import Index\nfrom zvt.recorders.em import em_api\n\n\nclass EMIndexRecorder(Recorder):\n    provider = \"em\"\n    data_schema = Index\n\n    def run(self):\n        df = em_api.get_tradable_list(entity_type=\"index\", limit=100)\n        self.logger.info(df)\n        df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nif __name__ == \"__main__\":\n    recorder = EMIndexRecorder()\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMIndexRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/em_indexhk_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain.meta.indexhk_meta import Indexhk\nfrom zvt.recorders.em import em_api\n\n\nclass EMIndexhkRecorder(Recorder):\n    provider = \"em\"\n    data_schema = Indexhk\n\n    def run(self):\n        df = em_api.get_tradable_list(entity_type=\"indexhk\")\n        self.logger.info(df)\n        df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nif __name__ == \"__main__\":\n    recorder = EMIndexhkRecorder()\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMIndexhkRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/em_indexus_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain.meta.indexus_meta import Indexus\nfrom zvt.recorders.em import em_api\n\n\nclass EMIndexusRecorder(Recorder):\n    provider = \"em\"\n    data_schema = Indexus\n\n    def run(self):\n        df = em_api.get_tradable_list(entity_type=\"indexus\")\n        self.logger.info(df)\n        df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nif __name__ == \"__main__\":\n    recorder = EMIndexusRecorder()\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMIndexusRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/em_stock_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nfrom zvt.contract import Exchange\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain import Stock\nfrom zvt.recorders.em import em_api\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\nclass EMStockRecorder(Recorder):\n    provider = \"em\"\n    data_schema = Stock\n\n    def run(self):\n        for exchange in [Exchange.sh, Exchange.sz, Exchange.bj]:\n            df = em_api.get_tradable_list(entity_type=\"stock\", exchange=exchange, limit=100)\n\n            if pd_is_not_null(df):\n                df[\"total_cap\"] = df[\"total_cap\"].fillna(0)\n                df[\"float_cap\"] = df[\"float_cap\"].fillna(0)\n\n                self.logger.info(df.head())\n                df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n                for item in df[[\"id\", \"name\", \"total_cap\", \"float_cap\"]].values.tolist():\n                    entity_id = item[0]\n                    datas: List[Stock] = Stock.query_data(\n                        entity_id=entity_id, return_type=\"domain\", session=self.session\n                    )\n\n                    if datas:\n                        entity_domain = datas[0]\n\n                        if \"退\" in entity_domain.name:\n                            self.logger.info(f\"Ignore: {entity_domain.entity_id} {entity_domain.name} {item}\")\n                            continue\n\n                        entity_domain.name = item[1]\n                        entity_domain.total_cap = item[2]\n                        entity_domain.float_cap = item[3]\n\n                        if not entity_domain.list_date:\n                            basic_info = em_api.get_basic_info(entity_id=entity_id, session=self.http_session)\n                            self.logger.info(f\"basic info: {basic_info}\")\n                            if basic_info:\n                                entity_domain.timestamp = to_pd_timestamp(basic_info.get(\"LISTING_DATE\"))\n                                entity_domain.list_date = to_pd_timestamp(basic_info.get(\"LISTING_DATE\"))\n                            else:\n                                entity_domain.name = entity_domain.name + \"退市\"\n                        self.session.add(entity_domain)\n                        self.session.commit()\n\n\nif __name__ == \"__main__\":\n    recorder = EMStockRecorder()\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMStockRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/em_stockhk_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain.meta.stockhk_meta import Stockhk\nfrom zvt.recorders.em import em_api\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\nclass EMStockhkRecorder(Recorder):\n    provider = \"em\"\n    data_schema = Stockhk\n    record_south_only = True\n\n    def run(self):\n        df_south = em_api.get_tradable_list(entity_type=\"stockhk\", hk_south=True)\n\n        if pd_is_not_null(df_south):\n            df_south = df_south.set_index(\"code\", drop=False)\n            df_south[\"total_cap\"] = df_south[\"total_cap\"].fillna(0)\n            df_south[\"float_cap\"] = df_south[\"float_cap\"].fillna(0)\n            df_south[\"south\"] = True\n            df_to_db(df=df_south, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n            if not self.record_south_only:\n                df = em_api.get_tradable_list(entity_type=\"stockhk\")\n\n                if pd_is_not_null(df):\n                    self.logger.info(f\"stockhk count: {len(df)}\")\n                    self.logger.info(df.head())\n\n                    df = df.set_index(\"code\", drop=False)\n                    df[\"total_cap\"] = df[\"total_cap\"].fillna(0)\n                    df[\"float_cap\"] = df[\"float_cap\"].fillna(0)\n\n                    df_other = df.loc[~df.index.isin(df_south.index)].copy()\n                    df_other[\"south\"] = False\n\n                    df_to_db(\n                        df=df_other,\n                        data_schema=self.data_schema,\n                        provider=self.provider,\n                        force_update=self.force_update,\n                    )\n            else:\n                df = df_south\n\n            for item in df[[\"id\", \"name\", \"total_cap\", \"float_cap\"]].values.tolist():\n                entity_id = item[0]\n                datas: List[Stockhk] = Stockhk.query_data(\n                    entity_id=entity_id, return_type=\"domain\", session=self.session\n                )\n\n                if datas:\n                    entity_domain = datas[0]\n\n                    if \"退市\" in entity_domain.name:\n                        self.logger.info(f\"Ignore: {entity_domain.entity_id} {entity_domain.name} {item}\")\n                        continue\n\n                    entity_domain.name = item[1]\n                    entity_domain.total_cap = item[2]\n                    entity_domain.float_cap = item[3]\n\n                    if not entity_domain.list_date or not entity_domain.industry:\n                        basic_info = em_api.get_basic_info(entity_id=entity_id, session=self.http_session)\n                        self.logger.info(f\"basic info: {basic_info}\")\n                        if basic_info:\n                            entity_domain.industry = basic_info.get(\"BELONG_INDUSTRY\")\n                            entity_domain.timestamp = to_pd_timestamp(basic_info.get(\"LISTING_DATE\"))\n                            entity_domain.list_date = to_pd_timestamp(basic_info.get(\"LISTING_DATE\"))\n                        else:\n                            entity_domain.name = entity_domain.name + \"退市\"\n                    self.session.add(entity_domain)\n                    self.session.commit()\n\n\nif __name__ == \"__main__\":\n    recorder = EMStockhkRecorder()\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMStockhkRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/meta/em_stockus_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain.meta.stockus_meta import Stockus\nfrom zvt.recorders.em import em_api\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\nclass EMStockusRecorder(Recorder):\n    provider = \"em\"\n    data_schema = Stockus\n\n    def run(self):\n        df = em_api.get_tradable_list(entity_type=\"stockus\")\n\n        if pd_is_not_null(df):\n            self.logger.info(f\"stockus count: {len(df)}\")\n            self.logger.info(df.head())\n\n            df[\"total_cap\"] = df[\"total_cap\"].fillna(0)\n            df[\"float_cap\"] = df[\"float_cap\"].fillna(0)\n\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n            for item in df[[\"id\", \"name\", \"total_cap\", \"float_cap\"]].values.tolist():\n                entity_id = item[0]\n                datas: List[Stockus] = Stockus.query_data(\n                    entity_id=entity_id, return_type=\"domain\", session=self.session\n                )\n\n                if datas:\n                    entity_domain = datas[0]\n\n                    if \"退市\" in entity_domain.name:\n                        self.logger.info(f\"Ignore: {entity_domain.entity_id} {entity_domain.name} {item}\")\n                        continue\n\n                    entity_domain.name = item[1]\n                    entity_domain.total_cap = item[2]\n                    entity_domain.float_cap = item[3]\n\n                    if not entity_domain.industry or not entity_domain.list_date:\n                        basic_info = em_api.get_basic_info(entity_id=entity_id, session=self.http_session)\n                        self.logger.info(f\"basic info: {basic_info}\")\n                        if basic_info:\n                            entity_domain.industry = basic_info.get(\"BELONG_INDUSTRY\")\n                            entity_domain.timestamp = to_pd_timestamp(basic_info.get(\"LISTING_DATE\"))\n                            entity_domain.list_date = to_pd_timestamp(basic_info.get(\"LISTING_DATE\"))\n                        else:\n                            entity_domain.name = entity_domain.name + \"退市\"\n                    self.session.add(entity_domain)\n                    self.session.commit()\n\n\nif __name__ == \"__main__\":\n    recorder = EMStockusRecorder()\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMStockusRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/misc/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule em_stock_news_recorder\nfrom .em_stock_news_recorder import *\nfrom .em_stock_news_recorder import __all__ as _em_stock_news_recorder_all\n\n__all__ += _em_stock_news_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/em/misc/em_stock_news_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.contract.api import df_to_db, decode_entity_id\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import Stock, Stockus, Stockhk\nfrom zvt.domain.misc.stock_news import StockNews\nfrom zvt.recorders.em import em_api\nfrom zvt.utils.time_utils import count_interval, now_pd_timestamp, recent_year_date\n\n\nclass EMStockNewsRecorder(FixedCycleDataRecorder):\n    original_page_url = \"https://wap.eastmoney.com/quote/stock/0.002572.html\"\n    url = \"https://np-listapi.eastmoney.com/comm/wap/getListInfo?cb=callback&client=wap&type=1&mTypeAndCode=0.002572&pageSize=200&pageIndex={}&callback=jQuery1830017478247906740352_1644568731256&_=1644568879493\"\n\n    entity_schema = Stock\n    data_schema = StockNews\n    entity_provider = \"em\"\n    provider = \"em\"\n\n    def init_entities(self):\n        if self.entity_ids:\n            entity_type, _, _ = decode_entity_id(self.entity_ids[0])\n            if entity_type == \"stockus\":\n                self.entity_schema = Stockus\n            elif entity_type == \"stockhk\":\n                self.entity_schema = Stockhk\n\n        super().init_entities()\n\n    def record(self, entity, start, end, size, timestamps):\n        from_date = recent_year_date()\n        if not start or (start < from_date):\n            start = from_date\n\n        if count_interval(start, now_pd_timestamp()) <= 30:\n            ps = 30\n        else:\n            ps = 200\n\n        latest_news: StockNews = self.get_latest_saved_record(entity=entity)\n\n        news = em_api.get_news(\n            session=self.http_session,\n            entity_id=entity.id,\n            ps=ps,\n            start_timestamp=start,\n            latest_code=latest_news.news_code if latest_news else None,\n        )\n        if news:\n            df = pd.DataFrame.from_records(news)\n            self.logger.info(df)\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nif __name__ == \"__main__\":\n    # df = Stock.query_data(filters=[Stock.exchange == \"bj\"], provider=\"em\")\n    # entity_ids = df[\"entity_id\"].tolist()\n    r = EMStockNewsRecorder(entity_ids=[\"stock_sh_600345\"], sleeping_time=0)\n    r.run()\n\n\n# the __all__ is generated\n__all__ = [\"EMStockNewsRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/em/quotes/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule em_kdata_recorder\nfrom .em_kdata_recorder import *\nfrom .em_kdata_recorder import __all__ as _em_kdata_recorder_all\n\n__all__ += _em_kdata_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/em/quotes/em_kdata_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.api.kdata import get_kdata_schema, get_kdata\nfrom zvt.api.selector import get_entity_ids_by_filter\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import (\n    Stock,\n    Index,\n    Block,\n    StockKdataCommon,\n    IndexKdataCommon,\n    StockhkKdataCommon,\n    StockusKdataCommon,\n    BlockKdataCommon,\n    Indexus,\n    IndexusKdataCommon,\n    Future,\n    FutureKdataCommon,\n    Currency,\n    CurrencyKdataCommon,\n    IndexhkKdataCommon,\n)\nfrom zvt.domain.meta.indexhk_meta import Indexhk\nfrom zvt.domain.meta.stockhk_meta import Stockhk\nfrom zvt.domain.meta.stockus_meta import Stockus\nfrom zvt.recorders.em import em_api\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import count_interval, now_pd_timestamp, current_date\n\n\nclass BaseEMStockKdataRecorder(FixedCycleDataRecorder):\n    default_size = 50000\n    entity_provider: str = \"em\"\n\n    provider = \"em\"\n\n    def __init__(\n        self,\n        force_update=True,\n        sleeping_time=10,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"ignore\",\n        start_timestamp=None,\n        end_timestamp=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        kdata_use_begin_time=False,\n        one_day_trading_minutes=24 * 60,\n        adjust_type=AdjustType.qfq,\n        return_unfinished=False,\n    ) -> None:\n        level = IntervalLevel(level)\n        self.adjust_type = AdjustType(adjust_type)\n        self.entity_type = self.entity_schema.__name__.lower()\n\n        self.data_schema = get_kdata_schema(entity_type=self.entity_type, level=level, adjust_type=self.adjust_type)\n\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            codes,\n            day_data,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n            level,\n            kdata_use_begin_time,\n            one_day_trading_minutes,\n            return_unfinished,\n        )\n\n    def need_redownload_qfq(self, entity_id, df):\n        if self.adjust_type == AdjustType.qfq and pd_is_not_null(df):\n            datas = get_kdata(\n                entity_id=entity_id,\n                provider=self.provider,\n                limit=1,\n                level=self.level,\n                adjust_type=self.adjust_type,\n                order=self.data_schema.timestamp.desc(),\n                return_type=\"domain\",\n            )\n            if datas:\n                latest_kdata = datas[0]\n                check_df = df[df[\"timestamp\"] == latest_kdata.timestamp]\n                if pd_is_not_null(check_df):\n                    old = latest_kdata.close\n                    new = check_df.iloc[0, :][\"close\"]\n                    # 相同时间的close不同，表明前复权需要重新计算\n                    if round(old, 2) != round(new, 2):\n                        # 删掉重新获取\n                        self.session.query(self.data_schema).filter(self.data_schema.entity_id == entity_id).delete()\n                        return True\n                    else:\n                        return False\n                else:\n                    self.logger.warning(f\"no data found for {entity_id} at {latest_kdata.timestamp}, 前复权检查失败\")\n        return False\n\n    def record(self, entity, start, end, size, timestamps):\n        df = em_api.get_kdata(\n            session=self.http_session, entity_id=entity.id, limit=size, adjust_type=self.adjust_type, level=self.level\n        )\n\n        if self.need_redownload_qfq(entity.id, df):\n            df = em_api.get_kdata(\n                session=self.http_session,\n                entity_id=entity.id,\n                limit=self.default_size,\n                adjust_type=self.adjust_type,\n                level=self.level,\n            )\n\n        delisted = False\n        if pd_is_not_null(df):\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n            latest_timestamp = df.iloc[-1, :][\"timestamp\"]\n            days = count_interval(latest_timestamp, now_pd_timestamp())\n            if days > 200:\n                delisted = True\n        else:\n            self.logger.info(f\"no kdata for {entity.id}\")\n\n        if delisted and (\"退市\" not in entity.name):\n            entity.name = entity.name + \"退市\"\n            self.logger.info(f\"set {entity.id} name as {entity.name}\")\n            self.entity_session.add(entity)\n            self.entity_session.commit()\n\n    def on_finish_entity(self, entity):\n        # fill timestamp\n        if not entity.timestamp or not entity.list_date:\n            # get the first\n            kdatas = self.data_schema.query_data(\n                provider=self.provider,\n                entity_id=entity.id,\n                order=self.data_schema.timestamp.asc(),\n                limit=1,\n                return_type=\"domain\",\n            )\n            if kdatas:\n                timestamp = kdatas[0].timestamp\n\n                self.logger.info(f\"fill {entity.name} list_date as {timestamp}\")\n\n                if not entity.timestamp:\n                    entity.timestamp = timestamp\n                if not entity.list_date:\n                    entity.list_date = timestamp\n                self.entity_session.add(entity)\n                self.entity_session.commit()\n\n\nclass EMStockKdataRecorder(BaseEMStockKdataRecorder):\n    entity_schema = Stock\n    data_schema = StockKdataCommon\n    supported_levels = [\n        level for level in IntervalLevel if level not in (IntervalLevel.LEVEL_L2_QUOTE, IntervalLevel.LEVEL_TICK)\n    ]\n    supported_adjust_types = [AdjustType.qfq, AdjustType.hfq]\n\n    def on_finish_entity(self, entity):\n        super().on_finish_entity(entity)\n        # fill holder\n        if not entity.holder_modified_date or (count_interval(entity.holder_modified_date, now_pd_timestamp()) > 30):\n            holder = em_api.get_controlling_shareholder(code=entity.code)\n            if holder:\n                entity.controlling_holder = holder.get(\"holder\")\n                if holder.get(\"parent\"):\n                    entity.controlling_holder_parent = holder.get(\"parent\")\n                else:\n                    entity.controlling_holder_parent = holder.get(\"holder\")\n                entity.holder_modified_date = current_date()\n                self.entity_session.add(entity)\n                self.entity_session.commit()\n            holder_stats = em_api.get_top_ten_free_holder_stats(code=entity.code)\n            if holder_stats:\n                entity.top_ten_ratio = holder_stats.get(\"ratio\")\n                entity.holder_modified_date = current_date()\n                self.entity_session.add(entity)\n                self.entity_session.commit()\n\n\nclass EMStockusKdataRecorder(BaseEMStockKdataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Stockus\n    data_schema = StockusKdataCommon\n    supported_levels = [IntervalLevel.LEVEL_1DAY]\n    supported_adjust_types = [AdjustType.qfq, AdjustType.hfq]\n\n\nclass EMStockhkKdataRecorder(BaseEMStockKdataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Stockhk\n    data_schema = StockhkKdataCommon\n    supported_levels = [IntervalLevel.LEVEL_1DAY]\n    supported_adjust_types = [AdjustType.qfq, AdjustType.hfq]\n\n\nclass EMIndexhkKdataRecorder(BaseEMStockKdataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Indexhk\n    data_schema = IndexhkKdataCommon\n    supported_levels = [IntervalLevel.LEVEL_1DAY]\n\n\nclass EMIndexKdataRecorder(BaseEMStockKdataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Index\n    data_schema = IndexKdataCommon\n    supported_levels = [IntervalLevel.LEVEL_1DAY, IntervalLevel.LEVEL_1WEEK]\n\n\nclass EMIndexusKdataRecorder(BaseEMStockKdataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Indexus\n    data_schema = IndexusKdataCommon\n    supported_levels = [IntervalLevel.LEVEL_1DAY]\n\n\nclass EMBlockKdataRecorder(BaseEMStockKdataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Block\n    data_schema = BlockKdataCommon\n    supported_levels = [IntervalLevel.LEVEL_1DAY, IntervalLevel.LEVEL_1WEEK, IntervalLevel.LEVEL_1MON]\n\n\nclass EMFutureKdataRecorder(BaseEMStockKdataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Future\n    data_schema = FutureKdataCommon\n    supported_levels = [IntervalLevel.LEVEL_1DAY]\n\n\nclass EMCurrencyKdataRecorder(BaseEMStockKdataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Currency\n    data_schema = CurrencyKdataCommon\n    supported_levels = [IntervalLevel.LEVEL_1DAY]\n\n\nif __name__ == \"__main__\":\n    normal_ids = get_entity_ids_by_filter(entity_type=\"indexhk\", provider=\"em\")\n\n    recorder = EMIndexhkKdataRecorder(\n        entity_ids=normal_ids,\n        level=IntervalLevel.LEVEL_1DAY,\n        sleeping_time=2,\n        adjust_type=AdjustType.qfq,\n        day_data=True,\n    )\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\n    \"BaseEMStockKdataRecorder\",\n    \"EMStockKdataRecorder\",\n    \"EMStockusKdataRecorder\",\n    \"EMStockhkKdataRecorder\",\n    \"EMIndexhkKdataRecorder\",\n    \"EMIndexKdataRecorder\",\n    \"EMIndexusKdataRecorder\",\n    \"EMBlockKdataRecorder\",\n    \"EMFutureKdataRecorder\",\n    \"EMCurrencyKdataRecorder\",\n]\n"
  },
  {
    "path": "src/zvt/recorders/em/trading/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule em_dragon_and_tiger_recorder\nfrom .em_dragon_and_tiger_recorder import *\nfrom .em_dragon_and_tiger_recorder import __all__ as _em_dragon_and_tiger_recorder_all\n\n__all__ += _em_dragon_and_tiger_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/em/trading/em_dragon_and_tiger_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport pandas as pd\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain import DragonAndTiger\nfrom zvt.recorders.em import em_api\nfrom zvt.utils.time_utils import to_pd_timestamp, to_date_time_str, TIME_FORMAT_DAY, date_time_by_interval, current_date\n\n\nclass EMDragonAndTigerRecorder(Recorder):\n    provider = \"em\"\n    data_schema = DragonAndTiger\n\n    def run(self):\n        latest_infos = DragonAndTiger.query_data(\n            provider=self.provider, order=DragonAndTiger.timestamp.desc(), limit=1, return_type=\"domain\"\n        )\n        if latest_infos:\n            start_date = latest_infos[0].timestamp\n        else:\n            start_date = date_time_by_interval(current_date(), -10)\n\n        dragon_list = em_api.get_dragon_and_tiger_list(start_date=start_date)\n\n        entity_list = [{\"code\": data[\"SECURITY_CODE\"], \"exchange\": data[\"MARKET\"].lower()} for data in dragon_list]\n        df = pd.DataFrame(entity_list)\n        unique_df = df.drop_duplicates(subset=[\"code\"])\n        unique_list = unique_df.to_dict(\"records\")\n\n        self.logger.info(\"Get dragon and tiger for entities: %s\", unique_list)\n        for entity in unique_list:\n            code = entity[\"code\"]\n            entity_id = f\"stock_{entity['exchange']}_{entity['code']}\"\n\n            datas = em_api.get_dragon_and_tiger(code=entity[\"code\"], start_date=start_date)\n            if datas:\n                records = []\n                for data in datas:\n                    timestamp = to_pd_timestamp(data[\"TRADE_DATE\"])\n                    record = {\n                        \"id\": f\"{entity_id}_{data['TRADE_ID']}_{to_date_time_str(timestamp, fmt=TIME_FORMAT_DAY)}\",\n                        \"entity_id\": entity_id,\n                        \"timestamp\": timestamp,\n                        \"code\": code,\n                        \"name\": data[\"SECURITY_NAME_ABBR\"],\n                        \"reason\": data[\"EXPLANATION\"],\n                        \"turnover\": data[\"ACCUM_AMOUNT\"],\n                        \"change_pct\": data[\"CHANGE_RATE\"],\n                        \"net_in\": data[\"NET_BUY\"],\n                    }\n\n                    # 营业部列表\n                    deps = data[\"LIST\"]\n                    for dep in deps:\n                        flag = \"\" if dep[\"TRADE_DIRECTION\"] == \"0\" else \"_\"\n                        rank = dep[\"RANK\"]\n                        dep_name = f\"dep{flag}{rank}\"\n                        dep_in = f\"{dep_name}_in\"\n                        dep_out = f\"{dep_name}_out\"\n                        dep_rate = f\"{dep_name}_rate\"\n\n                        record[dep_name] = dep[\"OPERATEDEPT_NAME\"]\n                        record[dep_in] = dep[\"BUY_AMT_REAL\"]\n                        record[dep_out] = dep[\"SELL_AMT_REAL\"]\n                        record[dep_rate] = (dep[\"BUY_RATIO\"] if dep[\"BUY_RATIO\"] else 0) - (\n                            dep[\"SELL_RATIO\"] if dep[\"SELL_RATIO\"] else 0\n                        )\n\n                    records.append(record)\n                df = pd.DataFrame.from_records(records)\n                df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n            else:\n                self.logger.info(f\"no data for {entity_id}\")\n\n\nif __name__ == \"__main__\":\n    EMDragonAndTigerRecorder(sleeping_time=2).run()\n\n\n# the __all__ is generated\n__all__ = [\"EMDragonAndTigerRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/exchange/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule api\nfrom .api import *\nfrom .api import __all__ as _api_all\n\n__all__ += _api_all\n\n# import all from submodule exchange_index_recorder\nfrom .exchange_index_recorder import *\nfrom .exchange_index_recorder import __all__ as _exchange_index_recorder_all\n\n__all__ += _exchange_index_recorder_all\n\n# import all from submodule exchange_stock_meta_recorder\nfrom .exchange_stock_meta_recorder import *\nfrom .exchange_stock_meta_recorder import __all__ as _exchange_stock_meta_recorder_all\n\n__all__ += _exchange_stock_meta_recorder_all\n\n# import all from submodule exchange_stock_summary_recorder\nfrom .exchange_stock_summary_recorder import *\nfrom .exchange_stock_summary_recorder import __all__ as _exchange_stock_summary_recorder_all\n\n__all__ += _exchange_stock_summary_recorder_all\n\n# import all from submodule exchange_etf_meta_recorder\nfrom .exchange_etf_meta_recorder import *\nfrom .exchange_etf_meta_recorder import __all__ as _exchange_etf_meta_recorder_all\n\n__all__ += _exchange_etf_meta_recorder_all\n\n# import all from submodule exchange_index_stock_recorder\nfrom .exchange_index_stock_recorder import *\nfrom .exchange_index_stock_recorder import __all__ as _exchange_index_stock_recorder_all\n\n__all__ += _exchange_index_stock_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/exchange/api/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule cs_index_stock_api\nfrom .cs_index_stock_api import *\nfrom .cs_index_stock_api import __all__ as _cs_index_stock_api_all\n\n__all__ += _cs_index_stock_api_all\n\n# import all from submodule cn_index_stock_api\nfrom .cn_index_stock_api import *\nfrom .cn_index_stock_api import __all__ as _cn_index_stock_api_all\n\n__all__ += _cn_index_stock_api_all\n\n# import all from submodule cn_index_api\nfrom .cn_index_api import *\nfrom .cn_index_api import __all__ as _cn_index_api_all\n\n__all__ += _cn_index_api_all\n\n# import all from submodule cs_index_api\nfrom .cs_index_api import *\nfrom .cs_index_api import __all__ as _cs_index_api_all\n\n__all__ += _cs_index_api_all\n"
  },
  {
    "path": "src/zvt/recorders/exchange/api/cn_index_api.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\n\nimport pandas as pd\nimport requests\n\nfrom zvt.domain import IndexCategory\nfrom zvt.recorders.consts import DEFAULT_HEADER\nfrom zvt.utils.time_utils import to_pd_timestamp\n\nlogger = logging.getLogger(__name__)\n\noriginal_page_url = \"http://www.cnindex.com.cn/zh_indices/sese/index.html?act_menu=1&index_type=-1\"\nurl = \"http://www.cnindex.com.cn/index/indexList?channelCode={}&rows=1000&pageNum=1\"\n\n# 中证指数 抓取 风格指数 行业指数 规模指数 基金指数\ncni_category_map_url = {\n    IndexCategory.style: url.format(\"202\"),\n    IndexCategory.industry: url.format(\"201\"),\n    IndexCategory.scope: url.format(\"200\"),\n    IndexCategory.fund: url.format(\"207\"),\n}\n\n# 深证指数 只取规模指数\nsz_category_map_url = {\n    IndexCategory.scope: url.format(\"100\"),\n}\n\n\ndef _get_resp_data(resp: requests.Response):\n    resp.raise_for_status()\n    return resp.json()[\"data\"]\n\n\ndef get_cn_index(index_type=\"cni\", category=IndexCategory.style):\n    if index_type == \"cni\":\n        category_map_url = cni_category_map_url\n    elif index_type == \"sz\":\n        category_map_url = sz_category_map_url\n    else:\n        logger.error(f\"not support index_type: {index_type}\")\n        assert False\n\n    requests_session = requests.Session()\n\n    url = category_map_url.get(category)\n\n    resp = requests_session.get(url, headers=DEFAULT_HEADER)\n\n    results = _get_resp_data(resp)[\"rows\"]\n    # e.g\n    # amount: 277743699997.9\n    # closeingPoint: 6104.7592\n    # docchannel: 1039\n    # freeMarketValue: 10794695531696.15\n    # id: 142\n    # indexcode: \"399370\"\n    # indexename: \"CNI Growth\"\n    # indexfullcname: \"国证1000成长指数\"\n    # indexfullename: \"CNI 1000 Growth Index\"\n    # indexname: \"国证成长\"\n    # indexsource: \"1\"\n    # indextype: \"202\"\n    # pb: 5.34\n    # peDynamic: 29.8607\n    # peStatic: 33.4933\n    # percent: 0.0022\n    # prefixmonth: null\n    # realtimemarket: \"1\"\n    # remark: \"\"\n    # sampleshowdate: null\n    # samplesize: 332\n    # showcnindex: \"1\"\n    # totalMarketValue: 23113641352198.32\n    the_list = []\n\n    logger.info(f\"category: {category} \")\n    logger.info(f\"results: {results} \")\n    for i, result in enumerate(results):\n        logger.info(f\"to {i}/{len(results)}\")\n        code = result[\"indexcode\"]\n        info_resp = requests_session.get(f\"http://www.cnindex.com.cn/index-intro?indexcode={code}\")\n        # fbrq: \"2010-01-04\"\n        # jd: 1000\n        # jr: \"2002-12-31\"\n        # jsfs: \"自由流通市值\"\n        # jsjj: \"国证成长由国证1000指数样本股中成长风格突出的股票组成，为投资者提供更丰富的指数化投资工具。\"\n        # qzsx: null\n        # typl: 2\n        # xyfw: \"沪深A股\"\n        # xygz: \"在国证1000指数样本股中，选取主营业务收入增长率、净利润增长率和净资产收益率综合排名前332只\"\n        index_info = _get_resp_data(info_resp)\n        name = result[\"indexname\"]\n        entity_id = f\"index_sz_{code}\"\n        index_item = {\n            \"id\": entity_id,\n            \"entity_id\": entity_id,\n            \"timestamp\": to_pd_timestamp(index_info[\"jr\"]),\n            \"entity_type\": \"index\",\n            \"exchange\": \"sz\",\n            \"code\": code,\n            \"name\": name,\n            \"category\": category.value,\n            \"list_date\": to_pd_timestamp(index_info[\"fbrq\"]),\n            \"base_point\": index_info[\"jd\"],\n            \"publisher\": \"cnindex\",\n        }\n        logger.info(index_item)\n        the_list.append(index_item)\n        time.sleep(3)\n    if the_list:\n        return pd.DataFrame.from_records(the_list)\n\n\nif __name__ == \"__main__\":\n    df = get_cn_index()\n    print(df)\n\n\n# the __all__ is generated\n__all__ = [\"get_cn_index\"]\n"
  },
  {
    "path": "src/zvt/recorders/exchange/api/cn_index_stock_api.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nimport pandas as pd\nimport requests\n\nfrom zvt.api.utils import china_stock_code_to_id, value_to_pct, value_multiply\nfrom zvt.recorders.consts import DEFAULT_HEADER\nfrom zvt.utils.time_utils import to_pd_timestamp, to_date_time_str, TIME_FORMAT_MON\n\nlogger = logging.getLogger(__name__)\n\noriginal_page_url = \"http://www.cnindex.com.cn/module/index-detail.html?act_menu=1&indexCode=399001\"\nurl = \"http://www.cnindex.com.cn/sample-detail/detail?indexcode={}&dateStr={}&pageNum=1&rows=5000\"\n\n\ndef _get_resp_data(resp: requests.Response):\n    resp.raise_for_status()\n    return resp.json()[\"data\"]\n\n\ndef get_cn_index_stock(code, timestamp, name=None):\n    entity_type = \"index\"\n    exchange = \"sz\"\n    entity_id = f\"{entity_type}_{exchange}_{code}\"\n    data_str = to_date_time_str(timestamp, fmt=TIME_FORMAT_MON)\n    resp = requests.get(url.format(code, data_str), headers=DEFAULT_HEADER)\n    data = _get_resp_data(resp)\n    if not data:\n        return\n    results = _get_resp_data(resp)[\"rows\"]\n\n    the_list = []\n    for result in results:\n        # date: 1614268800000\n        # dateStr: \"2021-02-26\"\n        # freeMarketValue: 10610.8\n        # indexcode: \"399370\"\n        # market: null\n        # seccode: \"600519\"\n        # secname: \"贵州茅台\"\n        # totalMarketValue: 26666.32\n        # trade: \"主要消费\"\n        # weight: 10.01\n        stock_code = result[\"seccode\"]\n        stock_name = result[\"secname\"]\n        stock_id = china_stock_code_to_id(stock_code)\n\n        the_list.append(\n            {\n                \"id\": \"{}_{}_{}\".format(entity_id, result[\"dateStr\"], stock_id),\n                \"entity_id\": entity_id,\n                \"entity_type\": entity_type,\n                \"exchange\": exchange,\n                \"code\": code,\n                \"name\": name,\n                \"timestamp\": to_pd_timestamp(result[\"dateStr\"]),\n                \"stock_id\": stock_id,\n                \"stock_code\": stock_code,\n                \"stock_name\": stock_name,\n                \"proportion\": value_to_pct(result[\"weight\"], 0),\n                \"market_cap\": value_multiply(result[\"freeMarketValue\"], 100000000, 0),\n            }\n        )\n    if the_list:\n        df = pd.DataFrame.from_records(the_list)\n        return df\n\n\nif __name__ == \"__main__\":\n    df = get_cn_index_stock(timestamp=\"2021-08-01\", code=\"399370\", name=\"国证成长\")\n    print(df)\n\n\n# the __all__ is generated\n__all__ = [\"get_cn_index_stock\"]\n"
  },
  {
    "path": "src/zvt/recorders/exchange/api/cs_index_api.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nimport pandas as pd\nimport requests\n\nfrom zvt.domain import IndexCategory\nfrom zvt.recorders.consts import DEFAULT_HEADER\nfrom zvt.utils.time_utils import to_pd_timestamp\n\nlogger = logging.getLogger(__name__)\n\noriginal_page_url = \"https://www.csindex.com.cn/zh-CN#/indices/family/list?index_series=2\"\n\nurl = \"https://www.csindex.com.cn/csindex-home/index-list/query-index-item\"\n\nindex_category_map = {IndexCategory.scope: \"17\", IndexCategory.industry: \"18\", IndexCategory.style: \"19\"}\n\n\ndef _get_resp_data(resp: requests.Response):\n    resp.raise_for_status()\n    return resp.json()[\"data\"]\n\n\ndef _get_params(index_type, category: IndexCategory):\n    if index_type == \"csi\":\n        index_series = [\"1\"]\n    elif index_type == \"sh\":\n        index_series = [\"2\"]\n    else:\n        logger.warning(f\"not support index type: {index_type}\")\n        assert False\n    index_classify = index_category_map.get(category)\n\n    return {\n        \"sorter\": {\"sortField\": \"index_classify\", \"sortOrder\": \"asc\"},\n        \"pager\": {\"pageNum\": 1, \"pageSize\": 10},\n        \"indexFilter\": {\n            \"ifCustomized\": None,\n            \"ifTracked\": None,\n            \"ifWeightCapped\": None,\n            \"indexCompliance\": None,\n            \"hotSpot\": None,\n            \"indexClassify\": [index_classify],\n            \"currency\": None,\n            \"region\": None,\n            \"indexSeries\": index_series,\n            \"undefined\": None,\n        },\n    }\n\n\ndef get_cs_index(index_type=\"sh\"):\n    if index_type == \"csi\":\n        category_list = [IndexCategory.scope, IndexCategory.industry, IndexCategory.style]\n    elif index_type == \"sh\":\n        category_list = [IndexCategory.scope]\n    else:\n        logger.warning(f\"not support index type: {index_type}\")\n        assert False\n\n    requests_session = requests.Session()\n\n    for category in category_list:\n        data = _get_params(index_type=index_type, category=category)\n        print(data)\n        resp = requests_session.post(url, headers=DEFAULT_HEADER, json=data)\n\n        print(resp)\n        results = _get_resp_data(resp)\n        the_list = []\n\n        logger.info(f\"category: {category} \")\n        logger.info(f\"results: {results} \")\n        for i, result in enumerate(results):\n            logger.info(f\"to {i}/{len(results)}\")\n            code = result[\"indexCode\"]\n\n            info_url = f\"https://www.csindex.com.cn/csindex-home/indexInfo/index-basic-info/{code}\"\n            info = _get_resp_data(requests_session.get(info_url))\n\n            name = result[\"indexName\"]\n            entity_id = f\"index_sh_{code}\"\n            index_item = {\n                \"id\": entity_id,\n                \"entity_id\": entity_id,\n                \"timestamp\": to_pd_timestamp(info[\"basicDate\"]),\n                \"entity_type\": \"index\",\n                \"exchange\": \"sh\",\n                \"code\": code,\n                \"name\": name,\n                \"category\": category.value,\n                \"list_date\": to_pd_timestamp(result[\"publishDate\"]),\n                \"base_point\": info[\"basicIndex\"],\n                \"publisher\": \"csindex\",\n            }\n            logger.info(index_item)\n            the_list.append(index_item)\n        if the_list:\n            return pd.DataFrame.from_records(the_list)\n\n\nif __name__ == \"__main__\":\n    df = get_cs_index()\n    print(df)\n\n\n# the __all__ is generated\n__all__ = [\"get_cs_index\"]\n"
  },
  {
    "path": "src/zvt/recorders/exchange/api/cs_index_stock_api.py",
    "content": "# -*- coding: utf-8 -*-\nimport io\nimport logging\n\nimport pandas as pd\nimport requests\n\nfrom zvt.api.utils import china_stock_code_to_id\nfrom zvt.recorders.consts import DEFAULT_HEADER\nfrom zvt.utils.time_utils import now_pd_timestamp\n\nlogger = logging.getLogger(__name__)\n\noriginal_page_url = \"http://www.csindex.com.cn/zh-CN/downloads/indices\"\nurl = \"http://www.csindex.com.cn/uploads/file/autofile/cons/{}cons.xls\"\n\n\ndef get_cs_index_stock(code, timestamp, name=None):\n    entity_type = \"index\"\n    exchange = \"sh\"\n    entity_id = f\"{entity_type}_{exchange}_{code}\"\n\n    response = requests.get(url.format(code), headers=DEFAULT_HEADER)\n    response.raise_for_status()\n\n    df = pd.read_excel(io.BytesIO(response.content))\n\n    df = df[[\"日期Date\", \"成分券代码Constituent Code\", \"成分券名称Constituent Name\"]].rename(\n        columns={\"日期Date\": \"timestamp\", \"成分券代码Constituent Code\": \"stock_code\", \"成分券名称Constituent Name\": \"stock_name\"}\n    )\n\n    df[\"entity_id\"] = entity_id\n    df[\"entity_type\"] = \"index\"\n    df[\"exchange\"] = \"sh\"\n    df[\"code\"] = code\n    df[\"name\"] = name\n    df[\"stock_id\"] = df[\"stock_code\"].apply(lambda x: china_stock_code_to_id(str(x)))\n    # id format: {entity_id}_{timestamp}_{stock_id}\n    df[\"id\"] = df[[\"entity_id\", \"timestamp\", \"stock_id\"]].apply(lambda x: \"_\".join(x.astype(str)), axis=1)\n    df[\"timestamp\"] = pd.to_datetime(df[\"timestamp\"])\n\n    return df\n\n\nif __name__ == \"__main__\":\n    df = get_cs_index_stock(code=\"000001\", name=\"上证指数\", timestamp=now_pd_timestamp())\n    print(df)\n\n\n# the __all__ is generated\n__all__ = [\"get_cs_index_stock\"]\n"
  },
  {
    "path": "src/zvt/recorders/exchange/exchange_etf_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport io\nimport re\n\nimport demjson3\nimport pandas as pd\nimport requests\n\nfrom zvt.api.utils import china_stock_code_to_id\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain import EtfStock, Etf\nfrom zvt.recorders.consts import DEFAULT_SH_ETF_LIST_HEADER\nfrom zvt.utils.time_utils import now_pd_timestamp\n\n\nclass ChinaETFListSpider(Recorder):\n    data_schema = EtfStock\n\n    def __init__(self, force_update=False, sleeping_time=10.0, provider=\"exchange\") -> None:\n        self.provider = provider\n        super().__init__(force_update, sleeping_time)\n\n    def run(self):\n        # 抓取沪市 ETF 列表\n        url = \"http://query.sse.com.cn/commonQuery.do?sqlId=COMMON_SSE_ZQPZ_ETFLB_L_NEW\"\n        response = requests.get(url, headers=DEFAULT_SH_ETF_LIST_HEADER)\n        response_dict = demjson3.decode(response.text)\n\n        df = pd.DataFrame(response_dict.get(\"result\", []))\n        self.persist_etf_list(df, exchange=\"sh\")\n        self.logger.info(\"沪市 ETF 列表抓取完成...\")\n\n        # 抓取沪市 ETF 成分股\n        self.download_sh_etf_component(df)\n        self.logger.info(\"沪市 ETF 成分股抓取完成...\")\n\n        # 抓取深市 ETF 列表\n        url = \"http://www.szse.cn/api/report/ShowReport?SHOWTYPE=xlsx&CATALOGID=1945\"\n        response = requests.get(url)\n\n        df = pd.read_excel(io.BytesIO(response.content), dtype=str)\n        self.persist_etf_list(df, exchange=\"sz\")\n        self.logger.info(\"深市 ETF 列表抓取完成...\")\n\n        # 抓取深市 ETF 成分股\n        self.download_sz_etf_component(df)\n        self.logger.info(\"深市 ETF 成分股抓取完成...\")\n\n    def persist_etf_list(self, df: pd.DataFrame, exchange: str):\n        if df is None:\n            return\n\n        df = df.copy()\n        if exchange == \"sh\":\n            df = df[[\"FUND_ID\", \"FUND_NAME\"]]\n        elif exchange == \"sz\":\n            df = df[[\"证券代码\", \"证券简称\"]]\n\n        df.columns = [\"code\", \"name\"]\n        df[\"id\"] = df[\"code\"].apply(lambda code: f\"etf_{exchange}_{code}\")\n        df[\"entity_id\"] = df[\"id\"]\n        df[\"exchange\"] = exchange\n        df[\"entity_type\"] = \"etf\"\n        df[\"category\"] = \"etf\"\n\n        df = df.dropna(axis=0, how=\"any\")\n        df = df.drop_duplicates(subset=\"id\", keep=\"last\")\n\n        df_to_db(df=df, data_schema=Etf, provider=self.provider, force_update=False)\n\n    def download_sh_etf_component(self, df: pd.DataFrame):\n        query_url = (\n            \"http://query.sse.com.cn/infodisplay/queryConstituentStockInfo.do?\" \"isPagination=false&type={}&etfClass={}\"\n        )\n\n        etf_df = df[(df[\"ETF_CLASS\"] == \"1\") | (df[\"ETF_CLASS\"] == \"2\")]\n        etf_df = self.populate_sh_etf_type(etf_df)\n\n        for _, etf in etf_df.iterrows():\n            url = query_url.format(etf[\"ETF_TYPE\"], etf[\"ETF_CLASS\"])\n            response = requests.get(url, headers=DEFAULT_SH_ETF_LIST_HEADER)\n            response_dict = demjson3.decode(response.text)\n            response_df = pd.DataFrame(response_dict.get(\"result\", []))\n\n            etf_code = etf[\"FUND_ID\"]\n            etf_id = f\"etf_sh_{etf_code}\"\n            response_df = response_df[[\"instrumentId\", \"instrumentName\"]].copy()\n            response_df.rename(columns={\"instrumentId\": \"stock_code\", \"instrumentName\": \"stock_name\"}, inplace=True)\n\n            response_df[\"entity_id\"] = etf_id\n            response_df[\"entity_type\"] = \"etf\"\n            response_df[\"exchange\"] = \"sh\"\n            response_df[\"code\"] = etf_code\n            response_df[\"name\"] = etf[\"FUND_NAME\"]\n            response_df[\"timestamp\"] = now_pd_timestamp()\n\n            response_df[\"stock_id\"] = response_df[\"stock_code\"].apply(lambda code: china_stock_code_to_id(code))\n            response_df[\"id\"] = response_df[\"stock_id\"].apply(lambda x: f\"{etf_id}_{x}\")\n\n            df_to_db(data_schema=self.data_schema, df=response_df, provider=self.provider)\n            self.logger.info(f'{etf[\"FUND_NAME\"]} - {etf_code} 成分股抓取完成...')\n\n            self.sleep()\n\n    def download_sz_etf_component(self, df: pd.DataFrame):\n        query_url = \"http://vip.stock.finance.sina.com.cn/corp/go.php/vII_NewestComponent/indexid/{}.phtml\"\n\n        self.parse_sz_etf_underlying_index(df)\n        for _, etf in df.iterrows():\n            underlying_index = etf[\"拟合指数\"]\n            etf_code = etf[\"证券代码\"]\n\n            if len(underlying_index) == 0:\n                self.logger.info(f'{etf[\"证券简称\"]} - {etf_code} 非 A 股市场指数，跳过...')\n                continue\n\n            url = query_url.format(underlying_index)\n            response = requests.get(url)\n            response.encoding = \"gbk\"\n\n            try:\n                dfs = pd.read_html(response.text, header=1)\n            except ValueError as error:\n                self.logger.error(f\"HTML parse error: {error}, response: {response.text}\")\n                continue\n\n            if len(dfs) < 4:\n                continue\n\n            response_df = dfs[3].copy()\n            response_df = response_df.dropna(axis=1, how=\"any\")\n            response_df[\"品种代码\"] = response_df[\"品种代码\"].apply(lambda x: f\"{x:06d}\")\n\n            etf_id = f\"etf_sz_{etf_code}\"\n            response_df = response_df[[\"品种代码\", \"品种名称\"]].copy()\n            response_df.rename(columns={\"品种代码\": \"stock_code\", \"品种名称\": \"stock_name\"}, inplace=True)\n\n            response_df[\"entity_id\"] = etf_id\n            response_df[\"entity_type\"] = \"etf\"\n            response_df[\"exchange\"] = \"sz\"\n            response_df[\"code\"] = etf_code\n            response_df[\"name\"] = etf[\"证券简称\"]\n            response_df[\"timestamp\"] = now_pd_timestamp()\n\n            response_df[\"stock_id\"] = response_df[\"stock_code\"].apply(lambda code: china_stock_code_to_id(code))\n            response_df[\"id\"] = response_df[\"stock_id\"].apply(lambda x: f\"{etf_id}_{x}\")\n\n            df_to_db(data_schema=self.data_schema, df=response_df, provider=self.provider)\n            self.logger.info(f'{etf[\"证券简称\"]} - {etf_code} 成分股抓取完成...')\n\n            self.sleep()\n\n    @staticmethod\n    def populate_sh_etf_type(df: pd.DataFrame):\n        \"\"\"\n        填充沪市 ETF 代码对应的 TYPE 到列表数据中\n        :param df: ETF 列表数据\n        :return: 包含 ETF 对应 TYPE 的列表数据\n        \"\"\"\n        query_url = (\n            \"http://query.sse.com.cn/infodisplay/queryETFNewAllInfo.do?\"\n            \"isPagination=false&type={}&pageHelp.pageSize=25\"\n        )\n\n        type_df = pd.DataFrame()\n        for etf_class in [1, 2]:\n            url = query_url.format(etf_class)\n            response = requests.get(url, headers=DEFAULT_SH_ETF_LIST_HEADER)\n            response_dict = demjson3.decode(response.text)\n            response_df = pd.DataFrame(response_dict.get(\"result\", []))\n            response_df = response_df[[\"fundid1\", \"etftype\"]]\n\n            type_df = pd.concat([type_df, response_df])\n\n        result_df = df.copy()\n        result_df = result_df.sort_values(by=\"FUND_ID\").reset_index(drop=True)\n        type_df = type_df.sort_values(by=\"fundid1\").reset_index(drop=True)\n\n        result_df[\"ETF_TYPE\"] = type_df[\"etftype\"]\n\n        return result_df\n\n    @staticmethod\n    def parse_sz_etf_underlying_index(df: pd.DataFrame):\n        \"\"\"\n        解析深市 ETF 对应跟踪的指数代码\n        :param df: ETF 列表数据\n        :return: 解析完成 ETF 对应指数代码的列表数据\n        \"\"\"\n\n        def parse_index(text):\n            if len(text) == 0:\n                return \"\"\n\n            result = re.search(r\"(\\d+).*\", text)\n            if result is None:\n                return \"\"\n            else:\n                return result.group(1)\n\n        df[\"拟合指数\"] = df[\"拟合指数\"].apply(parse_index)\n\n\n__all__ = [\"ChinaETFListSpider\"]\n\nif __name__ == \"__main__\":\n    spider = ChinaETFListSpider(provider=\"exchange\")\n    spider.run()\n\n\n# the __all__ is generated\n__all__ = [\"ChinaETFListSpider\"]\n"
  },
  {
    "path": "src/zvt/recorders/exchange/exchange_index_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain import Index\nfrom zvt.recorders.exchange.api import cn_index_api, cs_index_api\n\n\nclass ExchangeIndexRecorder(Recorder):\n    provider = \"exchange\"\n    data_schema = Index\n\n    def run(self):\n        # 深圳\n        self.record_cn_index(\"sz\")\n        # 国证\n        self.record_cn_index(\"cni\")\n\n        # 上海\n        self.record_cs_index(\"sh\")\n        # 中证\n        self.record_cs_index(\"csi\")\n\n    # 中证，上海\n    def record_cs_index(self, index_type):\n        df = cs_index_api.get_cs_index(index_type=index_type)\n        df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n        self.logger.info(f\"finish record {index_type} index\")\n\n    # 国证，深圳\n    def record_cn_index(self, index_type):\n        if index_type == \"cni\":\n            category_map_url = cn_index_api.cni_category_map_url\n        elif index_type == \"sz\":\n            category_map_url = cn_index_api.sz_category_map_url\n        else:\n            self.logger.error(f\"not support index_type: {index_type}\")\n            assert False\n\n        for category, _ in category_map_url.items():\n            df = cn_index_api.get_cn_index(index_type=index_type, category=category)\n            df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n            self.logger.info(f\"finish record {index_type} index:{category.value}\")\n\n\nif __name__ == \"__main__\":\n    # init_log('china_stock_category.log')\n    ExchangeIndexRecorder().run()\n\n\n# the __all__ is generated\n__all__ = [\"ExchangeIndexRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/exchange/exchange_index_stock_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom typing import List\n\nimport pandas as pd\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import TimestampsDataRecorder\nfrom zvt.domain import Index, IndexStock\nfrom zvt.recorders.exchange.api import cs_index_stock_api, cn_index_stock_api\nfrom zvt.utils.time_utils import pre_month_start_date\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\nclass ExchangeIndexStockRecorder(TimestampsDataRecorder):\n    entity_provider = \"exchange\"\n    entity_schema = Index\n\n    provider = \"exchange\"\n    data_schema = IndexStock\n\n    def __init__(\n        self,\n        force_update=False,\n        sleeping_time=5,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"add\",\n        start_timestamp=None,\n        end_timestamp=None,\n        record_history=False,\n    ) -> None:\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            codes,\n            day_data,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n        )\n        self.record_history = record_history\n\n    def init_timestamps(self, entity_item) -> List[pd.Timestamp]:\n        last_valid_date = pre_month_start_date()\n        if self.record_history:\n            # 每个月记录一次\n            return [to_pd_timestamp(item) for item in pd.date_range(entity_item.list_date, last_valid_date, freq=\"M\")]\n        else:\n            return [last_valid_date]\n\n    def record(self, entity, start, end, size, timestamps):\n        if entity.publisher == \"cnindex\":\n            for timestamp in timestamps:\n                df = cn_index_stock_api.get_cn_index_stock(code=entity.code, timestamp=timestamp, name=entity.name)\n                df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n        elif entity.publisher == \"csindex\":\n            # cs index not support history data\n            df = cs_index_stock_api.get_cs_index_stock(code=entity.code, timestamp=None, name=entity.name)\n            df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n\n\nif __name__ == \"__main__\":\n    # ExchangeIndexMetaRecorder().run()\n    ExchangeIndexStockRecorder(codes=[\"399370\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"ExchangeIndexStockRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/exchange/exchange_stock_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport io\n\nimport pandas as pd\nimport requests\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain import Stock, StockDetail\nfrom zvt.recorders.consts import DEFAULT_SH_HEADER, DEFAULT_SZ_HEADER\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\nclass ExchangeStockMetaRecorder(Recorder):\n    data_schema = Stock\n    provider = \"exchange\"\n\n    original_page_url = \"http://www.sse.com.cn/assortment/stock/list/share/\"\n\n    def run(self):\n        url = (\n            \"http://query.sse.com.cn/security/stock/downloadStockListFile.do?csrcCode=&stockCode=&areaName=&stockType=1\"\n        )\n        resp = requests.get(url, headers=DEFAULT_SH_HEADER)\n        self.download_stock_list(response=resp, exchange=\"sh\")\n\n        url = (\n            \"http://query.sse.com.cn/security/stock/downloadStockListFile.do?csrcCode=&stockCode=&areaName=&stockType=8\"\n        )\n        resp = requests.get(url, headers=DEFAULT_SH_HEADER)\n        self.download_stock_list(response=resp, exchange=\"sh\")\n\n        url = \"http://www.szse.cn/api/report/ShowReport?SHOWTYPE=xlsx&CATALOGID=1110&TABKEY=tab1&random=0.20932135244582617\"\n        resp = requests.get(url, headers=DEFAULT_SZ_HEADER)\n        self.download_stock_list(response=resp, exchange=\"sz\")\n\n    def download_stock_list(self, response, exchange):\n        df = None\n        if exchange == \"sh\":\n            df = pd.read_csv(\n                io.BytesIO(response.content),\n                sep=\"\\s+\",\n                encoding=\"GB2312\",\n                dtype=str,\n                parse_dates=[\"上市日期\"],\n                date_format=\"%Y-m-d\",\n                on_bad_lines=\"skip\",\n            )\n            print(df)\n            if df is not None:\n                df = df.loc[:, [\"公司代码\", \"公司简称\", \"上市日期\"]]\n\n        elif exchange == \"sz\":\n            df = pd.read_excel(\n                io.BytesIO(response.content),\n                sheet_name=\"A股列表\",\n                dtype=str,\n                parse_dates=[\"A股上市日期\"],\n                date_format=\"%Y-m-d\",\n            )\n            if df is not None:\n                df = df.loc[:, [\"A股代码\", \"A股简称\", \"A股上市日期\"]]\n\n        if df is not None:\n            df.columns = [\"code\", \"name\", \"list_date\"]\n\n            df = df.dropna(subset=[\"code\"])\n\n            # handle the dirty data\n            # 600996,贵广网络,2016-12-26,2016-12-26,sh,stock,stock_sh_600996,,次新股,贵州,,\n            df.loc[df[\"code\"] == \"600996\", \"list_date\"] = \"2016-12-26\"\n            print(df[df[\"list_date\"] == \"-\"])\n            print(df[\"list_date\"])\n            df[\"list_date\"] = df[\"list_date\"].apply(lambda x: to_pd_timestamp(x))\n            df[\"exchange\"] = exchange\n            df[\"entity_type\"] = \"stock\"\n            df[\"id\"] = df[[\"entity_type\", \"exchange\", \"code\"]].apply(lambda x: \"_\".join(x.astype(str)), axis=1)\n            df[\"entity_id\"] = df[\"id\"]\n            df[\"timestamp\"] = df[\"list_date\"]\n            df = df.dropna(axis=0, how=\"any\")\n            df = df.drop_duplicates(subset=(\"id\"), keep=\"last\")\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=False)\n            # persist StockDetail too\n            df_to_db(df=df, data_schema=StockDetail, provider=self.provider, force_update=False)\n            self.logger.info(df.tail())\n            self.logger.info(\"persist stock list successs\")\n\n\n__all__ = [\"ExchangeStockMetaRecorder\"]\n\nif __name__ == \"__main__\":\n    recorder = ExchangeStockMetaRecorder()\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"ExchangeStockMetaRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/exchange/exchange_stock_summary_recorder.py",
    "content": "import demjson3\nimport pandas as pd\nimport requests\n\nfrom zvt.contract.recorder import TimestampsDataRecorder\nfrom zvt.domain import Index\nfrom zvt.domain.misc import StockSummary\nfrom zvt.recorders.consts import DEFAULT_SH_SUMMARY_HEADER\nfrom zvt.utils.time_utils import to_date_time_str\nfrom zvt.utils.utils import to_float\n\n\nclass ExchangeStockSummaryRecorder(TimestampsDataRecorder):\n    entity_provider = \"exchange\"\n    entity_schema = Index\n\n    provider = \"exchange\"\n    data_schema = StockSummary\n\n    original_page_url = \"http://www.sse.com.cn/market/stockdata/overview/day/\"\n\n    url = \"http://query.sse.com.cn/marketdata/tradedata/queryTradingByProdTypeData.do?jsonCallBack=jsonpCallback30731&searchDate={}&prodType=gp&_=1515717065511\"\n\n    def __init__(\n        self,\n        force_update=False,\n        sleeping_time=5,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"add\",\n        start_timestamp=None,\n        end_timestamp=None,\n    ) -> None:\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            [\"000001\"],\n            day_data,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n        )\n\n    def init_timestamps(self, entity):\n        return pd.date_range(start=entity.timestamp, end=pd.Timestamp.now(), freq=\"B\").tolist()\n\n    def record(self, entity, start, end, size, timestamps):\n        json_results = []\n        for timestamp in timestamps:\n            timestamp_str = to_date_time_str(timestamp)\n            url = self.url.format(timestamp_str)\n            response = requests.get(url=url, headers=DEFAULT_SH_SUMMARY_HEADER)\n\n            results = demjson3.decode(response.text[response.text.index(\"(\") + 1 : response.text.index(\")\")])[\"result\"]\n            result = [result for result in results if result[\"productType\"] == \"1\"]\n            if result and len(result) == 1:\n                result_json = result[0]\n                # 有些较老的数据不存在,默认设为0.0\n                json_results.append(\n                    {\n                        \"provider\": \"exchange\",\n                        \"timestamp\": timestamp,\n                        \"name\": \"上证指数\",\n                        \"pe\": to_float(result_json[\"profitRate\"], 0.0),\n                        \"total_value\": to_float(result_json[\"marketValue1\"] + \"亿\", 0.0),\n                        \"total_tradable_vaule\": to_float(result_json[\"negotiableValue1\"] + \"亿\", 0.0),\n                        \"volume\": to_float(result_json[\"trdVol1\"] + \"万\", 0.0),\n                        \"turnover\": to_float(result_json[\"trdAmt1\"] + \"亿\", 0.0),\n                        \"turnover_rate\": to_float(result_json[\"exchangeRate\"], 0.0),\n                    }\n                )\n\n                if len(json_results) > 30:\n                    return json_results\n\n        return json_results\n\n    def get_data_map(self):\n        return None\n\n\nif __name__ == \"__main__\":\n    ExchangeStockSummaryRecorder().run()\n\n\n# the __all__ is generated\n__all__ = [\"ExchangeStockSummaryRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule quotes\nfrom .quotes import *\nfrom .quotes import __all__ as _quotes_all\n\n__all__ += _quotes_all\n\n# import all from submodule common\nfrom .common import *\nfrom .common import __all__ as _common_all\n\n__all__ += _common_all\n\n# import all from submodule fundamental\nfrom .fundamental import *\nfrom .fundamental import __all__ as _fundamental_all\n\n__all__ += _fundamental_all\n\n# import all from submodule misc\nfrom .misc import *\nfrom .misc import __all__ as _misc_all\n\n__all__ += _misc_all\n\n# import all from submodule meta\nfrom .meta import *\nfrom .meta import __all__ as _meta_all\n\n__all__ += _meta_all\n\n# import all from submodule overall\nfrom .overall import *\nfrom .overall import __all__ as _overall_all\n\n__all__ += _overall_all\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/common.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.contract import IntervalLevel\nfrom zvt.domain import ReportPeriod\n\n\ndef to_jq_trading_level(trading_level: IntervalLevel):\n    if trading_level < IntervalLevel.LEVEL_1HOUR:\n        return trading_level.value\n\n    if trading_level == IntervalLevel.LEVEL_1HOUR:\n        return \"60m\"\n    if trading_level == IntervalLevel.LEVEL_4HOUR:\n        return \"240m\"\n    if trading_level == IntervalLevel.LEVEL_1DAY:\n        return \"1d\"\n    if trading_level == IntervalLevel.LEVEL_1WEEK:\n        return \"1w\"\n    if trading_level == IntervalLevel.LEVEL_1MON:\n        return \"1M\"\n\n\ndef to_jq_entity_id(security_item):\n    if security_item.entity_type == \"stock\" or security_item.entity_type == \"index\":\n        if security_item.exchange == \"sh\":\n            return \"{}.XSHG\".format(security_item.code)\n        if security_item.exchange == \"sz\":\n            return \"{}.XSHE\".format(security_item.code)\n\n\ndef to_entity_id(jq_code: str, entity_type):\n    try:\n        code, exchange = jq_code.split(\".\")\n        if exchange == \"XSHG\":\n            exchange = \"sh\"\n        elif exchange == \"XSHE\":\n            exchange = \"sz\"\n    except:\n        code = jq_code\n        exchange = \"sz\"\n\n    return f\"{entity_type}_{exchange}_{code}\"\n\n\ndef jq_to_report_period(jq_report_type):\n    if jq_report_type == \"第一季度\":\n        return ReportPeriod.season1.value\n    if jq_report_type == \"第二季度\":\n        return ReportPeriod.season2.value\n    if jq_report_type == \"第三季度\":\n        return ReportPeriod.season3.value\n    if jq_report_type == \"第四季度\":\n        return ReportPeriod.season4.value\n    if jq_report_type == \"半年度\":\n        return ReportPeriod.half_year.value\n    if jq_report_type == \"年度\":\n        return ReportPeriod.year.value\n    assert False\n\n\n# the __all__ is generated\n__all__ = [\"to_jq_trading_level\", \"to_jq_entity_id\", \"to_entity_id\", \"jq_to_report_period\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/fundamental/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule jq_margin_trading_recorder\nfrom .jq_margin_trading_recorder import *\nfrom .jq_margin_trading_recorder import __all__ as _jq_margin_trading_recorder_all\n\n__all__ += _jq_margin_trading_recorder_all\n\n# import all from submodule jq_stock_valuation_recorder\nfrom .jq_stock_valuation_recorder import *\nfrom .jq_stock_valuation_recorder import __all__ as _jq_stock_valuation_recorder_all\n\n__all__ += _jq_stock_valuation_recorder_all\n\n# import all from submodule jq_etf_valuation_recorder\nfrom .jq_etf_valuation_recorder import *\nfrom .jq_etf_valuation_recorder import __all__ as _jq_etf_valuation_recorder_all\n\n__all__ += _jq_etf_valuation_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/fundamental/jq_etf_valuation_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.api.portfolio import get_etf_stocks\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import TimeSeriesDataRecorder\nfrom zvt.domain import StockValuation, Etf, EtfValuation\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import now_pd_timestamp\n\n\nclass JqChinaEtfValuationRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Etf\n\n    # 数据来自jq\n    provider = \"joinquant\"\n\n    data_schema = EtfValuation\n\n    def record(self, entity, start, end, size, timestamps):\n        if not end:\n            end = now_pd_timestamp()\n\n        date_range = pd.date_range(start=start, end=end, freq=\"1D\").tolist()\n        for date in date_range:\n            # etf包含的个股和比例\n            etf_stock_df = get_etf_stocks(code=entity.code, timestamp=date, provider=self.provider)\n\n            if pd_is_not_null(etf_stock_df):\n                all_pct = etf_stock_df[\"proportion\"].sum()\n\n                if all_pct >= 1.2 or all_pct <= 0.8:\n                    self.logger.error(f\"ignore etf:{entity.id}  date:{date} proportion sum:{all_pct}\")\n                    break\n\n                etf_stock_df.set_index(\"stock_id\", inplace=True)\n\n                # 个股的估值数据\n                stock_valuation_df = StockValuation.query_data(\n                    entity_ids=etf_stock_df.index.to_list(),\n                    filters=[StockValuation.timestamp == date],\n                    index=\"entity_id\",\n                )\n\n                if pd_is_not_null(stock_valuation_df):\n                    stock_count = len(etf_stock_df)\n                    valuation_count = len(stock_valuation_df)\n\n                    self.logger.info(\n                        f\"etf:{entity.id} date:{date} stock count: {stock_count},\" f\"valuation count:{valuation_count}\"\n                    )\n\n                    pct = abs(stock_count - valuation_count) / stock_count\n\n                    if pct >= 0.2:\n                        self.logger.error(f\"ignore etf:{entity.id}  date:{date} pct:{pct}\")\n                        break\n\n                    se = pd.Series(\n                        {\n                            \"id\": \"{}_{}\".format(entity.id, date),\n                            \"entity_id\": entity.id,\n                            \"timestamp\": date,\n                            \"code\": entity.code,\n                            \"name\": entity.name,\n                        }\n                    )\n                    for col in [\"pe\", \"pe_ttm\", \"pb\", \"ps\", \"pcf\"]:\n                        # PE=P/E\n                        # 这里的算法为：将其价格都设为PE,那么Earning为1(亏钱为-1)，结果为 总价格(PE)/总Earning\n\n                        value = 0\n                        price = 0\n\n                        # 权重估值\n                        positive_df = stock_valuation_df[[col]][stock_valuation_df[col] > 0]\n                        positive_df[\"count\"] = 1\n                        positive_df = positive_df.multiply(etf_stock_df[\"proportion\"], axis=\"index\")\n                        if pd_is_not_null(positive_df):\n                            value = positive_df[\"count\"].sum()\n                            price = positive_df[col].sum()\n\n                        negative_df = stock_valuation_df[[col]][stock_valuation_df[col] < 0]\n                        if pd_is_not_null(negative_df):\n                            negative_df[\"count\"] = 1\n                            negative_df = negative_df.multiply(etf_stock_df[\"proportion\"], axis=\"index\")\n                            value = value - negative_df[\"count\"].sum()\n                            price = price + negative_df[col].sum()\n\n                        se[f\"{col}1\"] = price / value\n\n                        # 简单算术平均估值\n                        positive_df = stock_valuation_df[col][stock_valuation_df[col] > 0]\n                        positive_count = len(positive_df)\n\n                        negative_df = stock_valuation_df[col][stock_valuation_df[col] < 0]\n                        negative_count = len(negative_df)\n\n                        value = positive_count - negative_count\n                        price = positive_df.sum() + abs(negative_df.sum())\n\n                        se[col] = price / value\n                    df = se.to_frame().T\n\n                    self.logger.info(df)\n\n                    df_to_db(\n                        df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update\n                    )\n\n        return None\n\n\nif __name__ == \"__main__\":\n    # 上证50\n    JqChinaEtfValuationRecorder(codes=[\"512290\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"JqChinaEtfValuationRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/fundamental/jq_margin_trading_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport pandas as pd\nfrom jqdatapy.api import get_mtss\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import TimeSeriesDataRecorder\nfrom zvt.domain import Stock, MarginTrading\nfrom zvt.recorders.joinquant.common import to_jq_entity_id\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_date_time_str, TIME_FORMAT_DAY\n\n\nclass MarginTradingRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Stock\n\n    # 数据来自jq\n    provider = \"joinquant\"\n\n    data_schema = MarginTrading\n\n    def record(self, entity, start, end, size, timestamps):\n        df = get_mtss(code=to_jq_entity_id(entity), date=to_date_time_str(start))\n\n        if pd_is_not_null(df):\n            df[\"entity_id\"] = entity.id\n            df[\"code\"] = entity.code\n            df.rename(columns={\"date\": \"timestamp\"}, inplace=True)\n            df[\"timestamp\"] = pd.to_datetime(df[\"timestamp\"])\n            df[\"id\"] = df[[\"entity_id\", \"timestamp\"]].apply(\n                lambda se: \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"], fmt=TIME_FORMAT_DAY)),\n                axis=1,\n            )\n\n            print(df)\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n        return None\n\n\nif __name__ == \"__main__\":\n    MarginTradingRecorder(codes=[\"000004\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"MarginTradingRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/fundamental/jq_stock_valuation_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport pandas as pd\nfrom jqdatapy.api import get_fundamentals\nfrom pandas._libs.tslibs.timedeltas import Timedelta\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import TimeSeriesDataRecorder\nfrom zvt.domain import Stock, StockValuation, Etf\nfrom zvt.recorders.joinquant.common import to_jq_entity_id\nfrom zvt.utils.time_utils import now_pd_timestamp, to_date_time_str, to_pd_timestamp\n\n\nclass JqChinaStockValuationRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Stock\n\n    # 数据来自jq\n    provider = \"joinquant\"\n\n    data_schema = StockValuation\n\n    def record(self, entity, start, end, size, timestamps):\n        start = max(start, to_pd_timestamp(\"2005-01-01\"))\n        end = min(now_pd_timestamp(), start + Timedelta(days=500))\n\n        count: Timedelta = end - start\n\n        # df = get_fundamentals_continuously(q, end_date=now_time_str(), count=count.days + 1, panel=False)\n        df = get_fundamentals(\n            table=\"valuation\", code=to_jq_entity_id(entity), date=to_date_time_str(end), count=min(count.days, 500)\n        )\n        df[\"entity_id\"] = entity.id\n        df[\"timestamp\"] = pd.to_datetime(df[\"day\"])\n        df[\"code\"] = entity.code\n        df[\"name\"] = entity.name\n        df[\"id\"] = df[\"timestamp\"].apply(lambda x: \"{}_{}\".format(entity.id, to_date_time_str(x)))\n        df = df.rename(\n            {\"pe_ratio_lyr\": \"pe\", \"pe_ratio\": \"pe_ttm\", \"pb_ratio\": \"pb\", \"ps_ratio\": \"ps\", \"pcf_ratio\": \"pcf\"},\n            axis=\"columns\",\n        )\n\n        df[\"market_cap\"] = df[\"market_cap\"] * 100000000\n        df[\"circulating_market_cap\"] = df[\"circulating_market_cap\"] * 100000000\n        df[\"capitalization\"] = df[\"capitalization\"] * 10000\n        df[\"circulating_cap\"] = df[\"circulating_cap\"] * 10000\n        df[\"turnover_ratio\"] = df[\"turnover_ratio\"] * 0.01\n        df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n        return None\n\n\nif __name__ == \"__main__\":\n    # 上证50\n    df = Etf.get_stocks(code=\"510050\")\n    stocks = df.stock_id.tolist()\n    print(stocks)\n    print(len(stocks))\n\n    JqChinaStockValuationRecorder(entity_ids=[\"stock_sz_300999\"], force_update=True).run()\n\n\n# the __all__ is generated\n__all__ = [\"JqChinaStockValuationRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/meta/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule jq_fund_meta_recorder\nfrom .jq_fund_meta_recorder import *\nfrom .jq_fund_meta_recorder import __all__ as _jq_fund_meta_recorder_all\n\n__all__ += _jq_fund_meta_recorder_all\n\n# import all from submodule jq_trade_day_recorder\nfrom .jq_trade_day_recorder import *\nfrom .jq_trade_day_recorder import __all__ as _jq_trade_day_recorder_all\n\n__all__ += _jq_trade_day_recorder_all\n\n# import all from submodule jq_stock_meta_recorder\nfrom .jq_stock_meta_recorder import *\nfrom .jq_stock_meta_recorder import __all__ as _jq_stock_meta_recorder_all\n\n__all__ += _jq_stock_meta_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/meta/jq_fund_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\nfrom jqdatapy.api import run_query\n\nfrom zvt.api.portfolio import portfolio_relate_stock\nfrom zvt.api.utils import china_stock_code_to_id\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder, TimeSeriesDataRecorder\nfrom zvt.domain.meta.fund_meta import Fund, FundStock\nfrom zvt.recorders.joinquant.common import to_entity_id, jq_to_report_period\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_date_time_str, date_time_by_interval, now_pd_timestamp, is_same_date\n\n\nclass JqChinaFundRecorder(Recorder):\n    provider = \"joinquant\"\n    data_schema = Fund\n\n    def run(self):\n        # 按不同类别抓取\n        # 编码\t基金运作方式\n        # 401001\t开放式基金\n        # 401002\t封闭式基金\n        # 401003\tQDII\n        # 401004\tFOF\n        # 401005\tETF\n        # 401006\tLOF\n        for operate_mode_id in (401001, 401002, 401005):\n            year_count = 2\n            while True:\n                latest = Fund.query_data(\n                    filters=[Fund.operate_mode_id == operate_mode_id],\n                    order=Fund.timestamp.desc(),\n                    limit=1,\n                    return_type=\"domain\",\n                )\n                start_timestamp = \"2000-01-01\"\n                if latest:\n                    start_timestamp = latest[0].timestamp\n\n                end_timestamp = min(date_time_by_interval(start_timestamp, 365 * year_count), now_pd_timestamp())\n\n                df = run_query(\n                    table=\"finance.FUND_MAIN_INFO\",\n                    conditions=f\"operate_mode_id#=#{operate_mode_id}&start_date#>=#{to_date_time_str(start_timestamp)}&start_date#<=#{to_date_time_str(end_timestamp)}\",\n                    parse_dates=[\"start_date\", \"end_date\"],\n                    dtype={\"main_code\": str},\n                )\n                if not pd_is_not_null(df) or (df[\"start_date\"].max().year < end_timestamp.year):\n                    year_count = year_count + 1\n\n                if pd_is_not_null(df):\n                    df.rename(columns={\"start_date\": \"timestamp\"}, inplace=True)\n                    df[\"timestamp\"] = pd.to_datetime(df[\"timestamp\"])\n                    df[\"list_date\"] = df[\"timestamp\"]\n                    df[\"end_date\"] = pd.to_datetime(df[\"end_date\"])\n\n                    df[\"code\"] = df[\"main_code\"]\n                    df[\"entity_id\"] = df[\"code\"].apply(lambda x: to_entity_id(entity_type=\"fund\", jq_code=x))\n                    df[\"id\"] = df[\"entity_id\"]\n                    df[\"entity_type\"] = \"fund\"\n                    df[\"exchange\"] = \"sz\"\n                    df_to_db(df, data_schema=Fund, provider=self.provider, force_update=self.force_update)\n                    self.logger.info(\n                        f\"persist fund {operate_mode_id} list success {start_timestamp} to {end_timestamp}\"\n                    )\n\n                if is_same_date(end_timestamp, now_pd_timestamp()):\n                    break\n\n\nclass JqChinaFundStockRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Fund\n\n    provider = \"joinquant\"\n    data_schema = FundStock\n\n    def init_entities(self):\n        # 只抓股票型，混合型并且没退市的持仓,\n        self.entities = Fund.query_data(\n            entity_ids=self.entity_ids,\n            codes=self.codes,\n            return_type=\"domain\",\n            provider=self.entity_provider,\n            filters=[Fund.underlying_asset_type.in_((\"股票型\", \"混合型\")), Fund.end_date.is_(None)],\n        )\n\n    def record(self, entity, start, end, size, timestamps):\n        # 忽略退市的\n        if entity.end_date:\n            return None\n        redundant_times = 1\n        while redundant_times > 0:\n            df = run_query(\n                table=\"finance.FUND_PORTFOLIO_STOCK\",\n                conditions=f\"pub_date#>=#{to_date_time_str(start)}&code#=#{entity.code}\",\n                parse_dates=None,\n            )\n            df = df.dropna()\n            if pd_is_not_null(df):\n                # data format\n                #          id    code period_start  period_end    pub_date  report_type_id report_type  rank  symbol  name      shares    market_cap  proportion\n                # 0   8640569  159919   2018-07-01  2018-09-30  2018-10-26          403003        第三季度     1  601318  中国平安  19869239.0  1.361043e+09        7.09\n                # 1   8640570  159919   2018-07-01  2018-09-30  2018-10-26          403003        第三季度     2  600519  贵州茅台    921670.0  6.728191e+08        3.50\n                # 2   8640571  159919   2018-07-01  2018-09-30  2018-10-26          403003        第三季度     3  600036  招商银行  18918815.0  5.806184e+08        3.02\n                # 3   8640572  159919   2018-07-01  2018-09-30  2018-10-26          403003        第三季度     4  601166  兴业银行  22862332.0  3.646542e+08        1.90\n                df[\"timestamp\"] = pd.to_datetime(df[\"pub_date\"])\n\n                df.rename(columns={\"symbol\": \"stock_code\", \"name\": \"stock_name\"}, inplace=True)\n                df[\"proportion\"] = df[\"proportion\"] * 0.01\n\n                df = portfolio_relate_stock(df, entity)\n\n                df[\"stock_id\"] = df[\"stock_code\"].apply(lambda x: china_stock_code_to_id(x))\n                df[\"id\"] = df[[\"entity_id\", \"stock_id\", \"pub_date\", \"id\"]].apply(\n                    lambda x: \"_\".join(x.astype(str)), axis=1\n                )\n                df[\"report_date\"] = pd.to_datetime(df[\"period_end\"])\n                df[\"report_period\"] = df[\"report_type\"].apply(lambda x: jq_to_report_period(x))\n\n                saved = df_to_db(\n                    df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update\n                )\n\n                # 取不到非重复的数据\n                if saved == 0:\n                    return None\n\n                # self.logger.info(df.tail())\n                self.logger.info(\n                    f\"persist fund {entity.code}({entity.name}) portfolio success {df.iloc[-1]['pub_date']}\"\n                )\n                latest = df[\"timestamp\"].max()\n\n                # 取到了最近两年的数据，再请求一次,确保取完最新的数据\n                if latest.year >= now_pd_timestamp().year - 1:\n                    redundant_times = redundant_times - 1\n                start = latest\n            else:\n                return None\n\n        return None\n\n\nif __name__ == \"__main__\":\n    # JqChinaFundRecorder().run()\n    JqChinaFundStockRecorder(codes=[\"000053\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"JqChinaFundRecorder\", \"JqChinaFundStockRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/meta/jq_stock_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\nfrom jqdatapy.api import get_all_securities, run_query\n\nfrom zvt.api.portfolio import portfolio_relate_stock\nfrom zvt.api.utils import china_stock_code_to_id\nfrom zvt.contract.api import df_to_db, get_entity_exchange, get_entity_code\nfrom zvt.contract.recorder import Recorder, TimeSeriesDataRecorder\nfrom zvt.domain import EtfStock, Stock, Etf, StockDetail\nfrom zvt.recorders.joinquant.common import to_entity_id, jq_to_report_period\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_date_time_str\n\n\nclass BaseJqChinaMetaRecorder(Recorder):\n    provider = \"joinquant\"\n\n    def __init__(self, force_update=True, sleeping_time=10) -> None:\n        super().__init__(force_update, sleeping_time)\n\n    def to_zvt_entity(self, df, entity_type, category=None):\n        df = df.set_index(\"code\")\n        df.index.name = \"entity_id\"\n        df = df.reset_index()\n        # 上市日期\n        df.rename(columns={\"start_date\": \"timestamp\"}, inplace=True)\n        df[\"timestamp\"] = pd.to_datetime(df[\"timestamp\"])\n        df[\"list_date\"] = df[\"timestamp\"]\n        df[\"end_date\"] = pd.to_datetime(df[\"end_date\"])\n\n        df[\"entity_id\"] = df[\"entity_id\"].apply(lambda x: to_entity_id(entity_type=entity_type, jq_code=x))\n        df[\"id\"] = df[\"entity_id\"]\n        df[\"entity_type\"] = entity_type\n        df[\"exchange\"] = df[\"entity_id\"].apply(lambda x: get_entity_exchange(x))\n        df[\"code\"] = df[\"entity_id\"].apply(lambda x: get_entity_code(x))\n        df[\"name\"] = df[\"display_name\"]\n\n        if category:\n            df[\"category\"] = category\n\n        return df\n\n\nclass JqChinaStockRecorder(BaseJqChinaMetaRecorder):\n    data_schema = Stock\n\n    def run(self):\n        # 抓取股票列表\n        df_stock = self.to_zvt_entity(get_all_securities(code=\"stock\"), entity_type=\"stock\")\n        df_to_db(df_stock, data_schema=Stock, provider=self.provider, force_update=self.force_update)\n        # persist StockDetail too\n        df_to_db(df=df_stock, data_schema=StockDetail, provider=self.provider, force_update=self.force_update)\n\n        # self.logger.info(df_stock)\n        self.logger.info(\"persist stock list success\")\n\n\nclass JqChinaEtfRecorder(BaseJqChinaMetaRecorder):\n    data_schema = Etf\n\n    def run(self):\n        # 抓取etf列表\n        df_index = self.to_zvt_entity(get_all_securities(code=\"etf\"), entity_type=\"etf\", category=\"etf\")\n        df_to_db(df_index, data_schema=Etf, provider=self.provider, force_update=self.force_update)\n\n        # self.logger.info(df_index)\n        self.logger.info(\"persist etf list success\")\n\n\nclass JqChinaStockEtfPortfolioRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Etf\n\n    # 数据来自jq\n    provider = \"joinquant\"\n\n    data_schema = EtfStock\n\n    def record(self, entity, start, end, size, timestamps):\n        df = run_query(\n            table=\"finance.FUND_PORTFOLIO_STOCK\",\n            conditions=f\"pub_date#>=#{to_date_time_str(start)}&code#=#{entity.code}\",\n            parse_dates=None,\n        )\n        if pd_is_not_null(df):\n            #          id    code period_start  period_end    pub_date  report_type_id report_type  rank  symbol  name      shares    market_cap  proportion\n            # 0   8640569  159919   2018-07-01  2018-09-30  2018-10-26          403003        第三季度     1  601318  中国平安  19869239.0  1.361043e+09        7.09\n            # 1   8640570  159919   2018-07-01  2018-09-30  2018-10-26          403003        第三季度     2  600519  贵州茅台    921670.0  6.728191e+08        3.50\n            # 2   8640571  159919   2018-07-01  2018-09-30  2018-10-26          403003        第三季度     3  600036  招商银行  18918815.0  5.806184e+08        3.02\n            # 3   8640572  159919   2018-07-01  2018-09-30  2018-10-26          403003        第三季度     4  601166  兴业银行  22862332.0  3.646542e+08        1.90\n            df[\"timestamp\"] = pd.to_datetime(df[\"pub_date\"])\n\n            df.rename(columns={\"symbol\": \"stock_code\", \"name\": \"stock_name\"}, inplace=True)\n            df[\"proportion\"] = df[\"proportion\"] * 0.01\n\n            df = portfolio_relate_stock(df, entity)\n\n            df[\"stock_id\"] = df[\"stock_code\"].apply(lambda x: china_stock_code_to_id(x))\n            df[\"id\"] = df[[\"entity_id\", \"stock_id\", \"pub_date\", \"id\"]].apply(lambda x: \"_\".join(x.astype(str)), axis=1)\n            df[\"report_date\"] = pd.to_datetime(df[\"period_end\"])\n            df[\"report_period\"] = df[\"report_type\"].apply(lambda x: jq_to_report_period(x))\n\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n            # self.logger.info(df.tail())\n            self.logger.info(f\"persist etf {entity.code} portfolio success {df.iloc[-1]['pub_date']}\")\n\n        return None\n\n\nif __name__ == \"__main__\":\n    # JqChinaEtfRecorder().run()\n    JqChinaStockEtfPortfolioRecorder(codes=[\"510050\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"BaseJqChinaMetaRecorder\", \"JqChinaStockRecorder\", \"JqChinaEtfRecorder\", \"JqChinaStockEtfPortfolioRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/meta/jq_trade_day_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\nfrom jqdatapy.api import get_trade_days\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import TimeSeriesDataRecorder\nfrom zvt.domain import StockTradeDay, Stock\nfrom zvt.utils.time_utils import to_date_time_str\n\n\nclass StockTradeDayRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Stock\n\n    provider = \"joinquant\"\n    data_schema = StockTradeDay\n\n    def __init__(\n        self,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        day_data=False,\n        force_update=False,\n        sleeping_time=5,\n        real_time=False,\n        fix_duplicate_way=\"add\",\n        start_timestamp=None,\n        end_timestamp=None,\n        entity_filters=None,\n    ) -> None:\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            codes=[\"000001\"],\n            day_data=day_data,\n            entity_filters=entity_filters,\n            ignore_failed=True,\n            real_time=real_time,\n            fix_duplicate_way=fix_duplicate_way,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n        )\n\n    def record(self, entity, start, end, size, timestamps):\n        df = pd.DataFrame()\n        dates = get_trade_days(date=to_date_time_str(start))\n        dates = dates.iloc[:, 0]\n        self.logger.info(f\"add dates:{dates}\")\n        df[\"timestamp\"] = pd.to_datetime(dates)\n        df[\"id\"] = [to_date_time_str(date) for date in dates]\n        df[\"entity_id\"] = \"stock_sz_000001\"\n\n        df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nif __name__ == \"__main__\":\n    r = StockTradeDayRecorder()\n    r.run()\n\n\n# the __all__ is generated\n__all__ = [\"StockTradeDayRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/misc/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule jq_hk_holder_recorder\nfrom .jq_hk_holder_recorder import *\nfrom .jq_hk_holder_recorder import __all__ as _jq_hk_holder_recorder_all\n\n__all__ += _jq_hk_holder_recorder_all\n\n# import all from submodule jq_index_money_flow_recorder\nfrom .jq_index_money_flow_recorder import *\nfrom .jq_index_money_flow_recorder import __all__ as _jq_index_money_flow_recorder_all\n\n__all__ += _jq_index_money_flow_recorder_all\n\n# import all from submodule jq_stock_money_flow_recorder\nfrom .jq_stock_money_flow_recorder import *\nfrom .jq_stock_money_flow_recorder import __all__ as _jq_stock_money_flow_recorder_all\n\n__all__ += _jq_stock_money_flow_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/misc/jq_hk_holder_recorder.py",
    "content": "import pandas as pd\nfrom jqdatapy.api import run_query\n\nfrom zvt.contract.api import df_to_db, get_data\nfrom zvt.contract.recorder import TimestampsDataRecorder\nfrom zvt.domain import Index\nfrom zvt.domain.misc.holder import HkHolder\nfrom zvt.recorders.joinquant.common import to_entity_id\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_date_time_str, TIME_FORMAT_DAY, to_pd_timestamp\n\n\n# 这里选择继承TimestampsDataRecorder是因为\n# 1)时间上就是交易日的列表,这个是可知的，可以以此为增量计算点\n# 2)HkHolder数据结构的设计：\n# 沪股通/深股通 每日 持有 标的(股票)的情况\n# 抓取的角度是entity从Index中获取 沪股通/深股通，然后按 每日 去获取\n\n\nclass JoinquantHkHolderRecorder(TimestampsDataRecorder):\n    entity_provider = \"exchange\"\n    entity_schema = Index\n\n    provider = \"joinquant\"\n    data_schema = HkHolder\n\n    def __init__(\n        self,\n        day_data=False,\n        force_update=False,\n        sleeping_time=5,\n        real_time=False,\n        start_timestamp=None,\n        end_timestamp=None,\n    ) -> None:\n        # 聚宽编码\n        # 市场通编码\t市场通名称\n        # 310001\t沪股通\n        # 310002\t深股通\n        # 310003\t港股通（沪）\n        # 310004\t港股通（深）\n        codes = [\"310001\", \"310002\"]\n\n        super().__init__(\n            force_update,\n            sleeping_time,\n            [\"cn\"],\n            None,\n            codes,\n            day_data,\n            real_time=real_time,\n            fix_duplicate_way=\"ignore\",\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n        )\n\n    def init_timestamps(self, entity):\n        # 聚宽数据从2017年3月17开始\n        return pd.date_range(start=to_pd_timestamp(\"2017-3-17\"), end=pd.Timestamp.now(), freq=\"B\").tolist()\n\n    # 覆盖这个方式是因为，HkHolder里面entity其实是股票，而recorder中entity是 Index类型(沪股通/深股通)\n    def get_latest_saved_record(self, entity):\n        order = eval(\"self.data_schema.{}.desc()\".format(self.get_evaluated_time_field()))\n\n        records = get_data(\n            filters=[HkHolder.holder_code == entity.code],\n            provider=self.provider,\n            data_schema=self.data_schema,\n            order=order,\n            limit=1,\n            return_type=\"domain\",\n            session=self.session,\n        )\n        if records:\n            return records[0]\n        return None\n\n    def record(self, entity, start, end, size, timestamps):\n        for timestamp in timestamps:\n            df = run_query(\n                table=\"finance.STK_HK_HOLD_INFO\",\n                conditions=f\"link_id#=#{entity.code}&day#=#{to_date_time_str(timestamp)}\",\n            )\n            print(df)\n\n            if pd_is_not_null(df):\n                df.rename(\n                    columns={\"day\": \"timestamp\", \"link_id\": \"holder_code\", \"link_name\": \"holder_name\"}, inplace=True\n                )\n                df[\"timestamp\"] = pd.to_datetime(df[\"timestamp\"])\n\n                df[\"entity_id\"] = df[\"code\"].apply(lambda x: to_entity_id(entity_type=\"stock\", jq_code=x))\n                df[\"code\"] = df[\"code\"].apply(lambda x: x.split(\".\")[0])\n\n                # id格式为:{holder_name}_{entity_id}_{timestamp}\n                df[\"id\"] = df[[\"holder_name\", \"entity_id\", \"timestamp\"]].apply(\n                    lambda se: \"{}_{}_{}\".format(\n                        se[\"holder_name\"], se[\"entity_id\"], to_date_time_str(se[\"timestamp\"], fmt=TIME_FORMAT_DAY)\n                    ),\n                    axis=1,\n                )\n\n                df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nif __name__ == \"__main__\":\n    JoinquantHkHolderRecorder(sleeping_time=10).run()\n\n\n# the __all__ is generated\n__all__ = [\"JoinquantHkHolderRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/misc/jq_index_money_flow_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import IndexMoneyFlow, Index, StockMoneyFlow\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_date_time_str\n\n\nclass JoinquantIndexMoneyFlowRecorder(FixedCycleDataRecorder):\n    entity_provider = \"exchange\"\n    entity_schema = Index\n\n    provider = \"joinquant\"\n    data_schema = IndexMoneyFlow\n\n    def __init__(\n        self,\n        force_update=True,\n        sleeping_time=10,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"ignore\",\n        start_timestamp=None,\n        end_timestamp=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        kdata_use_begin_time=False,\n        one_day_trading_minutes=24 * 60,\n        return_unfinished=False,\n    ) -> None:\n        # 上证指数，深证成指，创业板指，科创板\n        support_codes = [\"000001\", \"399001\", \"399006\", \"000688\"]\n        if not codes:\n            codes = support_codes\n        else:\n            codes = list(set(codes) & set(support_codes))\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            codes,\n            day_data,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n            level,\n            kdata_use_begin_time,\n            one_day_trading_minutes,\n            return_unfinished,\n        )\n\n    def record(self, entity, start, end, size, timestamps):\n        # 上证\n        if entity.code == \"000001\":\n            all_df = StockMoneyFlow.query_data(\n                provider=self.provider, start_timestamp=start, filters=[StockMoneyFlow.entity_id.like(\"stock_sh%\")]\n            )\n        # 深证\n        elif entity.code == \"399001\":\n            all_df = StockMoneyFlow.query_data(\n                provider=self.provider, start_timestamp=start, filters=[StockMoneyFlow.entity_id.like(\"stock_sz%\")]\n            )\n        # 创业板\n        elif entity.code == \"399006\":\n            all_df = StockMoneyFlow.query_data(\n                provider=self.provider, start_timestamp=start, filters=[StockMoneyFlow.code.like(\"300%\")]\n            )\n        # 科创板\n        elif entity.code == \"000688\":\n            all_df = StockMoneyFlow.query_data(\n                provider=self.provider, start_timestamp=start, filters=[StockMoneyFlow.code.like(\"688%\")]\n            )\n\n        if pd_is_not_null(all_df):\n            g = all_df.groupby(\"timestamp\")\n            for timestamp, df in g:\n                se = pd.Series(\n                    {\n                        \"id\": \"{}_{}\".format(entity.id, to_date_time_str(timestamp)),\n                        \"entity_id\": entity.id,\n                        \"timestamp\": timestamp,\n                        \"code\": entity.code,\n                        \"name\": entity.name,\n                    }\n                )\n                for col in [\n                    \"net_main_inflows\",\n                    \"net_huge_inflows\",\n                    \"net_big_inflows\",\n                    \"net_medium_inflows\",\n                    \"net_small_inflows\",\n                ]:\n                    se[col] = df[col].sum()\n\n                for col in [\n                    \"net_main_inflow_rate\",\n                    \"net_huge_inflow_rate\",\n                    \"net_big_inflow_rate\",\n                    \"net_medium_inflow_rate\",\n                    \"net_small_inflow_rate\",\n                ]:\n                    se[col] = df[col].sum() / len(df)\n\n                index_df = se.to_frame().T\n\n                self.logger.info(index_df)\n\n                df_to_db(\n                    df=index_df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update\n                )\n\n        return None\n\n\nif __name__ == \"__main__\":\n    JoinquantIndexMoneyFlowRecorder(start_timestamp=\"2020-12-01\").run()\n\n\n# the __all__ is generated\n__all__ = [\"JoinquantIndexMoneyFlowRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/misc/jq_stock_money_flow_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\nfrom jqdatapy import get_token, get_money_flow\n\nfrom zvt import zvt_config\nfrom zvt.api.kdata import generate_kdata_id\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import StockMoneyFlow, Stock\nfrom zvt.recorders.joinquant.common import to_jq_entity_id\nfrom zvt.recorders.joinquant.misc.jq_index_money_flow_recorder import JoinquantIndexMoneyFlowRecorder\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import TIME_FORMAT_DAY, to_date_time_str\n\n\nclass JoinquantStockMoneyFlowRecorder(FixedCycleDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Stock\n\n    provider = \"joinquant\"\n    data_schema = StockMoneyFlow\n\n    def __init__(\n        self,\n        force_update=True,\n        sleeping_time=10,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"ignore\",\n        start_timestamp=None,\n        end_timestamp=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        kdata_use_begin_time=False,\n        one_day_trading_minutes=24 * 60,\n        compute_index_money_flow=False,\n        return_unfinished=False,\n    ) -> None:\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            codes,\n            day_data,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n            level,\n            kdata_use_begin_time,\n            one_day_trading_minutes,\n            return_unfinished,\n        )\n        self.compute_index_money_flow = compute_index_money_flow\n        get_token(zvt_config[\"jq_username\"], zvt_config[\"jq_password\"], force=True)\n\n    def generate_domain_id(self, entity, original_data):\n        return generate_kdata_id(entity_id=entity.id, timestamp=original_data[\"timestamp\"], level=self.level)\n\n    def on_finish(self):\n        # 根据 个股资金流 计算 大盘资金流\n        if self.compute_index_money_flow:\n            JoinquantIndexMoneyFlowRecorder().run()\n\n    def record(self, entity, start, end, size, timestamps):\n        if not self.end_timestamp:\n            df = get_money_flow(code=to_jq_entity_id(entity), date=to_date_time_str(start))\n        else:\n            df = get_money_flow(code=to_jq_entity_id(entity), date=start, end_date=to_date_time_str(self.end_timestamp))\n\n        df = df.dropna()\n\n        if pd_is_not_null(df):\n            df[\"name\"] = entity.name\n            df.rename(\n                columns={\n                    \"date\": \"timestamp\",\n                    \"net_amount_main\": \"net_main_inflows\",\n                    \"net_pct_main\": \"net_main_inflow_rate\",\n                    \"net_amount_xl\": \"net_huge_inflows\",\n                    \"net_pct_xl\": \"net_huge_inflow_rate\",\n                    \"net_amount_l\": \"net_big_inflows\",\n                    \"net_pct_l\": \"net_big_inflow_rate\",\n                    \"net_amount_m\": \"net_medium_inflows\",\n                    \"net_pct_m\": \"net_medium_inflow_rate\",\n                    \"net_amount_s\": \"net_small_inflows\",\n                    \"net_pct_s\": \"net_small_inflow_rate\",\n                },\n                inplace=True,\n            )\n\n            # 转换到标准float\n            inflows_cols = [\n                \"net_main_inflows\",\n                \"net_huge_inflows\",\n                \"net_big_inflows\",\n                \"net_medium_inflows\",\n                \"net_small_inflows\",\n            ]\n            for col in inflows_cols:\n                df[col] = pd.to_numeric(df[col], errors=\"coerce\")\n            df = df.dropna()\n\n            if not pd_is_not_null(df):\n                return None\n\n            df[inflows_cols] = df[inflows_cols].apply(lambda x: x * 10000)\n\n            inflow_rate_cols = [\n                \"net_main_inflow_rate\",\n                \"net_huge_inflow_rate\",\n                \"net_big_inflow_rate\",\n                \"net_medium_inflow_rate\",\n                \"net_small_inflow_rate\",\n            ]\n            for col in inflow_rate_cols:\n                df[col] = pd.to_numeric(df[col], errors=\"coerce\")\n            df = df.dropna()\n            if not pd_is_not_null(df):\n                return None\n\n            df[inflow_rate_cols] = df[inflow_rate_cols].apply(lambda x: x / 100)\n\n            # 计算总流入\n            df[\"net_inflows\"] = (\n                df[\"net_huge_inflows\"] + df[\"net_big_inflows\"] + df[\"net_medium_inflows\"] + df[\"net_small_inflows\"]\n            )\n            # 计算总流入率\n            amount = df[\"net_main_inflows\"] / df[\"net_main_inflow_rate\"]\n            df[\"net_inflow_rate\"] = df[\"net_inflows\"] / amount\n\n            df[\"entity_id\"] = entity.id\n            df[\"timestamp\"] = pd.to_datetime(df[\"timestamp\"])\n            df[\"provider\"] = \"joinquant\"\n            df[\"code\"] = entity.code\n\n            def generate_kdata_id(se):\n                return \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"], fmt=TIME_FORMAT_DAY))\n\n            df[\"id\"] = df[[\"entity_id\", \"timestamp\"]].apply(generate_kdata_id, axis=1)\n\n            df = df.drop_duplicates(subset=\"id\", keep=\"last\")\n\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n        return None\n\n\nif __name__ == \"__main__\":\n    JoinquantStockMoneyFlowRecorder(codes=[\"000578\"]).run()\n\n\n# the __all__ is generated\n__all__ = [\"JoinquantStockMoneyFlowRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/overall/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule jq_margin_trading_recorder\nfrom .jq_margin_trading_recorder import *\nfrom .jq_margin_trading_recorder import __all__ as _jq_margin_trading_recorder_all\n\n__all__ += _jq_margin_trading_recorder_all\n\n# import all from submodule jq_stock_summary_recorder\nfrom .jq_stock_summary_recorder import *\nfrom .jq_stock_summary_recorder import __all__ as _jq_stock_summary_recorder_all\n\n__all__ += _jq_stock_summary_recorder_all\n\n# import all from submodule jq_cross_market_recorder\nfrom .jq_cross_market_recorder import *\nfrom .jq_cross_market_recorder import __all__ as _jq_cross_market_recorder_all\n\n__all__ += _jq_cross_market_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/overall/jq_cross_market_recorder.py",
    "content": "from jqdatapy.api import run_query\n\nfrom zvt.contract.recorder import TimeSeriesDataRecorder\nfrom zvt.domain import Index, CrossMarketSummary\nfrom zvt.utils.time_utils import to_date_time_str\nfrom zvt.utils.utils import multiple_number\n\n\nclass CrossMarketSummaryRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Index\n\n    provider = \"joinquant\"\n    data_schema = CrossMarketSummary\n\n    def __init__(self, force_update=False, sleeping_time=5, real_time=False, fix_duplicate_way=\"add\") -> None:\n\n        # 聚宽编码\n        # 市场通编码\t市场通名称\n        # 310001\t沪股通\n        # 310002\t深股通\n        # 310003\t港股通（沪）\n        # 310004\t港股通（深）\n\n        codes = [\"310001\", \"310002\", \"310003\", \"310004\"]\n        super().__init__(\n            force_update,\n            sleeping_time,\n            [\"cn\"],\n            None,\n            codes=codes,\n            day_data=True,\n            real_time=real_time,\n            fix_duplicate_way=fix_duplicate_way,\n        )\n\n    def init_entities(self):\n        super().init_entities()\n\n    def record(self, entity, start, end, size, timestamps):\n        df = run_query(\n            table=\"finance.STK_ML_QUOTA\", conditions=f\"link_id#=#{entity.code}&day#>=#{to_date_time_str(start)}\"\n        )\n        print(df)\n\n        json_results = []\n\n        for item in df.to_dict(orient=\"records\"):\n            result = {\n                \"provider\": self.provider,\n                \"timestamp\": item[\"day\"],\n                \"name\": entity.name,\n                \"buy_amount\": multiple_number(item[\"buy_amount\"], 100000000),\n                \"buy_volume\": item[\"buy_volume\"],\n                \"sell_amount\": multiple_number(item[\"sell_amount\"], 100000000),\n                \"sell_volume\": item[\"sell_volume\"],\n                \"quota_daily\": multiple_number(item[\"quota_daily\"], 100000000),\n                \"quota_daily_balance\": multiple_number(item[\"quota_daily_balance\"], 100000000),\n            }\n\n            json_results.append(result)\n\n        if len(json_results) < 100:\n            self.one_shot = True\n\n        return json_results\n\n    def get_data_map(self):\n        return None\n\n\nif __name__ == \"__main__\":\n    CrossMarketSummaryRecorder().run()\n\n\n# the __all__ is generated\n__all__ = [\"CrossMarketSummaryRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/overall/jq_margin_trading_recorder.py",
    "content": "from jqdatapy.api import run_query\n\nfrom zvt.contract.recorder import TimeSeriesDataRecorder\nfrom zvt.domain import Index, MarginTradingSummary\nfrom zvt.utils.time_utils import to_date_time_str\n\n# 聚宽编码\n# XSHG-上海证券交易所\n# XSHE-深圳证券交易所\n\ncode_map_jq = {\"000001\": \"XSHG\", \"399106\": \"XSHE\"}\n\n\nclass MarginTradingSummaryRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"exchange\"\n    entity_schema = Index\n\n    provider = \"joinquant\"\n    data_schema = MarginTradingSummary\n\n    def __init__(\n        self,\n        force_update=False,\n        sleeping_time=5,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"add\",\n        start_timestamp=None,\n        end_timestamp=None,\n    ) -> None:\n        # 上海A股,深圳市场\n        codes = [\"000001\", \"399106\"]\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            codes=codes,\n            day_data=day_data,\n            entity_filters=entity_filters,\n            ignore_failed=ignore_failed,\n            real_time=real_time,\n            fix_duplicate_way=fix_duplicate_way,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n        )\n\n    def record(self, entity, start, end, size, timestamps):\n        jq_code = code_map_jq.get(entity.code)\n\n        df = run_query(\n            table=\"finance.STK_MT_TOTAL\",\n            conditions=f\"exchange_code#=#{jq_code}&date#>=#{to_date_time_str(start)}\",\n            parse_dates=[\"date\"],\n        )\n        print(df)\n\n        json_results = []\n\n        for item in df.to_dict(orient=\"records\"):\n            result = {\n                \"provider\": self.provider,\n                \"timestamp\": item[\"date\"],\n                \"name\": entity.name,\n                \"margin_value\": item[\"fin_value\"],\n                \"margin_buy\": item[\"fin_buy_value\"],\n                \"short_value\": item[\"sec_value\"],\n                \"short_volume\": item[\"sec_sell_volume\"],\n                \"total_value\": item[\"fin_sec_value\"],\n            }\n\n            json_results.append(result)\n\n        if len(json_results) < 100:\n            self.one_shot = True\n\n        return json_results\n\n    def get_data_map(self):\n        return None\n\n\nif __name__ == \"__main__\":\n    MarginTradingSummaryRecorder().run()\n\n\n# the __all__ is generated\n__all__ = [\"MarginTradingSummaryRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/overall/jq_stock_summary_recorder.py",
    "content": "from jqdatapy.api import run_query\n\nfrom zvt.contract.recorder import TimeSeriesDataRecorder\nfrom zvt.domain import Index\nfrom zvt.domain import StockSummary\nfrom zvt.utils.time_utils import to_date_time_str\nfrom zvt.utils.utils import multiple_number\n\n# 聚宽编码\n# 322001\t上海市场\n# 322002\t上海A股\n# 322003\t上海B股\n# 322004\t深圳市场\t该市场交易所未公布成交量和成交笔数\n# 322005\t深市主板\n# 322006\t中小企业板\n# 322007\t创业板\n\ncode_map_jq = {\"000001\": \"322002\", \"399106\": \"322004\", \"399001\": \"322005\", \"399005\": \"322006\", \"399006\": \"322007\"}\n\n\nclass StockSummaryRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"exchange\"\n    entity_schema = Index\n\n    provider = \"joinquant\"\n    data_schema = StockSummary\n\n    def __init__(\n        self,\n        force_update=False,\n        sleeping_time=5,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"add\",\n        start_timestamp=None,\n        end_timestamp=None,\n    ) -> None:\n        # 上海A股,深圳市场,深圳成指,中小板,创业板\n        codes = [\"000001\", \"399106\", \"399001\", \"399005\", \"399006\"]\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            codes=codes,\n            day_data=day_data,\n            entity_filters=entity_filters,\n            ignore_failed=ignore_failed,\n            real_time=real_time,\n            fix_duplicate_way=fix_duplicate_way,\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n        )\n\n    def record(self, entity, start, end, size, timestamps):\n        jq_code = code_map_jq.get(entity.code)\n\n        df = run_query(\n            table=\"finance.STK_EXCHANGE_TRADE_INFO\",\n            conditions=f\"exchange_code#=#{jq_code}&date#>=#{to_date_time_str(start)}\",\n            parse_dates=[\"date\"],\n        )\n        print(df)\n\n        json_results = []\n\n        for item in df.to_dict(orient=\"records\"):\n            result = {\n                \"provider\": self.provider,\n                \"timestamp\": item[\"date\"],\n                \"name\": entity.name,\n                \"pe\": item[\"pe_average\"],\n                \"total_value\": multiple_number(item[\"total_market_cap\"], 100000000),\n                \"total_tradable_vaule\": multiple_number(item[\"circulating_market_cap\"], 100000000),\n                \"volume\": multiple_number(item[\"volume\"], 10000),\n                \"turnover\": multiple_number(item[\"money\"], 100000000),\n                \"turnover_rate\": item[\"turnover_ratio\"],\n            }\n\n            json_results.append(result)\n\n        if len(json_results) < 100:\n            self.one_shot = True\n\n        return json_results\n\n    def get_data_map(self):\n        return None\n\n\nif __name__ == \"__main__\":\n    StockSummaryRecorder().run()\n\n\n# the __all__ is generated\n__all__ = [\"StockSummaryRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/quotes/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule jq_index_kdata_recorder\nfrom .jq_index_kdata_recorder import *\nfrom .jq_index_kdata_recorder import __all__ as _jq_index_kdata_recorder_all\n\n__all__ += _jq_index_kdata_recorder_all\n\n# import all from submodule jq_stock_kdata_recorder\nfrom .jq_stock_kdata_recorder import *\nfrom .jq_stock_kdata_recorder import __all__ as _jq_stock_kdata_recorder_all\n\n__all__ += _jq_stock_kdata_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/quotes/jq_index_kdata_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport argparse\n\nimport pandas as pd\nfrom jqdatapy.api import get_token, get_bars\n\nfrom zvt import init_log, zvt_config\nfrom zvt.api.kdata import generate_kdata_id, get_kdata_schema, get_kdata\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import Index, IndexKdataCommon\nfrom zvt.recorders.joinquant.common import to_jq_trading_level, to_jq_entity_id\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_date_time_str, TIME_FORMAT_DAY, TIME_FORMAT_ISO8601\n\n\nclass JqChinaIndexKdataRecorder(FixedCycleDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Index\n\n    # 数据来自jq\n    provider = \"joinquant\"\n\n    # 只是为了把recorder注册到data_schema\n    data_schema = IndexKdataCommon\n    supported_levels = [IntervalLevel.LEVEL_1DAY, IntervalLevel.LEVEL_1WEEK]\n\n    def __init__(\n        self,\n        force_update=True,\n        sleeping_time=10,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"ignore\",\n        start_timestamp=None,\n        end_timestamp=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        kdata_use_begin_time=False,\n        one_day_trading_minutes=24 * 60,\n        return_unfinished=False,\n    ) -> None:\n        level = IntervalLevel(level)\n        self.data_schema = get_kdata_schema(entity_type=\"index\", level=level)\n        self.jq_trading_level = to_jq_trading_level(level)\n        get_token(zvt_config[\"jq_username\"], zvt_config[\"jq_password\"], force=True)\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            codes,\n            day_data,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n            level,\n            kdata_use_begin_time,\n            one_day_trading_minutes,\n            return_unfinished,\n        )\n\n    def init_entities(self):\n        super().init_entities()\n        # ignore no data index\n        self.entities = [\n            entity for entity in self.entities if entity.code not in [\"310001\", \"310002\", \"310003\", \"310004\"]\n        ]\n\n    def generate_domain_id(self, entity, original_data):\n        return generate_kdata_id(entity_id=entity.id, timestamp=original_data[\"timestamp\"], level=self.level)\n\n    def record(self, entity, start, end, size, timestamps):\n        if not self.end_timestamp:\n            df = get_bars(\n                to_jq_entity_id(entity),\n                count=size,\n                unit=self.jq_trading_level,\n                # fields=['date', 'open', 'close', 'low', 'high', 'volume', 'money']\n            )\n        else:\n            end_timestamp = to_date_time_str(self.end_timestamp)\n            df = get_bars(\n                to_jq_entity_id(entity),\n                count=size,\n                unit=self.jq_trading_level,\n                # fields=['date', 'open', 'close', 'low', 'high', 'volume', 'money'],\n                end_date=end_timestamp,\n            )\n        if pd_is_not_null(df):\n            df[\"name\"] = entity.name\n            df.rename(columns={\"money\": \"turnover\", \"date\": \"timestamp\"}, inplace=True)\n\n            df[\"entity_id\"] = entity.id\n            df[\"timestamp\"] = pd.to_datetime(df[\"timestamp\"])\n            df[\"provider\"] = \"joinquant\"\n            df[\"level\"] = self.level.value\n            df[\"code\"] = entity.code\n\n            def generate_kdata_id(se):\n                if self.level >= IntervalLevel.LEVEL_1DAY:\n                    return \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"], fmt=TIME_FORMAT_DAY))\n                else:\n                    return \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"], fmt=TIME_FORMAT_ISO8601))\n\n            df[\"id\"] = df[[\"entity_id\", \"timestamp\"]].apply(generate_kdata_id, axis=1)\n\n            df = df.drop_duplicates(subset=\"id\", keep=\"last\")\n\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n        return None\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--level\", help=\"trading level\", default=\"1d\", choices=[item.value for item in IntervalLevel])\n    parser.add_argument(\"--codes\", help=\"codes\", default=[\"000001\"], nargs=\"+\")\n\n    args = parser.parse_args()\n\n    level = IntervalLevel(args.level)\n    codes = args.codes\n\n    init_log(\"jq_china_stock_{}_kdata.log\".format(args.level))\n    JqChinaIndexKdataRecorder(level=level, sleeping_time=0, codes=codes, real_time=False).run()\n\n    print(get_kdata(entity_id=\"index_sh_000001\", limit=10))\n\n\n# the __all__ is generated\n__all__ = [\"JqChinaIndexKdataRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/joinquant/quotes/jq_stock_kdata_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport pandas as pd\nfrom jqdatapy.api import get_token, get_bars\n\nfrom zvt import zvt_config\nfrom zvt.api.kdata import generate_kdata_id, get_kdata_schema, get_kdata\nfrom zvt.contract import IntervalLevel, AdjustType, AdjustType\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import Stock, StockKdataCommon, Stock1wkHfqKdata\nfrom zvt.recorders.joinquant.common import to_jq_trading_level, to_jq_entity_id\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_date_time_str, now_pd_timestamp, TIME_FORMAT_DAY, TIME_FORMAT_ISO8601\n\n\nclass JqChinaStockKdataRecorder(FixedCycleDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Stock\n\n    # 数据来自jq\n    provider = \"joinquant\"\n\n    # 只是为了把recorder注册到data_schema\n    data_schema = StockKdataCommon\n    supported_levels = [\n        level for level in IntervalLevel if level not in (IntervalLevel.LEVEL_L2_QUOTE, IntervalLevel.LEVEL_TICK)\n    ]\n    supported_adjust_types = [AdjustType.qfq, AdjustType.hfq]\n\n    def __init__(\n        self,\n        force_update=True,\n        sleeping_time=10,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"ignore\",\n        start_timestamp=None,\n        end_timestamp=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        kdata_use_begin_time=False,\n        one_day_trading_minutes=24 * 60,\n        adjust_type=AdjustType.qfq,\n        return_unfinished=False,\n    ) -> None:\n        level = IntervalLevel(level)\n        adjust_type = AdjustType(adjust_type)\n        self.data_schema = get_kdata_schema(entity_type=\"stock\", level=level, adjust_type=adjust_type)\n        self.jq_trading_level = to_jq_trading_level(level)\n\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            codes,\n            day_data,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n            level,\n            kdata_use_begin_time,\n            one_day_trading_minutes,\n            return_unfinished,\n        )\n\n        self.adjust_type = adjust_type\n\n        get_token(zvt_config[\"jq_username\"], zvt_config[\"jq_password\"], force=True)\n\n    def init_entities(self):\n        super().init_entities()\n        # 过滤掉退市的\n        self.entities = [\n            entity for entity in self.entities if (entity.end_date is None) or (entity.end_date > now_pd_timestamp())\n        ]\n\n    def generate_domain_id(self, entity, original_data):\n        return generate_kdata_id(entity_id=entity.id, timestamp=original_data[\"timestamp\"], level=self.level)\n\n    def recompute_qfq(self, entity, qfq_factor, last_timestamp):\n        # 重新计算前复权数据\n        if qfq_factor != 0:\n            kdatas = get_kdata(\n                provider=self.provider,\n                entity_id=entity.id,\n                level=self.level.value,\n                order=self.data_schema.timestamp.asc(),\n                return_type=\"domain\",\n                session=self.session,\n                filters=[self.data_schema.timestamp < last_timestamp],\n            )\n            if kdatas:\n                self.logger.info(\"recomputing {} qfq kdata,factor is:{}\".format(entity.code, qfq_factor))\n                for kdata in kdatas:\n                    kdata.open = round(kdata.open * qfq_factor, 2)\n                    kdata.close = round(kdata.close * qfq_factor, 2)\n                    kdata.high = round(kdata.high * qfq_factor, 2)\n                    kdata.low = round(kdata.low * qfq_factor, 2)\n                self.session.add_all(kdatas)\n                self.session.commit()\n\n    def record(self, entity, start, end, size, timestamps):\n        if self.adjust_type == AdjustType.hfq:\n            fq_ref_date = \"2000-01-01\"\n        else:\n            fq_ref_date = to_date_time_str(now_pd_timestamp())\n\n        if not self.end_timestamp:\n            df = get_bars(\n                to_jq_entity_id(entity),\n                count=size,\n                unit=self.jq_trading_level,\n                # fields=['date', 'open', 'close', 'low', 'high', 'volume', 'money'],\n                fq_ref_date=fq_ref_date,\n            )\n        else:\n            end_timestamp = to_date_time_str(self.end_timestamp)\n            df = get_bars(\n                to_jq_entity_id(entity),\n                count=size,\n                unit=self.jq_trading_level,\n                # fields=['date', 'open', 'close', 'low', 'high', 'volume', 'money'],\n                end_date=end_timestamp,\n                fq_ref_date=fq_ref_date,\n            )\n        if pd_is_not_null(df):\n            df[\"name\"] = entity.name\n            df.rename(columns={\"money\": \"turnover\", \"date\": \"timestamp\"}, inplace=True)\n\n            df[\"entity_id\"] = entity.id\n            df[\"timestamp\"] = pd.to_datetime(df[\"timestamp\"])\n            df[\"provider\"] = \"joinquant\"\n            df[\"level\"] = self.level.value\n            df[\"code\"] = entity.code\n\n            # 判断是否需要重新计算之前保存的前复权数据\n            if self.adjust_type == AdjustType.qfq:\n                check_df = df.head(1)\n                check_date = check_df[\"timestamp\"][0]\n                current_df = get_kdata(\n                    entity_id=entity.id,\n                    provider=self.provider,\n                    start_timestamp=check_date,\n                    end_timestamp=check_date,\n                    limit=1,\n                    level=self.level,\n                    adjust_type=self.adjust_type,\n                )\n                if pd_is_not_null(current_df):\n                    old = current_df.iloc[0, :][\"close\"]\n                    new = check_df[\"close\"][0]\n                    # 相同时间的close不同，表明前复权需要重新计算\n                    if round(old, 2) != round(new, 2):\n                        qfq_factor = new / old\n                        last_timestamp = pd.Timestamp(check_date)\n                        self.recompute_qfq(entity, qfq_factor=qfq_factor, last_timestamp=last_timestamp)\n\n            def generate_kdata_id(se):\n                if self.level >= IntervalLevel.LEVEL_1DAY:\n                    return \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"], fmt=TIME_FORMAT_DAY))\n                else:\n                    return \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"], fmt=TIME_FORMAT_ISO8601))\n\n            df[\"id\"] = df[[\"entity_id\", \"timestamp\"]].apply(generate_kdata_id, axis=1)\n\n            df = df.drop_duplicates(subset=\"id\", keep=\"last\")\n\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n        return None\n\n\nif __name__ == \"__main__\":\n    Stock1wkHfqKdata.record_data(codes=[\"300999\"])\n\n\n# the __all__ is generated\n__all__ = [\"JqChinaStockKdataRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/jqka/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule emotion\nfrom .emotion import *\nfrom .emotion import __all__ as _emotion_all\n\n__all__ += _emotion_all\n\n# import all from submodule jqka_api\nfrom .jqka_api import *\nfrom .jqka_api import __all__ as _jqka_api_all\n\n__all__ += _jqka_api_all\n"
  },
  {
    "path": "src/zvt/recorders/jqka/emotion/JqkaEmotionRecorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport re\nfrom typing import List\n\nimport pandas as pd\n\nfrom zvt.api.utils import china_stock_code_to_id\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import TimestampsDataRecorder\nfrom zvt.domain import Stock\nfrom zvt.domain.emotion.emotion import LimitUpInfo, LimitDownInfo, Emotion\nfrom zvt.recorders.jqka import jqka_api\nfrom zvt.utils.time_utils import to_date_time_str, date_time_by_interval, current_date, to_pd_timestamp\n\n\ndef _get_high_days_count(high_days_str: str):\n    if not high_days_str or (high_days_str == \"首板\"):\n        return 1\n    pattern = r\"\\d+\"\n    result = re.findall(pattern, high_days_str)\n    return int(result[-1])\n\n\nclass JqkaLimitUpRecorder(TimestampsDataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Stock\n\n    provider = \"jqka\"\n    data_schema = LimitUpInfo\n\n    def init_entities(self):\n        # fake entity to for trigger run\n        self.entities = [Stock(id=\"stock_sz_000001\")]\n\n    def init_timestamps(self, entity_item) -> List[pd.Timestamp]:\n        latest_infos = LimitUpInfo.query_data(\n            provider=self.provider, order=LimitUpInfo.timestamp.desc(), limit=1, return_type=\"domain\"\n        )\n        if latest_infos and not self.force_update:\n            start_date = latest_infos[0].timestamp\n        else:\n            # 最近一年的数据\n            start_date = date_time_by_interval(current_date(), -360)\n        return pd.date_range(start=start_date, end=pd.Timestamp.now(), freq=\"B\").tolist()\n\n    def record(self, entity, start, end, size, timestamps):\n        for timestamp in timestamps:\n            the_date = to_date_time_str(timestamp)\n            self.logger.info(f\"record {self.data_schema} to {the_date}\")\n            limit_ups = jqka_api.get_limit_up(date=the_date)\n            if limit_ups:\n                records = []\n                for data in limit_ups:\n                    entity_id = china_stock_code_to_id(code=data[\"code\"])\n\n                    first_limit_up_time = current_date()\n                    try:\n                        first_limit_up_time = pd.Timestamp.fromtimestamp(int(data[\"first_limit_up_time\"]))\n                    except Exception:\n                        pass\n\n                    last_limit_up_time = current_date()\n                    try:\n                        last_limit_up_time = pd.Timestamp.fromtimestamp(int(data[\"last_limit_up_time\"]))\n                    except Exception:\n                        pass\n                    record = {\n                        \"id\": \"{}_{}\".format(entity_id, the_date),\n                        \"entity_id\": entity_id,\n                        \"timestamp\": to_pd_timestamp(the_date),\n                        \"code\": data[\"code\"],\n                        \"name\": data[\"name\"],\n                        \"is_new\": data[\"is_new\"],\n                        \"is_again_limit\": data[\"is_again_limit\"],\n                        \"open_count\": data[\"open_num\"] if data[\"open_num\"] else 0,\n                        \"first_limit_up_time\": first_limit_up_time,\n                        \"last_limit_up_time\": last_limit_up_time,\n                        \"limit_up_type\": data[\"limit_up_type\"],\n                        \"order_amount\": data[\"order_amount\"],\n                        \"success_rate\": data[\"limit_up_suc_rate\"],\n                        \"currency_value\": data[\"currency_value\"],\n                        \"change_pct\": data[\"change_rate\"] / 100,\n                        \"turnover_rate\": data[\"turnover_rate\"] / 100,\n                        \"reason\": data[\"reason_type\"],\n                        \"high_days\": data[\"high_days\"],\n                        \"high_days_count\": _get_high_days_count(data[\"high_days\"]),\n                    }\n                    records.append(record)\n                df = pd.DataFrame.from_records(records)\n                df_to_db(\n                    data_schema=self.data_schema,\n                    df=df,\n                    provider=self.provider,\n                    force_update=True,\n                    drop_duplicates=True,\n                )\n\n\nclass JqkaLimitDownRecorder(TimestampsDataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Stock\n\n    provider = \"jqka\"\n    data_schema = LimitDownInfo\n\n    def init_entities(self):\n        # fake entity to for trigger run\n        self.entities = [Stock(id=\"stock_sz_000001\")]\n\n    def init_timestamps(self, entity_item) -> List[pd.Timestamp]:\n        latest_infos = LimitDownInfo.query_data(\n            provider=self.provider, order=LimitDownInfo.timestamp.desc(), limit=1, return_type=\"domain\"\n        )\n        if latest_infos and not self.force_update:\n            start_date = latest_infos[0].timestamp\n        else:\n            # 最近一年的数据\n            start_date = date_time_by_interval(current_date(), -360)\n        return pd.date_range(start=start_date, end=pd.Timestamp.now(), freq=\"B\").tolist()\n\n    def record(self, entity, start, end, size, timestamps):\n        for timestamp in timestamps:\n            the_date = to_date_time_str(timestamp)\n            self.logger.info(f\"record {self.data_schema} to {the_date}\")\n            limit_downs = jqka_api.get_limit_down(date=the_date)\n            if limit_downs:\n                records = []\n                for idx, data in enumerate(limit_downs):\n                    entity_id = china_stock_code_to_id(code=data[\"code\"])\n                    record = {\n                        \"id\": \"{}_{}\".format(entity_id, the_date),\n                        \"entity_id\": entity_id,\n                        \"timestamp\": to_pd_timestamp(the_date),\n                        \"code\": data[\"code\"],\n                        \"name\": data[\"name\"],\n                        \"is_new\": data[\"is_new\"],\n                        \"is_again_limit\": data[\"is_again_limit\"],\n                        \"currency_value\": data[\"currency_value\"],\n                        \"change_pct\": data[\"change_rate\"] / 100,\n                        \"turnover_rate\": data[\"turnover_rate\"] / 100,\n                    }\n                    records.append(record)\n                df = pd.DataFrame.from_records(records)\n                df_to_db(\n                    data_schema=self.data_schema,\n                    df=df,\n                    provider=self.provider,\n                    force_update=True,\n                    drop_duplicates=True,\n                )\n\n\ndef _cal_power_and_max_height(continuous_limit_up: dict):\n    max_height = 0\n    power = 0\n    for item in continuous_limit_up:\n        if max_height < item[\"height\"]:\n            max_height = item[\"height\"]\n        power = power + item[\"height\"] * item[\"number\"]\n    return max_height, power\n\n\nclass JqkaEmotionRecorder(TimestampsDataRecorder):\n    entity_provider = \"em\"\n    entity_schema = Stock\n\n    provider = \"jqka\"\n    data_schema = Emotion\n\n    def init_entities(self):\n        # fake entity to for trigger run\n        self.entities = [Stock(id=\"stock_sz_000001\")]\n\n    def init_timestamps(self, entity_item) -> List[pd.Timestamp]:\n        latest_infos = Emotion.query_data(\n            provider=self.provider, order=Emotion.timestamp.desc(), limit=1, return_type=\"domain\"\n        )\n        if latest_infos and not self.force_update:\n            start_date = latest_infos[0].timestamp\n        else:\n            # 最近一年的数据\n            start_date = date_time_by_interval(current_date(), -365)\n        return pd.date_range(start=start_date, end=pd.Timestamp.now(), freq=\"B\").tolist()\n\n    def record(self, entity, start, end, size, timestamps):\n        for timestamp in timestamps:\n            the_date = to_date_time_str(timestamp)\n            self.logger.info(f\"record {self.data_schema} to {the_date}\")\n            limit_stats = jqka_api.get_limit_stats(date=the_date)\n            continuous_limit_up = jqka_api.get_continuous_limit_up(date=the_date)\n            max_height, continuous_power = _cal_power_and_max_height(continuous_limit_up=continuous_limit_up)\n\n            if limit_stats:\n                # 大盘\n                entity_id = \"stock_sh_000001\"\n                record = {\n                    \"id\": \"{}_{}\".format(entity_id, the_date),\n                    \"entity_id\": entity_id,\n                    \"timestamp\": to_pd_timestamp(the_date),\n                    \"limit_up_count\": limit_stats[\"limit_up_count\"][\"today\"][\"num\"],\n                    \"limit_up_open_count\": limit_stats[\"limit_up_count\"][\"today\"][\"open_num\"],\n                    \"limit_up_success_rate\": limit_stats[\"limit_up_count\"][\"today\"][\"rate\"],\n                    \"limit_down_count\": limit_stats[\"limit_down_count\"][\"today\"][\"num\"],\n                    \"limit_down_open_count\": limit_stats[\"limit_down_count\"][\"today\"][\"open_num\"],\n                    \"limit_down_success_rate\": limit_stats[\"limit_down_count\"][\"today\"][\"rate\"],\n                    \"max_height\": max_height,\n                    \"continuous_power\": continuous_power,\n                }\n                df = pd.DataFrame.from_records([record])\n                df_to_db(\n                    data_schema=self.data_schema,\n                    df=df,\n                    provider=self.provider,\n                    force_update=True,\n                    drop_duplicates=True,\n                )\n\n\nif __name__ == \"__main__\":\n    # JqkaLimitDownRecorder().run()\n    LimitDownInfo.record_data(start_timestamp=\"2024-02-02\", end_timestamp=\"2024-02-16\", force_update=True)\n\n\n# the __all__ is generated\n__all__ = [\"JqkaLimitUpRecorder\", \"JqkaLimitDownRecorder\", \"JqkaEmotionRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/jqka/emotion/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule JqkaEmotionRecorder\nfrom .JqkaEmotionRecorder import *\nfrom .JqkaEmotionRecorder import __all__ as _JqkaEmotionRecorder_all\n\n__all__ += _JqkaEmotionRecorder_all\n"
  },
  {
    "path": "src/zvt/recorders/jqka/jqka_api.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport requests\n\nfrom zvt.utils.time_utils import now_timestamp_ms, to_date_time_str, TIME_FORMAT_DAY1\nfrom zvt.utils.utils import chrome_copy_header_to_dict\n\n_JKQA_HEADER = chrome_copy_header_to_dict(\n    \"\"\"\nAccept: application/json, text/plain, */*\nAccept-Encoding: gzip, deflate, br\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\nConnection: keep-alive\nHost: data.10jqka.com.cn\nReferer: https://data.10jqka.com.cn/datacenterph/limitup/limtupInfo.html?fontzoom=no&client_userid=cA2fp&share_hxapp=gsc&share_action=webpage_share.1&back_source=wxhy\nsec-ch-ua: \"Not_A Brand\";v=\"99\", \"Google Chrome\";v=\"109\", \"Chromium\";v=\"109\"\nsec-ch-ua-mobile: ?1\nsec-ch-ua-platform: \"Android\"\nSec-Fetch-Dest: empty\nSec-Fetch-Mode: cors\nSec-Fetch-Site: same-origin\nUser-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36\n\"\"\"\n)\n\n\ndef get_continuous_limit_up(date: str):\n    date_str = to_date_time_str(date_time=date, fmt=TIME_FORMAT_DAY1)\n    url = f\"https://data.10jqka.com.cn/dataapi/limit_up/continuous_limit_up?filter=HS,GEM2STAR&date={date_str}\"\n    resp = requests.get(url, headers=_JKQA_HEADER)\n    if resp.status_code == 200:\n        json_result = resp.json()\n        if json_result:\n            return json_result[\"data\"]\n    raise RuntimeError(f\"request jkqa data code: {resp.status_code}, error: {resp.text}\")\n\n\ndef get_limit_stats(date: str):\n    date_str = to_date_time_str(date_time=date, fmt=TIME_FORMAT_DAY1)\n    url = f\"https://data.10jqka.com.cn/dataapi/limit_up/limit_up_pool?page=1&limit=1&field=199112,10,9001,330323,330324,330325,9002,330329,133971,133970,1968584,3475914,9003,9004&filter=HS,GEM2STAR&date={date_str}&order_field=330324&order_type=0&_={now_timestamp_ms()}\"\n    resp = requests.get(url, headers=_JKQA_HEADER)\n    if resp.status_code == 200:\n        json_result = resp.json()\n        if json_result:\n            return {\n                \"limit_up_count\": json_result[\"data\"][\"limit_up_count\"],\n                \"limit_down_count\": json_result[\"data\"][\"limit_down_count\"],\n            }\n    raise RuntimeError(f\"request jkqa data code: {resp.status_code}, error: {resp.text}\")\n\n\ndef get_limit_up(date: str):\n    date_str = to_date_time_str(date_time=date, fmt=TIME_FORMAT_DAY1)\n    url = f\"https://data.10jqka.com.cn/dataapi/limit_up/limit_up_pool?field=199112,10,9001,330323,330324,330325,9002,330329,133971,133970,1968584,3475914,9003,9004&filter=HS,GEM2STAR&order_field=199112&order_type=0&date={date_str}\"\n    return get_jkqa_data(url=url)\n\n\ndef get_limit_down(date: str):\n    date_str = to_date_time_str(date_time=date, fmt=TIME_FORMAT_DAY1)\n    url = f\"https://data.10jqka.com.cn/dataapi/limit_up/lower_limit_pool?field=199112,10,9001,330323,330324,330325,9002,330329,133971,133970,1968584,3475914,9003,9004&filter=HS,GEM2STAR&order_field=199112&order_type=0&date={date_str}\"\n    return get_jkqa_data(url=url)\n\n\ndef get_jkqa_data(url, pn=1, ps=200, fetch_all=True, headers=_JKQA_HEADER):\n    requesting_url = url + f\"&page={pn}&limit={ps}&_={now_timestamp_ms()}\"\n    print(requesting_url)\n    resp = requests.get(requesting_url, headers=headers)\n    if resp.status_code == 200:\n        json_result = resp.json()\n        if json_result and json_result[\"data\"]:\n            data: list = json_result[\"data\"][\"info\"]\n            if fetch_all:\n                if pn < json_result[\"data\"][\"page\"][\"count\"]:\n                    next_data = get_jkqa_data(\n                        pn=pn + 1,\n                        ps=ps,\n                        url=url,\n                        fetch_all=fetch_all,\n                    )\n                    if next_data:\n                        data = data + next_data\n                        if pn == 1 and len(data) != json_result[\"data\"][\"page\"][\"total\"]:\n                            raise RuntimeError(\n                                f\"Assertion failed, the total length of data should be {json_result['data']['page']['total']}, only {len(data)} fetched\"\n                            )\n                        return data\n                    else:\n                        return data\n                else:\n                    return data\n            else:\n                return data\n        return None\n    raise RuntimeError(f\"request jkqa data code: {resp.status_code}, error: {resp.text}\")\n\n\nif __name__ == \"__main__\":\n    # result = get_limit_up(date=\"20210716\")\n    # print(result)\n    # result = get_limit_stats(date=\"20210716\")\n    # print(result)\n    # result = get_limit_down(date=\"20210716\")\n    # print(result)\n    result = get_continuous_limit_up(date=\"20210716\")\n    print(result)\n\n\n# the __all__ is generated\n__all__ = [\"get_continuous_limit_up\", \"get_limit_stats\", \"get_limit_up\", \"get_limit_down\", \"get_jkqa_data\"]\n"
  },
  {
    "path": "src/zvt/recorders/qmt/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule index\nfrom .index import *\nfrom .index import __all__ as _index_all\n\n__all__ += _index_all\n\n# import all from submodule quotes\nfrom .quotes import *\nfrom .quotes import __all__ as _quotes_all\n\n__all__ += _quotes_all\n\n# import all from submodule meta\nfrom .meta import *\nfrom .meta import __all__ as _meta_all\n\n__all__ += _meta_all\n"
  },
  {
    "path": "src/zvt/recorders/qmt/index/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule qmt_index_recorder\nfrom .qmt_index_recorder import *\nfrom .qmt_index_recorder import __all__ as _qmt_index_recorder_all\n\n__all__ += _qmt_index_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/qmt/index/qmt_index_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.api.kdata import get_kdata_schema\nfrom zvt.broker.qmt import qmt_quote\nfrom zvt.consts import IMPORTANT_INDEX\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.contract.utils import evaluate_size_from_timestamp\nfrom zvt.domain import Index, IndexKdataCommon\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import TIME_FORMAT_DAY, TIME_FORMAT_MINUTE, current_date, to_date_time_str\n\n\nclass QmtIndexRecorder(FixedCycleDataRecorder):\n    provider = \"qmt\"\n    # class level kdata schema should always use common\n    data_schema = IndexKdataCommon\n    entity_provider = \"em\"\n    entity_schema = Index\n    download_history_data = False\n\n    def __init__(\n        self,\n        force_update=True,\n        sleeping_time=10,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"ignore\",\n        start_timestamp=None,\n        end_timestamp=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        kdata_use_begin_time=False,\n        one_day_trading_minutes=24 * 60,\n        return_unfinished=False,\n        download_history_data=False,\n    ) -> None:\n        level = IntervalLevel(level)\n        self.entity_type = \"index\"\n        self.download_history_data = download_history_data\n\n        self.data_schema = get_kdata_schema(entity_type=self.entity_type, level=level, adjust_type=None)\n\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            codes,\n            day_data,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n            level,\n            kdata_use_begin_time,\n            one_day_trading_minutes,\n            return_unfinished,\n        )\n        self.one_day_trading_minutes = 240\n\n    def record(self, entity, start, end, size, timestamps):\n        if start and (self.level == IntervalLevel.LEVEL_1DAY):\n            start = start.date()\n        if not start:\n            start = \"2005-01-01\"\n        if not end:\n            end = current_date()\n\n        # 统一高频数据习惯，减小数据更新次数，分钟K线需要直接多读1根K线，以兼容start_timestamp=9:30, end_timestamp=15:00的情况\n        if self.level == IntervalLevel.LEVEL_1MIN:\n            end += pd.Timedelta(seconds=1)\n\n        df = qmt_quote.get_kdata(\n            entity_id=entity.id,\n            start_timestamp=start,\n            end_timestamp=end,\n            adjust_type=None,\n            level=self.level,\n            download_history=self.download_history_data,\n        )\n        time_str_fmt = TIME_FORMAT_DAY if self.level == IntervalLevel.LEVEL_1DAY else TIME_FORMAT_MINUTE\n        if pd_is_not_null(df):\n            df[\"entity_id\"] = entity.id\n            df[\"timestamp\"] = pd.to_datetime(df.index)\n            df[\"id\"] = df.apply(\n                lambda row: f\"{row['entity_id']}_{to_date_time_str(row['timestamp'], fmt=time_str_fmt)}\", axis=1\n            )\n            df[\"provider\"] = \"qmt\"\n            df[\"level\"] = self.level.value\n            df[\"code\"] = entity.code\n            df[\"name\"] = entity.name\n            df.rename(columns={\"amount\": \"turnover\"}, inplace=True)\n            df[\"change_pct\"] = (df[\"close\"] - df[\"preClose\"]) / df[\"preClose\"]\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n        else:\n            self.logger.info(f\"no kdata for {entity.id}\")\n\n    def evaluate_start_end_size_timestamps(self, entity):\n        if self.download_history_data and self.start_timestamp and self.end_timestamp:\n            # 历史数据可能碎片化，允许按照实际start和end之间有没有写满数据\n            expected_size = evaluate_size_from_timestamp(\n                start_timestamp=self.start_timestamp,\n                end_timestamp=self.end_timestamp,\n                level=self.level,\n                one_day_trading_minutes=self.one_day_trading_minutes,\n            )\n\n            recorded_size = (\n                self.session.query(self.data_schema)\n                .filter(\n                    self.data_schema.entity_id == entity.id,\n                    self.data_schema.timestamp >= self.start_timestamp,\n                    self.data_schema.timestamp <= self.end_timestamp,\n                )\n                .count()\n            )\n\n            if expected_size != recorded_size:\n                # print(f\"expected_size: {expected_size}, recorded_size: {recorded_size}\")\n                return self.start_timestamp, self.end_timestamp, self.default_size, None\n\n        start_timestamp, end_timestamp, size, timestamps = super().evaluate_start_end_size_timestamps(entity)\n        # start_timestamp is the last updated timestamp\n        if self.end_timestamp is not None:\n            if start_timestamp >= self.end_timestamp:\n                return start_timestamp, end_timestamp, 0, None\n            else:\n                size = evaluate_size_from_timestamp(\n                    start_timestamp=start_timestamp,\n                    level=self.level,\n                    one_day_trading_minutes=self.one_day_trading_minutes,\n                    end_timestamp=self.end_timestamp,\n                )\n                return start_timestamp, self.end_timestamp, size, timestamps\n\n        return start_timestamp, end_timestamp, size, timestamps\n\n    # # 中证，上海\n    # def record_cs_index(self, index_type):\n    #     df = cs_index_api.get_cs_index(index_type=index_type)\n    #     df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n    #     self.logger.info(f\"finish record {index_type} index\")\n    #\n    # # 国证，深圳\n    # def record_cn_index(self, index_type):\n    #     if index_type == \"cni\":\n    #         category_map_url = cn_index_api.cni_category_map_url\n    #     elif index_type == \"sz\":\n    #         category_map_url = cn_index_api.sz_category_map_url\n    #     else:\n    #         self.logger.error(f\"not support index_type: {index_type}\")\n    #         assert False\n    #\n    #     for category, _ in category_map_url.items():\n    #         df = cn_index_api.get_cn_index(index_type=index_type, category=category)\n    #         df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n    #         self.logger.info(f\"finish record {index_type} index:{category.value}\")\n\n\nif __name__ == \"__main__\":\n    # init_log('china_stock_category.log')\n    start_timestamp = pd.Timestamp(\"2024-12-01\")\n    end_timestamp = pd.Timestamp(\"2024-12-03\")\n    QmtIndexRecorder(\n        codes=IMPORTANT_INDEX,\n        level=IntervalLevel.LEVEL_1MIN,\n        sleeping_time=0,\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        download_history_data=True,\n    ).run()\n\n\n# the __all__ is generated\n__all__ = [\"QmtIndexRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/qmt/meta/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule qmt_stock_meta_recorder\nfrom .qmt_stock_meta_recorder import *\nfrom .qmt_stock_meta_recorder import __all__ as _qmt_stock_meta_recorder_all\n\n__all__ += _qmt_stock_meta_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/qmt/meta/qmt_stock_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.broker.qmt import qmt_quote\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain import Stock\n\n\nclass QMTStockRecorder(Recorder):\n    provider = \"qmt\"\n    data_schema = Stock\n\n    def run(self):\n        df = qmt_quote.get_entity_list()\n        self.logger.info(df.tail())\n        df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=True)\n\n\nif __name__ == \"__main__\":\n    recorder = QMTStockRecorder()\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"QMTStockRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/qmt/quotes/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule qmt_kdata_recorder\nfrom .qmt_kdata_recorder import *\nfrom .qmt_kdata_recorder import __all__ as _qmt_kdata_recorder_all\n\n__all__ += _qmt_kdata_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/qmt/quotes/qmt_kdata_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.api.kdata import get_kdata_schema, get_kdata\nfrom zvt.broker.qmt import qmt_quote\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.contract.api import df_to_db, get_db_session, get_entities\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import (\n    Stock,\n    StockKdataCommon,\n)\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import current_date, to_date_time_str, now_date_time_str\n\n\nclass BaseQmtKdataRecorder(FixedCycleDataRecorder):\n    default_size = 50000\n    entity_provider: str = \"qmt\"\n\n    provider = \"qmt\"\n\n    def __init__(\n        self,\n        force_update=True,\n        sleeping_time=10,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"ignore\",\n        start_timestamp=None,\n        end_timestamp=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        kdata_use_begin_time=False,\n        one_day_trading_minutes=24 * 60,\n        adjust_type=AdjustType.qfq,\n        return_unfinished=False,\n    ) -> None:\n        level = IntervalLevel(level)\n        self.adjust_type = AdjustType(adjust_type)\n        self.entity_type = self.entity_schema.__name__.lower()\n\n        self.data_schema = get_kdata_schema(entity_type=self.entity_type, level=level, adjust_type=self.adjust_type)\n\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            codes,\n            day_data,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n            level,\n            kdata_use_begin_time,\n            one_day_trading_minutes,\n            return_unfinished,\n        )\n\n    def init_entities(self):\n        \"\"\"\n        init the entities which we would record data for\n\n        \"\"\"\n        if self.entity_provider == self.provider and self.entity_schema == self.data_schema:\n            self.entity_session = self.session\n        else:\n            self.entity_session = get_db_session(provider=self.entity_provider, data_schema=self.entity_schema)\n\n        if self.day_data:\n            df = self.data_schema.query_data(\n                start_timestamp=now_date_time_str(), columns=[\"entity_id\", \"timestamp\"], provider=self.provider\n            )\n            if pd_is_not_null(df):\n                entity_ids = df[\"entity_id\"].tolist()\n                self.logger.info(f\"ignore entity_ids:{entity_ids}\")\n                if self.entity_filters:\n                    self.entity_filters.append(self.entity_schema.entity_id.notin_(entity_ids))\n                else:\n                    self.entity_filters = [self.entity_schema.entity_id.notin_(entity_ids)]\n\n        #: init the entity list\n        self.entities = get_entities(\n            session=self.entity_session,\n            entity_schema=self.entity_schema,\n            exchanges=self.exchanges,\n            entity_ids=self.entity_ids,\n            codes=self.codes,\n            return_type=\"domain\",\n            provider=self.entity_provider,\n            filters=self.entity_filters,\n        )\n\n    def record(self, entity, start, end, size, timestamps):\n        if start and (self.level == IntervalLevel.LEVEL_1DAY):\n            start = start.date()\n\n        # 判断是否需要重新计算之前保存的前复权数据\n        if start and (self.adjust_type == AdjustType.qfq):\n            check_df = qmt_quote.get_kdata(\n                entity_id=entity.id,\n                start_timestamp=start,\n                end_timestamp=start,\n                adjust_type=self.adjust_type,\n                level=self.level,\n                download_history=False,\n            )\n            if pd_is_not_null(check_df):\n                current_df = get_kdata(\n                    entity_id=entity.id,\n                    provider=self.provider,\n                    start_timestamp=start,\n                    end_timestamp=start,\n                    limit=1,\n                    level=self.level,\n                    adjust_type=self.adjust_type,\n                )\n                if pd_is_not_null(current_df):\n                    old = current_df[\"close\"].iloc[0]\n                    new = check_df[\"close\"].iloc[0]\n                    # 相同时间的close不同，表明前复权需要重新计算\n                    if round(old, 2) != round(new, 2):\n                        # 删掉重新获取\n                        self.session.query(self.data_schema).filter(self.data_schema.entity_id == entity.id).delete()\n                        start = \"2005-01-01\"\n                else:\n                    self.logger.warning(\"前复权检查失败，无法获取存储的最新数据\")\n\n        if not start:\n            start = \"2005-01-01\"\n        if not end:\n            end = current_date()\n\n        df = qmt_quote.get_kdata(\n            entity_id=entity.id,\n            start_timestamp=start,\n            end_timestamp=end,\n            adjust_type=self.adjust_type,\n            level=self.level,\n            download_history=False,\n        )\n        if pd_is_not_null(df):\n            df[\"entity_id\"] = entity.id\n            df[\"timestamp\"] = pd.to_datetime(df.index)\n            df[\"id\"] = df.apply(lambda row: f\"{row['entity_id']}_{to_date_time_str(row['timestamp'])}\", axis=1)\n            df[\"provider\"] = \"qmt\"\n            df[\"level\"] = self.level.value\n            df[\"code\"] = entity.code\n            df[\"name\"] = entity.name\n            df.rename(columns={\"amount\": \"turnover\"}, inplace=True)\n            df[\"change_pct\"] = (df[\"close\"] - df[\"preClose\"]) / df[\"preClose\"]\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n        else:\n            self.logger.info(f\"no kdata for {entity.id}\")\n\n\nclass QMTStockKdataRecorder(BaseQmtKdataRecorder):\n    entity_schema = Stock\n    data_schema = StockKdataCommon\n    supported_levels = [IntervalLevel.LEVEL_1DAY]\n    supported_adjust_types = [AdjustType.qfq, AdjustType.hfq]\n\n\nif __name__ == \"__main__\":\n    # Stock.record_data(provider=\"qmt\")\n    QMTStockKdataRecorder(entity_id=\"stock_sz_301611\", adjust_type=AdjustType.qfq).run()\n\n\n# the __all__ is generated\n__all__ = [\"BaseQmtKdataRecorder\", \"QMTStockKdataRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/sina/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule money_flow\nfrom .money_flow import *\nfrom .money_flow import __all__ as _money_flow_all\n\n__all__ += _money_flow_all\n\n# import all from submodule quotes\nfrom .quotes import *\nfrom .quotes import __all__ as _quotes_all\n\n__all__ += _quotes_all\n\n# import all from submodule meta\nfrom .meta import *\nfrom .meta import __all__ as _meta_all\n\n__all__ += _meta_all\n"
  },
  {
    "path": "src/zvt/recorders/sina/meta/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule sina_block_recorder\nfrom .sina_block_recorder import *\nfrom .sina_block_recorder import __all__ as _sina_block_recorder_all\n\n__all__ += _sina_block_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/sina/meta/sina_block_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport json\n\nimport demjson3\nimport pandas as pd\nimport requests\n\nfrom zvt.api.utils import china_stock_code_to_id\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder, TimeSeriesDataRecorder\nfrom zvt.domain import BlockStock, BlockCategory, Block\nfrom zvt.utils.time_utils import now_pd_timestamp\n\n\nclass SinaBlockRecorder(Recorder):\n    provider = \"sina\"\n    data_schema = Block\n\n    # 用于抓取行业/概念/地域列表\n    category_map_url = {\n        BlockCategory.industry: \"http://vip.stock.finance.sina.com.cn/q/view/newSinaHy.php\",\n        BlockCategory.concept: \"http://money.finance.sina.com.cn/q/view/newFLJK.php?param=class\"\n        # StockCategory.area: 'http://money.finance.sina.com.cn/q/view/newFLJK.php?param=area',\n    }\n\n    def run(self):\n        # get stock blocks from sina\n        for category, url in self.category_map_url.items():\n            resp = requests.get(url)\n            resp.encoding = \"GBK\"\n\n            tmp_str = resp.text\n            json_str = tmp_str[tmp_str.index(\"{\") : tmp_str.index(\"}\") + 1]\n            tmp_json = json.loads(json_str)\n\n            the_list = []\n\n            for code in tmp_json:\n                name = tmp_json[code].split(\",\")[1]\n                entity_id = f\"block_cn_{code}\"\n                the_list.append(\n                    {\n                        \"id\": entity_id,\n                        \"entity_id\": entity_id,\n                        \"entity_type\": \"block\",\n                        \"exchange\": \"cn\",\n                        \"code\": code,\n                        \"name\": name,\n                        \"category\": category.value,\n                    }\n                )\n            if the_list:\n                df = pd.DataFrame.from_records(the_list)\n                df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n\n            self.logger.info(f\"finish record sina blocks:{category.value}\")\n\n\nclass SinaChinaBlockStockRecorder(TimeSeriesDataRecorder):\n    entity_provider = \"sina\"\n    entity_schema = Block\n\n    provider = \"sina\"\n    data_schema = BlockStock\n\n    # 用于抓取行业包含的股票\n    category_stocks_url = \"http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/Market_Center.getHQNodeData?page={}&num=5000&sort=symbol&asc=1&node={}&symbol=&_s_r_a=page\"\n\n    def record(self, entity, start, end, size, timestamps):\n        for page in range(1, 5):\n            resp = requests.get(self.category_stocks_url.format(page, entity.code))\n            try:\n                if resp.text == \"null\" or resp.text is None:\n                    break\n                category_jsons = demjson3.decode(resp.text)\n                the_list = []\n                for category in category_jsons:\n                    stock_code = category[\"code\"]\n                    stock_id = china_stock_code_to_id(stock_code)\n                    block_id = entity.id\n                    the_list.append(\n                        {\n                            \"id\": \"{}_{}\".format(block_id, stock_id),\n                            \"entity_id\": block_id,\n                            \"entity_type\": \"block\",\n                            \"exchange\": entity.exchange,\n                            \"code\": entity.code,\n                            \"name\": entity.name,\n                            \"timestamp\": now_pd_timestamp(),\n                            \"stock_id\": stock_id,\n                            \"stock_code\": stock_code,\n                            \"stock_name\": category[\"name\"],\n                        }\n                    )\n                if the_list:\n                    df = pd.DataFrame.from_records(the_list)\n                    df_to_db(data_schema=self.data_schema, df=df, provider=self.provider, force_update=True)\n\n                self.logger.info(\"finish recording BlockStock:{},{}\".format(entity.category, entity.name))\n\n            except Exception as e:\n                self.logger.error(\"error:,resp.text:\", e, resp.text)\n            self.sleep()\n\n\nif __name__ == \"__main__\":\n    # init_log('sina_china_stock_category.log')\n    SinaBlockRecorder().run()\n    recorder = SinaChinaBlockStockRecorder(codes=[\"new_cbzz\"])\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"SinaBlockRecorder\", \"SinaChinaBlockStockRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/sina/money_flow/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule sina_stock_money_flow_recorder\nfrom .sina_stock_money_flow_recorder import *\nfrom .sina_stock_money_flow_recorder import __all__ as _sina_stock_money_flow_recorder_all\n\n__all__ += _sina_stock_money_flow_recorder_all\n\n# import all from submodule sina_block_money_flow_recorder\nfrom .sina_block_money_flow_recorder import *\nfrom .sina_block_money_flow_recorder import __all__ as _sina_block_money_flow_recorder_all\n\n__all__ += _sina_block_money_flow_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/sina/money_flow/sina_block_money_flow_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport time\n\nimport requests\n\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import BlockMoneyFlow, BlockCategory, Block\nfrom zvt.utils.time_utils import to_pd_timestamp\nfrom zvt.utils.utils import to_float\n\n\n# 实时资金流\n# 'http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/MoneyFlow.ssl_bkzj_bk?page=1&num=20&sort=netamount&asc=0&fenlei=1'\n# 'http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/MoneyFlow.ssl_bkzj_bk?page=1&num=20&sort=netamount&asc=0&fenlei=0'\n\n\nclass SinaBlockMoneyFlowRecorder(FixedCycleDataRecorder):\n    # entity的信息从哪里来\n    entity_provider = \"sina\"\n    # entity的schema\n    entity_schema = Block\n\n    # 记录的信息从哪里来\n    provider = \"sina\"\n    # 记录的schema\n    data_schema = BlockMoneyFlow\n\n    url = \"http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/MoneyFlow.ssl_bkzj_zjlrqs?page=1&num={}&sort=opendate&asc=0&bankuai={}%2F{}\"\n\n    def generate_url(self, category, code, number):\n        if category == BlockCategory.industry.value:\n            block = 0\n        elif category == BlockCategory.concept.value:\n            block = 1\n\n        return self.url.format(number, block, code)\n\n    def get_data_map(self):\n        return {}\n\n    def record(self, entity, start, end, size, timestamps):\n        url = self.generate_url(category=entity.category, code=entity.code, number=size)\n\n        resp = requests.get(url)\n\n        opendate = \"opendate\"\n        avg_price = \"avg_price\"\n        avg_changeratio = \"avg_changeratio\"\n        turnover = \"turnover\"\n        netamount = \"netamount\"\n        ratioamount = \"ratioamount\"\n        r0_net = \"r0_net\"\n        r0_ratio = \"r0_ratio\"\n        r0x_ratio = \"r0x_ratio\"\n        cnt_r0x_ratio = \"cnt_r0x_ratio\"\n\n        json_list = []\n        try:\n            json_list = eval(resp.text)\n        except Exception as e:\n            resp.encoding = \"GBK\"\n            self.logger.error(resp.text)\n            time.sleep(60 * 5)\n\n        result_list = []\n        for item in json_list:\n            result_list.append(\n                {\n                    \"name\": entity.name,\n                    \"timestamp\": to_pd_timestamp(item[\"opendate\"]),\n                    \"close\": to_float(item[\"avg_price\"]),\n                    \"change_pct\": to_float(item[\"avg_changeratio\"]),\n                    \"turnover_rate\": to_float(item[\"turnover\"]) / 10000,\n                    \"net_inflows\": to_float(item[\"netamount\"]),\n                    \"net_inflow_rate\": to_float(item[\"ratioamount\"]),\n                    \"net_main_inflows\": to_float(item[\"r0_net\"]),\n                    \"net_main_inflow_rate\": to_float(item[\"r0_ratio\"]),\n                }\n            )\n\n        return result_list\n\n\nif __name__ == \"__main__\":\n    SinaBlockMoneyFlowRecorder(codes=[\"new_fjzz\"]).run()\n    # SinaIndexMoneyFlowRecorder().run()\n\n\n# the __all__ is generated\n__all__ = [\"SinaBlockMoneyFlowRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/sina/money_flow/sina_stock_money_flow_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nimport time\n\nimport requests\n\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import StockMoneyFlow, Stock, StockTradeDay\nfrom zvt.utils.time_utils import to_pd_timestamp, is_same_date, now_pd_timestamp\nfrom zvt.utils.utils import to_float\n\n\nclass SinaStockMoneyFlowRecorder(FixedCycleDataRecorder):\n    entity_provider = \"joinquant\"\n    entity_schema = Stock\n\n    provider = \"sina\"\n    data_schema = StockMoneyFlow\n\n    url = \"http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/MoneyFlow.ssl_qsfx_lscjfb?page=1&num={}&sort=opendate&asc=0&daima={}\"\n\n    def init_entities(self):\n        super().init_entities()\n        # 过滤掉退市的\n        self.entities = [\n            entity for entity in self.entities if (entity.end_date is None) or (entity.end_date > now_pd_timestamp())\n        ]\n\n    # TODO:more general for the case using StockTradeDay\n    def evaluate_start_end_size_timestamps(self, entity):\n        start, end, size, timestamps = super().evaluate_start_end_size_timestamps(entity)\n        if start:\n            trade_day = StockTradeDay.query_data(limit=1, order=StockTradeDay.timestamp.desc(), return_type=\"domain\")\n            if trade_day:\n                if is_same_date(trade_day[0].timestamp, start):\n                    size = 0\n        return start, end, size, timestamps\n\n    def generate_url(self, code, number):\n        return self.url.format(number, code)\n\n    def get_data_map(self):\n        return {}\n\n    def record(self, entity, start, end, size, timestamps):\n        param = {\n            \"url\": self.generate_url(code=\"{}{}\".format(entity.exchange, entity.code), number=size),\n            \"security_item\": entity,\n        }\n\n        resp = requests.get(param[\"url\"])\n        # {opendate:\"2019-04-29\",trade:\"10.8700\",changeratio:\"-0.0431338\",turnover:\"74.924\",netamount:\"-2903349.8500\",\n        # ratioamount:\"-0.155177\",r0:\"0.0000\",r1:\"2064153.0000\",r2:\"6485031.0000\",r3:\"10622169.2100\",r0_net:\"0.0000\",\n        # r1_net:\"2064153.0000\",r2_net:\"-1463770.0000\",r3_net:\"-3503732.8500\"}\n        opendate = \"opendate\"\n        trade = \"trade\"\n        changeratio = \"changeratio\"\n        turnover = \"turnover\"\n        netamount = \"netamount\"\n        ratioamount = \"ratioamount\"\n        r0 = \"r0\"\n        r1 = \"r1\"\n        r2 = \"r2\"\n        r3 = \"r3\"\n        r0_net = \"r0_net\"\n        r1_net = \"r1_net\"\n        r2_net = \"r2_net\"\n        r3_net = \"r3_net\"\n\n        json_list = []\n\n        try:\n            json_list = eval(resp.text)\n        except Exception as e:\n            resp.encoding = \"GBK\"\n            self.logger.error(resp.text)\n            time.sleep(60 * 5)\n\n        result_list = []\n        for item in json_list:\n            amount = to_float(item[\"r0\"]) + to_float(item[\"r1\"]) + to_float(item[\"r2\"]) + to_float(item[\"r3\"])\n\n            result = {\n                \"timestamp\": to_pd_timestamp(item[\"opendate\"]),\n                \"name\": entity.name,\n                \"close\": to_float(item[\"trade\"]),\n                \"change_pct\": to_float(item[\"changeratio\"]),\n                \"turnover_rate\": to_float(item[\"turnover\"]) / 10000,\n                \"net_inflows\": to_float(item[\"netamount\"]),\n                \"net_inflow_rate\": to_float(item[\"ratioamount\"]),\n                #     # 主力=超大单+大单\n                #     net_main_inflows = Column(Float)\n                #     net_main_inflow_rate = Column(Float)\n                #     # 超大单\n                #     net_huge_inflows = Column(Float)\n                #     net_huge_inflow_rate = Column(Float)\n                #     # 大单\n                #     net_big_inflows = Column(Float)\n                #     net_big_inflow_rate = Column(Float)\n                #\n                #     # 中单\n                #     net_medium_inflows = Column(Float)\n                #     net_medium_inflow_rate = Column(Float)\n                #     # 小单\n                #     net_small_inflows = Column(Float)\n                #     net_small_inflow_rate = Column(Float)\n                \"net_main_inflows\": to_float(item[\"r0_net\"]) + to_float(item[\"r1_net\"]),\n                \"net_huge_inflows\": to_float(item[\"r0_net\"]),\n                \"net_big_inflows\": to_float(item[\"r1_net\"]),\n                \"net_medium_inflows\": to_float(item[\"r2_net\"]),\n                \"net_small_inflows\": to_float(item[\"r3_net\"]),\n            }\n\n            if amount != 0:\n                result[\"net_main_inflow_rate\"] = (to_float(item[\"r0_net\"]) + to_float(item[\"r1_net\"])) / amount\n                result[\"net_huge_inflow_rate\"] = to_float(item[\"r0_net\"]) / amount\n                result[\"net_big_inflow_rate\"] = to_float(item[\"r1_net\"]) / amount\n                result[\"net_medium_inflow_rate\"] = to_float(item[\"r2_net\"]) / amount\n                result[\"net_small_inflow_rate\"] = to_float(item[\"r3_net\"]) / amount\n\n            result_list.append(result)\n\n        return result_list\n\n\nif __name__ == \"__main__\":\n    SinaStockMoneyFlowRecorder(codes=[\"000406\"]).run()\n    # SinaStockMoneyFlowRecorder().run()\n\n\n# the __all__ is generated\n__all__ = [\"SinaStockMoneyFlowRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/sina/quotes/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule sina_index_kdata_recorder\nfrom .sina_index_kdata_recorder import *\nfrom .sina_index_kdata_recorder import __all__ as _sina_index_kdata_recorder_all\n\n__all__ += _sina_index_kdata_recorder_all\n\n# import all from submodule sina_etf_kdata_recorder\nfrom .sina_etf_kdata_recorder import *\nfrom .sina_etf_kdata_recorder import __all__ as _sina_etf_kdata_recorder_all\n\n__all__ += _sina_etf_kdata_recorder_all\n"
  },
  {
    "path": "src/zvt/recorders/sina/quotes/sina_etf_kdata_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport demjson3\nimport pandas as pd\nimport requests\n\nfrom zvt import init_log\nfrom zvt.api.kdata import generate_kdata_id, get_kdata\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import Etf, Etf1dKdata\nfrom zvt.recorders.consts import EASTMONEY_ETF_NET_VALUE_HEADER\nfrom zvt.utils.time_utils import to_date_time_str\n\n\nclass ChinaETFDayKdataRecorder(FixedCycleDataRecorder):\n    entity_provider = \"exchange\"\n    entity_schema = Etf\n\n    provider = \"sina\"\n    data_schema = Etf1dKdata\n    url = (\n        \"http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?\"\n        \"symbol={}{}&scale=240&&datalen={}&ma=no\"\n    )\n\n    def get_data_map(self):\n        return {}\n\n    def generate_domain_id(self, entity, original_data):\n        return generate_kdata_id(entity_id=entity.id, timestamp=original_data[\"timestamp\"], level=self.level)\n\n    def on_finish_entity(self, entity):\n        kdatas = get_kdata(\n            entity_id=entity.id,\n            level=IntervalLevel.LEVEL_1DAY.value,\n            order=Etf1dKdata.timestamp.asc(),\n            return_type=\"domain\",\n            session=self.session,\n            filters=[Etf1dKdata.cumulative_net_value.is_(None)],\n        )\n\n        if kdatas and len(kdatas) > 0:\n            start = kdatas[0].timestamp\n            end = kdatas[-1].timestamp\n\n            # 从东方财富获取基金累计净值\n            df = self.fetch_cumulative_net_value(entity, start, end)\n\n            if df is not None and not df.empty:\n                for kdata in kdatas:\n                    if kdata.timestamp in df.index:\n                        kdata.cumulative_net_value = df.loc[kdata.timestamp, \"LJJZ\"]\n                        kdata.change_pct = df.loc[kdata.timestamp, \"JZZZL\"]\n                self.session.commit()\n                self.logger.info(f\"{entity.code} - {entity.name}累计净值更新完成...\")\n\n    def fetch_cumulative_net_value(self, security_item, start, end) -> pd.DataFrame:\n        query_url = (\n            \"http://api.fund.eastmoney.com/f10/lsjz?\" \"fundCode={}&pageIndex={}&pageSize=200&startDate={}&endDate={}\"\n        )\n\n        page = 1\n        df = pd.DataFrame()\n        while True:\n            url = query_url.format(security_item.code, page, to_date_time_str(start), to_date_time_str(end))\n\n            response = requests.get(url, headers=EASTMONEY_ETF_NET_VALUE_HEADER)\n            response_json = demjson3.decode(response.text)\n            response_df = pd.DataFrame(response_json[\"Data\"][\"LSJZList\"])\n\n            # 最后一页\n            if response_df.empty:\n                break\n\n            response_df[\"FSRQ\"] = pd.to_datetime(response_df[\"FSRQ\"])\n            response_df[\"JZZZL\"] = pd.to_numeric(response_df[\"JZZZL\"], errors=\"coerce\")\n            response_df[\"LJJZ\"] = pd.to_numeric(response_df[\"LJJZ\"], errors=\"coerce\")\n            response_df = response_df.fillna(0)\n            response_df.set_index(\"FSRQ\", inplace=True, drop=True)\n\n            df = pd.concat([df, response_df])\n            page += 1\n\n            self.sleep()\n\n        return df\n\n    def record(self, entity, start, end, size, timestamps):\n        # 此 url 不支持分页，如果超过我们想取的条数，则只能取最大条数\n        if start is None or size > self.default_size:\n            size = 8000\n\n        param = {\"security_item\": entity, \"level\": self.level.value, \"size\": size}\n\n        security_item = param[\"security_item\"]\n        size = param[\"size\"]\n\n        url = ChinaETFDayKdataRecorder.url.format(security_item.exchange, security_item.code, size)\n\n        response = requests.get(url)\n        response_json = demjson3.decode(response.text)\n\n        if response_json is None or len(response_json) == 0:\n            return []\n\n        df = pd.DataFrame(response_json)\n        df.rename(columns={\"day\": \"timestamp\"}, inplace=True)\n        df[\"timestamp\"] = pd.to_datetime(df[\"timestamp\"])\n        df[\"name\"] = security_item.name\n        df[\"provider\"] = \"sina\"\n        df[\"level\"] = param[\"level\"]\n\n        return df.to_dict(orient=\"records\")\n\n\n__all__ = [\"ChinaETFDayKdataRecorder\"]\n\nif __name__ == \"__main__\":\n    init_log(\"sina_china_etf_day_kdata.log\")\n    ChinaETFDayKdataRecorder(level=IntervalLevel.LEVEL_1DAY).run()\n\n\n# the __all__ is generated\n__all__ = [\"ChinaETFDayKdataRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/sina/quotes/sina_index_kdata_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport time\n\nimport pandas as pd\nimport requests\n\nfrom zvt.api.kdata import generate_kdata_id, get_kdata_schema\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import Index, IndexKdataCommon\nfrom zvt.utils.time_utils import get_year_quarters, is_same_date\n\n\nclass ChinaIndexDayKdataRecorder(FixedCycleDataRecorder):\n    entity_provider = \"exchange\"\n    entity_schema = Index\n\n    provider = \"sina\"\n    data_schema = IndexKdataCommon\n    url = \"http://vip.stock.finance.sina.com.cn/corp/go.php/vMS_MarketHistory/stockid/{}/type/S.phtml?year={}&jidu={}\"\n\n    def __init__(\n        self,\n        force_update=True,\n        sleeping_time=10,\n        exchanges=None,\n        entity_id=None,\n        entity_ids=None,\n        code=None,\n        codes=None,\n        day_data=False,\n        entity_filters=None,\n        ignore_failed=True,\n        real_time=False,\n        fix_duplicate_way=\"ignore\",\n        start_timestamp=None,\n        end_timestamp=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        kdata_use_begin_time=False,\n        one_day_trading_minutes=24 * 60,\n        return_unfinished=False,\n    ) -> None:\n        level = IntervalLevel(level)\n        self.adjust_type = AdjustType.qfq\n        self.entity_type = self.entity_schema.__name__.lower()\n\n        self.data_schema = get_kdata_schema(entity_type=self.entity_type, level=level, adjust_type=self.adjust_type)\n\n        super().__init__(\n            force_update,\n            sleeping_time,\n            exchanges,\n            entity_id,\n            entity_ids,\n            code,\n            codes,\n            day_data,\n            entity_filters,\n            ignore_failed,\n            real_time,\n            fix_duplicate_way,\n            start_timestamp,\n            end_timestamp,\n            level,\n            kdata_use_begin_time,\n            one_day_trading_minutes,\n            return_unfinished,\n        )\n\n    def get_data_map(self):\n        return {}\n\n    def generate_domain_id(self, entity, original_data):\n        return generate_kdata_id(entity.id, timestamp=original_data[\"timestamp\"], level=self.level)\n\n    def record(self, entity, start, end, size, timestamps):\n        the_quarters = get_year_quarters(start)\n        if not is_same_date(entity.timestamp, start) and len(the_quarters) > 1:\n            the_quarters = the_quarters[1:]\n\n        param = {\"security_item\": entity, \"quarters\": the_quarters, \"level\": self.level.value}\n\n        security_item = param[\"security_item\"]\n        quarters = param[\"quarters\"]\n        level = param[\"level\"]\n\n        result_df = pd.DataFrame()\n        for year, quarter in quarters:\n            query_url = self.url.format(security_item.code, year, quarter)\n            response = requests.get(query_url)\n            response.encoding = \"gbk\"\n\n            try:\n                dfs = pd.read_html(response.text)\n            except ValueError as error:\n                self.logger.error(f\"skip ({year}-{quarter:02d}){security_item.code}{security_item.name}({error})\")\n                time.sleep(10.0)\n                continue\n\n            if len(dfs) < 5:\n                time.sleep(10.0)\n                continue\n\n            df = dfs[4].copy()\n            df = df.iloc[1:]\n            df.columns = [\"timestamp\", \"open\", \"high\", \"close\", \"low\", \"volume\", \"turnover\"]\n            df[\"name\"] = security_item.name\n            df[\"level\"] = level\n            df[\"timestamp\"] = pd.to_datetime(df[\"timestamp\"])\n            df[\"provider\"] = \"sina\"\n\n            result_df = pd.concat([result_df, df])\n\n            self.logger.info(f\"({security_item.code}{security_item.name})({year}-{quarter:02d})\")\n            time.sleep(10.0)\n\n        result_df = result_df.sort_values(by=\"timestamp\")\n\n        return result_df.to_dict(orient=\"records\")\n\n\n__all__ = [\"ChinaIndexDayKdataRecorder\"]\n\nif __name__ == \"__main__\":\n    ChinaIndexDayKdataRecorder().run()\n\n\n# the __all__ is generated\n__all__ = [\"ChinaIndexDayKdataRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/wb/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule wb_economy_recorder\nfrom .wb_economy_recorder import *\nfrom .wb_economy_recorder import __all__ as _wb_economy_recorder_all\n\n__all__ += _wb_economy_recorder_all\n\n# import all from submodule wb_country_recorder\nfrom .wb_country_recorder import *\nfrom .wb_country_recorder import __all__ as _wb_country_recorder_all\n\n__all__ += _wb_country_recorder_all\n\n# import all from submodule wb_api\nfrom .wb_api import *\nfrom .wb_api import __all__ as _wb_api_all\n\n__all__ += _wb_api_all\n"
  },
  {
    "path": "src/zvt/recorders/wb/wb_api.py",
    "content": "# -*- coding: utf-8 -*-\nimport itertools\nimport re\nfrom copy import copy\n\nimport pandas as pd\nimport requests\n\nfrom zvt.contract.api import get_entity_code\nfrom zvt.utils.pd_utils import normal_index_df\nfrom zvt.utils.time_utils import to_pd_timestamp\n\nWORLD_BANK_URL = \"http://api.worldbank.org/v2\"\n\n# thanks to https://github.com/mwouts/world_bank_data\n\n_economy_indicator_map = {\n    \"population\": \"SP.POP.TOTL\",\n    \"gdp\": \"NY.GDP.MKTP.CD\",\n    \"gdp_per_capita\": \"NY.GDP.PCAP.CD\",\n    \"gdp_per_employed\": \"SL.GDP.PCAP.EM.KD\",\n    \"gdp_growth\": \"NY.GDP.MKTP.KD.ZG\",\n    \"agriculture_growth\": \"NV.AGR.TOTL.KD.ZG\",\n    \"industry_growth\": \"NV.IND.TOTL.KD.ZG\",\n    \"manufacturing_growth\": \"NV.IND.MANF.KD.ZG\",\n    \"service_growth\": \"NV.SRV.TOTL.KD.ZG\",\n    \"consumption_growth\": \"NE.CON.TOTL.KD.ZG\",\n    \"capital_growth\": \"NE.GDI.TOTL.KD.ZG\",\n    \"exports_growth\": \"NE.EXP.GNFS.KD.ZG\",\n    \"imports_growth\": \"NE.IMP.GNFS.KD.ZG\",\n    \"gni\": \"NY.GNP.ATLS.CD\",\n    \"gni_per_capita\": \"NY.GNP.PCAP.CD\",\n    \"gross_saving\": \"NY.GNS.ICTR.ZS\",\n    \"cpi\": \"FP.CPI.TOTL\",\n    \"unemployment_rate\": \"SL.UEM.TOTL.ZS\",\n    \"fdi_of_gdp\": \"BX.KLT.DINV.WD.GD.ZS\",\n}\n\n\ndef _collapse(values):\n    \"\"\"Collapse multiple values to a colon-separated list of values\"\"\"\n    if isinstance(values, str):\n        return values\n    if values is None:\n        return \"all\"\n    if isinstance(values, list):\n        return \";\".join([_collapse(v) for v in values])\n    return str(values)\n\n\ndef _extract_preferred_field(data, id_or_value):\n    \"\"\"In case the preferred representation of data when the latter has multiple representations\"\"\"\n    if not id_or_value:\n        return data\n\n    if not data:\n        return \"\"\n\n    if isinstance(data, dict):\n        if id_or_value in data:\n            return data[id_or_value]\n\n    if isinstance(data, list):\n        return \",\".join([_extract_preferred_field(i, id_or_value) for i in data])\n\n    return data\n\n\ndef _wb_get(paths: dict = None, **kwargs):\n    params = copy(kwargs)\n    params.setdefault(\"format\", \"json\")\n    params.setdefault(\"per_page\", 20000)\n\n    url = \"/\".join([WORLD_BANK_URL] + list(itertools.chain.from_iterable([(k, _collapse(paths[k])) for k in paths])))\n\n    response = requests.get(url=url, params=params)\n    response.raise_for_status()\n    try:\n        data = response.json()\n    except ValueError:\n        raise ValueError(\n            \"{msg}\\nurl={url}\\nparams={params}\".format(msg=_extract_message(response.text), url=url, params=params)\n        )\n    if isinstance(data, list) and data and \"message\" in data[0]:\n        try:\n            msg = data[0][\"message\"][0][\"value\"]\n        except (KeyError, IndexError):\n            msg = str(msg)\n\n        raise ValueError(\"{msg}\\nurl={url}\\nparams={params}\".format(msg=msg, url=url, params=params))\n\n    # Redo the request and get the full information when the first response is incomplete\n    if isinstance(data, list):\n        page_information, data = data\n        if \"page\" not in params:\n            current_page = 1\n            while current_page < int(page_information[\"pages\"]):\n                params[\"page\"] = current_page = int(page_information[\"page\"]) + 1\n                response = requests.get(url=url, params=params)\n                response.raise_for_status()\n                page_information, new_data = response.json()\n                data.extend(new_data)\n\n    if not data:\n        raise RuntimeError(\"The request returned no data:\\nurl={url}\\nparams={params}\".format(url=url, params=params))\n\n    return data\n\n\ndef _extract_message(msg):\n    \"\"\"'ï»¿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n    <wb:error xmlns:wb=\"http://www.worldbank.org\">\n      <wb:message id=\"175\" key=\"Invalid format\">The indicator was not found. It may have been deleted or archived.</wb:message>\n    </wb:error>'\"\"\"\n    if \"wb:message\" not in msg:\n        return msg\n    return re.sub(\n        re.compile(\".*<wb:message[^>]*>\", re.DOTALL), \"\", re.sub(re.compile(\"</wb:message>.*\", re.DOTALL), \"\", msg)\n    )\n\n\ndef _get_meta(name, filters=None, expected=None, **params):\n    \"\"\"Request data and return it in the form of a data frame\"\"\"\n    filters = _collapse(filters)\n    id_or_value = \"value\"\n\n    if expected and id_or_value not in expected:\n        raise ValueError(\"'id_or_value' should be one of '{}'\".format(\"', '\".join(expected)))\n\n    data = _wb_get(paths={name: filters}, **params)\n\n    # We get a list (countries) of dictionary (properties)\n    columns = data[0].keys()\n    records = {}\n\n    for col in columns:\n        records[col] = [_extract_preferred_field(cnt[col], id_or_value) for cnt in data]\n\n    return pd.DataFrame(records, columns=columns)\n\n\ndef get_countries():\n    df = _get_meta(\"country\", expected=[\"id\", \"iso2code\", \"value\"])\n\n    for col in [\"latitude\", \"longitude\"]:\n        df[col] = pd.to_numeric(df[col])\n    df.rename(\n        columns={\n            \"iso2Code\": \"code\",\n            \"incomeLevel\": \"income_level\",\n            \"lendingType\": \"lending_type\",\n            \"capitalCity\": \"capital_city\",\n        },\n        inplace=True,\n    )\n    df[\"entity_type\"] = \"country\"\n    df[\"exchange\"] = \"galaxy\"\n    df[\"entity_id\"] = df[[\"entity_type\", \"exchange\", \"code\"]].apply(lambda x: \"_\".join(x.astype(str)), axis=1)\n    df[\"id\"] = df[\"entity_id\"]\n    return df\n\n\ndef get_indicators(indicator=None, language=None, id_or_value=None, **params):\n    \"\"\"Return a DataFrame that describes one, multiple or all indicators, indexed by the indicator id.\n    :param indicator: None (all indicators), the id of an indicator, or a list of multiple ids\n    :param language: Desired language\n    :param id_or_value: Choose either 'id' or 'value' for columns 'source' and 'topics'\"\"\"\n\n    if id_or_value == \"iso2code\":\n        id_or_value = \"id\"\n\n    return _get_meta(\n        \"indicator\", indicator, language=language, id_or_value=id_or_value, expected=[\"id\", \"value\"], **params\n    )\n\n\ndef get_indicator_data(indicator, indicator_name=None, country=None, date=None):\n    datas = _wb_get(paths={\"country\": country, \"indicator\": indicator}, date=date)\n    records = [\n        {\n            \"code\": item[\"country\"][\"id\"],\n            \"timestamp\": to_pd_timestamp(item[\"date\"]),\n            item[\"indicator\"][\"id\"] if not indicator_name else indicator_name: item[\"value\"],\n        }\n        for item in datas\n    ]\n    df = pd.DataFrame.from_records(data=records)\n    df = df.set_index([\"code\", \"timestamp\"])\n    return df\n\n\ndef get_regions(region=None, language=None, **params):\n    \"\"\"Return a DataFrame that describes one, multiple or all regions, indexed by the region id.\n    :param region: None (all regions), the id of a region, or a list of multiple ids\n    :param language: Desired language\"\"\"\n    return _get_meta(\"region\", region, language, **params)\n\n\ndef get_sources(source=None, language=None, **params):\n    \"\"\"Return a DataFrame that describes one, multiple or all sources, indexed by the source id.\n    :param source: None (all sources), the id of a source, or a list of multiple ids\n    :param language: Desired language\"\"\"\n    return _get_meta(\"source\", source, language, **params)\n\n\ndef get_topics(topic=None, language=None, **params):\n    \"\"\"Return a DataFrame that describes one, multiple or all sources, indexed by the source id.\n    :param topic: None (all topics), the id of a topic, or a list of multiple ids\n    :param language: Desired language\"\"\"\n    return _get_meta(\"topic\", topic, language, **params)\n\n\ndef get_incomelevels(incomelevel=None, language=None, **params):\n    \"\"\"Return a DataFrame that describes one, multiple or all income levels, indexed by the IL id.\n    :param incomelevel: None (all income levels), the id of an income level, or a list of multiple ids\n    :param language: Desired language\"\"\"\n    return _get_meta(\"incomelevel\", incomelevel, language, **params)\n\n\ndef get_lendingtypes(lendingtype=None, language=None, **params):\n    \"\"\"Return a DataFrame that describes one, multiple or all lending types, indexed by the LT id.\n    :param lendingtype: None (all lending types), the id of a lending type, or a list of multiple ids\n    :param language: Desired language\"\"\"\n    return _get_meta(\"lendingtype\", lendingtype, language, **params)\n\n\ndef get_economy_data(entity_id, indicators=None, date=None):\n    country = get_entity_code(entity_id=entity_id)\n    if not indicators:\n        indicators = _economy_indicator_map.keys()\n    dfs = []\n    for indicator in indicators:\n        data = get_indicator_data(\n            indicator=_economy_indicator_map.get(indicator), indicator_name=indicator, country=country, date=date\n        )\n        dfs.append(data)\n    df = pd.concat(dfs, axis=1)\n    df = df.reset_index(drop=False)\n    df[\"entity_id\"] = entity_id\n    df[\"id\"] = df[[\"entity_id\", \"timestamp\"]].apply(lambda x: \"_\".join(x.astype(str)), axis=1)\n    df = normal_index_df(df, drop=False)\n    return df\n\n\nif __name__ == \"__main__\":\n    # df = get_countries()\n    # print(df)\n    df = get_economy_data(entity_id=\"country_galaxy_CN\")\n    print(df)\n    # df = get_sources()\n    # print(df)\n\n\n# the __all__ is generated\n__all__ = [\n    \"get_countries\",\n    \"get_indicators\",\n    \"get_indicator_data\",\n    \"get_regions\",\n    \"get_sources\",\n    \"get_topics\",\n    \"get_incomelevels\",\n    \"get_lendingtypes\",\n    \"get_economy_data\",\n]\n"
  },
  {
    "path": "src/zvt/recorders/wb/wb_country_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import Recorder\nfrom zvt.domain.meta.country_meta import Country\nfrom zvt.recorders.wb import wb_api\n\n\nclass WBCountryRecorder(Recorder):\n    provider = \"wb\"\n    data_schema = Country\n\n    def run(self):\n        df = wb_api.get_countries()\n        df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n\n\nif __name__ == \"__main__\":\n    recorder = WBCountryRecorder()\n    recorder.run()\n\n\n# the __all__ is generated\n__all__ = [\"WBCountryRecorder\"]\n"
  },
  {
    "path": "src/zvt/recorders/wb/wb_economy_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom zvt.contract.api import df_to_db\nfrom zvt.contract.recorder import FixedCycleDataRecorder\nfrom zvt.domain import Country, Economy\nfrom zvt.recorders.wb import wb_api\nfrom zvt.utils.time_utils import current_date\n\n\nclass WBEconomyRecorder(FixedCycleDataRecorder):\n    entity_schema = Country\n    data_schema = Economy\n    entity_provider = \"wb\"\n    provider = \"wb\"\n\n    def record(self, entity, start, end, size, timestamps):\n        date = None\n        if start:\n            date = f\"{start.year}:{current_date().year}\"\n        try:\n            df = wb_api.get_economy_data(entity_id=entity.id, date=date)\n            df[\"name\"] = entity.name\n            df_to_db(df=df, data_schema=self.data_schema, provider=self.provider, force_update=self.force_update)\n        # 一些地方获取不到数据会报错\n        except Exception as e:\n            self.logger.warning(f\"Failed to get {entity.name} economy data\", e)\n\n\nif __name__ == \"__main__\":\n    entity_ids = [\"country_galaxy_CN\", \"country_galaxy_US\"]\n    r = WBEconomyRecorder(entity_ids=entity_ids)\n    r.run()\n\n\n# the __all__ is generated\n__all__ = [\"WBEconomyRecorder\"]\n"
  },
  {
    "path": "src/zvt/resources/concept_main_tag_mapping.json",
    "content": "{\n  \"广电\": \"文化传媒\",\n  \"数字阅读\": \"文化传媒\",\n  \"网络游戏\": \"文化传媒\",\n  \"手游概念\": \"文化传媒\",\n  \"影视概念\": \"文化传媒\",\n  \"稀土永磁\": \"资源\",\n  \"可燃冰\": \"资源\",\n  \"油价相关\": \"资源\",\n  \"低碳冶金\": \"资源\",\n  \"基本金属\": \"资源\",\n  \"黄金概念\": \"资源\",\n  \"天然气\": \"资源\",\n  \"稀缺资源\": \"资源\",\n  \"页岩气\": \"资源\",\n  \"煤化工\": \"资源\",\n  \"油气设服\": \"资源\",\n  \"小金属概念\": \"资源\",\n  \"新能源\": \"新能源\",\n  \"BC电池\": \"新能源\",\n  \"TOPCon电池\": \"新能源\",\n  \"钒电池\": \"新能源\",\n  \"钙钛矿电池\": \"新能源\",\n  \"麒麟电池\": \"新能源\",\n  \"动力电池回收\": \"新能源\",\n  \"钠离子电池\": \"新能源\",\n  \"固态电池\": \"新能源\",\n  \"刀片电池\": \"新能源\",\n  \"HIT电池\": \"新能源\",\n  \"燃料电池\": \"新能源\",\n  \"锂电池\": \"新能源\",\n  \"储能\": \"新能源\",\n  \"充电桩\": \"新能源\",\n  \"熔盐储能\": \"新能源\",\n  \"换电概念\": \"新能源\",\n  \"盐湖提锂\": \"新能源\",\n  \"风能\": \"新能源\",\n  \"太阳能\": \"新能源\",\n  \"氢能源\": \"新能源\",\n  \"抽水蓄能\": \"新能源\",\n  \"光伏建筑一体化\": \"新能源\",\n  \"人造太阳\": \"新能源\",\n  \"可控核聚变\": \"新能源\",\n  \"核能核电\": \"电力\",\n  \"绿色电力\": \"电力\",\n  \"特高压\": \"电力\",\n  \"虚拟电厂\": \"电力\",\n  \"智能电网\": \"电力\",\n  \"超超临界发电\": \"电力\",\n  \"半导体概念\": \"半导体\",\n  \"中芯概念\": \"半导体\",\n  \"存储芯片\": \"半导体\",\n  \"AI芯片\": \"半导体\",\n  \"汽车芯片\": \"半导体\",\n  \"国产芯片\": \"半导体\",\n  \"第三代半导体\": \"半导体\",\n  \"第四代半导体\": \"半导体\",\n  \"英伟达概念\": \"半导体\",\n  \"玻璃基板\": \"半导体\",\n  \"高带宽内存\": \"半导体\",\n  \"Chiplet概念\": \"半导体\",\n  \"光刻胶\": \"半导体\",\n  \"氮化镓\": \"半导体\",\n  \"EDA概念\": \"半导体\",\n  \"IGBT概念\": \"半导体\",\n  \"PCB\": \"半导体\",\n  \"新能源车\": \"车路云\",\n  \"华为汽车\": \"车路云\",\n  \"汽车拆解\": \"车路云\",\n  \"汽车一体化压铸\": \"车路云\",\n  \"小米汽车\": \"车路云\",\n  \"汽车热管理\": \"车路云\",\n  \"电子后视镜\": \"车路云\",\n  \"高压快充\": \"车路云\",\n  \"车联网\": \"车路云\",\n  \"激光雷达\": \"车路云\",\n  \"特斯拉\": \"车路云\",\n  \"EDR概念\": \"车路云\",\n  \"无人驾驶\": \"车路云\",\n  \"ETC\": \"车路云\",\n  \"物联网\": \"智能机器\",\n  \"2025规划\": \"智能机器\",\n  \"智能机器\": \"智能机器\",\n  \"工业互联\": \"智能机器\",\n  \"轮毂电机\": \"智能机器\",\n  \"发电机概念\": \"智能机器\",\n  \"同步磁阻电机\": \"智能机器\",\n  \"机器人执行器\": \"智能机器\",\n  \"新型工业化\": \"智能机器\",\n  \"工业母机\": \"智能机器\",\n  \"工业4.0\": \"智能机器\",\n  \"减速器\": \"智能机器\",\n  \"机器人概念\": \"智能机器\",\n  \"PLC概念\": \"智能机器\",\n  \"机器视觉\": \"智能机器\",\n  \"生物医药\": \"医药\",\n  \"痘病毒防治\": \"医药\",\n  \"地塞米松\": \"医药\",\n  \"消毒剂\": \"医药\",\n  \"口罩\": \"医药\",\n  \"肝素概念\": \"医药\",\n  \"健康中国\": \"医药\",\n  \"幽门螺杆菌概念\": \"医药\",\n  \"代糖概念\": \"医药\",\n  \"医疗器械概念\": \"医药\",\n  \"生物疫苗\": \"医药\",\n  \"维生素\": \"医药\",\n  \"注射器概念\": \"医药\",\n  \"流感\": \"医药\",\n  \"AI制药\": \"医药\",\n  \"中药概念\": \"医药\",\n  \"减肥药\": \"医药\",\n  \"创新药\": \"医药\",\n  \"新冠药物\": \"医药\",\n  \"长寿药\": \"医药\",\n  \"独家药品\": \"医药\",\n  \"病毒防治\": \"医药\",\n  \"SPD概念\": \"医药\",\n  \"辅助生殖\": \"医药\",\n  \"肝炎概念\": \"医药\",\n  \"蒙脱石散\": \"医药\",\n  \"血氧仪\": \"医药\",\n  \"熊去氧胆酸\": \"医药\",\n  \"抗原检测\": \"医药\",\n  \"抗菌面料\": \"医药\",\n  \"千金藤素\": \"医药\",\n  \"DRG/DIP\": \"医药\",\n  \"CRO\": \"医药\",\n  \"阿兹海默\": \"医药\",\n  \"CAR-T细胞疗法\": \"医药\",\n  \"新冠检测\": \"医药\",\n  \"青蒿素\": \"医药\",\n  \"超级真菌\": \"医药\",\n  \"气溶胶检测\": \"医药\",\n  \"重组蛋白\": \"医药\",\n  \"疫苗冷链\": \"医药\",\n  \"精准医疗\": \"医药\",\n  \"单抗概念\": \"医药\",\n  \"免疫治疗\": \"医药\",\n  \"基因测序\": \"医药\",\n  \"体外诊断\": \"医药\",\n  \"互联医疗\": \"医药\",\n  \"人脑工程\": \"医药\",\n  \"啤酒概念\": \"大消费\",\n  \"进口博览\": \"大消费\",\n  \"退税商店\": \"大消费\",\n  \"拼多多概念\": \"大消费\",\n  \"抖音小店\": \"大消费\",\n  \"乳业\": \"大消费\",\n  \"C2M概念\": \"大消费\",\n  \"调味品概念\": \"大消费\",\n  \"毛发医疗\": \"大消费\",\n  \"化妆品概念\": \"大消费\",\n  \"白酒\": \"大消费\",\n  \"医疗美容\": \"大消费\",\n  \"户外露营\": \"大消费\",\n  \"在线旅游\": \"大消费\",\n  \"跨境电商\": \"大消费\",\n  \"电商概念\": \"大消费\",\n  \"新零售\": \"大消费\",\n  \"智能家居\": \"大消费\",\n  \"网红直播\": \"大消费\",\n  \"免税概念\": \"大消费\",\n  \"预制菜概念\": \"大消费\",\n  \"培育钻石\": \"大消费\",\n  \"婴童概念\": \"大消费\",\n  \"托育服务\": \"大消费\",\n  \"智慧灯杆\": \"消费电子\",\n  \"UWB概念\": \"消费电子\",\n  \"电子纸概念\": \"消费电子\",\n  \"胎压监测\": \"消费电子\",\n  \"3D玻璃\": \"消费电子\",\n  \"屏下摄像\": \"消费电子\",\n  \"超清视频\": \"消费电子\",\n  \"植物照明\": \"消费电子\",\n  \"LED\": \"消费电子\",\n  \"3D摄像头\": \"消费电子\",\n  \"eSIM\": \"消费电子\",\n  \"蓝宝石\": \"消费电子\",\n  \"无线耳机\": \"消费电子\",\n  \"智能穿戴\": \"消费电子\",\n  \"AI手机\": \"消费电子\",\n  \"AIPC\": \"消费电子\",\n  \"柔性屏(折叠屏)\": \"消费电子\",\n  \"星闪概念\": \"消费电子\",\n  \"传感器\": \"消费电子\",\n  \"被动元件\": \"消费电子\",\n  \"小米概念\": \"消费电子\",\n  \"无线充电\": \"消费电子\",\n  \"智能电视\": \"消费电子\",\n  \"空间计算\": \"消费电子\",\n  \"裸眼3D\": \"消费电子\",\n  \"混合现实\": \"消费电子\",\n  \"增强现实\": \"消费电子\",\n  \"虚拟现实\": \"消费电子\",\n  \"MicroLED\": \"消费电子\",\n  \"MiniLED\": \"消费电子\",\n  \"OLED\": \"消费电子\",\n  \"VPN\": \"消费电子\",\n  \"IPv6\": \"消费电子\",\n  \"WiFi\": \"消费电子\",\n  \"毫米波概念\": \"消费电子\",\n  \"5G概念\": \"消费电子\",\n  \"6G概念\": \"消费电子\",\n  \"F5G概念\": \"消费电子\",\n  \"量子通信\": \"消费电子\",\n  \"化工原料\": \"化工\",\n  \"环氧丙烷\": \"化工\",\n  \"PVDF概念\": \"化工\",\n  \"新材料\": \"化工\",\n  \"MLCC\": \"化工\",\n  \"碳纤维\": \"化工\",\n  \"PEEK材料概念\": \"化工\",\n  \"磷化工\": \"化工\",\n  \"碳基材料\": \"化工\",\n  \"纳米银\": \"化工\",\n  \"碳化硅\": \"化工\",\n  \"复合集流体\": \"化工\",\n  \"有机硅\": \"化工\",\n  \"石墨烯\": \"化工\",\n  \"氟化工\": \"化工\",\n  \"草甘膦\": \"化工\",\n  \"钛白粉\": \"化工\",\n  \"降解塑料\": \"化工\",\n  \"工业气体\": \"化工\",\n  \"氦气概念\": \"化工\",\n  \"超级电容\": \"化工\",\n  \"军民融合\": \"军工\",\n  \"海工装备\": \"军工\",\n  \"军工\": \"军工\",\n  \"航母概念\": \"军工\",\n  \"国家安防\": \"军工\",\n  \"空间站概念\": \"军工\",\n  \"大飞机\": \"军工\",\n  \"铜缆高速连接\": \"AI\",\n  \"ERP概念\": \"AI\",\n  \"数字哨兵\": \"AI\",\n  \"电子身份证\": \"AI\",\n  \"电子车牌\": \"AI\",\n  \"大数据\": \"AI\",\n  \"智慧城市\": \"AI\",\n  \"云计算\": \"AI\",\n  \"国产软件\": \"AI\",\n  \"生物识别\": \"AI\",\n  \"RCS概念\": \"AI\",\n  \"远程办公\": \"AI\",\n  \"在线教育\": \"AI\",\n  \"百度概念\": \"AI\",\n  \"人工智能\": \"AI\",\n  \"液冷概念\": \"AI\",\n  \"光通信模块\": \"AI\",\n  \"CPO概念\": \"AI\",\n  \"AI语料\": \"AI\",\n  \"Kimi概念\": \"AI\",\n  \"Sora概念\": \"AI\",\n  \"短剧互动游戏\": \"AI\",\n  \"多模态AI\": \"AI\",\n  \"数据要素\": \"AI\",\n  \"算力概念\": \"AI\",\n  \"MLOps概念\": \"AI\",\n  \"ChatGPT概念\": \"AI\",\n  \"AIGC概念\": \"AI\",\n  \"数据确权\": \"AI\",\n  \"Web3.0\": \"AI\",\n  \"虚拟数字人\": \"AI\",\n  \"数字水印\": \"AI\",\n  \"数据安全\": \"AI\",\n  \"云游戏\": \"AI\",\n  \"数字孪生\": \"AI\",\n  \"边缘计算\": \"AI\",\n  \"数据中心\": \"AI\",\n  \"华为概念\": \"AI\",\n  \"鸿蒙概念\": \"AI\",\n  \"华为欧拉\": \"AI\",\n  \"华为昇腾\": \"AI\",\n  \"国资云概念\": \"AI\",\n  \"东数西算\": \"AI\",\n  \"网络安全\": \"AI\",\n  \"元宇宙概念\": \"AI\",\n  \"NFT概念\": \"AI\",\n  \"信创\": \"AI\",\n  \"数字经济\": \"AI\",\n  \"区块链\": \"AI\",\n  \"智慧政务\": \"AI\",\n  \"数字货币\": \"AI\",\n  \"电子竞技\": \"AI\",\n  \"知识产权\": \"AI\",\n  \"时空大数据\": \"AI\",\n  \"低空经济\": \"低空经济\",\n  \"飞行汽车(eVTOL)\": \"低空经济\",\n  \"无人机\": \"低空经济\",\n  \"建筑节能\": \"房地产\",\n  \"REITs概念\": \"房地产\",\n  \"租售同权\": \"房地产\",\n  \"铁路基建\": \"房地产\",\n  \"PPP模式\": \"房地产\",\n  \"工程机械概念\": \"房地产\",\n  \"新型城镇化\": \"房地产\",\n  \"装配建筑\": \"房地产\",\n  \"地下管网\": \"房地产\",\n  \"民爆概念\": \"房地产\",\n  \"参股期货\": \"金融\",\n  \"参股券商\": \"金融\",\n  \"参股保险\": \"金融\",\n  \"跨境支付\": \"金融\",\n  \"互联金融\": \"金融\",\n  \"券商概念\": \"金融\",\n  \"移动支付\": \"金融\",\n  \"参股银行\": \"金融\",\n  \"粮食概念\": \"农业\",\n  \"水产养殖\": \"农业\",\n  \"生态农业\": \"农业\",\n  \"蝗虫防治\": \"农业\",\n  \"农业种植\": \"农业\",\n  \"鸡肉概念\": \"农业\",\n  \"转基因\": \"农业\",\n  \"人造肉\": \"农业\",\n  \"食品安全\": \"农业\",\n  \"猪肉概念\": \"农业\",\n  \"生物质能发电\": \"公用\",\n  \"噪声防治\": \"公用\",\n  \"土壤修复\": \"公用\",\n  \"地热能\": \"公用\",\n  \"海绵城市\": \"公用\",\n  \"节能环保\": \"公用\",\n  \"尾气治理\": \"公用\",\n  \"职业教育\": \"公用\",\n  \"医废处理\": \"公用\",\n  \"快递概念\": \"物流\",\n  \"RCEP概念\": \"物流\",\n  \"央企改革\": \"国企\",\n  \"中特估\": \"国企\",\n  \"中字头\": \"国企\",\n  \"沪企改革\": \"国企\",\n  \"国企改革\": \"国企\",\n  \"世界杯\": \"其他\",\n  \"东盟自贸区概念\": \"其他\",\n  \"娃哈哈概念\": \"其他\",\n  \"空气能热泵\": \"其他\",\n  \"核酸采样亭\": \"其他\",\n  \"中俄贸易概念\": \"其他\",\n  \"净水概念\": \"其他\",\n  \"京津冀\": \"其他\",\n  \"低价股\": \"其他\",\n  \"商汤概念\": \"其他\",\n  \"粤港自贸\": \"其他\",\n  \"土地流转\": \"其他\",\n  \"壳资源\": \"其他\",\n  \"盲盒经济\": \"其他\",\n  \"内贸流通\": \"其他\",\n  \"京东金融\": \"其他\",\n  \"乡村振兴\": \"其他\",\n  \"东北振兴\": \"其他\",\n  \"社区团购\": \"其他\",\n  \"地摊经济\": \"其他\",\n  \"快手概念\": \"其他\",\n  \"蚂蚁概念\": \"其他\",\n  \"证金持股\": \"其他\",\n  \"养老概念\": \"其他\",\n  \"冷链物流\": \"其他\",\n  \"贬值受益\": \"其他\",\n  \"纾困概念\": \"其他\",\n  \"阿里概念\": \"其他\",\n  \"深圳特区\": \"其他\",\n  \"超级品牌\": \"其他\",\n  \"中超概念\": \"其他\",\n  \"养老金\": \"其他\",\n  \"专精特新\": \"其他\",\n  \"统一大市场\": \"其他\",\n  \"光伏高速公路\": \"其他\",\n  \"核污染防治\": \"其他\",\n  \"磁悬浮概念\": \"其他\",\n  \"垃圾分类\": \"其他\",\n  \"电子烟\": \"其他\",\n  \"工业大麻\": \"其他\",\n  \"全息技术\": \"其他\",\n  \"超导概念\": \"其他\",\n  \"北交所概念\": \"其他\",\n  \"赛马概念\": \"其他\",\n  \"体育产业\": \"其他\",\n  \"雄安新区\": \"其他\",\n  \"共享经济\": \"其他\",\n  \"彩票概念\": \"其他\",\n  \"苹果概念\": \"其他\",\n  \"供销社概念\": \"其他\",\n  \"水利建设\": \"其他\",\n  \"3D打印\": \"其他\",\n  \"创投\": \"其他\",\n  \"字节概念\": \"其他\",\n  \"海洋经济\": \"其他\",\n  \"上海自贸\": \"其他\",\n  \"一带一路\": \"其他\",\n  \"碳交易\": \"其他\",\n  \"宠物经济\": \"其他\",\n  \"商业航天\": \"商业航天\",\n  \"航天概念\": \"商业航天\",\n  \"天基互联\": \"商业航天\",\n  \"北斗导航\": \"商业航天\",\n  \"通用航空\": \"商业航天\"\n}"
  },
  {
    "path": "src/zvt/resources/hk_industry_main_tag_mapping.json",
    "content": "{\n  \"地产\": \"房地产\",\n  \"建筑\": \"基建\",\n  \"工业工程\": \"基建\",\n  \"其他金融\": \"金融\",\n  \"药品及生物科技\": \"医药\",\n  \"医疗保健设备和服务\": \"医药\",\n  \"软件服务\": \"AI\",\n  \"电讯\": \"AI\",\n  \"专业零售\": \"大消费\",\n  \"旅游及消闲设施\": \"大消费\",\n  \"食物饮品\": \"大消费\",\n  \"纺织及服饰\": \"大消费\",\n  \"消费者主要零售商\": \"大消费\",\n  \"媒体及娱乐\": \"文化传媒\",\n  \"家庭电器及用品\": \"消费电子\",\n  \"资讯科技器材\": \"消费电子\",\n  \"工用运输\": \"物流\",\n  \"一般金属及矿石\": \"资源\",\n  \"原材料\": \"资源\",\n  \"汽车\": \"车路云\",\n  \"农业产品\": \"农业\",\n  \"煤炭\": \"资源\",\n  \"黄金及贵金属\": \"资源\",\n  \"石油及天然气\": \"资源\",\n  \"工用支援\": \"其他\",\n  \"综合企业\": \"其他\",\n  \"支援服务\": \"其他\"\n}"
  },
  {
    "path": "src/zvt/resources/industry_main_tag_mapping.json",
    "content": "{\n  \"风电设备\": \"新能源\",\n  \"电池\": \"新能源\",\n  \"光伏设备\": \"新能源\",\n  \"能源金属\": \"新能源\",\n  \"电源设备\": \"新能源\",\n  \"半导体\": \"半导体\",\n  \"电子化学品\": \"半导体\",\n  \"医疗服务\": \"医药\",\n  \"中药\": \"医药\",\n  \"化学制药\": \"医药\",\n  \"生物制品\": \"医药\",\n  \"医药商业\": \"医药\",\n  \"医疗器械\": \"医药\",\n  \"贸易行业\": \"大消费\",\n  \"家用轻工\": \"大消费\",\n  \"造纸印刷\": \"大消费\",\n  \"酿酒行业\": \"大消费\",\n  \"珠宝首饰\": \"大消费\",\n  \"美容护理\": \"大消费\",\n  \"食品饮料\": \"大消费\",\n  \"旅游酒店\": \"大消费\",\n  \"商业百货\": \"大消费\",\n  \"纺织服装\": \"大消费\",\n  \"家电行业\": \"大消费\",\n  \"航空机场\": \"大消费\",\n  \"小金属\": \"资源\",\n  \"贵金属\": \"资源\",\n  \"有色金属\": \"资源\",\n  \"煤炭行业\": \"资源\",\n  \"石油行业\": \"资源\",\n  \"燃气\": \"资源\",\n  \"采掘行业\": \"资源\",\n  \"消费电子\": \"消费电子\",\n  \"电子元件\": \"消费电子\",\n  \"光学光电子\": \"消费电子\",\n  \"汽车零部件\": \"车路云\",\n  \"汽车服务\": \"车路云\",\n  \"汽车整车\": \"车路云\",\n  \"交运设备\": \"车路云\",\n  \"电机\": \"智能机器\",\n  \"通用设备\": \"智能机器\",\n  \"专用设备\": \"智能机器\",\n  \"仪器仪表\": \"智能机器\",\n  \"电网设备\": \"电力\",\n  \"电力行业\": \"电力\",\n  \"房地产开发\": \"房地产\",\n  \"房地产服务\": \"房地产\",\n  \"工程建设\": \"房地产\",\n  \"水泥建材\": \"房地产\",\n  \"装修装饰\": \"房地产\",\n  \"装修建材\": \"房地产\",\n  \"工程咨询服务\": \"房地产\",\n  \"钢铁行业\": \"房地产\",\n  \"工程机械\": \"房地产\",\n  \"非金属材料\": \"化工\",\n  \"包装材料\": \"化工\",\n  \"化学制品\": \"化工\",\n  \"化肥行业\": \"化工\",\n  \"化学原料\": \"化工\",\n  \"化纤行业\": \"化工\",\n  \"塑料制品\": \"化工\",\n  \"玻璃玻纤\": \"化工\",\n  \"橡胶制品\": \"化工\",\n  \"证券\": \"金融\",\n  \"保险\": \"金融\",\n  \"银行\": \"金融\",\n  \"多元金融\": \"金融\",\n  \"通信服务\": \"AI\",\n  \"通信设备\": \"AI\",\n  \"互联网服务\": \"AI\",\n  \"软件开发\": \"AI\",\n  \"计算机设备\": \"AI\",\n  \"文化传媒\": \"文化传媒\",\n  \"教育\": \"文化传媒\",\n  \"游戏\": \"文化传媒\",\n  \"农牧饲渔\": \"农业\",\n  \"农药兽药\": \"农业\",\n  \"物流行业\": \"物流\",\n  \"航运港口\": \"物流\",\n  \"铁路公路\": \"物流\",\n  \"航天航空\": \"军工\",\n  \"船舶制造\": \"军工\",\n  \"环保行业\": \"环保\",\n  \"公用事业\": \"其他\",\n  \"专业服务\": \"其他\",\n  \"综合行业\": \"其他\"\n}"
  },
  {
    "path": "src/zvt/resources/log_conf.yaml",
    "content": "version: 1\ndisable_existing_loggers: False\nformatters:\n  default:\n    # \"()\": uvicorn.logging.DefaultFormatter\n    format: '%(asctime)s  %(levelname)s  %(threadName)s  %(message)s'\n  access:\n    # \"()\": uvicorn.logging.AccessFormatter\n    format: '%(asctime)s  %(levelname)s  %(threadName)s  %(message)s'\nhandlers:\n  default:\n    formatter: default\n    class: logging.StreamHandler\n    stream: ext://sys.stderr\n  file:\n    class: logging.handlers.RotatingFileHandler\n    formatter: default\n    filename: server.log\n    maxBytes: 524288000\n    level: INFO\n    backupCount: 10\n  access:\n    formatter: access\n    class: logging.StreamHandler\n    stream: ext://sys.stdout\nloggers:\n  uvicorn.error:\n    level: INFO\n    handlers:\n      - default\n    propagate: no\n  uvicorn.access:\n    level: INFO\n    handlers:\n      - access\n    propagate: no\nroot:\n  level: INFO\n  handlers:\n    - default\n    - file\n  propagate: no"
  },
  {
    "path": "src/zvt/resources/missed_concept.json",
    "content": "[\n  \"北京冬奥\",\n  \"长江三角\",\n  \"HS300_\",\n  \"股权激励\",\n  \"标准普尔\",\n  \"预盈预增\",\n  \"上证50_\",\n  \"预亏预减\",\n  \"上证180_\",\n  \"滨海新区\",\n  \"MSCI中国\",\n  \"沪股通\",\n  \"转债标的\",\n  \"昨日涨停_含一字\",\n  \"万达概念\",\n  \"成渝特区\",\n  \"ST股\",\n  \"机构重仓\",\n  \"IPO受益\",\n  \"参股新三板\",\n  \"破净股\",\n  \"上证380\",\n  \"宁组合\",\n  \"茅指数\",\n  \"深证100R\",\n  \"深股通\",\n  \"举牌\",\n  \"债转股\",\n  \"昨日连板_含一字\",\n  \"融资融券\",\n  \"央视50_\",\n  \"昨日触板\",\n  \"科创板做市股\",\n  \"QFII重仓\",\n  \"科创板做市商\",\n  \"独角兽\",\n  \"AB股\",\n  \"基金重仓\",\n  \"富士康\",\n  \"创业板综\",\n  \"中证500\",\n  \"次新股\",\n  \"富时罗素\",\n  \"百元股\",\n  \"创业成份\",\n  \"送转预期\",\n  \"B股\",\n  \"杭州亚运会\",\n  \"深成500\",\n  \"股权转让\",\n  \"社保重仓\",\n  \"昨日连板\",\n  \"微盘股\",\n  \"昨日涨停\",\n  \"GDR\",\n  \"分拆预期\",\n  \"高送转\",\n  \"湖北自贸\",\n  \"AH股\"\n]"
  },
  {
    "path": "src/zvt/resources/missed_industry.json",
    "content": "[]"
  },
  {
    "path": "src/zvt/resources/us_industry_main_tag_mapping.json",
    "content": "{\n  \"生物科技\": \"医药\",\n  \"制药\": \"医药\",\n  \"药品零售\": \"医药\",\n  \"医疗保健设备\": \"医药\",\n  \"医疗保健技术\": \"医药\",\n  \"生命科学工具和服务\": \"医药\",\n  \"医疗保健用品\": \"医药\",\n  \"投资银行业与经纪业\": \"金融\",\n  \"区域性银行\": \"金融\",\n  \"互助储蓄与抵押信贷金融服务\": \"金融\",\n  \"资产管理与托管银行\": \"金融\",\n  \"综合金融服务\": \"金融\",\n  \"综合性银行\": \"金融\",\n  \"财产与意外伤害保险\": \"金融\",\n  \"医疗保健房地产投资信托\": \"金融\",\n  \"多样化房地产投资信托\": \"金融\",\n  \"抵押房地产投资信托\": \"金融\",\n  \"特殊金融服务\": \"金融\",\n  \"金融交易所和数据\": \"金融\",\n  \"消费信贷\": \"金融\",\n  \"保险经纪商\": \"金融\",\n  \"综合性资本市场\": \"金融\",\n  \"人寿与健康保险\": \"金融\",\n  \"再保险\": \"金融\",\n  \"多元化保险\": \"金融\",\n  \"多领域控股\": \"金融\",\n  \"餐馆\": \"大消费\",\n  \"啤酒酿造商\": \"大消费\",\n  \"包装食品与肉类\": \"大消费\",\n  \"食品零售\": \"大消费\",\n  \"互联网与直销零售\": \"大消费\",\n  \"服装、服饰与奢侈品\": \"大消费\",\n  \"个人护理用品\": \"大消费\",\n  \"食品分销商\": \"大消费\",\n  \"服装零售\": \"大消费\",\n  \"保健护理服务\": \"大消费\",\n  \"消闲用品\": \"大消费\",\n  \"软饮料与不含酒精饮料\": \"大消费\",\n  \"酒店、度假村与豪华游轮\": \"大消费\",\n  \"鞋类\": \"大消费\",\n  \"酿酒商与葡萄酒商\": \"大消费\",\n  \"综合货品商店\": \"大消费\",\n  \"家用器具与特殊消费品\": \"大消费\",\n  \"纺织品\": \"大消费\",\n  \"日常消费品零售\": \"大消费\",\n  \"百货商店\": \"大消费\",\n  \"其他专卖店\": \"大消费\",\n  \"保健护理产品经销商\": \"大消费\",\n  \"经销商\": \"大消费\",\n  \"居家用品\": \"大消费\",\n  \"保健护理机构\": \"大消费\",\n  \"家庭装饰零售\": \"大消费\",\n  \"家庭装潢零售\": \"大消费\",\n  \"烟草\": \"大消费\",\n  \"特殊消费者服务\": \"大消费\",\n  \"家庭装饰品\": \"大消费\",\n  \"纸制品\": \"大消费\",\n  \"机场服务\": \"大消费\",\n  \"管理型保健护理\": \"大消费\",\n  \"办公服务与用品\": \"大消费\",\n  \"消费电子产品\": \"消费电子\",\n  \"电子元件\": \"消费电子\",\n  \"电脑与电子产品零售\": \"消费电子\",\n  \"家用电器\": \"消费电子\",\n  \"电气部件与设备\": \"消费电子\",\n  \"电子设备和仪器\": \"消费电子\",\n  \"电子制造服务\": \"消费电子\",\n  \"应用软件\": \"AI\",\n  \"互联网服务与基础设施\": \"AI\",\n  \"数据处理与外包服务\": \"AI\",\n  \"信息科技咨询与其它服务\": \"AI\",\n  \"通信设备\": \"AI\",\n  \"电脑硬件、储存设备及电脑周边\": \"AI\",\n  \"系统软件\": \"AI\",\n  \"综合电信业务\": \"AI\",\n  \"无线电信业务\": \"AI\",\n  \"技术产品经销商\": \"AI\",\n  \"半导体产品\": \"半导体\",\n  \"半导体材料与设备\": \"半导体\",\n  \"石油与天然气的勘探与生产\": \"资源\",\n  \"石油与天然气钻井\": \"资源\",\n  \"石油天然气设备与服务\": \"资源\",\n  \"石油与天然气的储存和运输\": \"资源\",\n  \"石油与天然气的炼制和营销\": \"资源\",\n  \"综合性石油与天然气企业\": \"资源\",\n  \"多种金属与采矿\": \"资源\",\n  \"贵重金属与矿石\": \"资源\",\n  \"铝\": \"资源\",\n  \"黄金\": \"资源\",\n  \"煤与消费用燃料\": \"资源\",\n  \"燃气公用事业\": \"资源\",\n  \"水公用事业\": \"资源\",\n  \"钢铁\": \"资源\",\n  \"金属、玻璃及塑料器皿\": \"资源\",\n  \"电力公用事业\": \"电力\",\n  \"新能源发电业者\": \"电力\",\n  \"独立电力生产商与能源贸易商\": \"电力\",\n  \"重型电气设备\": \"电力\",\n  \"航天航空与国防\": \"军工\",\n  \"安全和报警服务\": \"军工\",\n  \"电影与娱乐\": \"文化传媒\",\n  \"赌场与赌博\": \"文化传媒\",\n  \"互动媒体与服务\": \"文化传媒\",\n  \"出版\": \"文化传媒\",\n  \"广告\": \"文化传媒\",\n  \"广播\": \"文化传媒\",\n  \"教育服务\": \"文化传媒\",\n  \"互动家庭娱乐\": \"文化传媒\",\n  \"消闲设施\": \"文化传媒\",\n  \"调查和咨询服务\": \"文化传媒\",\n  \"有线和卫星电视\": \"文化传媒\",\n  \"房地产开发\": \"房地产\",\n  \"房地产服务\": \"房地产\",\n  \"住宅建筑\": \"房地产\",\n  \"建筑材料\": \"房地产\",\n  \"建筑与工程\": \"房地产\",\n  \"建筑产品\": \"房地产\",\n  \"建筑机械与重型运输设备\": \"房地产\",\n  \"房地产经营公司\": \"房地产\",\n  \"零售业房地产投资信托\": \"房地产\",\n  \"工业房地产投资信托\": \"房地产\",\n  \"酒店及度假村房地产投资信托\": \"房地产\",\n  \"特种房地产投资信托\": \"房地产\",\n  \"办公房地产投资信托\": \"房地产\",\n  \"多样化房地产活动\": \"房地产\",\n  \"住宅房地产投资信托\": \"房地产\",\n  \"汽车零售\": \"车路云\",\n  \"汽车制造商\": \"车路云\",\n  \"摩托车制造商\": \"车路云\",\n  \"汽车零件与设备\": \"车路云\",\n  \"陆运\": \"物流\",\n  \"航空货运与物流\": \"物流\",\n  \"客运航空公司\": \"物流\",\n  \"铁路\": \"物流\",\n  \"海上运输\": \"物流\",\n  \"公路与铁路\": \"物流\",\n  \"海港与服务\": \"物流\",\n  \"特种化学制品\": \"化工\",\n  \"多种化学制品\": \"化工\",\n  \"商品化工\": \"化工\",\n  \"化肥与农用药剂\": \"化工\",\n  \"轮胎与橡胶\": \"化工\",\n  \"工业气体\": \"化工\",\n  \"农用农业机械\": \"农业\",\n  \"林业产品\": \"农业\",\n  \"农产品与服务\": \"农业\",\n  \"环境与设施服务\": \"环保\",\n  \"贸易公司与经销商\": \"其他\",\n  \"工业机械、物料与部件\": \"其他\",\n  \"人力资源与就业服务\": \"其他\",\n  \"综合支持服务\": \"其他\",\n  \"复合型公用事业\": \"其他\",\n  \"纸质和塑料包装产品及材料\": \"其他\",\n  \"商业印刷\": \"其他\",\n  \"工业集团企业\": \"其他\",\n  \"非传统电信运营商\": \"其他\"\n}"
  },
  {
    "path": "src/zvt/rest/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "src/zvt/rest/data.py",
    "content": "# -*- coding: utf-8 -*-\nfrom fastapi import APIRouter\nfrom fastapi.encoders import jsonable_encoder\n\nimport zvt.contract as contract\nimport zvt.contract.api as contract_api\n\ndata_router = APIRouter(\n    prefix=\"/api/data\",\n    tags=[\"data\"],\n    responses={404: {\"description\": \"Not found\"}},\n)\n\n\n@data_router.get(\n    \"/providers\",\n    response_model=list,\n)\ndef get_data_providers():\n    \"\"\"\n    Get data providers\n    \"\"\"\n    return contract_api.get_providers()\n\n\n@data_router.get(\n    \"/schemas\",\n    response_model=list,\n)\ndef get_data_schemas(provider):\n    \"\"\"\n    Get schemas by provider\n    \"\"\"\n    return [schema.__name__ for schema in contract_api.get_schemas(provider=provider)]\n\n\n@data_router.get(\n    \"/query_data\",\n    response_model=list,\n)\ndef query_data(provider: str, schema: str):\n    \"\"\"\n    Get schemas by provider\n    \"\"\"\n    model: contract.Mixin = contract_api.get_schema_by_name(schema)\n    with contract_api.DBSession(provider=provider, data_schema=model)() as session:\n        return jsonable_encoder(model.query_data(session=session, limit=100, return_type=\"domain\"))\n"
  },
  {
    "path": "src/zvt/rest/factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nfrom fastapi import APIRouter\n\nfrom zvt.contract import zvt_context\nfrom zvt.factors import factor_service\nfrom zvt.factors.factor_models import FactorRequestModel, TradingSignalModel\n\nfactor_router = APIRouter(\n    prefix=\"/api/factor\",\n    tags=[\"factor\"],\n    responses={404: {\"description\": \"Not found\"}},\n)\n\n\n@factor_router.get(\"/get_factors\", response_model=List[str])\ndef get_factors():\n    return [name for name in zvt_context.factor_cls_registry.keys()]\n\n\n@factor_router.post(\"/query_factor_result\", response_model=List[TradingSignalModel])\ndef query_factor_result(factor_request_model: FactorRequestModel):\n    return factor_service.query_factor_result(factor_request_model)\n"
  },
  {
    "path": "src/zvt/rest/misc.py",
    "content": "# -*- coding: utf-8 -*-\nfrom fastapi import APIRouter\n\nfrom zvt.misc import misc_service\nfrom zvt.misc.misc_models import TimeMessage\n\nmisc_router = APIRouter(\n    prefix=\"/api/misc\",\n    tags=[\"misc\"],\n    responses={404: {\"description\": \"Not found\"}},\n)\n\n\n@misc_router.get(\n    \"/time_message\",\n    response_model=TimeMessage,\n)\ndef get_time_message():\n    \"\"\"\n    Get time message\n    \"\"\"\n    return misc_service.get_time_message()\n"
  },
  {
    "path": "src/zvt/rest/trading.py",
    "content": "import platform\nfrom typing import List, Optional\n\nfrom fastapi import APIRouter, HTTPException\nfrom fastapi_pagination import Page\n\nimport zvt.contract.api as contract_api\nimport zvt.trading.trading_service as trading_service\nfrom zvt.common.trading_models import BuyParameter, SellParameter, TradingResult\nfrom zvt.tag.tag_schemas import MainTagInfo\nfrom zvt.trading.trading_models import (\n    BuildTradingPlanModel,\n    TradingPlanModel,\n    QueryTradingPlanModel,\n    QueryStockQuoteModel,\n    StockQuoteStatsModel,\n    QueryStockQuoteSettingModel,\n    BuildQueryStockQuoteSettingModel,\n    QueryTagQuoteModel,\n    TagQuoteStatsModel,\n    KdataModel,\n    KdataRequestModel,\n    TSModel,\n    TSRequestModel,\n    QuoteStatsModel,\n)\nfrom zvt.trading.trading_schemas import QueryStockQuoteSetting\n\ntrading_router = APIRouter(\n    prefix=\"/api/trading\",\n    tags=[\"trading\"],\n    # dependencies=[Depends(get_current_user)],\n    responses={404: {\"description\": \"Not found\"}},\n)\n\n\n@trading_router.post(\"/query_kdata\", response_model=Optional[List[KdataModel]])\ndef query_kdata(kdata_request_model: KdataRequestModel):\n    return trading_service.query_kdata(kdata_request_model)\n\n\n@trading_router.post(\"/query_ts\", response_model=Optional[List[TSModel]])\ndef query_kdata(ts_request_model: TSRequestModel):\n    return trading_service.query_ts(ts_request_model)\n\n\n@trading_router.get(\"/get_quote_stats\", response_model=Optional[QuoteStatsModel])\ndef get_quote_stats():\n    return trading_service.query_quote_stats()\n\n\n@trading_router.get(\"/get_query_stock_quote_setting\", response_model=Optional[QueryStockQuoteSettingModel])\ndef get_query_stock_quote_setting():\n    with contract_api.DBSession(provider=\"zvt\", data_schema=QueryStockQuoteSetting)() as session:\n        query_setting: List[QueryStockQuoteSetting] = QueryStockQuoteSetting.query_data(\n            session=session, return_type=\"domain\"\n        )\n        df = MainTagInfo.query_data(return_type=\"df\")\n        tags = df[\"tag\"].tolist()\n\n        if query_setting:\n            return QueryStockQuoteSettingModel(stock_pool_name=query_setting[0].stock_pool_name, main_tags=tags)\n        return QueryStockQuoteSettingModel(stock_pool_name=\"A股\", main_tags=tags)\n\n\n@trading_router.post(\"/build_query_stock_quote_setting\", response_model=QueryStockQuoteSettingModel)\ndef build_query_stock_quote_setting(build_query_stock_quote_setting_model: BuildQueryStockQuoteSettingModel):\n    return trading_service.build_query_stock_quote_setting(build_query_stock_quote_setting_model)\n\n\n@trading_router.post(\"/query_tag_quotes\", response_model=List[TagQuoteStatsModel])\ndef query_tag_quotes(query_tag_quote_model: QueryTagQuoteModel):\n    return trading_service.query_tag_quotes(query_tag_quote_model)\n\n\n@trading_router.post(\"/query_stock_quotes\", response_model=Optional[StockQuoteStatsModel])\ndef query_stock_quotes(query_stock_quote_model: QueryStockQuoteModel):\n    return trading_service.query_stock_quotes(query_stock_quote_model)\n\n\n@trading_router.post(\"/build_trading_plan\", response_model=TradingPlanModel)\ndef build_trading_plan(build_trading_plan_model: BuildTradingPlanModel):\n    return trading_service.build_trading_plan(build_trading_plan_model)\n\n\n@trading_router.post(\"/query_trading_plan\", response_model=Page[TradingPlanModel])\ndef query_trading_plan(query_trading_plan_model: QueryTradingPlanModel):\n    return trading_service.query_trading_plan(query_trading_plan_model)\n\n\n@trading_router.get(\"/get_current_trading_plan\", response_model=List[TradingPlanModel])\ndef get_current_trading_plan():\n    return trading_service.get_current_trading_plan()\n\n\n@trading_router.get(\"/get_future_trading_plan\", response_model=List[TradingPlanModel])\ndef get_future_trading_plan():\n    return trading_service.get_future_trading_plan()\n\n\n@trading_router.post(\"/buy\", response_model=TradingResult)\ndef buy(buy_position_strategy: BuyParameter):\n    if platform.system() == \"Windows\":\n        from zvt.broker.qmt.context import qmt_context\n\n        return qmt_context.qmt_account.buy(buy_position_strategy)\n    else:\n        raise HTTPException(status_code=500, detail=\"Please use qmt in windows! \")\n\n\n@trading_router.post(\"/sell\", response_model=TradingResult)\ndef sell(sell_position_strategy: SellParameter):\n    if platform.system() == \"Windows\":\n        from zvt.broker.qmt.context import qmt_context\n\n        return qmt_context.qmt_account.sell(sell_position_strategy)\n    else:\n        raise HTTPException(status_code=500, detail=\"Please use qmt in windows! \")\n"
  },
  {
    "path": "src/zvt/rest/work.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List, Optional\n\nfrom fastapi import APIRouter\n\nimport zvt.contract.api as contract_api\nimport zvt.tag.tag_service as tag_service\nfrom zvt.domain import Stock\nfrom zvt.tag.common import TagType\nfrom zvt.tag.tag_models import (\n    TagInfoModel,\n    CreateTagInfoModel,\n    StockTagsModel,\n    SimpleStockTagsModel,\n    SetStockTagsModel,\n    CreateStockPoolInfoModel,\n    StockPoolInfoModel,\n    CreateStockPoolsModel,\n    StockPoolsModel,\n    QueryStockTagStatsModel,\n    StockTagStatsModel,\n    QueryStockTagsModel,\n    QuerySimpleStockTagsModel,\n    ActivateSubTagsResultModel,\n    ActivateSubTagsModel,\n    BatchSetStockTagsModel,\n    StockTagOptions,\n    MainTagIndustryRelation,\n    MainTagSubTagRelation,\n    IndustryInfoModel,\n    ChangeMainTagModel,\n    BuildMainTagIndustryRelationModel,\n    BuildMainTagSubTagRelationModel,\n)\nfrom zvt.tag.tag_schemas import (\n    StockTags,\n    MainTagInfo,\n    SubTagInfo,\n    HiddenTagInfo,\n    StockPoolInfo,\n    StockPools,\n    IndustryInfo,\n)\nfrom zvt.utils.time_utils import current_date\n\nwork_router = APIRouter(\n    prefix=\"/api/work\",\n    tags=[\"work\"],\n    # dependencies=[Depends(get_current_user)],\n    responses={404: {\"description\": \"Not found\"}},\n)\n\n\n@work_router.post(\"/create_stock_pool_info\", response_model=StockPoolInfoModel)\ndef create_stock_pool_info(create_stock_pool_info_model: CreateStockPoolInfoModel):\n    return tag_service.build_stock_pool_info(create_stock_pool_info_model, timestamp=current_date())\n\n\n@work_router.get(\"/get_stock_pool_info\", response_model=List[StockPoolInfoModel])\ndef get_stock_pool_info():\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockPoolInfo)() as session:\n        stock_pool_info: List[StockPoolInfo] = StockPoolInfo.query_data(session=session, return_type=\"domain\")\n        return stock_pool_info\n\n\n@work_router.post(\"/create_stock_pools\", response_model=StockPoolsModel)\ndef create_stock_pools(create_stock_pools_model: CreateStockPoolsModel):\n    return tag_service.build_stock_pool(create_stock_pools_model, current_date())\n\n\n@work_router.delete(\"/delete_stock_pool\", response_model=str)\ndef delete_stock_pool(stock_pool_name: str):\n    return tag_service.delete_stock_pool(stock_pool_name=stock_pool_name)\n\n\n@work_router.get(\"/get_stock_pools\", response_model=Optional[StockPoolsModel])\ndef get_stock_pools(stock_pool_name: str):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockPools)() as session:\n        stock_pools: List[StockPools] = StockPools.query_data(\n            session=session,\n            filters=[StockPools.stock_pool_name == stock_pool_name],\n            order=StockPools.timestamp.desc(),\n            limit=1,\n            return_type=\"domain\",\n        )\n        if stock_pools:\n            return stock_pools[0]\n        return None\n\n\n@work_router.get(\"/get_main_tag_info\", response_model=List[TagInfoModel])\ndef get_main_tag_info():\n    \"\"\"\n    Get main_tag info\n    \"\"\"\n    with contract_api.DBSession(provider=\"zvt\", data_schema=MainTagInfo)() as session:\n        tags_info: List[MainTagInfo] = MainTagInfo.query_data(session=session, return_type=\"domain\")\n        return tags_info\n\n\n@work_router.get(\"/get_sub_tag_info\", response_model=List[TagInfoModel])\ndef get_sub_tag_info():\n    \"\"\"\n    Get sub_tag info\n    \"\"\"\n    with contract_api.DBSession(provider=\"zvt\", data_schema=SubTagInfo)() as session:\n        tags_info: List[SubTagInfo] = SubTagInfo.query_data(session=session, return_type=\"domain\")\n        return tags_info\n\n\n@work_router.get(\"/get_main_tag_sub_tag_relation\", response_model=MainTagSubTagRelation)\ndef get_main_tag_sub_tag_relation(main_tag):\n    return tag_service.get_main_tag_sub_tag_relation(main_tag=main_tag)\n\n\n@work_router.get(\"/get_industry_info\", response_model=List[IndustryInfoModel])\ndef get_industry_info():\n    \"\"\"\n    Get industry info\n    \"\"\"\n    with contract_api.DBSession(provider=\"zvt\", data_schema=IndustryInfo)() as session:\n        industry_info: List[IndustryInfo] = IndustryInfo.query_data(session=session, return_type=\"domain\")\n        return industry_info\n\n\n@work_router.get(\"/get_main_tag_industry_relation\", response_model=MainTagIndustryRelation)\ndef get_main_tag_industry_relation(main_tag):\n    return tag_service.get_main_tag_industry_relation(main_tag=main_tag)\n\n\n@work_router.get(\"/get_hidden_tag_info\", response_model=List[TagInfoModel])\ndef get_hidden_tag_info():\n    \"\"\"\n    Get hidden_tag info\n    \"\"\"\n    with contract_api.DBSession(provider=\"zvt\", data_schema=MainTagInfo)() as session:\n        tags_info: List[HiddenTagInfo] = HiddenTagInfo.query_data(session=session, return_type=\"domain\")\n        return tags_info\n\n\n@work_router.post(\"/create_main_tag_info\", response_model=TagInfoModel)\ndef create_main_tag_info(tag_info: CreateTagInfoModel):\n    return tag_service.create_tag_info(tag_info, tag_type=TagType.main_tag)\n\n\n@work_router.delete(\"/delete_main_tag\", response_model=str)\ndef delete_main_tag(tag: str):\n    tag_service.delete_tag(tag=tag, tag_type=TagType.main_tag)\n    return \"ok\"\n\n\n@work_router.post(\"/create_sub_tag_info\", response_model=TagInfoModel)\ndef create_sub_tag_info(tag_info: CreateTagInfoModel):\n    return tag_service.create_tag_info(tag_info, TagType.sub_tag)\n\n\n@work_router.delete(\"/delete_sub_tag\", response_model=str)\ndef delete_sub_tag(tag: str):\n    tag_service.delete_tag(tag=tag, tag_type=TagType.sub_tag)\n    return \"ok\"\n\n\n@work_router.post(\"/create_hidden_tag_info\", response_model=TagInfoModel)\ndef create_hidden_tag_info(tag_info: CreateTagInfoModel):\n    return tag_service.create_tag_info(tag_info, TagType.hidden_tag)\n\n\n@work_router.delete(\"/delete_hidden_tag\", response_model=str)\ndef delete_hidden_tag(tag: str):\n    tag_service.delete_tag(tag=tag, tag_type=TagType.hidden_tag)\n    return \"ok\"\n\n\n@work_router.post(\"/query_stock_tags\", response_model=List[StockTagsModel])\ndef query_stock_tags(query_stock_tags_model: QueryStockTagsModel):\n    \"\"\"\n    Get entity tags\n    \"\"\"\n    filters = [StockTags.entity_id.in_(query_stock_tags_model.entity_ids)]\n\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        tags: List[StockTags] = StockTags.query_data(\n            session=session, filters=filters, return_type=\"domain\", order=StockTags.timestamp.desc()\n        )\n        tags_dict = {tag.entity_id: tag for tag in tags}\n        sorted_tags = [tags_dict[entity_id] for entity_id in query_stock_tags_model.entity_ids]\n        return sorted_tags\n\n\n@work_router.post(\"/query_simple_stock_tags\", response_model=List[SimpleStockTagsModel])\ndef query_simple_stock_tags(query_simple_stock_tags_model: QuerySimpleStockTagsModel):\n    \"\"\"\n    Get simple entity tags\n    \"\"\"\n\n    entity_ids = query_simple_stock_tags_model.entity_ids\n\n    filters = [StockTags.entity_id.in_(entity_ids)]\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        tags: List[dict] = StockTags.query_data(\n            session=session, filters=filters, return_type=\"dict\", order=StockTags.timestamp.desc()\n        )\n        entity_tag_map = {item[\"entity_id\"]: item for item in tags}\n        result_tags = []\n        stocks = Stock.query_data(provider=\"em\", entity_ids=[tag[\"entity_id\"] for tag in tags], return_type=\"domain\")\n        stocks_map = {item.entity_id: item for item in stocks}\n        for entity_id in entity_ids:\n            tag = entity_tag_map.get(entity_id)\n            if not tag:\n                continue\n            tag[\"name\"] = stocks_map.get(entity_id).name\n            if stocks_map.get(entity_id).controlling_holder_parent:\n                tag[\"controlling_holder_parent\"] = stocks_map.get(entity_id).controlling_holder_parent\n            else:\n                tag[\"controlling_holder_parent\"] = stocks_map.get(entity_id).controlling_holder\n            tag[\"top_ten_ratio\"] = stocks_map.get(entity_id).top_ten_ratio\n            result_tags.append(tag)\n        return result_tags\n\n\n@work_router.get(\"/get_stock_tag_options\", response_model=StockTagOptions)\ndef get_stock_tag_options(entity_id: str):\n    \"\"\"\n    Get stock tag options\n    \"\"\"\n    return tag_service.get_stock_tag_options(entity_id=entity_id)\n\n\n@work_router.post(\"/set_stock_tags\", response_model=StockTagsModel)\ndef set_stock_tags(set_stock_tags_model: SetStockTagsModel):\n    \"\"\"\n    Set stock tags\n    \"\"\"\n    return tag_service.build_stock_tags(\n        set_stock_tags_model=set_stock_tags_model, timestamp=current_date(), set_by_user=True\n    )\n\n\n@work_router.post(\"/build_stock_tags\", response_model=List[StockTagsModel])\ndef build_stock_tags(set_stock_tags_model_list: List[SetStockTagsModel]):\n    \"\"\"\n    Set stock tags in batch\n    \"\"\"\n    return [\n        tag_service.build_stock_tags(\n            set_stock_tags_model=set_stock_tags_model, timestamp=current_date(), set_by_user=True\n        )\n        for set_stock_tags_model in set_stock_tags_model_list\n    ]\n\n\n@work_router.post(\"/query_stock_tag_stats\", response_model=List[StockTagStatsModel])\ndef query_stock_tag_stats(query_stock_tag_stats_model: QueryStockTagStatsModel):\n    \"\"\"\n    Get stock tag stats\n    \"\"\"\n\n    return tag_service.query_stock_tag_stats(query_stock_tag_stats_model=query_stock_tag_stats_model)\n\n\n@work_router.get(\"/get_main_tags_in_stock_pool\", response_model=List[str])\ndef get_main_tags_in_stock_pool(stock_pool_name: str):\n    \"\"\"\n    Get stock tag stats\n    \"\"\"\n\n    return tag_service.get_main_tags_in_stock_pool(stock_pool_name)\n\n\n@work_router.post(\"/activate_sub_tags\", response_model=ActivateSubTagsResultModel)\ndef activate_sub_tags(activate_sub_tags_model: ActivateSubTagsModel):\n    \"\"\"\n    Activate sub tags\n    \"\"\"\n\n    return tag_service.activate_sub_tags(activate_sub_tags_model=activate_sub_tags_model)\n\n\n@work_router.post(\"/batch_set_stock_tags\", response_model=List[StockTagsModel])\ndef batch_set_stock_tags(batch_set_stock_tags_model: BatchSetStockTagsModel):\n    return tag_service.batch_set_stock_tags(batch_set_stock_tags_model=batch_set_stock_tags_model)\n\n\n@work_router.post(\"/build_main_tag_industry_relation\", response_model=str)\ndef build_main_tag_industry_relation(build_relation_model: BuildMainTagIndustryRelationModel):\n    tag_service.build_main_tag_industry_relation(build_relation_model=build_relation_model)\n    return \"success\"\n\n\n@work_router.post(\"/build_main_tag_sub_tag_relation\", response_model=str)\ndef build_main_tag_sub_tag_relation(build_relation_model: BuildMainTagSubTagRelationModel):\n    tag_service.build_main_tag_sub_tag_relation(build_relation_model=build_relation_model)\n    return \"ok\"\n\n\n@work_router.post(\"/change_main_tag\", response_model=List[StockTagsModel])\ndef change_main_tag(change_main_tag_model: ChangeMainTagModel):\n    return tag_service.change_main_tag(change_main_tag_model=change_main_tag_model)\n"
  },
  {
    "path": "src/zvt/samples/__init__.py",
    "content": "# -*- coding: utf-8 -*-\nfrom .stock_traders import *\n"
  },
  {
    "path": "src/zvt/samples/stock_traders.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.contract import IntervalLevel\nfrom zvt.factors.ma.ma_factor import CrossMaFactor\nfrom zvt.factors.macd.macd_factor import BullFactor\nfrom zvt.trader.trader import StockTrader\n\n\nclass MyMaTrader(StockTrader):\n    def init_factors(\n        self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, adjust_type=None\n    ):\n        return [\n            CrossMaFactor(\n                entity_ids=entity_ids,\n                entity_schema=entity_schema,\n                exchanges=exchanges,\n                codes=codes,\n                start_timestamp=start_timestamp,\n                end_timestamp=end_timestamp,\n                windows=[5, 10],\n                need_persist=False,\n                adjust_type=adjust_type,\n            )\n        ]\n\n\nclass MyBullTrader(StockTrader):\n    def init_factors(\n        self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, adjust_type=None\n    ):\n        return [\n            BullFactor(\n                entity_ids=entity_ids,\n                entity_schema=entity_schema,\n                exchanges=exchanges,\n                codes=codes,\n                start_timestamp=start_timestamp,\n                end_timestamp=end_timestamp,\n                adjust_type=adjust_type,\n            )\n        ]\n\n\nif __name__ == \"__main__\":\n    # single stock with cross ma factor\n    MyMaTrader(\n        codes=[\"000338\"],\n        level=IntervalLevel.LEVEL_1DAY,\n        start_timestamp=\"2018-01-01\",\n        end_timestamp=\"2019-06-30\",\n        trader_name=\"000338_ma_trader\",\n    ).run()\n\n    # single stock with bull factor\n    # MyBullTrader(codes=['000338'], level=IntervalLevel.LEVEL_1DAY, start_timestamp='2018-01-01',\n    #              end_timestamp='2019-06-30', trader_name='000338_bull_trader').run()\n\n    #  multiple stocks with cross ma factor\n    # MyMaTrader(codes=SAMPLE_STOCK_CODES, level=IntervalLevel.LEVEL_1DAY, start_timestamp='2018-01-01',\n    #            end_timestamp='2019-06-30', trader_name='sample_stocks_ma_trader').run()\n\n    # multiple stocks with bull factor\n    # MyBullTrader(codes=SAMPLE_STOCK_CODES, level=IntervalLevel.LEVEL_1DAY, start_timestamp='2018-01-01',\n    #              end_timestamp='2019-06-30', trader_name='sample_stocks_bull_trader').run()\n"
  },
  {
    "path": "src/zvt/sched/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "src/zvt/sched/sched.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport os\n\nfrom apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor\nfrom apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt import ZVT_HOME\n\nlogger = logging.getLogger(__name__)\n\njobs_db_path = os.path.join(ZVT_HOME, \"jobs.db\")\n\n\njobstores = {\"default\": SQLAlchemyJobStore(url=f\"sqlite:///{jobs_db_path}\")}\n\nexecutors = {\"default\": ThreadPoolExecutor(20), \"processpool\": ProcessPoolExecutor(5)}\njob_defaults = {\"coalesce\": False, \"max_instances\": 1}\n\nzvt_scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults)\n\n\ndef sched_tasks():\n    import platform\n\n    if platform.system() == \"Windows\":\n        try:\n            from zvt.broker.qmt.qmt_quote import record_stock_quote\n\n            zvt_scheduler.add_job(func=record_stock_quote, trigger=\"cron\", hour=9, minute=19, day_of_week=\"mon-fri\")\n        except Exception as e:\n            logger.error(\"QMT not work\", e)\n    else:\n        logger.warning(\"QMT need run in Windows!\")\n\n    zvt_scheduler.start()\n\n\nif __name__ == \"__main__\":\n    sched_tasks()\n"
  },
  {
    "path": "src/zvt/tag/__init__.py",
    "content": "# -*- coding: utf-8 -*-#\n\n\n# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/tag/ai_suggestion.py",
    "content": "# -*- coding: utf-8 -*-\nimport json\nimport logging\nimport re\nfrom typing import List\n\nimport pandas as pd\nfrom openai import OpenAI\nfrom sqlalchemy import func, or_\n\nimport zvt.contract.api as contract_api\nfrom zvt import zvt_config\nfrom zvt.domain import StockNews, Stock\nfrom zvt.tag.tag_utils import match_tag\nfrom zvt.utils.time_utils import date_time_by_interval, current_date\n\nlogger = logging.getLogger(__name__)\n\n\ndef normalize_tag_suggestions(tag_suggestions):\n    for direction in [\"up\", \"down\"]:\n        if direction in tag_suggestions:\n            for item in tag_suggestions[direction]:\n                tag_type, tag = match_tag(item[\"block\"])\n                item[\"tag\"] = tag\n                item[\"tag_type\"] = tag_type\n                if item[\"stocks\"]:\n                    stocks = Stock.query_data(\n                        filters=[Stock.name.in_(item[\"stocks\"])], return_type=\"dict\", provider=\"em\"\n                    )\n                    if len(stocks) != len(item[\"stocks\"]):\n                        logger.warning(\n                            f\"Stocks not found in zvt:{set(item['stocks']) - set([item['name'] for item in stocks])}\"\n                        )\n                    item[\"stocks\"] = [{\"entity_id\": item[\"entity_id\"], \"name\": item[\"name\"]} for item in stocks]\n    return tag_suggestions\n\n\ndef set_stock_news_tag_suggestions(stock_news, tag_suggestions, session):\n    if stock_news.news_analysis:\n        stock_news.news_analysis = dict(stock_news.news_analysis)\n    else:\n        stock_news.news_analysis = {}\n\n    result = normalize_tag_suggestions(tag_suggestions)\n    logger.debug(result)\n    stock_news.news_analysis[\"tag_suggestions\"] = result\n    session.add(stock_news)\n    session.commit()\n\n\ndef build_tag_suggestions(entity_id):\n    with contract_api.DBSession(provider=\"em\", data_schema=StockNews)() as session:\n        start_date = date_time_by_interval(current_date(), -30)\n        datas: List[StockNews] = StockNews.query_data(\n            entity_id=entity_id,\n            limit=1,\n            order=StockNews.timestamp.desc(),\n            filters=[\n                StockNews.timestamp >= start_date,\n                func.json_extract(StockNews.news_analysis, f'$.\"tag_suggestions\"') != None,\n            ],\n            return_type=\"domain\",\n        )\n        if datas:\n            latest_data = datas[0]\n        else:\n            latest_data = None\n\n        filters = [\n            or_(\n                StockNews.news_title.like(\"%上涨%\"),\n                StockNews.news_title.like(\"%拉升%\"),\n                StockNews.news_title.like(\"%涨停%\"),\n                StockNews.news_title.like(\"%下跌%\"),\n                StockNews.news_title.like(\"%跌停%\"),\n            ),\n            StockNews.timestamp >= start_date,\n            func.json_extract(StockNews.news_analysis, f'$.\"tag_suggestions\"') == None,\n        ]\n        if latest_data:\n            filters = filters + [\n                StockNews.timestamp >= latest_data.timestamp,\n                StockNews.news_code != latest_data.news_code,\n            ]\n\n        stock_news_list: List[StockNews] = StockNews.query_data(\n            entity_id=entity_id,\n            session=session,\n            order=StockNews.news_code.asc(),\n            return_type=\"domain\",\n            filters=filters,\n        )\n\n        if not stock_news_list:\n            logger.info(\"all stock news has been analyzed\")\n            return\n\n        example = {\n            \"up\": [{\"block\": \"block_a\", \"stocks\": [\"stock_a\", \"stock_b\"]}],\n            \"down\": [{\"block\": \"block_b\", \"stocks\": [\"stock_1\", \"stock_2\"]}],\n        }\n\n        client = OpenAI(\n            api_key=zvt_config[\"qwen_api_key\"],\n            base_url=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n        )\n\n        for stock_news in stock_news_list:\n            # same news\n            if latest_data and (stock_news.news_code == latest_data.news_code):\n                tag_suggestions = latest_data.news_analysis.get(\"tag_suggestions\")\n                if tag_suggestions:\n                    set_stock_news_tag_suggestions(stock_news, tag_suggestions, session)\n                    continue\n\n            news_title = stock_news.news_title\n            news_content = stock_news.news_content\n            logger.info(news_title)\n            logger.info(news_content)\n\n            completion = client.chat.completions.create(\n                model=\"qwen-max\",\n                messages=[\n                    {\n                        \"role\": \"system\",\n                        \"content\": f\"请从新闻标题和内容中识别是上涨还是下跌，提取相应的板块和个股，按照格式: {example} 输出一个 JSON 对象\",\n                    },\n                    {\n                        \"role\": \"user\",\n                        \"content\": f\"新闻标题:{news_title}, 新闻内容:{news_content}\",\n                    },\n                ],\n                temperature=0.2,\n            )\n            content = completion.choices[0].message.content\n            content = content.replace(\"```json\", \"\")\n            content = content.replace(\"```\", \"\")\n            content = re.sub(r\"\\s+\", \"\", content)\n            logger.info(f\"message content: {content}\")\n            tag_suggestions = json.loads(content)\n            set_stock_news_tag_suggestions(stock_news, tag_suggestions, session)\n\n\ndef extract_info(tag_dict):\n    extracted_info = []\n    for key, value in tag_dict.items():\n        for item in value:\n            extracted_info.append({\"tag\": item[\"tag\"], \"stocks\": [stock[\"name\"] for stock in item[\"stocks\"]]})\n    return extracted_info\n\n\ndef build_tag_suggestions_stats():\n    with contract_api.DBSession(provider=\"em\", data_schema=StockNews)() as session:\n        start_date = date_time_by_interval(current_date(), -10)\n        stock_news_list: List[StockNews] = StockNews.query_data(\n            session=session,\n            order=StockNews.timestamp.desc(),\n            distinct=StockNews.news_code,\n            return_type=\"dict\",\n            filters=[\n                StockNews.timestamp >= start_date,\n                func.json_extract(StockNews.news_analysis, f'$.\"tag_suggestions\"') != None,\n            ],\n        )\n        datas = []\n        for stock_news in stock_news_list:\n            tag_suggestions = stock_news[\"news_analysis\"].get(\"tag_suggestions\")\n            if tag_suggestions:\n                for key in (\"up\", \"down\"):\n                    suggestions = tag_suggestions.get(key)\n                    if suggestions:\n                        datas = datas + [\n                            {\n                                \"tag\": item[\"tag\"],\n                                \"tag_type\": item[\"tag_type\"],\n                                \"entity_ids\": [stock[\"entity_id\"] for stock in item[\"stocks\"]],\n                                \"stock_names\": [stock[\"name\"] for stock in item[\"stocks\"]],\n                            }\n                            for item in suggestions\n                        ]\n        df = pd.DataFrame.from_records(data=datas)\n        grouped_df = (\n            df.groupby(\"tag\")\n            .agg(\n                tag_count=(\"tag\", \"count\"),\n                tag_type=(\"tag_type\", \"first\"),\n                entity_ids=(\"entity_ids\", \"sum\"),\n                stock_names=(\"stock_names\", \"sum\"),\n            )\n            .reset_index()\n        )\n        grouped_df[\"entity_ids\"] = grouped_df[\"entity_ids\"].apply(set).apply(list)\n        grouped_df[\"stock_names\"] = grouped_df[\"stock_names\"].apply(set).apply(list)\n        grouped_df[\"entity_ids_count\"] = grouped_df[\"entity_ids\"].apply(len)\n\n        sorted_df = grouped_df.sort_values(by=[\"tag_count\", \"entity_ids_count\"], ascending=[False, False])\n        return sorted_df.to_dict(orient=\"records\")\n\n\nif __name__ == \"__main__\":\n    build_tag_suggestions_stats()\n"
  },
  {
    "path": "src/zvt/tag/common.py",
    "content": "# -*- coding: utf-8 -*-\nfrom enum import Enum\n\n\nclass StockPoolType(Enum):\n    system = \"system\"\n    custom = \"custom\"\n    dynamic = \"dynamic\"\n\n\nclass DynamicPoolType(Enum):\n    limit_up = \"limit_up\"\n    limit_down = \"limit_down\"\n\n\nclass InsertMode(Enum):\n    overwrite = \"overwrite\"\n    append = \"append\"\n\n\nclass TagType(Enum):\n    #: A tag is a main tag due to its extensive capacity.\n    main_tag = \"main_tag\"\n    sub_tag = \"sub_tag\"\n    hidden_tag = \"hidden_tag\"\n\n\nclass TagStatsQueryType(Enum):\n    simple = \"simple\"\n    details = \"details\"\n\n\n# the __all__ is generated\n__all__ = [\"StockPoolType\", \"DynamicPoolType\", \"InsertMode\", \"TagType\", \"TagStatsQueryType\"]\n"
  },
  {
    "path": "src/zvt/tag/tag_models.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import Dict, Union, List, Optional\n\nfrom pydantic import field_validator, Field\nfrom pydantic_core.core_schema import ValidationInfo\n\nfrom zvt.contract.model import MixinModel, CustomModel\nfrom zvt.tag.common import StockPoolType, TagType, TagStatsQueryType, InsertMode\nfrom zvt.tag.tag_utils import get_stock_pool_names\n\n\nclass TagInfoModel(MixinModel):\n    tag: str\n    tag_reason: Optional[str] = Field(default=None)\n    main_tag: Optional[str] = Field(default=None)\n\n\nclass CreateTagInfoModel(CustomModel):\n    tag: str\n    tag_reason: Optional[str] = Field(default=None)\n\n\nclass IndustryInfoModel(MixinModel):\n    industry_name: str\n    description: str\n    # related main tag\n    main_tag: str\n\n\nclass MainTagIndustryRelation(CustomModel):\n    main_tag: str\n    industry_list: List[str]\n\n\nclass BuildMainTagIndustryRelationModel(CustomModel):\n    main_tag: str\n    industry_list: List[str]\n    activate: bool = Field(default=False)\n\n\nclass MainTagSubTagRelation(CustomModel):\n    main_tag: str\n    sub_tag_list: List[str]\n\n\nclass BuildMainTagSubTagRelationModel(CustomModel):\n    main_tag: str\n    sub_tag_list: List[str]\n    activate: bool = Field(default=False)\n\n\nclass ChangeMainTagModel(CustomModel):\n    current_main_tag: str\n    new_main_tag: str\n\n\nclass StockTagsModel(MixinModel):\n    main_tag: Optional[str] = Field(default=None)\n    main_tag_reason: Optional[str] = Field(default=None)\n    main_tags: Dict[str, str]\n\n    sub_tag: Optional[str] = Field(default=None)\n    sub_tag_reason: Optional[str] = Field(default=None)\n    sub_tags: Union[Dict[str, str], None]\n\n    active_hidden_tags: Union[Dict[str, str], None]\n    hidden_tags: Union[Dict[str, str], None]\n    set_by_user: bool = False\n\n\nclass SimpleStockTagsModel(CustomModel):\n    entity_id: str\n    name: str\n    main_tag: Optional[str] = Field(default=None)\n    main_tag_reason: Optional[str] = Field(default=None)\n    main_tags: Dict[str, str]\n    sub_tag: Union[str, None]\n    sub_tag_reason: Optional[str] = Field(default=None)\n    sub_tags: Union[Dict[str, str], None]\n    active_hidden_tags: Union[Dict[str, str], None]\n    controlling_holder_parent: Optional[str] = Field(default=None)\n    top_ten_ratio: Optional[float] = Field(default=None)\n\n\nclass QueryStockTagsModel(CustomModel):\n    entity_ids: List[str]\n\n\nclass QuerySimpleStockTagsModel(CustomModel):\n    entity_ids: List[str]\n\n\nclass BatchSetStockTagsModel(CustomModel):\n    entity_ids: List[str]\n    tag: str\n    tag_reason: Optional[str] = Field(default=None)\n    tag_type: TagType\n\n\nclass TagParameter(CustomModel):\n    main_tag: str\n    main_tag_reason: Optional[str] = Field(default=None)\n    sub_tag: Optional[str] = Field(default=None)\n    sub_tag_reason: Optional[str] = Field(default=None)\n    hidden_tag: Optional[str] = Field(default=None)\n    hidden_tag_reason: Optional[str] = Field(default=None)\n\n\nclass StockTagOptions(CustomModel):\n    main_tag: Optional[str] = Field(default=None)\n    sub_tag: Optional[str] = Field(default=None)\n    # hidden_tags: Optional[List[str]] = Field(default=None)\n    active_hidden_tags: Optional[Dict[str, str]] = Field(default=None)\n    main_tag_options: List[CreateTagInfoModel]\n    sub_tag_options: List[CreateTagInfoModel]\n    hidden_tag_options: List[CreateTagInfoModel]\n\n\nclass SetStockTagsModel(CustomModel):\n    entity_id: str\n    main_tag: str\n    main_tag_reason: Optional[str] = Field(default=None)\n    sub_tag: Optional[str] = Field(default=None)\n    sub_tag_reason: Optional[str] = Field(default=None)\n    active_hidden_tags: Optional[Dict[str, str]] = Field(default=None)\n\n    # @field_validator(\"main_tag\")\n    # @classmethod\n    # def main_tag_must_be_in(cls, v: str) -> str:\n    #     if v not in get_main_tags():\n    #         raise ValueError(f\"main_tag: {v} must be created at main_tag_info at first\")\n    #     return v\n    #\n    # @field_validator(\"sub_tag\")\n    # @classmethod\n    # def sub_tag_must_be_in(cls, v: str) -> str:\n    #     if v and (v not in get_sub_tags()):\n    #         raise ValueError(f\"sub_tag: {v} must be created at sub_tag_info at first\")\n    #     return v\n    #\n    # @field_validator(\"active_hidden_tags\")\n    # @classmethod\n    # def hidden_tag_must_be_in(cls, v: Union[Dict[str, str], None]) -> Union[Dict[str, str], None]:\n    #     if v:\n    #         for item in v.keys():\n    #             if item not in get_hidden_tags():\n    #                 raise ValueError(f\"hidden_tag: {v} must be created at hidden_tag_info at first\")\n    #     return v\n\n\nclass StockPoolModel(MixinModel):\n    stock_pool_name: str\n    entity_ids: List[str]\n\n\nclass StockPoolInfoModel(MixinModel):\n    stock_pool_type: StockPoolType\n    stock_pool_name: str\n\n\nclass CreateStockPoolInfoModel(CustomModel):\n    stock_pool_type: StockPoolType\n    stock_pool_name: str\n\n    @field_validator(\"stock_pool_name\")\n    @classmethod\n    def stock_pool_name_existed(cls, v: str) -> str:\n        if v in get_stock_pool_names():\n            raise ValueError(f\"stock_pool_name: {v} has been used\")\n        return v\n\n\nclass StockPoolsModel(MixinModel):\n    stock_pool_name: str\n    entity_ids: List[str]\n\n\nclass CreateStockPoolsModel(CustomModel):\n    entity_type: str = Field(default=\"stock\")\n    stock_pool_name: str\n    entity_ids: List[str]\n    insert_mode: InsertMode = Field(default=InsertMode.overwrite)\n\n    # @field_validator(\"stock_pool_name\")\n    # @classmethod\n    # def stock_pool_name_must_be_in(cls, v: str) -> str:\n    #     if v:\n    #         if v not in get_stock_pool_names():\n    #             raise ValueError(f\"stock_pool_name: {v} must be created at stock_pool_info at first\")\n    #     return v\n\n\nclass QueryStockTagStatsModel(CustomModel):\n    stock_pool_name: Optional[str] = Field(default=None)\n    entity_ids: Optional[List[str]] = Field(default=None)\n    query_type: Optional[TagStatsQueryType] = Field(default=TagStatsQueryType.details)\n\n    @field_validator(\"stock_pool_name\", \"entity_ids\")\n    @classmethod\n    def phone_or_mobile_must_set_only_one(cls, v, validation_info: ValidationInfo, **kwargs):\n        if validation_info.field_name == \"stock_pool_name\":\n            other_field = \"entity_ids\"\n        else:\n            other_field = \"stock_pool_name\"\n\n        other_value = kwargs.get(other_field)\n\n        if v and other_value:\n            raise ValueError(f\"Only one of 'stock_pool_name' or 'entity_ids' should be set.\")\n        elif not v and not other_value:\n            raise ValueError(\"Either 'stock_pool_name' or 'entity_ids' must be set.\")\n\n        return v\n\n    @field_validator(\"stock_pool_name\")\n    @classmethod\n    def stock_pool_name_must_be_in(cls, v: str) -> str:\n        if v:\n            if v not in get_stock_pool_names():\n                raise ValueError(f\"stock_pool_name: {v} not existed\")\n        return v\n\n\nclass StockTagDetailsModel(CustomModel):\n    entity_id: str\n    main_tag: Optional[str] = Field(default=None)\n    sub_tag: Optional[str] = Field(default=None)\n    hidden_tags: Union[List[str], None]\n\n    #: 代码\n    code: str\n    #: 名字\n    name: str\n    #: 减持\n    recent_reduction: Optional[bool] = Field(default=None)\n    #: 增持\n    recent_acquisition: Optional[bool] = Field(default=None)\n    #: 解禁\n    recent_unlock: Optional[bool] = Field(default=None)\n    #: 增发配股\n    recent_additional_or_rights_issue: Optional[bool] = Field(default=None)\n    #: 业绩利好\n    recent_positive_earnings_news: Optional[bool] = Field(default=None)\n    #: 业绩利空\n    recent_negative_earnings_news: Optional[bool] = Field(default=None)\n    #: 上榜次数\n    recent_dragon_and_tiger_count: Optional[int] = Field(default=None)\n    #: 违规行为\n    recent_violation_alert: Optional[bool] = Field(default=None)\n    #: 利好\n    recent_positive_news: Optional[bool] = Field(default=None)\n    #: 利空\n    recent_negative_news: Optional[bool] = Field(default=None)\n    #: 新闻总结\n    recent_news_summary: Optional[Dict[str, str]] = Field(default=None)\n\n\nclass StockTagStatsModel(MixinModel):\n    main_tag: str\n    turnover: Optional[float] = Field(default=None)\n    entity_count: Optional[int] = Field(default=None)\n    position: Optional[int] = Field(default=None)\n    is_main_line: Optional[bool] = Field(default=None)\n    main_line_continuous_days: Optional[int] = Field(default=None)\n    entity_ids: Optional[List[str]] = Field(default=None)\n    stock_details: Optional[List[StockTagDetailsModel]] = Field(default=None)\n\n\nclass ActivateSubTagsModel(CustomModel):\n    sub_tags: List[str]\n\n\nclass ActivateSubTagsResultModel(CustomModel):\n    tag_entity_ids: Dict[str, Union[List[str], None]]\n\n\n# the __all__ is generated\n__all__ = [\n    \"TagInfoModel\",\n    \"CreateTagInfoModel\",\n    \"StockTagsModel\",\n    \"SimpleStockTagsModel\",\n    \"QueryStockTagsModel\",\n    \"QuerySimpleStockTagsModel\",\n    \"BatchSetStockTagsModel\",\n    \"TagParameter\",\n    \"StockTagOptions\",\n    \"SetStockTagsModel\",\n    \"StockPoolModel\",\n    \"StockPoolInfoModel\",\n    \"CreateStockPoolInfoModel\",\n    \"StockPoolsModel\",\n    \"CreateStockPoolsModel\",\n    \"QueryStockTagStatsModel\",\n    \"StockTagDetailsModel\",\n    \"StockTagStatsModel\",\n    \"ActivateSubTagsModel\",\n    \"ActivateSubTagsResultModel\",\n]\n"
  },
  {
    "path": "src/zvt/tag/tag_schemas.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sqlalchemy import Column, String, JSON, Boolean, Float, Integer\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nStockTagsBase = declarative_base()\n\n\nclass IndustryInfo(StockTagsBase, Mixin):\n    __tablename__ = \"industry_info\"\n\n    industry_name = Column(String, unique=True)\n    description = Column(String)\n    # related main tag\n    main_tag = Column(String)\n\n\nclass MainTagInfo(StockTagsBase, Mixin):\n    __tablename__ = \"main_tag_info\"\n\n    tag = Column(String, unique=True)\n    tag_reason = Column(String)\n\n\nclass SubTagInfo(StockTagsBase, Mixin):\n    __tablename__ = \"sub_tag_info\"\n\n    tag = Column(String, unique=True)\n    tag_reason = Column(String)\n\n    # related main tag\n    main_tag = Column(String)\n\n\nclass HiddenTagInfo(StockTagsBase, Mixin):\n    __tablename__ = \"hidden_tag_info\"\n\n    tag = Column(String, unique=True)\n    tag_reason = Column(String)\n\n\nclass StockTags(StockTagsBase, Mixin):\n    \"\"\"\n    Schema for storing stock tags\n    \"\"\"\n\n    __tablename__ = \"stock_tags\"\n\n    entity_type = Column(String(length=64))\n\n    code = Column(String(length=64))\n    name = Column(String(length=128))\n\n    main_tag = Column(String)\n    main_tag_reason = Column(String)\n    main_tags = Column(JSON)\n\n    sub_tag = Column(String)\n    sub_tag_reason = Column(String)\n    sub_tags = Column(JSON)\n\n    active_hidden_tags = Column(JSON)\n    hidden_tags = Column(JSON)\n    set_by_user = Column(Boolean, default=False)\n\n\nclass StockSystemTags(StockTagsBase, Mixin):\n    __tablename__ = \"stock_system_tags\"\n    #: 编码\n    code = Column(String(length=64))\n    #: 名字\n    name = Column(String(length=128))\n    #: 减持\n    recent_reduction = Column(Boolean)\n    #: 增持\n    recent_acquisition = Column(Boolean)\n    #: 解禁\n    recent_unlock = Column(Boolean)\n    #: 增发配股\n    recent_additional_or_rights_issue = Column(Boolean)\n    #: 业绩利好\n    recent_positive_earnings_news = Column(Boolean)\n    #: 业绩利空\n    recent_negative_earnings_news = Column(Boolean)\n    #: 上榜次数\n    recent_dragon_and_tiger_count = Column(Integer)\n    #: 违规行为\n    recent_violation_alert = Column(Boolean)\n    #: 利好\n    recent_positive_news = Column(Boolean)\n    #: 利空\n    recent_negative_news = Column(Boolean)\n    #: 新闻总结\n    recent_news_summary = Column(JSON)\n\n\nclass StockPoolInfo(StockTagsBase, Mixin):\n    __tablename__ = \"stock_pool_info\"\n    stock_pool_type = Column(String)\n    stock_pool_name = Column(String, unique=True)\n\n\nclass StockPools(StockTagsBase, Mixin):\n    __tablename__ = \"stock_pools\"\n\n    entity_type = Column(String(length=64))\n\n    stock_pool_name = Column(String)\n    entity_ids = Column(JSON)\n\n\nclass TagStats(StockTagsBase, Mixin):\n    __tablename__ = \"tag_stats\"\n\n    entity_type = Column(String(length=64))\n\n    stock_pool_name = Column(String)\n    main_tag = Column(String)\n    turnover = Column(Float)\n    entity_count = Column(Integer)\n    position = Column(Integer)\n    is_main_line = Column(Boolean)\n    main_line_continuous_days = Column(Integer)\n    entity_ids = Column(JSON)\n\n\nregister_schema(db_name=\"stock_tags\", schema_base=StockTagsBase, internal=True)\n\n\n# the __all__ is generated\n__all__ = [\n    \"IndustryInfo\",\n    \"MainTagInfo\",\n    \"SubTagInfo\",\n    \"HiddenTagInfo\",\n    \"StockTags\",\n    \"StockSystemTags\",\n    \"StockPoolInfo\",\n    \"StockPools\",\n    \"TagStats\",\n]\n"
  },
  {
    "path": "src/zvt/tag/tag_service.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nfrom typing import List\n\nimport pandas as pd\nfrom fastapi import HTTPException\nfrom sqlalchemy import func, text\n\nimport zvt.contract.api as contract_api\nfrom zvt.api.selector import get_entity_ids_by_filter\nfrom zvt.contract.api import decode_entity_id\nfrom zvt.domain import BlockStock, Block, Stock, Stockus, Stockhk\nfrom zvt.tag.common import TagType, TagStatsQueryType, StockPoolType, InsertMode\nfrom zvt.tag.tag_models import (\n    SetStockTagsModel,\n    CreateStockPoolInfoModel,\n    CreateStockPoolsModel,\n    QueryStockTagStatsModel,\n    ActivateSubTagsModel,\n    BatchSetStockTagsModel,\n    TagParameter,\n    CreateTagInfoModel,\n    StockTagOptions,\n    ChangeMainTagModel,\n    BuildMainTagIndustryRelationModel,\n)\nfrom zvt.tag.tag_schemas import (\n    StockTags,\n    StockPools,\n    StockPoolInfo,\n    TagStats,\n    StockSystemTags,\n    MainTagInfo,\n    SubTagInfo,\n    HiddenTagInfo,\n    IndustryInfo,\n)\nfrom zvt.tag.tag_utils import (\n    get_sub_tags,\n    get_stock_pool_names,\n    get_main_tag_by_sub_tag,\n    get_main_tag_by_industry,\n)\nfrom zvt.utils.time_utils import to_pd_timestamp, to_date_time_str, current_date, now_pd_timestamp\nfrom zvt.utils.utils import fill_dict, compare_dicts, flatten_list\n\nlogger = logging.getLogger(__name__)\n\n\ndef _stock_tags_need_update(stock_tags: StockTags, set_stock_tags_model: SetStockTagsModel):\n    if (\n        stock_tags.main_tag != set_stock_tags_model.main_tag\n        or stock_tags.main_tag_reason != set_stock_tags_model.main_tag_reason\n        or stock_tags.sub_tag != set_stock_tags_model.sub_tag\n        or stock_tags.sub_tag_reason != set_stock_tags_model.sub_tag_reason\n        or not compare_dicts(stock_tags.active_hidden_tags, set_stock_tags_model.active_hidden_tags)\n    ):\n        return True\n    return False\n\n\ndef get_stock_tag_options(entity_id: str) -> StockTagOptions:\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        datas: List[StockTags] = StockTags.query_data(\n            entity_id=entity_id, order=StockTags.timestamp.desc(), limit=1, return_type=\"domain\", session=session\n        )\n        main_tag_options = []\n        sub_tag_options = []\n        hidden_tag_options = []\n\n        main_tag = None\n        sub_tag = None\n        active_hidden_tags = None\n        stock_tags = None\n        if datas:\n            stock_tags = datas[0]\n            main_tag = stock_tags.main_tag\n            sub_tag = stock_tags.sub_tag\n\n            if stock_tags.main_tags:\n                main_tag_options = [\n                    CreateTagInfoModel(tag=tag, tag_reason=tag_reason)\n                    for tag, tag_reason in stock_tags.main_tags.items()\n                ]\n\n            if stock_tags.sub_tags:\n                sub_tag_options = [\n                    CreateTagInfoModel(tag=tag, tag_reason=tag_reason)\n                    for tag, tag_reason in stock_tags.sub_tags.items()\n                ]\n\n            if stock_tags.active_hidden_tags:\n                active_hidden_tags = stock_tags.active_hidden_tags\n\n            if stock_tags.hidden_tags:\n                hidden_tag_options = [\n                    CreateTagInfoModel(tag=tag, tag_reason=tag_reason)\n                    for tag, tag_reason in stock_tags.hidden_tags.items()\n                ]\n\n        main_tags_info: List[MainTagInfo] = MainTagInfo.query_data(session=session, return_type=\"domain\")\n        if not main_tag:\n            main_tag = main_tags_info[0].tag\n\n        main_tag_options = main_tag_options + [\n            CreateTagInfoModel(tag=item.tag, tag_reason=item.tag_reason)\n            for item in main_tags_info\n            if not stock_tags or (not stock_tags.main_tags) or (item.tag not in stock_tags.main_tags)\n        ]\n\n        sub_tags_info: List[SubTagInfo] = SubTagInfo.query_data(session=session, return_type=\"domain\")\n        if not sub_tag:\n            sub_tag = sub_tags_info[0].tag\n        sub_tag_options = sub_tag_options + [\n            CreateTagInfoModel(tag=item.tag, tag_reason=item.tag_reason)\n            for item in sub_tags_info\n            if not stock_tags or (not stock_tags.sub_tags) or (item.tag not in stock_tags.sub_tags)\n        ]\n\n        hidden_tags_info: List[HiddenTagInfo] = HiddenTagInfo.query_data(session=session, return_type=\"domain\")\n        hidden_tag_options = hidden_tag_options + [\n            CreateTagInfoModel(tag=item.tag, tag_reason=item.tag_reason)\n            for item in hidden_tags_info\n            if not stock_tags or (not stock_tags.hidden_tags) or (item.tag not in stock_tags.hidden_tags)\n        ]\n\n        return StockTagOptions(\n            main_tag=main_tag,\n            sub_tag=sub_tag,\n            active_hidden_tags=active_hidden_tags,\n            main_tag_options=main_tag_options,\n            sub_tag_options=sub_tag_options,\n            hidden_tag_options=hidden_tag_options,\n        )\n\n\ndef build_stock_tags(\n    set_stock_tags_model: SetStockTagsModel, timestamp: pd.Timestamp, set_by_user: bool, keep_current=False\n):\n    logger.info(set_stock_tags_model)\n\n    main_tag_info = CreateTagInfoModel(\n        tag=set_stock_tags_model.main_tag, tag_reason=set_stock_tags_model.main_tag_reason\n    )\n    if not is_tag_info_existed(tag=main_tag_info.tag, tag_type=TagType.main_tag):\n        create_tag_info(tag_info=main_tag_info, tag_type=TagType.main_tag)\n\n    if set_stock_tags_model.sub_tag:\n        sub_tag_info = CreateTagInfoModel(\n            tag=set_stock_tags_model.sub_tag, tag_reason=set_stock_tags_model.sub_tag_reason\n        )\n        if not is_tag_info_existed(tag=sub_tag_info.tag, tag_type=TagType.sub_tag):\n            create_tag_info(tag_info=sub_tag_info, tag_type=TagType.sub_tag)\n\n    if set_stock_tags_model.active_hidden_tags:\n        for tag in set_stock_tags_model.active_hidden_tags:\n            hidden_tag_info = CreateTagInfoModel(tag=tag, tag_reason=set_stock_tags_model.active_hidden_tags.get(tag))\n            if not is_tag_info_existed(tag=hidden_tag_info.tag, tag_type=TagType.hidden_tag):\n                create_tag_info(tag_info=hidden_tag_info, tag_type=TagType.hidden_tag)\n\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        entity_id = set_stock_tags_model.entity_id\n        main_tags = {}\n        sub_tags = {}\n        hidden_tags = {}\n\n        entity_type, _, _ = decode_entity_id(entity_id)\n\n        datas = StockTags.query_data(\n            session=session,\n            entity_id=entity_id,\n            filters=[StockTags.entity_type == entity_type],\n            limit=1,\n            return_type=\"domain\",\n        )\n\n        if datas:\n            current_stock_tags: StockTags = datas[0]\n\n            # nothing change\n            if not _stock_tags_need_update(current_stock_tags, set_stock_tags_model):\n                logger.info(f\"Not change stock_tags for {set_stock_tags_model.entity_id}\")\n                return current_stock_tags\n\n            if current_stock_tags.main_tags:\n                main_tags = dict(current_stock_tags.main_tags)\n            if current_stock_tags.sub_tags:\n                sub_tags = dict(current_stock_tags.sub_tags)\n            if current_stock_tags.hidden_tags:\n                hidden_tags = dict(current_stock_tags.hidden_tags)\n\n        else:\n            current_stock_tags = StockTags(\n                entity_type=entity_type,\n                id=f\"{entity_id}_tags\",\n                entity_id=entity_id,\n                timestamp=timestamp,\n            )\n\n        # update tag\n        if not keep_current:\n            current_stock_tags.main_tag = set_stock_tags_model.main_tag\n            current_stock_tags.main_tag_reason = set_stock_tags_model.main_tag_reason\n\n            if set_stock_tags_model.sub_tag:\n                current_stock_tags.sub_tag = set_stock_tags_model.sub_tag\n            if set_stock_tags_model.sub_tag_reason:\n                current_stock_tags.sub_tag_reason = set_stock_tags_model.sub_tag_reason\n            # could update to None\n            current_stock_tags.active_hidden_tags = set_stock_tags_model.active_hidden_tags\n        # update tags\n        main_tags[set_stock_tags_model.main_tag] = set_stock_tags_model.main_tag_reason\n        if set_stock_tags_model.sub_tag:\n            sub_tags[set_stock_tags_model.sub_tag] = set_stock_tags_model.sub_tag_reason\n        if set_stock_tags_model.active_hidden_tags:\n            for k, v in set_stock_tags_model.active_hidden_tags.items():\n                hidden_tags[k] = v\n        current_stock_tags.main_tags = main_tags\n        current_stock_tags.sub_tags = sub_tags\n        current_stock_tags.hidden_tags = hidden_tags\n\n        current_stock_tags.set_by_user = set_by_user\n\n        session.add(current_stock_tags)\n        session.commit()\n        session.refresh(current_stock_tags)\n        return current_stock_tags\n\n\ndef build_tag_parameter(tag_type: TagType, tag, tag_reason, stock_tag: StockTags):\n    hidden_tag = None\n    hidden_tag_reason = None\n\n    if tag_type == TagType.main_tag:\n        main_tag = tag\n        if main_tag in stock_tag.main_tags:\n            main_tag_reason = stock_tag.main_tags.get(main_tag, tag_reason)\n        else:\n            main_tag_reason = tag_reason\n        sub_tag = stock_tag.sub_tag\n        sub_tag_reason = stock_tag.sub_tag_reason\n    elif tag_type == TagType.sub_tag:\n        sub_tag = tag\n        if sub_tag in stock_tag.sub_tags:\n            sub_tag_reason = stock_tag.sub_tags.get(sub_tag, tag_reason)\n        else:\n            sub_tag_reason = tag_reason\n        main_tag = stock_tag.main_tag\n        main_tag_reason = stock_tag.main_tag_reason\n    elif tag_type == TagType.hidden_tag:\n        hidden_tag = tag\n        if stock_tag.hidden_tags and (hidden_tag in stock_tag.hidden_tags):\n            hidden_tag_reason = stock_tag.hidden_tags.get(hidden_tag, tag_reason)\n        else:\n            hidden_tag_reason = tag_reason\n\n        sub_tag = stock_tag.sub_tag\n        sub_tag_reason = stock_tag.sub_tag_reason\n\n        main_tag = stock_tag.main_tag\n        main_tag_reason = stock_tag.main_tag_reason\n\n    else:\n        assert False\n\n    return TagParameter(\n        main_tag=main_tag,\n        main_tag_reason=main_tag_reason,\n        sub_tag=sub_tag,\n        sub_tag_reason=sub_tag_reason,\n        hidden_tag=hidden_tag,\n        hidden_tag_reason=hidden_tag_reason,\n    )\n\n\ndef batch_set_stock_tags(batch_set_stock_tags_model: BatchSetStockTagsModel):\n    if not batch_set_stock_tags_model.entity_ids:\n        return []\n\n    tag_info = CreateTagInfoModel(tag=batch_set_stock_tags_model.tag, tag_reason=batch_set_stock_tags_model.tag_reason)\n    if not is_tag_info_existed(tag=tag_info.tag, tag_type=batch_set_stock_tags_model.tag_type):\n        create_tag_info(tag_info=tag_info, tag_type=batch_set_stock_tags_model.tag_type)\n\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        tag_type = batch_set_stock_tags_model.tag_type\n        if tag_type == TagType.main_tag:\n            main_tag = batch_set_stock_tags_model.tag\n            stock_tags: List[StockTags] = StockTags.query_data(\n                entity_ids=batch_set_stock_tags_model.entity_ids,\n                filters=[StockTags.main_tag != main_tag],\n                session=session,\n                return_type=\"domain\",\n            )\n        elif tag_type == TagType.sub_tag:\n            sub_tag = batch_set_stock_tags_model.tag\n            stock_tags: List[StockTags] = StockTags.query_data(\n                entity_ids=batch_set_stock_tags_model.entity_ids,\n                filters=[StockTags.sub_tag != sub_tag],\n                session=session,\n                return_type=\"domain\",\n            )\n        elif tag_type == TagType.hidden_tag:\n            hidden_tag = batch_set_stock_tags_model.tag\n            stock_tags: List[StockTags] = StockTags.query_data(\n                entity_ids=batch_set_stock_tags_model.entity_ids,\n                # 需要sqlite3版本>=3.37.0\n                filters=[func.json_extract(StockTags.active_hidden_tags, f'$.\"{hidden_tag}\"') == None],\n                session=session,\n                return_type=\"domain\",\n            )\n\n        for stock_tag in stock_tags:\n            tag_parameter: TagParameter = build_tag_parameter(\n                tag_type=tag_type,\n                tag=batch_set_stock_tags_model.tag,\n                tag_reason=batch_set_stock_tags_model.tag_reason,\n                stock_tag=stock_tag,\n            )\n            if tag_type == TagType.hidden_tag:\n                active_hidden_tags = {batch_set_stock_tags_model.tag: batch_set_stock_tags_model.tag_reason}\n            else:\n                active_hidden_tags = stock_tag.active_hidden_tags\n\n            set_stock_tags_model = SetStockTagsModel(\n                entity_id=stock_tag.entity_id,\n                main_tag=tag_parameter.main_tag,\n                main_tag_reason=tag_parameter.main_tag_reason,\n                sub_tag=tag_parameter.sub_tag,\n                sub_tag_reason=tag_parameter.sub_tag_reason,\n                active_hidden_tags=active_hidden_tags,\n            )\n\n            build_stock_tags(\n                set_stock_tags_model=set_stock_tags_model,\n                timestamp=now_pd_timestamp(),\n                set_by_user=True,\n                keep_current=False,\n            )\n            session.refresh(stock_tag)\n        return stock_tags\n\n\ndef build_default_main_tag(entity_type=\"stock\", entity_ids=None, force_rebuild=False):\n    \"\"\"\n    build default main tag by industry\n\n    :param entity_type:\n    :param entity_ids: entity ids\n    :param force_rebuild: always rebuild it if True otherwise only build which not existed\n    \"\"\"\n    if not entity_ids:\n        entity_ids = get_entity_ids_by_filter(\n            entity_type=entity_type, provider=\"em\", ignore_delist=True, ignore_st=False, ignore_new_stock=False\n        )\n\n    if entity_type == \"stock\":\n        df_block = Block.query_data(provider=\"em\", filters=[Block.category == \"industry\"])\n        industry_codes = df_block[\"code\"].tolist()\n        block_stocks: List[BlockStock] = BlockStock.query_data(\n            provider=\"em\",\n            filters=[BlockStock.code.in_(industry_codes), BlockStock.stock_id.in_(entity_ids)],\n            return_type=\"domain\",\n        )\n        entity_industry_mapping = {block_stock.stock_id: block_stock.name for block_stock in block_stocks}\n    elif entity_type == \"stockus\":\n        datas: List[Stockus] = Stockus.query_data(entity_ids=entity_ids, return_type=\"domain\")\n        entity_industry_mapping = {item.entity_id: item.industry for item in datas}\n    elif entity_type == \"stockhk\":\n        datas: List[Stockhk] = Stockhk.query_data(entity_ids=entity_ids, return_type=\"domain\")\n        entity_industry_mapping = {item.entity_id: item.industry for item in datas}\n    else:\n        raise ValueError(f\"Unsupported entity_type: {entity_type}\")\n\n    for entity_id in entity_ids:\n        stock_tags: List[StockTags] = StockTags.query_data(entity_id=entity_id, return_type=\"domain\")\n        if not force_rebuild and stock_tags:\n            logger.info(f\"{entity_id} main tag has been set.\")\n            continue\n\n        logger.info(f\"build main tag for: {entity_id}\")\n\n        industry = entity_industry_mapping.get(entity_id)\n        if industry:\n            main_tag = get_main_tag_by_industry(industry_name=industry)\n            main_tag_reason = f\"来自行业:{industry}\"\n        else:\n            main_tag = \"其他\"\n            main_tag_reason = \"其他\"\n\n        build_stock_tags(\n            set_stock_tags_model=SetStockTagsModel(\n                entity_id=entity_id,\n                main_tag=main_tag,\n                main_tag_reason=main_tag_reason,\n                sub_tag=None,\n                sub_tag_reason=None,\n                active_hidden_tags=None,\n            ),\n            timestamp=now_pd_timestamp(),\n            set_by_user=False,\n            keep_current=False,\n        )\n\n\ndef build_default_sub_tags(entity_ids=None):\n    if not entity_ids:\n        entity_ids = get_entity_ids_by_filter(\n            provider=\"em\", ignore_delist=True, ignore_st=False, ignore_new_stock=False\n        )\n\n    for entity_id in entity_ids:\n        logger.info(f\"build sub tag for: {entity_id}\")\n        datas = StockTags.query_data(entity_id=entity_id, limit=1, return_type=\"domain\")\n        if not datas:\n            raise AssertionError(f\"Main tag must be set at first for {entity_id}\")\n\n        current_stock_tags: StockTags = datas[0]\n        keep_current = False\n        if current_stock_tags.set_by_user:\n            logger.info(f\"keep current tags set by user for: {entity_id}\")\n            keep_current = True\n\n        current_sub_tag = current_stock_tags.sub_tag\n        filters = [BlockStock.stock_id == entity_id]\n        if current_sub_tag:\n            logger.info(f\"{entity_id} current_sub_tag: {current_sub_tag}\")\n            current_sub_tags = current_stock_tags.sub_tags.keys()\n            filters = filters + [BlockStock.name.notin_(current_sub_tags)]\n\n        df_block = Block.query_data(provider=\"em\", filters=[Block.category == \"concept\"])\n        concept_codes = df_block[\"code\"].tolist()\n        filters = filters + [BlockStock.code.in_(concept_codes)]\n\n        block_stocks: List[BlockStock] = BlockStock.query_data(\n            provider=\"em\",\n            filters=filters,\n            return_type=\"domain\",\n        )\n        if not block_stocks:\n            logger.info(f\"no block_stocks for: {entity_id}\")\n            continue\n\n        for block_stock in block_stocks:\n            sub_tag = block_stock.name\n            if sub_tag in get_sub_tags():\n                sub_tag_reason = f\"来自概念:{sub_tag}\"\n\n                main_tag = get_main_tag_by_sub_tag(sub_tag)\n                main_tag_reason = sub_tag_reason\n                if (main_tag == \"其他\" or not main_tag) and current_stock_tags.main_tag:\n                    main_tag = current_stock_tags.main_tag\n                    main_tag_reason = current_stock_tags.main_tag_reason\n\n                build_stock_tags(\n                    set_stock_tags_model=SetStockTagsModel(\n                        entity_id=entity_id,\n                        main_tag=main_tag,\n                        main_tag_reason=main_tag_reason,\n                        sub_tag=sub_tag,\n                        sub_tag_reason=sub_tag_reason,\n                        active_hidden_tags=current_stock_tags.active_hidden_tags,\n                    ),\n                    timestamp=now_pd_timestamp(),\n                    set_by_user=False,\n                    keep_current=keep_current,\n                )\n            else:\n                logger.info(f\"ignore {sub_tag} not in sub_tag_info yet\")\n\n\ndef get_tag_info_schema(tag_type: TagType):\n    if tag_type == TagType.main_tag:\n        data_schema = MainTagInfo\n    elif tag_type == TagType.sub_tag:\n        data_schema = SubTagInfo\n    elif tag_type == TagType.hidden_tag:\n        data_schema = HiddenTagInfo\n    else:\n        assert False\n\n    return data_schema\n\n\ndef is_tag_info_existed(tag: str, tag_type: TagType):\n    data_schema = get_tag_info_schema(tag_type=tag_type)\n    with contract_api.DBSession(provider=\"zvt\", data_schema=data_schema)() as session:\n        current_tags_info = data_schema.query_data(\n            session=session, filters=[data_schema.tag == tag], return_type=\"domain\"\n        )\n        if current_tags_info:\n            return True\n        return False\n\n\ndef create_tag_info(tag_info: CreateTagInfoModel, tag_type: TagType):\n    \"\"\"\n    Create tags info\n    \"\"\"\n    if is_tag_info_existed(tag=tag_info.tag, tag_type=tag_type):\n        raise HTTPException(status_code=409, detail=f\"This tag has been registered in {tag_type}\")\n\n    data_schema = get_tag_info_schema(tag_type=tag_type)\n    with contract_api.DBSession(provider=\"zvt\", data_schema=data_schema)() as session:\n        timestamp = current_date()\n        entity_id = \"admin\"\n        tag_info_db = data_schema(\n            id=f\"admin_{tag_info.tag}\",\n            entity_id=entity_id,\n            timestamp=timestamp,\n            tag=tag_info.tag,\n            tag_reason=tag_info.tag_reason,\n        )\n        session.add(tag_info_db)\n        session.commit()\n        session.refresh(tag_info_db)\n        return tag_info_db\n\n\ndef build_stock_pool_info(create_stock_pool_info_model: CreateStockPoolInfoModel, timestamp):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockPoolInfo)() as session:\n        stock_pool_info = StockPoolInfo(\n            entity_id=\"admin\",\n            timestamp=to_pd_timestamp(timestamp),\n            id=f\"admin_{create_stock_pool_info_model.stock_pool_name}\",\n            stock_pool_type=create_stock_pool_info_model.stock_pool_type.value,\n            stock_pool_name=create_stock_pool_info_model.stock_pool_name,\n        )\n        session.add(stock_pool_info)\n        session.commit()\n        session.refresh(stock_pool_info)\n        return stock_pool_info\n\n\ndef build_stock_pool(create_stock_pools_model: CreateStockPoolsModel, target_date=current_date()):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockPools)() as session:\n        entity_type = create_stock_pools_model.entity_type\n        stock_pool_name = create_stock_pools_model.stock_pool_name\n\n        if stock_pool_name not in get_stock_pool_names():\n            build_stock_pool_info(\n                CreateStockPoolInfoModel(stock_pool_type=StockPoolType.custom, stock_pool_name=stock_pool_name),\n                timestamp=target_date,\n            )\n        # one instance per day for entity_type\n        stock_pool_id = f\"{entity_type}_{stock_pool_name}_{to_date_time_str(target_date)}\"\n        datas: List[StockPools] = StockPools.query_data(\n            session=session,\n            filters=[\n                StockPools.id == stock_pool_id,\n            ],\n            return_type=\"domain\",\n        )\n        if datas:\n            stock_pool = datas[0]\n            if create_stock_pools_model.insert_mode == InsertMode.overwrite:\n                stock_pool.entity_ids = create_stock_pools_model.entity_ids\n            else:\n                stock_pool.entity_ids = list(set(stock_pool.entity_ids + create_stock_pools_model.entity_ids))\n        else:\n            stock_pool = StockPools(\n                entity_id=f\"{entity_type}_{stock_pool_name}\",\n                timestamp=to_pd_timestamp(target_date),\n                id=stock_pool_id,\n                entity_type=entity_type,\n                stock_pool_name=stock_pool_name,\n                entity_ids=create_stock_pools_model.entity_ids,\n            )\n        session.add(stock_pool)\n        session.commit()\n        session.refresh(stock_pool)\n        return stock_pool\n\n\ndef delete_stock_pool(stock_pool_name: str):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockPoolInfo)() as session:\n        stock_pool_info = StockPoolInfo.query_data(\n            session=session,\n            filters=[StockPoolInfo.stock_pool_name == stock_pool_name],\n            return_type=\"domain\",\n        )\n\n        contract_api.del_data(data_schema=StockPools, filters=[StockPools.stock_pool_name == stock_pool_name])\n\n        if stock_pool_info:\n            session.delete(stock_pool_info[0])\n            session.commit()\n            return \"success\"\n        return \"not found\"\n\n\ndef get_main_tags_in_stock_pool(stock_pool_name: str) -> List[str]:\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockPools)() as session:\n        stock_pool_info = StockPoolInfo.query_data(\n            session=session,\n            filters=[StockPoolInfo.stock_pool_name == stock_pool_name],\n            return_type=\"domain\",\n        )\n        if not stock_pool_info:\n            raise HTTPException(status_code=404, detail=f\"Stock pool info {stock_pool_name} not found\")\n\n        entity_ids = None\n        if stock_pool_name != \"all\":\n            stock_pools: List[StockPools] = StockPools.query_data(\n                session=session,\n                filters=[StockPools.stock_pool_name == stock_pool_name],\n                order=StockPools.timestamp.desc(),\n                limit=1,\n                return_type=\"domain\",\n            )\n            if not stock_pools:\n                raise HTTPException(status_code=404, detail=f\"Stock pool {stock_pool_name} not found\")\n\n            entity_ids = stock_pools[0].entity_ids\n            if not entity_ids:\n                return []\n\n        df = StockTags.query_data(\n            session=session,\n            entity_ids=entity_ids,\n            columns=[\"main_tag\", \"entity_id\"],\n            return_type=\"df\",\n        )\n        grouped_df = df.groupby(\"main_tag\").agg(entity_count=(\"entity_id\", \"count\")).reset_index()\n        sorted_df = grouped_df.sort_values(by=[\"entity_count\"], ascending=[False])\n        return sorted_df[\"main_tag\"].tolist()\n\n\ndef query_stock_tag_stats(query_stock_tag_stats_model: QueryStockTagStatsModel):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=TagStats)() as session:\n        datas = TagStats.query_data(\n            session=session,\n            filters=[TagStats.stock_pool_name == query_stock_tag_stats_model.stock_pool_name],\n            order=TagStats.timestamp.desc(),\n            limit=1,\n            return_type=\"domain\",\n        )\n        if not datas:\n            return []\n\n        target_date = datas[0].timestamp\n\n        tag_stats_list: List[dict] = TagStats.query_data(\n            session=session,\n            filters=[\n                TagStats.stock_pool_name == query_stock_tag_stats_model.stock_pool_name,\n                TagStats.timestamp == target_date,\n            ],\n            return_type=\"dict\",\n            order=TagStats.position.asc(),\n        )\n\n        if query_stock_tag_stats_model.query_type == TagStatsQueryType.simple:\n            return tag_stats_list\n\n        entity_ids = flatten_list([tag_stats[\"entity_ids\"] for tag_stats in tag_stats_list])\n\n        # get stocks meta\n        stocks = Stock.query_data(provider=\"em\", entity_ids=entity_ids, return_type=\"domain\")\n        entity_map = {item.entity_id: item for item in stocks}\n\n        # get stock tags\n        tags_dict = StockTags.query_data(\n            session=session,\n            filters=[StockTags.entity_id.in_(entity_ids)],\n            return_type=\"dict\",\n        )\n        entity_tags_map = {item[\"entity_id\"]: item for item in tags_dict}\n\n        # get stock system tags\n        system_tags_dict = StockSystemTags.query_data(\n            session=session,\n            filters=[StockSystemTags.timestamp == target_date, StockSystemTags.entity_id.in_(entity_ids)],\n            return_type=\"dict\",\n        )\n        entity_system_tags_map = {item[\"entity_id\"]: item for item in system_tags_dict}\n\n        for tag_stats in tag_stats_list:\n            stock_details = []\n            for entity_id in tag_stats[\"entity_ids\"]:\n                stock_details_model = {\n                    \"entity_id\": entity_id,\n                    \"main_tag\": tag_stats[\"main_tag\"],\n                    \"code\": entity_map.get(entity_id).code,\n                    \"name\": entity_map.get(entity_id).name,\n                }\n\n                stock_tags = entity_tags_map.get(entity_id)\n                stock_details_model[\"sub_tag\"] = stock_tags[\"sub_tag\"]\n                if stock_tags[\"active_hidden_tags\"] is not None:\n                    stock_details_model[\"hidden_tags\"] = stock_tags[\"active_hidden_tags\"].keys()\n                else:\n                    stock_details_model[\"hidden_tags\"] = None\n\n                stock_system_tags = entity_system_tags_map.get(entity_id)\n                stock_details_model = fill_dict(stock_system_tags, stock_details_model)\n\n                stock_details.append(stock_details_model)\n            tag_stats[\"stock_details\"] = stock_details\n\n        return tag_stats_list\n\n\ndef refresh_main_tag_by_sub_tag(stock_tag: StockTags, set_by_user=False) -> StockTags:\n    if not stock_tag.sub_tags:\n        logger.warning(f\"{stock_tag.entity_id} has no sub_tags yet\")\n        return stock_tag\n\n    sub_tag = stock_tag.sub_tag\n    sub_tag_reason = stock_tag.sub_tags[sub_tag]\n\n    main_tag = get_main_tag_by_sub_tag(sub_tag)\n    main_tag_reason = sub_tag_reason\n    if main_tag == \"其他\":\n        main_tag = stock_tag.main_tag\n        main_tag_reason = stock_tag.main_tag_reason\n\n    set_stock_tags_model = SetStockTagsModel(\n        entity_id=stock_tag.entity_id,\n        main_tag=main_tag,\n        main_tag_reason=main_tag_reason,\n        sub_tag=sub_tag,\n        sub_tag_reason=sub_tag_reason,\n        active_hidden_tags=stock_tag.active_hidden_tags,\n    )\n    logger.info(f\"set_stock_tags_model:{set_stock_tags_model}\")\n\n    return build_stock_tags(\n        set_stock_tags_model=set_stock_tags_model,\n        timestamp=stock_tag.timestamp,\n        set_by_user=set_by_user,\n        keep_current=False,\n    )\n\n\ndef refresh_all_main_tag_by_sub_tag():\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        stock_tags = StockTags.query_data(\n            session=session,\n            return_type=\"domain\",\n        )\n        for stock_tag in stock_tags:\n            refresh_main_tag_by_sub_tag(stock_tag)\n\n\ndef reset_to_default_main_tag(current_main_tag: str):\n    df = StockTags.query_data(\n        filters=[StockTags.main_tag == current_main_tag],\n        columns=[StockTags.entity_id],\n        return_type=\"df\",\n    )\n    entity_ids = df[\"entity_id\"].tolist()\n    if not entity_ids:\n        logger.info(f\"all stocks with main_tag: {current_main_tag} has been reset\")\n        return\n    build_default_main_tag(entity_ids=entity_ids, force_rebuild=True)\n\n\ndef activate_industry_list(industry_list: List[str]):\n    df_block = Block.query_data(provider=\"em\", filters=[Block.category == \"industry\", Block.name.in_(industry_list)])\n    industry_codes = df_block[\"code\"].tolist()\n    block_stocks: List[BlockStock] = BlockStock.query_data(\n        provider=\"em\",\n        filters=[BlockStock.code.in_(industry_codes)],\n        return_type=\"domain\",\n    )\n    entity_ids = [block_stock.stock_id for block_stock in block_stocks]\n\n    if not entity_ids:\n        logger.info(f\"No stocks in {industry_list}\")\n        return\n\n    build_default_main_tag(entity_ids=entity_ids, force_rebuild=True)\n\n\ndef activate_sub_tags(activate_sub_tags_model: ActivateSubTagsModel):\n    sub_tags = activate_sub_tags_model.sub_tags\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        result = {}\n        for sub_tag in sub_tags:\n            # df = StockTags.query_data(\n            #     session=session,\n            #     filters=[StockTags.sub_tag != sub_tag],\n            #     columns=[StockTags.entity_id],\n            #     return_type=\"df\",\n            # )\n            # entity_ids = df[\"entity_id\"].tolist()\n            entity_ids = None\n\n            # stock_tag with sub_tag but not set to related main_tag yet\n            stock_tags = StockTags.query_data(\n                session=session,\n                entity_ids=entity_ids,\n                # 需要sqlite3版本>=3.37.0\n                filters=[func.json_extract(StockTags.sub_tags, f'$.\"{sub_tag}\"') != None],\n                return_type=\"domain\",\n            )\n            if not stock_tags:\n                logger.info(f\"all stocks with sub_tag: {sub_tag} has been activated\")\n                continue\n            for stock_tag in stock_tags:\n                stock_tag.sub_tag = sub_tag\n                session.commit()\n                session.refresh(stock_tag)\n                result[stock_tag.entity_id] = refresh_main_tag_by_sub_tag(stock_tag, set_by_user=True)\n        return result\n\n\ndef delete_main_tag(main_tag: str):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        stock_tags = StockTags.query_data(\n            session=session,\n            # 需要sqlite3版本>=3.37.0\n            filters=[func.json_extract(StockTags.main_tags, f'$.\"{main_tag}\"') != None],\n            return_type=\"domain\",\n        )\n\n        sql = text(f'update industry_info set main_tag = \"其他\" where main_tag = \"{main_tag}\"')\n        session.execute(sql)\n        session.commit()\n\n        sql = text(f'update sub_tag_info set main_tag = \"其他\" where main_tag = \"{main_tag}\"')\n        session.execute(sql)\n        session.commit()\n\n        for stock_tag in stock_tags:\n            logger.info(f\"remove main_tag: {main_tag} for {stock_tag.entity_id}\")\n\n            main_tags = dict(stock_tag.main_tags)\n\n            logger.info(f\"main_tags before deleted: {main_tags}\")\n            main_tags.pop(main_tag)\n            logger.info(f\"main_tags after deleted: {main_tags}\")\n\n            stock_tag.main_tags = main_tags\n\n            if main_tag == stock_tag.main_tag:\n                logger.info(f\"main_tag before deleted: {stock_tag.main_tag}\")\n\n                if main_tags:\n                    # set to first main tag\n                    stock_tag.main_tag = list(main_tags.keys())[0]\n                    stock_tag.main_tag_reason = main_tags[stock_tag.main_tag]\n                else:\n                    stock_tag.main_tag = \"其他\"\n                    stock_tag.main_tag_reason = \"其他\"\n\n                logger.info(f\"main_tag after deleted: {stock_tag.main_tag}\")\n            session.commit()\n\n\ndef delete_sub_tag(sub_tag: str):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        stock_tags = StockTags.query_data(\n            session=session,\n            # 需要sqlite3版本>=3.37.0\n            filters=[func.json_extract(StockTags.sub_tags, f'$.\"{sub_tag}\"') != None],\n            return_type=\"domain\",\n        )\n\n        for stock_tag in stock_tags:\n            logger.info(f\"remove sub_tag: {sub_tag} for {stock_tag.entity_id}\")\n\n            sub_tags = dict(stock_tag.sub_tags)\n\n            logger.info(f\"sub_tags before deleted: {sub_tags}\")\n            sub_tags.pop(sub_tag)\n            logger.info(f\"sub_tags after deleted: {sub_tags}\")\n\n            stock_tag.sub_tags = sub_tags\n\n            if sub_tag == stock_tag.sub_tag:\n                logger.info(f\"sub_tag before deleted: {stock_tag.sub_tag}\")\n\n                if sub_tags:\n                    # set to first main tag\n                    stock_tag.sub_tag = list(sub_tags.keys())[0]\n                    stock_tag.sub_tag_reason = sub_tags[stock_tag.sub_tag]\n                else:\n                    stock_tag.sub_tag = \"其他\"\n                    stock_tag.sub_tag_reason = \"其他\"\n\n                logger.info(f\"sub_tag after deleted: {stock_tag.sub_tag}\")\n            session.commit()\n\n\ndef delete_hidden_tag(hidden_tag: str):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        stock_tags = StockTags.query_data(\n            session=session,\n            # 需要sqlite3版本>=3.37.0\n            filters=[func.json_extract(StockTags.hidden_tags, f'$.\"{hidden_tag}\"') != None],\n            return_type=\"domain\",\n        )\n\n        for stock_tag in stock_tags:\n            logger.info(f\"delete hidden_tag: {hidden_tag} for {stock_tag.entity_id}\")\n\n            hidden_tags = dict(stock_tag.hidden_tags)\n\n            logger.info(f\"hidden_tags before deleted: {hidden_tags}\")\n            hidden_tags.pop(hidden_tag)\n            stock_tag.hidden_tags = hidden_tags\n            logger.info(f\"hidden_tags after deleted: {hidden_tags}\")\n\n            if stock_tag.active_hidden_tags and (hidden_tag in stock_tag.active_hidden_tags):\n                active_hidden_tags = dict(stock_tag.active_hidden_tags)\n\n                logger.info(f\"active_hidden_tags before deleted: {active_hidden_tags}\")\n                active_hidden_tags.pop(hidden_tag)\n                stock_tag.active_hidden_tags = active_hidden_tags\n                logger.info(f\"active_hidden_tags after deleted: {active_hidden_tags}\")\n\n            session.commit()\n\n\ndef delete_tag(tag: str, tag_type: TagType):\n    data_schema = get_tag_info_schema(tag_type=tag_type)\n    with contract_api.DBSession(provider=\"zvt\", data_schema=data_schema)() as session:\n        current_tags_info = data_schema.query_data(\n            session=session, filters=[data_schema.tag == tag], return_type=\"domain\"\n        )\n\n        if not current_tags_info:\n            logger.info(f\"tag_type: {tag_type}, tag: {tag} not exists, ignore delete tag info\")\n        else:\n            logger.info(f\"delete tag info, tag_type: {tag_type}, tag: {tag} \")\n            session.delete(current_tags_info[0])\n            session.commit()\n\n        if tag_type == TagType.main_tag:\n            delete_main_tag(main_tag=tag)\n        elif tag_type == TagType.sub_tag:\n            delete_sub_tag(sub_tag=tag)\n        elif tag_type == TagType.hidden_tag:\n            delete_hidden_tag(hidden_tag=tag)\n        else:\n            raise HTTPException(status_code=400, detail=f\"Unsupported tag type: {tag_type}\")\n\n\ndef _create_main_tag_if_not_existed(main_tag, main_tag_reason):\n    main_tag_info = CreateTagInfoModel(tag=main_tag, tag_reason=main_tag_reason)\n    if not is_tag_info_existed(tag=main_tag_info.tag, tag_type=TagType.main_tag):\n        create_tag_info(tag_info=main_tag_info, tag_type=TagType.main_tag)\n\n\ndef get_main_tag_industry_relation(main_tag):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        df = IndustryInfo.query_data(\n            session=session,\n            columns=[IndustryInfo.industry_name],\n            filters=[IndustryInfo.main_tag == main_tag],\n            return_type=\"df\",\n        )\n        return {\"main_tag\": main_tag, \"industry_list\": df[\"industry_name\"].tolist()}\n\n\ndef get_main_tag_sub_tag_relation(main_tag):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        df = SubTagInfo.query_data(\n            session=session,\n            columns=[SubTagInfo.tag],\n            filters=[SubTagInfo.main_tag == main_tag],\n            return_type=\"df\",\n        )\n        return {\"main_tag\": main_tag, \"sub_tag_list\": df[\"tag\"].tolist()}\n\n\ndef build_main_tag_industry_relation(build_relation_model: BuildMainTagIndustryRelationModel):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        main_tag = build_relation_model.main_tag\n        _create_main_tag_if_not_existed(main_tag=main_tag, main_tag_reason=main_tag)\n\n        industry_list = build_relation_model.industry_list\n\n        datas: List[IndustryInfo] = IndustryInfo.query_data(\n            session=session,\n            filters=[IndustryInfo.main_tag == main_tag, IndustryInfo.industry_name.notin_(industry_list)],\n            return_type=\"domain\",\n        )\n        for data in datas:\n            data.main_tag = \"其他\"\n        session.commit()\n\n        industry_info_list: List[IndustryInfo] = IndustryInfo.query_data(\n            session=session,\n            filters=[IndustryInfo.industry_name.in_(industry_list)],\n            return_type=\"domain\",\n        )\n        for industry_info in industry_info_list:\n            industry_info.main_tag = main_tag\n        session.commit()\n\n        if build_relation_model.activate:\n            # activate industry\n            activate_industry_list(industry_list=industry_list)\n\n\ndef build_main_tag_sub_tag_relation(build_relation_model: BuildMainTagIndustryRelationModel):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=SubTagInfo)() as session:\n        main_tag = build_relation_model.main_tag\n        _create_main_tag_if_not_existed(main_tag=main_tag, main_tag_reason=main_tag)\n\n        sub_tag_list = build_relation_model.sub_tag_list\n\n        # update others to \"其他\"\n        datas: List[SubTagInfo] = SubTagInfo.query_data(\n            session=session,\n            filters=[SubTagInfo.main_tag == main_tag, SubTagInfo.tag.notin_(sub_tag_list)],\n            return_type=\"domain\",\n        )\n        for data in datas:\n            data.main_tag = \"其他\"\n        session.commit()\n\n        # update sub tag info\n        sub_tag_info_list: List[SubTagInfo] = SubTagInfo.query_data(\n            session=session,\n            filters=[SubTagInfo.tag.in_(sub_tag_list)],\n            return_type=\"domain\",\n        )\n        for sub_tag_info in sub_tag_info_list:\n            sub_tag_info.main_tag = main_tag\n        session.commit()\n\n        if build_relation_model.activate:\n            # activate sub tags\n            activate_sub_tags(ActivateSubTagsModel(sub_tags=sub_tag_list))\n\n\ndef change_main_tag(change_main_tag_model: ChangeMainTagModel):\n    current_main_tag = change_main_tag_model.current_main_tag\n    new_main_tag = change_main_tag_model.new_main_tag\n\n    _create_main_tag_if_not_existed(main_tag=new_main_tag, main_tag_reason=new_main_tag)\n    with contract_api.DBSession(provider=\"zvt\", data_schema=StockTags)() as session:\n        stock_tags: List[StockTags] = StockTags.query_data(\n            filters=[StockTags.main_tag == current_main_tag],\n            session=session,\n            return_type=\"domain\",\n        )\n\n        for stock_tag in stock_tags:\n            tag_parameter: TagParameter = build_tag_parameter(\n                tag_type=TagType.main_tag,\n                tag=new_main_tag,\n                tag_reason=new_main_tag,\n                stock_tag=stock_tag,\n            )\n            set_stock_tags_model = SetStockTagsModel(\n                entity_id=stock_tag.entity_id,\n                main_tag=tag_parameter.main_tag,\n                main_tag_reason=tag_parameter.main_tag_reason,\n                sub_tag=tag_parameter.sub_tag,\n                sub_tag_reason=tag_parameter.sub_tag_reason,\n                active_hidden_tags=stock_tag.active_hidden_tags,\n            )\n\n            build_stock_tags(\n                set_stock_tags_model=set_stock_tags_model,\n                timestamp=now_pd_timestamp(),\n                set_by_user=True,\n                keep_current=False,\n            )\n            session.refresh(stock_tag)\n        return stock_tags\n\n\nif __name__ == \"__main__\":\n    # print(get_main_tags_in_stock_pool(\"涨停梯队\"))\n    # print(delete_tag(tag=\"赛马概念\", tag_type=TagType.sub_tag))\n    activate_industry_list(industry_list=[\"航天航空\"])\n    # activate_sub_tags(ActivateSubTagsModel(sub_tags=[\"航天概念\", \"天基互联\", \"北斗导航\", \"通用航空\"]))\n    # build_default_main_tag(entity_type=\"stockhk\")\n\n# the __all__ is generated\n__all__ = [\n    \"get_stock_tag_options\",\n    \"build_stock_tags\",\n    \"build_tag_parameter\",\n    \"batch_set_stock_tags\",\n    \"build_default_main_tag\",\n    \"build_default_sub_tags\",\n    \"get_tag_info_schema\",\n    \"is_tag_info_existed\",\n    \"create_tag_info\",\n    \"build_stock_pool_info\",\n    \"build_stock_pool\",\n    \"query_stock_tag_stats\",\n    \"refresh_main_tag_by_sub_tag\",\n    \"refresh_all_main_tag_by_sub_tag\",\n    \"reset_to_default_main_tag\",\n    \"activate_industry_list\",\n    \"activate_sub_tags\",\n]\n"
  },
  {
    "path": "src/zvt/tag/tag_stats.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nfrom typing import List\n\nimport pandas as pd\nimport sqlalchemy\n\nfrom zvt.api.kdata import get_kdata_schema\nfrom zvt.contract import AdjustType, IntervalLevel\nfrom zvt.contract.api import df_to_db, get_db_session\nfrom zvt.domain.quotes import KdataCommon\nfrom zvt.factors.top_stocks import TopStocks, get_top_stocks\nfrom zvt.tag.common import InsertMode\nfrom zvt.tag.tag_models import CreateStockPoolsModel\nfrom zvt.tag.tag_schemas import TagStats, StockTags, StockPools\nfrom zvt.tag.tag_service import build_stock_pool\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_pd_timestamp, current_date, next_date\n\nlogger = logging.getLogger(__name__)\n\n\ndef build_system_stock_pools(start_date, entity_type, force_update=False):\n    if entity_type == \"stock\":\n        stock_pool_names = [\"主线\", \"年线\", \"大局\"]\n    elif entity_type == \"stockus\":\n        stock_pool_names = [\"美股主线\"]\n    elif entity_type == \"stockhk\":\n        stock_pool_names = [\"港股主线\"]\n    else:\n        raise ValueError(f\"entity_type {entity_type} not supported\")\n\n    for stock_pool_name in stock_pool_names:\n        if not force_update:\n            datas = StockPools.query_data(\n                limit=1,\n                filters=[StockPools.entity_type == entity_type, StockPools.stock_pool_name == stock_pool_name],\n                order=StockPools.timestamp.desc(),\n                return_type=\"domain\",\n            )\n            if datas:\n                start_date = max(next_date(the_time=datas[0].timestamp), start_date)\n\n        df = TopStocks.query_data(\n            start_timestamp=start_date,\n            filters=[TopStocks.entity_type == entity_type],\n            columns=[TopStocks.timestamp],\n            order=TopStocks.timestamp.asc(),\n        )\n        if not pd_is_not_null(df):\n            logger.info(f\"no data for top_stocks {entity_type} {start_date}\")\n            continue\n\n        dates = df[\"timestamp\"].tolist()\n        for target_date in dates:\n            logger.info(f\"build_system_stock_pools {entity_type} {stock_pool_name} to {target_date}\")\n            if stock_pool_name == \"主线\" or stock_pool_name == \"美股主线\" or stock_pool_name == \"港股主线\":\n                short_stocks = get_top_stocks(entity_type=entity_type, target_date=target_date, return_type=\"short\")\n                long_stocks = get_top_stocks(entity_type=entity_type, target_date=target_date, return_type=\"long\")\n                entity_ids = list(set(short_stocks + long_stocks))\n            elif stock_pool_name == \"年线\":\n                small_stocks = get_top_stocks(\n                    entity_type=entity_type, target_date=target_date, return_type=\"small_vol_up\"\n                )\n                big_stocks = get_top_stocks(entity_type=entity_type, target_date=target_date, return_type=\"big_vol_up\")\n                entity_ids = list(set(small_stocks + big_stocks))\n            elif stock_pool_name == \"大局\":\n                entity_ids = get_top_stocks(entity_type=entity_type, target_date=target_date, return_type=\"all\")\n            else:\n                assert False\n\n            if not entity_ids:\n                logger.info(f\"no data for {entity_type} {stock_pool_name} {target_date}\")\n                break\n\n            create_stock_pools_model: CreateStockPoolsModel = CreateStockPoolsModel(\n                entity_type=entity_type,\n                stock_pool_name=stock_pool_name,\n                entity_ids=entity_ids,\n                insert_mode=InsertMode.overwrite,\n            )\n            build_stock_pool(create_stock_pools_model, target_date=target_date)\n\n\ndef build_stock_pool_tag_stats(\n    stock_pool_name,\n    entity_type=\"stock\",\n    force_rebuild_latest=False,\n    target_date=None,\n    adjust_type=AdjustType.qfq,\n    provider=\"em\",\n):\n    datas = TagStats.query_data(\n        limit=1,\n        filters=[TagStats.entity_type == entity_type, TagStats.stock_pool_name == stock_pool_name],\n        order=TagStats.timestamp.desc(),\n        return_type=\"domain\",\n    )\n    start = target_date\n    current_df = None\n    if datas:\n        if force_rebuild_latest:\n            session = get_db_session(\"zvt\", data_schema=TagStats)\n            session.query(TagStats).filter(TagStats.entity_type == entity_type).filter(\n                TagStats.stock_pool_name == stock_pool_name\n            ).filter(TagStats.timestamp == datas[0].timestamp).delete()\n            session.commit()\n            return build_stock_pool_tag_stats(stock_pool_name=stock_pool_name, force_rebuild_latest=False)\n\n        latest_tag_stats_timestamp = datas[0].timestamp\n        current_df = TagStats.query_data(\n            filters=[\n                TagStats.entity_type == entity_type,\n                TagStats.stock_pool_name == stock_pool_name,\n                TagStats.timestamp == latest_tag_stats_timestamp,\n            ]\n        )\n        start = next_date(the_time=latest_tag_stats_timestamp)\n\n    stock_pools: List[StockPools] = StockPools.query_data(\n        start_timestamp=start,\n        filters=[StockPools.entity_type == entity_type, StockPools.stock_pool_name == stock_pool_name],\n        order=StockPools.timestamp.asc(),\n        return_type=\"domain\",\n    )\n    if not stock_pools:\n        logger.info(f\"no data to build tag stats: {entity_type} {stock_pool_name} {start}\")\n        return None\n\n    for stock_pool in stock_pools:\n        target_date = stock_pool.timestamp\n        logger.info(f\"build_stock_pool_tag_stats for {entity_type} {stock_pool_name} {target_date}\")\n\n        entity_ids = stock_pool.entity_ids\n        tags_df = StockTags.query_data(\n            entity_ids=entity_ids, filters=[StockTags.entity_type == entity_type], return_type=\"df\", index=\"entity_id\"\n        )\n        kdata_schema: KdataCommon = get_kdata_schema(\n            entity_type=entity_type, level=IntervalLevel.LEVEL_1DAY, adjust_type=adjust_type\n        )\n        kdata_df = kdata_schema.query_data(\n            provider=provider,\n            entity_ids=entity_ids,\n            filters=[kdata_schema.timestamp == to_pd_timestamp(target_date)],\n            columns=[kdata_schema.entity_id, kdata_schema.name, kdata_schema.turnover],\n            index=\"entity_id\",\n        )\n\n        df = pd.concat([tags_df, kdata_df[[\"turnover\", \"name\"]]], axis=1)\n\n        grouped_df = (\n            df.groupby(\"main_tag\")\n            .agg(\n                turnover=(\"turnover\", \"sum\"),\n                entity_count=(\"entity_id\", \"count\"),\n                entity_ids=(\"entity_id\", lambda entity_id: list(entity_id)),\n            )\n            .reset_index()\n        )\n        sorted_df = grouped_df.sort_values(by=[\"turnover\", \"entity_count\"], ascending=[False, False])\n        # sorted_df = grouped_df.sort_values(by=[\"entity_count\"], ascending=[False])\n        sorted_df = sorted_df.reset_index(drop=True)\n        sorted_df[\"position\"] = sorted_df.index\n        sorted_df[\"is_main_line\"] = sorted_df.index < 5\n        sorted_df[\"main_line_continuous_days\"] = sorted_df[\"is_main_line\"].apply(lambda x: 1 if x else 0)\n        # logger.info(f\"current_df\\n: {current_df}\")\n        if pd_is_not_null(current_df):\n            sorted_df.set_index(\"main_tag\", inplace=True, drop=False)\n            current_df.set_index(\"main_tag\", inplace=True, drop=False)\n            common_index = sorted_df[sorted_df[\"is_main_line\"]].index.intersection(\n                current_df[current_df[\"is_main_line\"]].index\n            )\n            pre_selected = current_df.loc[common_index]\n            if pd_is_not_null(pre_selected):\n                pre_selected = pre_selected.reindex(sorted_df.index, fill_value=0)\n                sorted_df[\"main_line_continuous_days\"] = (\n                    sorted_df[\"main_line_continuous_days\"] + pre_selected[\"main_line_continuous_days\"]\n                )\n        sorted_df[\"entity_id\"] = f\"{entity_type}_{stock_pool_name}\"\n        sorted_df[\"timestamp\"] = target_date\n        sorted_df[\"entity_type\"] = entity_type\n        sorted_df[\"stock_pool_name\"] = stock_pool_name\n        sorted_df[\"id\"] = sorted_df[[\"entity_id\", \"timestamp\", \"main_tag\"]].apply(\n            lambda x: \"_\".join(x.astype(str)), axis=1\n        )\n        df_to_db(\n            provider=\"zvt\",\n            df=sorted_df,\n            data_schema=TagStats,\n            force_update=True,\n            dtype={\"entity_ids\": sqlalchemy.JSON},\n        )\n        current_df = sorted_df\n\n\ndef refresh_stock_pool(stock_pool_name, entity_ids, insert_mode=InsertMode.append, target_date=current_date()):\n    create_stock_pools_model: CreateStockPoolsModel = CreateStockPoolsModel(\n        stock_pool_name=stock_pool_name, entity_ids=entity_ids, insert_mode=insert_mode\n    )\n\n    build_stock_pool(create_stock_pools_model, target_date=target_date)\n\n\ndef get_tags_order_by_position(stock_pool_name, entity_type=\"stock\"):\n    latest_data = TagStats.query_data(\n        filters=[TagStats.entity_type == entity_type, TagStats.stock_pool_name == stock_pool_name],\n        order=TagStats.timestamp.desc(),\n        limit=1,\n        return_type=\"domain\",\n    )\n    target_date = to_pd_timestamp(latest_data[0].timestamp)\n\n    tag_stats_list: List[TagStats] = TagStats.query_data(\n        filters=[\n            TagStats.timestamp == to_pd_timestamp(target_date),\n            TagStats.entity_type == entity_type,\n            TagStats.stock_pool_name == stock_pool_name,\n        ],\n        order=TagStats.position.asc(),\n        return_type=\"domain\",\n    )\n\n    all_tags = [item.main_tag for item in tag_stats_list]\n    main_line_tag1 = all_tags[0:1]\n    main_line_tag2 = all_tags[1:2]\n    sub_line_tags1 = all_tags[2:4]\n    sub_line_tags2 = all_tags[4:6]\n    following_tags = all_tags[6:8]\n    other_tags = all_tags[8:]\n    return main_line_tag1, main_line_tag2, sub_line_tags1, sub_line_tags2, following_tags, other_tags\n\n\nif __name__ == \"__main__\":\n    # build_system_stock_pools()\n    # build_stock_pool_tag_stats(stock_pool_name=\"主线\", force_rebuild_latest=True)\n    # build_stock_pool_tag_stats(stock_pool_name=\"年线\")\n    print(get_tags_order_by_position(stock_pool_name=\"主线\", entity_type=\"stockus\"))\n\n\n# the __all__ is generated\n__all__ = [\"build_system_stock_pools\", \"build_stock_pool_tag_stats\", \"refresh_stock_pool\"]\n"
  },
  {
    "path": "src/zvt/tag/tag_utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport json\nimport os\nfrom typing import List, Dict\n\nimport pandas as pd\n\nfrom zvt import zvt_env\nfrom zvt.contract.api import df_to_db\nfrom zvt.domain import Block\nfrom zvt.tag.common import StockPoolType\nfrom zvt.tag.tag_schemas import MainTagInfo, SubTagInfo, HiddenTagInfo, StockPoolInfo, IndustryInfo\nfrom zvt.utils.time_utils import now_pd_timestamp\n\n\ndef _get_default_industry_main_tag_mapping() -> Dict[str, str]:\n    result = {}\n    with open(os.path.join(zvt_env[\"resource_path\"], \"industry_main_tag_mapping.json\"), encoding=\"utf-8\") as f:\n        result = json.load(f)\n    with open(os.path.join(zvt_env[\"resource_path\"], \"us_industry_main_tag_mapping.json\"), encoding=\"utf-8\") as f:\n        result.update(json.load(f))\n    with open(os.path.join(zvt_env[\"resource_path\"], \"hk_industry_main_tag_mapping.json\"), encoding=\"utf-8\") as f:\n        result.update(json.load(f))\n\n    return result\n\n\ndef _get_default_main_tag_industry_mapping() -> Dict[str, List[str]]:\n    mapping = _get_default_industry_main_tag_mapping()\n    result = {}\n    for industry, main_tag in mapping.items():\n        result.setdefault(main_tag, [])\n        result.get(main_tag).append(industry)\n    return result\n\n\ndef _get_default_concept_main_tag_mapping() -> Dict[str, str]:\n    with open(os.path.join(zvt_env[\"resource_path\"], \"concept_main_tag_mapping.json\"), encoding=\"utf-8\") as f:\n        return json.load(f)\n\n\ndef _get_default_main_tag_concept_mapping() -> Dict[str, List[str]]:\n    mapping = _get_default_concept_main_tag_mapping()\n    result = {}\n    for concept, main_tag in mapping.items():\n        result.setdefault(main_tag, [])\n        result.get(main_tag).append(concept)\n    return result\n\n\ndef _get_initial_sub_tags() -> List[str]:\n    return list(_get_default_concept_main_tag_mapping().keys())\n\n\ndef _get_industry_list():\n    df = Block.query_data(\n        filters=[Block.category == \"industry\"], columns=[Block.name], return_type=\"df\", order=Block.timestamp.desc()\n    )\n    return df[\"name\"].tolist()\n\n\ndef _get_concept_list():\n    df = Block.query_data(\n        filters=[Block.category == \"concept\"], columns=[Block.name], return_type=\"df\", order=Block.timestamp.desc()\n    )\n\n    return df[\"name\"].tolist()\n\n\ndef _check_missed_industry():\n    current_industry_list = _get_default_industry_main_tag_mapping().keys()\n    return list(set(_get_industry_list()) - set(current_industry_list))\n\n\ndef _check_missed_concept():\n    current_concept_list = _get_default_concept_main_tag_mapping().keys()\n    return list(set(_get_concept_list()) - set(current_concept_list))\n\n\ndef _get_initial_main_tag_info():\n    timestamp = now_pd_timestamp()\n    entity_id = \"admin\"\n\n    from_industry = [\n        {\n            \"id\": f\"{entity_id}_{main_tag}\",\n            \"entity_id\": entity_id,\n            \"timestamp\": timestamp,\n            \"tag\": main_tag,\n            \"tag_reason\": f\"来自这些行业:{industry}\",\n        }\n        for main_tag, industry in _get_default_main_tag_industry_mapping().items()\n    ]\n\n    from_concept = []\n    for tag, concepts in _get_default_main_tag_concept_mapping().items():\n        if tag not in _get_default_main_tag_industry_mapping():\n            from_concept.append(\n                {\n                    \"id\": f\"{entity_id}_{tag}\",\n                    \"entity_id\": entity_id,\n                    \"timestamp\": timestamp,\n                    \"tag\": tag,\n                    \"tag_reason\": f\"来自这些概念:{','.join(concepts)}\",\n                }\n            )\n\n    return from_industry + from_concept\n\n\ndef _get_initial_industry_info():\n    timestamp = now_pd_timestamp()\n    entity_id = \"admin\"\n    industry_info = [\n        {\n            \"id\": f\"{entity_id}_{industry}\",\n            \"entity_id\": entity_id,\n            \"timestamp\": timestamp,\n            \"industry_name\": industry,\n            \"description\": industry,\n            \"main_tag\": main_tag,\n        }\n        for industry, main_tag in _get_default_industry_main_tag_mapping().items()\n    ]\n    return industry_info\n\n\ndef _get_initial_sub_tag_info():\n    timestamp = now_pd_timestamp()\n    entity_id = \"admin\"\n\n    return [\n        {\n            \"id\": f\"{entity_id}_{sub_tag}\",\n            \"entity_id\": entity_id,\n            \"timestamp\": timestamp,\n            \"tag\": sub_tag,\n            \"tag_reason\": sub_tag,\n            \"main_tag\": main_tag,\n        }\n        for sub_tag, main_tag in _get_default_concept_main_tag_mapping().items()\n    ]\n\n\ndef _get_initial_stock_pool_info():\n    timestamp = now_pd_timestamp()\n    entity_id = \"admin\"\n    return [\n        {\n            \"id\": f\"{entity_id}_{stock_pool_name}\",\n            \"entity_id\": entity_id,\n            \"timestamp\": timestamp,\n            \"stock_pool_type\": StockPoolType.system.value,\n            \"stock_pool_name\": stock_pool_name,\n        }\n        for stock_pool_name in [\"主线\", \"年线\", \"大局\", \"A股\", \"美股主线\", \"港股主线\"]\n    ]\n\n\n_hidden_tags = {\n    \"中字头\": \"央企，国资委控股\",\n    \"核心资产\": \"高ROE 高现金流 高股息 低应收 低资本开支 低财务杠杆 有增长\",\n    \"高股息\": \"高股息\",\n    \"微盘股\": \"市值50亿以下\",\n    \"次新股\": \"上市未满两年\",\n}\n\n\ndef _get_initial_hidden_tag_info():\n    timestamp = now_pd_timestamp()\n    entity_id = \"admin\"\n    return [\n        {\n            \"id\": f\"{entity_id}_{tag}\",\n            \"entity_id\": entity_id,\n            \"timestamp\": timestamp,\n            \"tag\": tag,\n            \"tag_reason\": tag_reason,\n        }\n        for tag, tag_reason in _hidden_tags.items()\n    ]\n\n\ndef build_initial_main_tag_info(force_update=False):\n    main_tag_info_list = _get_initial_main_tag_info()\n    df = pd.DataFrame.from_records(main_tag_info_list)\n    df_to_db(df=df, data_schema=MainTagInfo, provider=\"zvt\", force_update=force_update)\n\n\ndef build_initial_industry_info(force_update=False):\n    initial_industry_info = _get_initial_industry_info()\n    df = pd.DataFrame.from_records(initial_industry_info)\n    df_to_db(df=df, data_schema=IndustryInfo, provider=\"zvt\", force_update=force_update)\n\n\ndef build_initial_sub_tag_info(force_update=False):\n    sub_tag_info_list = _get_initial_sub_tag_info()\n    df = pd.DataFrame.from_records(sub_tag_info_list)\n    df_to_db(df=df, data_schema=SubTagInfo, provider=\"zvt\", force_update=force_update)\n\n\ndef build_initial_stock_pool_info(force_update=False):\n    stock_pool_info_list = _get_initial_stock_pool_info()\n    df = pd.DataFrame.from_records(stock_pool_info_list)\n    df_to_db(df=df, data_schema=StockPoolInfo, provider=\"zvt\", force_update=force_update)\n\n\ndef build_initial_hidden_tag_info(force_update=False):\n    hidden_tag_info_list = _get_initial_hidden_tag_info()\n    df = pd.DataFrame.from_records(hidden_tag_info_list)\n    df_to_db(df=df, data_schema=HiddenTagInfo, provider=\"zvt\", force_update=force_update)\n\n\ndef get_main_tags():\n    df = MainTagInfo.query_data(columns=[MainTagInfo.tag])\n    return df[\"tag\"].tolist()\n\n\ndef get_main_tag_by_sub_tag(sub_tag):\n    datas: List[SubTagInfo] = SubTagInfo.query_data(filters=[SubTagInfo.tag == sub_tag], return_type=\"domain\")\n    if datas:\n        return datas[0].main_tag\n    else:\n        return _get_default_concept_main_tag_mapping().get(sub_tag, \"其他\")\n\n\ndef get_main_tag_by_industry(industry_name):\n    datas: List[IndustryInfo] = IndustryInfo.query_data(\n        filters=[IndustryInfo.industry_name == industry_name], return_type=\"domain\"\n    )\n    if datas:\n        return datas[0].main_tag\n    else:\n        _get_default_industry_main_tag_mapping().get(industry_name, \"其他\")\n\n\ndef get_sub_tags():\n    df = SubTagInfo.query_data(columns=[SubTagInfo.tag])\n    return df[\"tag\"].tolist()\n\n\ndef get_hidden_tags():\n    df = HiddenTagInfo.query_data(columns=[HiddenTagInfo.tag])\n    return df[\"tag\"].tolist()\n\n\ndef get_stock_pool_names():\n    df = StockPoolInfo.query_data(columns=[StockPoolInfo.stock_pool_name])\n    return df[\"stock_pool_name\"].tolist()\n\n\ndef match_tag_by_type(alias, tag_type=\"main_tag\"):\n    if tag_type == \"main_tag\":\n        tags = get_main_tags()\n    elif tag_type == \"sub_tag\":\n        tags = get_sub_tags()\n    elif tag_type == \"industry\":\n        tags = _get_industry_list()\n    else:\n        assert False\n\n    max_intersection_length = 0\n    max_tag = None\n\n    for tag in tags:\n        intersection_length = len(set(alias) & set(tag))\n        # at least 2 same chars\n        if intersection_length < 2:\n            continue\n\n        if intersection_length > max_intersection_length:\n            max_intersection_length = intersection_length\n            max_tag = tag\n\n    return max_tag\n\n\ndef match_tag(alias):\n    tag = match_tag_by_type(alias, tag_type=\"main_tag\")\n    if tag:\n        return \"main_tag\", tag\n\n    tag = match_tag_by_type(alias, tag_type=\"sub_tag\")\n    if tag:\n        return \"sub_tag\", tag\n\n    tag = match_tag_by_type(alias, tag_type=\"industry\")\n    if tag:\n        return \"main_tag\", get_main_tag_by_industry(tag)\n\n    return \"new_tag\", alias\n\n\nif __name__ == \"__main__\":\n    print(build_initial_industry_info())\n    print(build_initial_main_tag_info())\n\n\n# the __all__ is generated\n__all__ = [\n    \"build_initial_main_tag_info\",\n    \"build_initial_industry_info\",\n    \"build_initial_sub_tag_info\",\n    \"build_initial_stock_pool_info\",\n    \"build_initial_hidden_tag_info\",\n    \"get_main_tags\",\n    \"get_main_tag_by_sub_tag\",\n    \"get_main_tag_by_industry\",\n    \"get_sub_tags\",\n    \"get_hidden_tags\",\n    \"get_stock_pool_names\",\n    \"match_tag_by_type\",\n    \"match_tag\",\n]\n"
  },
  {
    "path": "src/zvt/tag/tagger.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nfrom typing import Type\n\nfrom zvt.contract import Mixin\nfrom zvt.contract import TradableEntity\nfrom zvt.contract.api import get_db_session\nfrom zvt.contract.base_service import OneStateService\nfrom zvt.contract.zvt_info import TaggerState\nfrom zvt.domain import Stock\nfrom zvt.tag.tag_schemas import StockTags\n\nlogger = logging.getLogger(__name__)\n\n\nclass Tagger(OneStateService):\n    state_schema = TaggerState\n\n    entity_schema: Type[TradableEntity] = None\n\n    data_schema: Type[Mixin] = None\n\n    start_timestamp = \"2018-01-01\"\n\n    def __init__(self, force=False) -> None:\n        super().__init__()\n        assert self.entity_schema is not None\n        assert self.data_schema is not None\n        self.force = force\n        self.session = get_db_session(provider=\"zvt\", data_schema=self.data_schema)\n        if self.state and not self.force:\n            logger.info(f\"get start_timestamp from state\")\n            self.start_timestamp = self.state[\"current_timestamp\"]\n        logger.info(f\"tag start_timestamp: {self.start_timestamp}\")\n\n    def tag(self):\n        raise NotImplementedError\n\n\nclass StockTagger(Tagger):\n    data_schema = StockTags\n    entity_schema = Stock\n\n    def tag(self):\n        raise NotImplementedError\n\n\n# the __all__ is generated\n__all__ = [\"Tagger\", \"StockTagger\"]\n"
  },
  {
    "path": "src/zvt/tasks/init_tag_system.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain import Block, BlockStock, Stock\nfrom zvt.tag.tag_service import build_default_main_tag, build_default_sub_tags\nfrom zvt.tag.tag_utils import (\n    build_initial_stock_pool_info,\n    build_initial_main_tag_info,\n    build_initial_sub_tag_info,\n    build_initial_industry_info,\n)\nfrom zvt.trading.trading_service import build_default_query_stock_quote_setting\n\nif __name__ == \"__main__\":\n    # init industry info\n    build_initial_industry_info()\n\n    # init tag info\n    build_initial_main_tag_info()\n    build_initial_sub_tag_info()\n    build_initial_stock_pool_info()\n    build_default_query_stock_quote_setting()\n\n    Stock.record_data(provider=\"em\")\n    Block.record_data(provider=\"em\", sleeping_time=0)\n    BlockStock.record_data(provider=\"em\", sleeping_time=0)\n    # init default main tag\n    build_default_main_tag()\n\n    # init default sub tags\n    build_default_sub_tags()\n"
  },
  {
    "path": "src/zvt/tasks/qmt_data_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\n\nimport pandas as pd\nfrom xtquant import xtdata\n\nfrom zvt import init_log\nfrom zvt.broker.qmt.qmt_quote import get_qmt_stocks\nfrom zvt.contract import AdjustType\nfrom zvt.recorders.qmt.meta import QMTStockRecorder\nfrom zvt.recorders.qmt.quotes import QMTStockKdataRecorder\n\nlogger = logging.getLogger(__name__)\n\n\ndef download_data(download_tick=False):\n    period = \"1d\"\n    xtdata.download_sector_data()\n    stock_codes = get_qmt_stocks()\n    stock_codes = sorted(stock_codes)\n    count = len(stock_codes)\n    download_status = {\"ok\": False}\n\n    def update_progress(data, download_status: dict = download_status):\n        logger.info(data)\n        finished = data[\"finished\"]\n        total = data[\"total\"]\n        download_status[\"finished\"] = finished\n        download_status[\"total\"] = total\n        if finished == total:\n            download_status[\"ok\"] = True\n\n    start_time = time.time()\n\n    xtdata.download_history_data2(stock_list=stock_codes, period=period, callback=update_progress)\n\n    while True:\n        logger.info(f\"current download_status:{download_status}\")\n        if download_status[\"ok\"]:\n            logger.info(f\"finish download 1d kdata\")\n            break\n        cost_time = time.time() - start_time\n        if cost_time >= 60 * 30:\n            logger.info(f\"timeout download 1d kdata\")\n            break\n        time.sleep(10)\n\n    QMTStockRecorder().run()\n    QMTStockKdataRecorder(adjust_type=AdjustType.qfq, sleeping_time=0).run()\n\n    xtdata.download_financial_data2(\n        stock_list=stock_codes, table_list=[\"Capital\"], start_time=\"\", end_time=\"\", callback=lambda x: print(x)\n    )\n    logger.info(\"download capital data ok\")\n\n    if download_tick:\n        for index, stock_code in enumerate(stock_codes):\n            logger.info(f\"run to {index + 1}/{count}\")\n\n            records = xtdata.get_market_data(\n                stock_list=[stock_code],\n                period=period,\n                count=5,\n                dividend_type=\"front\",\n                fill_data=False,\n            )\n            dfs = []\n            for col in records:\n                df = records[col].T\n                df.columns = [col]\n                dfs.append(df)\n            kdatas = pd.concat(dfs, axis=1)\n            start_time = kdatas.index.to_list()[0]\n            xtdata.download_history_data(stock_code, period=\"tick\", start_time=start_time)\n            logger.info(f\"download {stock_code} tick from {start_time} ok\")\n\n\nif __name__ == \"__main__\":\n    init_log(\"qmt_data_runner.log\")\n    from apscheduler.schedulers.background import BackgroundScheduler\n\n    sched = BackgroundScheduler()\n    download_data()\n    sched.add_job(func=download_data, trigger=\"cron\", hour=15, minute=30, day_of_week=\"mon-fri\")\n    sched.start()\n    sched._thread.join()\n"
  },
  {
    "path": "src/zvt/tasks/qmt_tick_runner.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt import init_log\nfrom zvt.broker.qmt.qmt_quote import record_stock_quote\n\nif __name__ == \"__main__\":\n    init_log(\"qmt_tick_runner.log\")\n    from apscheduler.schedulers.background import BackgroundScheduler\n\n    sched = BackgroundScheduler()\n    record_stock_quote()\n    sched.add_job(func=record_stock_quote, trigger=\"cron\", hour=9, minute=18, day_of_week=\"mon-fri\")\n    sched.start()\n    sched._thread.join()\n"
  },
  {
    "path": "src/zvt/tasks/stock_pool_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt import zvt_config, init_log\nfrom zvt.api.kdata import get_latest_kdata_date, get_kdata_schema\nfrom zvt.api.selector import get_entity_ids_by_filter\nfrom zvt.contract import AdjustType\nfrom zvt.contract.api import get_entity_ids, get_data_count\nfrom zvt.domain import (\n    Stock,\n    Block,\n    Block1dKdata,\n    BlockCategory,\n    Index,\n    Index1dKdata,\n    LimitUpInfo,\n)\nfrom zvt.factors import compute_top_stocks\nfrom zvt.informer import EmailInformer\nfrom zvt.informer.inform_utils import inform_email\nfrom zvt.informer.informer import QiyeWechatBot\nfrom zvt.tag.tag_stats import build_system_stock_pools, build_stock_pool_tag_stats\nfrom zvt.utils.recorder_utils import run_data_recorder\nfrom zvt.utils.time_utils import current_date\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\nemail_informer = EmailInformer()\nbot = QiyeWechatBot()\n\n\ndef report_limit_up():\n    latest_data = LimitUpInfo.query_data(order=LimitUpInfo.timestamp.desc(), limit=1, return_type=\"domain\")\n    timestamp = latest_data[0].timestamp\n    df = LimitUpInfo.query_data(start_timestamp=timestamp, end_timestamp=timestamp, columns=[\"code\", \"name\", \"reason\"])\n    df[\"reason\"] = df[\"reason\"].str.split(\"+\")\n    print(df)\n    email_informer.send_message(zvt_config[\"email_username\"], f\"{timestamp} 热门报告\", f\"{df}\")\n\n\ndef record_stock_data(data_provider=\"em\", entity_provider=\"em\", sleeping_time=0, adjust_type=AdjustType.qfq):\n    # 涨停数据\n    run_data_recorder(domain=LimitUpInfo, data_provider=None, force_update=False)\n    report_limit_up()\n\n    # A股指数\n    run_data_recorder(domain=Index, data_provider=data_provider, force_update=False)\n    # A股指数行情\n    run_data_recorder(\n        domain=Index1dKdata,\n        data_provider=data_provider,\n        entity_provider=entity_provider,\n        day_data=True,\n        sleeping_time=0,\n    )\n\n    # 板块(概念，行业)\n    run_data_recorder(domain=Block, entity_provider=entity_provider, data_provider=entity_provider, force_update=False)\n\n    entity_ids = get_entity_ids(entity_schema=Block, filters=[Block.timestamp == None], provider=entity_provider)\n    if entity_ids:\n        # 板块行情(概念，行业)\n        run_data_recorder(\n            entity_ids=entity_ids,\n            split_entity_ids_size=0,\n            domain=Block1dKdata,\n            entity_provider=entity_provider,\n            data_provider=entity_provider,\n            day_data=True,\n            sleeping_time=0,\n        )\n\n    # 报告新概念和行业\n    df = Block.query_data(\n        filters=[Block.category == BlockCategory.concept.value],\n        order=Block.list_date.desc(),\n        index=\"entity_id\",\n        limit=7,\n    )\n\n    inform_email(\n        entity_ids=df.index.tolist(),\n        entity_type=\"block\",\n        target_date=current_date(),\n        title=\"report 新概念\",\n        provider=\"em\",\n    )\n\n    target_date = get_latest_kdata_date(entity_type=\"index\", provider=data_provider)\n    # A股标的\n    run_data_recorder(domain=Stock, data_provider=data_provider, force_update=False)\n    # A股行情\n    normal_stock_ids = get_entity_ids_by_filter(entity_type=\"stock\", provider=\"em\", ignore_delist=True)\n\n    kdata_schema = get_kdata_schema(entity_type=\"stock\", level=\"1d\", adjust_type=adjust_type)\n\n    run_data_recorder(\n        entity_ids=normal_stock_ids,\n        split_entity_ids_size=0,\n        domain=kdata_schema,\n        data_provider=data_provider,\n        entity_provider=entity_provider,\n        day_data=True,\n        sleeping_time=5,\n        return_unfinished=True,\n        end_timestamp=target_date,\n    )\n\n\ndef record_stock_data_and_build_stock_pools(provider=\"em\", adjust_type=AdjustType.qfq):\n    # 获取 涨停 指数 板块(概念) 个股行情数据\n    record_stock_data(data_provider=provider, adjust_type=adjust_type)\n\n    kdata_date = get_latest_kdata_date(provider=provider, entity_type=\"stock\", adjust_type=adjust_type)\n\n    kdata_schema = get_kdata_schema(entity_type=\"stock\", level=\"1d\", adjust_type=adjust_type)\n    kdata_size = get_data_count(kdata_schema, filters=[kdata_schema.timestamp == kdata_date], provider=provider)\n\n    if kdata_size < 5000:\n        logger.warning(f\"当前行情数据量过少: {kdata_size}, 不进行股票池构建\")\n        email_informer.send_message(zvt_config[\"email_username\"], f\"{kdata_date} k线数据不完整\", \"k线数据不完整\")\n        bot.send_message(f\"{kdata_date} k线数据不完整\")\n        return\n\n    # 计算短期/中期最强 放量突破年线半年线个股\n    compute_top_stocks(start_date=kdata_date, entity_type=\"stock\", force_update=False, adjust_type=adjust_type)\n    # 放入股票池\n    build_system_stock_pools(start_date=kdata_date, entity_type=\"stock\", force_update=False)\n    for stock_pool_name in [\"主线\", \"年线\", \"大局\"]:\n        build_stock_pool_tag_stats(stock_pool_name=stock_pool_name, entity_type=\"stock\", force_rebuild_latest=True)\n\n    bot.send_message(f\"{kdata_date} 股票池构建完成\")\n\n\nif __name__ == \"__main__\":\n    init_log(\"stock_pool_runner.log\")\n    record_stock_data_and_build_stock_pools()\n    sched.add_job(\n        func=record_stock_data_and_build_stock_pools, trigger=\"cron\", hour=16, minute=00, day_of_week=\"mon-fri\"\n    )\n    sched.start()\n    sched._thread.join()\n"
  },
  {
    "path": "src/zvt/tasks/today_shoot_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\nfrom typing import List\n\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt import init_log\nfrom zvt.api.selector import get_shoot_today\nfrom zvt.domain import Stock\nfrom zvt.informer.inform_utils import add_to_eastmoney\nfrom zvt.tag.common import InsertMode\nfrom zvt.tag.tag_schemas import StockPools\nfrom zvt.tag.tag_stats import refresh_stock_pool\nfrom zvt.utils.time_utils import (\n    now_pd_timestamp,\n    current_date,\n)\n\nlogger = logging.getLogger(__name__)\n\nsched = BackgroundScheduler()\n\n\ndef calculate_shoot():\n    first_time = True\n    stock_pool_name = \"今日异动\"\n    while True:\n        if not first_time and Stock.in_trading_time() and not Stock.in_real_trading_time():\n            logger.info(f\"Sleeping time......\")\n            time.sleep(30 * 1)\n            continue\n\n        try:\n            shoot_up, shoot_down = get_shoot_today()\n\n            if len(shoot_up) > 80:\n                logger.warning(f\"shoot_up count size: {len(shoot_up)}\")\n                shoot_up = shoot_up[:80]\n\n            if shoot_up:\n                over_write = False\n                stock_pools: List[StockPools] = StockPools.query_data(\n                    filters=[StockPools.stock_pool_name == stock_pool_name],\n                    order=StockPools.timestamp.desc(),\n                    limit=1,\n                    return_type=\"domain\",\n                )\n                if stock_pools and (len(stock_pools[0].entity_ids) > 80):\n                    over_write = True\n\n                refresh_stock_pool(\n                    entity_ids=shoot_up,\n                    stock_pool_name=stock_pool_name,\n                    insert_mode=InsertMode.overwrite if over_write else InsertMode.append,\n                    target_date=current_date(),\n                )\n                add_to_eastmoney(\n                    codes=[entity_id.split(\"_\")[2] for entity_id in shoot_up],\n                    group=stock_pool_name,\n                    over_write=over_write,\n                )\n        except Exception as e:\n            logger.error(\"Failed to handle shoot today\", exc_info=True)\n\n        first_time = False\n\n        if not Stock.in_trading_time():\n            current_timestamp = now_pd_timestamp()\n            logger.info(f\"calculate shoot finished at: {current_timestamp}\")\n            break\n\n        logger.info(f\"Sleep 10 seconds\")\n        time.sleep(10)\n\n\nif __name__ == \"__main__\":\n    init_log(\"today_shoot_runner.log\")\n    calculate_shoot()\n    sched.add_job(func=calculate_shoot, trigger=\"cron\", hour=9, minute=50, day_of_week=\"mon-fri\")\n    sched.start()\n    sched._thread.join()\n"
  },
  {
    "path": "src/zvt/tasks/today_top_runner.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\nfrom typing import List\n\nimport eastmoneypy\nfrom apscheduler.schedulers.background import BackgroundScheduler\n\nfrom zvt import init_log\nfrom zvt.api.selector import get_top_up_today, get_top_down_today, get_top_vol\nfrom zvt.domain import Stock\nfrom zvt.informer.inform_utils import add_to_eastmoney\nfrom zvt.recorders.em.em_api import record_hot_topic\nfrom zvt.tag.common import InsertMode\nfrom zvt.tag.tag_schemas import StockPools\nfrom zvt.tag.tag_stats import refresh_stock_pool\nfrom zvt.utils.time_utils import now_pd_timestamp, current_date\n\nlogger = logging.getLogger(__name__)\n\n\nsched = BackgroundScheduler()\n\n\ndef calculate_top(clear_em=True):\n    if clear_em:\n        try:\n            eastmoneypy.del_group(\"今日强势\")\n        except:\n            pass\n\n    seed = 0\n    add_all_to_em = False\n    while True:\n        current_timestamp = now_pd_timestamp()\n\n        if not Stock.in_trading_time():\n            logger.info(f\"calculate top finished at: {current_timestamp}\")\n            break\n\n        if Stock.in_trading_time() and not Stock.in_real_trading_time():\n            logger.info(f\"Sleeping time......\")\n            time.sleep(60 * 1)\n            continue\n\n        if seed == 0:\n            record_hot_topic()\n            seed = seed + 1\n            if seed == 5:\n                seed = 0\n        target_date = current_date()\n        top_up_entity_ids = get_top_up_today()\n        if top_up_entity_ids:\n            refresh_stock_pool(\n                entity_ids=top_up_entity_ids,\n                stock_pool_name=\"今日强势\",\n                insert_mode=InsertMode.append,\n                target_date=target_date,\n            )\n            try:\n                to_added = top_up_entity_ids\n                if add_all_to_em:\n                    stock_pools: List[StockPools] = StockPools.query_data(\n                        filters=[StockPools.stock_pool_name == \"今日强势\"],\n                        order=StockPools.timestamp.desc(),\n                        limit=1,\n                        return_type=\"domain\",\n                    )\n                    if stock_pools:\n                        to_added = stock_pools[0].entity_ids\n                        if len(to_added) > 500:\n                            to_added = get_top_vol(entity_ids=to_added, limit=500)\n\n                add_to_eastmoney(\n                    codes=[entity_id.split(\"_\")[2] for entity_id in to_added], group=\"今日强势\", over_write=False\n                )\n                add_all_to_em = False\n            except Exception as e:\n                logger.error(e)\n                add_all_to_em = True\n\n        top_down_entity_ids = get_top_down_today()\n        if top_down_entity_ids:\n            refresh_stock_pool(\n                entity_ids=top_down_entity_ids,\n                stock_pool_name=\"今日弱势\",\n                insert_mode=InsertMode.append,\n                target_date=target_date,\n            )\n\n        logger.info(f\"Sleep 2 minutes to compute {target_date} top stock tag stats\")\n        time.sleep(60 * 2)\n\n\nif __name__ == \"__main__\":\n    init_log(\"today_top_runner.log\")\n    calculate_top(clear_em=False)\n    sched.add_job(func=calculate_top, trigger=\"cron\", hour=9, minute=26, day_of_week=\"mon-fri\")\n    sched.start()\n    sched._thread.join()\n"
  },
  {
    "path": "src/zvt/trader/__init__.py",
    "content": "# -*- coding: utf-8 -*-\nfrom enum import Enum\nfrom typing import Union, List\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.utils.decorator import to_string\n\n\nclass TradingSignalType(Enum):\n    open_long = \"open_long\"\n    open_short = \"open_short\"\n    keep_long = \"keep_long\"\n    keep_short = \"keep_short\"\n    close_long = \"close_long\"\n    close_short = \"close_short\"\n\n\nclass OrderType(Enum):\n    order_long = \"order_long\"\n    order_short = \"order_short\"\n    order_close_long = \"order_close_long\"\n    order_close_short = \"order_close_short\"\n\n\ndef trading_signal_type_to_order_type(trading_signal_type):\n    if trading_signal_type == TradingSignalType.open_long:\n        return OrderType.order_long\n    elif trading_signal_type == TradingSignalType.open_short:\n        return OrderType.order_short\n    elif trading_signal_type == TradingSignalType.close_long:\n        return OrderType.order_close_long\n    elif trading_signal_type == TradingSignalType.close_short:\n        return OrderType.order_close_short\n\n\n@to_string\nclass TradingSignal:\n    def __init__(\n        self,\n        entity_id: str,\n        due_timestamp: Union[str, pd.Timestamp],\n        happen_timestamp: Union[str, pd.Timestamp],\n        trading_level: IntervalLevel,\n        trading_signal_type: TradingSignalType,\n        position_pct: float = None,\n        order_money: float = None,\n        order_amount: int = None,\n    ):\n        \"\"\"\n\n        :param entity_id: the entity id\n        :param due_timestamp: the signal due time\n        :param happen_timestamp: the time when generating the signal\n        :param trading_level: the level\n        :param trading_signal_type:\n        :param position_pct: percentage of account to order\n        :param order_money: money to order\n        :param order_amount: amount to order\n        \"\"\"\n        self.entity_id = entity_id\n        self.due_timestamp = due_timestamp\n        self.happen_timestamp = happen_timestamp\n        self.trading_level = trading_level\n        self.trading_signal_type = trading_signal_type\n\n        if len([x for x in (position_pct, order_money, order_amount) if x is not None]) != 1:\n            assert False\n        # use position_pct or order_money or order_amount\n        self.position_pct = position_pct\n        # when close the position,just use position_pct\n        self.order_money = order_money\n        self.order_amount = order_amount\n\n\nclass TradingListener(object):\n    def on_trading_open(self, timestamp):\n        raise NotImplementedError\n\n    def on_trading_signals(self, trading_signals: List[TradingSignal]):\n        raise NotImplementedError\n\n    def on_trading_close(self, timestamp):\n        raise NotImplementedError\n\n    def on_trading_finish(self, timestamp):\n        raise NotImplementedError\n\n    def on_trading_error(self, timestamp, error):\n        raise NotImplementedError\n\n\nclass AccountService(TradingListener):\n    def get_positions(self):\n        pass\n\n    def get_current_position(self, entity_id, create_if_not_exist=False):\n        \"\"\"\n        overwrite it to provide your real position\n\n        :param entity_id:\n        \"\"\"\n        pass\n\n    def get_current_account(self):\n        pass\n\n    def order_by_position_pct(\n        self,\n        entity_id,\n        order_price,\n        order_timestamp,\n        order_type,\n        order_position_pct: float,\n    ):\n        pass\n\n    def order_by_money(\n        self,\n        entity_id,\n        order_price,\n        order_timestamp,\n        order_type,\n        order_money,\n    ):\n        pass\n\n    def order_by_amount(\n        self,\n        entity_id,\n        order_price,\n        order_timestamp,\n        order_type,\n        order_amount,\n    ):\n        pass\n\n\n# the __all__ is generated\n__all__ = [\"TradingSignalType\", \"TradingListener\", \"OrderType\", \"AccountService\", \"trading_signal_type_to_order_type\"]\n\n# __init__.py structure:\n# common code of the package\n# export interface in __all__ which contains __all__ of its sub modules\n\n# import all from submodule trader\nfrom .trader import *\nfrom .trader import __all__ as _trader_all\n\n__all__ += _trader_all\n\n# import all from submodule errors\nfrom .errors import *\nfrom .errors import __all__ as _errors_all\n\n__all__ += _errors_all\n\n# import all from submodule account\nfrom .sim_account import *\nfrom .sim_account import __all__ as _account_all\n\n__all__ += _account_all\n"
  },
  {
    "path": "src/zvt/trader/errors.py",
    "content": "# -*- coding: utf-8 -*-\nclass TraderError(Exception):\n    \"\"\"Base class for exceptions in this module.\"\"\"\n\n    pass\n\n\nclass InvalidOrderParamError(TraderError):\n    def __init__(self, message=\"invalid order param\"):\n        self.message = message\n\n\nclass NotEnoughMoneyError(TraderError):\n    def __init__(self, message=\"not enough money\"):\n        self.message = message\n\n\nclass NotEnoughPositionError(TraderError):\n    def __init__(self, message=\"not enough position\"):\n        self.message = message\n\n\nclass InvalidOrderError(TraderError):\n    def __init__(self, message=\"invalid order\"):\n        self.message = message\n\n\nclass WrongKdataError(TraderError):\n    def __init__(self, message=\"wrong kdata\"):\n        self.message = message\n\n\n# the __all__ is generated\n__all__ = [\n    \"TraderError\",\n    \"InvalidOrderParamError\",\n    \"NotEnoughMoneyError\",\n    \"NotEnoughPositionError\",\n    \"InvalidOrderError\",\n    \"WrongKdataError\",\n]\n"
  },
  {
    "path": "src/zvt/trader/sim_account.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport math\nfrom typing import List, Optional\n\nfrom zvt.api.kdata import get_kdata, get_kdata_schema\nfrom zvt.contract import IntervalLevel, TradableEntity, AdjustType\nfrom zvt.contract.api import get_db_session, decode_entity_id\nfrom zvt.trader import TradingSignal, AccountService, OrderType, trading_signal_type_to_order_type\nfrom zvt.trader.errors import (\n    NotEnoughMoneyError,\n    InvalidOrderError,\n    NotEnoughPositionError,\n    InvalidOrderParamError,\n    WrongKdataError,\n)\nfrom zvt.trader.trader_info_api import get_trader_info, clear_trader\nfrom zvt.trader.trader_models import AccountStatsModel, PositionModel\nfrom zvt.trader.trader_schemas import AccountStats, Position, Order, TraderInfo\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import to_pd_timestamp, to_date_time_str, TIME_FORMAT_ISO8601, is_same_date\nfrom zvt.utils.utils import fill_domain_from_dict\n\n\nclass SimAccountService(AccountService):\n    def __init__(\n        self,\n        entity_schema: TradableEntity,\n        trader_name,\n        timestamp,\n        provider=None,\n        level=IntervalLevel.LEVEL_1DAY,\n        base_capital=1000000,\n        buy_cost=0.001,\n        sell_cost=0.001,\n        slippage=0.001,\n        rich_mode=True,\n        adjust_type: AdjustType = None,\n        keep_history=False,\n        real_time=False,\n        kdata_use_begin_time=False,\n    ):\n        self.logger = logging.getLogger(self.__class__.__name__)\n\n        self.entity_schema = entity_schema\n        self.base_capital = base_capital\n        self.buy_cost = buy_cost\n        self.sell_cost = sell_cost\n        self.slippage = slippage\n        self.rich_mode = rich_mode\n        self.adjust_type = adjust_type\n        self.trader_name = trader_name\n\n        self.session = get_db_session(\"zvt\", data_schema=TraderInfo)\n        self.provider = provider\n        self.level = level\n        self.start_timestamp = timestamp\n        self.keep_history = keep_history\n        self.real_time = real_time\n        self.kdata_use_begin_time = kdata_use_begin_time\n\n        self.account = self.init_account()\n\n        account_info = (\n            f\"init_account,holding size:{len(self.account.positions)} profit:{self.account.profit} input_money:{self.account.input_money} \"\n            f\"cash:{self.account.cash} value:{self.account.value} all_value:{self.account.all_value}\"\n        )\n        self.logger.info(account_info)\n\n    def input_money(self, money=1000000):\n        self.account.input_money += money\n        self.account.cash += money\n\n    def clear_account(self):\n        trader_info = get_trader_info(session=self.session, trader_name=self.trader_name, return_type=\"domain\", limit=1)\n\n        if trader_info:\n            self.logger.warning(\"trader:{} has run before,old result would be deleted\".format(self.trader_name))\n            clear_trader(session=self.session, trader_name=self.trader_name)\n\n    def init_account(self) -> AccountStats:\n        # 清除历史数据\n        if not self.keep_history:\n            self.clear_account()\n\n        # 读取之前保存的账户\n        if self.keep_history:\n            self.account = self.load_account()\n            if self.account:\n                return self.account\n\n        # init trader info\n        entity_type = self.entity_schema.__name__.lower()\n        sim_account = TraderInfo(\n            id=self.trader_name,\n            entity_id=f\"trader_zvt_{self.trader_name}\",\n            timestamp=self.start_timestamp,\n            trader_name=self.trader_name,\n            entity_type=entity_type,\n            start_timestamp=self.start_timestamp,\n            provider=self.provider,\n            level=self.level.value,\n            real_time=self.real_time,\n            kdata_use_begin_time=self.kdata_use_begin_time,\n            kdata_adjust_type=self.adjust_type.value,\n        )\n        self.session.add(sim_account)\n        self.session.commit()\n\n        return AccountStats(\n            entity_id=f\"trader_zvt_{self.trader_name}\",\n            timestamp=self.start_timestamp,\n            trader_name=self.trader_name,\n            cash=self.base_capital,\n            input_money=self.base_capital,\n            all_value=self.base_capital,\n            value=0,\n            closing=False,\n        )\n\n    def load_account(self) -> AccountStats:\n        records = AccountStats.query_data(\n            filters=[AccountStats.trader_name == self.trader_name],\n            order=AccountStats.timestamp.desc(),\n            limit=1,\n            return_type=\"domain\",\n        )\n        if not records:\n            return self.account\n        latest_record: AccountStats = records[0]\n\n        # create new orm object from latest record\n        account_stats_model = AccountStatsModel.from_orm(latest_record)\n        account = AccountStats()\n        fill_domain_from_dict(account, account_stats_model.model_dump(exclude={\"id\", \"positions\"}))\n\n        positions: List[Position] = []\n        for position_domain in latest_record.positions:\n            position_model = PositionModel.from_orm(position_domain)\n            self.logger.debug(\"current position:{}\".format(position_model))\n            position = Position()\n            fill_domain_from_dict(position, position_model.model_dump())\n            positions.append(position)\n\n        account.positions = positions\n\n        return account\n\n    def on_trading_open(self, timestamp):\n        self.logger.info(\"on_trading_open:{}\".format(timestamp))\n        if is_same_date(timestamp, self.start_timestamp):\n            return\n        self.account = self.load_account()\n\n    def on_trading_error(self, timestamp, error):\n        pass\n\n    def on_trading_finish(self, timestamp):\n        pass\n\n    def on_trading_signals(self, trading_signals: List[TradingSignal]):\n        for trading_signal in trading_signals:\n            try:\n                self.handle_trading_signal(trading_signal)\n            except Exception as e:\n                self.logger.exception(e)\n                self.on_trading_error(timestamp=trading_signal.happen_timestamp, error=e)\n\n    def handle_trading_signal(self, trading_signal: TradingSignal):\n        entity_id = trading_signal.entity_id\n        happen_timestamp = trading_signal.happen_timestamp\n        order_type = trading_signal_type_to_order_type(trading_signal.trading_signal_type)\n        trading_level = trading_signal.trading_level.value\n        if order_type:\n            try:\n                kdata = get_kdata(\n                    provider=self.provider,\n                    entity_id=entity_id,\n                    level=trading_level,\n                    start_timestamp=happen_timestamp,\n                    end_timestamp=happen_timestamp,\n                    limit=1,\n                    adjust_type=self.adjust_type,\n                )\n            except Exception as e:\n                self.logger.error(e)\n                raise WrongKdataError(\"could not get kdata\")\n\n            if pd_is_not_null(kdata):\n                entity_type, _, _ = decode_entity_id(kdata[\"entity_id\"][0])\n\n                the_price = kdata[\"close\"][0]\n\n                if the_price:\n                    if trading_signal.position_pct:\n                        self.order_by_position_pct(\n                            entity_id=entity_id,\n                            order_price=the_price,\n                            order_timestamp=happen_timestamp,\n                            order_position_pct=trading_signal.position_pct,\n                            order_type=order_type,\n                        )\n                    elif trading_signal.order_money:\n                        self.order_by_money(\n                            entity_id=entity_id,\n                            order_price=the_price,\n                            order_timestamp=happen_timestamp,\n                            order_money=trading_signal.order_money,\n                            order_type=order_type,\n                        )\n                    elif trading_signal.order_amount:\n                        self.order_by_amount(\n                            entity_id=entity_id,\n                            order_price=the_price,\n                            order_timestamp=happen_timestamp,\n                            order_amount=trading_signal.order_amount,\n                            order_type=order_type,\n                        )\n                    else:\n                        assert False\n                else:\n                    self.logger.warning(\n                        \"ignore trading signal,wrong kdata,entity_id:{},timestamp:{},kdata:{}\".format(\n                            entity_id, happen_timestamp, kdata.to_dict(orient=\"records\")\n                        )\n                    )\n\n            else:\n                self.logger.warning(\n                    \"ignore trading signal,could not get kdata,entity_id:{},timestamp:{}\".format(\n                        entity_id, happen_timestamp\n                    )\n                )\n\n    def on_trading_close(self, timestamp):\n        self.logger.info(\"on_trading_close:{}\".format(timestamp))\n        # remove the empty position\n        self.account.positions = [\n            position for position in self.account.positions if position.long_amount > 0 or position.short_amount > 0\n        ]\n\n        # clear the data which need recomputing\n        the_id = \"{}_{}\".format(self.trader_name, to_date_time_str(timestamp, TIME_FORMAT_ISO8601))\n\n        self.account.value = 0\n        self.account.all_value = 0\n        for position in self.account.positions:\n            entity_type, _, _ = decode_entity_id(position.entity_id)\n            data_schema = get_kdata_schema(entity_type, level=IntervalLevel.LEVEL_1DAY, adjust_type=self.adjust_type)\n\n            kdata = get_kdata(\n                provider=self.provider,\n                level=IntervalLevel.LEVEL_1DAY,\n                entity_id=position.entity_id,\n                order=data_schema.timestamp.desc(),\n                end_timestamp=timestamp,\n                limit=1,\n                adjust_type=self.adjust_type,\n            )\n\n            closing_price = kdata[\"close\"][0]\n\n            position.available_long = position.long_amount\n            position.available_short = position.short_amount\n\n            if closing_price:\n                if (position.long_amount is not None) and position.long_amount > 0:\n                    position.value = position.long_amount * closing_price\n                    self.account.value += position.value\n                elif (position.short_amount is not None) and position.short_amount > 0:\n                    position.value = 2 * (position.short_amount * position.average_short_price)\n                    position.value -= position.short_amount * closing_price\n                    self.account.value += position.value\n\n                # refresh profit\n                position.profit = (closing_price - position.average_long_price) * position.long_amount\n                position.profit_rate = position.profit / (position.average_long_price * position.long_amount)\n\n            else:\n                self.logger.warning(\n                    \"could not refresh close value for position:{},timestamp:{}\".format(position.entity_id, timestamp)\n                )\n\n            position.id = \"{}_{}_{}\".format(\n                self.trader_name, position.entity_id, to_date_time_str(timestamp, TIME_FORMAT_ISO8601)\n            )\n            position.timestamp = to_pd_timestamp(timestamp)\n            position.account_stats_id = the_id\n\n        self.account.id = the_id\n        self.account.all_value = self.account.value + self.account.cash\n        self.account.closing = True\n        self.account.timestamp = to_pd_timestamp(timestamp)\n        self.account.profit = self.account.all_value - self.account.input_money\n        self.account.profit_rate = self.account.profit / self.account.input_money\n\n        self.session.add(self.account)\n        self.session.commit()\n        account_info = (\n            f\"on_trading_close,holding size:{len(self.account.positions)} profit:{self.account.profit} input_money:{self.account.input_money} \"\n            f\"cash:{self.account.cash} value:{self.account.value} all_value:{self.account.all_value}\"\n        )\n        self.logger.info(account_info)\n\n    def get_current_position(self, entity_id, create_if_not_exist=False) -> Optional[Position]:\n        \"\"\"\n        get position for entity_id\n\n        :param entity_id: the entity id\n        :param create_if_not_exist: create an empty position if not exist in current account\n        :return:\n        \"\"\"\n        for position in self.account.positions:\n            if position.entity_id == entity_id:\n                return position\n        if create_if_not_exist:\n            trading_t = self.entity_schema.get_trading_t()\n            current_position = Position(\n                trader_name=self.trader_name,\n                entity_id=entity_id,\n                long_amount=0,\n                available_long=0,\n                average_long_price=0,\n                short_amount=0,\n                available_short=0,\n                average_short_price=0,\n                profit=0,\n                value=0,\n                trading_t=trading_t,\n            )\n            # add it to account\n            self.account.positions.append(current_position)\n            return current_position\n        return None\n\n    def get_current_account(self):\n        return self.account\n\n    def update_position(self, current_position, order_amount, current_price, order_type, timestamp):\n        \"\"\"\n\n        :param timestamp:\n        :type timestamp:\n        :param current_position:\n        :type current_position: Position\n        :param order_amount:\n        :type order_amount:\n        :param current_price:\n        :type current_price:\n        :param order_type:\n        :type order_type:\n        \"\"\"\n        if order_type == OrderType.order_long:\n            need_money = (order_amount * current_price) * (1 + self.slippage + self.buy_cost)\n            if self.account.cash < need_money:\n                if self.rich_mode:\n                    self.input_money()\n                else:\n                    raise NotEnoughMoneyError()\n\n            self.account.cash -= need_money\n\n            # 计算平均价\n            long_amount = current_position.long_amount + order_amount\n            if long_amount == 0:\n                current_position.average_long_price = 0\n            current_position.average_long_price = (\n                current_position.average_long_price * current_position.long_amount + current_price * order_amount\n            ) / long_amount\n\n            current_position.long_amount = long_amount\n\n            if current_position.trading_t == 0:\n                current_position.available_long += order_amount\n\n        elif order_type == OrderType.order_short:\n            need_money = (order_amount * current_price) * (1 + self.slippage + self.buy_cost)\n            if self.account.cash < need_money:\n                if self.rich_mode:\n                    self.input_money()\n                else:\n                    raise NotEnoughMoneyError()\n\n            self.account.cash -= need_money\n\n            short_amount = current_position.short_amount + order_amount\n            current_position.average_short_price = (\n                current_position.average_short_price * current_position.short_amount + current_price * order_amount\n            ) / short_amount\n\n            current_position.short_amount = short_amount\n\n            if current_position.trading_t == 0:\n                current_position.available_short += order_amount\n\n        elif order_type == OrderType.order_close_long:\n            self.account.cash += order_amount * current_price * (1 - self.slippage - self.sell_cost)\n            # FIXME:如果没卖完，重新计算计算平均价\n\n            current_position.available_long -= order_amount\n            current_position.long_amount -= order_amount\n\n        elif order_type == OrderType.order_close_short:\n            self.account.cash += 2 * (order_amount * current_position.average_short_price)\n            self.account.cash -= order_amount * current_price * (1 + self.slippage + self.sell_cost)\n\n            current_position.available_short -= order_amount\n            current_position.short_amount -= order_amount\n        else:\n            assert False\n\n        # save the order info to db\n        order_id = \"{}_{}_{}_{}\".format(\n            self.trader_name, order_type, current_position.entity_id, to_date_time_str(timestamp, TIME_FORMAT_ISO8601)\n        )\n        order = Order(\n            id=order_id,\n            timestamp=to_pd_timestamp(timestamp),\n            trader_name=self.trader_name,\n            entity_id=current_position.entity_id,\n            order_price=current_price,\n            order_amount=order_amount,\n            order_type=order_type.value,\n            level=self.level.value,\n            status=\"success\",\n        )\n        self.session.add(order)\n        self.session.commit()\n\n    def cal_amount_by_money(\n        self,\n        order_price: float,\n        order_money: float,\n    ):\n        if order_money > self.account.cash:\n            if self.rich_mode:\n                self.input_money()\n            else:\n                raise NotEnoughMoneyError()\n\n        cost = order_price * (1 + self.slippage + self.buy_cost)\n        order_amount = order_money // cost\n\n        return order_amount\n\n    def cal_amount_by_position_pct(self, entity_id, order_price: float, order_position_pct: float, order_type):\n        if order_type == OrderType.order_long or order_type == OrderType.order_short:\n            cost = order_price * (1 + self.slippage + self.buy_cost)\n            want_pay = self.account.cash * order_position_pct\n            order_amount = want_pay // cost\n\n            if order_amount < 1:\n                if self.rich_mode:\n                    self.input_money()\n                    order_amount = max((self.account.cash * order_position_pct) // cost, 1)\n                else:\n                    raise NotEnoughMoneyError()\n            return order_amount\n        elif order_type == OrderType.order_close_long or order_type == OrderType.order_close_short:\n            current_position = self.get_current_position(entity_id=entity_id, create_if_not_exist=True)\n            if order_type == OrderType.order_close_long:\n                available = current_position.available_long\n            else:\n                available = current_position.available_short\n            if available > 0:\n                if order_position_pct == 1.0:\n                    order_amount = available\n                else:\n                    order_amount = math.floor(available * order_position_pct)\n                return order_amount\n            else:\n                raise NotEnoughPositionError()\n\n    def order_by_position_pct(\n        self,\n        entity_id,\n        order_timestamp,\n        order_price: float,\n        order_type: OrderType,\n        order_position_pct: float = 0.2,\n    ):\n        order_amount = self.cal_amount_by_position_pct(\n            entity_id=entity_id, order_price=order_price, order_position_pct=order_position_pct, order_type=order_type\n        )\n\n        self.order_by_amount(\n            entity_id=entity_id,\n            order_price=order_price,\n            order_amount=order_amount,\n            order_timestamp=order_timestamp,\n            order_type=order_type,\n        )\n\n    def order_by_money(\n        self,\n        entity_id,\n        order_timestamp,\n        order_price: float,\n        order_type: OrderType,\n        order_money: float,\n    ):\n        if order_type not in (OrderType.order_long, OrderType.order_short):\n            raise InvalidOrderParamError(f\"order type: {order_type.value} not support order_by_money\")\n\n        order_amount = self.cal_amount_by_money(order_price=order_price, order_money=order_money)\n        self.order_by_amount(\n            entity_id=entity_id,\n            order_price=order_price,\n            order_amount=order_amount,\n            order_timestamp=order_timestamp,\n            order_type=order_type,\n        )\n\n    def order_by_amount(\n        self,\n        entity_id,\n        order_price,\n        order_timestamp,\n        order_type,\n        order_amount,\n    ):\n        current_position = self.get_current_position(entity_id=entity_id, create_if_not_exist=True)\n\n        # 开多\n        if order_type == OrderType.order_long:\n            if current_position.short_amount > 0:\n                raise InvalidOrderError(\"close the short position before open long\")\n\n            self.update_position(current_position, order_amount, order_price, order_type, order_timestamp)\n        # 开空\n        elif order_type == OrderType.order_short:\n            if current_position.long_amount > 0:\n                raise InvalidOrderError(\"close the long position before open short\")\n\n            self.update_position(current_position, order_amount, order_price, order_type, order_timestamp)\n        # 平多\n        elif order_type == OrderType.order_close_long:\n            if current_position.available_long >= order_amount:\n                self.update_position(current_position, order_amount, order_price, order_type, order_timestamp)\n            else:\n                raise NotEnoughPositionError()\n        # 平空\n        elif order_type == OrderType.order_close_short:\n            if current_position.available_short >= order_amount:\n                self.update_position(current_position, order_amount, order_price, order_type, order_timestamp)\n            else:\n                raise Exception(\"not enough position\")\n\n\n# the __all__ is generated\n__all__ = [\"AccountService\", \"SimAccountService\"]\n"
  },
  {
    "path": "src/zvt/trader/trader.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\nfrom typing import List, Union, Type, Tuple\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel, TradableEntity, AdjustType\nfrom zvt.contract.drawer import Drawer\nfrom zvt.contract.factor import Factor, TargetType\nfrom zvt.contract.normal_data import NormalData\nfrom zvt.domain import Stock\nfrom zvt.trader import TradingSignal, TradingSignalType, TradingListener\nfrom zvt.trader.sim_account import SimAccountService\nfrom zvt.trader.trader_info_api import AccountStatsReader\nfrom zvt.trader.trader_schemas import AccountStats, Position\nfrom zvt.utils.time_utils import (\n    to_pd_timestamp,\n    now_pd_timestamp,\n    to_date_time_str,\n    is_same_date,\n    date_time_by_interval,\n)\n\n\nclass Trader(object):\n    entity_schema: Type[TradableEntity] = None\n\n    def __init__(\n        self,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        provider: str = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        trader_name: str = None,\n        real_time: bool = False,\n        kdata_use_begin_time: bool = False,\n        draw_result: bool = True,\n        rich_mode: bool = False,\n        adjust_type: AdjustType = None,\n        profit_threshold=(3, -0.3),\n        keep_history=False,\n        pre_load_days=365,\n    ) -> None:\n        assert self.entity_schema is not None\n        assert start_timestamp is not None\n        assert end_timestamp is not None\n\n        self.logger = logging.getLogger(__name__)\n\n        if trader_name:\n            self.trader_name = trader_name\n        else:\n            self.trader_name = type(self).__name__.lower()\n\n        self.entity_ids = entity_ids\n        self.exchanges = exchanges\n        self.codes = codes\n        self.provider = provider\n        # make sure the min level factor correspond to the provider and level\n        self.level = IntervalLevel(level)\n        self.real_time = real_time\n        self.start_timestamp = to_pd_timestamp(start_timestamp)\n        self.end_timestamp = to_pd_timestamp(end_timestamp)\n        self.pre_load_days = pre_load_days\n\n        self.trading_dates = self.entity_schema.get_trading_dates(\n            start_date=self.start_timestamp, end_date=self.end_timestamp\n        )\n\n        if real_time:\n            self.logger.info(\n                \"real_time mode, end_timestamp should be future,you could set it big enough for running forever\"\n            )\n            assert self.end_timestamp >= now_pd_timestamp()\n\n        # false: 收到k线时，该k线已完成\n        # true: 收到k线时，该k线可能未完成\n        self.kdata_use_begin_time = kdata_use_begin_time\n        self.draw_result = draw_result\n        self.rich_mode = rich_mode\n\n        self.adjust_type = AdjustType(adjust_type)\n        self.profit_threshold = profit_threshold\n        self.keep_history = keep_history\n\n        self.level_map_long_targets = {}\n        self.level_map_short_targets = {}\n        self.trading_signals: List[TradingSignal] = []\n        self.trading_signal_listeners: List[TradingListener] = []\n\n        self.account_service = SimAccountService(\n            entity_schema=self.entity_schema,\n            trader_name=self.trader_name,\n            timestamp=self.start_timestamp,\n            provider=self.provider,\n            level=self.level,\n            rich_mode=self.rich_mode,\n            adjust_type=self.adjust_type,\n            keep_history=self.keep_history,\n        )\n\n        self.register_trading_signal_listener(self.account_service)\n\n        self.factors = self.init_factors(\n            entity_ids=self.entity_ids,\n            entity_schema=self.entity_schema,\n            exchanges=self.exchanges,\n            codes=self.codes,\n            start_timestamp=date_time_by_interval(self.start_timestamp, -self.pre_load_days),\n            end_timestamp=self.end_timestamp,\n            adjust_type=self.adjust_type,\n        )\n\n        if self.factors:\n            self.trading_level_asc = list(set([IntervalLevel(factor.level) for factor in self.factors]))\n            self.trading_level_asc.sort()\n\n            self.logger.info(f\"trader level:{self.level},factors level:{self.trading_level_asc}\")\n\n            if self.level != self.trading_level_asc[0]:\n                raise Exception(\"trader level should be the min of the factors\")\n\n            self.trading_level_desc = list(self.trading_level_asc)\n            self.trading_level_desc.reverse()\n        else:\n            self.trading_level_asc = [self.level]\n            self.trading_level_desc = [self.level]\n        self.on_init()\n\n    def on_init(self):\n        self.logger.info(f\"trader:{self.trader_name} on_start\")\n\n    def init_entities(self, timestamp):\n        \"\"\"\n        init the entities for timestamp\n\n        :param timestamp:\n        :return:\n        \"\"\"\n        self.logger.info(f\"timestamp: {timestamp} init_entities\")\n        return self.entity_ids\n\n    def init_factors(\n        self, entity_ids, entity_schema, exchanges, codes, start_timestamp, end_timestamp, adjust_type=None\n    ):\n        \"\"\"\n        overwrite it to init factors if you want to use factor computing model\n        :param adjust_type:\n\n        \"\"\"\n        return []\n\n    def update_targets_by_level(\n        self,\n        level: IntervalLevel,\n        long_targets: List[str],\n        short_targets: List[str],\n    ) -> None:\n        \"\"\"\n        the trading signals is generated in min level,before that,we should cache targets of all levels\n\n        :param level:\n        :param long_targets:\n        :param short_targets:\n        \"\"\"\n        self.logger.debug(\n            f\"level:{level},old long targets:{self.level_map_long_targets.get(level)},new long targets:{long_targets}\"\n        )\n        self.level_map_long_targets[level] = long_targets\n\n        self.logger.debug(\n            f\"level:{level},old short targets:{self.level_map_short_targets.get(level)},new short targets:{short_targets}\"\n        )\n        self.level_map_short_targets[level] = short_targets\n\n    def get_long_targets_by_level(self, level: IntervalLevel) -> List[str]:\n        return self.level_map_long_targets.get(level)\n\n    def get_short_targets_by_level(self, level: IntervalLevel) -> List[str]:\n        return self.level_map_short_targets.get(level)\n\n    def on_targets_selected_from_levels(self, timestamp) -> Tuple[List[str], List[str]]:\n        \"\"\"\n        this method's called in every min level cycle to select targets in all levels generated by the previous cycle\n        the default implementation is selecting the targets in all levels\n        overwrite it for your custom logic\n\n        :param timestamp: current event time\n        :return: long targets, short targets\n        \"\"\"\n\n        long_selected = None\n\n        short_selected = None\n\n        for level in self.trading_level_desc:\n            long_targets = self.level_map_long_targets.get(level)\n            # long must in all\n            if long_targets:\n                long_targets = set(long_targets)\n                if long_selected is None:\n                    long_selected = long_targets\n                else:\n                    long_selected = long_selected & long_targets\n            else:\n                long_selected = set()\n\n            short_targets = self.level_map_short_targets.get(level)\n            # short any\n            if short_targets:\n                short_targets = set(short_targets)\n                if short_selected is None:\n                    short_selected = short_targets\n                else:\n                    short_selected = short_selected | short_targets\n\n        return long_selected, short_selected\n\n    def get_current_account(self) -> AccountStats:\n        return self.account_service.get_current_account()\n\n    def get_current_positions(self) -> List[Position]:\n        return self.get_current_account().positions\n\n    def long_position_control(self):\n        positions = self.get_current_positions()\n\n        position_pct = 1.0\n        if not positions:\n            # 没有仓位，买2成\n            position_pct = 0.2\n        elif len(positions) <= 10:\n            # 小于10个持仓，买5成\n            position_pct = 0.5\n\n        # 买完\n        return position_pct\n\n    def short_position_control(self):\n        # 卖完\n        return 1.0\n\n    def on_profit_control(self):\n        if self.profit_threshold and self.get_current_positions():\n            positive = self.profit_threshold[0]\n            negative = self.profit_threshold[1]\n            close_long_entity_ids = []\n            for position in self.get_current_positions():\n                if position.available_long > 1:\n                    # 止盈\n                    if position.profit_rate >= positive:\n                        close_long_entity_ids.append(position.entity_id)\n                        self.logger.info(f\"close profit {position.profit_rate} for {position.entity_id}\")\n                    # 止损\n                    if position.profit_rate <= negative:\n                        close_long_entity_ids.append(position.entity_id)\n                        self.logger.info(f\"cut lost {position.profit_rate} for {position.entity_id}\")\n\n            return close_long_entity_ids, None\n        return None, None\n\n    def buy(self, timestamp, entity_ids, ignore_in_position=True):\n        if ignore_in_position:\n            account = self.get_current_account()\n            current_holdings = []\n            if account.positions:\n                current_holdings = [\n                    position.entity_id\n                    for position in account.positions\n                    if position != None and position.available_long > 0\n                ]\n\n            entity_ids = set(entity_ids) - set(current_holdings)\n\n        if entity_ids:\n            position_pct = self.long_position_control()\n            position_pct = (1.0 / len(entity_ids)) * position_pct\n\n            due_timestamp = to_pd_timestamp(timestamp) + pd.Timedelta(seconds=self.level.to_second())\n            for entity_id in entity_ids:\n                trading_signal = TradingSignal(\n                    entity_id=entity_id,\n                    due_timestamp=due_timestamp,\n                    happen_timestamp=timestamp,\n                    trading_signal_type=TradingSignalType.open_long,\n                    trading_level=self.level,\n                    position_pct=position_pct,\n                )\n                self.trading_signals.append(trading_signal)\n\n    def sell(self, timestamp, entity_ids):\n        # current position\n        account = self.get_current_account()\n        current_holdings = []\n        if account.positions:\n            current_holdings = [\n                position.entity_id for position in account.positions if position != None and position.available_long > 0\n            ]\n\n        shorted = set(current_holdings) & set(entity_ids)\n\n        if shorted:\n            position_pct = self.short_position_control()\n\n            due_timestamp = to_pd_timestamp(timestamp) + pd.Timedelta(seconds=self.level.to_second())\n            for entity_id in shorted:\n                trading_signal = TradingSignal(\n                    entity_id=entity_id,\n                    due_timestamp=due_timestamp,\n                    happen_timestamp=timestamp,\n                    trading_signal_type=TradingSignalType.close_long,\n                    trading_level=self.level,\n                    position_pct=position_pct,\n                )\n                self.trading_signals.append(trading_signal)\n\n    def on_finish(self, timestamp):\n        self.on_trading_finish(timestamp)\n        # show the result\n        if self.draw_result:\n            reader = AccountStatsReader(trader_names=[self.trader_name])\n            df = reader.data_df\n            drawer = Drawer(\n                main_data=NormalData(df.copy()[[\"trader_name\", \"timestamp\", \"all_value\"]], category_field=\"trader_name\")\n            )\n            drawer.draw_line(show=True)\n\n    def on_factor_targets_filtered(\n        self, timestamp, level, factor: Factor, long_targets: List[str], short_targets: List[str]\n    ) -> Tuple[List[str], List[str]]:\n        \"\"\"\n        overwrite it to filter the targets from factor\n\n        :param timestamp: the event time\n        :param level: the level\n        :param factor: the factor\n        :param long_targets: the long targets from the factor\n        :param short_targets: the short targets from the factor\n        :return: filtered long targets, filtered short targets\n        \"\"\"\n        self.logger.info(f\"on_targets_filtered {level} long:{long_targets}\")\n\n        if len(long_targets) > 10:\n            long_targets = long_targets[0:10]\n        self.logger.info(f\"on_targets_filtered {level} filtered long:{long_targets}\")\n\n        return long_targets, short_targets\n\n    def in_trading_date(self, timestamp):\n        return to_date_time_str(timestamp) in self.trading_dates\n\n    def on_time(self, timestamp: pd.Timestamp):\n        \"\"\"\n        called in every min level cycle\n\n        :param timestamp: event time\n        \"\"\"\n        self.logger.debug(f\"current timestamp:{timestamp}\")\n\n    def on_trading_signals(self, trading_signals: List[TradingSignal]):\n        for l in self.trading_signal_listeners:\n            l.on_trading_signals(trading_signals)\n        # clear after all listener handling\n        self.trading_signals = []\n\n    def on_trading_open(self, timestamp):\n        for l in self.trading_signal_listeners:\n            l.on_trading_open(timestamp)\n\n    def on_trading_close(self, timestamp):\n        for l in self.trading_signal_listeners:\n            l.on_trading_close(timestamp)\n\n    def on_trading_finish(self, timestamp):\n        for l in self.trading_signal_listeners:\n            l.on_trading_finish(timestamp)\n\n    def on_trading_error(self, timestamp, error):\n        for l in self.trading_signal_listeners:\n            l.on_trading_error(timestamp, error)\n\n    def on_non_trading_day(self, timestamp):\n        self.logger.info(f\"on_non_trading_day: {timestamp}\")\n\n    def get_factors_by_level(self, level):\n        return [factor for factor in self.factors if factor.level == level]\n\n    def handle_factor_targets(self, timestamp: pd.Timestamp):\n        \"\"\"\n        select targets from factors\n        :param timestamp: the timestamp for next kdata coming\n        \"\"\"\n        # 一般来说factor计算 多标的 历史数据比较快，多级别的计算也比较方便，常用于全市场标的粗过滤\n        # 更细节的控制可以在on_targets_filtered里进一步处理\n        # 也可以在on_time里面设计一些自己的逻辑配合过滤\n        # 多级别的遍历算法要点:\n        # 1)计算各级别的 标的，通过 on_factor_targets_filtered 过滤，缓存在level_map_long_targets，level_map_short_targets\n        # 2)在最小的level通过 on_targets_selected_from_levels 根据多级别的缓存标的，生成最终的选中标的\n        # 这里需要注意的是，小级别拿到上一个周期的大级别的标的，这是合理的\n        for level in self.trading_level_asc:\n            self.logger.info(f\"level: {level}\")\n            # in every cycle, all level factor do its job in its time\n            if self.entity_schema.is_finished_kdata_timestamp(timestamp=timestamp, level=level):\n                all_long_targets = []\n                all_short_targets = []\n\n                # 从该level的factor中过滤targets\n                current_level_factors = self.get_factors_by_level(level=level)\n                for factor in current_level_factors:\n                    long_targets = factor.get_targets(timestamp=timestamp, target_type=TargetType.positive)\n                    short_targets = factor.get_targets(timestamp=timestamp, target_type=TargetType.negative)\n\n                    if long_targets or short_targets:\n                        long_targets, short_targets = self.on_factor_targets_filtered(\n                            timestamp=timestamp,\n                            level=level,\n                            factor=factor,\n                            long_targets=long_targets,\n                            short_targets=short_targets,\n                        )\n\n                    if long_targets:\n                        all_long_targets += long_targets\n                    if short_targets:\n                        all_short_targets += short_targets\n\n                # 将各级别的targets缓存在level_map_long_targets，level_map_short_targets\n                self.update_targets_by_level(level, all_long_targets, all_short_targets)\n\n    def run(self):\n        # iterate timestamp of the min level,e.g,9:30,9:35,9.40...for 5min level\n        # timestamp represents the timestamp in kdata\n        for timestamp in self.entity_schema.get_interval_timestamps(\n            start_date=self.start_timestamp, end_date=self.end_timestamp, level=self.level\n        ):\n            self.logger.info(f\">>>>>>>>>>\")\n\n            self.entity_ids = self.init_entities(timestamp=timestamp)\n            self.logger.info(f\"current entities: {self.entity_ids}\")\n\n            if not self.in_trading_date(timestamp=timestamp):\n                self.on_non_trading_day(timestamp=timestamp)\n                continue\n\n            # on_trading_open to set the account\n            if self.level >= IntervalLevel.LEVEL_1DAY or (\n                self.level != IntervalLevel.LEVEL_1DAY and self.entity_schema.is_open_timestamp(timestamp)\n            ):\n                self.on_trading_open(timestamp=timestamp)\n\n            # the signals were generated by previous timestamp kdata\n            if self.trading_signals:\n                self.logger.info(\"current signals:\")\n                for signal in self.trading_signals:\n                    self.logger.info(str(signal))\n                self.on_trading_signals(self.trading_signals)\n\n            for factor in self.factors:\n                factor.add_entities(entity_ids=self.entity_ids)\n\n            waiting_seconds = 0\n\n            if self.level == IntervalLevel.LEVEL_1DAY:\n                if is_same_date(timestamp, now_pd_timestamp()):\n                    while True:\n                        self.logger.info(f\"time is:{now_pd_timestamp()},just smoke for minutes\")\n                        time.sleep(600)\n                        current = now_pd_timestamp()\n                        if current.hour >= 19:\n                            waiting_seconds = 20\n                            break\n\n            elif self.real_time:\n                # all factor move on to handle the coming data\n                if self.kdata_use_begin_time:\n                    real_end_timestamp = timestamp + pd.Timedelta(seconds=self.level.to_second())\n                else:\n                    real_end_timestamp = timestamp\n\n                seconds = (now_pd_timestamp() - real_end_timestamp).total_seconds()\n                waiting_seconds = self.level.to_second() - seconds\n\n            # meaning the future kdata not ready yet,we could move on to check\n            if waiting_seconds > 0:\n                # iterate the factor from min to max which in finished timestamp kdata\n                for level in self.trading_level_asc:\n                    if self.entity_schema.is_finished_kdata_timestamp(timestamp=timestamp, level=level):\n                        factors = self.get_factors_by_level(level=level)\n                        for factor in factors:\n                            factor.move_on(to_timestamp=timestamp, timeout=waiting_seconds + 20)\n\n            if self.factors:\n                self.handle_factor_targets(timestamp=timestamp)\n\n            self.on_time(timestamp=timestamp)\n\n            long_selected, short_selected = self.on_targets_selected_from_levels(timestamp)\n\n            # 处理 止赢 止损\n            passive_short, _ = self.on_profit_control()\n            if passive_short:\n                if not short_selected:\n                    short_selected = passive_short\n                else:\n                    short_selected = list(set(short_selected) | set(passive_short))\n\n            if short_selected:\n                self.sell(timestamp=timestamp, entity_ids=short_selected)\n            if long_selected:\n                self.buy(timestamp=timestamp, entity_ids=long_selected)\n\n            # on_trading_close to calculate date account\n            if self.level >= IntervalLevel.LEVEL_1DAY or (\n                self.level != IntervalLevel.LEVEL_1DAY and self.entity_schema.is_close_timestamp(timestamp)\n            ):\n                self.on_trading_close(timestamp)\n\n            self.logger.info(f\"<<<<<<<<<<\\n\")\n\n        self.on_finish(timestamp)\n\n    def register_trading_signal_listener(self, listener):\n        if listener not in self.trading_signal_listeners:\n            self.trading_signal_listeners.append(listener)\n\n    def deregister_trading_signal_listener(self, listener):\n        if listener in self.trading_signal_listeners:\n            self.trading_signal_listeners.remove(listener)\n\n\nclass StockTrader(Trader):\n    entity_schema = Stock\n\n    def __init__(\n        self,\n        entity_ids: List[str] = None,\n        exchanges: List[str] = None,\n        codes: List[str] = None,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        provider: str = None,\n        level: Union[str, IntervalLevel] = IntervalLevel.LEVEL_1DAY,\n        trader_name: str = None,\n        real_time: bool = False,\n        kdata_use_begin_time: bool = False,\n        draw_result: bool = True,\n        rich_mode: bool = False,\n        adjust_type: AdjustType = AdjustType.hfq,\n        profit_threshold=(3, -0.3),\n        keep_history=False,\n    ) -> None:\n        super().__init__(\n            entity_ids,\n            exchanges,\n            codes,\n            start_timestamp,\n            end_timestamp,\n            provider,\n            level,\n            trader_name,\n            real_time,\n            kdata_use_begin_time,\n            draw_result,\n            rich_mode,\n            adjust_type,\n            profit_threshold,\n            keep_history,\n        )\n\n\n# the __all__ is generated\n__all__ = [\"Trader\", \"StockTrader\"]\n"
  },
  {
    "path": "src/zvt/trader/trader_info_api.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List, Union\n\nimport pandas as pd\n\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.api import get_data, get_db_session\nfrom zvt.contract.drawer import Drawer\nfrom zvt.contract.normal_data import NormalData\nfrom zvt.contract.reader import DataReader\nfrom zvt.trader.trader_schemas import AccountStats, Order, TraderInfo, Position\n\n\ndef clear_trader(trader_name, session=None):\n    if not session:\n        session = get_db_session(\"zvt\", data_schema=TraderInfo)\n    session.query(TraderInfo).filter(TraderInfo.trader_name == trader_name).delete()\n    session.query(AccountStats).filter(AccountStats.trader_name == trader_name).delete()\n    session.query(Position).filter(Position.trader_name == trader_name).delete()\n    session.query(Order).filter(Order.trader_name == trader_name).delete()\n    session.commit()\n\n\ndef get_trader_info(\n    trader_name=None,\n    return_type=\"df\",\n    start_timestamp=None,\n    end_timestamp=None,\n    filters=None,\n    session=None,\n    order=None,\n    limit=None,\n) -> List[TraderInfo]:\n    if trader_name:\n        if filters:\n            filters = filters + [TraderInfo.trader_name == trader_name]\n        else:\n            filters = [TraderInfo.trader_name == trader_name]\n\n    return get_data(\n        data_schema=TraderInfo,\n        entity_id=None,\n        codes=None,\n        level=None,\n        provider=\"zvt\",\n        columns=None,\n        return_type=return_type,\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        filters=filters,\n        session=session,\n        order=order,\n        limit=limit,\n    )\n\n\ndef get_order_securities(trader_name):\n    items = (\n        get_db_session(provider=\"zvt\", data_schema=Order)\n        .query(Order.entity_id)\n        .filter(Order.trader_name == trader_name)\n        .group_by(Order.entity_id)\n        .all()\n    )\n\n    return [item[0] for item in items]\n\n\nclass AccountStatsReader(DataReader):\n    def __init__(\n        self,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        level: IntervalLevel = IntervalLevel.LEVEL_1DAY,\n        trader_names: List[str] = None,\n    ) -> None:\n        self.trader_names = trader_names\n\n        self.filters = filters\n\n        if self.trader_names:\n            filter = [AccountStats.trader_name == name for name in self.trader_names]\n            if self.filters:\n                self.filters += filter\n            else:\n                self.filters = filter\n        super().__init__(\n            AccountStats,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            self.filters,\n            order,\n            None,\n            level,\n            category_field=\"trader_name\",\n            time_field=\"timestamp\",\n            keep_window=None,\n        )\n\n    def draw_line(self, show=True):\n        drawer = Drawer(\n            main_data=NormalData(\n                self.data_df.copy()[[\"trader_name\", \"timestamp\", \"all_value\"]], category_field=\"trader_name\"\n            )\n        )\n        return drawer.draw_line(show=show)\n\n\nclass OrderReader(DataReader):\n    def __init__(\n        self,\n        start_timestamp: Union[str, pd.Timestamp] = None,\n        end_timestamp: Union[str, pd.Timestamp] = None,\n        columns: List = None,\n        filters: List = None,\n        order: object = None,\n        level: IntervalLevel = None,\n        trader_names: List[str] = None,\n    ) -> None:\n        self.trader_names = trader_names\n\n        self.filters = filters\n\n        if self.trader_names:\n            filter = [Order.trader_name == name for name in self.trader_names]\n            if self.filters:\n                self.filters += filter\n            else:\n                self.filters = filter\n\n        super().__init__(\n            Order,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            start_timestamp,\n            end_timestamp,\n            columns,\n            self.filters,\n            order,\n            None,\n            level,\n            category_field=\"trader_name\",\n            time_field=\"timestamp\",\n            keep_window=None,\n        )\n\n\nif __name__ == \"__main__\":\n    reader = AccountStatsReader(trader_names=[\"000338_ma_trader\"])\n    drawer = Drawer(\n        main_data=NormalData(\n            reader.data_df.copy()[[\"trader_name\", \"timestamp\", \"all_value\"]], category_field=\"trader_name\"\n        )\n    )\n    drawer.draw_line()\n# the __all__ is generated\n__all__ = [\"clear_trader\", \"get_trader_info\", \"get_order_securities\", \"AccountStatsReader\", \"OrderReader\"]\n"
  },
  {
    "path": "src/zvt/trader/trader_models.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nfrom zvt.contract.model import MixinModel\n\n\nclass PositionModel(MixinModel):\n    #: 机器人名字\n    trader_name: str\n    #: 做多数量\n    long_amount: float\n    #: 可平多数量\n    available_long: float\n    #: 平均做多价格\n    average_long_price: float\n    #: 做空数量\n    short_amount: float\n    #: 可平空数量\n    available_short: float\n    #: 平均做空价格\n    average_short_price: float\n    #: 盈亏\n    profit: float\n    #: 盈亏比例\n    profit_rate: float\n    #: 市值 或者 占用的保证金(方便起见，总是100%)\n    value: float\n    #: 交易类型(0代表T+0,1代表T+1)\n    trading_t: int\n\n\nclass AccountStatsModel(MixinModel):\n    #: 投入金额\n    input_money: float\n    #: 机器人名字\n    trader_name: str\n    #: 具体仓位\n    positions: List[PositionModel]\n    #: 市值\n    value: float\n    #: 可用现金\n    cash: float\n    #: value + cash\n    all_value: float\n\n    #: 盈亏\n    profit: float\n    #: 盈亏比例\n    profit_rate: float\n\n    #: 收盘计算\n    closing: bool\n"
  },
  {
    "path": "src/zvt/trader/trader_schemas.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, String, DateTime, Boolean, Float, Integer, ForeignKey\nfrom sqlalchemy.orm import declarative_base\nfrom sqlalchemy.orm import relationship\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\nfrom zvt.utils.decorator import to_string\n\nTraderBase = declarative_base()\n\n\nclass TraderInfo(TraderBase, Mixin):\n    \"\"\"\n    trader info\n    \"\"\"\n\n    __tablename__ = \"trader_info\"\n    #: 机器人名字\n    trader_name = Column(String(length=128))\n\n    entity_type = Column(String(length=128))\n    start_timestamp = Column(DateTime)\n    end_timestamp = Column(DateTime)\n    provider = Column(String(length=32))\n    level = Column(String(length=32))\n    real_time = Column(Boolean)\n    kdata_use_begin_time = Column(Boolean)\n    kdata_adjust_type = Column(String(length=32))\n\n\n@to_string\nclass AccountStats(TraderBase, Mixin):\n    \"\"\"\n    account stats of every day\n    \"\"\"\n\n    __tablename__ = \"account_stats\"\n\n    input_money = Column(Float)\n\n    #: 机器人名字\n    trader_name = Column(String(length=128))\n    #: 可用现金\n    cash = Column(Float)\n    #: 具体仓位\n    positions = relationship(\"Position\", back_populates=\"account_stats\")\n    #: 市值\n    value = Column(Float)\n    #: 市值+cash\n    all_value = Column(Float)\n\n    #: 盈亏\n    profit = Column(Float)\n    #: 盈亏比例\n    profit_rate = Column(Float)\n\n    #: 收盘计算\n    closing = Column(Boolean)\n\n\n#: the position for specific entity of every day\nclass Position(TraderBase, Mixin):\n    __tablename__ = \"position\"\n\n    #: 机器人名字\n    trader_name = Column(String(length=128))\n    #: 账户id\n    account_stats_id = Column(Integer, ForeignKey(\"account_stats.id\"))\n    account_stats = relationship(\"AccountStats\", back_populates=\"positions\")\n\n    #: 做多数量\n    long_amount = Column(Float)\n    #: 可平多数量\n    available_long = Column(Float)\n    #: 平均做多价格\n    average_long_price = Column(Float)\n\n    #: 做空数量\n    short_amount = Column(Float)\n    #: 可平空数量\n    available_short = Column(Float)\n    #: 平均做空价格\n    average_short_price = Column(Float)\n\n    #: 盈亏\n    profit = Column(Float)\n    #: 盈亏比例\n    profit_rate = Column(Float)\n    #: 市值 或者 占用的保证金(方便起见，总是100%)\n    value = Column(Float)\n    #: 交易类型(0代表T+0,1代表T+1)\n    trading_t = Column(Integer)\n\n\n#: 委托单\nclass Order(TraderBase, Mixin):\n    __tablename__ = \"order\"\n\n    #: 机器人名字\n    trader_name = Column(String(length=128))\n    #: 订单价格\n    order_price = Column(Float)\n    #: 订单数量\n    order_amount = Column(Float)\n    #: 订单类型\n    order_type = Column(String(length=64))\n    #: 订单状态\n    status = Column(String(length=64))\n\n    #: 产生订单的selector/factor level\n    level = Column(String(length=32))\n\n\nregister_schema(db_name=\"trader_info\", schema_base=TraderBase, internal=True)\n\n# the __all__ is generated\n__all__ = [\"TraderInfo\", \"AccountStats\", \"Position\", \"Order\"]\n"
  },
  {
    "path": "src/zvt/trading/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n\n\n# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/trading/common.py",
    "content": "# -*- coding: utf-8 -*-\nfrom enum import Enum\n\n\nclass ExecutionStatus(Enum):\n    init = \"init\"\n    success = \"success\"\n    failed = \"failed\"\n\n\n# the __all__ is generated\n__all__ = [\"ExecutionStatus\"]\n"
  },
  {
    "path": "src/zvt/trading/trading_models.py",
    "content": "# -*- coding: utf-8 -*-\nfrom datetime import datetime\nfrom typing import List, Optional\nfrom typing import Union\n\nfrom pydantic import BaseModel, Field\nfrom pydantic import field_validator\n\nfrom zvt.common.query_models import TimeRange, OrderByType\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.contract.model import MixinModel, CustomModel\nfrom zvt.tag.tag_utils import get_stock_pool_names\nfrom zvt.trader import TradingSignalType\nfrom zvt.trading.common import ExecutionStatus\nfrom zvt.utils.time_utils import date_time_by_interval, current_date\nfrom zvt.utils.time_utils import tomorrow_date, to_pd_timestamp\n\n\nclass KdataRequestModel(BaseModel):\n    entity_ids: List[str]\n    data_provider: str = Field(default=\"em\")\n    start_timestamp: datetime = Field(default=date_time_by_interval(current_date(), -500))\n    end_timestamp: Optional[datetime] = Field(default=None)\n    level: IntervalLevel = Field(default=IntervalLevel.LEVEL_1DAY)\n    adjust_type: AdjustType = Field(default=AdjustType.qfq)\n\n\nclass KdataModel(BaseModel):\n    entity_id: str\n    code: str\n    name: str\n    level: IntervalLevel = Field(default=IntervalLevel.LEVEL_1DAY)\n    datas: List\n\n\nclass TSRequestModel(BaseModel):\n    entity_ids: List[str]\n    data_provider: str = Field(default=\"qmt\")\n    days_count: int = Field(default=5)\n\n\nclass TSModel(BaseModel):\n    entity_id: str\n    code: str\n    name: str\n    datas: List\n\n\nclass QuoteStatsModel(BaseModel):\n    #: UNIX时间戳\n    time: int\n    #: 涨停数\n    limit_up_count: int\n    #: 跌停数\n    limit_down_count: int\n    #: 上涨数\n    up_count: int\n    #: 下跌数\n    down_count: int\n    #: 涨幅\n    change_pct: float\n    #: 成交额\n    turnover: float\n    #: 昨日成交额\n    pre_turnover: Optional[float] = Field(default=None)\n    #: 同比\n    turnover_change: Optional[float] = Field(default=None)\n\n\nclass QueryStockQuoteSettingModel(CustomModel):\n    stock_pool_name: Optional[str] = Field(default=None)\n    main_tags: Optional[List[str]] = Field(default=None)\n\n\nclass BuildQueryStockQuoteSettingModel(CustomModel):\n    stock_pool_name: str\n    main_tags: Optional[List[str]] = Field(default=None)\n\n    @field_validator(\"stock_pool_name\")\n    @classmethod\n    def stock_pool_name_existed(cls, v: str) -> str:\n        if v not in get_stock_pool_names():\n            raise ValueError(f\"Invalid stock_pool_name: {v}\")\n        return v\n\n\nclass QueryTagQuoteModel(CustomModel):\n    stock_pool_name: str\n    main_tags: List[str]\n\n\nclass QueryStockQuoteModel(CustomModel):\n    main_tag: Optional[str] = Field(default=None)\n    entity_ids: Optional[List[str]] = Field(default=None)\n    stock_pool_name: Optional[str] = Field(default=None)\n    # the amount is not huge, just ignore now\n    limit: int = Field(default=100)\n    order_by_type: Optional[OrderByType] = Field(default=OrderByType.desc)\n    order_by_field: Optional[str] = Field(default=\"change_pct\")\n\n\nclass StockQuoteModel(MixinModel):\n    #: 代码\n    code: str\n    #: 名字\n    name: str\n\n    #: UNIX时间戳\n    time: int\n    #: 最新价\n    price: float\n    # 涨跌幅\n    change_pct: float\n    # 成交金额\n    turnover: float\n    # 换手率\n    turnover_rate: float\n    #: 是否涨停\n    is_limit_up: bool\n    #: 封涨停金额\n    # limit_up_amount: Optional[float] = Field(default=None)\n    #: 是否跌停\n    is_limit_down: bool\n    #: 封跌停金额\n    # limit_down_amount: Optional[float] = Field(default=None)\n    #: 5挡卖单金额\n    # ask_amount: float\n    #: 5挡买单金额\n    # bid_amount: float\n    #: 流通市值\n    float_cap: float\n    #: 总市值\n    total_cap: float\n\n    main_tag: Optional[str] = Field(default=None)\n    sub_tag: Union[str, None] = Field(default=None)\n    hidden_tags: Union[List[str], None] = Field(default=None)\n\n\nclass TagQuoteStatsModel(CustomModel):\n    main_tag: str\n    #: 涨停数\n    limit_up_count: int\n    #: 跌停数\n    limit_down_count: int\n    #: 上涨数\n    up_count: int\n    #: 下跌数\n    down_count: int\n    #: 涨幅\n    change_pct: float\n    #: 成交额\n    turnover: float\n\n\nclass StockQuoteStatsModel(CustomModel):\n    #: 涨停数\n    limit_up_count: int\n    #: 跌停数\n    limit_down_count: int\n    #: 上涨数\n    up_count: int\n    #: 下跌数\n    down_count: int\n    #: 涨幅\n    change_pct: float\n    #: 成交额\n    turnover: float\n\n    quotes: List[StockQuoteModel]\n\n\nclass TradingPlanModel(MixinModel):\n    stock_id: str\n    stock_code: str\n    stock_name: str\n    # 执行交易日\n    trading_date: datetime\n    # 预期开盘涨跌幅\n    expected_open_pct: float\n    buy_price: Optional[float]\n    sell_price: Optional[float]\n    # 操作理由\n    trading_reason: str\n    # 交易信号\n    trading_signal_type: TradingSignalType\n    # 执行状态\n    status: ExecutionStatus = Field(default=ExecutionStatus.init)\n    # 复盘\n    review: Optional[str]\n\n\nclass BuildTradingPlanModel(BaseModel):\n    stock_id: str\n    # 执行交易日\n    trading_date: datetime\n    # 预期开盘涨跌幅\n    expected_open_pct: float\n    buy_price: Optional[float]\n    sell_price: Optional[float]\n    # 操作理由\n    trading_reason: str\n    # 交易信号\n    trading_signal_type: TradingSignalType\n\n    @field_validator(\"trading_date\")\n    @classmethod\n    def trading_date_must_be_future(cls, v: str) -> str:\n        if to_pd_timestamp(v) < tomorrow_date():\n            raise ValueError(f\"trading_date: {v} must set to future trading date\")\n        return v\n\n\nclass QueryTradingPlanModel(BaseModel):\n    time_range: TimeRange\n\n\n# the __all__ is generated\n__all__ = [\n    \"QueryTagQuoteModel\",\n    \"QueryStockQuoteSettingModel\",\n    \"BuildQueryStockQuoteSettingModel\",\n    \"QueryStockQuoteModel\",\n    \"StockQuoteModel\",\n    \"StockQuoteStatsModel\",\n    \"TradingPlanModel\",\n    \"BuildTradingPlanModel\",\n    \"QueryTradingPlanModel\",\n]\n"
  },
  {
    "path": "src/zvt/trading/trading_schemas.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sqlalchemy import Column, Float, DateTime, Integer\nfrom sqlalchemy import String, JSON\nfrom sqlalchemy.orm import declarative_base\n\nfrom zvt.contract import Mixin\nfrom zvt.contract.register import register_schema\n\nTradingBase = declarative_base()\n\n\nclass TagQuoteStats(Mixin, TradingBase):\n    __tablename__ = \"tag_quote_stats\"\n    stock_pool_name = Column(String)\n    main_tag = Column(String)\n    limit_up_count = Column(Integer)\n    limit_down_count = Column(Integer)\n    up_count = Column(Integer)\n    down_count = Column(Integer)\n    change_pct = Column(Float)\n    turnover = Column(Float)\n\n\nclass TradingPlan(TradingBase, Mixin):\n    __tablename__ = \"trading_plan\"\n    stock_id = Column(String)\n    stock_code = Column(String)\n    stock_name = Column(String)\n    trading_date = Column(DateTime)\n    # 预期开盘涨跌幅\n    expected_open_pct = Column(Float, nullable=False)\n    buy_price = Column(Float)\n    sell_price = Column(Float)\n    # 操作理由\n    trading_reason = Column(String)\n    # 交易信号\n    trading_signal_type = Column(String)\n    # 执行状态\n    status = Column(String)\n    # 复盘\n    review = Column(String)\n\n\nclass QueryStockQuoteSetting(TradingBase, Mixin):\n    __tablename__ = \"query_stock_quote_setting\"\n    stock_pool_name = Column(String)\n    main_tags = Column(JSON)\n\n\nregister_schema(db_name=\"stock_trading\", schema_base=TradingBase, internal=True)\n\n\n# the __all__ is generated\n__all__ = [\"TradingPlan\", \"QueryStockQuoteSetting\"]\n"
  },
  {
    "path": "src/zvt/trading/trading_service.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nfrom typing import List\n\nimport pandas as pd\nfrom fastapi import HTTPException\nfrom fastapi_pagination.ext.sqlalchemy import paginate\n\nimport zvt.api.kdata as kdata_api\nimport zvt.contract.api as contract_api\nfrom zvt.common.query_models import TimeUnit\nfrom zvt.domain import Stock, StockQuote, Stock1mQuote\nfrom zvt.domain.quotes.stockhk.stockhk_quote import StockhkQuote\nfrom zvt.domain.quotes.stockus.stockus_quote import StockusQuote\nfrom zvt.tag.tag_schemas import StockTags, StockPools, StockPoolInfo\nfrom zvt.trading.common import ExecutionStatus\nfrom zvt.trading.trading_models import (\n    BuildTradingPlanModel,\n    QueryTradingPlanModel,\n    QueryTagQuoteModel,\n    QueryStockQuoteModel,\n    BuildQueryStockQuoteSettingModel,\n    KdataRequestModel,\n    TSRequestModel,\n)\nfrom zvt.trading.trading_schemas import TradingPlan, QueryStockQuoteSetting, TagQuoteStats\nfrom zvt.utils.pd_utils import pd_is_not_null\nfrom zvt.utils.time_utils import (\n    to_date_time_str,\n    to_pd_timestamp,\n    now_pd_timestamp,\n    date_time_by_interval,\n    current_date,\n    date_and_time,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef query_kdata(kdata_request_model: KdataRequestModel):\n    kdata_df = kdata_api.get_kdata(\n        entity_ids=kdata_request_model.entity_ids,\n        provider=kdata_request_model.data_provider,\n        start_timestamp=kdata_request_model.start_timestamp,\n        end_timestamp=kdata_request_model.end_timestamp,\n        adjust_type=kdata_request_model.adjust_type,\n    )\n    if pd_is_not_null(kdata_df):\n        kdata_df[\"timestamp\"] = kdata_df[\"timestamp\"].apply(lambda x: int(x.timestamp()))\n        kdata_df[\"data\"] = kdata_df.apply(\n            lambda x: x[\n                [\"timestamp\", \"open\", \"high\", \"low\", \"close\", \"volume\", \"turnover\", \"change_pct\", \"turnover_rate\"]\n            ].values.tolist(),\n            axis=1,\n        )\n        df = kdata_df.groupby(\"entity_id\").agg(\n            code=(\"code\", \"first\"),\n            name=(\"name\", \"first\"),\n            level=(\"level\", \"first\"),\n            datas=(\"data\", lambda data: list(data)),\n        )\n        df = df.reset_index(drop=False)\n        return df.to_dict(orient=\"records\")\n\n\ndef query_ts(ts_request_model: TSRequestModel):\n    trading_dates = kdata_api.get_recent_trade_dates(entity_type=\"stock\", days_count=ts_request_model.days_count)\n    ts_df = Stock1mQuote.query_data(\n        entity_ids=ts_request_model.entity_ids,\n        provider=ts_request_model.data_provider,\n        start_timestamp=trading_dates[0],\n    )\n    if pd_is_not_null(ts_df):\n        ts_df[\"data\"] = ts_df.apply(\n            lambda x: x[\n                [\"time\", \"price\", \"avg_price\", \"change_pct\", \"volume\", \"turnover\", \"turnover_rate\"]\n            ].values.tolist(),\n            axis=1,\n        )\n        df = ts_df.groupby(\"entity_id\").agg(\n            code=(\"code\", \"first\"),\n            name=(\"name\", \"first\"),\n            datas=(\"data\", lambda data: list(data)),\n        )\n        df = df.reset_index(drop=False)\n        return df.to_dict(orient=\"records\")\n\n\ndef build_trading_plan(build_trading_plan_model: BuildTradingPlanModel):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=TradingPlan)() as session:\n        stock_id = build_trading_plan_model.stock_id\n        trading_date_str = to_date_time_str(build_trading_plan_model.trading_date)\n        trading_date = to_pd_timestamp(trading_date_str)\n        signal = build_trading_plan_model.trading_signal_type.value\n        plan_id = f\"{stock_id}_{trading_date_str}_{signal}\"\n\n        datas = TradingPlan.query_data(\n            session=session, filters=[TradingPlan.id == plan_id], limit=1, return_type=\"domain\"\n        )\n        if datas:\n            assert len(datas) == 1\n            plan = datas[0]\n        else:\n            datas = Stock.query_data(provider=\"em\", entity_id=stock_id, return_type=\"domain\")\n            stock = datas[0]\n            plan = TradingPlan(\n                id=plan_id,\n                entity_id=stock_id,\n                stock_id=stock_id,\n                stock_code=stock.code,\n                stock_name=stock.name,\n                trading_date=trading_date,\n                expected_open_pct=build_trading_plan_model.expected_open_pct,\n                buy_price=build_trading_plan_model.buy_price,\n                sell_price=build_trading_plan_model.sell_price,\n                trading_reason=build_trading_plan_model.trading_reason,\n                trading_signal_type=signal,\n                status=ExecutionStatus.init.value,\n            )\n        plan.timestamp = now_pd_timestamp()\n        session.add(plan)\n        session.commit()\n        session.refresh(plan)\n        return plan\n\n\ndef query_trading_plan(query_trading_plan_model: QueryTradingPlanModel):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=TradingPlan)() as session:\n        time_range = query_trading_plan_model.time_range\n        if time_range.relative_time_range:\n            start_timestamp = date_time_by_interval(\n                current_date(), time_range.relative_time_range.interval, time_range.relative_time_range.time_unit\n            )\n            end_timestamp = None\n        else:\n            start_timestamp = time_range.absolute_time_range.start_timestamp\n            end_timestamp = time_range.absolute_time_range.end_timestamp\n        selectable = TradingPlan.query_data(\n            session=session, start_timestamp=start_timestamp, end_timestamp=end_timestamp, return_type=\"select\"\n        )\n        return paginate(session, selectable)\n\n\ndef get_current_trading_plan():\n    with contract_api.DBSession(provider=\"zvt\", data_schema=TradingPlan)() as session:\n        return TradingPlan.query_data(\n            session=session,\n            filters=[TradingPlan.status == ExecutionStatus.pending.value],\n            order=TradingPlan.trading_date.asc(),\n            return_type=\"domain\",\n        )\n\n\ndef get_future_trading_plan():\n    with contract_api.DBSession(provider=\"zvt\", data_schema=TradingPlan)() as session:\n        return TradingPlan.query_data(\n            session=session,\n            filters=[TradingPlan.status == ExecutionStatus.init.value],\n            order=TradingPlan.trading_date.asc(),\n            return_type=\"domain\",\n        )\n\n\ndef check_trading_plan():\n    with contract_api.DBSession(provider=\"zvt\", data_schema=TradingPlan)() as session:\n        plans = TradingPlan.query_data(\n            session=session,\n            filters=[TradingPlan.status == ExecutionStatus.init.value, TradingPlan.trading_date == current_date()],\n            order=TradingPlan.trading_date.asc(),\n            return_type=\"domain\",\n        )\n\n        logger.debug(f\"current plans:{plans}\")\n\n\ndef query_quote_stats():\n    quote_df = StockQuote.query_data(\n        return_type=\"df\",\n        filters=[StockQuote.change_pct >= -0.31, StockQuote.change_pct <= 0.31],\n        columns=[\"timestamp\", \"entity_id\", \"time\", \"change_pct\", \"turnover\", \"is_limit_up\", \"is_limit_down\"],\n    )\n    current_stats = cal_quote_stats(quote_df)\n    start_timestamp = current_stats[\"timestamp\"]\n\n    pre_date_df = Stock1mQuote.query_data(\n        filters=[Stock1mQuote.timestamp < to_date_time_str(start_timestamp)],\n        order=Stock1mQuote.timestamp.desc(),\n        limit=1,\n        columns=[\"timestamp\"],\n    )\n\n    if pd_is_not_null(pre_date_df):\n        pre_date = pre_date_df[\"timestamp\"].tolist()[0]\n\n        if start_timestamp.hour >= 15:\n            start_timestamp = date_and_time(pre_date, \"15:00\")\n        else:\n            start_timestamp = date_and_time(pre_date, f\"{start_timestamp.hour}:{start_timestamp.minute}\")\n        end_timestamp = date_time_by_interval(start_timestamp, 1, TimeUnit.minute)\n\n        pre_df = Stock1mQuote.query_data(\n            return_type=\"df\",\n            start_timestamp=start_timestamp,\n            end_timestamp=end_timestamp,\n            filters=[Stock1mQuote.change_pct >= -0.31, Stock1mQuote.change_pct <= 0.31],\n            columns=[\"timestamp\", \"entity_id\", \"time\", \"change_pct\", \"turnover\", \"is_limit_up\", \"is_limit_down\"],\n        )\n\n        if pd_is_not_null(pre_df):\n            pre_df = pre_df.drop_duplicates(subset=[\"entity_id\"], keep=\"first\")\n            pre_stats = cal_quote_stats(pre_df)\n            current_stats[\"pre_turnover\"] = pre_stats[\"turnover\"]\n            current_stats[\"turnover_change\"] = current_stats[\"turnover\"] - current_stats[\"pre_turnover\"]\n    else:\n        current_stats[\"pre_turnover\"] = current_stats[\"turnover\"]\n        current_stats[\"turnover_change\"] = 0\n\n    return current_stats\n\n\ndef cal_quote_stats(quote_df):\n    quote_df[\"ss\"] = 1\n\n    df = (\n        quote_df.groupby(\"ss\")\n        .agg(\n            timestamp=(\"timestamp\", \"last\"),\n            time=(\"time\", \"last\"),\n            up_count=(\"change_pct\", lambda x: (x > 0).sum()),\n            down_count=(\"change_pct\", lambda x: (x <= 0).sum()),\n            turnover=(\"turnover\", \"sum\"),\n            change_pct=(\"change_pct\", \"mean\"),\n            limit_up_count=(\"is_limit_up\", \"sum\"),\n            limit_down_count=(\"is_limit_down\", lambda x: (x == True).sum()),\n        )\n        .reset_index(drop=True)\n    )\n\n    return df.to_dict(orient=\"records\")[0]\n\n\ndef cal_tag_quote_stats(stock_pool_name):\n    stock_pools: List[StockPools] = StockPools.query_data(\n        filters=[StockPools.stock_pool_name == stock_pool_name],\n        order=StockPools.timestamp.desc(),\n        limit=1,\n        return_type=\"domain\",\n    )\n    if stock_pools:\n        entity_ids = stock_pools[0].entity_ids\n    else:\n        entity_ids = None\n\n    tag_df = StockTags.query_data(\n        entity_ids=entity_ids,\n        filters=[StockTags.main_tag.isnot(None)],\n        columns=[StockTags.entity_id, StockTags.main_tag],\n        return_type=\"df\",\n        index=\"entity_id\",\n    )\n\n    entity_ids = tag_df[\"entity_id\"].tolist()\n\n    quote_df = StockQuote.query_data(entity_ids=entity_ids, return_type=\"df\", index=\"entity_id\")\n    timestamp = quote_df[\"timestamp\"].tolist()[0]\n\n    df = pd.concat([tag_df, quote_df], axis=1)\n    grouped_df = (\n        df.groupby(\"main_tag\")\n        .agg(\n            up_count=(\"change_pct\", lambda x: (x > 0).sum()),\n            down_count=(\"change_pct\", lambda x: (x <= 0).sum()),\n            turnover=(\"turnover\", \"sum\"),\n            change_pct=(\"change_pct\", \"mean\"),\n            limit_up_count=(\"is_limit_up\", \"sum\"),\n            limit_down_count=(\"is_limit_down\", lambda x: (x == True).sum()),\n            total_count=(\"main_tag\", \"size\"),  # 添加计数，计算每个分组的总行数\n        )\n        .reset_index(drop=False)\n    )\n    grouped_df[\"stock_pool_name\"] = stock_pool_name\n\n    grouped_df[\"entity_id\"] = grouped_df[[\"stock_pool_name\", \"main_tag\"]].apply(\n        lambda se: \"{}_{}\".format(se[\"stock_pool_name\"], se[\"main_tag\"]), axis=1\n    )\n    grouped_df[\"timestamp\"] = timestamp\n    grouped_df[\"id\"] = grouped_df[[\"entity_id\", \"timestamp\"]].apply(\n        lambda se: \"{}_{}\".format(se[\"entity_id\"], to_date_time_str(se[\"timestamp\"])), axis=1\n    )\n\n    print(grouped_df)\n\n    contract_api.df_to_db(\n        df=grouped_df, data_schema=TagQuoteStats, provider=\"zvt\", force_update=True, drop_duplicates=False\n    )\n\n\ndef query_tag_quotes(query_tag_quote_model: QueryTagQuoteModel):\n    stock_pools: List[StockPools] = StockPools.query_data(\n        filters=[StockPools.stock_pool_name == query_tag_quote_model.stock_pool_name],\n        order=StockPools.timestamp.desc(),\n        limit=1,\n        return_type=\"domain\",\n    )\n    if stock_pools:\n        entity_ids = stock_pools[0].entity_ids\n    else:\n        entity_ids = None\n\n    entity_type = \"stock\"\n    if entity_ids:\n        entity_id = entity_ids[0]\n        entity_type, _, _ = contract_api.decode_entity_id(entity_id)\n\n    tag_df = StockTags.query_data(\n        entity_ids=entity_ids,\n        filters=[StockTags.entity_type == entity_type],\n        columns=[StockTags.entity_id, StockTags.main_tag],\n        return_type=\"df\",\n        index=\"entity_id\",\n    )\n\n    entity_ids = tag_df[\"entity_id\"].tolist()\n\n    if entity_type == \"stock\":\n        quote_df = StockQuote.query_data(entity_ids=entity_ids, return_type=\"df\", index=\"entity_id\")\n    elif entity_type == \"stockus\":\n        quote_df = StockusQuote.query_data(entity_ids=entity_ids, return_type=\"df\", index=\"entity_id\")\n    elif entity_type == \"stockhk\":\n        quote_df = StockhkQuote.query_data(entity_ids=entity_ids, return_type=\"df\", index=\"entity_id\")\n    else:\n        raise HTTPException(status_code=400, detail=f\"Unsupported entity type: {entity_type}\")\n\n    df = pd.concat([tag_df, quote_df], axis=1)\n    grouped_df = (\n        df.groupby(\"main_tag\")\n        .agg(\n            up_count=(\"change_pct\", lambda x: (x > 0).sum()),\n            down_count=(\"change_pct\", lambda x: (x <= 0).sum()),\n            turnover=(\"turnover\", \"sum\"),\n            change_pct=(\"change_pct\", \"mean\"),\n            limit_up_count=(\"is_limit_up\", \"sum\"),\n            limit_down_count=(\"is_limit_down\", lambda x: (x == True).sum()),\n            total_count=(\"main_tag\", \"size\"),  # 添加计数，计算每个分组的总行数\n        )\n        .reset_index(drop=False)\n    )\n    sorted_df = grouped_df.sort_values(by=[\"turnover\", \"total_count\"], ascending=[False, False])\n\n    return sorted_df.to_dict(orient=\"records\")\n\n\ndef query_stock_quotes(query_stock_quote_model: QueryStockQuoteModel):\n    entity_ids = None\n    if query_stock_quote_model.stock_pool_name:\n        stock_pool_name = query_stock_quote_model.stock_pool_name\n        stock_pool_info = StockPoolInfo.query_data(\n            filters=[StockPoolInfo.stock_pool_name == stock_pool_name],\n            return_type=\"domain\",\n        )\n        if not stock_pool_info:\n            raise HTTPException(status_code=404, detail=f\"Stock pool info {stock_pool_name} not found\")\n\n        if stock_pool_name != \"A股\":\n            stock_pools: List[StockPools] = StockPools.query_data(\n                filters=[StockPools.stock_pool_name == stock_pool_name],\n                order=StockPools.timestamp.desc(),\n                limit=1,\n                return_type=\"domain\",\n            )\n            if not stock_pools:\n                raise HTTPException(status_code=404, detail=f\"Stock pool {stock_pool_name} not found\")\n            if stock_pools:\n                entity_ids = stock_pools[0].entity_ids\n    else:\n        entity_ids = query_stock_quote_model.entity_ids\n\n    entity_type = \"stock\"\n    if entity_ids:\n        entity_id = entity_ids[0]\n        entity_type, _, _ = contract_api.decode_entity_id(entity_id)\n\n    if query_stock_quote_model.main_tag:\n        tags_dict = StockTags.query_data(\n            entity_ids=entity_ids,\n            filters=[StockTags.entity_type == entity_type, StockTags.main_tag == query_stock_quote_model.main_tag],\n            return_type=\"dict\",\n        )\n        if not tags_dict:\n            return None\n        entity_ids = [item[\"entity_id\"] for item in tags_dict]\n    else:\n        tags_dict = StockTags.query_data(\n            return_type=\"dict\",\n        )\n\n    entity_tags_map = {item[\"entity_id\"]: item for item in tags_dict}\n\n    if entity_type == \"stock\":\n        order = eval(\n            f\"StockQuote.{query_stock_quote_model.order_by_field}.{query_stock_quote_model.order_by_type.value}()\"\n        )\n        df = StockQuote.query_data(order=order, entity_ids=entity_ids, return_type=\"df\")\n    elif entity_type == \"stockus\":\n        order = eval(\n            f\"StockusQuote.{query_stock_quote_model.order_by_field}.{query_stock_quote_model.order_by_type.value}()\"\n        )\n        df = StockusQuote.query_data(order=order, entity_ids=entity_ids, return_type=\"df\")\n    elif entity_type == \"stockhk\":\n        order = eval(\n            f\"StockhkQuote.{query_stock_quote_model.order_by_field}.{query_stock_quote_model.order_by_type.value}()\"\n        )\n        df = StockhkQuote.query_data(order=order, entity_ids=entity_ids, return_type=\"df\")\n    else:\n        raise HTTPException(status_code=400, detail=f\"Unsupported entity type: {entity_type}\")\n\n    if not pd_is_not_null(df):\n        return None\n\n    def set_tags(quote):\n        entity_id = quote[\"entity_id\"]\n        main_tag = entity_tags_map.get(entity_id, {}).get(\"main_tag\", None)\n        sub_tag = entity_tags_map.get(entity_id, {}).get(\"sub_tag\", None)\n        active_hidden_tags = entity_tags_map.get(entity_id, {}).get(\"active_hidden_tags\", None)\n        if active_hidden_tags:\n            hidden_tags = list(active_hidden_tags.keys())\n        else:\n            hidden_tags = None\n        return pd.Series({\"main_tag\": main_tag, \"sub_tag\": sub_tag, \"hidden_tags\": hidden_tags})\n\n    df[[\"main_tag\", \"sub_tag\", \"hidden_tags\"]] = df.apply(set_tags, axis=1)\n\n    up_count = (df[\"change_pct\"] > 0).sum()\n    down_count = (df[\"change_pct\"] < 0).sum()\n    turnover = df[\"turnover\"].sum()\n    change_pct = df[\"change_pct\"].mean()\n    limit_up_count = df[\"is_limit_up\"].sum()\n    limit_down_count = df[\"is_limit_down\"].sum()\n\n    quotes = df.to_dict(orient=\"records\")\n\n    result = {\n        \"up_count\": up_count,\n        \"down_count\": down_count,\n        \"turnover\": turnover,\n        \"change_pct\": change_pct,\n        \"limit_up_count\": limit_up_count,\n        \"limit_down_count\": limit_down_count,\n        \"quotes\": quotes[: query_stock_quote_model.limit],\n    }\n    return result\n\n\ndef buy_stocks():\n    pass\n\n\ndef sell_stocks():\n    pass\n\n\ndef build_query_stock_quote_setting(build_query_stock_quote_setting_model: BuildQueryStockQuoteSettingModel):\n    with contract_api.DBSession(provider=\"zvt\", data_schema=QueryStockQuoteSetting)() as session:\n        the_id = \"admin_setting\"\n        datas = QueryStockQuoteSetting.query_data(ids=[the_id], session=session, return_type=\"domain\")\n        if datas:\n            query_setting = datas[0]\n        else:\n            query_setting = QueryStockQuoteSetting(entity_id=\"admin\", id=the_id)\n        query_setting.timestamp = current_date()\n        query_setting.stock_pool_name = build_query_stock_quote_setting_model.stock_pool_name\n        query_setting.main_tags = build_query_stock_quote_setting_model.main_tags\n        session.add(query_setting)\n        session.commit()\n        session.refresh(query_setting)\n        return query_setting\n\n\ndef build_default_query_stock_quote_setting():\n    datas = QueryStockQuoteSetting.query_data(ids=[\"admin_setting\"], return_type=\"domain\")\n    if datas:\n        return\n    build_query_stock_quote_setting(BuildQueryStockQuoteSettingModel(stock_pool_name=\"all\", main_tags=[\"消费电子\"]))\n\n\nif __name__ == \"__main__\":\n    # print(query_tag_quotes(QueryTagQuoteModel(stock_pool_name=\"all\", main_tags=[\"低空经济\", \"半导体\", \"化工\", \"消费电子\"])))\n    # print(query_stock_quotes(QueryStockQuoteModel(stock_pool_name=\"all\", main_tag=\"半导体\")))\n    print(query_quote_stats())\n# the __all__ is generated\n__all__ = [\n    \"build_trading_plan\",\n    \"query_trading_plan\",\n    \"get_current_trading_plan\",\n    \"get_future_trading_plan\",\n    \"check_trading_plan\",\n    \"query_stock_quotes\",\n    \"buy_stocks\",\n    \"sell_stocks\",\n    \"build_query_stock_quote_setting\",\n]\n"
  },
  {
    "path": "src/zvt/ui/__init__.py",
    "content": "# -*- coding: utf-8 -*-\nimport os\n\nimport dash\nimport dash_bootstrap_components as dbc\n\nassets_path = os.path.abspath(os.path.join(os.path.dirname(__file__), \"assets\"))\n\nzvt_app = dash.Dash(\n    __name__,\n    meta_tags=[{\"name\": \"viewport\", \"content\": \"width=device-width\"}],\n    assets_folder=assets_path,\n    external_stylesheets=[dbc.themes.BOOTSTRAP],\n)\n\nzvt_app.config.suppress_callback_exceptions = True\n\nserver = zvt_app.server\n"
  },
  {
    "path": "src/zvt/ui/apps/factor_app.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List\n\nimport dash_daq as daq\nfrom dash import dash\nfrom dash import dcc\nfrom dash import html\nfrom dash.dependencies import Input, Output, State\n\nfrom zvt.contract import Mixin\nfrom zvt.contract import zvt_context, IntervalLevel\nfrom zvt.contract.api import get_entities, get_schema_by_name, get_schema_columns\nfrom zvt.contract.drawer import StackedDrawer\nfrom zvt.trader.trader_info_api import AccountStatsReader, OrderReader, get_order_securities\nfrom zvt.trader.trader_info_api import get_trader_info\nfrom zvt.trader.trader_schemas import TraderInfo\nfrom zvt.ui import zvt_app\nfrom zvt.ui.components.dcc_components import get_account_stats_figure\nfrom zvt.utils.pd_utils import pd_is_not_null\n\naccount_readers = []\norder_readers = []\n\n# init the data\ntraders: List[TraderInfo] = []\n\ntrader_names: List[str] = []\n\n\ndef order_type_flag(order_type):\n    if order_type == \"order_long\" or order_type == \"order_close_short\":\n        return \"B\"\n    else:\n        return \"S\"\n\n\ndef order_type_color(order_type):\n    if order_type == \"order_long\" or order_type == \"order_close_short\":\n        return \"#ec0000\"\n    else:\n        return \"#00da3c\"\n\n\ndef load_traders():\n    global traders\n    global trader_names\n\n    traders = get_trader_info(return_type=\"domain\")\n    account_readers.clear()\n    order_readers.clear()\n    for trader in traders:\n        account_readers.append(AccountStatsReader(level=trader.level, trader_names=[trader.trader_name]))\n        order_readers.append(\n            OrderReader(start_timestamp=trader.start_timestamp, level=trader.level, trader_names=[trader.trader_name])\n        )\n\n    trader_names = [item.trader_name for item in traders]\n\n\nload_traders()\n\n\ndef factor_layout():\n    layout = html.Div(\n        [\n            # controls\n            html.Div(\n                className=\"three columns card\",\n                children=[\n                    html.Div(\n                        className=\"bg-white user-control\",\n                        children=[\n                            html.Div(\n                                className=\"padding-top-bot\",\n                                children=[\n                                    html.H6(\"select trader:\"),\n                                    dcc.Dropdown(\n                                        id=\"trader-selector\",\n                                        placeholder=\"select the trader\",\n                                        options=[{\"label\": item, \"value\": i} for i, item in enumerate(trader_names)],\n                                    ),\n                                ],\n                            ),\n                            # select entity type\n                            html.Div(\n                                className=\"padding-top-bot\",\n                                children=[\n                                    html.H6(\"select entity type:\"),\n                                    dcc.Dropdown(\n                                        id=\"entity-type-selector\",\n                                        placeholder=\"select entity type\",\n                                        options=[\n                                            {\"label\": name, \"value\": name}\n                                            for name in zvt_context.tradable_schema_map.keys()\n                                        ],\n                                        value=\"stock\",\n                                        clearable=False,\n                                    ),\n                                ],\n                            ),\n                            # select entity provider\n                            html.Div(\n                                className=\"padding-top-bot\",\n                                children=[\n                                    html.H6(\"select entity provider:\"),\n                                    dcc.Dropdown(id=\"entity-provider-selector\", placeholder=\"select entity provider\"),\n                                ],\n                            ),\n                            # select entity\n                            html.Div(\n                                className=\"padding-top-bot\",\n                                children=[\n                                    html.H6(\"select entity:\"),\n                                    dcc.Dropdown(id=\"entity-selector\", placeholder=\"select entity\"),\n                                ],\n                            ),\n                            # select levels\n                            html.Div(\n                                className=\"padding-top-bot\",\n                                children=[\n                                    html.H6(\"select levels:\"),\n                                    dcc.Dropdown(\n                                        id=\"levels-selector\",\n                                        options=[\n                                            {\"label\": level.name, \"value\": level.value}\n                                            for level in (IntervalLevel.LEVEL_1WEEK, IntervalLevel.LEVEL_1DAY)\n                                        ],\n                                        value=\"1d\",\n                                        multi=True,\n                                    ),\n                                ],\n                            ),\n                            # select factor\n                            html.Div(\n                                className=\"padding-top-bot\",\n                                children=[\n                                    html.H6(\"select factor:\"),\n                                    dcc.Dropdown(\n                                        id=\"factor-selector\",\n                                        placeholder=\"select factor\",\n                                        options=[\n                                            {\"label\": name, \"value\": name}\n                                            for name in zvt_context.factor_cls_registry.keys()\n                                        ],\n                                        value=\"TechnicalFactor\",\n                                    ),\n                                ],\n                            ),\n                            # select data\n                            html.Div(\n                                children=[\n                                    html.Div(\n                                        [\n                                            html.H6(\n                                                \"related/all data to show in sub graph\",\n                                                style={\"display\": \"inline-block\"},\n                                            ),\n                                            daq.BooleanSwitch(\n                                                id=\"data-switch\",\n                                                on=True,\n                                                style={\n                                                    \"display\": \"inline-block\",\n                                                    \"float\": \"right\",\n                                                    \"vertical-align\": \"middle\",\n                                                    \"padding\": \"8px\",\n                                                },\n                                            ),\n                                        ],\n                                    ),\n                                    dcc.Dropdown(id=\"data-selector\", placeholder=\"schema\"),\n                                ],\n                                style={\"padding-top\": \"12px\"},\n                            ),\n                            # select properties\n                            html.Div(\n                                children=[dcc.Dropdown(id=\"schema-column-selector\", placeholder=\"properties\")],\n                                style={\"padding-top\": \"6px\"},\n                            ),\n                        ],\n                    )\n                ],\n            ),\n            # Graph\n            html.Div(\n                className=\"nine columns card-left\",\n                children=[\n                    html.Div(\n                        id=\"trader-details\",\n                        className=\"bg-white\",\n                    ),\n                    html.Div(id=\"factor-details\"),\n                ],\n            ),\n        ]\n    )\n\n    return layout\n\n\n@zvt_app.callback(\n    [\n        Output(\"trader-details\", \"children\"),\n        Output(\"entity-type-selector\", \"options\"),\n        Output(\"entity-provider-selector\", \"options\"),\n        Output(\"entity-selector\", \"options\"),\n    ],\n    [\n        Input(\"trader-selector\", \"value\"),\n        Input(\"entity-type-selector\", \"value\"),\n        Input(\"entity-provider-selector\", \"value\"),\n    ],\n)\ndef update_trader_details(trader_index, entity_type, entity_provider):\n    if trader_index is not None:\n        # change entity_type options\n        entity_type = traders[trader_index].entity_type\n        if not entity_type:\n            entity_type = \"stock\"\n        entity_type_options = [{\"label\": entity_type, \"value\": entity_type}]\n\n        # account stats\n        account_stats = get_account_stats_figure(account_stats_reader=account_readers[trader_index])\n\n        providers = zvt_context.tradable_schema_map.get(entity_type).get_providers()\n        entity_provider_options = [{\"label\": name, \"value\": name} for name in providers]\n\n        # entities\n        entity_ids = get_order_securities(trader_name=trader_names[trader_index])\n        df = get_entities(\n            provider=entity_provider,\n            entity_type=entity_type,\n            entity_ids=entity_ids,\n            columns=[\"entity_id\", \"code\", \"name\"],\n            index=\"entity_id\",\n        )\n        entity_options = [\n            {\"label\": f'{entity_id}({entity[\"name\"]})', \"value\": entity_id} for entity_id, entity in df.iterrows()\n        ]\n\n        return account_stats, entity_type_options, entity_provider_options, entity_options\n    else:\n        entity_type_options = [{\"label\": name, \"value\": name} for name in zvt_context.tradable_schema_map.keys()]\n        account_stats = None\n        providers = zvt_context.tradable_schema_map.get(entity_type).get_providers()\n        entity_provider_options = [{\"label\": name, \"value\": name} for name in providers]\n        df = get_entities(\n            provider=entity_provider, entity_type=entity_type, columns=[\"entity_id\", \"code\", \"name\"], index=\"entity_id\"\n        )\n        entity_options = [\n            {\"label\": f'{entity_id}({entity[\"name\"]})', \"value\": entity_id} for entity_id, entity in df.iterrows()\n        ]\n        return account_stats, entity_type_options, entity_provider_options, entity_options\n\n\n@zvt_app.callback(\n    Output(\"data-selector\", \"options\"), [Input(\"entity-type-selector\", \"value\"), Input(\"data-switch\", \"on\")]\n)\ndef update_entity_selector(entity_type, related):\n    if entity_type is not None:\n        if related:\n            schemas = zvt_context.entity_map_schemas.get(entity_type)\n        else:\n            schemas = zvt_context.schemas\n        return [{\"label\": schema.__name__, \"value\": schema.__name__} for schema in schemas]\n    raise dash.PreventUpdate()\n\n\n@zvt_app.callback(Output(\"schema-column-selector\", \"options\"), [Input(\"data-selector\", \"value\")])\ndef update_column_selector(schema_name):\n    if schema_name:\n        schema = get_schema_by_name(name=schema_name)\n        cols = get_schema_columns(schema=schema)\n\n        return [{\"label\": col, \"value\": col} for col in cols]\n    raise dash.PreventUpdate()\n\n\n@zvt_app.callback(\n    Output(\"factor-details\", \"children\"),\n    [\n        Input(\"factor-selector\", \"value\"),\n        Input(\"entity-type-selector\", \"value\"),\n        Input(\"entity-selector\", \"value\"),\n        Input(\"levels-selector\", \"value\"),\n        Input(\"schema-column-selector\", \"value\"),\n    ],\n    [State(\"trader-selector\", \"value\"), State(\"data-selector\", \"value\")],\n)\ndef update_factor_details(factor, entity_type, entity, levels, columns, trader_index, schema_name):\n    if factor and entity_type and entity and levels:\n        sub_df = None\n        # add sub graph\n        if columns:\n            if type(columns) == str:\n                columns = [columns]\n            columns = columns + [\"entity_id\", \"timestamp\"]\n            schema: Mixin = get_schema_by_name(name=schema_name)\n            sub_df = schema.query_data(entity_id=entity, columns=columns)\n\n        # add trading signals as annotation\n        annotation_df = None\n        if trader_index is not None:\n            order_reader = order_readers[trader_index]\n            annotation_df = order_reader.data_df.copy()\n            annotation_df = annotation_df[annotation_df.entity_id == entity].copy()\n            if pd_is_not_null(annotation_df):\n                annotation_df[\"value\"] = annotation_df[\"order_price\"]\n                annotation_df[\"flag\"] = annotation_df[\"order_type\"].apply(lambda x: order_type_flag(x))\n                annotation_df[\"color\"] = annotation_df[\"order_type\"].apply(lambda x: order_type_color(x))\n            print(annotation_df.tail())\n\n        if type(levels) is list and len(levels) >= 2:\n            levels.sort()\n            drawers = []\n            for level in levels:\n                drawers.append(\n                    zvt_context.factor_cls_registry[factor](\n                        entity_schema=zvt_context.tradable_schema_map[entity_type], level=level, entity_ids=[entity]\n                    ).drawer()\n                )\n            stacked = StackedDrawer(*drawers)\n\n            return dcc.Graph(id=f\"{factor}-{entity_type}-{entity}\", figure=stacked.draw_kline(show=False, height=900))\n        else:\n            if type(levels) is list:\n                level = levels[0]\n            else:\n                level = levels\n            drawer = zvt_context.factor_cls_registry[factor](\n                entity_schema=zvt_context.tradable_schema_map[entity_type],\n                level=level,\n                entity_ids=[entity],\n                need_persist=False,\n            ).drawer()\n            if pd_is_not_null(sub_df):\n                drawer.add_sub_df(sub_df)\n            if pd_is_not_null(annotation_df):\n                drawer.annotation_df = annotation_df\n\n            return dcc.Graph(id=f\"{factor}-{entity_type}-{entity}\", figure=drawer.draw_kline(show=False, height=800))\n    raise dash.PreventUpdate()\n"
  },
  {
    "path": "src/zvt/ui/assets/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "src/zvt/ui/assets/base.css",
    "content": "/* Table of contents\n––––––––––––––––––––––––––––––––––––––––––––––––––\n- Plotly.js\n- Grid\n- Base Styles\n- Typography\n- Links\n- Buttons\n- Forms\n- Lists\n- Code\n- Tables\n- Spacing\n- Utilities\n- Clearing\n- Media Queries\n*/\n\n/* PLotly.js\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n/* plotly.js's modebar's z-index is 1001 by default\n * https://github.com/plotly/plotly.js/blob/7e4d8ab164258f6bd48be56589dacd9bdd7fded2/src/css/_modebar.scss#L5\n * In case a dropdown is above the graph, the dropdown's options\n * will be rendered below the modebar\n * Increase the select option's z-index\n */\n\n/* This was actually not quite right -\n   dropdowns were overlapping each other (edited October 26)\n\n.Select {\n    z-index: 1002;\n}*/\n\n/* Grid\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n.container {\n    position: relative;\n    width: 100%;\n    max-width: 960px;\n    margin: 0 auto;\n    padding: 0 20px;\n    box-sizing: border-box;\n}\n\n.column,\n.columns {\n    width: 100%;\n    float: left;\n    box-sizing: border-box;\n}\n\n/* For devices larger than 400px */\n@media (min-width: 400px) {\n    .container {\n        width: 85%;\n        padding: 0;\n    }\n}\n\n/* For devices larger than 550px */\n@media (min-width: 550px) {\n    .container {\n        width: 80%;\n    }\n\n    .column,\n    .columns {\n        margin-left: 4%;\n    }\n\n    .column:first-child,\n    .columns:first-child {\n        margin-left: 0;\n    }\n\n    .one.column,\n    .one.columns {\n        width: 4.66666666667%;\n    }\n\n    .two.columns {\n        width: 13.3333333333%;\n    }\n\n    .three.columns {\n        width: 22%;\n    }\n\n    .four.columns {\n        width: 30.6666666667%;\n    }\n\n    .five.columns {\n        width: 39.3333333333%;\n    }\n\n    .six.columns {\n        width: 48%;\n    }\n\n    .seven.columns {\n        width: 56.6666666667%;\n    }\n\n    .eight.columns {\n        width: 65.3333333333%;\n    }\n\n    .nine.columns {\n        width: 74.0%;\n    }\n\n    .ten.columns {\n        width: 82.6666666667%;\n    }\n\n    .eleven.columns {\n        width: 91.3333333333%;\n    }\n\n    .twelve.columns {\n        width: 100%;\n        margin-left: 0;\n    }\n\n    .one-third.column {\n        width: 30.6666666667%;\n    }\n\n    .two-thirds.column {\n        width: 65.3333333333%;\n    }\n\n    .one-half.column {\n        width: 48%;\n    }\n\n    /* Offsets */\n    .offset-by-one.column,\n    .offset-by-one.columns {\n        margin-left: 8.66666666667%;\n    }\n\n    .offset-by-two.column,\n    .offset-by-two.columns {\n        margin-left: 17.3333333333%;\n    }\n\n    .offset-by-three.column,\n    .offset-by-three.columns {\n        margin-left: 26%;\n    }\n\n    .offset-by-four.column,\n    .offset-by-four.columns {\n        margin-left: 34.6666666667%;\n    }\n\n    .offset-by-five.column,\n    .offset-by-five.columns {\n        margin-left: 43.3333333333%;\n    }\n\n    .offset-by-six.column,\n    .offset-by-six.columns {\n        margin-left: 52%;\n    }\n\n    .offset-by-seven.column,\n    .offset-by-seven.columns {\n        margin-left: 60.6666666667%;\n    }\n\n    .offset-by-eight.column,\n    .offset-by-eight.columns {\n        margin-left: 69.3333333333%;\n    }\n\n    .offset-by-nine.column,\n    .offset-by-nine.columns {\n        margin-left: 78.0%;\n    }\n\n    .offset-by-ten.column,\n    .offset-by-ten.columns {\n        margin-left: 86.6666666667%;\n    }\n\n    .offset-by-eleven.column,\n    .offset-by-eleven.columns {\n        margin-left: 95.3333333333%;\n    }\n\n    .offset-by-one-third.column,\n    .offset-by-one-third.columns {\n        margin-left: 34.6666666667%;\n    }\n\n    .offset-by-two-thirds.column,\n    .offset-by-two-thirds.columns {\n        margin-left: 69.3333333333%;\n    }\n\n    .offset-by-one-half.column,\n    .offset-by-one-half.columns {\n        margin-left: 52%;\n    }\n\n}\n\n\n/* Base Styles\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n/* NOTE\nhtml is set to 62.5% so that all the REM measurements throughout Skeleton\nare based on 10px sizing. So basically 1.5rem = 15px :) */\nhtml {\n    font-size: 62.5%;\n}\n\nbody {\n    font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */\n    line-height: 1.6;\n    font-weight: 400;\n    font-family: \"Open Sans\", \"HelveticaNeue\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n    color: rgb(50, 50, 50);\n}\n\n\n/* Typography\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\nh1, h2, h3, h4, h5, h6 {\n    margin-top: 0;\n    margin-bottom: 0;\n    font-weight: 300;\n}\n\nh1 {\n    font-size: 4.5rem;\n    line-height: 1.2;\n    letter-spacing: -.1rem;\n    margin-bottom: 2rem;\n}\n\nh2 {\n    font-size: 3.6rem;\n    line-height: 1.25;\n    letter-spacing: -.1rem;\n    margin-bottom: 1.8rem;\n    margin-top: 1.8rem;\n}\n\nh3 {\n    font-size: 3.0rem;\n    line-height: 1.3;\n    letter-spacing: -.1rem;\n    margin-bottom: 1.5rem;\n    margin-top: 1.5rem;\n}\n\nh4 {\n    font-size: 2.6rem;\n    line-height: 1.35;\n    letter-spacing: -.08rem;\n    margin-bottom: 1.2rem;\n    margin-top: 1.2rem;\n}\n\nh5 {\n    font-size: 2.2rem;\n    line-height: 1.5;\n    letter-spacing: -.05rem;\n    margin-bottom: 0.6rem;\n    margin-top: 0.6rem;\n}\n\nh6 {\n    font-size: 2.0rem;\n    line-height: 1.6;\n    letter-spacing: 0;\n    margin-bottom: 0.75rem;\n    margin-top: 0.75rem;\n}\n\np {\n    margin-top: 0;\n}\n\n\n/* Blockquotes\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\nblockquote {\n    border-left: 4px lightgrey solid;\n    padding-left: 1rem;\n    margin-top: 2rem;\n    margin-bottom: 2rem;\n    margin-left: 0rem;\n}\n\n\n/* Links\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\na {\n    color: #1EAEDB;\n    text-decoration: underline;\n    cursor: pointer;\n}\n\na:hover {\n    color: #0FA0CE;\n}\n\n\n/* Buttons\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n.button,\nbutton,\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n    display: inline-block;\n    height: 38px;\n    padding: 0 30px;\n    color: #555;\n    text-align: center;\n    font-size: 11px;\n    font-weight: 600;\n    line-height: 38px;\n    letter-spacing: .1rem;\n    text-transform: uppercase;\n    text-decoration: none;\n    white-space: nowrap;\n    background-color: transparent;\n    border-radius: 4px;\n    border: 1px solid #bbb;\n    cursor: pointer;\n    box-sizing: border-box;\n}\n\n.button:hover,\nbutton:hover,\ninput[type=\"submit\"]:hover,\ninput[type=\"reset\"]:hover,\ninput[type=\"button\"]:hover,\n.button:focus,\nbutton:focus,\ninput[type=\"submit\"]:focus,\ninput[type=\"reset\"]:focus,\ninput[type=\"button\"]:focus {\n    color: #333;\n    border-color: #888;\n    outline: 0;\n}\n\n.button.button-primary,\nbutton.button-primary,\ninput[type=\"submit\"].button-primary,\ninput[type=\"reset\"].button-primary,\ninput[type=\"button\"].button-primary {\n    color: #FFF;\n    background-color: #33C3F0;\n    border-color: #33C3F0;\n}\n\n.button.button-primary:hover,\nbutton.button-primary:hover,\ninput[type=\"submit\"].button-primary:hover,\ninput[type=\"reset\"].button-primary:hover,\ninput[type=\"button\"].button-primary:hover,\n.button.button-primary:focus,\nbutton.button-primary:focus,\ninput[type=\"submit\"].button-primary:focus,\ninput[type=\"reset\"].button-primary:focus,\ninput[type=\"button\"].button-primary:focus {\n    color: #FFF;\n    background-color: #1EAEDB;\n    border-color: #1EAEDB;\n}\n\n\n/* Forms\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ninput[type=\"text\"],\ninput[type=\"tel\"],\ninput[type=\"url\"],\ninput[type=\"password\"],\ntextarea,\nselect {\n    height: 38px;\n    padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */\n    background-color: #fff;\n    border: 1px solid #D1D1D1;\n    border-radius: 4px;\n    box-shadow: none;\n    box-sizing: border-box;\n    font-family: inherit;\n    font-size: inherit; /*https://stackoverflow.com/questions/6080413/why-doesnt-input-inherit-the-font-from-body*/\n}\n\n/* Removes awkward default styles on some inputs for iOS */\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ninput[type=\"text\"],\ninput[type=\"tel\"],\ninput[type=\"url\"],\ninput[type=\"password\"],\ntextarea {\n    -webkit-appearance: none;\n    -moz-appearance: none;\n    appearance: none;\n}\n\ntextarea {\n    min-height: 65px;\n    padding-top: 6px;\n    padding-bottom: 6px;\n}\n\ninput[type=\"email\"]:focus,\ninput[type=\"number\"]:focus,\ninput[type=\"search\"]:focus,\ninput[type=\"text\"]:focus,\ninput[type=\"tel\"]:focus,\ninput[type=\"url\"]:focus,\ninput[type=\"password\"]:focus,\ntextarea:focus,\nselect:focus {\n    border: 1px solid #33C3F0;\n    outline: 0;\n}\n\nlabel,\nlegend {\n    display: block;\n    margin-bottom: 0px;\n}\n\nfieldset {\n    padding: 0;\n    border-width: 0;\n}\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n    display: inline;\n}\n\nlabel > .label-body {\n    display: inline-block;\n    margin-left: .5rem;\n    font-weight: normal;\n}\n\n\n/* Lists\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\nul {\n    list-style: circle inside;\n}\n\nol {\n    list-style: decimal inside;\n}\n\nol, ul {\n    padding-left: 0;\n    margin-top: 0;\n}\n\nul ul,\nul ol,\nol ol,\nol ul {\n    margin: 1.5rem 0 1.5rem 3rem;\n    font-size: 90%;\n}\n\nli {\n    margin-bottom: 1rem;\n}\n\n\n/* Tables\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\ntable {\n    border-collapse: collapse;\n}\n\nth,\ntd {\n    padding: 12px 15px;\n    text-align: left;\n    border-bottom: 1px solid #E1E1E1;\n}\n\nth:first-child,\ntd:first-child {\n    padding-left: 0;\n}\n\nth:last-child,\ntd:last-child {\n    padding-right: 0;\n}\n\n\n/* Spacing\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\nbutton,\n.button {\n    margin-bottom: 0rem;\n}\n\ninput,\ntextarea,\nselect,\nfieldset {\n    margin-bottom: 0rem;\n}\n\npre,\ndl,\nfigure,\ntable,\nform {\n    margin-bottom: 0rem;\n}\n\np,\nul,\nol {\n    margin-bottom: 0.75rem;\n}\n\n/* Utilities\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n.u-full-width {\n    width: 100%;\n    box-sizing: border-box;\n}\n\n.u-max-full-width {\n    max-width: 100%;\n    box-sizing: border-box;\n}\n\n.u-pull-right {\n    float: right;\n}\n\n.u-pull-left {\n    float: left;\n}\n\n\n/* Misc\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\nhr {\n    margin-top: 3rem;\n    margin-bottom: 3.5rem;\n    border-width: 0;\n    border-top: 1px solid #E1E1E1;\n}\n\n\n/* Clearing\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n\n/* Self Clearing Goodness */\n.container:after,\n.row:after,\n.u-cf {\n    content: \"\";\n    display: table;\n    clear: both;\n}\n\n\n/* Media Queries\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n/*\nNote: The best way to structure the use of media queries is to create the queries\nnear the relevant code. For example, if you wanted to change the styles for buttons\non small devices, paste the mobile query code up in the buttons section and style it\nthere.\n*/\n\n\n/* Larger than mobile */\n@media (min-width: 400px) {\n}\n\n/* Larger than phablet (also point when grid becomes active) */\n@media (min-width: 550px) {\n}\n\n/* Larger than tablet */\n@media (min-width: 750px) {\n}\n\n/* Larger than desktop */\n@media (min-width: 1000px) {\n}\n\n/* Larger than Desktop HD */\n@media (min-width: 1200px) {\n}"
  },
  {
    "path": "src/zvt/ui/assets/custom.css",
    "content": "/*Fonts ––––––––––––––––––––––––––––––––––––––––––––––––––*/\n@import url('https://fonts.googleapis.com/css?family=Roboto&display=swap');\n\nbody {\n    margin: 0px;\n    padding: 0px;\n    background-color: #F3F4F9;\n    font-family: 'Roboto';\n    color: #203cb3;\n}\n\n.zvt-banner {\n    color: #7f7f7f;\n    font-weight: 600;\n    font-size: 20px;\n    background: #fafbfc;\n    padding: 12px;\n    padding-left: 24px;\n    border-bottom: 2px solid lightgray;\n}\n\n.zvt-nav {\n    color: #7f7f7f;\n    background: #fafbfc;\n    padding: 12px;\n    padding-left: 36px;\n}\n\n.div-logo {\n    display: inline-block;\n    float: right;\n}\n\n.logo {\n    height: 35px;\n    padding: 6px;\n    margin-top: 3px;\n}\n\n.h2-title, .h2-title-mobile {\n    font-family: 'Roboto';\n    display: inline-block;\n    letter-spacing: 3.8px;\n    font-weight: 800;\n    font-size: 20px;\n}\n\n.h2-title-mobile {\n    display: none;\n}\n\nh5, h6 {\n    font-family: 'Roboto';\n    font-weight: 600;\n    font-size: 16px;\n}\n\nh5 {\n    padding-left: 42px;\n}\n\n.alert {\n    padding: 20px;\n    background-color: #f44336;\n    color: white;\n}\n\n.bg-white {\n    background-color: white;\n    padding: 24px 32px;\n}\n\n\n.card {\n    padding: 24px 12px 24px 12px;\n    margin-left: 4%;\n}\n\n.card-left {\n    padding: 24px 12px 24px 12px;\n    margin-left: 0px;\n}\n\n.padding-top-bot {\n    padding-top: 12px;\n    padding-bottom: 18px;\n}\n\n.upload {\n    width: 100%;\n    line-height: 60px;\n    border-width: 1px;\n    border-style: dashed;\n    border-radius: 5px;\n    text-align: center;\n}\n\n.upload p, .upload a {\n    display: inline;\n}\n\n.Select-control {\n    border: 1px solid #203cb3;\n}\n\n@media only screen and (max-width: 320px) {\n    .Select-menu-outer, .Select-value {\n        font-size: 10.5px;\n    }\n\n    .upload {\n        padding: 5px;\n    }\n}\n\n/* mobile */\n@media only screen and (max-width: 768px) {\n    .upload {\n        line-height: 60px;\n        border-width: 1px;\n        border-style: dashed;\n        border-radius: 5px;\n        text-align: center;\n        font-size: small;\n    }\n\n    .columns {\n        width: 100%;\n    }\n\n    .card, .card-left {\n        padding: 12px;\n        margin: 0px;\n    }\n\n    .bg-white {\n        height: auto;\n    }\n\n    .logo {\n        height: 28px;\n        padding-left: 0px;\n        padding-bottom: 0px;\n    }\n\n    .div-logo {\n        float: left;\n        display: block;\n        width: 100%;\n    }\n\n    .h2-title {\n        display: none;\n    }\n\n    .h2-title-mobile {\n        display: block;\n        float: left;\n    }\n\n    .app-body {\n        margin-left: 0px;\n    }\n\n    .columns {\n        text-align: center;\n    }\n\n    .user-control {\n        padding-top: 24px;\n        padding-bottom: 24px;\n    }\n}\n"
  },
  {
    "path": "src/zvt/ui/components/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "src/zvt/ui/components/dcc_components.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom dash import dcc\n\nfrom zvt.api.kdata import get_kdata_schema\nfrom zvt.contract import zvt_context\nfrom zvt.contract.api import decode_entity_id\nfrom zvt.contract.drawer import Drawer\nfrom zvt.contract.reader import DataReader\nfrom zvt.trader.trader_info_api import OrderReader, AccountStatsReader\nfrom zvt.utils.pd_utils import pd_is_not_null\n\n\ndef order_type_color(order_type):\n    if order_type == \"order_long\" or order_type == \"order_close_short\":\n        return \"#ec0000\"\n    else:\n        return \"#00da3c\"\n\n\ndef order_type_flag(order_type):\n    if order_type == \"order_long\" or order_type == \"order_close_short\":\n        return \"B\"\n    else:\n        return \"S\"\n\n\ndef get_trading_signals_figure(\n    order_reader: OrderReader, entity_id: str, start_timestamp=None, end_timestamp=None, adjust_type=None\n):\n    entity_type, _, _ = decode_entity_id(entity_id)\n\n    data_schema = get_kdata_schema(entity_type=entity_type, level=order_reader.level, adjust_type=adjust_type)\n    if not start_timestamp:\n        start_timestamp = order_reader.start_timestamp\n    if not end_timestamp:\n        end_timestamp = order_reader.end_timestamp\n    kdata_reader = DataReader(\n        data_schema=data_schema,\n        entity_schema=zvt_context.tradable_schema_map.get(entity_type),\n        entity_ids=[entity_id],\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        level=order_reader.level,\n    )\n\n    # generate the annotation df\n    order_reader.move_on(timeout=0)\n    df = order_reader.data_df.copy()\n    df = df[df.entity_id == entity_id].copy()\n    if pd_is_not_null(df):\n        df[\"value\"] = df[\"order_price\"]\n        df[\"flag\"] = df[\"order_type\"].apply(lambda x: order_type_flag(x))\n        df[\"color\"] = df[\"order_type\"].apply(lambda x: order_type_color(x))\n    print(df.tail())\n\n    drawer = Drawer(main_df=kdata_reader.data_df, annotation_df=df)\n    return drawer.draw_kline(show=False, height=800)\n\n\ndef get_account_stats_figure(account_stats_reader: AccountStatsReader):\n    graph_list = []\n\n    # 账户统计曲线\n    if account_stats_reader:\n        fig = account_stats_reader.draw_line(show=False)\n\n        for trader_name in account_stats_reader.trader_names:\n            graph_list.append(dcc.Graph(id=\"{}-account\".format(trader_name), figure=fig))\n\n    return graph_list\n"
  },
  {
    "path": "src/zvt/utils/__init__.py",
    "content": "# the __all__ is generated\n__all__ = []\n"
  },
  {
    "path": "src/zvt/utils/decorator.py",
    "content": "# -*- coding: utf-8 -*-\ndef to_string(cls):\n    def __str__(self):\n        return \"%s(%s)\" % (type(self).__name__, \", \".join(\"%s=%s\" % item for item in vars(self).items()))\n\n    cls.__str__ = __str__\n    return cls\n\n\n# the __all__ is generated\n__all__ = [\"to_string\"]\n"
  },
  {
    "path": "src/zvt/utils/file_utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport os\nfrom typing import List, Optional\n\n\ndef list_all_files(\n    dir_path: str = \"./domain\", ext: Optional[str] = \".py\", excludes=None, includes=None, return_base_name=False\n) -> List[str]:\n    \"\"\"\n    list all files with extension in specific directory recursively\n\n    :param includes: including files, None means all\n    :param dir_path: the directory path\n    :param ext: file extension\n    :param excludes: excluding files\n    :param return_base_name: return file name if True otherwise abs path\n    :return:\n    \"\"\"\n    files = []\n    for entry in os.scandir(dir_path):\n        if entry.is_dir():\n            files += list_all_files(entry.path, ext=ext, excludes=excludes, return_base_name=return_base_name)\n        elif entry.is_file():\n            if not ext or (ext and entry.path.endswith(ext)):\n                if excludes and entry.path.endswith(excludes):\n                    continue\n                if includes and not entry.path.endswith(includes):\n                    continue\n                if return_base_name:\n                    files.append(os.path.basename(entry.path))\n                else:\n                    files.append(entry.path)\n        else:\n            pass\n    return files\n\n\n# the __all__ is generated\n__all__ = [\"list_all_files\"]\n"
  },
  {
    "path": "src/zvt/utils/git_utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport subprocess\n\n\ndef get_git_user_name():\n    try:\n        return subprocess.check_output([\"git\", \"config\", \"--get\", \"user.name\"]).decode(\"utf8\").strip()\n    except:\n        return \"foolcage\"\n\n\ndef get_git_user_email():\n    try:\n        return subprocess.check_output([\"git\", \"config\", \"--get\", \"user.email\"]).decode(\"utf8\").strip()\n    except:\n        return \"\"\n\n\n# the __all__ is generated\n__all__ = [\"get_git_user_name\", \"get_git_user_email\"]\n"
  },
  {
    "path": "src/zvt/utils/model_utils.py",
    "content": "# -*- coding: utf-8 -*-\ndef update_model(db_model, schema):\n    for key, value in schema.dict().items():\n        if value is not None:\n            setattr(db_model, key, value)\n\n\n# the __all__ is generated\n__all__ = [\"update_model\"]\n"
  },
  {
    "path": "src/zvt/utils/pd_utils.py",
    "content": "# -*- coding: utf-8 -*-\nfrom typing import List, Union\n\nimport pandas as pd\n\n\ndef drop_continue_duplicate(s: Union[pd.Series, pd.DataFrame], col=None):\n    if type(s) == pd.Series:\n        return s[s.shift() != s]\n    if type(s) == pd.DataFrame:\n        ss = s[col]\n        selected = ss[ss.shift() != ss]\n        return s.loc[selected.index, :]\n\n\ndef is_filter_result_df(df: pd.DataFrame):\n    return pd_is_not_null(df) and \"filter_result\" in df.columns\n\n\ndef is_score_result_df(df: pd.DataFrame):\n    return pd_is_not_null(df) and \"score_result\" in df.columns\n\n\ndef pd_is_not_null(df: Union[pd.DataFrame, pd.Series]):\n    return df is not None and not df.empty\n\n\ndef group_by_entity_id(input_df: pd.DataFrame):\n    return input_df.groupby(level=0)\n\n\ndef normalize_group_compute_result(group_result):\n    if group_result.index.nlevels == 3:\n        return group_result.reset_index(level=0, drop=True)\n    return group_result\n\n\ndef merge_filter_result(input_df: pd.DataFrame, filter_result: pd.Series):\n    if is_filter_result_df(input_df):\n        input_df[\"filter_result\"] = input_df[\"filter_result\"] & filter_result\n    else:\n        input_df[\"filter_result\"] = filter_result\n\n    return input_df\n\n\ndef index_df(df, index=\"timestamp\", inplace=True, drop=False, time_field=\"timestamp\"):\n    if time_field:\n        df[time_field] = pd.to_datetime(df[time_field])\n\n    if inplace:\n        df.set_index(index, drop=drop, inplace=inplace)\n    else:\n        df = df.set_index(index, drop=drop, inplace=inplace)\n\n    if type(index) == str:\n        df = df.sort_index()\n    elif type(index) == list:\n        df.index.names = index\n        level = list(range(len(index)))\n        df = df.sort_index(level=level)\n    return df\n\n\ndef normal_index_df(df, category_field=\"entity_id\", time_filed=\"timestamp\", drop=True, default_entity=\"entity\"):\n    if type(df) == pd.Series:\n        df = df.to_frame(name=\"value\")\n\n    index = [category_field, time_filed]\n    if is_normal_df(df):\n        return df\n\n    if df.index.nlevels == 1:\n        if (time_filed != df.index.name) and (time_filed not in df.columns):\n            assert False\n        if category_field not in df.columns:\n            df[category_field] = default_entity\n        if time_filed not in df.columns:\n            df = df.reset_index()\n\n    return index_df(df=df, index=index, drop=drop, time_field=\"timestamp\")\n\n\ndef is_normal_df(df, category_field=\"entity_id\", time_filed=\"timestamp\"):\n    if pd_is_not_null(df) and df.index.nlevels == 2:\n        names = df.index.names\n\n        if len(names) == 2 and names[0] == category_field and names[1] == time_filed:\n            return True\n\n    return False\n\n\ndef df_subset(df, columns=None):\n    if columns:\n        return df.loc[:, columns]\n    return df\n\n\ndef fill_with_same_index(df_list: List[pd.DataFrame]):\n    idx = None\n    for df in df_list:\n        if idx is None:\n            idx = df.index\n        else:\n            idx = idx.append(df.index).drop_duplicates()\n    idx = idx.sort_values()\n\n    result = []\n    for df in df_list:\n        # print(df[df.index.duplicated()])\n        added_index = idx.difference(df.index.drop_duplicates())\n        added_df = pd.DataFrame(index=added_index, columns=df.columns)\n\n        # df1 = df.reindex(idx)\n        # df1 = df.append(added_df)\n        df1 = pd.concat([df, added_df])\n        df1 = df1.sort_index()\n        result.append(df1)\n    return result\n\n\n# the __all__ is generated\n__all__ = [\n    \"drop_continue_duplicate\",\n    \"is_filter_result_df\",\n    \"is_score_result_df\",\n    \"pd_is_not_null\",\n    \"group_by_entity_id\",\n    \"normalize_group_compute_result\",\n    \"merge_filter_result\",\n    \"index_df\",\n    \"normal_index_df\",\n    \"is_normal_df\",\n    \"df_subset\",\n    \"fill_with_same_index\",\n]\n"
  },
  {
    "path": "src/zvt/utils/recorder_utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport time\nfrom typing import Type\n\nimport zvt as zvt\nfrom zvt import zvt_config\nfrom zvt.informer import EmailInformer\n\nlogger = logging.getLogger(\"__name__\")\n\n\ndef run_data_recorder(\n    domain: Type[\"zvt.contract.Mixin\"],\n    entity_provider=None,\n    data_provider=None,\n    entity_ids=None,\n    split_entity_ids_size=0,\n    retry_times=10,\n    sleeping_time=10,\n    return_unfinished=False,\n    **recorder_kv,\n):\n    if (entity_ids is not None) and split_entity_ids_size > 0:\n        import numpy as np\n\n        size = len(entity_ids)\n        if size >= split_entity_ids_size:\n            step_size = int(size / split_entity_ids_size)\n            if size % split_entity_ids_size:\n                step_size = step_size + 1\n        else:\n            step_size = 1\n\n        entity_ids_list = np.array_split(entity_ids, step_size)\n\n        for split_entity_ids in entity_ids_list:\n            run_data_recorder(\n                domain=domain,\n                entity_provider=entity_provider,\n                data_provider=data_provider,\n                entity_ids=list(split_entity_ids),\n                split_entity_ids_size=0,\n                retry_times=retry_times,\n                sleeping_time=sleeping_time,\n                return_unfinished=return_unfinished,\n                **recorder_kv,\n            )\n            time.sleep(90)\n        return\n\n    logger.info(f\" record data: {domain.__name__}, entity_provider: {entity_provider}, data_provider: {data_provider}\")\n\n    unfinished_entity_ids = entity_ids\n    email_action = EmailInformer()\n\n    while retry_times > 0:\n        try:\n            entity_size = len(entity_ids) if entity_ids else \"all\"\n\n            if return_unfinished:\n                unfinished_entity_ids = domain.record_data(\n                    entity_ids=unfinished_entity_ids,\n                    provider=data_provider,\n                    sleeping_time=sleeping_time,\n                    return_unfinished=return_unfinished,\n                    **recorder_kv,\n                )\n                if unfinished_entity_ids:\n                    logger.info(f\"unfinished_entity_ids({len(unfinished_entity_ids)}): {unfinished_entity_ids}\")\n                    raise Exception(\"Would retry with unfinished latter!\")\n            else:\n                domain.record_data(\n                    entity_ids=entity_ids,\n                    provider=data_provider,\n                    sleeping_time=sleeping_time,\n                    return_unfinished=return_unfinished,\n                    **recorder_kv,\n                )\n\n            msg = f\"record {domain.__name__} {entity_size} success\"\n            logger.info(msg)\n            email_action.send_message(zvt_config[\"email_username\"], msg, msg)\n            break\n        except Exception as e:\n            logger.exception(\"report error:{}\".format(e))\n            time.sleep(60 * 2)\n            retry_times = retry_times - 1\n            if retry_times == 0:\n                email_action.send_message(\n                    zvt_config[\"email_username\"],\n                    f\"record {domain.__name__} error\",\n                    f\"record {domain.__name__} error: {e}\",\n                )\n\n\nif __name__ == \"__main__\":\n    run_data_recorder()\n# the __all__ is generated\n__all__ = [\"run_data_recorder\"]\n"
  },
  {
    "path": "src/zvt/utils/str_utils.py",
    "content": "# -*- coding: utf-8 -*-\n\n\ndef to_snake_str(input: str) -> str:\n    parts = []\n    part = \"\"\n    for c in input:\n        if c.isupper() or c.isdigit():\n            if part:\n                parts.append(part)\n            part = c.lower()\n        else:\n            part = part + c\n\n    parts.append(part)\n\n    if len(parts) > 1:\n        return \"_\".join(parts)\n    elif parts:\n        return parts[0]\n\n\ndef to_camel_str(input: str) -> str:\n    parts = input.split(\"_\")\n    domain_name = \"\"\n    for part in parts:\n        domain_name = domain_name + part.capitalize()\n    return domain_name\n\n\n# the __all__ is generated\n__all__ = [\"to_snake_str\", \"to_camel_str\"]\n"
  },
  {
    "path": "src/zvt/utils/time_utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport calendar\nimport datetime\n\nimport arrow\nimport pandas as pd\n\nfrom zvt.common.query_models import TimeUnit\n\nCHINA_TZ = \"Asia/Shanghai\"\nUS_TZ = \"America/New_York\"\n\nTIME_FORMAT_ISO8601 = \"YYYY-MM-DDTHH:mm:ss.SSS\"\n\nTIME_FORMAT_MON = \"YYYY-MM\"\n\nTIME_FORMAT_DAY = \"YYYY-MM-DD\"\n\nTIME_FORMAT_DAY1 = \"YYYYMMDD\"\n\nTIME_FORMAT_MINUTE = \"YYYYMMDDHHmm\"\n\nTIME_FORMAT_SECOND = \"YYYYMMDDHHmmss\"\n\nTIME_FORMAT_MINUTE1 = \"HH:mm\"\n\nTIME_FORMAT_MINUTE2 = \"YYYY-MM-DD HH:mm:ss\"\n\n\n# ms(int) or second(float) or str\ndef to_pd_timestamp(the_time, tz=None) -> pd.Timestamp | None:\n    if the_time is None:\n        return None\n    # treat int as milliseconds, e.g. return from js\n    if type(the_time) == int:\n        return pd.Timestamp.fromtimestamp(the_time / 1000)\n\n    # treat float as seconds, e.g. return from\n    # timestamp_seconds = time.time()\n    if type(the_time) == float:\n        return pd.Timestamp.fromtimestamp(the_time)\n\n    return pd.Timestamp(the_time, tz=tz)\n\n\ndef get_local_timezone():\n    now = datetime.datetime.now()\n    local_now = now.astimezone()\n    local_tz = local_now.tzinfo\n    return local_tz\n\n\ndef to_timestamp_ms(the_time, tz=None) -> int:\n    \"\"\"\n    Convert a time to a timestamp in milliseconds.\n\n    :param the_time:\n    :param tz:\n    :return:\n    \"\"\"\n    if not tz:\n        tz = get_local_timezone()\n    return int(to_pd_timestamp(the_time).tz_localize(tz).timestamp() * 1000)\n\n\ndef now_timestamp_ms():\n    return int(pd.Timestamp.utcnow().timestamp() * 1000)\n\n\ndef now_pd_timestamp(tz=None) -> pd.Timestamp:\n    if tz:\n        return pd.Timestamp(arrow.now(tz=tz).datetime)\n    else:\n        return pd.Timestamp.now()\n\n\ndef current_date(tz=None) -> pd.Timestamp:\n    return to_pd_timestamp(now_pd_timestamp(tz=tz).date())\n\n\ndef tomorrow_date(tz=None):\n    return to_pd_timestamp(date_time_by_interval(now_pd_timestamp(tz=tz), 1).date())\n\n\ndef to_date_time_str(date_time, fmt=TIME_FORMAT_DAY, tz=None):\n    try:\n        return arrow.get(to_pd_timestamp(date_time, tz=tz)).format(fmt)\n    except Exception as e:\n        return date_time\n\n\ndef now_date_time_str(fmt=TIME_FORMAT_DAY, tz=None):\n    return to_date_time_str(date_time=now_pd_timestamp(), tz=tz, fmt=fmt)\n\n\ndef recent_year_date(tz=None):\n    return date_time_by_interval(the_time=current_date(tz=tz), interval=-365)\n\n\ndef next_date(the_time, tz=None):\n    \"\"\"\n    Get the next date from the given time.\n    :param the_time: The time to get the next date from.\n    :param tz: The timezone to use.\n    :return: The next date as a pd.Timestamp.\n    \"\"\"\n    return date_time_by_interval(the_time=the_time, interval=1, unit=TimeUnit.day, tz=tz)\n\n\ndef date_time_by_interval(the_time, interval=1, unit: TimeUnit = TimeUnit.day, tz=None):\n    time_delta = None\n    if unit == TimeUnit.year:\n        time_delta = datetime.timedelta(days=interval * 365)\n    elif unit == TimeUnit.month:\n        time_delta = datetime.timedelta(days=interval * 30)\n    elif unit == TimeUnit.day:\n        time_delta = datetime.timedelta(days=interval)\n    elif unit == TimeUnit.minute:\n        time_delta = datetime.timedelta(minutes=interval)\n    elif unit == TimeUnit.second:\n        time_delta = datetime.timedelta(seconds=interval)\n\n    return to_pd_timestamp(the_time, tz=tz) + time_delta\n\n\ndef pre_month(t=None, tz=None):\n    if not t:\n        t = current_date()\n    else:\n        t = to_pd_timestamp(t, tz=tz)\n    t = t.replace(day=1)\n    if t.month > 1:\n        year = t.year\n        month = t.month - 1\n    else:\n        year = t.year - 1\n        month = 12\n    last_valid_date = t.replace(year=year, month=month)\n    return last_valid_date\n\n\ndef pre_month_start_date(t=current_date()):\n    return month_start_date(pre_month(t))\n\n\ndef pre_month_end_date(t=current_date()):\n    return month_end_date(pre_month(t))\n\n\ndef month_start_date(the_date):\n    the_date = to_pd_timestamp(the_date)\n    return the_date.replace(day=1)\n\n\ndef month_end_date(the_date):\n    the_date = to_pd_timestamp(the_date)\n\n    _, day = calendar.monthrange(the_date.year, the_date.month)\n    return the_date.replace(day=day)\n\n\ndef month_start_end_ranges(start_date, end_date):\n    days = pd.date_range(start=start_date, end=end_date, freq=\"M\")\n    return [(month_start_date(d), month_end_date(d)) for d in days]\n\n\ndef is_same_date(one, two):\n    return to_pd_timestamp(one).date() == to_pd_timestamp(two).date()\n\n\ndef is_same_date_time(one, two):\n    return to_timestamp_ms(one) == to_timestamp_ms(two)\n\n\ndef get_year_quarter(time):\n    time = to_pd_timestamp(time)\n    return time.year, ((time.month - 1) // 3) + 1\n\n\ndef day_offset_today(offset=0):\n    return now_pd_timestamp() + datetime.timedelta(days=offset)\n\n\ndef get_year_quarters(start, end=pd.Timestamp.now()):\n    start_year_quarter = get_year_quarter(start)\n    current_year_quarter = get_year_quarter(end)\n    if current_year_quarter[0] == start_year_quarter[0]:\n        return [(current_year_quarter[0], x) for x in range(start_year_quarter[1], current_year_quarter[1] + 1)]\n    elif current_year_quarter[0] - start_year_quarter[0] == 1:\n        return [(start_year_quarter[0], x) for x in range(start_year_quarter[1], 5)] + [\n            (current_year_quarter[0], x) for x in range(1, current_year_quarter[1] + 1)\n        ]\n    elif current_year_quarter[0] - start_year_quarter[0] > 1:\n        return (\n            [(start_year_quarter[0], x) for x in range(start_year_quarter[1], 5)]\n            + [(x, y) for x in range(start_year_quarter[0] + 1, current_year_quarter[0]) for y in range(1, 5)]\n            + [(current_year_quarter[0], x) for x in range(1, current_year_quarter[1] + 1)]\n        )\n    else:\n        raise Exception(\"wrong start time:{}\".format(start))\n\n\ndef date_and_time(the_date, the_time, tz=None):\n    time_str = \"{}T{}:00.000\".format(to_date_time_str(the_date), the_time)\n\n    return to_pd_timestamp(time_str, tz=tz)\n\n\ndef split_time_interval(start, end, method=None, interval=30, freq=\"D\"):\n    start = to_pd_timestamp(start)\n    end = to_pd_timestamp(end)\n    if not method:\n        while start < end:\n            interval_end = min(date_time_by_interval(the_time=start, interval=interval), end)\n            yield pd.date_range(start=start, end=interval_end, freq=freq)\n            start = date_time_by_interval(interval_end, 1)\n\n    if method == \"month\":\n        while start <= end:\n            _, day = calendar.monthrange(start.year, start.month)\n\n            interval_end = min(to_pd_timestamp(f\"{start.year}-{start.month}-{day}\"), end)\n            yield pd.date_range(start=start, end=interval_end, freq=freq)\n            start = date_time_by_interval(interval_end, 1)\n\n\ndef count_interval(start_date, end_date):\n    start_date = to_pd_timestamp(start_date)\n    end_date = to_pd_timestamp(end_date)\n    delta = end_date - start_date\n    return delta.days\n\n\nif __name__ == \"__main__\":\n    print(recent_year_date(tz=CHINA_TZ))\n    print(recent_year_date(tz=US_TZ))\n# the __all__ is generated\n__all__ = [\n    \"CHINA_TZ\",\n    \"TIME_FORMAT_ISO8601\",\n    \"TIME_FORMAT_MON\",\n    \"TIME_FORMAT_DAY\",\n    \"TIME_FORMAT_DAY1\",\n    \"TIME_FORMAT_MINUTE\",\n    \"TIME_FORMAT_SECOND\",\n    \"TIME_FORMAT_MINUTE1\",\n    \"TIME_FORMAT_MINUTE2\",\n    \"to_pd_timestamp\",\n    \"get_local_timezone\",\n    \"to_timestamp_ms\",\n    \"now_timestamp_ms\",\n    \"now_pd_timestamp\",\n    \"current_date\",\n    \"tomorrow_date\",\n    \"to_date_time_str\",\n    \"now_date_time_str\",\n    \"recent_year_date\",\n    \"date_time_by_interval\",\n    \"pre_month\",\n    \"pre_month_start_date\",\n    \"pre_month_end_date\",\n    \"month_start_date\",\n    \"month_end_date\",\n    \"month_start_end_ranges\",\n    \"is_same_date\",\n    \"is_same_date_time\",\n    \"get_year_quarter\",\n    \"day_offset_today\",\n    \"get_year_quarters\",\n    \"date_and_time\",\n    \"split_time_interval\",\n    \"count_interval\",\n]\n"
  },
  {
    "path": "src/zvt/utils/utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport logging\nimport numbers\nfrom decimal import *\nfrom urllib import parse\n\nimport pandas as pd\n\ngetcontext().prec = 16\n\nlogger = logging.getLogger(__name__)\n\nnone_values = [\"不变\", \"--\", \"-\", \"新进\"]\nzero_values = [\"不变\", \"--\", \"-\", \"新进\"]\n\n\ndef first_item_to_float(the_list):\n    return to_float(the_list[0])\n\n\ndef second_item_to_float(the_list):\n    return to_float(the_list[1])\n\n\ndef add_func_to_value(the_map, the_func):\n    for k, v in the_map.items():\n        the_map[k] = (v, the_func)\n    return the_map\n\n\ndef to_float(the_str, default=None):\n    if not the_str:\n        return default\n    if the_str in none_values:\n        return None\n\n    if \"%\" in the_str:\n        return pct_to_float(the_str)\n    try:\n        scale = 1.0\n        if the_str[-2:] == \"万亿\":\n            the_str = the_str[0:-2]\n            scale = 1000000000000\n        elif the_str[-1] == \"亿\":\n            the_str = the_str[0:-1]\n            scale = 100000000\n        elif the_str[-1] == \"万\":\n            the_str = the_str[0:-1]\n            scale = 10000\n        if not the_str:\n            return default\n        return float(Decimal(the_str.replace(\",\", \"\")) * Decimal(scale))\n    except Exception as e:\n        logger.error(\"the_str:{}\".format(the_str))\n        logger.exception(e)\n        return default\n\n\ndef pct_to_float(the_str, default=None):\n    if the_str in none_values:\n        return None\n\n    try:\n        return float(Decimal(the_str.replace(\"%\", \"\")) / Decimal(100))\n    except Exception as e:\n        logger.exception(e)\n        return default\n\n\ndef float_to_pct(input_float: float) -> str:\n    # Convert the float to a percentage and format it to two decimal places\n    return f\"{input_float * 100:.2f}%\"\n\n\ndef format_number_to_yi(number):\n    return f\"{number / 1e8:.2f}亿\"\n\n\ndef json_callback_param(the_str):\n    json_str = the_str[the_str.index(\"(\") + 1 : the_str.rindex(\")\")].replace(\"null\", \"None\")\n    return eval(json_str)\n\n\ndef fill_domain_from_dict(the_domain, the_dict: dict, the_map: dict = None, default_func=lambda x: x):\n    \"\"\"\n    use field map and related func to fill properties from the dict to the domain\n\n\n    :param the_domain:\n    :type the_domain: DeclarativeMeta\n    :param the_dict:\n    :type the_dict: dict\n    :param the_map:\n    :type the_map: dict\n    :param default_func:\n    :type default_func: function\n    \"\"\"\n    if not the_map:\n        the_map = {}\n        for k in the_dict:\n            the_map[k] = (k, default_func)\n\n    for k, v in the_map.items():\n        if isinstance(v, tuple):\n            field_in_dict = v[0]\n            the_func = v[1]\n        else:\n            field_in_dict = v\n            the_func = default_func\n\n        the_value = the_dict.get(field_in_dict)\n        if the_value is not None:\n            to_value = the_value\n            if to_value in none_values:\n                setattr(the_domain, k, None)\n            else:\n                result_value = the_func(to_value)\n                setattr(the_domain, k, result_value)\n                exec(\"the_domain.{}=result_value\".format(k))\n\n\nSUPPORT_ENCODINGS = [\"GB2312\", \"GBK\", \"GB18030\", \"UTF-8\"]\n\n\ndef read_csv(f, encoding, sep=None, na_values=None):\n    encodings = [encoding] + SUPPORT_ENCODINGS\n    for encoding in encodings:\n        try:\n            if sep:\n                return pd.read_csv(f, sep=sep, encoding=encoding, na_values=na_values)\n            else:\n                return pd.read_csv(f, encoding=encoding, na_values=na_values)\n        except UnicodeDecodeError as e:\n            logger.warning(\"read_csv failed by using encoding:{}\".format(encoding), e)\n            f.seek(0)\n            continue\n    return None\n\n\ndef chrome_copy_header_to_dict(src):\n    lines = src.split(\"\\n\")\n    header = {}\n    if lines:\n        for line in lines:\n            try:\n                index = line.index(\":\")\n                key = line[:index]\n                value = line[index + 1 :]\n                if key and value:\n                    header.setdefault(key.strip(), value.strip())\n            except Exception:\n                pass\n    return header\n\n\ndef to_positive_number(number):\n    if isinstance(number, numbers.Number):\n        return abs(number)\n\n    return 0\n\n\ndef multiple_number(number, factor):\n    try:\n        return number * factor\n    except:\n        return number\n\n\ndef add_to_map_list(the_map, key, value):\n    result = []\n    if key in the_map:\n        result = the_map[key]\n    else:\n        the_map[key] = result\n\n    if value not in result:\n        result.append(value)\n\n\ndef iterate_with_step(data, sub_size=100):\n    size = len(data)\n    if size >= sub_size:\n        step_count = int(size / sub_size)\n        if size % sub_size:\n            step_count = step_count + 1\n    else:\n        step_count = 1\n\n    for step in range(step_count):\n        if type(data) == pd.DataFrame or type(data) == pd.Series:\n            yield data.iloc[sub_size * step : sub_size * (step + 1)]\n        else:\n            yield data[sub_size * step : sub_size * (step + 1)]\n\n\ndef url_unquote(url):\n    return parse.unquote(url)\n\n\ndef parse_url_params(url):\n    url = url_unquote(url)\n    return parse.parse_qs(parse.urlsplit(url).query)\n\n\ndef set_one_and_only_one(**kwargs):\n    all_none = all(kwargs[v] is None for v in kwargs)\n    if all_none:\n        raise ValueError(f\"{kwargs} must be set one at least\")\n\n    set_size = len([v for v in kwargs if kwargs[v] is not None])\n    if set_size != 1:\n        raise ValueError(f\"{kwargs} could only set one\")\n\n    return True\n\n\ndef flatten_list(input_list):\n    if not input_list:\n        return input_list\n    result = []\n    for item in input_list:\n        if isinstance(item, list):\n            result.extend(item)\n        elif isinstance(item, dict):\n            result.append(item)\n        else:\n            result.append(item)\n    return result\n\n\ndef to_str(str_or_list):\n    if not str_or_list:\n        return None\n    if isinstance(str_or_list, str):\n        return str_or_list\n    if isinstance(str_or_list, list):\n        str_list = [str(item) for item in str_or_list]\n        return \";\".join(str_list)\n\n\ndef compare_dicts(dict1, dict2):\n    # Check if both dictionaries are None\n    if dict1 is None and dict2 is None:\n        return True\n\n    # Check if only one dictionary is None\n    if dict1 is None or dict2 is None:\n        return False\n\n    # Check if the keys are the same\n    if set(dict1.keys()) != set(dict2.keys()):\n        return False\n\n    # Check if the values are the same for each key\n    for key in dict1:\n        if dict1[key] != dict2[key]:\n            return False\n\n    # If all keys and values match, return True\n    return True\n\n\ndef fill_dict(src, dst):\n    \"\"\"\n    Fills items from the source dictionary (src) into the destination dictionary (dst)\n    if the keys are not already present in dst.\n\n    Args:\n        src (dict): The source dictionary to copy items from.\n        dst (dict): The destination dictionary to copy items into.\n\n    Returns:\n        dict: The updated destination dictionary with new items from the source dictionary.\n    \"\"\"\n    if not src:\n        return dst\n    for key, value in src.items():\n        if key not in dst:\n            dst[key] = value\n    return dst\n\n\nif __name__ == \"__main__\":\n    url = url_unquote(\n        \"https://datacenter.eastmoney.com/securities/api/data/get?type=RPT_DAILYBILLBOARD_DETAILS&sty=ALL&source=DataCenter&client=WAP&p=1&ps=20&sr=-1,1&st=TRADE_DATE,SECURITY_CODE&filter=(TRADE_DATE%3E=%272022-04-01%27)(TRADE_DATE%3C=%272022-04-29%27)(MARKET=%22SH%22)&?v=05160638952989893\"\n    )\n    print(url)\n\n# the __all__ is generated\n__all__ = [\n    \"none_values\",\n    \"zero_values\",\n    \"first_item_to_float\",\n    \"second_item_to_float\",\n    \"add_func_to_value\",\n    \"to_float\",\n    \"pct_to_float\",\n    \"float_to_pct\",\n    \"json_callback_param\",\n    \"fill_domain_from_dict\",\n    \"SUPPORT_ENCODINGS\",\n    \"read_csv\",\n    \"chrome_copy_header_to_dict\",\n    \"to_positive_number\",\n    \"multiple_number\",\n    \"add_to_map_list\",\n    \"iterate_with_step\",\n    \"url_unquote\",\n    \"parse_url_params\",\n    \"set_one_and_only_one\",\n    \"flatten_list\",\n    \"to_str\",\n    \"compare_dicts\",\n    \"fill_dict\",\n]\n"
  },
  {
    "path": "src/zvt/utils/zip_utils.py",
    "content": "# -*- coding: utf-8 -*-\n\nimport datetime\nimport os\nimport pathlib\nimport zipfile\n\n\ndef zip_dir(src_dir, dst_dir=None, zip_file_name=None):\n    if not zip_file_name:\n        zip_file_name = \"data-{}.zip\".format(datetime.datetime.today())\n\n    if dst_dir:\n        dst_path = os.path.join(dst_dir, zip_file_name)\n    else:\n        dst_path = zip_file_name\n\n    # os.remove(dst_path)\n\n    the_zip_file = zipfile.ZipFile(dst_path, \"w\")\n\n    for folder, subfolders, files in os.walk(src_dir):\n        for file in files:\n            the_path = os.path.join(folder, file)\n            # if 'zvt_business.db' in the_path:\n            #     continue\n            print(\"zip {}\".format(the_path))\n            the_zip_file.write(the_path, os.path.relpath(the_path, src_dir), compress_type=zipfile.ZIP_DEFLATED)\n\n    the_zip_file.close()\n\n\ndef unzip(zip_file, dst_dir):\n    the_zip_file = zipfile.ZipFile(zip_file)\n    print(\"start unzip {} to {}\".format(zip_file, dst_dir))\n\n    for name in the_zip_file.namelist():\n        extracted_path = pathlib.Path(the_zip_file.extract(name, path=dst_dir))\n        extracted_path.rename(f\"{extracted_path}\".encode(\"cp437\").decode(\"gbk\"))\n    print(\"finish unzip {} to {}\".format(zip_file, dst_dir))\n    the_zip_file.close()\n\n\n# the __all__ is generated\n__all__ = [\"zip_dir\", \"unzip\"]\n"
  },
  {
    "path": "src/zvt/zvt_server.py",
    "content": "# -*- coding: utf-8 -*-\nimport os\n\nimport uvicorn\nfrom fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\nfrom fastapi.responses import ORJSONResponse\nfrom fastapi_pagination import add_pagination\n\nfrom zvt import zvt_env\nfrom zvt.rest.data import data_router\nfrom zvt.rest.factor import factor_router\nfrom zvt.rest.misc import misc_router\nfrom zvt.rest.trading import trading_router\nfrom zvt.rest.work import work_router\n\napp = FastAPI(default_response_class=ORJSONResponse)\n\norigins = [\"*\"]\n\napp.add_middleware(\n    CORSMiddleware,\n    allow_origins=origins,\n    allow_credentials=True,\n    allow_methods=[\"*\"],\n    allow_headers=[\"*\"],\n)\n\n\n@app.get(\"/\")\nasync def root():\n    return {\"message\": \"Hello World\"}\n\n\napp.include_router(data_router)\napp.include_router(factor_router)\napp.include_router(work_router)\napp.include_router(trading_router)\napp.include_router(misc_router)\n\nadd_pagination(app)\n\n\ndef main():\n    log_config = os.path.join(zvt_env[\"resource_path\"], \"log_conf.yaml\")\n    uvicorn.run(\"zvt_server:app\", host=\"0.0.0.0\", reload=True, port=8090, log_config=log_config)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "tests/__init__.py",
    "content": "# -*- coding: utf-8 -*-\nimport os\nimport sys\n\nos.environ.setdefault(\"TESTING_ZVT\", \"True\")\nos.environ.setdefault(\"SQLALCHEMY_WARN_20\", \"1\")\n\nsys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), \"../src\")))\n"
  },
  {
    "path": "tests/api/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/api/test_common.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.utils import get_recent_report_date\nfrom zvt.contract import IntervalLevel\nfrom zvt.api.kdata import get_kdata, to_high_level_kdata\nfrom ..context import init_test_context\n\ninit_test_context()\n\n\ndef test_to_high_level_kdata():\n    day_df = get_kdata(provider=\"joinquant\", level=IntervalLevel.LEVEL_1DAY, entity_id=\"stock_sz_000338\")\n    print(day_df)\n\n    df = to_high_level_kdata(kdata_df=day_df.loc[:\"2019-09-01\", :], to_level=IntervalLevel.LEVEL_1WEEK)\n\n    print(df)\n\n\ndef test_get_recent_report_date():\n    assert \"2018-12-31\" == get_recent_report_date(\"2019-01-01\", 0)\n    assert \"2018-09-30\" == get_recent_report_date(\"2019-01-01\", 1)\n    assert \"2018-06-30\" == get_recent_report_date(\"2019-01-01\", 2)\n    assert \"2018-03-31\" == get_recent_report_date(\"2019-01-01\", 3)\n"
  },
  {
    "path": "tests/api/test_dividend_financing.py",
    "content": "from ..context import init_test_context\n\ninit_test_context()\n\nfrom zvt.domain import SpoDetail, RightsIssueDetail, DividendFinancing\nfrom zvt.contract.api import get_db_session\nfrom zvt.utils.time_utils import to_pd_timestamp\n\nsession = get_db_session(provider=\"eastmoney\", db_name=\"dividend_financing\")  # type: sqlalchemy.orm.Session\n\n\n# 增发详情\ndef test_000778_spo_detial():\n    result = SpoDetail.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-09-30\",\n        order=SpoDetail.timestamp.desc(),\n    )\n    assert len(result) == 4\n    latest: SpoDetail = result[0]\n    assert latest.timestamp == to_pd_timestamp(\"2017-04-01\")\n    assert latest.spo_issues == 347600000\n    assert latest.spo_price == 5.15\n    assert latest.spo_raising_fund == 1766000000\n\n\n# 配股详情\ndef test_000778_rights_issue_detail():\n    result = RightsIssueDetail.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-09-30\",\n        order=RightsIssueDetail.timestamp.desc(),\n    )\n    assert len(result) == 2\n    latest: RightsIssueDetail = result[0]\n    assert latest.timestamp == to_pd_timestamp(\"2001-09-10\")\n    assert latest.rights_issues == 43570000\n    assert latest.rights_raising_fund == 492300000\n    assert latest.rights_issue_price == 11.3\n\n\n# 分红融资\ndef test_000778_dividend_financing():\n    result = DividendFinancing.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-09-30\",\n        order=DividendFinancing.timestamp.desc(),\n    )\n    assert len(result) == 22\n    latest: DividendFinancing = result[1]\n    assert latest.timestamp == to_pd_timestamp(\"2017\")\n    assert latest.dividend_money == 598632026.4\n    assert latest.spo_issues == 347572815.0\n    assert latest.rights_issues == 0\n    assert latest.ipo_issues == 0\n"
  },
  {
    "path": "tests/api/test_finance.py",
    "content": "from ..context import init_test_context\n\ninit_test_context()\n\nfrom zvt.domain import FinanceFactor, BalanceSheet, IncomeStatement, CashFlowStatement\nfrom zvt.contract.api import get_db_session\nfrom zvt.utils.time_utils import to_date_time_str\n\nsession = get_db_session(provider=\"eastmoney\", db_name=\"finance\")  # type: sqlalchemy.orm.Session\n\n\n# 银行指标\ndef test_000001_finance_factor():\n    correct_timestamps = [\n        \"2018-09-30\",\n        \"2018-06-30\",\n        \"2018-03-31\",\n        \"2017-12-31\",\n        \"2017-09-30\",\n        \"2017-06-30\",\n        \"2017-03-31\",\n        \"2016-12-31\",\n        \"2016-09-30\",\n        \"2016-06-30\",\n        \"2016-03-31\",\n        \"2015-12-31\",\n        \"2015-09-30\",\n        \"2015-06-30\",\n        \"2015-03-31\",\n        \"2014-12-31\",\n        \"2014-09-30\",\n        \"2014-06-30\",\n        \"2014-03-31\",\n        \"2013-12-31\",\n        \"2013-09-30\",\n        \"2013-06-30\",\n        \"2013-03-31\",\n        \"2012-12-31\",\n        \"2012-09-30\",\n        \"2012-06-30\",\n        \"2012-03-31\",\n        \"2011-12-31\",\n        \"2011-09-30\",\n        \"2011-06-30\",\n        \"2011-03-31\",\n        \"2010-12-31\",\n        \"2010-09-30\",\n        \"2010-06-30\",\n        \"2010-03-31\",\n        \"2009-12-31\",\n        \"2009-09-30\",\n        \"2009-06-30\",\n        \"2009-03-31\",\n        \"2008-12-31\",\n        \"2008-09-30\",\n        \"2008-06-30\",\n        \"2008-03-31\",\n        \"2007-12-31\",\n        \"2007-09-30\",\n        \"2007-06-30\",\n        \"2007-03-31\",\n        \"2006-12-31\",\n        \"2006-09-30\",\n        \"2006-06-30\",\n        \"2006-03-31\",\n        \"2005-12-31\",\n        \"2005-09-30\",\n        \"2005-06-30\",\n        \"2005-03-31\",\n        \"2004-12-31\",\n        \"2004-09-30\",\n        \"2004-06-30\",\n        \"2004-03-31\",\n        \"2003-12-31\",\n        \"2003-09-30\",\n        \"2003-06-30\",\n        \"2003-03-31\",\n        \"2002-12-31\",\n        \"2002-09-30\",\n        \"2002-06-30\",\n        \"2002-03-31\",\n        \"2001-12-31\",\n        \"2001-09-30\",\n        \"2001-06-30\",\n        \"2001-03-31\",\n        \"2000-12-31\",\n        \"2000-06-30\",\n        \"1999-12-31\",\n        \"1999-06-30\",\n        \"1998-12-31\",\n        \"1998-06-30\",\n        \"1997-12-31\",\n        \"1997-06-30\",\n        \"1996-12-31\",\n        \"1996-06-30\",\n        \"1995-12-31\",\n        \"1995-06-30\",\n        \"1994-12-31\",\n        \"1994-06-30\",\n        \"1993-12-31\",\n        \"1993-06-30\",\n        \"1992-12-31\",\n        \"1991-12-31\",\n        \"1990-12-31\",\n        \"1989-12-31\",\n    ]\n    result = FinanceFactor.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000001\"],\n        end_timestamp=\"2018-12-30\",\n        order=FinanceFactor.report_date.desc(),\n        time_field=\"report_date\",\n    )\n    assert len(correct_timestamps) == len(result)\n    timestamps = [to_date_time_str(item.report_date) for item in result]\n    assert set(correct_timestamps) == set(timestamps)\n    latest: FinanceFactor = result[0]\n    assert latest.basic_eps == 1.14\n    assert latest.deducted_eps == 1.13\n    assert latest.diluted_eps == 1.14\n    assert latest.bps == 12.538\n    assert latest.capital_reserve_ps == 3.2886\n    assert latest.undistributed_profit_ps == 5.3566\n    assert latest.op_cash_flow_ps == -0.6587\n\n    assert latest.total_op_income == 86660000000\n    assert latest.net_profit == 20460000000\n    assert latest.deducted_net_profit == 20350000000\n    assert latest.op_income_growth_yoy == 0.0856\n    assert latest.net_profit_growth_yoy == 0.068\n    assert latest.deducted_net_profit_growth_yoy == 0.0636\n    assert latest.op_income_growth_qoq == 0.0336\n    assert latest.net_profit_growth_qoq == 0.0202\n    assert latest.deducted_net_profit_growth_qoq == 0.0168\n\n    assert latest.roe == 0.0948\n    assert latest.deducted_roe == 0.0943\n    assert latest.rota == 0.0062\n    assert latest.net_margin == 0.2360\n\n    assert latest.debt_asset_ratio == 0.9298\n    assert latest.em == 14.25\n    assert latest.equity_ratio == 13.25\n\n    assert latest.fi_total_deposit == 2130000000000\n    assert latest.fi_total_loan == 1920000000000\n    assert latest.fi_loan_deposit_ratio == 0.9004\n    assert latest.fi_npl_ratio == 0.0168\n    assert latest.fi_npl_provision_coverage == 1.6914\n\n\n# 银行资产负债表\ndef test_000001_balance_sheet():\n    correct_timestamps = [\n        \"2018-09-30\",\n        \"2018-06-30\",\n        \"2018-03-31\",\n        \"2017-12-31\",\n        \"2017-09-30\",\n        \"2017-06-30\",\n        \"2017-03-31\",\n        \"2016-12-31\",\n        \"2016-09-30\",\n        \"2016-06-30\",\n        \"2016-03-31\",\n        \"2015-12-31\",\n        \"2015-09-30\",\n        \"2015-06-30\",\n        \"2015-03-31\",\n        \"2014-12-31\",\n        \"2014-09-30\",\n        \"2014-06-30\",\n        \"2014-03-31\",\n        \"2013-12-31\",\n        \"2013-09-30\",\n        \"2013-06-30\",\n        \"2013-03-31\",\n        \"2012-12-31\",\n        \"2012-09-30\",\n        \"2012-06-30\",\n        \"2012-03-31\",\n        \"2011-12-31\",\n        \"2011-09-30\",\n        \"2011-06-30\",\n        \"2011-03-31\",\n        \"2010-12-31\",\n        \"2010-09-30\",\n        \"2010-06-30\",\n        \"2010-03-31\",\n        \"2009-12-31\",\n        \"2009-09-30\",\n        \"2009-06-30\",\n        \"2009-03-31\",\n        \"2008-12-31\",\n        \"2008-09-30\",\n        \"2008-06-30\",\n        \"2008-03-31\",\n        \"2007-12-31\",\n        \"2007-09-30\",\n        \"2007-06-30\",\n        \"2007-03-31\",\n        \"2006-12-31\",\n        \"2006-09-30\",\n        \"2006-06-30\",\n        \"2006-03-31\",\n        \"2005-12-31\",\n        \"2005-09-30\",\n        \"2005-06-30\",\n        \"2005-03-31\",\n        \"2004-12-31\",\n        \"2004-09-30\",\n        \"2004-06-30\",\n        \"2004-03-31\",\n        \"2003-12-31\",\n        \"2003-09-30\",\n        \"2003-06-30\",\n        \"2003-03-31\",\n        \"2002-12-31\",\n        \"2002-09-30\",\n        \"2002-06-30\",\n        \"2002-03-31\",\n        \"2001-12-31\",\n        \"2001-06-30\",\n        \"2000-12-31\",\n        \"2000-06-30\",\n        \"1999-12-31\",\n        \"1999-06-30\",\n        \"1998-12-31\",\n        \"1998-06-30\",\n        \"1997-12-31\",\n        \"1997-06-30\",\n        \"1996-12-31\",\n        \"1996-06-30\",\n        \"1995-12-31\",\n        \"1995-06-30\",\n        \"1994-12-31\",\n        \"1994-06-30\",\n        \"1993-12-31\",\n        \"1992-12-31\",\n        \"1991-12-31\",\n        \"1990-12-31\",\n        \"1989-12-31\",\n    ]\n    result = BalanceSheet.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000001\"],\n        end_timestamp=\"2018-12-30\",\n        order=BalanceSheet.report_date.desc(),\n        time_field=\"report_date\",\n    )\n    assert len(correct_timestamps) == len(result)\n    timestamps = [to_date_time_str(item.report_date) for item in result]\n    assert set(correct_timestamps) == set(timestamps)\n    latest: BalanceSheet = result[0]\n    assert latest.fi_cash_and_deposit_in_central_bank == 287600000000\n    assert latest.fi_deposit_in_other_fi == 95310000000\n    assert latest.fi_expensive_metals == 72020000000\n    assert latest.fi_lending_to_other_fi == 88100000000\n    assert latest.fi_financial_assets_effect_current_income == 103500000000\n    assert latest.fi_financial_derivative_asset == 25650000000\n\n    assert latest.fi_buying_sell_back_fi__asset == 32760000000\n    assert latest.accounts_receivable == 22830000000\n    assert latest.fi_interest_receivable == 18310000000\n    assert latest.fi_disbursing_loans_and_advances == 1870000000000\n    assert latest.real_estate_investment == 194000000\n    assert latest.fixed_assets == 9374000000\n    assert latest.intangible_assets == 4722000000\n    assert latest.goodwill == 7568000000\n    assert latest.deferred_tax_assets == 28880000000\n    assert latest.fi_other_asset == 15630000000\n    assert latest.total_assets == 3350000000000\n\n    assert latest.fi_borrowings_from_central_bank == 149900000000\n    assert latest.fi_deposit_from_other_fi == 402300000000\n    assert latest.fi_borrowings_from_fi == 17830000000\n    assert latest.fi_financial_liability_effect_current_income == 9599000000\n    assert latest.fi_financial_derivative_liability == 20730000000\n    assert latest.fi_sell_buy_back_fi_asset == 2000000000\n    assert latest.fi_savings_absorption == 2130000000000\n    assert latest.fi_notes_payable == 0\n\n    assert latest.employee_benefits_payable == 10550000000\n    assert latest.taxes_payable == 7595000000\n    assert latest.interest_payable == 27670000000\n    assert latest.fi_estimated_liabilities == 24000000\n    assert latest.fi_bond_payable == 321500000000\n    assert latest.fi_other_liability == 12570000000\n    assert latest.total_liabilities == 3120000000000\n\n    assert latest.fi_capital == 17170000000\n    assert latest.fi_other_equity_instruments == 19950000000\n    assert latest.fi_preferred_stock == 19950000000\n    assert latest.capital_reserve == 56470000000\n    assert latest.surplus_reserve == 10780000000\n    assert latest.fi_generic_risk_reserve == 38550000000\n    assert latest.undistributed_profits == 91970000000\n\n    assert latest.equity == 235200000000\n    assert latest.total_liabilities_and_equity == 3350000000000\n\n\n# 银行利润表\ndef test_000001_income_statement():\n    correct_timestamps = [\n        \"2018-09-30\",\n        \"2018-06-30\",\n        \"2018-03-31\",\n        \"2017-12-31\",\n        \"2017-09-30\",\n        \"2017-06-30\",\n        \"2017-03-31\",\n        \"2016-12-31\",\n        \"2016-09-30\",\n        \"2016-06-30\",\n        \"2016-03-31\",\n        \"2015-12-31\",\n        \"2015-09-30\",\n        \"2015-06-30\",\n        \"2015-03-31\",\n        \"2014-12-31\",\n        \"2014-09-30\",\n        \"2014-06-30\",\n        \"2014-03-31\",\n        \"2013-12-31\",\n        \"2013-09-30\",\n        \"2013-06-30\",\n        \"2013-03-31\",\n        \"2012-12-31\",\n        \"2012-09-30\",\n        \"2012-06-30\",\n        \"2012-03-31\",\n        \"2011-12-31\",\n        \"2011-09-30\",\n        \"2011-06-30\",\n        \"2011-03-31\",\n        \"2010-12-31\",\n        \"2010-09-30\",\n        \"2010-06-30\",\n        \"2010-03-31\",\n        \"2009-12-31\",\n        \"2009-09-30\",\n        \"2009-06-30\",\n        \"2009-03-31\",\n        \"2008-12-31\",\n        \"2008-09-30\",\n        \"2008-06-30\",\n        \"2008-03-31\",\n        \"2007-12-31\",\n        \"2007-09-30\",\n        \"2007-06-30\",\n        \"2007-03-31\",\n        \"2006-12-31\",\n        \"2006-09-30\",\n        \"2006-06-30\",\n        \"2006-03-31\",\n        \"2005-12-31\",\n        \"2005-09-30\",\n        \"2005-06-30\",\n        \"2005-03-31\",\n        \"2004-12-31\",\n        \"2004-09-30\",\n        \"2004-06-30\",\n        \"2004-03-31\",\n        \"2003-12-31\",\n        \"2003-09-30\",\n        \"2003-06-30\",\n        \"2003-03-31\",\n        \"2002-12-31\",\n        \"2002-09-30\",\n        \"2002-06-30\",\n        \"2002-03-31\",\n        \"2001-12-31\",\n        \"2001-09-30\",\n        \"2001-06-30\",\n        \"2001-03-31\",\n        \"2000-12-31\",\n        \"2000-06-30\",\n        \"1999-12-31\",\n        \"1999-06-30\",\n        \"1998-12-31\",\n        \"1998-06-30\",\n        \"1997-12-31\",\n        \"1997-06-30\",\n        \"1996-12-31\",\n        \"1996-06-30\",\n        \"1995-12-31\",\n        \"1995-06-30\",\n        \"1994-12-31\",\n        \"1994-06-30\",\n        \"1993-12-31\",\n        \"1993-06-30\",\n        \"1992-12-31\",\n        \"1991-12-31\",\n        \"1990-12-31\",\n        \"1989-12-31\",\n    ]\n    result = IncomeStatement.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000001\"],\n        end_timestamp=\"2018-12-30\",\n        order=IncomeStatement.report_date.desc(),\n        time_field=\"report_date\",\n    )\n    assert len(correct_timestamps) == len(result)\n    timestamps = [to_date_time_str(item.report_date) for item in result]\n    assert set(correct_timestamps) == set(timestamps)\n    latest: IncomeStatement = result[0]\n\n    assert latest.operating_income == 86660000000\n    assert latest.fi_net_interest_income == 54530000000\n    assert latest.fi_interest_income == 121700000000\n    assert latest.fi_interest_expenses == 67130000000\n    assert latest.fi_net_incomes_from_fees_and_commissions == 23710000000\n    assert latest.fi_incomes_from_fees_and_commissions == 28920000000\n    assert latest.fi_expenses_for_fees_and_commissions == 5218000000\n    assert latest.investment_income == 7099000000\n    assert latest.fi_income_from_fair_value_change == 1047000000\n    assert latest.fi_income_from_exchange == -40000000\n    assert latest.fi_other_income == 144000000\n\n    assert latest.operating_costs == 26430000000\n    assert latest.business_taxes_and_surcharges == 847000000\n    assert latest.fi_operate_and_manage_expenses == 25580000000\n\n    assert latest.operating_profit == 26610000000\n    assert latest.non_operating_income == 14000000\n    assert latest.non_operating_costs == 62000000\n\n    assert latest.total_profits == 26570000000\n\n\n# 银行现金流量表\ndef test_000001_cash_flow_statement():\n    correct_timestamps = [\n        \"2018-09-30\",\n        \"2018-06-30\",\n        \"2018-03-31\",\n        \"2017-12-31\",\n        \"2017-09-30\",\n        \"2017-06-30\",\n        \"2017-03-31\",\n        \"2016-12-31\",\n        \"2016-09-30\",\n        \"2016-06-30\",\n        \"2016-03-31\",\n        \"2015-12-31\",\n        \"2015-09-30\",\n        \"2015-06-30\",\n        \"2015-03-31\",\n        \"2014-12-31\",\n        \"2014-09-30\",\n        \"2014-06-30\",\n        \"2014-03-31\",\n        \"2013-12-31\",\n        \"2013-09-30\",\n        \"2013-06-30\",\n        \"2013-03-31\",\n        \"2012-12-31\",\n        \"2012-09-30\",\n        \"2012-06-30\",\n        \"2012-03-31\",\n        \"2011-12-31\",\n        \"2011-09-30\",\n        \"2011-06-30\",\n        \"2011-03-31\",\n        \"2010-12-31\",\n        \"2010-09-30\",\n        \"2010-06-30\",\n        \"2010-03-31\",\n        \"2009-12-31\",\n        \"2009-09-30\",\n        \"2009-06-30\",\n        \"2009-03-31\",\n        \"2008-12-31\",\n        \"2008-09-30\",\n        \"2008-06-30\",\n        \"2008-03-31\",\n        \"2007-12-31\",\n        \"2007-09-30\",\n        \"2007-06-30\",\n        \"2007-03-31\",\n        \"2006-12-31\",\n        \"2006-09-30\",\n        \"2006-06-30\",\n        \"2006-03-31\",\n        \"2005-12-31\",\n        \"2005-09-30\",\n        \"2005-06-30\",\n        \"2005-03-31\",\n        \"2004-12-31\",\n        \"2004-09-30\",\n        \"2004-06-30\",\n        \"2004-03-31\",\n        \"2003-12-31\",\n        \"2003-09-30\",\n        \"2003-06-30\",\n        \"2003-03-31\",\n        \"2002-12-31\",\n        \"2002-06-30\",\n        \"2001-12-31\",\n        \"2001-06-30\",\n        \"2000-12-31\",\n        \"2000-06-30\",\n        \"1999-12-31\",\n        \"1999-06-30\",\n        \"1998-12-31\",\n        \"1998-06-30\",\n    ]\n    result = CashFlowStatement.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000001\"],\n        end_timestamp=\"2018-12-30\",\n        order=CashFlowStatement.report_date.desc(),\n        time_field=\"report_date\",\n    )\n    assert len(correct_timestamps) == len(result)\n    timestamps = [to_date_time_str(item.report_date) for item in result]\n    assert set(correct_timestamps) == set(timestamps)\n    latest: CashFlowStatement = result[0]\n\n    # 00000000\n    assert latest.fi_deposit_increase == 104700000000\n    assert latest.fi_borrow_from_central_bank_increase == 18960000000\n    assert latest.fi_deposit_in_others_decrease == 60880000000\n    assert latest.fi_lending_and_buy_repurchase_decrease == 12330000000\n    assert latest.fi_lending_decrease == 12270000000\n    assert latest.fi_buy_repurchase_decrease == 56000000\n    assert latest.fi_cash_from_interest_commission == 133700000000\n    assert latest.cash_from_other_op == 20540000000\n    assert latest.total_op_cash_inflows == 381200000000\n    assert latest.fi_loan_advance_increase == 250300000000\n    assert latest.fi_borrowing_and_sell_repurchase_decrease == 14350000000\n    assert latest.fi_borrowing_decrease == 10190000000\n    assert latest.fi_sell_repurchase_decrease == 4155000000\n    assert latest.fi_cash_to_interest_commission == 56760000000\n\n    assert latest.cash_to_employees == 12930000000\n    assert latest.taxes_and_surcharges == 20010000000\n    assert latest.cash_to_other_related_op == 38150000000\n    assert latest.total_op_cash_outflows == 392500000000\n    assert latest.net_op_cash_flows == -11310000000\n\n    assert latest.cash_from_disposal_of_investments == 348900000000\n    assert latest.cash_from_returns_on_investments == 21080000000\n    assert latest.cash_from_disposal_fixed_intangible_assets == 108000000\n    assert latest.total_investing_cash_inflows == 370100000000\n    assert latest.cash_to_investments == 294600000000\n    assert latest.cash_to_acquire_fixed_intangible_assets == 1518000000\n    assert latest.total_investing_cash_outflows == 296100000000\n    assert latest.net_investing_cash_flows == 73960000000\n    assert latest.cash_from_issuing_bonds == 581200000000\n    assert latest.total_financing_cash_inflows == 581200000000\n    assert latest.cash_to_repay_borrowings == 612500000000\n    assert latest.fi_cash_to_pay_interest == 2511000000\n    assert latest.cash_to_pay_interest_dividend == 3209000000\n    assert latest.total_financing_cash_outflows == 618300000000\n    assert latest.net_financing_cash_flows == -37080000000\n    assert latest.foreign_exchange_rate_effect == 2018000000\n    assert latest.net_cash_increase == 27590000000\n    assert latest.cash_at_beginning == 137000000000\n    assert latest.cash == 164600000000\n\n\n# 企业指标\ndef test_000778_finance_factor():\n    correct_timestamps = [\n        \"2018-09-30\",\n        \"2018-06-30\",\n        \"2018-03-31\",\n        \"2017-12-31\",\n        \"2017-09-30\",\n        \"2017-06-30\",\n        \"2017-03-31\",\n        \"2016-12-31\",\n        \"2016-09-30\",\n        \"2016-06-30\",\n        \"2016-03-31\",\n        \"2015-12-31\",\n        \"2015-09-30\",\n        \"2015-06-30\",\n        \"2015-03-31\",\n        \"2014-12-31\",\n        \"2014-09-30\",\n        \"2014-06-30\",\n        \"2014-03-31\",\n        \"2013-12-31\",\n        \"2013-09-30\",\n        \"2013-06-30\",\n        \"2013-03-31\",\n        \"2012-12-31\",\n        \"2012-09-30\",\n        \"2012-06-30\",\n        \"2012-03-31\",\n        \"2011-12-31\",\n        \"2011-09-30\",\n        \"2011-06-30\",\n        \"2011-03-31\",\n        \"2010-12-31\",\n        \"2010-09-30\",\n        \"2010-06-30\",\n        \"2010-03-31\",\n        \"2009-12-31\",\n        \"2009-09-30\",\n        \"2009-06-30\",\n        \"2009-03-31\",\n        \"2008-12-31\",\n        \"2008-09-30\",\n        \"2008-06-30\",\n        \"2008-03-31\",\n        \"2007-12-31\",\n        \"2007-09-30\",\n        \"2007-06-30\",\n        \"2007-03-31\",\n        \"2006-12-31\",\n        \"2006-09-30\",\n        \"2006-06-30\",\n        \"2006-03-31\",\n        \"2005-12-31\",\n        \"2005-09-30\",\n        \"2005-06-30\",\n        \"2005-03-31\",\n        \"2004-12-31\",\n        \"2004-09-30\",\n        \"2004-06-30\",\n        \"2004-03-31\",\n        \"2003-12-31\",\n        \"2003-09-30\",\n        \"2003-06-30\",\n        \"2003-03-31\",\n        \"2002-12-31\",\n        \"2002-09-30\",\n        \"2002-06-30\",\n        \"2002-03-31\",\n        \"2001-12-31\",\n        \"2001-06-30\",\n        \"2000-12-31\",\n        \"2000-06-30\",\n        \"1999-12-31\",\n        \"1999-06-30\",\n        \"1998-12-31\",\n        \"1998-06-30\",\n        \"1997-12-31\",\n        \"1997-06-30\",\n        \"1996-12-31\",\n        \"1995-12-31\",\n        \"1994-12-31\",\n    ]\n    result = FinanceFactor.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-12-30\",\n        order=FinanceFactor.report_date.desc(),\n        time_field=\"report_date\",\n    )\n    assert len(correct_timestamps) == len(result)\n    timestamps = [to_date_time_str(item.report_date) for item in result]\n    assert set(correct_timestamps) == set(timestamps)\n    latest: FinanceFactor = result[0]\n\n    assert latest.basic_eps == 0.4537\n    assert latest.diluted_eps == 0.4537\n    assert latest.bps == 5.0919\n    assert latest.capital_reserve_ps == 2.1769\n    assert latest.undistributed_profit_ps == 1.8132\n    assert latest.op_cash_flow_ps == 1.0148\n    assert latest.total_op_income == 31710000000\n    assert latest.gross_profit == 5491000000\n    assert latest.net_profit == 1811000000\n    assert latest.deducted_net_profit == 1897000000\n    assert latest.op_income_growth_yoy == -0.1024\n    assert latest.net_profit_growth_yoy == 1.2404\n    assert latest.deducted_net_profit_growth_yoy == 1.4813\n    assert latest.op_income_growth_qoq == 0.0408\n    assert latest.net_profit_growth_qoq == 0.2143\n    assert latest.deducted_net_profit_growth_qoq == 0.2955\n\n    assert latest.roe == 0.0882\n    assert latest.rota == 0.0376\n    assert latest.gross_profit_margin == 0.1731\n    assert latest.net_margin == 0.0591\n\n    assert latest.advance_receipts_per_op_income == 0.1\n    assert latest.sales_net_cash_flow_per_op_income == 0.88\n    assert latest.op_net_cash_flow_per_op_income == 0.13\n    assert latest.actual_tax_rate == 0.2362\n\n    assert latest.current_ratio == 1\n    assert latest.quick_ratio == 0.84\n    assert latest.cash_flow_ratio == 0.17\n    assert latest.debt_asset_ratio == 0.5766\n    assert latest.em == 2.36\n    assert latest.equity_ratio == 1.43\n\n    assert latest.total_assets_turnover_days == 423.91\n    assert latest.inventory_turnover_days == 32.88\n    assert latest.receivables_turnover_days == 52.23\n    assert latest.total_assets_turnover == 0.64\n    assert latest.inventory_turnover == 8.21\n    assert latest.receivables_turnover == 5.17\n\n\n# 企业资产负债表\ndef test_000778_balance_sheet():\n    correct_timestamps = [\n        \"2018-09-30\",\n        \"2018-06-30\",\n        \"2018-03-31\",\n        \"2017-12-31\",\n        \"2017-09-30\",\n        \"2017-06-30\",\n        \"2017-03-31\",\n        \"2016-12-31\",\n        \"2016-09-30\",\n        \"2016-06-30\",\n        \"2016-03-31\",\n        \"2015-12-31\",\n        \"2015-09-30\",\n        \"2015-06-30\",\n        \"2015-03-31\",\n        \"2014-12-31\",\n        \"2014-09-30\",\n        \"2014-06-30\",\n        \"2014-03-31\",\n        \"2013-12-31\",\n        \"2013-09-30\",\n        \"2013-06-30\",\n        \"2013-03-31\",\n        \"2012-12-31\",\n        \"2012-09-30\",\n        \"2012-06-30\",\n        \"2012-03-31\",\n        \"2011-12-31\",\n        \"2011-09-30\",\n        \"2011-06-30\",\n        \"2011-03-31\",\n        \"2010-12-31\",\n        \"2010-09-30\",\n        \"2010-06-30\",\n        \"2010-03-31\",\n        \"2009-12-31\",\n        \"2009-09-30\",\n        \"2009-06-30\",\n        \"2009-03-31\",\n        \"2008-12-31\",\n        \"2008-09-30\",\n        \"2008-06-30\",\n        \"2008-03-31\",\n        \"2007-12-31\",\n        \"2007-09-30\",\n        \"2007-06-30\",\n        \"2007-03-31\",\n        \"2006-12-31\",\n        \"2006-09-30\",\n        \"2006-06-30\",\n        \"2006-03-31\",\n        \"2005-12-31\",\n        \"2005-09-30\",\n        \"2005-06-30\",\n        \"2005-03-31\",\n        \"2004-12-31\",\n        \"2004-09-30\",\n        \"2004-06-30\",\n        \"2004-03-31\",\n        \"2003-12-31\",\n        \"2003-09-30\",\n        \"2003-06-30\",\n        \"2003-03-31\",\n        \"2002-12-31\",\n        \"2002-09-30\",\n        \"2002-06-30\",\n        \"2002-03-31\",\n        \"2001-12-31\",\n        \"2001-06-30\",\n        \"2000-12-31\",\n        \"2000-06-30\",\n        \"1999-12-31\",\n        \"1999-06-30\",\n        \"1998-12-31\",\n        \"1998-06-30\",\n        \"1997-12-31\",\n        \"1997-06-30\",\n        \"1996-12-31\",\n        \"1995-12-31\",\n        \"1994-12-31\",\n    ]\n    result = BalanceSheet.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-12-30\",\n        order=BalanceSheet.report_date.desc(),\n        time_field=\"report_date\",\n    )\n    assert len(correct_timestamps) == len(result)\n    timestamps = [to_date_time_str(item.report_date) for item in result]\n    assert set(correct_timestamps) == set(timestamps)\n    latest: BalanceSheet = result[0]\n\n    assert latest.cash_and_cash_equivalents == 6381000000\n    assert latest.note_receivable == 4655000000\n    assert latest.accounts_receivable == 1920000000\n    assert latest.advances_to_suppliers == 2112000000\n    assert latest.other_receivables == 4671000000\n    assert latest.inventories == 3891000000\n    assert latest.current_portion_of_non_current_assets == 270000000\n    assert latest.other_current_assets == 220100000\n    assert latest.total_current_assets == 24120000000\n\n    assert latest.fi_assets_saleable == 914900000\n    assert latest.long_term_receivables == 1078000000\n    assert latest.long_term_equity_investment == 6071000000\n    assert latest.real_estate_investment == 24130000\n    assert latest.fixed_assets == 15440000000\n    assert latest.construction_in_process == 1196000000\n    assert latest.intangible_assets == 853800000\n    assert latest.goodwill == 32990000\n    assert latest.long_term_prepaid_expenses == 2527000\n    assert latest.deferred_tax_assets == 292300000\n    assert latest.other_non_current_assets == 332800000\n    assert latest.total_assets == 50350000000\n\n    assert latest.short_term_borrowing == 9104000000\n    assert latest.accept_money_deposits == 0\n    assert latest.advances_from_customers == 3199000000\n    assert latest.employee_benefits_payable == 207800000\n    assert latest.taxes_payable == 735200000\n    assert latest.other_payable == 2022000000\n    assert latest.current_portion_of_non_current_liabilities == 1099000000\n    assert latest.other_current_liabilities == 1000000000\n    assert latest.total_current_liabilities == 24050000000\n\n    assert latest.long_term_borrowing == 914400000\n    assert latest.fi_bond_payable == 2992000000\n    assert latest.long_term_payable == 636800000\n    assert latest.deferred_tax_liabilities == 314800000\n    assert latest.other_non_current_liabilities == 127800000\n    assert latest.total_non_current_liabilities == 4986000000\n    assert latest.total_liabilities == 29040000000\n\n    assert latest.capital == 3991000000\n    assert latest.capital_reserve == 8688000000\n    assert latest.special_reserve == 40730000\n    assert latest.surplus_reserve == 1286000000\n    assert latest.undistributed_profits == 7236000000\n\n    assert latest.equity == 20320000000\n    assert latest.equity_as_minority_interest == 997400000\n    assert latest.total_equity == 21320000000\n    assert latest.total_liabilities_and_equity == 50350000000\n\n\n# 企业利润表\ndef test_000778_income_statement():\n    correct_timestamps = [\n        \"2018-09-30\",\n        \"2018-06-30\",\n        \"2018-03-31\",\n        \"2017-12-31\",\n        \"2017-09-30\",\n        \"2017-06-30\",\n        \"2017-03-31\",\n        \"2016-12-31\",\n        \"2016-09-30\",\n        \"2016-06-30\",\n        \"2016-03-31\",\n        \"2015-12-31\",\n        \"2015-09-30\",\n        \"2015-06-30\",\n        \"2015-03-31\",\n        \"2014-12-31\",\n        \"2014-09-30\",\n        \"2014-06-30\",\n        \"2014-03-31\",\n        \"2013-12-31\",\n        \"2013-09-30\",\n        \"2013-06-30\",\n        \"2013-03-31\",\n        \"2012-12-31\",\n        \"2012-09-30\",\n        \"2012-06-30\",\n        \"2012-03-31\",\n        \"2011-12-31\",\n        \"2011-09-30\",\n        \"2011-06-30\",\n        \"2011-03-31\",\n        \"2010-12-31\",\n        \"2010-09-30\",\n        \"2010-06-30\",\n        \"2010-03-31\",\n        \"2009-12-31\",\n        \"2009-09-30\",\n        \"2009-06-30\",\n        \"2009-03-31\",\n        \"2008-12-31\",\n        \"2008-09-30\",\n        \"2008-06-30\",\n        \"2008-03-31\",\n        \"2007-12-31\",\n        \"2007-09-30\",\n        \"2007-06-30\",\n        \"2007-03-31\",\n        \"2006-12-31\",\n        \"2006-09-30\",\n        \"2006-06-30\",\n        \"2006-03-31\",\n        \"2005-12-31\",\n        \"2005-09-30\",\n        \"2005-06-30\",\n        \"2005-03-31\",\n        \"2004-12-31\",\n        \"2004-09-30\",\n        \"2004-06-30\",\n        \"2004-03-31\",\n        \"2003-12-31\",\n        \"2003-09-30\",\n        \"2003-06-30\",\n        \"2003-03-31\",\n        \"2002-12-31\",\n        \"2002-09-30\",\n        \"2002-06-30\",\n        \"2002-03-31\",\n        \"2001-12-31\",\n        \"2001-06-30\",\n        \"2000-12-31\",\n        \"2000-06-30\",\n        \"1999-12-31\",\n        \"1999-06-30\",\n        \"1998-12-31\",\n        \"1998-06-30\",\n        \"1997-12-31\",\n        \"1997-06-30\",\n        \"1996-12-31\",\n        \"1995-12-31\",\n        \"1994-12-31\",\n    ]\n    result = IncomeStatement.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-12-30\",\n        order=IncomeStatement.report_date.desc(),\n        time_field=\"report_date\",\n    )\n    assert len(correct_timestamps) == len(result)\n    timestamps = [to_date_time_str(item.report_date) for item in result]\n    assert set(correct_timestamps) == set(timestamps)\n    latest: IncomeStatement = result[0]\n\n    assert latest.operating_income == 31710000000\n    assert latest.total_operating_costs == 29230000000\n    assert latest.operating_costs == 26220000000\n    assert latest.rd_costs == 185500000\n    assert latest.net_change_in_insurance_contract_reserves == 0\n    assert latest.business_taxes_and_surcharges == 359700000\n    assert latest.sales_costs == 771400000\n    assert latest.managing_costs == 472900000\n    assert latest.financing_costs == 397500000\n    assert latest.assets_devaluation == 824400000\n    assert latest.investment_income == 104100000\n    assert latest.investment_income_from_related_enterprise == 61290000\n\n    assert latest.operating_profit == 2637000000\n    assert latest.non_operating_income == 38340000\n    assert latest.non_operating_costs == 221700000\n\n    assert latest.total_profits == 2454000000\n    assert latest.tax_expense == 579600000\n    assert latest.net_profit == 1874000000\n    assert latest.net_profit_as_parent == 1811000000\n    assert latest.net_profit_as_minority_interest == 63570000\n    assert latest.deducted_net_profit == 1897000000\n\n    assert latest.eps == 0.4537\n    assert latest.diluted_eps == 0.4537\n\n    assert latest.other_comprehensive_income == -521000000\n    assert latest.other_comprehensive_income_as_parent == -522400000\n    assert latest.other_comprehensive_income_as_minority_interest == 1403000\n    assert latest.total_comprehensive_income == 1353000000\n    assert latest.total_comprehensive_income_as_parent == 1288000000\n    assert latest.total_comprehensive_income_as_minority_interest == 64980000\n\n\n# 银行现金流量表\ndef test_000778_cash_flow_statement():\n    correct_timestamps = [\n        \"2018-09-30\",\n        \"2018-06-30\",\n        \"2018-03-31\",\n        \"2017-12-31\",\n        \"2017-09-30\",\n        \"2017-06-30\",\n        \"2017-03-31\",\n        \"2016-12-31\",\n        \"2016-09-30\",\n        \"2016-06-30\",\n        \"2016-03-31\",\n        \"2015-12-31\",\n        \"2015-09-30\",\n        \"2015-06-30\",\n        \"2015-03-31\",\n        \"2014-12-31\",\n        \"2014-09-30\",\n        \"2014-06-30\",\n        \"2014-03-31\",\n        \"2013-12-31\",\n        \"2013-09-30\",\n        \"2013-06-30\",\n        \"2013-03-31\",\n        \"2012-12-31\",\n        \"2012-09-30\",\n        \"2012-06-30\",\n        \"2012-03-31\",\n        \"2011-12-31\",\n        \"2011-09-30\",\n        \"2011-06-30\",\n        \"2011-03-31\",\n        \"2010-12-31\",\n        \"2010-09-30\",\n        \"2010-06-30\",\n        \"2010-03-31\",\n        \"2009-12-31\",\n        \"2009-09-30\",\n        \"2009-06-30\",\n        \"2009-03-31\",\n        \"2008-12-31\",\n        \"2008-09-30\",\n        \"2008-06-30\",\n        \"2008-03-31\",\n        \"2007-12-31\",\n        \"2007-09-30\",\n        \"2007-06-30\",\n        \"2007-03-31\",\n        \"2006-12-31\",\n        \"2006-09-30\",\n        \"2006-06-30\",\n        \"2006-03-31\",\n        \"2005-12-31\",\n        \"2005-09-30\",\n        \"2005-06-30\",\n        \"2005-03-31\",\n        \"2004-12-31\",\n        \"2004-09-30\",\n        \"2004-06-30\",\n        \"2004-03-31\",\n        \"2003-12-31\",\n        \"2003-09-30\",\n        \"2003-06-30\",\n        \"2003-03-31\",\n        \"2002-12-31\",\n        \"2002-06-30\",\n        \"2001-12-31\",\n        \"2001-06-30\",\n        \"2000-12-31\",\n        \"2000-06-30\",\n        \"1999-12-31\",\n        \"1998-12-31\",\n        \"1998-06-30\",\n    ]\n    result = CashFlowStatement.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-12-30\",\n        order=CashFlowStatement.report_date.desc(),\n        time_field=\"report_date\",\n    )\n    assert len(correct_timestamps) == len(result)\n    timestamps = [to_date_time_str(item.report_date) for item in result]\n    assert set(correct_timestamps) == set(timestamps)\n    latest: CashFlowStatement = result[0]\n\n    assert latest.cash_from_selling == 27784000000\n    assert latest.tax_refund == 60700000\n    assert latest.cash_from_other_op == 1463000000\n    assert latest.total_op_cash_inflows == 29310000000\n    assert latest.cash_to_goods_services == 21210000000\n    assert latest.cash_to_employees == 1460000000\n    assert latest.taxes_and_surcharges == 2016000000\n    assert latest.cash_to_other_related_op == 573700000\n    assert latest.total_op_cash_outflows == 25260000000\n    assert latest.net_op_cash_flows == 4050000000\n\n    assert latest.cash_from_disposal_of_investments == 556500000\n    assert latest.cash_from_returns_on_investments == 44180000\n    assert latest.cash_from_disposal_fixed_intangible_assets == 457200\n    assert latest.cash_from_disposal_subsidiaries == 1046000000\n    assert latest.cash_from_other_investing == 553000000\n    assert latest.total_investing_cash_inflows == 2201000000\n    assert latest.cash_to_acquire_fixed_intangible_assets == 2521000000\n    assert latest.cash_to_investments == 1808000000\n    assert latest.total_investing_cash_outflows == 4329000000\n    assert latest.net_investing_cash_flows == -2128000000\n\n    assert latest.cash_from_accepting_investment == 24500000\n    assert latest.cash_from_subsidiaries_accepting_minority_interest == 24500000\n    assert latest.cash_from_borrowings == 10080000000\n    assert latest.cash_from_issuing_bonds == 997000000\n    assert latest.cash_from_other_financing == 200000000\n    assert latest.total_financing_cash_inflows == 11300000000\n    assert latest.cash_to_repay_borrowings == 11940000000\n    assert latest.cash_to_pay_interest_dividend == 892100000\n    assert latest.cash_to_other_financing == 328500000\n    assert latest.total_financing_cash_outflows == 13160000000\n    assert latest.net_financing_cash_flows == -1862000000\n\n    assert latest.foreign_exchange_rate_effect == 21350000\n    assert latest.net_cash_increase == 81240000\n    assert latest.cash_at_beginning == 5078000000\n    assert latest.cash == 5159000000\n"
  },
  {
    "path": "tests/api/test_holder.py",
    "content": "from zvt.contract.api import get_db_session\nfrom ..context import init_test_context\n\ninit_test_context()\n\nfrom typing import List\n\nfrom zvt.domain import TopTenHolder, TopTenTradableHolder\n\nsession = get_db_session(provider=\"eastmoney\", db_name=\"holder\")  # type: sqlalchemy.orm.Session\n\n\n# 十大股东\ndef test_000778_top_ten_holder():\n    result: List[TopTenHolder] = TopTenHolder.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-09-30\",\n        start_timestamp=\"2018-09-30\",\n        order=TopTenHolder.shareholding_ratio.desc(),\n    )\n    assert len(result) == 10\n    assert result[0].holder_name == \"新兴际华集团有限公司\"\n    assert result[0].shareholding_numbers == 1595000000\n    assert result[0].shareholding_ratio == 0.3996\n    assert result[0].change == 32080000\n    assert result[0].change_ratio == 0.0205\n\n\ndef test_000778_top_ten_tradable_holder():\n    result: List[TopTenHolder] = TopTenTradableHolder.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-09-30\",\n        start_timestamp=\"2018-09-30\",\n        order=TopTenTradableHolder.shareholding_ratio.desc(),\n    )\n    assert len(result) == 10\n    assert result[0].holder_name == \"新兴际华集团有限公司\"\n    assert result[0].shareholding_numbers == 1525000000\n    assert result[0].shareholding_ratio == 0.389\n    assert result[0].change == 38560000\n    assert result[0].change_ratio == 0.0259\n"
  },
  {
    "path": "tests/api/test_intent.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.intent import compare, distribute, composite, composite_all\nfrom zvt.contract.drawer import ChartType\nfrom zvt.domain import FinanceFactor, CashFlowStatement, BalanceSheet, Stock1dKdata\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\ndef test_compare_kdata():\n    entity_ids = [\"stock_sz_000338\", \"stock_sh_601318\"]\n    compare(entity_ids=entity_ids, scale_value=10)\n    compare(entity_ids=entity_ids, start_timestamp=\"2010-01-01\")\n\n\ndef test_compare_line():\n    entity_ids = [\"stock_sz_000338\", \"stock_sh_601318\"]\n    compare(entity_ids=entity_ids, schema_map_columns={FinanceFactor: [FinanceFactor.roe]})\n\n\ndef test_compare_scatter():\n    entity_ids = [\"stock_sz_000338\", \"stock_sh_601318\"]\n    compare(\n        entity_ids=entity_ids, schema_map_columns={FinanceFactor: [FinanceFactor.roe]}, chart_type=ChartType.scatter\n    )\n\n\ndef test_compare_area():\n    entity_ids = [\"stock_sz_000338\", \"stock_sh_601318\"]\n    compare(entity_ids=entity_ids, schema_map_columns={FinanceFactor: [FinanceFactor.roe]}, chart_type=ChartType.area)\n\n\ndef test_compare_bar():\n    entity_ids = [\"stock_sz_000338\", \"stock_sh_601318\"]\n    compare(entity_ids=entity_ids, schema_map_columns={FinanceFactor: [FinanceFactor.roe]}, chart_type=ChartType.bar)\n\n\ndef test_distribute():\n    distribute(entity_ids=None, data_schema=FinanceFactor, columns=[\"roe\"])\n\n\ndef test_composite():\n    composite(\n        entity_id=\"stock_sz_000338\",\n        data_schema=CashFlowStatement,\n        columns=[\n            CashFlowStatement.net_op_cash_flows,\n            CashFlowStatement.net_investing_cash_flows,\n            CashFlowStatement.net_financing_cash_flows,\n        ],\n        filters=[\n            CashFlowStatement.report_period == \"year\",\n            CashFlowStatement.report_date == to_pd_timestamp(\"2016-12-31\"),\n        ],\n    )\n    composite(\n        entity_id=\"stock_sz_000338\",\n        data_schema=BalanceSheet,\n        columns=[\n            BalanceSheet.total_current_assets,\n            BalanceSheet.total_non_current_assets,\n            BalanceSheet.total_current_liabilities,\n            BalanceSheet.total_non_current_liabilities,\n        ],\n        filters=[BalanceSheet.report_period == \"year\", BalanceSheet.report_date == to_pd_timestamp(\"2016-12-31\")],\n    )\n\n\ndef test_composite_all():\n    composite_all(\n        provider=\"joinquant\",\n        entity_ids=None,\n        data_schema=Stock1dKdata,\n        column=Stock1dKdata.turnover,\n        timestamp=to_pd_timestamp(\"2016-12-02\"),\n    )\n"
  },
  {
    "path": "tests/api/test_joinquant_quotes.py",
    "content": "from zvt.api.kdata import get_kdata\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.api import get_db_session\nfrom ..context import init_test_context\n\ninit_test_context()\n\nday_k_session = get_db_session(provider=\"joinquant\", db_name=\"stock_1d_kdata\")  # type: sqlalchemy.orm.Session\n\nday_1h_session = get_db_session(provider=\"joinquant\", db_name=\"stock_1h_kdata\")  # type: sqlalchemy.orm.Session\n\n\ndef test_jq_603220_kdata():\n    df = get_kdata(\n        entity_id=\"stock_sh_603220\", session=day_k_session, level=IntervalLevel.LEVEL_1DAY, provider=\"joinquant\"\n    )\n    print(df)\n    df = get_kdata(\n        entity_id=\"stock_sh_603220\", session=day_1h_session, level=IntervalLevel.LEVEL_1HOUR, provider=\"joinquant\"\n    )\n    print(df)\n"
  },
  {
    "path": "tests/api/test_kdata.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.kdata import get_kdata\nfrom zvt.api.kdata import get_latest_kdata_date\nfrom zvt.contract import IntervalLevel, AdjustType\n\n\ndef test_jq_1mon_kdata():\n    df = get_kdata(entity_id=\"stock_sz_000338\", provider=\"joinquant\", level=IntervalLevel.LEVEL_1MON)\n    se = df.loc[\"2010-01-29\"]\n    # make sure our fq is ok\n    assert round(se[\"open\"], 2) <= 5.44\n    assert round(se[\"high\"], 2) <= 6.43\n    assert round(se[\"low\"], 2) <= 5.2\n    assert round(se[\"close\"], 2) <= 5.45\n\n\ndef test_jq_1wk_kdata():\n    df = get_kdata(entity_id=\"stock_sz_000338\", provider=\"joinquant\", level=IntervalLevel.LEVEL_1WEEK)\n    print(df)\n\n\ndef test_jq_1d_kdata():\n    df = get_kdata(entity_id=\"stock_sz_000338\", provider=\"joinquant\", level=IntervalLevel.LEVEL_1DAY)\n    print(df)\n\n    se = df.loc[\"2019-04-08\"]\n    # make sure our fq is ok\n    assert round(se[\"open\"], 2) <= 12.86\n    assert round(se[\"high\"], 2) <= 14.16\n    assert round(se[\"low\"], 2) <= 12.86\n    assert round(se[\"close\"], 2) <= 14.08\n\n\ndef test_jq_1d_hfq_kdata():\n    df = get_kdata(entity_id=\"stock_sz_000338\", provider=\"joinquant\", level=IntervalLevel.LEVEL_1DAY, adjust_type=\"hfq\")\n    se = df.loc[\"2019-04-08\"]\n    print(se)\n    assert round(se[\"open\"], 2) == 249.29\n    assert round(se[\"high\"], 2) == 273.68\n    assert round(se[\"low\"], 2) == 249.29\n    assert round(se[\"close\"], 2) == 272.18\n\n\ndef test_get_latest_kdata_date():\n    date = get_latest_kdata_date(provider=\"joinquant\", entity_type=\"stock\", adjust_type=AdjustType.hfq)\n    assert date is not None\n"
  },
  {
    "path": "tests/api/test_technical.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.contract.api import get_entities\nfrom ..context import init_test_context\n\ninit_test_context()\n\n\ndef test_basic_get_securities():\n    items = get_entities(entity_type=\"stock\", provider=\"eastmoney\")\n    print(items)\n    items = get_entities(entity_type=\"index\", provider=\"exchange\")\n    print(items)\n"
  },
  {
    "path": "tests/api/test_trading.py",
    "content": "from zvt.contract.api import get_db_session\nfrom ..context import init_test_context\n\ninit_test_context()\n\nfrom typing import List\n\nfrom zvt.domain import HolderTrading, ManagerTrading\n\nsession = get_db_session(provider=\"eastmoney\", db_name=\"trading\")  # type: sqlalchemy.orm.Session\n\n\n# 股东交易\ndef test_000778_holder_trading():\n    result: List[HolderTrading] = HolderTrading.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-09-30\",\n        start_timestamp=\"2018-09-30\",\n        order=HolderTrading.holding_pct.desc(),\n    )\n    assert len(result) == 6\n    assert result[0].holder_name == \"新兴际华集团有限公司\"\n    assert result[0].change_pct == 0.0205\n    assert result[0].volume == 32080000\n    assert result[0].holding_pct == 0.3996\n\n\n# 高管交易\ndef test_000778_manager_trading():\n    result: List[ManagerTrading] = ManagerTrading.query_data(\n        session=session,\n        provider=\"eastmoney\",\n        return_type=\"domain\",\n        codes=[\"000778\"],\n        end_timestamp=\"2018-09-30\",\n        start_timestamp=\"2017-09-30\",\n        order=ManagerTrading.holding.desc(),\n    )\n    assert len(result) == 1\n    assert result[0].trading_person == \"巩国平\"\n    assert result[0].volume == 8400\n    assert result[0].price == None\n    assert result[0].holding == 18700\n    assert result[0].trading_way == \"增持\"\n    assert result[0].manager_position == \"职工监事\"\n    assert result[0].manager == \"巩国平\"\n    assert result[0].relationship_with_manager == \"本人\"\n"
  },
  {
    "path": "tests/context.py",
    "content": "# -*- coding: utf-8 -*-\n\n\ndef init_test_context():\n    import os\n    import sys\n\n    sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), \"../src\")))\n"
  },
  {
    "path": "tests/contract/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/contract/test_add_provider_plugin.py",
    "content": "# -*- coding: utf-8 -*-\n\n\ndef test_add_tushare_provider():\n    pass\n    # register_schema(providers=[\"tushare\"], db_name=\"stock_meta\", schema_base=StockMetaBase)\n    #\n    # from zvt.domain import Stock\n    #\n    # Stock.query_data(provider=\"tushare\")\n    # try:\n    #     Stock.record_data(provider=\"tushare\")\n    #     assert False\n    # except Exception as e:\n    #     print(e)\n"
  },
  {
    "path": "tests/contract/test_entity.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.contract import TradableEntity, IntervalLevel\nfrom zvt.utils.time_utils import to_pd_timestamp\n\n\ndef test_get_1min_timestamps():\n    timestamps = []\n    for timestamp in TradableEntity.get_interval_timestamps(\n        start_date=\"2020-06-17\", end_date=\"2020-06-18\", level=IntervalLevel.LEVEL_1MIN\n    ):\n        timestamps.append(timestamp)\n\n    assert to_pd_timestamp(\"2020-06-17 09:31:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-17 11:30:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-17 13:01:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-17 15:00:00\") in timestamps\n\n    assert to_pd_timestamp(\"2020-06-17 09:31:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-17 11:30:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-17 13:01:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-18 15:00:00\") in timestamps\n\n\ndef test_get_1h_timestamps():\n    timestamps = []\n    for timestamp in TradableEntity.get_interval_timestamps(\n        start_date=\"2020-06-17\", end_date=\"2020-06-18\", level=IntervalLevel.LEVEL_1HOUR\n    ):\n        timestamps.append(timestamp)\n\n    assert to_pd_timestamp(\"2020-06-17 10:30:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-17 11:30:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-17 14:00:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-17 15:00:00\") in timestamps\n\n    assert to_pd_timestamp(\"2020-06-17 10:30:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-17 11:30:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-17 14:00:00\") in timestamps\n    assert to_pd_timestamp(\"2020-06-18 15:00:00\") in timestamps\n\n\ndef test_is_finished_kdata_timestamp():\n    assert TradableEntity.is_finished_kdata_timestamp(\"2020-06-17 10:30\", IntervalLevel.LEVEL_30MIN)\n    assert not TradableEntity.is_finished_kdata_timestamp(\"2020-06-17 10:30\", IntervalLevel.LEVEL_1DAY)\n\n    assert TradableEntity.is_finished_kdata_timestamp(\"2020-06-17 11:30\", IntervalLevel.LEVEL_30MIN)\n    assert not TradableEntity.is_finished_kdata_timestamp(\"2020-06-17 11:30\", IntervalLevel.LEVEL_1DAY)\n\n    assert TradableEntity.is_finished_kdata_timestamp(\"2020-06-17 13:30\", IntervalLevel.LEVEL_30MIN)\n    assert not TradableEntity.is_finished_kdata_timestamp(\"2020-06-17 13:30\", IntervalLevel.LEVEL_1DAY)\n\n\ndef test_open_close():\n    assert TradableEntity.is_open_timestamp(\"2020-06-17 09:30\")\n    assert TradableEntity.is_close_timestamp(\"2020-06-17 15:00\")\n\n    timestamps = []\n    for timestamp in TradableEntity.get_interval_timestamps(\n        start_date=\"2020-06-17\", end_date=\"2020-06-18\", level=IntervalLevel.LEVEL_1HOUR\n    ):\n        timestamps.append(timestamp)\n\n    assert TradableEntity.is_open_timestamp(timestamps[0])\n    assert TradableEntity.is_close_timestamp(timestamps[-1])\n"
  },
  {
    "path": "tests/contract/test_reader.py",
    "content": "# -*- coding: utf-8 -*-\nfrom ..context import init_test_context\n\ninit_test_context()\n\nimport time\n\nfrom zvt.domain import Stock1dKdata, Stock\n\nfrom zvt.utils.time_utils import to_date_time_str\n\nfrom zvt.contract.reader import DataReader\nfrom zvt.contract import IntervalLevel\n\n\ndef test_china_stock_reader():\n    data_reader = DataReader(\n        provider=\"joinquant\",\n        data_schema=Stock1dKdata,\n        entity_schema=Stock,\n        entity_provider=\"eastmoney\",\n        codes=[\"002572\", \"000338\"],\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2019-06-10\",\n    )\n\n    categories = data_reader.data_df.index.levels[0].to_list()\n\n    df = data_reader.data_df\n\n    assert \"stock_sz_002572\" in categories\n    assert \"stock_sz_000338\" in categories\n\n    assert (\"stock_sz_002572\", \"2019-01-02\") in df.index\n    assert (\"stock_sz_000338\", \"2019-01-02\") in df.index\n    assert (\"stock_sz_002572\", \"2019-06-10\") in df.index\n    assert (\"stock_sz_000338\", \"2019-06-10\") in df.index\n\n    for timestamp in Stock.get_interval_timestamps(\n        start_date=\"2019-06-11\", end_date=\"2019-06-14\", level=IntervalLevel.LEVEL_1DAY\n    ):\n        data_reader.move_on(to_timestamp=timestamp)\n\n        df = data_reader.data_df\n\n        assert (\"stock_sz_002572\", timestamp) in df.index\n        assert (\"stock_sz_000338\", to_date_time_str(timestamp)) in df.index\n\n\ndef test_reader_move_on():\n    data_reader = DataReader(\n        data_schema=Stock1dKdata,\n        entity_schema=Stock,\n        entity_provider=\"eastmoney\",\n        codes=[\"002572\", \"000338\"],\n        start_timestamp=\"2019-06-13\",\n        end_timestamp=\"2019-06-14\",\n    )\n\n    data_reader.move_on(to_timestamp=\"2019-06-15\")\n    assert (\"stock_sz_002572\", \"2019-06-15\") not in data_reader.data_df.index\n    assert (\"stock_sz_000338\", \"2019-06-15\") not in data_reader.data_df.index\n\n    start_time = time.time()\n    data_reader.move_on(to_timestamp=\"2019-06-20\", timeout=5)\n    assert time.time() - start_time < 5\n"
  },
  {
    "path": "tests/contract/test_schema.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain import Stock, Stockhk, Stockus\n\n\ndef test_stock_trading_time():\n    assert Stock.before_trading_time(timestamp=\"2024-09-02 08:00\") is True\n    assert Stock.before_trading_time(timestamp=\"2024-09-02 12:00\") is False\n\n    assert Stock.after_trading_time(timestamp=\"2024-09-02 08:00\") is False\n    assert Stock.after_trading_time(timestamp=\"2024-09-02 12:00\") is False\n    assert Stock.after_trading_time(timestamp=\"2024-09-02 14:00\") is False\n    assert Stock.after_trading_time(timestamp=\"2024-09-02 16:00\") is True\n\n    assert Stock.in_real_trading_time(timestamp=\"2024-09-02 08:00\") is False\n    assert Stock.in_real_trading_time(timestamp=\"2024-09-02 09:20\") is True\n    assert Stock.in_real_trading_time(timestamp=\"2024-09-02 09:30\") is True\n    assert Stock.in_real_trading_time(timestamp=\"2024-09-02 11:00\") is True\n    assert Stock.in_real_trading_time(timestamp=\"2024-09-02 11:30\") is True\n    assert Stock.in_real_trading_time(timestamp=\"2024-09-02 11:40\") is False\n    assert Stock.in_real_trading_time(timestamp=\"2024-09-02 13:00\") is True\n    assert Stock.in_real_trading_time(timestamp=\"2024-09-02 15:00\") is True\n    assert Stock.in_real_trading_time(timestamp=\"2024-09-02 15:10\") is False\n    assert Stock.in_real_trading_time(timestamp=\"2024-09-02 16:10\") is False\n\n    assert Stock.in_trading_time(timestamp=\"2024-09-02 08:00\") is False\n    assert Stock.in_trading_time(timestamp=\"2024-09-02 09:20\") is True\n    assert Stock.in_trading_time(timestamp=\"2024-09-02 09:30\") is True\n    assert Stock.in_trading_time(timestamp=\"2024-09-02 11:00\") is True\n    assert Stock.in_trading_time(timestamp=\"2024-09-02 11:30\") is True\n    assert Stock.in_trading_time(timestamp=\"2024-09-02 11:40\") is True\n    assert Stock.in_trading_time(timestamp=\"2024-09-02 13:00\") is True\n    assert Stock.in_trading_time(timestamp=\"2024-09-02 15:00\") is True\n    assert Stock.in_trading_time(timestamp=\"2024-09-02 15:10\") is False\n    assert Stock.in_trading_time(timestamp=\"2024-09-02 16:10\") is False\n\n\ndef test_stock_hk_trading_time():\n    assert Stockhk.before_trading_time(timestamp=\"2024-09-02 08:00\") is True\n    assert Stockhk.before_trading_time(timestamp=\"2024-09-02 12:00\") is False\n\n    assert Stockhk.after_trading_time(timestamp=\"2024-09-02 08:00\") is False\n    assert Stockhk.after_trading_time(timestamp=\"2024-09-02 12:00\") is False\n    assert Stockhk.after_trading_time(timestamp=\"2024-09-02 14:00\") is False\n    assert Stockhk.after_trading_time(timestamp=\"2024-09-02 17:00\") is True\n\n    assert Stockhk.in_real_trading_time(timestamp=\"2024-09-02 08:00\") is False\n    assert Stockhk.in_real_trading_time(timestamp=\"2024-09-02 09:15\") is True\n    assert Stockhk.in_real_trading_time(timestamp=\"2024-09-02 09:30\") is True\n    assert Stockhk.in_real_trading_time(timestamp=\"2024-09-02 11:00\") is True\n    assert Stockhk.in_real_trading_time(timestamp=\"2024-09-02 12:00\") is True\n    assert Stockhk.in_real_trading_time(timestamp=\"2024-09-02 12:40\") is False\n    assert Stockhk.in_real_trading_time(timestamp=\"2024-09-02 13:00\") is True\n    assert Stockhk.in_real_trading_time(timestamp=\"2024-09-02 15:00\") is True\n    assert Stockhk.in_real_trading_time(timestamp=\"2024-09-02 16:10\") is False\n    assert Stockhk.in_real_trading_time(timestamp=\"2024-09-02 17:10\") is False\n\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 08:00\") is False\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 09:20\") is True\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 09:30\") is True\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 11:00\") is True\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 11:30\") is True\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 11:40\") is True\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 12:00\") is True\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 13:00\") is True\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 15:00\") is True\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 16:00\") is True\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 16:10\") is False\n    assert Stockhk.in_trading_time(timestamp=\"2024-09-02 17:10\") is False\n\n\ndef test_stock_us_trading_time():\n    assert Stockus.before_trading_time(timestamp=\"2024-09-02 08:00\") is True\n    assert Stockus.before_trading_time(timestamp=\"2024-09-02 11:00\") is False\n\n    assert Stockus.after_trading_time(timestamp=\"2024-09-02 17:00\") is True\n    assert Stockus.after_trading_time(timestamp=\"2024-09-02 15:00\") is False\n\n    assert Stockus.in_real_trading_time(timestamp=\"2024-09-02 08:00\") is False\n    assert Stockus.in_real_trading_time(timestamp=\"2024-09-02 09:30\") is True\n    assert Stockus.in_real_trading_time(timestamp=\"2024-09-02 11:00\") is True\n    assert Stockus.in_real_trading_time(timestamp=\"2024-09-02 12:00\") is True\n    assert Stockus.in_real_trading_time(timestamp=\"2024-09-02 12:40\") is True\n    assert Stockus.in_real_trading_time(timestamp=\"2024-09-02 13:00\") is True\n    assert Stockus.in_real_trading_time(timestamp=\"2024-09-02 15:00\") is True\n    assert Stockus.in_real_trading_time(timestamp=\"2024-09-02 16:00\") is True\n    assert Stockus.in_real_trading_time(timestamp=\"2024-09-02 16:10\") is False\n    assert Stockus.in_real_trading_time(timestamp=\"2024-09-02 17:10\") is False\n\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 08:00\") is False\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 09:20\") is False\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 09:30\") is True\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 11:00\") is True\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 11:30\") is True\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 11:40\") is True\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 12:00\") is True\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 13:00\") is True\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 15:00\") is True\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 16:00\") is True\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 16:10\") is False\n    assert Stockus.in_trading_time(timestamp=\"2024-09-02 17:10\") is False\n"
  },
  {
    "path": "tests/factors/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/factors/test_algorithm.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.factors.algorithm import point_in_range, intersect, intersect_ranges, combine, distance\n\n\ndef test_point_in_range():\n    assert point_in_range(1, (1, 2))\n    assert point_in_range(2, (1, 2))\n    assert point_in_range(1.5, (1, 2))\n\n    assert not point_in_range(0.5, (1, 2))\n    assert not point_in_range(2.4, (1, 2))\n\n\ndef test_intersect():\n    a = (1, 2)\n    b = (1.5, 3)\n    assert intersect(a, b) == (1.5, 2)\n    assert intersect(b, a) == (1.5, 2)\n\n    a = (1, 2)\n    b = (3, 4)\n    assert intersect(a, b) == None\n    assert intersect(b, a) == None\n\n    a = (1, 4)\n    b = (2, 3)\n    assert intersect(a, b) == (2, 3)\n    assert intersect(b, a) == (2, 3)\n\n\ndef test_intersect_ranges():\n    a = (1, 2)\n    b = (1.5, 3)\n    c = (1.5, 5)\n    assert intersect_ranges([a, b, c]) == (1.5, 2)\n    assert intersect_ranges([b, a, c]) == (1.5, 2)\n    assert intersect_ranges([a, c, b]) == (1.5, 2)\n\n    a = (1, 2)\n    b = (1.5, 3)\n    c = (4, 5)\n    assert intersect_ranges([a, b, c]) == None\n    assert intersect_ranges([b, a, c]) == None\n    assert intersect_ranges([a, c, b]) == None\n\n    a = (1, 10)\n    b = (1.5, 3)\n    c = (2, 5)\n    assert intersect_ranges([a, b, c]) == (2, 3)\n    assert intersect_ranges([b, a, c]) == (2, 3)\n    assert intersect_ranges([a, c, b]) == (2, 3)\n\n\ndef test_combine():\n    a = (1, 2)\n    b = (1.5, 3)\n    assert combine(a, b) == (1, 3)\n    assert combine(b, a) == (1, 3)\n\n    a = (1, 2)\n    b = (3, 4)\n    assert combine(a, b) == None\n    assert combine(b, a) == None\n\n    a = (1, 4)\n    b = (2, 3)\n    assert combine(a, b) == (1, 4)\n    assert combine(b, a) == (1, 4)\n\n\ndef test_distance():\n    a = (1, 2)\n    b = (1.5, 3)\n    assert distance(a, b) == (4.5 / 2 - 1.5) / 1.5\n    assert distance(b, a) == (1.5 - 4.5 / 2) / 2.25\n\n    a = (1, 2)\n    b = (3, 4)\n    assert distance(a, b) == (3.5 - 1.5) / 1.5\n    assert distance(b, a) == (1.5 - 3.5) / 3.5\n\n    assert distance(a, b, use_max=True) == 3\n    assert distance(b, a, use_max=True) == -3 / 4\n\n    a = (1, 4)\n    b = (2, 3)\n    assert distance(a, b) == 0\n    assert distance(b, a) == 0\n"
  },
  {
    "path": "tests/factors/test_factor_select_targets.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.contract import IntervalLevel\nfrom zvt.factors.ma.ma_factor import CrossMaFactor\n\nfrom zvt.contract.factor import TargetType\nfrom zvt.factors.macd.macd_factor import BullFactor\nfrom ..context import init_test_context\n\ninit_test_context()\n\n\ndef test_cross_ma_select_targets():\n    entity_ids = [\"stock_sz_000338\"]\n    start_timestamp = \"2018-01-01\"\n    end_timestamp = \"2019-06-30\"\n    factor = CrossMaFactor(\n        provider=\"joinquant\",\n        entity_ids=entity_ids,\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        keep_window=10,\n        windows=[5, 10],\n        need_persist=False,\n        level=IntervalLevel.LEVEL_1DAY,\n        adjust_type=\"hfq\",\n    )\n    assert \"stock_sz_000338\" in factor.get_targets(timestamp=\"2018-01-19\")\n\n\ndef test_bull_select_targets():\n    factor = BullFactor(\n        start_timestamp=\"2019-01-01\", end_timestamp=\"2019-06-10\", level=IntervalLevel.LEVEL_1DAY, provider=\"joinquant\"\n    )\n\n    targets = factor.get_targets(timestamp=\"2019-05-08\", target_type=TargetType.positive)\n\n    assert \"stock_sz_000338\" not in targets\n    assert \"stock_sz_002572\" not in targets\n\n    targets = factor.get_targets(\"2019-05-08\", target_type=TargetType.negative)\n    assert \"stock_sz_000338\" in targets\n    assert \"stock_sz_002572\" not in targets\n\n    factor.move_on(timeout=0)\n\n    targets = factor.get_targets(timestamp=\"2019-06-19\", target_type=TargetType.positive)\n\n    assert \"stock_sz_000338\" in targets\n\n    assert \"stock_sz_002572\" not in targets\n"
  },
  {
    "path": "tests/factors/test_factors.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.factors.zen.zen_factor import ZenFactor\n\n\ndef test_zen_factor():\n    z = ZenFactor(\n        codes=[\"000338\"],\n        need_persist=False,\n        provider=\"joinquant\",\n    )\n    z.draw(show=True)\n\n    z = ZenFactor(\n        codes=[\"000338\", \"601318\"],\n        need_persist=True,\n        provider=\"joinquant\",\n    )\n    z.draw(show=True)\n"
  },
  {
    "path": "tests/factors/test_technical_factor.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.contract import IntervalLevel\nfrom zvt.factors.algorithm import MaTransformer, MacdTransformer\nfrom zvt.factors.ma.ma_factor import CrossMaFactor\nfrom ..context import init_test_context\n\ninit_test_context()\n\nfrom zvt.factors.technical_factor import TechnicalFactor\n\n\ndef test_ma():\n    factor = TechnicalFactor(\n        provider=\"joinquant\",\n        codes=[\"000338\"],\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2019-06-10\",\n        level=IntervalLevel.LEVEL_1DAY,\n        keep_window=30,\n        transformer=MaTransformer(windows=[5, 10, 30]),\n        adjust_type=\"qfq\",\n    )\n\n    print(factor.factor_df.tail())\n\n    # compare with east money manually\n    ma5 = factor.factor_df[\"ma5\"]\n    ma10 = factor.factor_df[\"ma10\"]\n    ma30 = factor.factor_df[\"ma30\"]\n\n    assert round(ma5.loc[(\"stock_sz_000338\", \"2019-06-10\")], 2) <= 11.23\n    assert round(ma10.loc[(\"stock_sz_000338\", \"2019-06-10\")], 2) <= 11.43\n    assert round(ma30.loc[(\"stock_sz_000338\", \"2019-06-10\")], 2) <= 11.52\n\n    factor.move_on(to_timestamp=\"2019-06-17\")\n    ma5 = factor.factor_df[\"ma5\"]\n    ma10 = factor.factor_df[\"ma10\"]\n    ma30 = factor.factor_df[\"ma30\"]\n\n    assert round(ma5.loc[(\"stock_sz_000338\", \"2019-06-17\")], 2) <= 12.06\n    assert round(ma10.loc[(\"stock_sz_000338\", \"2019-06-17\")], 2) <= 11.64\n    assert round(ma30.loc[(\"stock_sz_000338\", \"2019-06-17\")], 2) <= 11.50\n\n\ndef test_macd():\n    factor = TechnicalFactor(\n        provider=\"joinquant\",\n        codes=[\"000338\"],\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2019-06-10\",\n        level=IntervalLevel.LEVEL_1DAY,\n        keep_window=None,\n        transformer=MacdTransformer(),\n        adjust_type=\"qfq\",\n    )\n\n    print(factor.factor_df.tail())\n\n    # compare with east money manually\n    diff = factor.factor_df[\"diff\"]\n    dea = factor.factor_df[\"dea\"]\n    macd = factor.factor_df[\"macd\"]\n\n    assert round(diff.loc[(\"stock_sz_000338\", \"2019-06-10\")], 2) == -0.14\n    assert round(dea.loc[(\"stock_sz_000338\", \"2019-06-10\")], 2) == -0.15\n    assert round(macd.loc[(\"stock_sz_000338\", \"2019-06-10\")], 2) == 0.02\n\n    factor.move_on(to_timestamp=\"2019-06-17\")\n    diff = factor.factor_df[\"diff\"]\n    dea = factor.factor_df[\"dea\"]\n    macd = factor.factor_df[\"macd\"]\n\n    assert round(diff.loc[(\"stock_sz_000338\", \"2019-06-17\")], 2) == 0.06\n    assert round(dea.loc[(\"stock_sz_000338\", \"2019-06-17\")], 2) == -0.03\n    assert round(macd.loc[(\"stock_sz_000338\", \"2019-06-17\")], 2) <= 0.19\n\n\ndef test_cross_ma():\n    factor = CrossMaFactor(\n        codes=[\"000338\"],\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2019-06-10\",\n        level=IntervalLevel.LEVEL_1DAY,\n        provider=\"joinquant\",\n        windows=[5, 10],\n        adjust_type=\"qfq\",\n    )\n    print(factor.factor_df.tail())\n    print(factor.result_df.tail())\n\n    score = factor.result_df[\"filter_result\"]\n\n    assert score[(\"stock_sz_000338\", \"2019-06-03\")] == True\n    assert score[(\"stock_sz_000338\", \"2019-06-04\")] == True\n    assert (\"stock_sz_000338\", \"2019-06-05\") not in score or score[(\"stock_sz_000338\", \"2019-06-05\")] == False\n    assert (\"stock_sz_000338\", \"2019-06-06\") not in score or score[(\"stock_sz_000338\", \"2019-06-06\")] == False\n    assert (\"stock_sz_000338\", \"2019-06-10\") not in score or score[(\"stock_sz_000338\", \"2019-06-10\")] == False\n\n    factor.move_on()\n    score = factor.result_df[\"filter_result\"]\n    assert score[(\"stock_sz_000338\", \"2019-06-17\")] == True\n"
  },
  {
    "path": "tests/factors/test_transformers.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.kdata import get_kdata\nfrom zvt.factors.algorithm import MaTransformer, MacdTransformer\n\n\ndef test_ma_transformer():\n    df = get_kdata(\n        entity_id=\"stock_sz_000338\",\n        start_timestamp=\"2019-01-01\",\n        provider=\"joinquant\",\n        index=[\"entity_id\", \"timestamp\"],\n    )\n\n    t = MaTransformer(windows=[5, 10])\n\n    result_df = t.transform(df)\n\n    print(result_df)\n\n\ndef test_MacdTransformer():\n    df = get_kdata(\n        entity_id=\"stock_sz_000338\",\n        start_timestamp=\"2019-01-01\",\n        provider=\"joinquant\",\n        index=[\"entity_id\", \"timestamp\"],\n    )\n\n    t = MacdTransformer()\n\n    result_df = t.transform(df)\n\n    print(result_df)\n"
  },
  {
    "path": "tests/ml/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/ml/test_sgd.py",
    "content": "# -*- coding: utf-8 -*-\nfrom sklearn.linear_model import SGDClassifier, SGDRegressor\nfrom sklearn.pipeline import make_pipeline\nfrom sklearn.preprocessing import StandardScaler\n\nfrom zvt.contract import AdjustType\nfrom zvt.ml import MaStockMLMachine\n\nstart_timestamp = \"2015-01-01\"\nend_timestamp = \"2019-01-01\"\npredict_start_timestamp = \"2018-06-01\"\n\n\ndef test_sgd_classification():\n    machine = MaStockMLMachine(\n        data_provider=\"joinquant\",\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        predict_start_timestamp=predict_start_timestamp,\n        entity_ids=[\"stock_sz_000001\"],\n        label_method=\"behavior_cls\",\n        adjust_type=AdjustType.qfq,\n    )\n    clf = make_pipeline(StandardScaler(), SGDClassifier(max_iter=1000, tol=1e-3))\n    machine.train(model=clf)\n    machine.predict()\n    machine.draw_result(entity_id=\"stock_sz_000001\")\n\n\ndef test_sgd_regressor():\n    machine = MaStockMLMachine(\n        data_provider=\"joinquant\",\n        start_timestamp=start_timestamp,\n        end_timestamp=end_timestamp,\n        predict_start_timestamp=predict_start_timestamp,\n        entity_ids=[\"stock_sz_000001\"],\n        label_method=\"raw\",\n        adjust_type=AdjustType.qfq,\n    )\n    reg = make_pipeline(StandardScaler(), SGDRegressor(max_iter=1000, tol=1e-3))\n    machine.train(model=reg)\n    machine.predict()\n    machine.draw_result(entity_id=\"stock_sz_000001\")\n"
  },
  {
    "path": "tests/recorders/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/recorders/common/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/recorders/common/test_china_stock_list_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom ...context import init_test_context\n\ninit_test_context()\n\nfrom zvt.recorders.eastmoney import EastmoneyStockRecorder\n\n\ndef test_china_stock_recorder():\n    recorder = EastmoneyStockRecorder()\n\n    try:\n        recorder.run()\n    except:\n        assert False\n"
  },
  {
    "path": "tests/recorders/eastmoney/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/recorders/eastmoney/test_dividend_financing_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain import DividendDetail, RightsIssueDetail, SpoDetail, DividendFinancing\nfrom ...context import init_test_context\n\ninit_test_context()\n\nfrom zvt.consts import SAMPLE_STOCK_CODES\n\n\ndef test_dividend_detail():\n    try:\n        DividendDetail.record_data(provider=\"eastmoney\", codes=SAMPLE_STOCK_CODES)\n    except:\n        assert False\n\n\ndef test_rights_issue_detail():\n    try:\n        RightsIssueDetail.record_data(provider=\"eastmoney\", codes=SAMPLE_STOCK_CODES)\n    except:\n        assert False\n\n\ndef test_spo_detail():\n    try:\n        SpoDetail.record_data(provider=\"eastmoney\", codes=SAMPLE_STOCK_CODES)\n    except:\n        assert False\n\n\ndef test_dividend_financing():\n    try:\n        DividendFinancing.record_data(provider=\"eastmoney\", codes=SAMPLE_STOCK_CODES)\n    except:\n        assert False\n"
  },
  {
    "path": "tests/recorders/eastmoney/test_finance.py",
    "content": "# -*- coding: utf-8 -*-\nfrom ...context import init_test_context\n\ninit_test_context()\n\nfrom zvt.consts import SAMPLE_STOCK_CODES\n\nfrom zvt.recorders.eastmoney.finance.eastmoney_finance_factor_recorder import ChinaStockFinanceFactorRecorder\nfrom zvt.recorders.eastmoney.finance.eastmoney_cash_flow_recorder import ChinaStockCashFlowRecorder\nfrom zvt.recorders.eastmoney.finance.eastmoney_balance_sheet_recorder import ChinaStockBalanceSheetRecorder\nfrom zvt.recorders.eastmoney.finance.eastmoney_income_statement_recorder import ChinaStockIncomeStatementRecorder\n\n\ndef test_finance_factor_recorder():\n    recorder = ChinaStockFinanceFactorRecorder(codes=SAMPLE_STOCK_CODES)\n    try:\n        recorder.run()\n    except:\n        assert False\n\n\ndef test_cash_flow_recorder():\n    recorder = ChinaStockCashFlowRecorder(codes=SAMPLE_STOCK_CODES)\n    try:\n        recorder.run()\n    except:\n        assert False\n\n\ndef test_balance_sheet_recorder():\n    recorder = ChinaStockBalanceSheetRecorder(codes=SAMPLE_STOCK_CODES)\n    try:\n        recorder.run()\n    except:\n        assert False\n\n\ndef test_income_statement_recorder():\n    recorder = ChinaStockIncomeStatementRecorder(codes=SAMPLE_STOCK_CODES)\n    try:\n        recorder.run()\n    except:\n        assert False\n"
  },
  {
    "path": "tests/recorders/eastmoney/test_holder_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom ...context import init_test_context\n\ninit_test_context()\n\nfrom zvt.consts import SAMPLE_STOCK_CODES\n\nfrom zvt.recorders.eastmoney.holder.eastmoney_top_ten_holder_recorder import TopTenHolderRecorder\nfrom zvt.recorders.eastmoney.holder.eastmoney_top_ten_tradable_holder_recorder import TopTenTradableHolderRecorder\n\n\ndef test_top_ten_holder_recorder():\n    recorder = TopTenHolderRecorder(codes=SAMPLE_STOCK_CODES)\n    try:\n        recorder.run()\n    except:\n        assert False\n\n\ndef test_top_ten_tradable_holder_recorder():\n    recorder = TopTenTradableHolderRecorder(codes=SAMPLE_STOCK_CODES)\n    try:\n        recorder.run()\n    except:\n        assert False\n"
  },
  {
    "path": "tests/recorders/eastmoney/test_meta_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom ...context import init_test_context\n\ninit_test_context()\n\nfrom zvt.recorders.eastmoney.meta.eastmoney_stock_meta_recorder import EastmoneyStockDetailRecorder\n\nfrom zvt.consts import SAMPLE_STOCK_CODES\n\n\ndef test_meta_recorder():\n    recorder = EastmoneyStockDetailRecorder(codes=SAMPLE_STOCK_CODES)\n    try:\n        recorder.run()\n    except:\n        assert False\n"
  },
  {
    "path": "tests/recorders/eastmoney/test_trading_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom ...context import init_test_context\n\ninit_test_context()\n\nfrom zvt.consts import SAMPLE_STOCK_CODES\n\nfrom zvt.recorders.eastmoney.trading.eastmoney_manager_trading_recorder import ManagerTradingRecorder\nfrom zvt.recorders.eastmoney.trading.eastmoney_holder_trading_recorder import HolderTradingRecorder\n\n\ndef test_manager_trading_recorder():\n    recorder = ManagerTradingRecorder(codes=SAMPLE_STOCK_CODES)\n    try:\n        recorder.run()\n    except:\n        assert False\n\n\ndef test_holder_trading_recorder():\n    recorder = HolderTradingRecorder(codes=SAMPLE_STOCK_CODES)\n    try:\n        recorder.run()\n    except:\n        assert False\n"
  },
  {
    "path": "tests/recorders/em/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/recorders/em/test_em_api.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.recorders.em import em_api\n\nimport requests\n\n\ndef test_get_kdata():\n    # 上证A股\n    session = requests.Session()\n    df = em_api.get_kdata(\n        session=session,\n        entity_id=\"stock_sh_601318\",\n        level=IntervalLevel.LEVEL_1DAY,\n        adjust_type=AdjustType.qfq,\n        limit=5,\n    )\n    print(df)\n    df = em_api.get_kdata(\n        session=session,\n        entity_id=\"stock_sh_601318\",\n        level=IntervalLevel.LEVEL_1DAY,\n        adjust_type=AdjustType.hfq,\n        limit=5,\n    )\n    print(df)\n    df = em_api.get_kdata(\n        session=session,\n        entity_id=\"stock_sh_601318\",\n        level=IntervalLevel.LEVEL_1DAY,\n        adjust_type=AdjustType.bfq,\n        limit=5,\n    )\n    print(df)\n    # 深圳A股\n    df = em_api.get_kdata(\n        session=session,\n        entity_id=\"stock_sz_000338\",\n        level=IntervalLevel.LEVEL_1DAY,\n        adjust_type=AdjustType.qfq,\n        limit=5,\n    )\n    print(df)\n    df = em_api.get_kdata(\n        session=session,\n        entity_id=\"stock_sz_000338\",\n        level=IntervalLevel.LEVEL_1DAY,\n        adjust_type=AdjustType.hfq,\n        limit=5,\n    )\n    print(df)\n    df = em_api.get_kdata(\n        session=session,\n        entity_id=\"stock_sz_000338\",\n        level=IntervalLevel.LEVEL_1DAY,\n        adjust_type=AdjustType.bfq,\n        limit=5,\n    )\n    print(df)\n"
  },
  {
    "path": "tests/recorders/em/test_kdata_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/recorders/joinquant/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/recorders/joinquant/test_quote_recorder.py",
    "content": "# -*- coding: utf-8 -*-\nfrom ...context import init_test_context\n\ninit_test_context()\n\nfrom zvt.contract import IntervalLevel\n\nfrom zvt.consts import SAMPLE_STOCK_CODES\nfrom zvt.recorders.joinquant.quotes.jq_stock_kdata_recorder import JqChinaStockKdataRecorder\n\n\ndef test_1wk_kdata_recorder():\n    recorder = JqChinaStockKdataRecorder(\n        codes=SAMPLE_STOCK_CODES, sleeping_time=0, level=IntervalLevel.LEVEL_1WEEK, real_time=False\n    )\n    try:\n        recorder.run()\n    except:\n        assert False\n\n\ndef test_1mon_kdata_recorder():\n    recorder = JqChinaStockKdataRecorder(\n        codes=SAMPLE_STOCK_CODES, sleeping_time=0, level=IntervalLevel.LEVEL_1MON, real_time=False\n    )\n    try:\n        recorder.run()\n    except:\n        assert False\n\n\ndef test_1d_kdata_recorder():\n    recorder = JqChinaStockKdataRecorder(\n        codes=SAMPLE_STOCK_CODES, sleeping_time=0, level=IntervalLevel.LEVEL_1DAY, real_time=False\n    )\n    try:\n        recorder.run()\n    except:\n        assert False\n\n\ndef test_1d_hfq_kdata_recorder():\n    recorder = JqChinaStockKdataRecorder(\n        codes=[\"000338\"], sleeping_time=0, level=IntervalLevel.LEVEL_1DAY, real_time=False, adjust_type=\"hfq\"\n    )\n    try:\n        recorder.run()\n    except:\n        assert False\n\n\ndef test_1h_kdata_recorder():\n    recorder = JqChinaStockKdataRecorder(\n        codes=[\"000338\"],\n        sleeping_time=0,\n        level=IntervalLevel.LEVEL_1HOUR,\n        real_time=False,\n        start_timestamp=\"2019-01-01\",\n    )\n    try:\n        recorder.run()\n    except:\n        assert False\n\n\ndef test_5m_kdata_recorder():\n    recorder = JqChinaStockKdataRecorder(\n        codes=[\"000338\"], sleeping_time=0, level=IntervalLevel.LEVEL_5MIN, real_time=False, start_timestamp=\"2019-01-01\"\n    )\n    try:\n        recorder.run()\n    except:\n        assert False\n"
  },
  {
    "path": "tests/recorders/joinquant/test_stock_money_flow.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.domain import StockMoneyFlow, Stock\n\n\ndef test_stock_money_flow():\n    provider = \"joinquant\"\n    # Stock.record_data(provider=provider)\n    StockMoneyFlow.record_data(\n        codes=[\"300999\", \"688981\"], provider=provider, start_timestamp=\"2020-12-14\", compute_index_money_flow=False\n    )\n\n    data_samples = [\n        {\n            \"id\": \"stock_sz_300999_2020-12-14\",\n            \"timestamp\": \"2020-12-14\",\n            \"code\": \"300999\",\n            \"net_main_inflows\": 46378.96 * 10000,\n            \"net_main_inflow_rate\": 9.3 / 100,\n            \"net_huge_inflows\": 50111.54 * 10000,\n            \"net_huge_inflow_rate\": 10.04 / 100,\n            \"net_big_inflows\": -3732.58 * 10000,\n            \"net_big_inflow_rate\": -0.75 / 100,\n            \"net_medium_inflows\": -23493.71 * 10000,\n            \"net_medium_inflow_rate\": -4.71 / 100,\n            \"net_small_inflows\": -22885.25 * 10000,\n            \"net_small_inflow_rate\": -4.59 / 100,\n        },\n        {\n            \"id\": \"stock_sh_688981_2020-12-14\",\n            \"timestamp\": \"2020-12-14\",\n            \"code\": \"688981\",\n            \"net_main_inflows\": -14523.55 * 10000,\n            \"net_main_inflow_rate\": -10.77 / 100,\n            \"net_huge_inflows\": -17053.72 * 10000,\n            \"net_huge_inflow_rate\": -12.65 / 100,\n            \"net_big_inflows\": 2530.17 * 10000,\n            \"net_big_inflow_rate\": 1.88 / 100,\n            \"net_medium_inflows\": 6945.23 * 10000,\n            \"net_medium_inflow_rate\": 5.15 / 100,\n            \"net_small_inflows\": 7578.32 * 10000,\n            \"net_small_inflow_rate\": 5.62 / 100,\n        },\n    ]\n    StockMoneyFlow.test_data_correctness(provider=provider, data_samples=data_samples)\n"
  },
  {
    "path": "tests/recorders/sina/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/recorders/sina/test_money_flow_recorder.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/test_generator.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.autocode.generator import _remove_start_end\n\n\ndef test_remove_start_end():\n    cls = _remove_start_end(\"class A(object)\", \"class \", \"(\")\n    assert cls == \"A\"\n\n    func = _remove_start_end(\"def aaa(arg1, arg2)\", \"def \", \"(\")\n    assert func == \"aaa\"\n\n    var = _remove_start_end(\"zvt_env = \", \"\", \" =\")\n    assert var == \"zvt_env\"\n"
  },
  {
    "path": "tests/trader/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/trader/test_trader.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.api.kdata import get_kdata\nfrom zvt.contract import IntervalLevel, AdjustType\nfrom zvt.samples import MyBullTrader, StockTrader\nfrom zvt.utils.time_utils import is_same_date\n\nbuy_timestamp = \"2019-05-29\"\nsell_timestamp = \"2020-01-06\"\n\n\nclass SingleTrader(StockTrader):\n    def on_time(self, timestamp):\n        if is_same_date(buy_timestamp, timestamp):\n            self.buy(timestamp=buy_timestamp, entity_ids=[\"stock_sz_000338\"])\n        if is_same_date(sell_timestamp, timestamp):\n            self.sell(timestamp=sell_timestamp, entity_ids=[\"stock_sz_000338\"])\n\n    def long_position_control(self):\n        return 1\n\n\ndef test_single_trader():\n    trader = SingleTrader(\n        provider=\"joinquant\",\n        codes=[\"000338\"],\n        level=IntervalLevel.LEVEL_1DAY,\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2020-01-10\",\n        trader_name=\"000338_single_trader\",\n        draw_result=True,\n    )\n    trader.run()\n\n    positions = trader.get_current_account().positions\n    print(positions)\n\n    account = trader.get_current_account()\n\n    print(account)\n\n    buy_price = get_kdata(\n        provider=\"joinquant\",\n        entity_id=\"stock_sz_000338\",\n        start_timestamp=buy_timestamp,\n        end_timestamp=buy_timestamp,\n        return_type=\"domain\",\n    )[0]\n    sell_price = get_kdata(\n        provider=\"joinquant\",\n        entity_id=\"stock_sz_000338\",\n        start_timestamp=sell_timestamp,\n        end_timestamp=sell_timestamp,\n        return_type=\"domain\",\n    )[0]\n\n    sell_lost = trader.account_service.slippage + trader.account_service.sell_cost\n    buy_lost = trader.account_service.slippage + trader.account_service.buy_cost\n    pct = (sell_price.close * (1 - sell_lost) - buy_price.close * (1 + buy_lost)) / buy_price.close * (1 + buy_lost)\n\n    profit_rate = (account.all_value - account.input_money) / account.input_money\n\n    assert round(profit_rate, 2) == round(pct, 2)\n\n\nclass MultipleTrader(StockTrader):\n    has_buy = False\n\n    def on_time(self, timestamp):\n        if is_same_date(buy_timestamp, timestamp):\n            self.buy(timestamp=timestamp, entity_ids=[\"stock_sz_000338\"])\n            self.has_buy = True\n            self.buy(timestamp=timestamp, entity_ids=[\"stock_sh_601318\"])\n        if is_same_date(sell_timestamp, timestamp):\n            self.sell(\n                timestamp=timestamp,\n                entity_ids=[\"stock_sz_000338\", \"stock_sh_601318\"],\n            )\n\n    def long_position_control(self):\n        if self.has_buy:\n            position_pct = 1.0\n        else:\n            position_pct = 0.5\n\n        return position_pct\n\n\ndef test_multiple_trader():\n    trader = MultipleTrader(\n        provider=\"joinquant\",\n        codes=[\"000338\", \"601318\"],\n        level=IntervalLevel.LEVEL_1DAY,\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2020-01-10\",\n        trader_name=\"multiple_trader\",\n        draw_result=False,\n        adjust_type=AdjustType.qfq,\n    )\n    trader.run()\n\n    positions = trader.get_current_account().positions\n    print(positions)\n\n    account = trader.get_current_account()\n\n    print(account)\n\n    # 000338\n    buy_price = get_kdata(\n        provider=\"joinquant\",\n        entity_id=\"stock_sz_000338\",\n        start_timestamp=buy_timestamp,\n        end_timestamp=buy_timestamp,\n        return_type=\"domain\",\n    )[0]\n    sell_price = get_kdata(\n        provider=\"joinquant\",\n        entity_id=\"stock_sz_000338\",\n        start_timestamp=sell_timestamp,\n        end_timestamp=sell_timestamp,\n        return_type=\"domain\",\n    )[0]\n\n    sell_lost = trader.account_service.slippage + trader.account_service.sell_cost\n    buy_lost = trader.account_service.slippage + trader.account_service.buy_cost\n    pct1 = (sell_price.close * (1 - sell_lost) - buy_price.close * (1 + buy_lost)) / buy_price.close * (1 + buy_lost)\n\n    # 601318\n    buy_price = get_kdata(\n        provider=\"joinquant\",\n        entity_id=\"stock_sh_601318\",\n        start_timestamp=buy_timestamp,\n        end_timestamp=buy_timestamp,\n        return_type=\"domain\",\n    )[0]\n    sell_price = get_kdata(\n        provider=\"joinquant\",\n        entity_id=\"stock_sh_601318\",\n        start_timestamp=sell_timestamp,\n        end_timestamp=sell_timestamp,\n        return_type=\"domain\",\n    )[0]\n\n    pct2 = (sell_price.close * (1 - sell_lost) - buy_price.close * (1 + buy_lost)) / buy_price.close * (1 + buy_lost)\n\n    profit_rate = (account.all_value - account.input_money) / account.input_money\n\n    assert profit_rate - (pct1 + pct2) / 2 <= 0.2\n\n\ndef test_basic_trader():\n    try:\n        MyBullTrader(\n            provider=\"joinquant\",\n            codes=[\"000338\"],\n            level=IntervalLevel.LEVEL_1DAY,\n            start_timestamp=\"2018-01-01\",\n            end_timestamp=\"2019-06-30\",\n            trader_name=\"000338_bull_trader\",\n            draw_result=False,\n        ).run()\n    except:\n        assert False\n"
  },
  {
    "path": "tests/utils/__init__.py",
    "content": "# -*- coding: utf-8 -*-\n"
  },
  {
    "path": "tests/utils/test_pd_utils.py",
    "content": "# -*- coding: utf-8 -*-\nimport pandas as pd\n\nfrom zvt.utils.pd_utils import drop_continue_duplicate\n\n\ndef test_drop_continue_duplicate():\n    data1 = [1, 2, 2, 3, 4, 4, 5]\n    s = pd.Series(data=data1)\n    s1 = drop_continue_duplicate(s=s)\n    assert s1.tolist() == [1, 2, 3, 4, 5]\n\n    data2 = [1, 2, 2, 2, 4, 4, 5]\n\n    df = pd.DataFrame(data={\"A\": data1, \"B\": data2})\n    print(df)\n    df1 = drop_continue_duplicate(s=df, col=\"A\")\n    assert df1[\"A\"].tolist() == [1, 2, 3, 4, 5]\n\n    df2 = drop_continue_duplicate(s=df, col=\"B\")\n    assert df2[\"A\"].tolist() == [1, 2, 4, 5]\n"
  },
  {
    "path": "tests/utils/test_time_utils.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.contract import IntervalLevel\nfrom zvt.contract.utils import evaluate_size_from_timestamp, next_timestamp_on_level, is_finished_kdata_timestamp\nfrom zvt.utils.time_utils import (\n    to_pd_timestamp,\n    split_time_interval,\n    is_same_date,\n    month_start_end_ranges,\n    count_interval,\n)\n\n\ndef test_evaluate_size_from_timestamp():\n    size = evaluate_size_from_timestamp(\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2019-01-02\",\n        level=IntervalLevel.LEVEL_1MON,\n        one_day_trading_minutes=4 * 60,\n    )\n\n    assert size == 2\n\n    size = evaluate_size_from_timestamp(\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2019-01-02\",\n        level=IntervalLevel.LEVEL_1WEEK,\n        one_day_trading_minutes=4 * 60,\n    )\n\n    assert size == 2\n\n    size = evaluate_size_from_timestamp(\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2019-01-02\",\n        level=IntervalLevel.LEVEL_1DAY,\n        one_day_trading_minutes=4 * 60,\n    )\n\n    assert size == 2\n\n    size = evaluate_size_from_timestamp(\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2019-01-02\",\n        level=IntervalLevel.LEVEL_1HOUR,\n        one_day_trading_minutes=4 * 60,\n    )\n\n    assert size == 9\n\n    size = evaluate_size_from_timestamp(\n        start_timestamp=\"2019-01-01\",\n        end_timestamp=\"2019-01-02\",\n        level=IntervalLevel.LEVEL_1MIN,\n        one_day_trading_minutes=4 * 60,\n    )\n\n    assert size == 481\n\n\ndef test_next_timestamp():\n    current = \"2019-01-10 13:15\"\n    assert next_timestamp_on_level(current, level=IntervalLevel.LEVEL_1MIN) == to_pd_timestamp(\"2019-01-10 13:16\")\n    assert next_timestamp_on_level(current, level=IntervalLevel.LEVEL_5MIN) == to_pd_timestamp(\"2019-01-10 13:20\")\n    assert next_timestamp_on_level(current, level=IntervalLevel.LEVEL_15MIN) == to_pd_timestamp(\"2019-01-10 13:30\")\n\n\ndef test_is_finished_kdata_timestamp():\n    timestamp = \"2019-01-10 13:05\"\n    assert not is_finished_kdata_timestamp(timestamp, level=IntervalLevel.LEVEL_1DAY)\n    assert not is_finished_kdata_timestamp(timestamp, level=IntervalLevel.LEVEL_1HOUR)\n    assert not is_finished_kdata_timestamp(timestamp, level=IntervalLevel.LEVEL_30MIN)\n    assert not is_finished_kdata_timestamp(timestamp, level=IntervalLevel.LEVEL_15MIN)\n    assert is_finished_kdata_timestamp(timestamp, level=IntervalLevel.LEVEL_5MIN)\n    assert is_finished_kdata_timestamp(timestamp, level=IntervalLevel.LEVEL_1MIN)\n\n    timestamp = \"2019-01-10\"\n    assert is_finished_kdata_timestamp(timestamp, level=IntervalLevel.LEVEL_1DAY)\n\n\ndef test_split_time_interval():\n    first = None\n    last = None\n    start = \"2020-01-01\"\n    end = \"2021-01-01\"\n    for interval in split_time_interval(start, end, interval=30):\n        if first is None:\n            first = interval\n        last = interval\n\n    print(first)\n    print(last)\n\n    assert is_same_date(first[0], start)\n    assert is_same_date(first[-1], \"2020-01-31\")\n\n    assert is_same_date(last[-1], end)\n\n\ndef test_split_time_interval_month():\n    first = None\n    last = None\n    start = \"2020-01-01\"\n    end = \"2021-01-01\"\n    for interval in split_time_interval(start, end, method=\"month\"):\n        if first is None:\n            first = interval\n        last = interval\n\n    print(first)\n    print(last)\n\n    assert is_same_date(first[0], start)\n    assert is_same_date(first[-1], \"2020-01-31\")\n\n    assert is_same_date(last[0], \"2021-01-01\")\n    assert is_same_date(last[-1], \"2021-01-01\")\n\n\ndef test_month_start_end_range():\n    start = \"2020-01-01\"\n    end = \"2021-01-01\"\n    ranges = month_start_end_ranges(start_date=start, end_date=end)\n    print(ranges)\n    assert is_same_date(ranges[0][0], \"2020-01-01\")\n    assert is_same_date(ranges[0][1], \"2020-01-31\")\n\n    assert is_same_date(ranges[-1][0], \"2020-12-01\")\n    assert is_same_date(ranges[-1][1], \"2020-12-31\")\n\n    start = \"2020-01-01\"\n    end = \"2021-01-31\"\n    ranges = month_start_end_ranges(start_date=start, end_date=end)\n    print(ranges)\n    assert is_same_date(ranges[0][0], \"2020-01-01\")\n    assert is_same_date(ranges[0][1], \"2020-01-31\")\n\n    assert is_same_date(ranges[-1][0], \"2021-01-01\")\n    assert is_same_date(ranges[-1][1], \"2021-01-31\")\n\n\ndef test_count_interval():\n    start = \"2020-01-01\"\n    end = \"2021-01-01\"\n    print(count_interval(start_date=start, end_date=end))\n"
  },
  {
    "path": "tests/utils/test_utils.py",
    "content": "# -*- coding: utf-8 -*-\nfrom zvt.contract.api import get_entities\nfrom zvt.utils.utils import iterate_with_step, to_str, float_to_pct\n\n\ndef test_iterate_with_step():\n    data = range(1000)\n    first = None\n    last = None\n    for sub_data in iterate_with_step(data):\n        if not first:\n            first = sub_data\n        last = sub_data\n\n    assert first[0] == 0\n    assert first[-1] == 99\n\n    assert last[0] == 900\n    assert last[-1] == 999\n\n\ndef test_iterate_entities():\n    data = get_entities(entity_type=\"stock\")\n    first = None\n    last = None\n    for sub_data in iterate_with_step(data):\n        if first is None:\n            first = sub_data\n        last = sub_data\n\n    assert len(first) == 100\n    assert len(last) <= 100\n\n\ndef test_to_str():\n    assert to_str(None) is None\n    assert to_str(\"\") is None\n    assert to_str(\"a\") == \"a\"\n    assert to_str([\"a\", \"b\"]) == \"a;b\"\n    assert to_str([1, 2]) == \"1;2\"\n\n\ndef test_float_to_pct():\n    assert float_to_pct(0.1) == \"10.00%\"\n    assert float_to_pct(0.111) == \"11.10%\"\n    assert float_to_pct(0.8) == \"80.00%\"\n    assert float_to_pct(0.555) == \"55.50%\"\n    assert float_to_pct(0.33333) == \"33.33%\"\n"
  }
]