{"id":16346680,"url":"https://github.com/zillionare/trader-gm-adaptor","last_synced_at":"2026-02-17T21:03:29.690Z","repository":{"id":192776108,"uuid":"467013773","full_name":"zillionare/trader-gm-adaptor","owner":"zillionare","description":"量化实盘接入（东财EMC）","archived":false,"fork":false,"pushed_at":"2023-09-05T09:02:14.000Z","size":246,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"feature/1.0","last_synced_at":"2025-10-19T07:51:02.182Z","etag":null,"topics":["quant","trade","trading-bot","trading-interface"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zillionare.png","metadata":{"files":{"readme":"README.md","changelog":"HISTORY.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-03-07T08:50:42.000Z","updated_at":"2025-05-01T16:03:32.000Z","dependencies_parsed_at":null,"dependency_job_id":"88429e50-2f4e-46d3-bc5d-3d40300fb3a4","html_url":"https://github.com/zillionare/trader-gm-adaptor","commit_stats":null,"previous_names":["zillionare/trader-gm-adaptor"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zillionare/trader-gm-adaptor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zillionare%2Ftrader-gm-adaptor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zillionare%2Ftrader-gm-adaptor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zillionare%2Ftrader-gm-adaptor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zillionare%2Ftrader-gm-adaptor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zillionare","download_url":"https://codeload.github.com/zillionare/trader-gm-adaptor/tar.gz/refs/heads/feature/1.0","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zillionare%2Ftrader-gm-adaptor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29558101,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T20:52:40.164Z","status":"ssl_error","status_checked_at":"2026-02-17T20:48:10.325Z","response_time":100,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["quant","trade","trading-bot","trading-interface"],"created_at":"2024-10-11T00:36:26.655Z","updated_at":"2026-02-17T21:03:29.674Z","avatar_url":"https://github.com/zillionare.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"【目录】\n\n- [1.1. 安装](#11-安装)\n- [1.2. 申请权限](#12-申请权限)\n- [1.3. 模拟和测试](#13-模拟和测试)\n  - [1.3.1. 配置账号](#131-配置账号)\n    - [1.3.1.1. gmadaptor的配置文件](#1311-gmadaptor的配置文件)\n    - [1.3.1.2. 配置EMC](#1312-配置emc)\n  - [1.3.2. 模拟运行](#132-模拟运行)\n- [1.4. 运行和维护](#14-运行和维护)\n  - [1.4.1. 启动](#141-启动)\n  - [1.4.2. 每日维护](#142-每日维护)\n- [2.1. 客户端请求](#21-客户端请求)\n- [2.2. 返回结果](#22-返回结果)\n- [2.3. 资产表](#23-资产表)\n- [2.4. 持仓表](#24-持仓表)\n- [2.5. 限价买入](#25-限价买入)\n- [2.6. 市价买入](#26-市价买入)\n- [2.7. 限价卖出](#27-限价卖出)\n- [2.8. 市价卖出](#28-市价卖出)\n- [2.9. 取消委托](#29-取消委托)\n- [2.10. 查询当日委托](#210-查询当日委托)\n- [撮合配置规则](#撮合配置规则)\n- [联系方式](#联系方式)\n\n# 1. 安装与配置\n## 1.1. 安装\n1. 在windows机器上安装[https://emt.eastmoneysec.com/down](https://emt.eastmoneysec.com/down)，下载并安装第二个软件：\n![](https://images.jieyu.ai/images/2023/03/20230403154605.png)\n\n2. 在同一台机器上安装conda，推荐安装miniconda，并创建虚拟运行环境（python版本3.8）：\n   ```\n   conda create -n gmclient python=3.8\n   ```\n3. 安装gmadaptor:\n    ```\n    pip install gmadaptor-1.1.3-py3-none-any.whl\n    ```\n## 1.2. 申请权限\n请加入东方财富量化仿真交流群：971584613 ，找管理员申请开通量化实盘权限。请先看群置顶文件。\n开通的主要门槛是要求100万初始资金。开通后可以撤资。\n\n## 1.3. 模拟和测试\n在等待实盘权限开通的过程中，可以通过 https://emt.18.cn/apply/test-apply-client 开通模拟账号，先把程序和配置调通。\n\n申请后，记录普通资金账号和密码，如下图：\n\n![](https://images.jieyu.ai/images/2023/04/仿真.jpg)\n\n在登录界面中，选择仿真交易：\n\n![](https://images.jieyu.ai/images/2023/04/login.jpg)\n\n登录后，界面显示如下：\n\n![](https://images.jieyu.ai/images/2023/04/20230403200024.png)\n\n### 1.3.1. 配置账号\n以下步骤对实盘和模拟盘均有效。\n\n#### 1.3.1.1. gmadaptor的配置文件\n需要将量化软件中的实盘账号配置到gmadaptor的配置文件中。在用户目录下，创建`gmadaptor/config`目录，放置以下文件：\n```yaml\n# defaults.yaml\nlog_level: INFO\n\nserver_info:\n    port: 9000\n    # client 使用这一token来访问 gmadaptor 提供的服务\n    access_token : \"84ae0899-7a8d-44ff-9983-4fa7cbbc424b\"\n\ngm_info:\n    fake: false\n    # 文件单输出目录\n    gm_output: \"~/gmadaptor/FileOrders/out\"\n    trade_fees:\n        commission: 2.5\n        stamp_duty: 10.0\n        transfer_fee: 0.1\n        minimum_cost: 5.0\n    accounts:\n        # 账号名\n        - name: fileorder_s01\n          acct_id: 1a66e81c-ae5d-11ec-aef5-00163e0a4100\n          # 文件单输入目录。东财量化终端将从这里读取文件单\n          acct_input: \"~/gmadaptor/FileOrders/inputs/fileorder_s01\"\n```\n上述配置中，access_token 可任意指定，任何要访问此服务的客户端，必须持有此 token。\n\ngm_output/acct_input 文件设置后，如果未创建，gmadaptor 将在启动时自动创建，请确保 gmadaptor 有权限读写这些文件夹。\n\naccounts \u003e name 中的值来自于在 EMC 终端中，您创建文件单输入时，指定的名称，见下图中的序号2：\n\n![](https://images.jieyu.ai/images/2023/04/20230403194653.png)\n\naccounts \u003e acct_id 来自于下面序号3的位置，点击`ID`即可复制：\n\n![](https://images.jieyu.ai/images/2023/04/20230403195425.png)\n\n#### 1.3.1.2. 配置EMC\n\n在 量化 \u003e 文件单 \u003e 文件单输出 中，对下图中的 4，5，6，7 进行配置。其中4选择我们在上面配置文件中gm_output中设置的路径；5选择`csv`作为输出格式；6选择自动启动；7将所有项目全选中。\n\n![](https://images.jieyu.ai/images/2023/04/output.jpg?1)\n\n在 量化 \u003e 文件单 \u003e 文件单输入 中，对下图中的 3和4进行配置。其中3选择我们在上面配置文件中设置的 acct_input 路径，4选择自动启动。\n\n![](https://images.jieyu.ai/images/2023/04/input.jpg?1)\n\n\n### 1.3.2. 模拟运行\n\n在前面生成的gmclient虚拟环境中，执行以下命令，以启动gmadaptor服务器：\n```\npython -m gmadaptor.server\n```\n如果出现如下界面，表明服务器启动成功：\n\n![](https://images.jieyu.ai/images/2023/04/started.jpg)\n\n此时我们另开一个`conda`窗口，同样使用`gmclient`的虚拟环境，通过以下命令进行测试:\n```\npython -m gmtest %account %token %server %port\n```\n\n这里的account即 gmadaptor配置文件中的 gm_info \u003e accounts \u003e account_id, token 即server_info \u003e access_token\n\n这里的 server 即gmadaptor 所在的机器IP， port为端口。如果不提供，默认地，这两项分别为localhost和9000。\n\n如果配置正常，这将打印出初始账号资金，当前持仓，和一笔买、卖的信息。\n## 1.4. 运行和维护\n\n另外启动一个计划任务，在每天早上8:45左右启动EMC。\n### 1.4.1. 启动\n使用下面的脚本来启动：\n```\n@echo off\ncall C:\\ProgramData\\Anaconda3\\Scripts\\activate.bat C:\\ProgramData\\anaconda3\ncall conda activate gmclient\npython -m gmadaptor.server\npause\n```\n\n### 1.4.2. 每日维护\nEMC量化终端有时候不稳定。我们可以通过定时重启来提高起稳定性。通过以下代码，在盘后退出EMC：\n```\nREM kill process\nTASKKILL /F /IM EMCTrade.exe\n\nREM sleep 5 seconds\nTIMEOUT  /T 5\n\nREM remove all file orders after process killed\n\nDEL /Q C:\\zillionare\\FileOrders\\real_input\\*.csv\n```\n\n!!! Warning\n    如果在输入输出目录中还有未归档的文件，则量化交易将无法自动启动。上述代码中最后一行的作用就是清理未归档文件。\n    这也要求使用者自行对委托进行核验，确保这些文件可以被自动删除。\n# 2. 客户端与服务器交互\n## 2.1. 客户端请求\n\n客户端通过 http request来请求gmadaptor。以下示例均以同步请求实现，但您也可以根据需要，改为异步请求。服务器对客户端的鉴权是通过`headers`来实现的，具体请看示例（任意一示例均可）。客户端向服务器发送数据，都使用的是post方法，gmadaptor所有的方法都只响应post请求。如果请求成功完成，则返回代码是200。\n\n在所有的操作中，股票代码都必须以简码+交易所后缀方式出现，其中上交所为.XSHG，深交所为.XSHE。在仿真测试时，对股票进行操作时，都有特定的响应（比如对某支股票，进行买入时，无论给的参数是多少，返回都会是部分成交；对另一支股票，则永远返回限制买入等等），具体响应文档请在东财量化Q群中，找管理员要文档。\n\n在一些会改变状态的请求中（比如买入操作），往往会需要`cid`参数和`timeout`参数，其作用是，调用会在指定的timeout期间等待EMC处理请求，并返回结果；但EMC也可能无法在指定的timeout期间返回结果，比如委买单报价过低，一直不能成交，此时将无法产生回报结果。在这种情况下，调用会在timeout之后返回。之后的委买结果查询，就需要依赖`cid`参数。注意`cid`是必选参数，`timeout`是可选参数。\n\n`cid`参数（即client entrust id）由客户端自行产生，建议使用以下代码：\n```python\nimport uuid\n\ncid = str(uuid.uuid4())\n```\n`cid`产生之后，请在客户端保存，直到事务结束，不再需要为止。\n\n为简练起见，以下示例中，可能删除了这些代码：\n```python\nimport httpx\nheaders = {\n    \"Authorization\": \"84ae0899-7a8d-44ff-9983-4fa7cbbc424b\",\n    \"Account-ID\": \"780dc4fda3d0af8a2d3ab0279bfa48c9\"\n}\n\n_url_prefix = \"http://192.168.100.100:9000/\"\n\nbuy_entrust_no = None\nsell_entrust_no = None\n```\n\n## 2.2. 返回结果\n返回错误可能发生在三个层面。一是http层（包括 bad request 或者 Internal server error); 二是 gmadaptor层，三是 emc可能返回错误。\n\n第一层的错误，我们通过 http status code来检查。比如，如果我们使用的客户端是httpx，则可以检查 `response.status_code`是否为200。\n\ngmadaptor始终通过`json`来返回响应，响应包括三个字段，即：\n```\nstatus: int,如果为零，则表明在此层没有发生错误，即gmadaptor已经将请求正确上报\nmsg: str, human readable message\ndata: dict 如果一切顺利，则返回数据在此项中\n```\n第三层的错误由 emc trader给出。即使gmadaptor正确上报的请求，也可能emc trader无法执行，此时它也会通过`status`和`reason`来给出错误信息。\n\n下面的示例给出了一个`response.json()`的输出：\n```json\n{\n    \"status\": 0,\n    \"msg\": \"OK\",\n    \"data\": {\n        \"code\": \"000001.XSHE\",\n        \"price\": 0.0,\n        \"volume\": 100,\n        \"order_side\": 1,\n        \"bid_type\": 2,\n        \"time\": \"2023-04-04 15:27:38.921555\",\n        \"entrust_no\": \"0d23bb4e-d81e-4ef2-ab21-c58e0fe6814f\",\n        \"status\": -1,\n        \"average_price\": 0.0,\n        \"filled\": 0,\n        \"filled_amount\": 0,\n        \"eid\": \"\",\n        \"trade_fees\": 0,\n        \"reason\": \"[Counter] [EMC_PC]不支持该下单类型\",\n        \"recv_at\": \"2023-04-04 15:27:38.924565\"\n    }\n}\n```\n因此，即使在gmadator层面给出的状态是成功，也并不意味着该笔委托成功。另一个例子是，以过低的价格委买，只要参数合法且被EMC接收，gmadaptor都会返回成功，但该委托是否真正成交，还得通过entrust_no来查询。\n## 2.3. 资产表\n```python\n# 请求资金信息\nimport httpx\nheaders = {\n    \"Authorization\": \"84ae0899-7a8d-44ff-9983-4fa7cbbc424b\",\n    \"Account-ID\": \"780dc4fda3d0af8a2d3ab0279bfa48c9\"\n}\n\n_url_prefix = \"http://192.168.100.100:9000/\"\n\ndef get_balance():\n    r = httpx.post(_url_prefix + \"balance\", headers=headers)\n    resp = r.json()\n    if r.status_code == 200 and resp['status'] == 0:\n        print(\"\\n------ 账户资金信息 ------\")\n        print(resp[\"data\"])\n```\n\n## 2.4. 持仓表\n```python\ndef get_positions():\n    r = httpx.post(_url_prefix + \"positions\", headers=headers)\n    \n    resp = r.json()\n    if r.status_code == 200 and resp['status'] == 0:\n        print(\"\\n----- 持仓信息 ------\")\n        print(resp[\"data\"])\n```\n\n## 2.5. 限价买入\n```python\n    r = httpx.post(_url_prefix + \"buy\", headers=headers, json={\n        \"security\": \"000001.XSHE\",\n        \"price\": 13,\n        \"volume\": 100,\n        \"cid\": str(uuid.uuid4()),\n        \"timeout\": 1\n    })\n\n    print(r.json())\n```\n\n## 2.6. 市价买入\n```python\ndef market_buy():\n    global buy_entrust_no\n    r = httpx.post(_url_prefix + \"market_buy\", headers=headers, json={\n        \"security\": \"000001.XSHE\",\n        \"volume\": 100,\n        \"cid\": cid,\n        \"timeout\": 1\n    })\n\n    resp = r.json()\n    if r.status_code == 200 and resp[\"status\"] == 0:\n        print(\"\\n ------ 委买成功 ------\")\n        print(resp[\"status\"], resp[\"msg\"], resp[\"data\"])\n        buy_entrust_no = resp[\"data\"][\"entrust_no\"]\n    else:\n        print(\"委买失败:\", r.status_code, resp)\n```\n\n## 2.7. 限价卖出\n```python\ndef sell():\n    global sell_entrust_no\n\n    r = httpx.post(_url_prefix + \"sell\", headers=headers, json={\n        \"security\": \"000001.XSHE\",\n        \"price\": 10,\n        \"volume\": 100,\n        \"cid\": cid,\n        \"timeout\": 1\n    })\n\n    resp = r.json()\n    if r.status_code == 200 and resp[\"status\"] == 0:\n        print(\"\\n ------ 限价委卖成功 ------\")\n        data = resp[\"data\"]\n        print(data)\n        sell_entrust_no = data[\"entrust_no\"]\n    else:\n        print(\"卖出失败:\", r.status_code, resp)\n```\n\n## 2.8. 市价卖出\n```python\ndef market_sell():\n    r = httpx.post(_url_prefix + \"market_sell\", headers=headers, json = {\n        \"security\": \"000001.XSHE\",\n        \"volume\": 100,\n        \"cid\": cid\n    })\n\n    resp = r.json()\n    if r.status_code == 200 and resp[\"status\"] == 0:\n        print(\"\\n ------ 市价委卖成功 ------\")\n        print(resp[\"data\"])\n    else:\n        print(resp)\n```\n## 2.9. 取消委托\n```python\ndef cancel_entrust():\n    global buy_entrust_no\n\n    r = httpx.post(_url_prefix + \"cancel_entrust\", headers=headers, json = {\n        \"entrust_no\": buy_entrust_no,\n        \"timeout\": 1\n    })\n\n    resp = r.json()\n    print(resp[\"status\"], resp[\"msg\"], resp[\"data\"])\n\n```\n## 2.10. 查询当日委托\n```python\ndef today_entrusts():\n    r = httpx.post(_url_prefix + \"today_entrusts\", headers=headers, json = {\n        # 此处可以传入记录的委托号。传入空数组时，表明取当天所有委托。\n        \"entrust_no\": [],\n        \"timeout\": 1\n    })\n\n    resp = r.json()\n    print(resp[\"status\"], resp[\"msg\"], resp[\"data\"])\n```\n\n# 3. 故障排除与帮助\n\n关于东财文件单，请参考：https://emquant.18.cn/file-help/?doc=file_order\n东财量化Q群：971584613\n\n即使实现了EMC的每日自动重启，也有可能偶发连接异常或者其它错误。此时可能需要手动执行：\n1. 重新连接\n2. 清除文件单，重新启动\n\n## 撮合配置规则\n在仿真交易测试中，EMTrader对每个品种，都指定了对应的响应。比如，对000572这个品种，买入一定会全部成交，对000010这个品种，则一定会拒绝。这是为了方便测试的需要。东财提供了名为《撮合配置规则》的文件，该文件可能随时更新，所以，需要在测试前，加他们技术人员QQ领取。\n\n该文件2023年3月份部分内容如下：\n```\n[全部成交]\n000572\tfull\n000725\tfull\n\n[分笔成交] \n分成两笔：\n000002\tlot      \t2\n\n[部分成交] 成交一半\n000001\tpart\n000004\tpart\n...\n[挂单]\t只有响应，没有成交\n018014\tpending\n020417\tpending\n...\n\n[拒单]\t\n000010\treject\n010609\treject\n\n[拒绝撤单] 部分成交，不可撤单\n000008\tcancel_reject\n000151\tcancel_reject\n```\n\n## 联系方式\n\n如果您在使用本模块中需要帮助，或者需要参加[《大富翁量化编程实战课》](https://github.com/zillionare)学习，请添加 宽粉 的微信：\n\n![](https://images.jieyu.ai/images/hot/quantfans.jpg)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzillionare%2Ftrader-gm-adaptor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzillionare%2Ftrader-gm-adaptor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzillionare%2Ftrader-gm-adaptor/lists"}