├── .github └── workflows │ └── release.yml ├── .gitignore ├── .readthedocs.yaml ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── cut1.png ├── cut2.png ├── docs ├── index.md └── requirements.txt ├── mkdocs.yml ├── msg.png ├── output.png ├── pyproject.toml ├── qrcode.png ├── tdxtrader ├── __init__.py ├── anis.py ├── file.py ├── index.py ├── logger.py ├── order.py ├── trader.py └── utils.py └── wxbot.png /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Python package 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | Test: 10 | runs-on: windows-latest 11 | permissions: write-all 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 # Ensure full history for semantic-release 18 | 19 | - name: Set up Python 3.10 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: '3.10' # You can specify your required Python version here 23 | 24 | - name: Install Flit 25 | run: | 26 | pip install flit 27 | shell: bash 28 | 29 | - name: Check release status 30 | id: release-status 31 | run: | 32 | pip install python-semantic-release 33 | if semantic-release --noop --strict version; then 34 | echo "::set-output name=released::true" 35 | else 36 | echo "::set-output name=released::false" 37 | fi 38 | shell: bash 39 | env: 40 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | 42 | - name: Semantic Release Version 43 | if: steps.release-status.outputs.released == 'true' 44 | run: | 45 | semantic-release version 46 | shell: bash 47 | env: 48 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} 50 | 51 | - name: Release to PyPI 52 | if: steps.release-status.outputs.released == 'true' 53 | run: | 54 | flit build 55 | flit publish --repository pypi 56 | shell: bash 57 | env: 58 | FLIT_USERNAME: __token__ 59 | FLIT_PASSWORD: ${{ secrets.PYPI_TOKEN }} 60 | 61 | - name: Release to GitHub 62 | if: steps.release-status.outputs.released == 'true' 63 | run: | 64 | git fetch --tags 65 | for file in ./dist/*; do 66 | gh release upload "${{ steps.release-status.outputs.tag }}" $file 67 | done 68 | shell: bash 69 | env: 70 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 110 | .pdm.toml 111 | .pdm-python 112 | .pdm-build/ 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | #.idea/ 163 | .pypirc 164 | test.ipynb -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-24.04 5 | tools: 6 | python: "3" 7 | 8 | python: 9 | install: 10 | - requirements: docs/requirements.txt 11 | 12 | mkdocs: 13 | configuration: mkdocs.yml -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 4 3 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | 4 | ## v0.4.5 (2025-02-08) 5 | 6 | ### Bug Fixes 7 | 8 | - 修改文档错误 9 | ([`a288e6b`](https://github.com/zsrl/tdxtrader/commit/a288e6b2aa714cf1d80de34d4de37adcda18ba5e)) 10 | 11 | 12 | ## v0.4.4 (2025-02-08) 13 | 14 | ### Performance Improvements 15 | 16 | - 买入卖出信号支持多个 17 | ([`d0cefc2`](https://github.com/zsrl/tdxtrader/commit/d0cefc2e80d5e098ff8a330fe293ee63f89358d9)) 18 | 19 | 20 | ## v0.4.3 (2025-01-24) 21 | 22 | ### Bug Fixes 23 | 24 | - 解决读取文件编码错误 25 | ([`bdf8baf`](https://github.com/zsrl/tdxtrader/commit/bdf8baf15f93d8fc6e33556020d3f3d1c16361c9)) 26 | 27 | 28 | ## v0.4.2 (2024-12-19) 29 | 30 | ### Bug Fixes 31 | 32 | - 修改a为a 33 | ([`85aac2d`](https://github.com/zsrl/tdxtrader/commit/85aac2d5b8964cf5cc0fde9417a6884803149872)) 34 | 35 | ### Performance Improvements 36 | 37 | - 测试文档站 38 | ([`71d483a`](https://github.com/zsrl/tdxtrader/commit/71d483af308da97641e746280dca947f3898154d)) 39 | 40 | 41 | ## v0.4.1 (2024-12-17) 42 | 43 | ### Performance Improvements 44 | 45 | - 文档站初始化 46 | ([`b07df14`](https://github.com/zsrl/tdxtrader/commit/b07df143b25cf7016f10d4871b8cf153846f8dea)) 47 | 48 | 49 | ## v0.4.0 (2024-12-16) 50 | 51 | ### Features 52 | 53 | - 更改文件读取逻辑,从源头解决错误行的问题 54 | ([`64da527`](https://github.com/zsrl/tdxtrader/commit/64da527d006a7d6dcbd4b50d0596a3337649cd0e)) 55 | 56 | ### Performance Improvements 57 | 58 | - 优化买入卖出事件入参 59 | ([`ed681d1`](https://github.com/zsrl/tdxtrader/commit/ed681d103e0d6e034a85432e1bc8d823d5feffed)) 60 | 61 | 62 | ## v0.3.10 (2024-12-12) 63 | 64 | ### Bug Fixes 65 | 66 | - 修改文件编码为gbk 67 | ([`f603429`](https://github.com/zsrl/tdxtrader/commit/f60342951b5f37e89b8ad13c0b8da26ad505c1d0)) 68 | 69 | 70 | ## v0.3.9 (2024-12-12) 71 | 72 | ### Bug Fixes 73 | 74 | - 删除print 75 | ([`fb47cdc`](https://github.com/zsrl/tdxtrader/commit/fb47cdc576cb47d6e7a80065354246f1134ec0e0)) 76 | 77 | - 启动时增加预置表头,解决预警文件首行报错的问题 78 | ([`f86efcc`](https://github.com/zsrl/tdxtrader/commit/f86efcc41b400b3f44ec1dff880288b7b72ed269)) 79 | 80 | 81 | ## v0.3.8 (2024-12-09) 82 | 83 | ### Bug Fixes 84 | 85 | - Qmt查询单持仓的api对etf有bug,已改为查询全部持仓 86 | ([`3d777b0`](https://github.com/zsrl/tdxtrader/commit/3d777b06e663d1fa5f91264b6da405900921e3d1)) 87 | 88 | 89 | ## v0.3.7 (2024-12-08) 90 | 91 | ### Performance Improvements 92 | 93 | - 买入事件增加持仓参数 94 | ([`4388927`](https://github.com/zsrl/tdxtrader/commit/4388927e9878e59320d7f385d080245a9c6e8d4b)) 95 | 96 | 97 | ## v0.3.6 (2024-12-07) 98 | 99 | ### Bug Fixes 100 | 101 | - 修复委托失败报错 102 | ([`504d0f9`](https://github.com/zsrl/tdxtrader/commit/504d0f926a200e369f662653938ff2df1793b64e)) 103 | 104 | ### Performance Improvements 105 | 106 | - 1. 日志报错只打印一次。2. 当买入或卖出时间返回None时不下单 107 | ([`ce18f4b`](https://github.com/zsrl/tdxtrader/commit/ce18f4b7797681affdaa6592ad11072c22a9c477)) 108 | 109 | 110 | ## v0.3.5 (2024-12-04) 111 | 112 | ### Bug Fixes 113 | 114 | - 删除不必要的输出 115 | ([`94be2f6`](https://github.com/zsrl/tdxtrader/commit/94be2f6fa349f3d183a5745d4c199277a2985b4e)) 116 | 117 | 118 | ## v0.3.4 (2024-12-04) 119 | 120 | ### Bug Fixes 121 | 122 | - 解决语法错误 123 | ([`7142fae`](https://github.com/zsrl/tdxtrader/commit/7142fae663de6f735e6642b2806e3ae1d2356feb)) 124 | 125 | 126 | ## v0.3.3 (2024-12-04) 127 | 128 | ### Bug Fixes 129 | 130 | - 处理南京港报错 131 | ([`c6a536f`](https://github.com/zsrl/tdxtrader/commit/c6a536f3e1455de7aef932fa2e240dd7fc232686)) 132 | 133 | 134 | ## v0.3.2 (2024-12-03) 135 | 136 | ### Bug Fixes 137 | 138 | - 图片打码 139 | ([`119d23d`](https://github.com/zsrl/tdxtrader/commit/119d23d3c1f8f62273b7148ef1802956a14319a7)) 140 | 141 | 142 | ## v0.3.1 (2024-12-03) 143 | 144 | ### Performance Improvements 145 | 146 | - 优化文档 147 | ([`980177d`](https://github.com/zsrl/tdxtrader/commit/980177d2aa4a411dba9cecd205610b086ad7081c)) 148 | 149 | 150 | ## v0.3.0 (2024-12-03) 151 | 152 | ### Features 153 | 154 | - 增加企业微信消息推送 155 | ([`4b5e3fb`](https://github.com/zsrl/tdxtrader/commit/4b5e3fbec16f7aa75692d7e04f6175c70913a349)) 156 | 157 | ### Performance Improvements 158 | 159 | - 优化日志 160 | ([`db14fc8`](https://github.com/zsrl/tdxtrader/commit/db14fc8596b9ac3972e0d01c034780e786f36b79)) 161 | 162 | 163 | ## v0.2.2 (2024-12-03) 164 | 165 | ### Performance Improvements 166 | 167 | - 优化测试 168 | ([`ffb1f3d`](https://github.com/zsrl/tdxtrader/commit/ffb1f3d66ef7d28de432408a8e3cff4d070c69be)) 169 | 170 | - 优化输出 171 | ([`ca26e4a`](https://github.com/zsrl/tdxtrader/commit/ca26e4a67d2d2d943711e227697492b9ec43240b)) 172 | 173 | - 增加按金额下单 174 | ([`e915b9f`](https://github.com/zsrl/tdxtrader/commit/e915b9fda4873551a61ec3490432794eae5cd470)) 175 | 176 | 177 | ## v0.2.1 (2024-12-01) 178 | 179 | ### Bug Fixes 180 | 181 | - 解决非交易日时间戳不对导致撤单判断有问题 182 | ([`14ff6ed`](https://github.com/zsrl/tdxtrader/commit/14ff6ed36aee49f73767041ecc8187278cfeeec3)) 183 | 184 | 185 | ## v0.2.0 (2024-11-28) 186 | 187 | ### Features 188 | 189 | - 增加自动撤单功能 190 | ([`62602fe`](https://github.com/zsrl/tdxtrader/commit/62602fe5c59a0b9d7c577e312fb339da12ff334e)) 191 | 192 | ### Performance Improvements 193 | 194 | - 监听可撤单的委托 195 | ([`1c3ae87`](https://github.com/zsrl/tdxtrader/commit/1c3ae874824906fad6a7a353c879d95ca09c7337)) 196 | 197 | 198 | ## v0.1.7 (2024-11-27) 199 | 200 | 201 | ## v0.1.6 (2024-11-27) 202 | 203 | ### Bug Fixes 204 | 205 | - 修改没有持仓时卖出报错 206 | ([`174e690`](https://github.com/zsrl/tdxtrader/commit/174e69010a9b3c8912714452e431945ff819b13f)) 207 | 208 | ### Performance Improvements 209 | 210 | - 优化异常捕获,下单报错不影响程序运行 211 | ([`06fb844`](https://github.com/zsrl/tdxtrader/commit/06fb8449ea50d5a92158befe03a8afec43396c68)) 212 | 213 | 214 | ## v0.1.5 (2024-11-27) 215 | 216 | ### Performance Improvements 217 | 218 | - 优化时间输出格式 219 | ([`fab0845`](https://github.com/zsrl/tdxtrader/commit/fab0845c1b1556eab41f42d02d721f33cdc82034)) 220 | 221 | 222 | ## v0.1.4 (2024-11-26) 223 | 224 | ### Bug Fixes 225 | 226 | - 修改 227 | ([`818e27b`](https://github.com/zsrl/tdxtrader/commit/818e27b1ec45014201503210eeaa7c7b403d4f90)) 228 | 229 | - 删除描述 230 | ([`99e0797`](https://github.com/zsrl/tdxtrader/commit/99e0797f1e3827e5266343df20c5265f78214894)) 231 | 232 | 233 | ## v0.1.3 (2024-11-26) 234 | 235 | ### Bug Fixes 236 | 237 | - 增加配置 238 | ([`403b6c1`](https://github.com/zsrl/tdxtrader/commit/403b6c1ebeaa4bdd60490aeab29a1ee5330535b2)) 239 | 240 | 241 | ## v0.1.2 (2024-11-26) 242 | 243 | ### Bug Fixes 244 | 245 | - 增加描述 246 | ([`2129ef0`](https://github.com/zsrl/tdxtrader/commit/2129ef061731a9e281398427f6d0a92ea5efc920)) 247 | 248 | 249 | ## v0.1.1 (2024-11-26) 250 | 251 | ### Bug Fixes 252 | 253 | - 调整版本号定义 254 | ([`cb0397c`](https://github.com/zsrl/tdxtrader/commit/cb0397c29cc8a69f3a9219aab06d6f210a427330)) 255 | 256 | ### Performance Improvements 257 | 258 | - 更新文档 259 | ([`73e6206`](https://github.com/zsrl/tdxtrader/commit/73e62065e1423fe173d5d731069a160f7c9be9a8)) 260 | 261 | 262 | ## v0.1.0 (2024-11-26) 263 | 264 | ### Bug Fixes 265 | 266 | - 删除多余的代码 267 | ([`3b901df`](https://github.com/zsrl/tdxtrader/commit/3b901dfeefcc098a1117dd2d4ed24c52025db95f)) 268 | 269 | - 发布修改 270 | ([`c5c0009`](https://github.com/zsrl/tdxtrader/commit/c5c0009de9638c61ab5be8ade130c6bd95a29a48)) 271 | 272 | - 解决农产品等带空格的股票报错 273 | ([`2eaabb6`](https://github.com/zsrl/tdxtrader/commit/2eaabb6338d5031dc9354e8eb18196d315ce9c9f)) 274 | 275 | ### Features 276 | 277 | - 完成初版 278 | ([`b10f618`](https://github.com/zsrl/tdxtrader/commit/b10f618dad3eace3343276647c35e61e8d7beae2)) 279 | 280 | ### Performance Improvements 281 | 282 | - 增加github actions 283 | ([`0289f6f`](https://github.com/zsrl/tdxtrader/commit/0289f6f9cad40d138d852d85ac3cf36e376d87a5)) 284 | 285 | - 增加说明 286 | ([`6909721`](https://github.com/zsrl/tdxtrader/commit/690972115231a23d1091e47ee692de9977189082)) 287 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Quant Helper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PyPI version](https://badge.fury.io/py/tdxtrader.svg)](https://badge.fury.io/py/tdxtrader) 2 | [![Downloads](https://static.pepy.tech/badge/tdxtrader/month)](https://pepy.tech/project/tdxtrader) 3 | 4 | # tdxtrader 5 | 6 | 通达信预警信号程序化交易 7 | 8 | > 声明:本项目仅用于学习和研究,不保证交易收益,不作为投资建议,风险自负,请充分使用QMT模拟盘测试。 9 | 10 | ## 欢迎加入知识星球 11 | 12 | ![知识星球](./qrcode.png) 13 | 14 | ## 运行效果 15 | 16 | ![效果](./output.png) 17 | 18 | ## 安装 19 | 20 | ```shell 21 | pip install tdxtrader 22 | ``` 23 | 24 | ## 预警指标设置 25 | 26 | 设置两个指标,一个作为买入信号,一个作为卖出信号 27 | 28 | ![预警指标](./cut2.png) 29 | 30 | ## 预警文件设置 31 | 32 | ![预警文件](./cut1.png) 33 | 34 | ## demo 35 | 36 | ### 基础示例 37 | 38 | ```python 39 | import tdxtrader 40 | # 参数 41 | account_id = 'xxxx' # 账号ID 42 | mini_qmt_path = r'D:\国金证券QMT交易端\userdata_mini' # mini_qmt 路径 43 | file_path = r'D:\new_tdx\sign.txt' # 预警文件路径 44 | interval = 1 # 轮询时间(秒) 45 | buy_sign = 'KDJ买入条件选股' # 买入信号 46 | sell_sign = 'KDJ卖出条件选股' # 卖出信号 47 | 48 | def buy_event(params): 49 | '''买入事件''' 50 | 51 | stock = params.get('stock') 52 | 53 | return { 54 | 'size': 200, 55 | 'price': -1, # 如果是限价,则设置价格 56 | 'type': '市价', # 市价,限价 57 | } 58 | 59 | def sell_event(params): 60 | '''卖出事件''' 61 | 62 | stock = params.get('stock') 63 | position = params.get('position') 64 | 65 | return { 66 | 'size': position.can_use_volume, # 卖全仓 67 | 'price': -1, # 如果是限价,则设置价格 68 | 'type': '市价' # 市价,限价 69 | } 70 | 71 | 72 | tdxtrader.start( 73 | account_id=account_id, 74 | mini_qmt_path=mini_qmt_path, 75 | file_path=file_path, 76 | interval=interval, 77 | buy_sign=buy_sign, 78 | sell_sign=sell_sign, 79 | buy_event=buy_event, 80 | sell_event=sell_event, 81 | cancel_after=10 # 10秒未成交则撤单 82 | ) 83 | ``` 84 | 85 | ### 限价委托(获取预警价格) 86 | 87 | stock对象中包含了当前股票的详细信息,可以通过price属性获取预警时的价格 88 | 89 | ```python 90 | def buy_event(params): 91 | '''买入事件''' 92 | 93 | stock = params.get('stock') 94 | 95 | return { 96 | 'size': 200, 97 | 'price': stock.get('price'), # 如果是市价,则设置-1 98 | 'type': '限价', # 市价,限价 99 | } 100 | 101 | def sell_event(params): 102 | '''卖出事件''' 103 | 104 | stock = params.get('stock') 105 | position = params.get('position') 106 | 107 | return { 108 | 'size': position.can_use_volume, # 卖全仓 109 | 'price': stock.get('price'), # 如果是市价,则设置-1 110 | 'type': '限价' # 市价,限价 111 | } 112 | ``` 113 | 114 | ### 按金额买卖 115 | 116 | ```python 117 | def buy_event(params): 118 | '''买入事件''' 119 | 120 | stock = params.get('stock') 121 | 122 | return { 123 | 'amount': 100000, 124 | 'price': stock.get('price'), # 如果是市价,则设置-1 125 | 'type': '限价', # 市价,限价 126 | } 127 | 128 | def sell_event(params): 129 | '''卖出事件''' 130 | 131 | stock = params.get('stock') 132 | 133 | return { 134 | 'amount': 100000, # 卖全仓 135 | 'price': stock.get('price'), # 如果是市价,则设置-1 136 | 'type': '限价' # 市价,限价 137 | } 138 | ``` 139 | 140 | ### 使用当前持仓判断是否买入 141 | 142 | ```python 143 | def buy_event(params): 144 | '''买入数量''' 145 | 146 | stock = params.get('stock') 147 | position = params.get('position') 148 | 149 | if position is None: 150 | return { 151 | 'amount': 100000, 152 | 'price': stock.get('price'), # 如果是市价,则设置-1 153 | 'type': '限价', # 市价,限价 154 | } 155 | else: 156 | return None 157 | 158 | def sell_event(params): 159 | '''卖出数量''' 160 | 161 | stock = params.get('stock') 162 | 163 | return { 164 | 'amount': 100000, # 卖全仓 165 | 'price': stock.get('price'), # 如果是限价,则设置价格 166 | 'type': '限价' # 市价,限价 167 | } 168 | ``` 169 | 170 | ### 按资金比例买入(卖出逻辑一致) 171 | 172 | ```python 173 | def buy_event(params): 174 | '''买入数量''' 175 | 176 | stock = params.get('stock') 177 | position = params.get('position') 178 | xt_trader = params.get('xt_trader') 179 | account = params.get('account') 180 | 181 | asset = xt_trader.query_stock_asset(account) 182 | 183 | if position is None: 184 | return { 185 | 'amount': asset.total_asset * 0.01, 186 | 'price': stock.get('price'), # 如果是市价,则设置-1 187 | 'type': '限价', # 市价,限价 188 | } 189 | else: 190 | return None 191 | ``` 192 | 193 | ### 多买入/卖出信号示例 194 | 195 | ```python 196 | import tdxtrader 197 | # 参数 198 | account_id = 'xxxx' # 账号ID 199 | mini_qmt_path = r'D:\国金证券QMT交易端\userdata_mini' # mini_qmt 路径 200 | file_path = r'D:\new_tdx\sign.txt' # 预警文件路径 201 | interval = 1 # 轮询时间(秒) 202 | buy_sign = ['KDJ买入条件选股', 'MACD买入条件选股'] # 多个买入信号 203 | sell_sign = ['KDJ卖出条件选股', 'MACD卖出条件选股'] # 多个卖出信号 204 | 205 | def buy_event(params): 206 | '''买入事件''' 207 | 208 | stock = params.get('stock') 209 | 210 | return { 211 | 'size': 200, 212 | 'price': -1, # 如果是限价,则设置价格 213 | 'type': '市价', # 市价,限价 214 | } 215 | 216 | def sell_event(params): 217 | '''卖出事件''' 218 | 219 | stock = params.get('stock') 220 | position = params.get('position') 221 | 222 | return { 223 | 'size': position.can_use_volume, # 卖全仓 224 | 'price': -1, # 如果是限价,则设置价格 225 | 'type': '市价' # 市价,限价 226 | } 227 | 228 | 229 | tdxtrader.start( 230 | account_id=account_id, 231 | mini_qmt_path=mini_qmt_path, 232 | file_path=file_path, 233 | interval=interval, 234 | buy_sign=buy_sign, 235 | sell_sign=sell_sign, 236 | buy_event=buy_event, 237 | sell_event=sell_event, 238 | cancel_after=10 # 10秒未成交则撤单 239 | ) 240 | ``` 241 | 242 | ### 企业微信通知 243 | 244 | 利用企业微信机器人发送通知 245 | 246 | 设置群机器人参看:https://open.work.weixin.qq.com/help2/pc/14931 247 | 248 | ```python 249 | tdxtrader.start( 250 | account_id=account_id, 251 | mini_qmt_path=mini_qmt_path, 252 | file_path=file_path, 253 | interval=interval, 254 | buy_sign=buy_sign, 255 | sell_sign=sell_sign, 256 | buy_event=buy_event, 257 | sell_event=sell_event, 258 | cancel_after=10, # 10秒未成交则撤单, 259 | wechat_webhook_url='你的webhook_url' # 企业微信机器人webhook url 260 | ) 261 | ``` 262 | 263 | ![微信机器人](./wxbot.png) 264 | 265 | ![消息示例](./msg.png) 266 | 267 | ## 详细参数 268 | 269 | ### account_id 270 | 271 | QMT账号ID 272 | 273 | ### mini_qmt_path 274 | 275 | QMT mini路径 276 | 277 | ### file_path 278 | 279 | 预警文件路径 280 | 281 | ### interval 282 | 283 | 轮询时间(秒) 284 | 285 | ### buy_sign 286 | 287 | 买入信号 288 | 289 | ### sell_sign 290 | 291 | 卖出信号 292 | 293 | ### buy_event 294 | 295 | 买入事件 296 | 297 | ### sell_event 298 | 299 | 卖出事件 300 | 301 | ### cancel_after 302 | 303 | 未成交撤单时间(秒) 304 | 305 | ### wechat_webhook_url 306 | 307 | 企业微信机器人webhook url 308 | -------------------------------------------------------------------------------- /cut1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zsrl/tdxtrader/1cbe6b744821309c54bba7a75e017f851a44482c/cut1.png -------------------------------------------------------------------------------- /cut2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zsrl/tdxtrader/1cbe6b744821309c54bba7a75e017f851a44482c/cut2.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # 测试一下 2 | 3 | ## 再测试一下 -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs-material -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | # Define the site name 2 | site_name: tdxtrader文档 3 | 4 | # Set the site URL by using a Read the Docs environment variable: 5 | # https://docs.readthedocs.io/en/stable/reference/environment-variables.html 6 | site_url: !ENV READTHEDOCS_CANONICAL_URL 7 | 8 | # Set the Material for MkDocs theme 9 | theme: 10 | name: material -------------------------------------------------------------------------------- /msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zsrl/tdxtrader/1cbe6b744821309c54bba7a75e017f851a44482c/msg.png -------------------------------------------------------------------------------- /output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zsrl/tdxtrader/1cbe6b744821309c54bba7a75e017f851a44482c/output.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core >=3.2,<4"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "tdxtrader" 7 | version = "0.4.5" 8 | description = "" 9 | authors = [{name = "myc", email = "mayuanchi1029@gmail.com"}] 10 | readme = "README.md" 11 | license = {file = "LICENSE"} 12 | classifiers = ["License :: OSI Approved :: MIT License"] 13 | dependencies = [ 14 | "xtquant" 15 | ] 16 | 17 | [project.urls] 18 | Home = "https://github.com/zsrl/tdxtrader" 19 | 20 | [tool.semantic_release] 21 | version_toml = [ 22 | "pyproject.toml:project.version" 23 | ] 24 | 25 | branch = "main" 26 | upload_to_PyPI = true 27 | upload_to_release = true 28 | commit_author = "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" 29 | 30 | [tool.semantic_release.commit_parser_options] 31 | allowed_tags = [ 32 | "build", 33 | "chore", 34 | "ci", 35 | "docs", 36 | "feat", 37 | "fix", 38 | "perf", 39 | "style", 40 | "refactor", 41 | "test" 42 | ] 43 | minor_tags = ["feat"] 44 | patch_tags = ["fix", "perf"] 45 | -------------------------------------------------------------------------------- /qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zsrl/tdxtrader/1cbe6b744821309c54bba7a75e017f851a44482c/qrcode.png -------------------------------------------------------------------------------- /tdxtrader/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is the main module of the tdxtrader package. 3 | It provides functionality for trading with TDX. 4 | """ 5 | 6 | from .index import start -------------------------------------------------------------------------------- /tdxtrader/anis.py: -------------------------------------------------------------------------------- 1 | # ANSI 转义序列 2 | RED = "\033[91m" 3 | GREEN = "\033[92m" 4 | YELLOW = "\033[93m" 5 | BLUE = "\033[94m" 6 | RESET = "\033[0m" -------------------------------------------------------------------------------- /tdxtrader/file.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from tdxtrader.logger import logger 3 | from tdxtrader.anis import RED, GREEN, YELLOW, BLUE, RESET 4 | 5 | # 定义列名 6 | COLUMNS = ['code', 'name', 'date', 'time', 'price', 'rate', 'value', 'sign'] 7 | 8 | # 需要修复的股票名称列表 9 | STOCK_LIST = ['新 和 成', '五 粮 液', '农 产 品', '南 玻A', '万 科A', '新 华 都', '奥 特 迅', '三 力 士', '诺 普 信', '达 意 隆', '海 利 得', '全 聚 德', '怡 亚 通', '粤 传 媒', '红 宝 丽', '远 望 谷', '报 喜 鸟', '安 纳 达', '生 意 宝', '金 螳 螂', '兔 宝 宝', '南 京 港', '苏 泊 尔', '七 匹 狼', '新 大 陆', '中 关 村', '新 希 望', '张 裕A', '罗 牛 山', '鲁 泰A', '英 力 特', '柳 工', '渝 开 发', '金 融 街', '盐 田 港', '深 赛 格', '特 力A'] 10 | 11 | def fix_stock_name(line): 12 | """ 13 | 修复股票名称中的空格。 14 | """ 15 | for stock in STOCK_LIST: 16 | fixed_stock = stock.replace(' ', '') # 去掉空格 17 | line = line.replace(stock, fixed_stock) # 替换原始行中的股票名称 18 | return line 19 | 20 | def process_line(line): 21 | """ 22 | 处理每一行数据,确保列数为 8,并将 'price' 转换为 float 并保留两位小数。 23 | """ 24 | line = line.strip() # 去掉首尾空白字符 25 | line = fix_stock_name(line) # 修复股票名称中的空格 26 | fields = line.split() # 按空白字符分割 27 | 28 | # 检查列数是否为 8 29 | if len(fields) != 8: 30 | logger.error(f"【错误行】 {line}") 31 | return None # 跳过该行 32 | 33 | # 将 'price' 转换为 float 并保留两位小数 34 | try: 35 | fields[4] = round(float(fields[4]), 2) # price 36 | except ValueError as e: 37 | logger.error(f"【类型转换错误】 {line}: {e}") 38 | return None # 跳过该行 39 | 40 | return fields 41 | 42 | def read_file(file_path): 43 | """ 44 | 读取文件并处理错误,支持多种编码格式。 45 | """ 46 | encodings = ['gbk', 'gb2312', 'utf-8'] 47 | 48 | for encoding in encodings: 49 | try: 50 | # 检查文件是否为空 51 | with open(file_path, 'r', encoding=encoding, errors='ignore') as file: 52 | first_line = file.readline() 53 | if not first_line: # 文件为空 54 | return pd.DataFrame(columns=COLUMNS) 55 | 56 | # 逐行读取文件并处理 57 | rows = [] 58 | with open(file_path, 'r', encoding=encoding, errors='ignore') as file: 59 | for line in file: 60 | processed_line = process_line(line) # 处理每一行 61 | if processed_line: # 如果处理成功 62 | rows.append(processed_line) 63 | 64 | # 构建 DataFrame 65 | df = pd.DataFrame(rows, columns=COLUMNS) 66 | logger.debug(f"使用 {encoding} 编码成功读取文件") 67 | return df 68 | 69 | except UnicodeError: 70 | continue 71 | except Exception as e: 72 | logger.error(f"读取文件时发生错误: {e}") 73 | continue 74 | 75 | logger.error("所有编码尝试均失败") 76 | return None 77 | 78 | def clear_file_content(file_path): 79 | try: 80 | with open(file_path, 'w', encoding='gbk') as file: 81 | file.truncate(0) 82 | # 写入表头 83 | # header = ' '.join(COLUMNS) + '\n' 84 | # file.write(header) 85 | logger.debug(f"【重置文件】内容已清空") 86 | except Exception as e: 87 | logger.error(f"清空文件内容时发生错误: {e}") -------------------------------------------------------------------------------- /tdxtrader/index.py: -------------------------------------------------------------------------------- 1 | import time 2 | from tdxtrader.file import clear_file_content 3 | from tdxtrader.trader import create_trader 4 | from tdxtrader.order import create_order, cancel_order 5 | from tdxtrader.logger import logger, add_wechat_handler 6 | from tdxtrader.anis import RED, RESET 7 | 8 | def start(account_id, mini_qmt_path, file_path, buy_sign, sell_sign, buy_event, sell_event, interval=1, cancel_after=None, wechat_webhook_url=None): 9 | 10 | add_wechat_handler(logger, wechat_webhook_url) 11 | 12 | xt_trader, account = create_trader(account_id, mini_qmt_path) 13 | 14 | previous_df = None 15 | 16 | # 启动前清空文件内容 17 | clear_file_content(file_path) 18 | 19 | while True: 20 | try: 21 | previous_df = create_order(xt_trader, account, file_path, previous_df, buy_sign, sell_sign, buy_event, sell_event) 22 | # 撤单 23 | cancel_order(xt_trader, account, cancel_after) 24 | 25 | except Exception as e: 26 | logger.error(f"{RED}【程序错误】{RESET}{e}") 27 | 28 | time.sleep(interval) 29 | -------------------------------------------------------------------------------- /tdxtrader/logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import requests 3 | import os 4 | import re 5 | from datetime import date 6 | 7 | formatter = logging.Formatter('[%(asctime)s]%(message)s', datefmt='%Y-%m-%d %H:%M:%S') 8 | 9 | class RemoveAnsiEscapeCodes(logging.Filter): 10 | def filter(self, record): 11 | record.msg = re.sub(r'\033\[[0-9;]*m', '', str(record.msg)) 12 | return True 13 | 14 | def create_logger(): 15 | 16 | logger = logging.getLogger('log') 17 | logger.setLevel(logging.DEBUG) 18 | 19 | stream_handler = logging.StreamHandler() 20 | stream_handler.setFormatter(formatter) 21 | logger.addHandler(stream_handler) 22 | 23 | # 创建日志文件夹(如果不存在) 24 | log_dir = 'logs' 25 | if not os.path.exists(log_dir): 26 | os.makedirs(log_dir) 27 | 28 | # 创建文件处理器,并将日志写入文件 29 | file_handler = logging.FileHandler(f"{log_dir}/{date.today().strftime('%Y-%m-%d')}.log", encoding='utf-8') 30 | file_handler.setLevel(logging.DEBUG) 31 | file_handler.setFormatter(formatter) 32 | file_handler.addFilter(RemoveAnsiEscapeCodes()) 33 | logger.addHandler(file_handler) 34 | 35 | return logger 36 | 37 | 38 | class WeChatHandler(logging.Handler): 39 | def __init__(self, webhook_url): 40 | super().__init__() 41 | self.webhook_url = webhook_url 42 | 43 | def emit(self, record): 44 | log_entry = self.format(record) 45 | payload = { 46 | "msgtype": "text", 47 | "text": { 48 | "content": log_entry 49 | } 50 | } 51 | try: 52 | response = requests.post(self.webhook_url, json=payload) 53 | response.raise_for_status() 54 | except requests.exceptions.RequestException as e: 55 | print(f"Failed to send log to WeChat: {e}") 56 | 57 | def add_wechat_handler(logger, wechat_webhook_url): 58 | 59 | if wechat_webhook_url is not None: 60 | 61 | # 创建企业微信处理器,并将日志发送到企业微信 62 | wechat_handler = WeChatHandler(wechat_webhook_url) 63 | wechat_handler.setLevel(logging.INFO) 64 | wechat_handler.setFormatter(formatter) 65 | wechat_handler.addFilter(RemoveAnsiEscapeCodes()) 66 | logger.addHandler(wechat_handler) 67 | 68 | 69 | logger = create_logger() 70 | -------------------------------------------------------------------------------- /tdxtrader/order.py: -------------------------------------------------------------------------------- 1 | from xtquant import xtconstant 2 | import time 3 | import math 4 | import inspect 5 | from tdxtrader.file import read_file 6 | from tdxtrader.utils import add_stock_suffix, timestamp_to_datetime_string, convert_to_current_date 7 | from tdxtrader.anis import RED, GREEN, YELLOW, BLUE, RESET 8 | from tdxtrader.logger import logger 9 | from tdxtrader.trader import error_orders 10 | 11 | def get_volume(paload, row): 12 | if paload.get('size') is not None: 13 | return paload.get('size') 14 | elif paload.get('amount') is not None: 15 | if paload.get('price') > 0: 16 | return math.floor(paload.get('amount') / paload.get('price') / 100) * 100 17 | else: 18 | return math.floor(paload.get('amount') / row.get('price') / 100) * 100 19 | else: 20 | return 100 21 | 22 | 23 | def get_position(positions, stock_code): 24 | for position in (positions or []): 25 | if position.stock_code == stock_code: 26 | return position 27 | return None 28 | 29 | def create_order(xt_trader, account, file_path, previous_df, buy_sign, sell_sign, buy_event, sell_event): 30 | current_df = read_file(file_path) 31 | if current_df is not None: 32 | if previous_df is not None: 33 | # 比较前后两次读取的 DataFrame,找出新增的行 34 | new_rows = current_df[~current_df.index.isin(previous_df.index)] 35 | if not new_rows.empty: 36 | for index, row in new_rows.iterrows(): 37 | 38 | stock_code = add_stock_suffix(row['code']) 39 | 40 | price_type_map = { 41 | '市价': xtconstant.LATEST_PRICE, 42 | '限价': xtconstant.FIX_PRICE 43 | } 44 | 45 | positions = xt_trader.query_stock_positions(account) 46 | 47 | position = get_position(positions, stock_code) 48 | 49 | params = { 50 | 'xt_trader': xt_trader, 51 | 'account': account, 52 | 'stock': row, 53 | 'position': position 54 | } 55 | 56 | # Convert buy_sign and sell_sign to lists if they are strings 57 | buy_signs = buy_sign if isinstance(buy_sign, list) else [buy_sign] 58 | sell_signs = sell_sign if isinstance(sell_sign, list) else [sell_sign] 59 | 60 | if row['sign'] in buy_signs: 61 | if len(inspect.signature(buy_event).parameters) > 1: # 检查 buy_event 是否需要额外的参数 62 | buy_paload = buy_event(row, position, xt_trader) 63 | else: 64 | buy_paload = buy_event(params) 65 | if buy_paload is not None: 66 | xt_trader.order_stock_async( 67 | account=account, 68 | stock_code=stock_code, 69 | order_type=xtconstant.STOCK_BUY, 70 | order_volume=get_volume(buy_paload, row), 71 | price_type=price_type_map.get(buy_paload.get('type')) or xtconstant.LATEST_PRICE, 72 | price=buy_paload.get('price') or -1, 73 | order_remark=row.get('name') 74 | ) 75 | elif row['sign'] in sell_signs: 76 | if position is not None: 77 | if len(inspect.signature(sell_event).parameters) > 1: # 检查 sell_event 是否需要额外的参数 78 | sell_paload = sell_event(row, position, xt_trader) 79 | else: 80 | sell_paload = sell_event(params) 81 | 82 | if sell_paload is not None: 83 | xt_trader.order_stock_async( 84 | account=account, 85 | stock_code=stock_code, 86 | order_type=xtconstant.STOCK_SELL, 87 | order_volume=get_volume(sell_paload, row), 88 | price_type=price_type_map.get(sell_paload.get('type')) or xtconstant.LATEST_PRICE, 89 | price=sell_paload.get('price') or -1, 90 | order_remark=row.get('name') 91 | ) 92 | else: 93 | logger.warning(f"{YELLOW}【无持仓】{RESET}没有查询到持仓信息,不执行卖出操作。股票代码:{stock_code}, 名称:{row['name']}") 94 | 95 | return current_df 96 | 97 | return None 98 | 99 | def cancel_order(xt_trader, account, cancel_after): 100 | if cancel_after is not None: 101 | orders = xt_trader.query_stock_orders(account, cancelable_only=True) 102 | for order in orders: 103 | if order.order_id in error_orders: 104 | return 105 | order_time = convert_to_current_date(order.order_time) 106 | if time.time() - order_time >= cancel_after: 107 | xt_trader.cancel_order_stock_async(account, order.order_id) -------------------------------------------------------------------------------- /tdxtrader/trader.py: -------------------------------------------------------------------------------- 1 | from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback 2 | from xtquant.xttype import StockAccount 3 | import random 4 | import logging 5 | from tdxtrader.utils import timestamp_to_datetime_string, parse_order_type, convert_to_current_date 6 | from tdxtrader.anis import RED, GREEN, YELLOW, BLUE, RESET 7 | from tdxtrader.logger import logger 8 | 9 | error_orders = [] 10 | 11 | class MyXtQuantTraderCallback(XtQuantTraderCallback): 12 | def on_disconnected(self): 13 | """ 14 | 连接状态回调 15 | :return: 16 | """ 17 | print("connection lost") 18 | def on_stock_order(self, order): 19 | """ 20 | 委托信息推送 21 | :param order: XtOrder对象 22 | :return: 23 | """ 24 | # 委托 25 | if order.order_status == 50: 26 | logger.info(f"{BLUE}【已委托】{RESET} {parse_order_type(order.order_type)} 代码:{order.stock_code} 名称:{order.order_remark} 委托价格:{order.price:.2f} 委托数量:{order.order_volume} 订单编号:{order.order_id} 委托时间:{timestamp_to_datetime_string(convert_to_current_date(order.order_time))}") 27 | elif order.order_status == 53 or order.order_status == 54: 28 | logger.warning(f"{YELLOW}【已撤单】{RESET} {parse_order_type(order.order_type)} 代码:{order.stock_code} 名称:{order.order_remark} 委托价格:{order.price:.2f} 委托数量:{order.order_volume} 订单编号:{order.order_id} 委托时间:{timestamp_to_datetime_string(convert_to_current_date(order.order_time))}") 29 | 30 | def on_stock_trade(self, trade): 31 | """ 32 | 成交信息推送 33 | :param trade: XtTrade对象 34 | :return: 35 | """ 36 | logger.info(f"{GREEN}【已成交】{RESET} {parse_order_type(trade.order_type)} 代码:{trade.stock_code} 名称:{trade.order_remark} 成交价格:{trade.traded_price:.2f} 成交数量:{trade.traded_volume} 成交编号:{trade.order_id} 成交时间:{timestamp_to_datetime_string(convert_to_current_date(trade.traded_time))}") 37 | 38 | def on_order_error(self, data): 39 | if data.order_id in error_orders: 40 | return 41 | error_orders.append(data.order_id) 42 | logger.error(f"{RED}【委托失败】{RESET}错误信息:{data.error_msg.strip()}") 43 | 44 | def on_cancel_error(self, data): 45 | if data.order_id in error_orders: 46 | return 47 | error_orders.append(data.order_id) 48 | logger.error(f"{RED}【撤单失败】{RESET}错误信息:{data.error_msg.strip()}") 49 | 50 | 51 | def create_trader(account_id, mini_qmt_path): 52 | # 创建session_id 53 | session_id = int(random.randint(100000, 999999)) 54 | # 创建交易对象 55 | xt_trader = XtQuantTrader(mini_qmt_path, session_id) 56 | # 启动交易对象 57 | xt_trader.start() 58 | # 连接客户端 59 | connect_result = xt_trader.connect() 60 | 61 | if connect_result == 0: 62 | logger.debug(f"{GREEN}【miniQMT连接成功】{RESET} 路径:{mini_qmt_path}") 63 | else: 64 | logger.error(f"{RED}【miniQMT连接失败】{RESET} 请检查") 65 | 66 | # 创建账号对象 67 | account = StockAccount(account_id) 68 | # 订阅账号 69 | xt_trader.subscribe(account) 70 | logger.debug(f"{GREEN}【账号订阅成功】{RESET} 账号ID:{account_id}") 71 | # 注册回调类 72 | xt_trader.register_callback(MyXtQuantTraderCallback()) 73 | 74 | return xt_trader, account -------------------------------------------------------------------------------- /tdxtrader/utils.py: -------------------------------------------------------------------------------- 1 | from xtquant import xtconstant 2 | from datetime import datetime 3 | from tdxtrader.anis import RED, GREEN, YELLOW, BLUE, RESET 4 | 5 | def add_stock_suffix(stock_code): 6 | """ 7 | 为给定的股票代码添加相应的后缀。 8 | """ 9 | # 检查股票代码是否为6位数字 10 | if len(stock_code) != 6 or not stock_code.isdigit(): 11 | raise ValueError("股票代码必须是6位数字") 12 | 13 | # 根据股票代码的前缀添加相应的后缀 14 | if stock_code.startswith("00") or stock_code.startswith("30") or stock_code.startswith("15") or stock_code.startswith("16") or stock_code.startswith("18") or stock_code.startswith("12"): 15 | return f"{stock_code}.SZ" # 深圳证券交易所 16 | elif stock_code.startswith("60") or stock_code.startswith("68") or stock_code.startswith("11"): 17 | return f"{stock_code}.SH" # 上海证券交易所 18 | elif stock_code.startswith("83") or stock_code.startswith("43"): 19 | return f"{stock_code}.BJ" # 北京证券交易所 20 | 21 | return f"{stock_code}.SH" 22 | 23 | def timestamp_to_datetime_string(timestamp): 24 | """ 25 | 将时间戳转换为时间字符串。 26 | 27 | :param timestamp: 时间戳(秒级) 28 | :return: 格式化的时间字符串 'YYYY-MM-DD HH:MM:SS' 29 | """ 30 | dt_object = datetime.fromtimestamp(timestamp) 31 | time_string = dt_object.strftime('%Y-%m-%d %H:%M:%S') 32 | return time_string 33 | 34 | def parse_order_type(order_type): 35 | if order_type == xtconstant.STOCK_BUY: 36 | return f"{RED}买入{RESET}" 37 | elif order_type == xtconstant.STOCK_SELL: 38 | return f"{GREEN}卖出{RESET}" 39 | 40 | def convert_to_current_date(timestamp): 41 | # 将时间戳转换为 datetime 对象 42 | dt = datetime.fromtimestamp(timestamp) 43 | 44 | # 获取当前日期 45 | current_date = datetime.now().date() 46 | 47 | # 创建一个新的 datetime 对象,使用当前日期和原始时间戳的时间部分 48 | new_dt = datetime.combine(current_date, dt.time()) 49 | 50 | return new_dt.timestamp() -------------------------------------------------------------------------------- /wxbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zsrl/tdxtrader/1cbe6b744821309c54bba7a75e017f851a44482c/wxbot.png --------------------------------------------------------------------------------