├── .gitbook └── assets │ ├── github-10.png │ └── nodequant_brand.jpg ├── LICENSE ├── NOTICE ├── README.md ├── nodequant ├── .gitignore ├── LICENSE ├── NOTICE ├── NodeQuantApp.js ├── README.md ├── app.js ├── bin │ └── www ├── common.js ├── engine │ ├── mainEngine.js │ └── strategyEngine.js ├── model │ ├── client │ │ ├── CTP │ │ │ ├── linux │ │ │ │ └── x64 │ │ │ │ │ ├── NodeQuant.node │ │ │ │ │ ├── thostmduserapi_se.so │ │ │ │ │ └── thosttraderapi_se.so │ │ │ └── win32 │ │ │ │ └── x64 │ │ │ │ ├── NodeQuant.node │ │ │ │ ├── thostmduserapi_se.dll │ │ │ │ ├── thostmduserapi_se.lib │ │ │ │ ├── thosttraderapi_se.dll │ │ │ │ └── thosttraderapi_se.lib │ │ ├── CTPClient.js │ │ └── ClientFactory.js │ └── influxdb │ │ └── influxdb.js ├── package.json ├── public │ └── stylesheets │ │ └── style.css ├── routes │ ├── index.js │ └── test │ │ ├── cancelOrder.js │ │ ├── getTradingDay.js │ │ ├── md │ │ └── getApiVersion.js │ │ ├── queryCommissionRate.js │ │ ├── queryInvestorPosition.js │ │ ├── querySettlementInfo.js │ │ ├── queryTradingAccount.js │ │ ├── sendFillAndKillLimitOrder.js │ │ ├── sendFillOrKillLimitOrder.js │ │ ├── sendLimitOrder.js │ │ ├── sendMarketIfTouchedOrder.js │ │ ├── sendMarketOrder.js │ │ ├── sendStopLimitOrder.js │ │ ├── subscribeContract.js │ │ ├── td │ │ └── getApiVersion.js │ │ └── unSubscribeContract.js ├── run_in_linux.sh ├── strategy │ ├── DemoStrategy.js │ ├── Open_CloseStrategy.js │ ├── TestTickToFinishSendOrder_Strategy.js │ └── baseStrategy.js ├── systemConfig.js ├── userConfig.js ├── util │ ├── DateTimeUtil.js │ ├── KBar.js │ ├── NodeQuantError.js │ ├── NodeQuantLog.js │ └── Position.js └── views │ ├── error.ejs │ └── index.ejs └── test ├── TestNodeQuant.json.postman_collection └── readme.docx /.gitbook/assets/github-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangshuiyong/nodequant/408e985555fbcfdb6be220acb4af95e192f27268/.gitbook/assets/github-10.png -------------------------------------------------------------------------------- /.gitbook/assets/nodequant_brand.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangshuiyong/nodequant/408e985555fbcfdb6be220acb4af95e192f27268/.gitbook/assets/nodequant_brand.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | nodequant 2 | https://github.com/zhangshuiyong/nodequant 3 | 4 | Copyright (C) 2017 by zhangshuiyong<632386504@qq.com> 5 | All rights reserved 6 | 7 | nodequant遵循Apache 2.0开源协议发布,并提供个人免费使用 8 | 9 | Apache Licence是著名的非盈利开源组织Apache采用的协议。 10 | 该协议和BSD类似,鼓励代码共享和尊重原作者的著作权, 11 | 允许代码修改,再作为开源或商业软件发布。需要满足 12 | 的条件: 13 | 1.需要给代码的用户一份Apache Licence; 14 | 2.如果你修改了代码,需要在被修改的文件中说明; 15 | 3.在延伸的代码中(修改和有源代码衍生的代码中)需要 16 | 带有原来代码中的协议,商标,专利声明和其他原来作者规 17 | 定需要包含的说明; 18 | 4.基于商业目的发布本软件之衍生物,本软件以及软件片段 19 | 需事前向原来作者取得特定书面许可; 20 | 5.如果再发布的产品中包含一个Notice文件,则在Notice文 21 | 件中需要带有本协议内容。你可以在Notice中增加自己的许可, 22 | 但不可以表现为对Apache Licence构成更改,具体的协议参 23 | 考:http://www.apache.org/licenses/LICENSE-2.0 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 28 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 29 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 30 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 31 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 32 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 33 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 35 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 | POSSIBILITY OF SUCH DAMAGE. 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NodeQuant 2 | 3 | ![](.gitbook/assets/nodequant_brand.jpg) 4 | 5 | ## NodeQuant的愿景 6 | 7 | 让Node.js社区轻巧地开发和部署量化金融交易程序,成为一个简单、高效、可依赖的量化交易平台 8 | 9 | ## NodeQuant简介 10 | 11 | 国内的量化交易平台大多是C、C++、C\#、Java、Python等语言编写量化策略。从事量化交易的人员在学会金融数据的分析的同时也要学好一门编程语言,往往学好一门编程语言对于很多人是一个不小的门槛。JavaScript语言是一门简单轻便的脚本语言,学习和编写JavaScript程序都非常简单。脚本语言具有弱类型的特点,不需要开发者在编写程序的过程中适配各种数据类型,入门快速。 12 | JavaScript有大量的开发者,它是GitHub上最热门的编程语言。JavaScript语言借助Node.js运行环境,可以使得JavaScript也可以像C++、C\#等高级语言一样运行在服务器端,可以进行读写文件,数据库,访问网络等操作。 13 | 14 | ### Github 10大最受欢迎语言 15 | 16 | ![](.gitbook/assets/github-10.png) 17 | 18 | Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 19 | 20 | Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。 21 | 22 | Node.js 的包管理器 npm,是全球最大的开源库生态系统。使用npm可以找到各种各样的第三方库,开发者可以集成到自己的程序当中。 23 | 24 | 量化金融交易程序是一种是基于高频网络访问和各种事件(OnTick,OnOrder,OnTrade)的数据密集型程序。由于Node.js非阻塞的,事件驱动的 I/O 操作等特点,使得它处理数据密集型实时应用时非常轻巧高效,可以认为是数据密集型分布式部署环境下的实时应用系统的完美解决方案。 25 | 26 | 使用Node.js来编写和运行量化交易策略程序是一个非常好的解决方案。 27 | 28 | 这就是NodeQuant量化金融交易平台诞生的背景。 29 | 30 | ## NodeQuant开发文档 31 | 32 | [NodeQuant开发文档](https://github.com/zhangshuiyong/nodequant-doc) 33 | 34 | -------------------------------------------------------------------------------- /nodequant/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | node_modules 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /nodequant/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /nodequant/NOTICE: -------------------------------------------------------------------------------- 1 | nodequant 2 | https://github.com/zhangshuiyong/nodequant 3 | 4 | Copyright (C) 2017 by zhangshuiyong<632386504@qq.com> 5 | All rights reserved 6 | 7 | nodequant遵循Apache 2.0开源协议发布,并提供个人免费使用 8 | 9 | Apache Licence是著名的非盈利开源组织Apache采用的协议。 10 | 该协议和BSD类似,鼓励代码共享和尊重原作者的著作权, 11 | 允许代码修改,再作为开源或商业软件发布。需要满足 12 | 的条件: 13 | 1.需要给代码的用户一份Apache Licence; 14 | 2.如果你修改了代码,需要在被修改的文件中说明; 15 | 3.在延伸的代码中(修改和有源代码衍生的代码中)需要 16 | 带有原来代码中的协议,商标,专利声明和其他原来作者规 17 | 定需要包含的说明; 18 | 4.基于商业目的发布本软件之衍生物,本软件以及软件片段 19 | 需事前向原来作者取得特定书面许可; 20 | 5.如果再发布的产品中包含一个Notice文件,则在Notice文 21 | 件中需要带有本协议内容。你可以在Notice中增加自己的许可, 22 | 但不可以表现为对Apache Licence构成更改,具体的协议参 23 | 考:http://www.apache.org/licenses/LICENSE-2.0 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 28 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 29 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 30 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 31 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 32 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 33 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 35 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 | POSSIBILITY OF SUCH DAMAGE. 37 | -------------------------------------------------------------------------------- /nodequant/NodeQuantApp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by majc_tom on 2017/6/11. 3 | */ 4 | 5 | require("./common"); 6 | require("./systemConfig"); 7 | require("./userConfig"); 8 | 9 | let EventEmitter = require('events').EventEmitter; 10 | let MainEngine=require("./engine/mainEngine"); 11 | let StrategyEngine=require("./engine/strategyEngine"); 12 | 13 | //事件触发器 14 | global.AppEventEmitter = new EventEmitter(); 15 | 16 | class NodeQuantApp{ 17 | constructor(application){ 18 | 19 | console.log(">>>>>>>>>> "+AppName+" <<<<<<<<<<"); 20 | console.log("Date :" + new Date(Date.now()).toLocaleTimeString()); 21 | 22 | let sendMarketOrder = require('./routes/test/sendMarketOrder'); 23 | let sendLimitOrder = require('./routes/test/sendLimitOrder'); 24 | let sendStopLimitOrder = require('./routes/test/sendStopLimitOrder'); 25 | let sendMarketIfTouchedOrder = require('./routes/test/sendMarketIfTouchedOrder'); 26 | let sendFillOrKillLimitOrder = require('./routes/test/sendFillOrKillLimitOrder'); 27 | let sendFillAndKillLimitOrder = require('./routes/test/sendFillAndKillLimitOrder'); 28 | let cancelOrder = require('./routes/test/cancelOrder'); 29 | let subscribeContract = require('./routes/test/subscribeContract'); 30 | let unSubscribeContract = require('./routes/test/unSubscribeContract'); 31 | let queryCommissionRate=require('./routes/test/queryCommissionRate'); 32 | let queryInvestorPosition = require('./routes/test/queryInvestorPosition'); 33 | let queryTradingAccount = require('./routes/test/queryTradingAccount'); 34 | let getTradingDay = require('./routes/test/getTradingDay'); 35 | let getMdApiVersion = require('./routes/test/md/getApiVersion'); 36 | let getTdApiVersion = require('./routes/test/td/getApiVersion'); 37 | let querySettlementInfo = require('./routes/test/querySettlementInfo'); 38 | 39 | application.use('/sendMarketOrder',sendMarketOrder); 40 | application.use('/sendLimitOrder',sendLimitOrder); 41 | application.use('/sendStopLimitOrder',sendStopLimitOrder); 42 | application.use('/sendMarketIfTouchedOrder',sendMarketIfTouchedOrder); 43 | application.use('/sendFillOrKillLimitOrder',sendFillOrKillLimitOrder); 44 | application.use('/sendFillAndKillLimitOrder',sendFillAndKillLimitOrder); 45 | 46 | application.use('/cancelOrder',cancelOrder); 47 | 48 | application.use('/subscribeContract',subscribeContract); 49 | application.use('/unSubscribeContract',unSubscribeContract); 50 | 51 | application.use('/queryCommissionRate',queryCommissionRate); 52 | application.use('/queryInvestorPosition',queryInvestorPosition); 53 | application.use('/queryTradingAccount',queryTradingAccount); 54 | application.use('/getTradingDay',getTradingDay); 55 | application.use('/md/getApiVersion',getMdApiVersion); 56 | application.use('/td/getApiVersion',getTdApiVersion); 57 | application.use('/querySettlementInfo',querySettlementInfo); 58 | 59 | let redis =require("redis"); 60 | //初始化配置系统数据库 61 | this.SystemDBClient = redis.createClient(System_DBConfig.Port,System_DBConfig.Host); 62 | if(System_DBConfig.Password!==undefined && System_DBConfig.Password!=="") 63 | { 64 | this.SystemDBClient.auth(System_DBConfig.Password); 65 | } 66 | 67 | this.SystemDBClient.on("error", function (err) { 68 | console.log("系统数据库出错,SystemDBClient Error " + err); 69 | }); 70 | 71 | //初始化配置行情数据库 72 | try 73 | { 74 | if(MarketData_DBConfig!==undefined && MarketData_DBConfig.Port!==undefined && MarketData_DBConfig.Host !== undefined) 75 | { 76 | let InfluxDB = require('./model/influxdb/influxdb'); 77 | this.MarketDataDBClient=new InfluxDB(); 78 | } 79 | }catch(err) 80 | { 81 | console.log("初始化行情数据库出错:" + err); 82 | } 83 | 84 | this.StrategyEngine=new StrategyEngine(); 85 | 86 | //上层引擎先实例化 87 | //主引擎负责启动底层各个客户端,还有启动上层的引擎 88 | //用于实现多策略,多品种。一个策略实例---只能交易一个品种?mq可以一个策略接收多个品种的Tick 89 | this.MainEngine=new MainEngine(); 90 | } 91 | 92 | Start() 93 | { 94 | if(this.MarketDataDBClient) 95 | { 96 | //数据库启动,主引擎才启动 97 | this.MarketDataDBClient.Start(function () { 98 | global.NodeQuant.MainEngine.Start(); 99 | }); 100 | }else 101 | { 102 | global.NodeQuant.MainEngine.Start(); 103 | } 104 | } 105 | 106 | Exit() 107 | { 108 | console.log('NodeQuant Exit'); 109 | global.NodeQuant.MainEngine.Stop(MainEngineStatus.Stop); 110 | 111 | // 关闭数据库连接(RedisDB) 112 | this.SystemDBClient.quit(); 113 | } 114 | } 115 | 116 | module.exports = NodeQuantApp; -------------------------------------------------------------------------------- /nodequant/README.md: -------------------------------------------------------------------------------- 1 | # NodeQuant:一个基于Node.js的开源量化金融交易平台 2 | 3 | 4 | 5 | ## NodeQuant系统架构 6 | - **Node.js** —— NodeQuant系统是一个基于Node.js的量化交易平台。JavaScript语言也可以开发量化金融交易策略。 7 | - **Express** —— NodeQuant系统使用了Node.js平台的Express网络框架。可以使用Express框架将NodeQuant扩展成为一个互联网量化交易平台 8 | - **NodeQuant** —— NodeQuant系统的核心是NodeQuant量化交易框架。使用NodeQuant量化交易框架,可以运行多个量化交易策略,可以进行多品种、多市场的趋势、套利交易 9 | - **Redis** —— NodeQuant系统使用Redis内存数据库,所有交易数据都保存在内存中,极速响应实盘交易策略 10 | 11 | 12 | ## NodeQuant量化交易框架 13 | 14 | 1. **策略层**:策略层代表各种量化策略实例。策略实例负责运行交易算法,集成了交易事件响应函数,有OnTick、OnNewBar、OnCloseBar、OnOrder、OnTrade等事件响应函数,驱动量化策略的运行。 15 | 2. **策略引擎层**:NodeQuant系统运行会唯一创建一个策略引擎实例,策略引擎层代表的就是这个策略引擎。策略引擎开始运行会根据用户的策略配置创建策略实例,也可以停止策略实例。响应策略的下单命令,推送策略订阅的合约行情,保存策略运行的仓位、订单、成交、结算信息等状态信息,接收交易Client的订单回报,成交回报。 16 | 3. **主引擎层**:NodeQuant系统运行会唯一创建一个主引擎实例,主引擎层代表的就是这个主引擎。主引擎负责启动上层策略引擎和连接底层的各个交易Client,接收来自底层交易Cient的行情信息,并且响应上层策略引擎的下单命令。 17 | 4. **交易客户端层**:交易客户端层代表着各个交易客户端。连接着各种证券交易所,商品交易所,接收推送来自交易所的行情信息,合约信息。同时,响应上层主引擎的在各个交易客户端的下单命令。目前已经实现了CTP Client,可交易中金所、上期所、大商所,郑商所的合约。 18 | 19 | ## NodeQuant 最新特性 20 | - 支持上期CTP的API客户端(**Windows Node.js-8-32位、Windows Node.js-8-64位、Linux x64 Node.js-8**)。可交易中金所、上期所、大商所、郑商所的所有期货品种合约 21 | - 支持飞鼠Sgit的API客户端(**Windows Node.js-8-32位、Linux x64 Node.js-8**)。可交易中金所、上期所、大商所、郑商所的所有期货品种合约,并且可交易上海黄金交易所的现货合约。可程序化交易现货黄金、白银。 22 | - 支持一个账号 —— 多策略,支持一个账号多个策略的量化产品模式 23 | - 支持一个策略 —— 多合约,支持趋势、套利交易 24 | - 支持一个策略 —— 多市场,支持跨市场交易、套利 25 | - 支持交易K线周期:秒、分钟、小时 26 | - 支持交易所支持的多种类型订单:限价单,市价单,FAK单,FOK单,条件单。可灵活使用于趋势,套利,钓鱼等交易策略 27 | - 支持盘后自动计算策略盈亏净值 28 | - 极速响应实盘交易。使用Redis内存数据库,记录与查询交易信息 29 | - 支持可视化策略运行状态。使用Redis数据库Windows图形化客户端查看策略运行交易数据,可以查看本地和云服务器的策略运行状态 30 | - 无人值守。支持配置非交易时间自动停止,交易时间自动启动交易策略 31 | - 支持打包加密策略 32 | - 更小的滑点成本。Windows系统中对CTP交易客户端进行测试,系统内Tick-To-FinishSendOrder平均耗时:1.5ms(基于python的vn.py平均耗时22.6ms) 33 | 34 | ## NodeQuant 2.0即将来到的特性 35 | - 支持连接Tick数据行情服务器,使得策略可预先加载Tick,分钟行情数据。方便策略获取预处理数据 36 | - 支持策略运行异常邮件通知 37 | 38 | ## 入门教程 39 | 40 | [NodeQuant开发文档](https://github.com/zhangshuiyong/nodequant-doc/blob/master/SUMMARY.md) 41 | 42 | ## Node.js科学计算库 43 | 44 | - [mathjs](https://github.com/josdejong/mathjs) - An extensive math library for JavaScript and Node.js. 45 | - [numeric](https://github.com/sloisel/numeric) - Numerical analysis in Javascript. 46 | - [numbers.js](https://github.com/numbers/numbers.js) - Advanced Mathematics Library for Node.js and JavaScript. 47 | - [jmat](https://github.com/lvandeve/jmat) - Complex special functions, numerical linear algebra and statistics in JavaScript. 48 | - [accounting.js](https://github.com/openexchangerates/accounting.js) - A lightweight JavaScript library for number, money and currency formatting - fully localisable, zero dependencies. 49 | - [science.js](https://github.com/jasondavies/science.js) - Scientific and statistical computing in JavaScript. 50 | - [linearReg.js](https://github.com/lastlegion/linearReg.js) - Linear regression with Gradient descent package for NPM. 51 | - [sylvester](https://github.com/jcoglan/sylvester) - Vector, matrix and geometry math JavaScript http://sylvester.jcoglan.com 52 | - [node-svm](https://github.com/nicolaspanel/node-svm) - Support Vector Machines for nodejs 53 | - [numjs](https://github.com/nicolaspanel/numjs) - Like NumPy, in JavaScript 54 | 55 | ## Node.js多线程库 56 | - [node-webworker-threads](https://github.com/audreyt/node-webworker-threads) - Lightweight Web Worker API implementation with native threads 57 | 58 | ## Node.js多进程库 59 | - [Cluster](https://nodejs.org/dist/latest-v6.x/docs/api/cluster.html) - A single instance of Node.js runs in a single thread. To take advantage of multi-core systems the user will sometimes want to launch a cluster of Node.js processes to handle the load. 60 | 61 | ## Node.js的机器学习库 62 | - [machine_learning](https://github.com/junku901/machine_learning) - Machine learning library for Node.js http://joonku.com/project/machine_learning 63 | 64 | - [各语言机器学习库整理](https://www.douban.com/group/topic/89453462/) 65 | 66 | 67 | ## Javascript前端图形库 68 | 69 | - [echarts 3](http://echarts.baidu.com/) 70 | 71 | ## 技术交流 72 | 73 | 欢迎有兴趣使用NodeQuant做量化金融策略的Quanter加入QQ群进行交流和建议 74 | 75 | QQ群:197110856 76 | 77 | ## 贡献 78 | - @James-F-Zhang 79 | -------------------------------------------------------------------------------- /nodequant/app.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let path = require('path'); 3 | let favicon = require('serve-favicon'); 4 | let logger = require('morgan'); 5 | let cookieParser = require('cookie-parser'); 6 | let bodyParser = require('body-parser'); 7 | 8 | let index = require('./routes/index'); 9 | 10 | let app = express(); 11 | 12 | //启动NodeQuant,绑定请求路由 13 | let NodeQuantApp=require("./NodeQuantApp"); 14 | let NodeQuantApplication = new NodeQuantApp(app); 15 | global.NodeQuant = NodeQuantApplication; 16 | global.NodeQuant.Start(); 17 | 18 | // view engine setup 19 | app.set('views', path.join(__dirname, 'views')); 20 | app.set('view engine', 'ejs'); 21 | 22 | // uncomment after placing your favicon in /public 23 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 24 | app.use(logger('dev')); 25 | app.use(bodyParser.json()); 26 | app.use(bodyParser.urlencoded({ extended: false })); 27 | app.use(cookieParser()); 28 | app.use(express.static(path.join(__dirname, 'public'))); 29 | 30 | 31 | app.use('/', index); 32 | 33 | // catch 404 and forward to error handler 34 | app.use(function(req, res, next) { 35 | let err = new Error('Not Found'); 36 | err.status = 404; 37 | next(err); 38 | }); 39 | 40 | // error handler 41 | app.use(function(err, req, res, next) { 42 | // set locals, only providing error in development 43 | res.locals.message = err.message; 44 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 45 | 46 | // render the error page 47 | res.status(err.status || 500); 48 | res.render('error'); 49 | }); 50 | 51 | 52 | process.on('uncaughtException',function (err) { 53 | //打印出错误 54 | console.log("uncaughtException:"); 55 | //打印出错误的调用栈方便调试 56 | console.log(err.stack); 57 | }); 58 | 59 | process.on('exit',function (code) { 60 | console.log(code); 61 | }); 62 | 63 | process.on('SIGINT', function() { 64 | 65 | //2秒后程序退出,这两秒需要做程序状态记录 66 | global.NodeQuant.Exit(); 67 | 68 | setTimeout(function () { 69 | console.log('Got SIGINT. Press Control-D/Control-C to exit.'); 70 | process.exit(); 71 | },2*1000); 72 | 73 | }); 74 | 75 | 76 | 77 | module.exports = app; 78 | -------------------------------------------------------------------------------- /nodequant/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('nodequantjs:server'); 9 | var http = require('http'); 10 | 11 | require("../systemConfig"); 12 | 13 | /** 14 | * Get port from environment and store in Express. 15 | */ 16 | 17 | var port = normalizePort(process.env.PORT || AppConfig.Port); 18 | app.set('port', port); 19 | 20 | /** 21 | * Create HTTP server. 22 | */ 23 | 24 | var server = http.createServer(app); 25 | 26 | /** 27 | * Listen on provided port, on all network interfaces. 28 | */ 29 | 30 | server.listen(port); 31 | server.on('error', onError); 32 | server.on('listening', onListening); 33 | 34 | /** 35 | * Normalize a port into a number, string, or false. 36 | */ 37 | 38 | function normalizePort(val) { 39 | var port = parseInt(val, 10); 40 | 41 | if (isNaN(port)) { 42 | // named pipe 43 | return val; 44 | } 45 | 46 | if (port >= 0) { 47 | // port number 48 | return port; 49 | } 50 | 51 | return false; 52 | } 53 | 54 | /** 55 | * Event listener for HTTP server "error" event. 56 | */ 57 | 58 | function onError(error) { 59 | if (error.syscall !== 'listen') { 60 | throw error; 61 | } 62 | 63 | var bind = typeof port === 'string' 64 | ? 'Pipe ' + port 65 | : 'Port ' + port; 66 | 67 | // handle specific listen errors with friendly messages 68 | switch (error.code) { 69 | case 'EACCES': 70 | console.error(bind + ' requires elevated privileges'); 71 | process.exit(1); 72 | break; 73 | case 'EADDRINUSE': 74 | console.error(bind + ' is already in use'); 75 | process.exit(1); 76 | break; 77 | default: 78 | throw error; 79 | } 80 | } 81 | 82 | /** 83 | * Event listener for HTTP server "listening" event. 84 | */ 85 | 86 | function onListening() { 87 | var addr = server.address(); 88 | var bind = typeof addr === 'string' 89 | ? 'pipe ' + addr 90 | : 'port ' + addr.port; 91 | debug('Listening on ' + bind); 92 | } 93 | -------------------------------------------------------------------------------- /nodequant/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/5/21. 3 | */ 4 | 5 | 6 | 7 | ///////////////////////////////////////////////////////////////////////// 8 | ///TFtdcDirectionType是一个买卖方向类型 9 | ///////////////////////////////////////////////////////////////////////// 10 | ///买 11 | //#define THOST_FTDC_D_Buy '0' 12 | ///卖 13 | //#define THOST_FTDC_D_Sell '1' 14 | 15 | Direction={Buy:'0',Sell:'1'}; 16 | DirectionReverse={'0':"Buy",'1':"Sell"}; 17 | 18 | ///////////////////////////////////////////////////////////////////////// 19 | ///TFtdcOffsetFlagType是一个开平标志类型 20 | ///////////////////////////////////////////////////////////////////////// 21 | ///开仓 22 | //#define THOST_FTDC_OF_Open '0' 23 | ///平仓 24 | //#define THOST_FTDC_OF_Close '1' 25 | ///强平 26 | //#define THOST_FTDC_OF_ForceClose '2' 27 | ///平今 28 | //#define THOST_FTDC_OF_CloseToday '3' 29 | ///平昨 30 | //#define THOST_FTDC_OF_CloseYesterday '4' 31 | ///强减 32 | //#define THOST_FTDC_OF_ForceOff '5' 33 | ///本地强平 34 | //#define THOST_FTDC_OF_LocalForceClose '6' 35 | 36 | //客户端只有4 种开平类型 37 | OpenCloseFlagType={Open:'0',Close:'1',CloseToday:'3',CloseYesterday:'4'}; 38 | OpenCloseFlagReverseType={'0':"Open",'1':"Close",'3':"CloseToday",'4':"CloseYesterday"}; 39 | ///////////////////////////////////////////////////////////////////////// 40 | ///TFtdcHedgeFlagType是一个投机套保标志类型 41 | ///////////////////////////////////////////////////////////////////////// 42 | ///投机 43 | //#define THOST_FTDC_HF_Speculation '1' 44 | ///套利 45 | //#define THOST_FTDC_HF_Arbitrage '2' 46 | ///套保 47 | //#define THOST_FTDC_HF_Hedge '3' 48 | ///做市商 49 | //#define THOST_FTDC_HF_MarketMaker '5' 50 | 51 | HedgeFlagType = {Speculation:'1',Arbitrage:'2',Hedge:'3',MarketMaker:'5'}; 52 | 53 | 54 | 55 | ///////////////////////////////////////////////////////////////////////// 56 | ///TFtdcOrderPriceTypeType是一个报单价格条件类型 57 | ///////////////////////////////////////////////////////////////////////// 58 | ///任意价 59 | //#define THOST_FTDC_OPT_AnyPrice '1' 60 | ///限价 61 | //#define THOST_FTDC_OPT_LimitPrice '2' 62 | ///最优价 63 | //#define THOST_FTDC_OPT_BestPrice '3' 64 | ///最新价 65 | //#define THOST_FTDC_OPT_LastPrice '4' 66 | ///最新价浮动上浮1个ticks 67 | //#define THOST_FTDC_OPT_LastPricePlusOneTicks '5' 68 | ///最新价浮动上浮2个ticks 69 | //#define THOST_FTDC_OPT_LastPricePlusTwoTicks '6' 70 | ///最新价浮动上浮3个ticks 71 | //#define THOST_FTDC_OPT_LastPricePlusThreeTicks '7' 72 | ///卖一价 73 | //#define THOST_FTDC_OPT_AskPrice1 '8' 74 | ///卖一价浮动上浮1个ticks 75 | //#define THOST_FTDC_OPT_AskPrice1PlusOneTicks '9' 76 | ///卖一价浮动上浮2个ticks 77 | //#define THOST_FTDC_OPT_AskPrice1PlusTwoTicks 'A' 78 | ///卖一价浮动上浮3个ticks 79 | //#define THOST_FTDC_OPT_AskPrice1PlusThreeTicks 'B' 80 | ///买一价 81 | //#define THOST_FTDC_OPT_BidPrice1 'C' 82 | ///买一价浮动上浮1个ticks 83 | //#define THOST_FTDC_OPT_BidPrice1PlusOneTicks 'D' 84 | ///买一价浮动上浮2个ticks 85 | //#define THOST_FTDC_OPT_BidPrice1PlusTwoTicks 'E' 86 | ///买一价浮动上浮3个ticks 87 | //#define THOST_FTDC_OPT_BidPrice1PlusThreeTicks 'F' 88 | ///五档价 89 | //#define THOST_FTDC_OPT_FiveLevelPrice 'G' 90 | 91 | OrderPriceType={ 92 | AnyPrice:'1', 93 | LimitPrice:'2', 94 | BestPrice:'3', 95 | LastPrice:'4', 96 | LastPricePlusOneTicks:'5', 97 | LastPricePlusTwoTicks:'6', 98 | LastPricePlusThreeTicks:'7', 99 | AskPrice1:'8', 100 | AskPrice1PlusOneTicks:'9', 101 | AskPrice1PlusTwoTicks:'A', 102 | AskPrice1PlusThreeTicks:'B', 103 | BidPrice1:'C', 104 | BidPrice1PlusOneTicks:'D', 105 | BidPrice1PlusTwoTicks:'E', 106 | BidPrice1PlusThreeTicks:'F', 107 | FiveLevelPrice:'G' 108 | }; 109 | 110 | 111 | ///////////////////////////////////////////////////////////////////////// 112 | ///TFtdcTimeConditionType是一个有效期类型类型 113 | ///////////////////////////////////////////////////////////////////////// 114 | ///立即完成,否则撤销 115 | //#define THOST_FTDC_TC_IOC '1' 116 | ///本节有效 117 | //#define THOST_FTDC_TC_GFS '2' 118 | ///当日有效 119 | //#define THOST_FTDC_TC_GFD '3' 120 | ///指定日期前有效 121 | //#define THOST_FTDC_TC_GTD '4' 122 | ///撤销前有效 123 | //#define THOST_FTDC_TC_GTC '5' 124 | ///集合竞价有效 125 | //#define THOST_FTDC_TC_GFA '6' 126 | 127 | TimeConditionType={ 128 | ImmediatelyOrCancel:'1', 129 | GoodForSection:'2', 130 | GoodForDay:'3', 131 | GoodTillDate:'4', 132 | GoodTillCancel:'5', 133 | GoodForAuction:'6'}; 134 | 135 | 136 | 137 | ///////////////////////////////////////////////////////////////////////// 138 | ///TFtdcContingentConditionType是一个触发条件类型 139 | ///////////////////////////////////////////////////////////////////////// 140 | ///立即 141 | //#define THOST_FTDC_CC_Immediately '1' 142 | ///止损 143 | //#define THOST_FTDC_CC_Touch '2' 144 | ///止赢 145 | //#define THOST_FTDC_CC_TouchProfit '3' 146 | ///预埋单 147 | //#define THOST_FTDC_CC_ParkedOrder '4' 148 | ///最新价大于条件价 149 | //#define THOST_FTDC_CC_LastPriceGreaterThanStopPrice '5' 150 | ///最新价大于等于条件价 151 | //#define THOST_FTDC_CC_LastPriceGreaterEqualStopPrice '6' 152 | ///最新价小于条件价 153 | //#define THOST_FTDC_CC_LastPriceLesserThanStopPrice '7' 154 | ///最新价小于等于条件价 155 | //#define THOST_FTDC_CC_LastPriceLesserEqualStopPrice '8' 156 | ///卖一价大于条件价 157 | //#define THOST_FTDC_CC_AskPriceGreaterThanStopPrice '9' 158 | ///卖一价大于等于条件价 159 | //#define THOST_FTDC_CC_AskPriceGreaterEqualStopPrice 'A' 160 | ///卖一价小于条件价 161 | //#define THOST_FTDC_CC_AskPriceLesserThanStopPrice 'B' 162 | ///卖一价小于等于条件价 163 | //#define THOST_FTDC_CC_AskPriceLesserEqualStopPrice 'C' 164 | ///买一价大于条件价 165 | //#define THOST_FTDC_CC_BidPriceGreaterThanStopPrice 'D' 166 | ///买一价大于等于条件价 167 | //#define THOST_FTDC_CC_BidPriceGreaterEqualStopPrice 'E' 168 | ///买一价小于条件价 169 | //#define THOST_FTDC_CC_BidPriceLesserThanStopPrice 'F' 170 | ///买一价小于等于条件价 171 | //#define THOST_FTDC_CC_BidPriceLesserEqualStopPrice 'H' 172 | ContingentConditionType={ 173 | Immediately:'1', 174 | Touch:'2', 175 | TouchProfit:'3', 176 | ParkedOrder:'4', 177 | LastPriceGreaterThanStopPrice:'5', 178 | LastPriceGreaterEqualStopPrice:'6', 179 | LastPriceLesserThanStopPrice:'7', 180 | LastPriceLesserEqualStopPrice:'8', 181 | AskPriceGreaterThanStopPrice:'9', 182 | AskPriceGreaterEqualStopPrice:'A', 183 | AskPriceLesserThanStopPrice:'B', 184 | AskPriceLesserEqualStopPrice:'C', 185 | BidPriceGreaterThanStopPrice:'D', 186 | BidPriceGreaterEqualStopPrice:'E', 187 | BidPriceLesserThanStopPrice:'F', 188 | BidPriceLesserEqualStopPrice:'H'}; 189 | 190 | ContingentConditionReverseType={ 191 | '1':"Immediately", 192 | '2':"Touch", 193 | '3':"TouchProfit", 194 | '4':"ParkedOrder", 195 | '5':"LastPriceGreaterThanStopPrice", 196 | '6':"LastPriceGreaterEqualStopPrice", 197 | '7':"LastPriceLesserThanStopPrice", 198 | '8':"LastPriceLesserEqualStopPrice", 199 | '9':"AskPriceGreaterThanStopPrice", 200 | 'A':"AskPriceGreaterEqualStopPrice", 201 | 'B':"AskPriceLesserThanStopPrice", 202 | 'C':"AskPriceLesserEqualStopPrice", 203 | 'D':"BidPriceGreaterThanStopPrice", 204 | 'E':"BidPriceGreaterEqualStopPrice", 205 | 'F':"BidPriceLesserThanStopPrice", 206 | 'H':"BidPriceLesserEqualStopPrice" 207 | }; 208 | 209 | ///////////////////////////////////////////////////////////////////////// 210 | ///TFtdcVolumeConditionType是一个成交量类型类型 211 | ///////////////////////////////////////////////////////////////////////// 212 | ///任何数量 213 | //#define THOST_FTDC_VC_AV '1' 214 | ///最小数量 215 | //#define THOST_FTDC_VC_MV '2' 216 | ///全部数量 217 | //#define THOST_FTDC_VC_CV '3' 218 | 219 | VolumeConditionType={AnyVolume:'1',MinVolume:'2',CompleteVolume:'3'}; 220 | 221 | 222 | ///////////////////////////////////////////////////////////////////////// 223 | ///TFtdcForceCloseReasonType是一个强平原因类型 224 | ///////////////////////////////////////////////////////////////////////// 225 | ///非强平 226 | //#define THOST_FTDC_FCC_NotForceClose '0' 227 | ///资金不足 228 | //#define THOST_FTDC_FCC_LackDeposit '1' 229 | ///客户超仓 230 | //#define THOST_FTDC_FCC_ClientOverPositionLimit '2' 231 | ///会员超仓 232 | //#define THOST_FTDC_FCC_MemberOverPositionLimit '3' 233 | ///持仓非整数倍 234 | //#define THOST_FTDC_FCC_NotMultiple '4' 235 | ///违规 236 | //#define THOST_FTDC_FCC_Violation '5' 237 | ///其它 238 | //#define THOST_FTDC_FCC_Other '6' 239 | ///自然人临近交割 240 | //#define THOST_FTDC_FCC_PersonDeliv '7' 241 | ForceCloseReasonType={ 242 | NotForceClose:'0', 243 | LackDeposit:'1', 244 | ClientOverPositionLimit:'2', 245 | MemberOverPositionLimit:'3', 246 | NotMultiple:'4', 247 | Violation:'5', 248 | Other:'6', 249 | PersonDeliv:'7' 250 | }; 251 | 252 | 253 | ///////////////////////////////////////////////////////////////////////// 254 | ///TFtdcProductClassType是一个产品类型类型 255 | ///////////////////////////////////////////////////////////////////////// 256 | ///期货 257 | //#define THOST_FTDC_PC_Futures '1' 258 | ///期货期权 259 | //#define THOST_FTDC_PC_Options '2' 260 | ///组合 261 | //#define THOST_FTDC_PC_Combination '3' 262 | ///即期 263 | //#define THOST_FTDC_PC_Spot '4' 264 | ///期转现 265 | //#define THOST_FTDC_PC_EFP '5' 266 | ///现货期权 267 | //#define THOST_FTDC_PC_SOrpotOption '6' 268 | ProductClassType={Futures:'1',Options:'2',Combination:'3',Spot:'4',EFP:'5',SpotOption:'6'}; 269 | ProductClassReverseType={'1':'Futures','2':'Options','3':'Combination','4':'Spot','5':'EFP','6':'SpotOption'}; 270 | //交易所类型 271 | ExchangeType = { 272 | EXCHANGE_CFFEX:'CFFEX', 273 | EXCHANGE_SHFE: 'SHFE', 274 | EXCHANGE_CZCE:'CZCE', 275 | EXCHANGE_DCE:'DCE', 276 | EXCHANGE_SSE:'SSE', 277 | EXCHANGE_INE:'INE', 278 | EXCHANGE_UNKNOWN:''}; 279 | 280 | 281 | 282 | ///////////////////////////////////////////////////////////////////////// 283 | ///TFtdcOrderStatusType是一个报单状态类型 284 | ///////////////////////////////////////////////////////////////////////// 285 | ///全部成交 286 | //#define THOST_FTDC_OST_AllTraded '0' 287 | ///部分成交还在队列中 288 | //#define THOST_FTDC_OST_PartTradedQueueing '1' 289 | ///部分成交不在队列中 290 | //#define THOST_FTDC_OST_PartTradedNotQueueing '2' 291 | ///未成交还在队列中 292 | //#define THOST_FTDC_OST_NoTradeQueueing '3' 293 | ///未成交不在队列中 294 | //#define THOST_FTDC_OST_NoTradeNotQueueing '4' 295 | ///撤单 296 | //#define THOST_FTDC_OST_Canceled '5' 297 | ///未知 298 | //#define THOST_FTDC_OST_Unknown 'a' 299 | ///尚未触发 300 | //#define THOST_FTDC_OST_NotTouched 'b' 301 | ///已触发 302 | //#define THOST_FTDC_OST_Touched 'c' 303 | 304 | OrderStatusReverseType={ 305 | "0":"AllTraded", 306 | "1":"PartTradedQueueing", 307 | "2":"PartTradedNotQueueing", 308 | "3":"NoTradeQueueing", 309 | "4":"NoTradeNotQueueing", 310 | "5":"Canceled", 311 | "a":"Unknown", 312 | "b":'NotTouched', 313 | "c":'Touched' 314 | }; 315 | 316 | OrderStatusType={ 317 | AllTraded:'0', 318 | PartTradedQueueing:'1', 319 | PartTradedNotQueueing:'2', 320 | NoTradeQueueing:'3', 321 | NoTradeNotQueueing:'4', 322 | Canceled:'5', 323 | Unknown:'a', 324 | NotTouched:'b', 325 | Touched:'c' 326 | }; 327 | 328 | 329 | ///////////////////////////////////////////////////////////////////////// 330 | ///TFtdcOrderActionStatusType是一个报单操作状态类型 331 | ///////////////////////////////////////////////////////////////////////// 332 | ///已经提交 333 | //#define THOST_FTDC_OAS_Submitted 'a' 334 | ///已经接受 335 | //#define THOST_FTDC_OAS_Accepted 'b' 336 | ///已经被拒绝 337 | //#define THOST_FTDC_OAS_Rejected 'c' 338 | //撤单的状态 339 | CancelOrderActionStatusType={ 340 | Submitted:'a', 341 | Accepted:'b', 342 | Rejected:'c' 343 | }; 344 | 345 | ///////////////////////////////////////////////////////////////////////// 346 | ///TFtdcPosiDirectionType是一个持仓多空方向类型 347 | ///////////////////////////////////////////////////////////////////////// 348 | ///净 349 | //#define THOST_FTDC_PD_Net '1' 350 | ///多头 351 | //#define THOST_FTDC_PD_Long '2' 352 | ///空头 353 | //#define THOST_FTDC_PD_Short '3' 354 | 355 | PosiDirectionReverseType={ 356 | '1':"Net", 357 | '2':"Long", 358 | '3':"Short" 359 | }; 360 | 361 | //6种订单类型 362 | OrderType={ 363 | Limit:"0", 364 | Condition:"1", 365 | FOK:"2", 366 | FAK:"3", 367 | Market:"4", 368 | MarketIfTouched:"5" 369 | }; 370 | 371 | OrderReverseType={ 372 | "0":"Limit", 373 | "1":"Condition", 374 | "2":"FOK", 375 | "3":"FAK", 376 | "4":"Market", 377 | "5":"MarketIfTouched" 378 | }; 379 | 380 | //事件定义 381 | EVENT={ 382 | OnLog:"EventLog", 383 | OnError:"EventError", 384 | OnContract:"EventContract", 385 | OnTick:"EventTick", 386 | OnOrder:"EventOrder", 387 | OnTrade:"EventTraded", 388 | OnCreateStrategyFailed:"EventCreateStrategyFailed", 389 | 390 | OnQueryPosition:"EventQueryPosition", 391 | OnQueryTradingAccount:"EventQueryTradingAccount", 392 | OnQueryCommissionRate:"EventQueryCommissionRate", 393 | OnQueryDeferFeeRate:"EventQueryDeferFeeRate", 394 | 395 | //连接 396 | OnDisconnected:"EventDisconnected", 397 | OnConnected:"EventConnected", 398 | //所有配置的交易客户端都连接上的事件 399 | OnAllConfigClientReadyed:"OnAllConfigClientReadyed", 400 | 401 | 402 | //是否订阅合约成功 403 | OnSubscribeContract:"EventSubscribeContract", 404 | OnUnSubscribeContract:"EventUnSubscribeContract", 405 | 406 | //合约查询后,所有合约接收完成 407 | OnReceivedAllContract:"EventReceivedAllContract", 408 | 409 | //重新连接客户端事件 410 | OnReconnected:"EventReconnectedClient", 411 | 412 | //结算信息 413 | OnQuerySettlementInfo:"EventQuerySettlementInfo", 414 | }; 415 | 416 | //Disconnect ReasonID 417 | /// 0x1001 4097 网络读失败 418 | /// 0x1002 4098 网络写失败 419 | /// 0x2001 8193 接收心跳超时 420 | /// 0x2002 8194 发送心跳失败 421 | /// 0x2003 8195 收到错误报文 422 | DisconnectReason={ 423 | NetworkWriteError:4097, 424 | NetworkReadError:4098, 425 | HeartBeatTimeout:8193, 426 | HeartBeatSendFailed:8194, 427 | ErrorMessage:8195 428 | }; 429 | 430 | //K线类型 431 | KBarType={ 432 | Tick:"TickKBar", 433 | Second:"SecondKBar", 434 | Minute:"MinuteKBar", 435 | Hour:"HourKBar", 436 | Day:"DayKBar" 437 | }; 438 | 439 | //手续费类型 440 | FeeType={ 441 | //成交手数 442 | ByVolume:"TradeByVolume", 443 | //成交金额 444 | ByMoney:"TradeByMoney" 445 | }; 446 | 447 | //出现错误的类型 448 | ErrorType={ 449 | Disconnected:"Disconnected with trade client", 450 | OperationAfterDisconnected:"Operation After Disconnected.Such as SendOrder after Disconnected", 451 | ClientRspError:"Error from the trade client response", 452 | RejectedOrder:"The order is rejected for some reason", 453 | StrategyError:"Strategy error happended", 454 | DBError:"MongoDB error happended" 455 | }; 456 | 457 | //日志Log的级别,只有两个Info,Error 458 | LogType={ 459 | INFO:"Info", 460 | ERROR:"Error" 461 | }; 462 | 463 | //主引擎的状态类型,开启,晚上收盘,白天收盘(要计算净值) 464 | MainEngineStatus={ 465 | Start:0, 466 | DayStop:1, 467 | NightStop:2, 468 | Stop:3 469 | }; 470 | 471 | AppName="NodeQuant"; 472 | 473 | Tick_DB_Name=AppName+"_Tick_DB"; 474 | System_Log_DB=AppName+"_System_Log"; 475 | System_Error_DB=AppName+"_System_Error"; 476 | -------------------------------------------------------------------------------- /nodequant/engine/mainEngine.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/12. 3 | */ 4 | let path=require("path"); 5 | require("../common.js"); 6 | require("../systemConfig"); 7 | require("../userConfig.js"); 8 | 9 | let NodeQuantLog=require("../util/NodeQuantLog"); 10 | let NodeQuantError=require("../util/NodeQuantError"); 11 | 12 | let ClientFactory=require("../model/client/ClientFactory"); 13 | 14 | function _isTimeToWork() { 15 | let systemConfigPath=path.resolve(__dirname+"/../systemConfig.js"); 16 | require.cache[systemConfigPath]=null; 17 | require("../systemConfig"); 18 | 19 | let NowDateTime=new Date(); 20 | let NowDateStr=NowDateTime.toLocaleDateString(); 21 | 22 | //夜盘结束时间 23 | let NightStopTimeStr=NowDateStr+" "+ SystemConfig.NightStopTime; 24 | let NightStopDateTime=new Date(NightStopTimeStr); 25 | 26 | 27 | //自然日的周末,周六的凌晨停止时间以后不用工作 28 | let weekDay=NowDateTime.getDay(); 29 | if(weekDay<1) 30 | { 31 | return MainEngineStatus.Stop; 32 | }else if(weekDay===6) 33 | { 34 | //周六凌晨03:00以后就不工作 35 | if(NightStopDateTimeDayStartDateTime && NightStopDateTime { 27 | if (!names.includes(Tick_DB_Name)) { 28 | return global.NodeQuant.MarketDataDBClient.influx.createDatabase(Tick_DB_Name); 29 | } 30 | }).then(() => { 31 | callback(); 32 | }).catch(err => { 33 | console.error(`Error createDatabase to InfluxDB! ${err.stack}`) 34 | }); 35 | } 36 | 37 | RecordTick(symbol,tick) 38 | { 39 | let point={ 40 | measurement: symbol, 41 | tags: {tradingDay: tick.date}, 42 | fields: { 43 | symbol: tick.symbol, 44 | exchange: tick.exchange, 45 | lastPrice: tick.lastPrice, //Tick的最新价 46 | closePrice: tick.closePrice, //Tick收盘价等于Tick的最新价 47 | openPrice: tick.openPrice, //Tick开盘价等于1天的开盘价 48 | highPrice: tick.highPrice, //Tick最高价是目前为止的行情的最高价 49 | lowPrice: tick.lowPrice, //Tick最低价是目前为止的行情的最低价 50 | preClosePrice: tick.preClosePrice, //Tick前一天的收盘价 51 | volume: tick.volume, 52 | openInterest: tick.openInterest, 53 | upperLimit: tick.upperLimit, 54 | lowerLimit: tick.lowerLimit, 55 | //自定义数据 56 | clientName: tick.clientName, 57 | date: tick.date, 58 | actionDate: tick.actionDate, 59 | timeStr: tick.timeStr, 60 | datetime: tick.datetime.toLocaleString(), 61 | actionDatetime: tick.actionDatetime.toLocaleString(), 62 | timeStamp: tick.timeStamp, 63 | Id: tick.Id, 64 | //五档买价 65 | bidPrice1: tick.bidPrice1, 66 | bidVolume1: tick.bidVolume1, 67 | bidPrice2: tick.bidPrice2, 68 | bidVolume2: tick.bidVolume2, 69 | bidPrice3: tick.bidPrice3, 70 | bidVolume3: tick.bidVolume3, 71 | bidPrice4: tick.bidPrice4, 72 | bidVolume4: tick.bidVolume4, 73 | bidPrice5: tick.bidPrice5, 74 | bidVolume5: tick.bidVolume5, 75 | //五档卖价格 76 | askPrice1: tick.askPrice1, 77 | askVolume1: tick.askVolume1, 78 | askPrice2: tick.askPrice2, 79 | askVolume2: tick.askVolume2, 80 | askPrice3: tick.askPrice3, 81 | askVolume3: tick.askVolume3, 82 | askPrice4: tick.askPrice4, 83 | askVolume4: tick.askVolume4, 84 | askPrice5: tick.askPrice5, 85 | askVolume5: tick.askVolume5 86 | }, 87 | timestamp: tick.Id, 88 | }; 89 | 90 | this.pointsBuffer.push(point); 91 | 92 | if(this.BufferInProgress) 93 | return; 94 | 95 | if(this.pointsBuffer.length>=this.BufferSize) { 96 | this.BufferInProgress=true; 97 | this.influx.writePoints(this.pointsBuffer.slice(0,this.BufferSize), { 98 | precision: 'ms' 99 | }).then(() => { 100 | this.BufferInProgress=false; 101 | this.pointsBuffer.splice(0, this.BufferSize); 102 | }).catch(err => { 103 | this.BufferInProgress=false; 104 | console.error(`Error saving ticks to InfluxDB! ${err.stack}`) 105 | }); 106 | } 107 | } 108 | 109 | RecordLog(log) 110 | { 111 | this.influx.writePoints([ 112 | { 113 | measurement:System_Log_DB, 114 | fields: { 115 | Source:log.Source, 116 | Type:log.Type, 117 | Message:log.Message, 118 | Datetime:log.Datetime 119 | } 120 | } 121 | ]).catch(err => { 122 | console.error(`Error saving log to InfluxDB! ${err.stack}`) 123 | }); 124 | } 125 | 126 | RecordError(error) 127 | { 128 | this.influx.writePoints([ 129 | { 130 | measurement:System_Error_DB, 131 | fields: { 132 | Source:error.Source, 133 | Type:error.Type, 134 | Message:error.Message 135 | } 136 | } 137 | ]).catch(err => { 138 | console.error(`Error saving error to InfluxDB! ${err.stack}`) 139 | }); 140 | } 141 | 142 | //获取单个合约的交易日列表 143 | GetTradingDayList(measurment,callback) 144 | { 145 | let influxQL="select distinct(date) from \""+measurment+"\""; 146 | this.influx.query(influxQL).then(dateResult => { 147 | let allDayCount = dateResult.length; 148 | 149 | let TradingDayList=[]; 150 | for(let i=0;i { 156 | console.log("Error: get Trading Days in "+measurment); 157 | console.log(err); 158 | callback(undefined); 159 | }); 160 | } 161 | 162 | //获取最新交易日 163 | GetLastTradingDay(measurment,callback) 164 | { 165 | let influxQL="select last(date) from \""+measurment+"\""; 166 | this.influx.query(influxQL).then(lastTradingDayResult => { 167 | 168 | if(lastTradingDayResult.length>0) 169 | { 170 | callback(lastTradingDayResult[0].last); 171 | }else 172 | { 173 | callback(""); 174 | } 175 | }).catch(err => { 176 | console.log("Error: get Trading Days in "+measurment); 177 | console.log(err); 178 | 179 | callback(""); 180 | }); 181 | } 182 | 183 | //回溯前x天的tick 184 | //当天:LookBackDays=0 185 | //前1天:LookBackDays=1 186 | LoadTick(measurment,LookBackDays,callback) 187 | { 188 | let influxClient=this; 189 | this.GetTradingDayList(measurment,function (tradingDayList) { 190 | if(tradingDayList===undefined) 191 | { 192 | callback(undefined); 193 | return; 194 | } 195 | 196 | let allDayCount=tradingDayList.length; 197 | 198 | let influxQL="select *::field from \""+measurment+"\" where"; 199 | if(allDayCount>=LookBackDays) { 200 | for (let i = allDayCount - LookBackDays - 1; i < allDayCount; i++) { 201 | let dateStr = tradingDayList[i]; 202 | influxQL += " tradingDay='" + dateStr + "'"; 203 | if (i != allDayCount - 1) { 204 | influxQL += " or"; 205 | } 206 | } 207 | 208 | influxClient.influx.query(influxQL).then(tickList => { 209 | callback(tickList); 210 | }).catch(err => { 211 | console.log("Error:get Tick list in " + measurment); 212 | console.log(err); 213 | 214 | callback(undefined); 215 | }); 216 | } 217 | }); 218 | } 219 | } 220 | 221 | module.exports=InfluxDB; -------------------------------------------------------------------------------- /nodequant/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodequant", 3 | "version": "2.0.0", 4 | "scripts": { 5 | "start": "node ./bin/www" 6 | }, 7 | "eslintConfig": { 8 | "parserOptions": { 9 | "ecmaVersion": 2017, 10 | "sourceType": "module" 11 | }, 12 | "env": { 13 | "node": true 14 | }, 15 | "rules": { 16 | "eqeqeq": [ 17 | "error" 18 | ], 19 | "curly": [ 20 | "error" 21 | ] 22 | } 23 | }, 24 | "dependencies": { 25 | "async": "^2.5.0", 26 | "body-parser": "~1.17.1", 27 | "cookie-parser": "~1.4.3", 28 | "debug": "~2.6.3", 29 | "ejs": "~2.5.6", 30 | "express": "~4.15.2", 31 | "influx": "^5.0.7", 32 | "morgan": "~1.9.1", 33 | "redis": "^2.7.1", 34 | "serve-favicon": "~2.4.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /nodequant/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /nodequant/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /nodequant/routes/test/cancelOrder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/6. 3 | */ 4 | var express = require('express'); 5 | var router = express.Router(); 6 | 7 | require("../../common.js"); 8 | 9 | /* GET home page. */ 10 | router.get('/', function(req, res, next) { 11 | 12 | let orderList=global.NodeQuant.StrategyEngine.GetUnFinishOrderList("Demo"); 13 | 14 | for(let index in orderList) 15 | { 16 | let order=orderList[index]; 17 | global.NodeQuant.StrategyEngine.CancelOrder(order); 18 | } 19 | 20 | //global.NodeQuant.StrategyEngine.LoadTickFromDB("Demo","Au(T+D)",30,undefined); 21 | //global.NodeQuant.StrategyEngine.LoadBarFromDB("Demo","Au(T+D)",5,KBarType.Minute,5,undefined); 22 | 23 | res.render('index', { title: 'CancelOrder successfully'}); 24 | }); 25 | 26 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/getTradingDay.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/6. 3 | */ 4 | var express = require('express'); 5 | var router = express.Router(); 6 | 7 | require("../../common.js"); 8 | 9 | /* GET home page. */ 10 | router.get('/', function(req, res, next) { 11 | 12 | let tradingDay=global.NodeQuant.MainEngine.GetTradingDay(); 13 | 14 | res.end(JSON.stringify({ TradingDay: tradingDay})); 15 | }); 16 | 17 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/md/getApiVersion.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/6. 3 | */ 4 | var express = require('express'); 5 | var router = express.Router(); 6 | 7 | require("../../../common.js"); 8 | 9 | /* GET home page. */ 10 | router.get('/', function(req, res, next) { 11 | 12 | let ApiVersion=global.NodeQuant.MainEngine.GetMdApiVersion(); 13 | 14 | res.end(JSON.stringify({ ApiVersion: ApiVersion})); 15 | }); 16 | 17 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/queryCommissionRate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/6. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | 7 | let URL = require('url'); 8 | require("../../common.js"); 9 | 10 | /* GET home page. */ 11 | router.get('/', function(req, res, next) { 12 | let request = URL.parse(req.url, true).query; 13 | let contractName=request.contractName.replace(" ","+"); 14 | let clientName=request.clientName; 15 | 16 | global.NodeQuant.MainEngine.QueryCommissionRate(clientName,contractName); 17 | 18 | //查询合约手续费 19 | global.AppEventEmitter.once(EVENT.OnQueryCommissionRate,function (commissionRateInfo) { 20 | res.end(JSON.stringify(commissionRateInfo)); 21 | }); 22 | }); 23 | 24 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/queryInvestorPosition.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/2. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | let URL = require('url'); 7 | require("../../common.js"); 8 | 9 | /* GET home page. */ 10 | router.get('/', function(req, res, next) { 11 | 12 | let queryReq = URL.parse(req.url, true).query; 13 | let clientName=queryReq.clientName; 14 | 15 | 16 | global.NodeQuant.MainEngine.QueryInvestorPosition(clientName,function (clientName,ret) { 17 | if(ret===-99) 18 | console.log('QueryInvestorPosition Failed.Error: Trader client have not logined'); 19 | else if(ret!==0) 20 | console.log('QueryInvestorPosition Failed.Error: '); 21 | else 22 | console.log('QueryInvestorPosition successfully'); 23 | }); 24 | 25 | 26 | //查询合约仓位 27 | global.AppEventEmitter.once(EVENT.OnQueryPosition,function (postionInfo) { 28 | res.end(JSON.stringify(postionInfo)); 29 | }); 30 | 31 | }); 32 | 33 | module.exports = router; 34 | 35 | -------------------------------------------------------------------------------- /nodequant/routes/test/querySettlementInfo.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let router = express.Router(); 3 | 4 | let URL = require('url'); 5 | require("../../common.js"); 6 | 7 | /* GET home page. */ 8 | router.get('/', function(req, res, next) { 9 | let request = URL.parse(req.url, true).query; 10 | let clientName=request.clientName; 11 | 12 | global.NodeQuant.MainEngine.QuerySettlementInfo(clientName); 13 | 14 | //查询结算信息 15 | global.AppEventEmitter.once(EVENT.OnQuerySettlementInfo,function (settlementInfo) { 16 | res.end(JSON.stringify(settlementInfo)); 17 | }); 18 | }); 19 | 20 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/queryTradingAccount.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/6. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | 7 | let URL = require('url'); 8 | require("../../common.js"); 9 | 10 | /* GET home page. */ 11 | router.get('/', function(req, res, next) { 12 | let request = URL.parse(req.url, true).query; 13 | let clientName=request.clientName; 14 | 15 | global.NodeQuant.MainEngine.QueryTradingAccount(clientName); 16 | 17 | //查询资金账户 18 | global.AppEventEmitter.once(EVENT.OnQueryTradingAccount,function (tradingAccountInfo) { 19 | res.end(JSON.stringify(tradingAccountInfo)); 20 | }); 21 | }); 22 | 23 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/sendFillAndKillLimitOrder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/5/31. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | let URL = require('url'); 7 | /* GET home page. */ 8 | router.get('/', function(req, res, next) { 9 | 10 | let FAKLimitOrderReq = URL.parse(req.url, true).query; 11 | let contractName=FAKLimitOrderReq.contractName.replace(" ","+"); 12 | let direction=FAKLimitOrderReq.direction; 13 | if(direction==="buy") 14 | direction=Direction.Buy; 15 | else if(direction==="sell") 16 | direction=Direction.Sell; 17 | 18 | let openclose=FAKLimitOrderReq.openclose; 19 | if(openclose==="open") 20 | openclose=OpenCloseFlagType.Open; 21 | else if(openclose==="close") 22 | openclose=OpenCloseFlagType.Close; 23 | else if(openclose==="closeToday") 24 | openclose=OpenCloseFlagType.CloseToday; 25 | else if(openclose==="closeYesterday") 26 | openclose=OpenCloseFlagType.CloseYesterday; 27 | 28 | let volume=parseInt(FAKLimitOrderReq.volume); 29 | 30 | let limitPrice=parseFloat(FAKLimitOrderReq.limitPrice); 31 | 32 | let DemoStrategy=global.NodeQuant.StrategyEngine.GetStrategy("Demo"); 33 | if(DemoStrategy) 34 | { 35 | let lastTick = global.NodeQuant.StrategyEngine.Symbol_LastTickDic[contractName]; 36 | DemoStrategy.SendOrder(lastTick.clientName,contractName,limitPrice,volume,direction,openclose,OrderType.FAK); 37 | } 38 | 39 | res.render('index', { title: 'FiveMAStrategy Send FAK Order'}); 40 | 41 | }); 42 | 43 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/sendFillOrKillLimitOrder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/5/31. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | let URL = require('url'); 7 | /* GET home page. */ 8 | router.get('/', function(req, res, next) { 9 | 10 | let FOKLimitOrderReq = URL.parse(req.url, true).query; 11 | let contractName=FOKLimitOrderReq.contractName.replace(" ","+"); 12 | let direction=FOKLimitOrderReq.direction; 13 | if(direction==="buy") 14 | direction=Direction.Buy; 15 | else if(direction==="sell") 16 | direction=Direction.Sell; 17 | 18 | let openclose=FOKLimitOrderReq.openclose; 19 | if(openclose==="open") 20 | openclose=OpenCloseFlagType.Open; 21 | else if(openclose==="close") 22 | openclose=OpenCloseFlagType.Close; 23 | else if(openclose==="closeToday") 24 | openclose=OpenCloseFlagType.CloseToday; 25 | else if(openclose==="closeYesterday") 26 | openclose=OpenCloseFlagType.CloseYesterday; 27 | 28 | let volume=parseInt(FOKLimitOrderReq.volume); 29 | 30 | let limitPrice=parseFloat(FOKLimitOrderReq.limitPrice); 31 | 32 | let DemoStrategy=global.NodeQuant.StrategyEngine.GetStrategy("Demo"); 33 | if(DemoStrategy) 34 | { 35 | let lastTick = global.NodeQuant.StrategyEngine.Symbol_LastTickDic[contractName]; 36 | DemoStrategy.SendOrder(lastTick.clientName,contractName,limitPrice,volume,direction,openclose,OrderType.FOK); 37 | } 38 | 39 | res.render('index', { title: 'FiveMAStrategy Send FOK Order'}); 40 | 41 | }); 42 | 43 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/sendLimitOrder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/5/31. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | let URL = require('url'); 7 | /* GET home page. */ 8 | router.get('/', function(req, res, next) { 9 | 10 | let limitOrderReq = URL.parse(req.url, true).query; 11 | let contractName=limitOrderReq.contractName.replace(" ","+"); 12 | let direction=limitOrderReq.direction; 13 | if(direction==="buy") 14 | direction=Direction.Buy; 15 | else if(direction==="sell") 16 | direction=Direction.Sell; 17 | 18 | let openclose=limitOrderReq.openclose; 19 | if(openclose==="open") 20 | openclose=OpenCloseFlagType.Open; 21 | else if(openclose==="close") 22 | openclose=OpenCloseFlagType.Close; 23 | else if(openclose==="closeToday") 24 | openclose=OpenCloseFlagType.CloseToday; 25 | else if(openclose==="closeYesterday") 26 | openclose=OpenCloseFlagType.CloseYesterday; 27 | 28 | let volume=parseInt(limitOrderReq.volume); 29 | 30 | let limitPrice=parseFloat(limitOrderReq.limitPrice); 31 | 32 | let DemoStrategy=global.NodeQuant.StrategyEngine.GetStrategy("Demo"); 33 | if(DemoStrategy) 34 | { 35 | let lastTick = global.NodeQuant.StrategyEngine.Symbol_LastTickDic[contractName]; 36 | DemoStrategy.SendOrder(lastTick.clientName,contractName,limitPrice,volume,direction,openclose); 37 | 38 | //test 1 39 | //DemoStrategy.SendOrder("T1709","94.710",volume,direction,openclose); 40 | //test 2 41 | /* 42 | DemoStrategy.SendOrder("T1709","94.710",volume,direction,openclose); 43 | DemoStrategy.SendOrder("TF1709","97.330",volume,direction,openclose); 44 | */ 45 | 46 | //test 3 47 | 48 | //DemoStrategy.SendOrder("bu1801",2740,volume,direction,openclose); 49 | //DemoStrategy.SendOrder("rb1801",3840,volume,direction,openclose); 50 | 51 | //DemoStrategy.SendOrder("bu1801",2580,volume,direction,openclose); 52 | //DemoStrategy.SendOrder("rb1801",3840,volume,direction,openclose); 53 | 54 | } 55 | 56 | res.render('index', { title: 'FiveMAStrategy SendOrder'}); 57 | }); 58 | 59 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/sendMarketIfTouchedOrder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/5/31. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | let URL = require('url'); 7 | /* GET home page. */ 8 | router.get('/', function(req, res, next) { 9 | 10 | let marketIfTouchedOrderReq = URL.parse(req.url, true).query; 11 | let contractName=marketIfTouchedOrderReq.contractName.replace(" ","+"); 12 | let direction=marketIfTouchedOrderReq.direction; 13 | if(direction==="buy") 14 | direction=Direction.Buy; 15 | else if(direction==="sell") 16 | direction=Direction.Sell; 17 | 18 | let openclose=marketIfTouchedOrderReq.openclose; 19 | if(openclose==="open") 20 | openclose=OpenCloseFlagType.Open; 21 | else if(openclose==="close") 22 | openclose=OpenCloseFlagType.Close; 23 | else if(openclose==="closeToday") 24 | openclose=OpenCloseFlagType.CloseToday; 25 | else if(openclose==="closeYesterday") 26 | openclose=OpenCloseFlagType.CloseYesterday; 27 | 28 | let volume=parseInt(marketIfTouchedOrderReq.volume); 29 | 30 | let stopPriceCondition=marketIfTouchedOrderReq.stopPriceCondition; 31 | if(stopPriceCondition==="gt") 32 | { 33 | stopPriceCondition=ContingentConditionType.LastPriceGreaterThanStopPrice; 34 | }else if(stopPriceCondition==="gte") 35 | { 36 | stopPriceCondition=ContingentConditionType.LastPriceGreaterEqualStopPrice; 37 | }else if(stopPriceCondition==="lt") 38 | { 39 | stopPriceCondition=ContingentConditionType.LastPriceLesserThanStopPrice; 40 | }else if(stopPriceCondition==="lte") 41 | { 42 | stopPriceCondition=ContingentConditionType.LastPriceLesserEqualStopPrice; 43 | } 44 | 45 | let stopPrice=parseFloat(marketIfTouchedOrderReq.stopPrice); 46 | 47 | let lastTick = global.NodeQuant.StrategyEngine.Symbol_LastTickDic[contractName]; 48 | global.NodeQuant.MainEngine.SendMarketIfTouchedOrder(lastTick.clientName,contractName,direction,openclose,volume,stopPriceCondition,stopPrice,function (clientName,ret) { 49 | 50 | if(ret===-99) 51 | res.render('index', { title: 'SendMarketIfTouchedOrder Failed.Error: Trader client have not logined' }); 52 | else if(ret!==0) 53 | res.render('index', { title: 'SendMarketIfTouchedOrder failed, err:'+ret }); 54 | else 55 | res.render('index', { title: 'SendMarketIfTouchedOrder successfully'}); 56 | }); 57 | 58 | }); 59 | 60 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/sendMarketOrder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/5/31. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | 7 | let URL = require('url'); 8 | 9 | /* GET home page. */ 10 | router.get('/', function(req, res, next) { 11 | 12 | let marketOrderReq = URL.parse(req.url, true).query; 13 | let contractName=marketOrderReq.contractName.replace(" ","+"); 14 | let direction=marketOrderReq.direction; 15 | if(direction==="buy") 16 | direction=Direction.Buy; 17 | else if(direction==="sell") 18 | direction=Direction.Sell; 19 | 20 | let openclose=marketOrderReq.openclose; 21 | if(openclose==="open") 22 | openclose=OpenCloseFlagType.Open; 23 | else if(openclose==="close") 24 | openclose=OpenCloseFlagType.Close; 25 | else if(openclose==="closeToday") 26 | openclose=OpenCloseFlagType.CloseToday; 27 | else if(openclose==="closeYesterday") 28 | openclose=OpenCloseFlagType.CloseYesterday; 29 | 30 | let volume=parseInt(marketOrderReq.volume); 31 | 32 | let DemoStrategy=global.NodeQuant.StrategyEngine.GetStrategy("Demo"); 33 | if(DemoStrategy) 34 | { 35 | let lastTick = global.NodeQuant.StrategyEngine.Symbol_LastTickDic[contractName]; 36 | DemoStrategy.SendOrder(lastTick.clientName,contractName,undefined,volume,direction,openclose,OrderType.Market,lastTick); 37 | } 38 | 39 | res.render('index', { title: 'FiveMAStrategy Send Market Order'}); 40 | }); 41 | 42 | module.exports = router; 43 | -------------------------------------------------------------------------------- /nodequant/routes/test/sendStopLimitOrder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/5/31. 3 | */ 4 | let express = require('express'); 5 | let router = express.Router(); 6 | let URL = require('url'); 7 | /* GET home page. */ 8 | router.get('/', function(req, res, next) { 9 | 10 | let stopLimitOrderReq = URL.parse(req.url, true).query; 11 | let contractName=stopLimitOrderReq.contractName.replace(" ","+"); 12 | let direction=stopLimitOrderReq.direction; 13 | if(direction==="buy") 14 | direction=Direction.Buy; 15 | else if(direction==="sell") 16 | direction=Direction.Sell; 17 | 18 | let openclose=stopLimitOrderReq.openclose; 19 | if(openclose==="open") 20 | openclose=OpenCloseFlagType.Open; 21 | else if(openclose==="close") 22 | openclose=OpenCloseFlagType.Close; 23 | else if(openclose==="closeToday") 24 | openclose=OpenCloseFlagType.CloseToday; 25 | else if(openclose==="closeYesterday") 26 | openclose=OpenCloseFlagType.CloseYesterday; 27 | 28 | let volume=parseInt(stopLimitOrderReq.volume); 29 | let limitPrice=parseFloat(stopLimitOrderReq.limitPrice); 30 | 31 | let stopPriceCondition=stopLimitOrderReq.stopPriceCondition; 32 | if(stopPriceCondition==="gt") 33 | { 34 | stopPriceCondition=ContingentConditionType.LastPriceGreaterThanStopPrice; 35 | }else if(stopPriceCondition==="gte") 36 | { 37 | stopPriceCondition=ContingentConditionType.LastPriceGreaterEqualStopPrice; 38 | }else if(stopPriceCondition==="lt") 39 | { 40 | stopPriceCondition=ContingentConditionType.LastPriceLesserThanStopPrice; 41 | }else if(stopPriceCondition==="lte") 42 | { 43 | stopPriceCondition=ContingentConditionType.LastPriceLesserEqualStopPrice; 44 | }else 45 | { 46 | res.render('index', { title: 'SendStopLimitOrder Failed.Error: Trader client is not yet support this stopPriceCondition' }); 47 | return; 48 | } 49 | 50 | let stopPrice=parseFloat(stopLimitOrderReq.stopPrice); 51 | 52 | 53 | let DemoStrategy=global.NodeQuant.StrategyEngine.GetStrategy("Demo"); 54 | if(DemoStrategy) 55 | { 56 | let lastTick = global.NodeQuant.StrategyEngine.Symbol_LastTickDic[contractName]; 57 | DemoStrategy.SendOrder(lastTick.clientName,contractName,limitPrice,volume,direction,openclose,OrderType.Condition,undefined,stopPriceCondition,stopPrice); 58 | } 59 | 60 | res.render('index', { title: 'FiveMAStrategy Send Stop Limit Order'}); 61 | 62 | }); 63 | 64 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/subscribeContract.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/16. 3 | */ 4 | 5 | let express = require('express'); 6 | let router = express.Router(); 7 | let URL = require('url'); 8 | require("../../common.js"); 9 | 10 | /* GET home page. */ 11 | router.get('/', function(req, res, next) { 12 | 13 | let SubscribeReq = URL.parse(req.url, true).query; 14 | let contractName=SubscribeReq.contractName.replace(" ","+"); 15 | 16 | let lastTick = global.NodeQuant.StrategyEngine.Symbol_LastTickDic[contractName]; 17 | global.NodeQuant.MainEngine.Subscribe("CTP",contractName,function (contractName,clientName,ret) { 18 | 19 | if(ret===-99) 20 | res.render('index', { title: clientName+' Subscribe Failed.Error: Trader client have not logined' }); 21 | else if(ret!==0) 22 | res.render('index', { title: clientName+' Subscribe Failed.Error: '+ret }); 23 | else 24 | res.render('index', { title: clientName+' Subscribe successfully'}); 25 | }); 26 | }); 27 | 28 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/td/getApiVersion.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/6. 3 | */ 4 | var express = require('express'); 5 | var router = express.Router(); 6 | 7 | require("../../../common.js"); 8 | 9 | /* GET home page. */ 10 | router.get('/', function(req, res, next) { 11 | 12 | let ApiVersion=global.NodeQuant.MainEngine.GetTdApiVersion(); 13 | 14 | res.end(JSON.stringify({ ApiVersion: ApiVersion})); 15 | }); 16 | 17 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/routes/test/unSubscribeContract.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/16. 3 | */ 4 | 5 | let express = require('express'); 6 | let router = express.Router(); 7 | let URL = require('url'); 8 | require("../../common.js"); 9 | 10 | /* GET home page. */ 11 | router.get('/', function(req, res, next) { 12 | 13 | let unSubscribeReq = URL.parse(req.url, true).query; 14 | let contractName=unSubscribeReq.contractName.replace(" ","+"); 15 | 16 | let lastTick = global.NodeQuant.StrategyEngine.Symbol_LastTickDic[contractName]; 17 | global.NodeQuant.MainEngine.UnSubscribe(lastTick.clientName,contractName,function (clientName,ret) { 18 | if(ret===-99) 19 | res.render('index', { title: clientName+' UnSubscribe Failed.Error: Market client have not logined' }); 20 | else if(ret!==0) 21 | res.render('index', { title: clientName+' UnSubscribe Failed.Error: '+ret }); 22 | else 23 | res.render('index', { title: clientName+' UnSubscribe successfully'}); 24 | }); 25 | }); 26 | 27 | module.exports = router; -------------------------------------------------------------------------------- /nodequant/run_in_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo cp ./model/client/CTP/linux/x64/lib* /usr/local/lib 3 | sudo cp ./model/client/Sgit/linux/x64/lib* /usr/local/lib 4 | sudo npm install 5 | sudo npm install forever -g 6 | sudo rm ~/.forever/forever.log 7 | sudo forever start -l forever.log ./bin/www -------------------------------------------------------------------------------- /nodequant/strategy/DemoStrategy.js: -------------------------------------------------------------------------------- 1 | let BaseStrategy=require("./baseStrategy"); 2 | class DemoStrategy extends BaseStrategy{ 3 | constructor(strategyConfig) 4 | { 5 | //一定要使用super(strategyConfig)进行基类实例初始化 6 | //strategyConfig为 userConfig.js 中的DemoStrategy类的策略配置对象 7 | //调用super(strategyConfig)的作用是基类BaseStrategy实例也需要根据strategyConfig来进行初始化 8 | super(strategyConfig); 9 | 10 | } 11 | 12 | OnClosedBar(closedBar) 13 | { 14 | console.log(this.name+"策略的"+closedBar.symbol+"K线结束,结束时间:"+closedBar.endDatetime.toLocaleString()+",Close价:"+closedBar.closePrice); 15 | } 16 | 17 | OnNewBar(newBar) 18 | { 19 | console.log(this.name+"策略的"+newBar.symbol+"K线开始,开始时间"+newBar.startDatetime.toLocaleString()+",Open价:"+newBar.openPrice); 20 | } 21 | 22 | OnTick(tick) 23 | { 24 | //调用基类的OnTick函数,否则无法触发OnNewBar、OnClosedBar等事件响应函数 25 | //如果策略不需要计算K线,只用到Tick行情,可以把super.OnTick(tick);这句代码去掉,加快速度 26 | super.OnTick(tick); 27 | console.log(this.name+"策略的"+tick.symbol+"的Tick,时间:"+tick.date+" "+tick.timeStr+",详情:"+JSON.stringify(tick)); 28 | } 29 | 30 | Stop(){ 31 | super.Stop(); 32 | } 33 | } 34 | module.exports=DemoStrategy; -------------------------------------------------------------------------------- /nodequant/strategy/Open_CloseStrategy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/7/4. 3 | */ 4 | 5 | var BaseStrategy=require("./baseStrategy"); 6 | 7 | /////////////////////// Private Method /////////////////////////////////////////////// 8 | 9 | class Open_CloseStrategy extends BaseStrategy{ 10 | 11 | //初始化 12 | constructor(strategyConfig) 13 | { 14 | //一定要使用super()初始化基类,这样无论基类还是子类的this都是指向子类实例 15 | super(strategyConfig); 16 | 17 | this.OpenTime=strategyConfig.OpenTime; 18 | this.isOpen=false; 19 | this.CloseTime=strategyConfig.CloseTime; 20 | } 21 | 22 | /////////////////////////////// Public Method ///////////////////////////////////// 23 | OnClosedBar(closedBar) 24 | { 25 | 26 | } 27 | 28 | OnNewBar(newBar) 29 | { 30 | 31 | } 32 | 33 | OnTick(tick) 34 | { 35 | super.OnTick(tick); 36 | 37 | let currentTime=new Date().toTimeString(); 38 | 39 | if(currentTime>this.OpenTime) 40 | { 41 | //开仓 42 | //是否有未完成的Order 43 | let unFinishOrderList=this.GetUnFinishOrderList(); 44 | if(unFinishOrderList.length===0) 45 | { 46 | //多仓是否存在 47 | let position = this.GetPosition(tick.symbol); 48 | if(position===undefined || position.longPosition===0) 49 | { 50 | if(this.isOpen===false) 51 | { 52 | this.isOpen=true; 53 | let price=this.PriceUp(tick.symbol,tick.lastPrice,Direction.Buy,2); 54 | this.SendOrder(tick.clientName,tick.symbol,price,1,Direction.Buy,OpenCloseFlagType.Open); 55 | } 56 | } 57 | }else 58 | { 59 | for(let index in unFinishOrderList) 60 | { 61 | let unFinishOrder=unFinishOrderList[index]; 62 | global.NodeQuant.StrategyEngine.CancelOrder(unFinishOrder); 63 | //再次开仓 64 | this.isOpen=false; 65 | } 66 | } 67 | } 68 | 69 | if(currentTime>this.CloseTime) 70 | { 71 | //平仓 72 | //是否有未完成的Order 73 | let unFinishOrderList=this.GetUnFinishOrderList(); 74 | if(unFinishOrderList.length===0) 75 | { 76 | let unlockPosition = this.GetTodayUnLockLongPosition(tick.symbol); 77 | if (unlockPosition > 0) 78 | { 79 | let price = this.PriceUp(tick.symbol, tick.lastPrice, Direction.Sell, 2); 80 | this.SendOrder(tick.clientName,tick.symbol, tick.lastPrice, unlockPosition, Direction.Sell, OpenCloseFlagType.CloseToday); 81 | } 82 | } 83 | } 84 | } 85 | 86 | Stop(){ 87 | //调用基类方法 88 | super.Stop(); 89 | } 90 | } 91 | 92 | module.exports=Open_CloseStrategy; -------------------------------------------------------------------------------- /nodequant/strategy/TestTickToFinishSendOrder_Strategy.js: -------------------------------------------------------------------------------- 1 | let BaseStrategy=require("./baseStrategy"); 2 | class TestTickToFinishSendOrder_Strategy extends BaseStrategy{ 3 | constructor(strategyConfig) 4 | { 5 | //一定要使用super(strategyConfig)进行基类实例初始化 6 | //strategyConfig为 userConfig.js 中的DemoStrategy类的策略配置对象 7 | //调用super(strategyConfig)的作用是基类BaseStrategy实例也需要根据strategyConfig来进行初始化 8 | super(strategyConfig); 9 | global.TickCount=0; 10 | } 11 | 12 | OnClosedBar(closedBar) 13 | { 14 | console.log(closedBar.symbol+"K线结束,结束时间:"+closedBar.endDatetime.toLocaleString()+",Close价:"+closedBar.closePrice); 15 | } 16 | 17 | OnNewBar(newBar) 18 | { 19 | console.log(newBar.symbol+"K线开始,开始时间"+newBar.startDatetime.toLocaleString()+",Open价:"+newBar.openPrice); 20 | } 21 | 22 | OnTick(tick) 23 | { 24 | if(global.TickCount===30) 25 | { 26 | this.SendOrder(tick.clientName,tick.symbol,tick.lastPrice,1,Direction.Buy,OpenCloseFlagType.Open); 27 | } 28 | global.TickCount++; 29 | } 30 | 31 | Stop(){ 32 | super.Stop(); 33 | } 34 | } 35 | module.exports=TestTickToFinishSendOrder_Strategy; -------------------------------------------------------------------------------- /nodequant/strategy/baseStrategy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/12. 3 | */ 4 | 5 | require("../common"); 6 | 7 | let NodeQuantError=require("../util/NodeQuantError"); 8 | let KBar = require("../util/KBar"); 9 | //////////////////////////////// Private Method //////////////////////////////////////////// 10 | function _createBar(myStrategy,tick) { 11 | 12 | if(myStrategy.Symbol_KBarId_TickListDic[tick.symbol]===undefined) 13 | { 14 | myStrategy.Symbol_KBarId_TickListDic[tick.symbol]={}; 15 | } 16 | 17 | //分钟线结束事件 18 | if(myStrategy.KBarMillSecondInterval) 19 | { 20 | let KBarId = parseInt(tick.Id/myStrategy.KBarMillSecondInterval); 21 | 22 | //不存在KBarId,说明有一个新K线产生 23 | if(myStrategy.Symbol_KBarId_TickListDic[tick.symbol][KBarId]===undefined) 24 | { 25 | //创建新K线包含的TickList缓存数组 26 | _createNewBar(myStrategy,KBarId,tick); 27 | 28 | //创建上一个完整K线,加入到策略订阅合约的K线列表 29 | for(let barId in myStrategy.Symbol_KBarId_TickListDic[tick.symbol]) 30 | { 31 | if(barId!==KBarId.toString()) 32 | { 33 | //创建上一个完整K线,加入到策略订阅合约的K线列表 34 | _createClosedBar(myStrategy,barId,tick); 35 | //创建完删除上一个完整K线的TickList缓存 36 | delete myStrategy.Symbol_KBarId_TickListDic[tick.symbol][barId]; 37 | } 38 | } 39 | }else 40 | { 41 | myStrategy.Symbol_KBarId_TickListDic[tick.symbol][KBarId].push(tick); 42 | } 43 | } 44 | } 45 | 46 | function _createNewBar(myStrategy,barId,tick) { 47 | 48 | let KBarTickList=[]; 49 | KBarTickList.push(tick); 50 | myStrategy.Symbol_KBarId_TickListDic[tick.symbol][barId]=KBarTickList; 51 | //通知策略产生了一个新Bar 52 | let bar_StartDatetime=KBarTickList[0].datetime; 53 | let bar_EndDatetime=KBarTickList[KBarTickList.length-1].datetime; 54 | let newBar=new KBar(barId,bar_StartDatetime,bar_EndDatetime,tick.symbol,tick.lastPrice,tick.lastPrice,tick.lastPrice,tick.lastPrice,tick.volume,tick.openInterest); 55 | myStrategy.OnNewBar(newBar); 56 | } 57 | 58 | function _createClosedBar(myStrategy,barId,tick) { 59 | let bar_TickList = myStrategy.Symbol_KBarId_TickListDic[tick.symbol][barId]; 60 | 61 | if(bar_TickList.length>0) 62 | { 63 | let bar_StartDatetime=bar_TickList[0].datetime; 64 | let bar_EndDatetime=bar_TickList[bar_TickList.length-1].datetime; 65 | let bar_Open=bar_TickList[0].lastPrice; 66 | let bar_Close=bar_TickList[bar_TickList.length-1].lastPrice; 67 | let bar_High = bar_TickList[0].lastPrice; 68 | let bar_Low = bar_TickList[0].lastPrice; 69 | let volume=0; 70 | let openInterest=bar_TickList[bar_TickList.length-1].openInterest; 71 | for(let i=0;i< bar_TickList.length;i++) 72 | { 73 | bar_High = Math.max(bar_High,bar_TickList[i].lastPrice); 74 | bar_Low = Math.min(bar_Low,bar_TickList[i].lastPrice); 75 | volume += bar_TickList[i].volume; 76 | } 77 | 78 | let bar=new KBar(barId,bar_StartDatetime,bar_EndDatetime,tick.symbol,bar_Open,bar_High,bar_Low,bar_Close,volume,openInterest); 79 | 80 | if(myStrategy.Symbol_KBarListDic[tick.symbol]===undefined) 81 | { 82 | let KBarList=[]; 83 | myStrategy.Symbol_KBarListDic[tick.symbol]=KBarList; 84 | } 85 | 86 | myStrategy.Symbol_KBarListDic[tick.symbol].push(bar); 87 | //通知策略产生了一个完整Bar 88 | myStrategy.OnClosedBar(bar); 89 | } 90 | } 91 | 92 | /// 93 | /// 对期货价格进行整理。如果价格不是最小单位的整数倍,则调整为最小价格的整数倍。 94 | /// 95 | /// 原下单价格 96 | /// 每跳价格 97 | /// price相对priceTick进行下舍入的价格 98 | function _trimPriceByPriceTick(price,priceTick){ 99 | let tickTime=price/priceTick; 100 | let trimPrice=tickTime*priceTick; 101 | return trimPrice; 102 | } 103 | 104 | //预加载Tick完成 105 | function _onFinishLoadTick(strategy,symbol,TickList) { 106 | //多策略 107 | strategy.OnFinishPreLoadTick(symbol,TickList); 108 | } 109 | 110 | //数据从数据库中预先加载 111 | function _loadTickFromDB(myStrategy,symbol,LookBackDays) 112 | { 113 | global.NodeQuant.StrategyEngine.LoadTickFromDB(myStrategy,symbol,LookBackDays,_onFinishLoadTick); 114 | } 115 | 116 | //预加载Bar完成 117 | function _onFinishLoadBar(strategy,symbol,BarType,BarInterval,ClosedBarList) { 118 | strategy.OnFinishPreLoadBar(symbol,BarType,BarInterval,ClosedBarList); 119 | } 120 | 121 | function _loadBarFromDB(myStrategy,symbol,LookBackCount,BarType,BarInterval) 122 | { 123 | global.NodeQuant.StrategyEngine.LoadBarFromDB(myStrategy,symbol,LookBackCount,BarType,BarInterval,_onFinishLoadBar); 124 | } 125 | 126 | class BaseStrategy{ 127 | constructor(strategyConfig){ 128 | this.name=strategyConfig.name; 129 | this.symbols=strategyConfig.symbols; 130 | this.KBarType=strategyConfig.BarType; 131 | this.KBarInterval=strategyConfig.BarInterval; 132 | 133 | this.Symbol_KBarListDic={}; 134 | this.KBarMillSecondInterval=undefined; 135 | 136 | this.Symbol_KBarId_TickListDic={}; 137 | 138 | if(this.KBarType!==undefined && this.KBarInterval!==undefined) 139 | { 140 | switch(this.KBarType) 141 | { 142 | case KBarType.Tick: 143 | break; 144 | case KBarType.Second: 145 | this.KBarMillSecondInterval=this.KBarInterval*1000; 146 | break; 147 | case KBarType.Minute: 148 | this.KBarMillSecondInterval=this.KBarInterval*60*1000; 149 | break; 150 | case KBarType.Hour: 151 | this.KBarMillSecondInterval=this.KBarInterval*60*60*1000; 152 | break; 153 | default: 154 | break; 155 | } 156 | } 157 | 158 | //预加载数据库中行情数据 159 | this.PreloadConfig=strategyConfig.PreloadConfig; 160 | if(this.PreloadConfig!==undefined) 161 | { 162 | if(global.NodeQuant.MarketDataDBClient) 163 | { 164 | if (this.PreloadConfig.BarType === KBarType.Tick) { 165 | for (let symbol in this.symbols) { 166 | _loadTickFromDB(this, symbol, this.PreloadConfig.LookBackDays); 167 | } 168 | } else { 169 | for (let symbol in this.symbols) { 170 | _loadBarFromDB(this, symbol, this.PreloadConfig.LookBackDays, this.PreloadConfig.BarType, this.PreloadConfig.BarInterval); 171 | } 172 | } 173 | }else 174 | { 175 | let message= "无法预加载数据,数据库客户端没有实例,请检查系统配置"; 176 | let error=new NodeQuantError(this.name,ErrorType.StrategyError,message); 177 | 178 | global.AppEventEmitter.emit(EVENT.OnError,error); 179 | } 180 | } 181 | } 182 | 183 | ////////////////////////////////////////////////////////////// Public Method /////////////////////////////////////////////////////////////////////////// 184 | 185 | 186 | //加载Tick完成 187 | OnFinishPreLoadTick(symbol,TickList) 188 | { 189 | 190 | } 191 | 192 | //加载Bar完成 193 | OnFinishPreLoadBar(symbol,BarType,BarInterval,ClosedBarList) 194 | { 195 | 196 | } 197 | 198 | /// 199 | /// 策略退出 200 | /// 201 | Stop(){ 202 | 203 | } 204 | 205 | OnTick(tick){ 206 | //KarType没有设置或者设置为Tick,默认都不生成K线,不触发OnClosedBar,OnNewBar事件 207 | if(this.KBarType===undefined || this.KBarType===KBarType.Tick) 208 | { 209 | return; 210 | }else 211 | { 212 | _createBar(this,tick); 213 | } 214 | } 215 | 216 | OnClosedBar(closedBar) 217 | { 218 | 219 | } 220 | 221 | OnNewBar(newBar) 222 | { 223 | 224 | } 225 | 226 | 227 | OnOrder(order){ 228 | 229 | } 230 | 231 | OnTrade(trade){ 232 | 233 | } 234 | 235 | QueryTradingAccount(clientName) 236 | { 237 | global.NodeQuant.StrategyEngine.QueryTradingAccount(clientName,this); 238 | } 239 | 240 | OnQueryTradingAccount(tradingAccountInfo) 241 | { 242 | 243 | } 244 | 245 | //通过合约名字获得合约最新Tick 246 | GetLastTick(symbol) 247 | { 248 | return global.NodeQuant.StrategyEngine.Symbol_LastTickDic[symbol]; 249 | } 250 | 251 | GetUnFinishOrderList() 252 | { 253 | let unFinishOrderList=global.NodeQuant.StrategyEngine.GetUnFinishOrderList(this.name); 254 | return unFinishOrderList; 255 | } 256 | 257 | 258 | /// 259 | /// 获取合约的仓位对象 260 | /// 261 | /// 仓位对象 262 | GetPosition(symbol) 263 | { 264 | if(symbol===undefined) 265 | return undefined; 266 | 267 | let position = global.NodeQuant.StrategyEngine.GetPosition(this.name,symbol); 268 | return position; 269 | } 270 | 271 | /// 272 | /// 下单接口 273 | /// 274 | ///订单发送的交易客户端名称 275 | /// 目标合约 276 | /// 限定价格 277 | /// 下单手数 278 | /// 方向 279 | /// 开平类型 280 | ///以上是限价单必须的参数,如果要下FAK、FOK单需要加多以下参数 281 | ///订单类型 282 | //以上是FAK、FOK单必须的参数,如果要下 市价单 需要加多以下参数 283 | ///市价单需要的tick信息,为了知道涨停、跌停价 284 | //以上是市价单必须的参数,如果要下 条件单 需要加多以下参数 285 | // 条件单的价格条件 286 | // 条件单的触发价格,可与limitePrice对比以触发contingentCondition 287 | /// 288 | SendOrder(clientName,symbol,limitePrice,volume,direction,openClose,orderType,tick,contingentCondition,stopPrice) 289 | { 290 | if(arguments.length<6) 291 | { 292 | let message= "Send Order Failed.Reason:Not Valid Parameters"; 293 | let error=new NodeQuantError(this.name,ErrorType.StrategyError,message); 294 | 295 | global.AppEventEmitter.emit(EVENT.OnError,error); 296 | return; 297 | } 298 | 299 | let sendOrderType= arguments[6] ? orderType : OrderType.Limit; 300 | 301 | //记录发出订单 302 | let orderRecord={}; 303 | 304 | switch(sendOrderType) 305 | { 306 | case OrderType.Limit: 307 | global.NodeQuant.StrategyEngine.SendLimitOrder(this,clientName,symbol,direction,openClose,volume,limitePrice); 308 | 309 | orderRecord.datetime = new Date(); 310 | orderRecord.clientName=clientName; 311 | orderRecord.Type= OrderReverseType[OrderType.Limit]; 312 | orderRecord.symbol = symbol; 313 | orderRecord.direction = DirectionReverse[direction]; 314 | orderRecord.offset = OpenCloseFlagReverseType[openClose]; 315 | orderRecord.price = limitePrice; 316 | orderRecord.volume = volume; 317 | orderRecord.contingentCondition = undefined; 318 | orderRecord.stopPrice = undefined; 319 | 320 | global.NodeQuant.StrategyEngine.RecordOrder(this.name,orderRecord); 321 | break; 322 | case OrderType.FAK: 323 | global.NodeQuant.StrategyEngine.SendFillAndKillLimitOrder(this,clientName,symbol,direction,openClose,volume,limitePrice); 324 | 325 | orderRecord.datetime = new Date(); 326 | orderRecord.clientName=clientName; 327 | orderRecord.Type= OrderReverseType[OrderType.FAK]; 328 | orderRecord.symbol = symbol; 329 | orderRecord.direction = DirectionReverse[direction]; 330 | orderRecord.offset = OpenCloseFlagReverseType[openClose]; 331 | orderRecord.price = limitePrice; 332 | orderRecord.volume = volume; 333 | orderRecord.contingentCondition = undefined; 334 | orderRecord.stopPrice = undefined; 335 | 336 | global.NodeQuant.StrategyEngine.RecordOrder(this.name,orderRecord); 337 | break; 338 | case OrderType.FOK: 339 | global.NodeQuant.StrategyEngine.SendFillOrKillLimitOrder(this,clientName,symbol,direction,openClose,volume,limitePrice); 340 | 341 | orderRecord.datetime = new Date(); 342 | orderRecord.clientName=clientName; 343 | orderRecord.Type= OrderReverseType[OrderType.FOK]; 344 | orderRecord.symbol = symbol; 345 | orderRecord.direction = DirectionReverse[direction]; 346 | orderRecord.offset = OpenCloseFlagReverseType[openClose]; 347 | orderRecord.price = limitePrice; 348 | orderRecord.volume = volume; 349 | orderRecord.contingentCondition = undefined; 350 | orderRecord.stopPrice = undefined; 351 | 352 | global.NodeQuant.StrategyEngine.RecordOrder(this.name,orderRecord); 353 | 354 | break; 355 | case OrderType.Market: 356 | if(arguments[7]!==undefined && tick.upperLimit!==undefined && tick.lowerLimit!==undefined) 357 | { 358 | if(direction===Direction.Buy) 359 | limitePrice=tick.upperLimit; 360 | else if(direction===Direction.Sell) 361 | limitePrice=tick.lowerLimit; 362 | 363 | global.NodeQuant.StrategyEngine.SendLimitOrder(this,clientName,symbol,direction,openClose,volume,limitePrice); 364 | 365 | orderRecord.datetime = new Date(); 366 | orderRecord.clientName=clientName; 367 | orderRecord.Type= OrderReverseType[OrderType.Market]; 368 | orderRecord.symbol = symbol; 369 | orderRecord.direction = DirectionReverse[direction]; 370 | orderRecord.offset = OpenCloseFlagReverseType[openClose]; 371 | orderRecord.price = limitePrice; 372 | orderRecord.volume = volume; 373 | orderRecord.contingentCondition = undefined; 374 | orderRecord.stopPrice = undefined; 375 | 376 | global.NodeQuant.StrategyEngine.RecordOrder(this.name,orderRecord); 377 | 378 | }else 379 | { 380 | let message="Send Market Order Failed.Reason:Miss Market Tick INFO"; 381 | let error=new NodeQuantError(this.name,ErrorType.StrategyError,message); 382 | 383 | global.AppEventEmitter.emit(EVENT.OnError,error); 384 | } 385 | break; 386 | case OrderType.Condition: 387 | if(arguments[8]===undefined) 388 | { 389 | 390 | let message= "Send Condition Order Failed.Reason:Miss ContingentCondition Type"; 391 | let error=new NodeQuantError(this.name,ErrorType.StrategyError,message); 392 | global.AppEventEmitter.emit(EVENT.OnError,error); 393 | 394 | }else if(arguments[9]===undefined) 395 | { 396 | let message="Send Condition Order Failed.Reason:Miss StopPrice"; 397 | let error=new NodeQuantError(this.name,ErrorType.StrategyError,message); 398 | global.AppEventEmitter.emit(EVENT.OnError,error); 399 | }else{ 400 | global.NodeQuant.StrategyEngine.SendStopLimitOrder(this,clientName,symbol,direction,openClose,volume,limitePrice,contingentCondition,stopPrice); 401 | 402 | orderRecord.datetime = new Date(); 403 | orderRecord.clientName=clientName; 404 | orderRecord.Type= OrderReverseType[OrderType.Condition]; 405 | orderRecord.symbol = symbol; 406 | orderRecord.direction = DirectionReverse[direction]; 407 | orderRecord.offset = OpenCloseFlagReverseType[openClose]; 408 | orderRecord.price = limitePrice; 409 | orderRecord.volume = volume; 410 | orderRecord.contingentCondition=ContingentConditionReverseType[contingentCondition]; 411 | orderRecord.stopPrice=stopPrice; 412 | 413 | global.NodeQuant.StrategyEngine.RecordOrder(this.name,orderRecord); 414 | } 415 | break; 416 | default: 417 | break; 418 | } 419 | } 420 | 421 | /// 422 | /// 劣势下单价格,在指定的交易方向进行加跳,以达到更好地成交成功率 423 | /// 424 | /// 目标合约 425 | /// 指定价格 426 | /// 交易方向 427 | /// 加跳数,默认2跳 428 | /// 加了条数的价格 429 | PriceUp(symbol,price,direction,priceTickCount){ 430 | if(arguments.length<3) 431 | { 432 | return undefined; 433 | } 434 | 435 | let tickCount= arguments[3]?priceTickCount:2; 436 | let symbolClientName=this.symbols[symbol].clientName; 437 | let contract = global.NodeQuant.MainEngine.GetContract(symbolClientName,symbol); 438 | let priceTick=contract.priceTick; 439 | 440 | let orderPrice= _trimPriceByPriceTick(price,priceTick); 441 | 442 | let worsePrice=undefined; 443 | if(direction===Direction.Buy) 444 | worsePrice=orderPrice+tickCount*priceTick; 445 | else if(direction===Direction.Sell) 446 | worsePrice=orderPrice-tickCount*priceTick; 447 | 448 | return worsePrice; 449 | } 450 | 451 | /// 452 | /// 优势下单价格,在指定的交易方向进行减跳,以达到更好地成交价格。( 注意:优势价格限价单可能不会立即成交 ) 453 | /// 454 | /// 目标合约 455 | /// 指定价格 456 | /// 交易方向 457 | /// 加跳数,默认2跳 458 | /// 减了条数的价格 459 | PriceDown(symbol,price,direction,priceTickCount){ 460 | if(arguments.length<3) 461 | { 462 | return undefined; 463 | } 464 | 465 | let tickCount= arguments[3] ? priceTickCount:2; 466 | let symbolClientName=this.symbols[symbol].clientName; 467 | let contract = global.NodeQuant.MainEngine.GetContract(symbolClientName,symbol); 468 | let priceTick=contract.priceTick; 469 | 470 | let orderPrice= _trimPriceByPriceTick(price,priceTick); 471 | 472 | let betterPrice=undefined; 473 | if(direction===Direction.Buy) 474 | betterPrice=orderPrice-tickCount*priceTick; 475 | else if(direction===Direction.Sell) 476 | betterPrice=orderPrice+tickCount*priceTick; 477 | 478 | return betterPrice; 479 | } 480 | } 481 | 482 | module.exports=BaseStrategy; 483 | 484 | -------------------------------------------------------------------------------- /nodequant/systemConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/6/27. 3 | */ 4 | 5 | AppConfig={ 6 | Port:"3000" 7 | }; 8 | 9 | SystemConfig={ 10 | DayStartTime : "8:00:00", 11 | DayStopTime: "16:00:00", 12 | NightStartTime:"19:30:00", 13 | NightStopTime: "2:40:00" 14 | }; 15 | 16 | SupportClients={ 17 | CTP:"CTP" 18 | }; 19 | 20 | //可在不同交易客户端订阅的合约 21 | //Tick过滤器会根据时间过滤 22 | FuturesConfig={ 23 | CTP:{ 24 | IC:{ 25 | name:"中证500", 26 | exchange:"CFE", 27 | AMOpen:"9:30:00", 28 | AMClose:"11:30:00", 29 | PMOpen:"13:00:00", 30 | PMClose:"15:00:00" 31 | }, 32 | IF:{ 33 | name:"沪深300", 34 | exchange:"CFE", 35 | AMOpen:"9:30:00", 36 | AMClose:"11:30:00", 37 | PMOpen:"13:00:00", 38 | PMClose:"15:00:00" 39 | }, 40 | IH:{ 41 | name:"上证50", 42 | exchange:"CFE", 43 | AMOpen:"9:30:00", 44 | AMClose:"11:30:00", 45 | PMOpen:"13:00:00", 46 | PMClose:"15:00:00" 47 | }, 48 | T:{ 49 | name:"十债", 50 | exchange:"CFE", 51 | AMOpen:"9:15:00", 52 | AMClose:"11:30:00", 53 | PMOpen:"13:00:00", 54 | PMClose:"15:15:00" 55 | }, 56 | TF:{ 57 | name:"五债", 58 | exchange:"CFE", 59 | AMOpen:"9:15:00", 60 | AMClose:"11:30:00", 61 | PMOpen:"13:00:00", 62 | PMClose:"15:15:00" 63 | }, 64 | AP:{ 65 | name:"苹果", 66 | exchange:"CZC", 67 | AMOpen:"9:00:00", 68 | AMBreak:"10:15:00", 69 | AMResume:"10:30:00", 70 | AMClose:"11:30:00", 71 | PMOpen:"13:30:00", 72 | PMClose:"15:00:00" 73 | }, 74 | CF:{ 75 | name:"郑棉", 76 | exchange:"CZC", 77 | AMOpen:"9:00:00", 78 | AMBreak:"10:15:00", 79 | AMResume:"10:30:00", 80 | AMClose:"11:30:00", 81 | PMOpen:"13:30:00", 82 | PMClose:"15:00:00", 83 | NightOpen:"21:00:00", 84 | NightClose:"23:30:00" 85 | }, 86 | FG:{ 87 | name:"玻璃", 88 | exchange:"CZC", 89 | AMOpen:"9:00:00", 90 | AMBreak:"10:15:00", 91 | AMResume:"10:30:00", 92 | AMClose:"11:30:00", 93 | PMOpen:"13:30:00", 94 | PMClose:"15:00:00", 95 | NightOpen:"21:00:00", 96 | NightClose:"23:30:00" 97 | }, 98 | JR:{ 99 | name:"粳稻", 100 | exchange:"CZC", 101 | AMOpen:"9:00:00", 102 | AMBreak:"10:15:00", 103 | AMResume:"10:30:00", 104 | AMClose:"11:30:00", 105 | PMOpen:"13:30:00", 106 | PMClose:"15:00:00" 107 | }, 108 | MA:{ 109 | name:"甲醇", 110 | exchange:"CZC", 111 | AMOpen:"9:00:00", 112 | AMBreak:"10:15:00", 113 | AMResume:"10:30:00", 114 | AMClose:"11:30:00", 115 | PMOpen:"13:30:00", 116 | PMClose:"15:00:00", 117 | NightOpen:"21:00:00", 118 | NightClose:"23:30:00" 119 | }, 120 | OI:{ 121 | name:"郑油", 122 | exchange:"CZC", 123 | AMOpen:"9:00:00", 124 | AMBreak:"10:15:00", 125 | AMResume:"10:30:00", 126 | AMClose:"11:30:00", 127 | PMOpen:"13:30:00", 128 | PMClose:"15:00:00" 129 | }, 130 | PM:{ 131 | name:"普麦", 132 | exchange:"CZC", 133 | AMOpen:"9:00:00", 134 | AMBreak:"10:15:00", 135 | AMResume:"10:30:00", 136 | AMClose:"11:30:00", 137 | PMOpen:"13:30:00", 138 | PMClose:"15:00:00" 139 | }, 140 | RI:{ 141 | name:"早稻", 142 | exchange:"CZC", 143 | AMOpen:"9:00:00", 144 | AMBreak:"10:15:00", 145 | AMResume:"10:30:00", 146 | AMClose:"11:30:00", 147 | PMOpen:"13:30:00", 148 | PMClose:"15:00:00" 149 | }, 150 | RM:{ 151 | name:"菜粕", 152 | exchange:"CZC", 153 | AMOpen:"9:00:00", 154 | AMBreak:"10:15:00", 155 | AMResume:"10:30:00", 156 | AMClose:"11:30:00", 157 | PMOpen:"13:30:00", 158 | PMClose:"15:00:00", 159 | NightOpen:"21:00:00", 160 | NightClose:"23:30:00" 161 | }, 162 | RS:{ 163 | name:"菜籽", 164 | exchange:"CZC", 165 | AMOpen:"9:00:00", 166 | AMBreak:"10:15:00", 167 | AMResume:"10:30:00", 168 | AMClose:"11:30:00", 169 | PMOpen:"13:30:00", 170 | PMClose:"15:00:00" 171 | }, 172 | SF:{ 173 | name:"硅铁", 174 | exchange:"CZC", 175 | AMOpen:"9:00:00", 176 | AMBreak:"10:15:00", 177 | AMResume:"10:30:00", 178 | AMClose:"11:30:00", 179 | PMOpen:"13:30:00", 180 | PMClose:"15:00:00" 181 | }, 182 | SM:{ 183 | name:"锰硅", 184 | exchange:"CZC", 185 | AMOpen:"9:00:00", 186 | AMBreak:"10:15:00", 187 | AMResume:"10:30:00", 188 | AMClose:"11:30:00", 189 | PMOpen:"13:30:00", 190 | PMClose:"15:00:00" 191 | }, 192 | SR:{ 193 | name:"白糖", 194 | exchange:"CZC", 195 | AMOpen:"9:00:00", 196 | AMBreak:"10:15:00", 197 | AMResume:"10:30:00", 198 | AMClose:"11:30:00", 199 | PMOpen:"13:30:00", 200 | PMClose:"15:00:00", 201 | NightOpen:"21:00:00", 202 | NightClose:"23:30:00" 203 | }, 204 | TA:{ 205 | name:"PTA", 206 | exchange:"CZC", 207 | AMOpen:"9:00:00", 208 | AMBreak:"10:15:00", 209 | AMResume:"10:30:00", 210 | AMClose:"11:30:00", 211 | PMOpen:"13:30:00", 212 | PMClose:"15:00:00", 213 | NightOpen:"21:00:00", 214 | NightClose:"23:30:00" 215 | }, 216 | WH:{ 217 | name:"郑麦", 218 | exchange:"CZC", 219 | AMOpen:"9:00:00", 220 | AMBreak:"10:15:00", 221 | AMResume:"10:30:00", 222 | AMClose:"11:30:00", 223 | PMOpen:"13:30:00", 224 | PMClose:"15:00:00" 225 | }, 226 | ZC:{ 227 | name:"郑煤", 228 | exchange:"CZC", 229 | AMOpen:"9:00:00", 230 | AMBreak:"10:15:00", 231 | AMResume:"10:30:00", 232 | AMClose:"11:30:00", 233 | PMOpen:"13:30:00", 234 | PMClose:"15:00:00", 235 | NightOpen:"21:00:00", 236 | NightClose:"23:30:00" 237 | }, 238 | A:{ 239 | name:"豆一", 240 | exchange:"DCE", 241 | AMOpen:"9:00:00", 242 | AMBreak:"10:15:00", 243 | AMResume:"10:30:00", 244 | AMClose:"11:30:00", 245 | PMOpen:"13:30:00", 246 | PMClose:"15:00:00", 247 | NightOpen:"21:00:00", 248 | NightClose:"23:30:00" 249 | }, 250 | B:{ 251 | name:"豆二", 252 | exchange:"DCE", 253 | AMOpen:"9:00:00", 254 | AMBreak:"10:15:00", 255 | AMResume:"10:30:00", 256 | AMClose:"11:30:00", 257 | PMOpen:"13:30:00", 258 | PMClose:"15:00:00", 259 | NightOpen:"21:00:00", 260 | NightClose:"23:30:00" 261 | }, 262 | BB:{ 263 | name:"胶板", 264 | exchange:"DCE", 265 | AMOpen:"9:00:00", 266 | AMBreak:"10:15:00", 267 | AMResume:"10:30:00", 268 | AMClose:"11:30:00", 269 | PMOpen:"13:30:00", 270 | PMClose:"15:00:00" 271 | }, 272 | C:{ 273 | name:"玉米", 274 | exchange:"DCE", 275 | AMOpen:"9:00:00", 276 | AMBreak:"10:15:00", 277 | AMResume:"10:30:00", 278 | AMClose:"11:30:00", 279 | PMOpen:"13:30:00", 280 | PMClose:"15:00:00" 281 | }, 282 | CS:{ 283 | name:"淀粉", 284 | exchange:"DCE", 285 | AMOpen:"9:00:00", 286 | AMBreak:"10:15:00", 287 | AMResume:"10:30:00", 288 | AMClose:"11:30:00", 289 | PMOpen:"13:30:00", 290 | PMClose:"15:00:00" 291 | }, 292 | FB:{ 293 | name:"纤板", 294 | exchange:"DCE", 295 | AMOpen:"9:00:00", 296 | AMBreak:"10:15:00", 297 | AMResume:"10:30:00", 298 | AMClose:"11:30:00", 299 | PMOpen:"13:30:00", 300 | PMClose:"15:00:00" 301 | }, 302 | I:{ 303 | name:"铁矿石", 304 | exchange:"DCE", 305 | AMOpen:"9:00:00", 306 | AMBreak:"10:15:00", 307 | AMResume:"10:30:00", 308 | AMClose:"11:30:00", 309 | PMOpen:"13:30:00", 310 | PMClose:"15:00:00", 311 | NightOpen:"21:00:00", 312 | NightClose:"23:30:00" 313 | }, 314 | J:{ 315 | name:"焦炭", 316 | exchange:"DCE", 317 | AMOpen:"9:00:00", 318 | AMBreak:"10:15:00", 319 | AMResume:"10:30:00", 320 | AMClose:"11:30:00", 321 | PMOpen:"13:30:00", 322 | PMClose:"15:00:00", 323 | NightOpen:"21:00:00", 324 | NightClose:"23:30:00" 325 | }, 326 | JD:{ 327 | name:"鸡蛋", 328 | exchange:"DCE", 329 | AMOpen:"9:00:00", 330 | AMBreak:"10:15:00", 331 | AMResume:"10:30:00", 332 | AMClose:"11:30:00", 333 | PMOpen:"13:30:00", 334 | PMClose:"15:00:00" 335 | }, 336 | JM:{ 337 | name:"焦煤", 338 | exchange:"DCE", 339 | AMOpen:"9:00:00", 340 | AMBreak:"10:15:00", 341 | AMResume:"10:30:00", 342 | AMClose:"11:30:00", 343 | PMOpen:"13:30:00", 344 | PMClose:"15:00:00", 345 | NightOpen:"21:00:00", 346 | NightClose:"23:30:00" 347 | }, 348 | L:{ 349 | name:"塑料", 350 | exchange:"DCE", 351 | AMOpen:"9:00:00", 352 | AMBreak:"10:15:00", 353 | AMResume:"10:30:00", 354 | AMClose:"11:30:00", 355 | PMOpen:"13:30:00", 356 | PMClose:"15:00:00" 357 | }, 358 | M:{ 359 | name:"豆粕", 360 | exchange:"DCE", 361 | AMOpen:"9:00:00", 362 | AMBreak:"10:15:00", 363 | AMResume:"10:30:00", 364 | AMClose:"11:30:00", 365 | PMOpen:"13:30:00", 366 | PMClose:"15:00:00", 367 | NightOpen:"21:00:00", 368 | NightClose:"23:30:00" 369 | }, 370 | P:{ 371 | name:"棕榈", 372 | exchange:"DCE", 373 | AMOpen:"9:00:00", 374 | AMBreak:"10:15:00", 375 | AMResume:"10:30:00", 376 | AMClose:"11:30:00", 377 | PMOpen:"13:30:00", 378 | PMClose:"15:00:00", 379 | NightOpen:"21:00:00", 380 | NightClose:"23:30:00" 381 | }, 382 | PP:{ 383 | name:"丙烯", 384 | exchange:"DCE", 385 | AMOpen:"9:00:00", 386 | AMBreak:"10:15:00", 387 | AMResume:"10:30:00", 388 | AMClose:"11:30:00", 389 | PMOpen:"13:30:00", 390 | PMClose:"15:00:00" 391 | }, 392 | V:{ 393 | name:"PVC", 394 | exchange:"DCE", 395 | AMOpen:"9:00:00", 396 | AMBreak:"10:15:00", 397 | AMResume:"10:30:00", 398 | AMClose:"11:30:00", 399 | PMOpen:"13:30:00", 400 | PMClose:"15:00:00" 401 | }, 402 | Y:{ 403 | name:"豆油", 404 | exchange:"DCE", 405 | AMOpen:"9:00:00", 406 | AMBreak:"10:15:00", 407 | AMResume:"10:30:00", 408 | AMClose:"11:30:00", 409 | PMOpen:"13:30:00", 410 | PMClose:"15:00:00", 411 | NightOpen:"21:00:00", 412 | NightClose:"23:30:00" 413 | }, 414 | AG:{ 415 | name:"白银", 416 | exchange:"SHF", 417 | AMOpen:"9:00:00", 418 | AMBreak:"10:15:00", 419 | AMResume:"10:30:00", 420 | AMClose:"11:30:00", 421 | PMOpen:"13:30:00", 422 | PMClose:"15:00:00", 423 | NightOpen:"21:00:00", 424 | NightClose:"2:30:00" 425 | }, 426 | AL:{ 427 | name:"沪铝", 428 | exchange:"SHF", 429 | AMOpen:"9:00:00", 430 | AMBreak:"10:15:00", 431 | AMResume:"10:30:00", 432 | AMClose:"11:30:00", 433 | PMOpen:"13:30:00", 434 | PMClose:"15:00:00", 435 | NightOpen:"21:00:00", 436 | NightClose:"1:00:00" 437 | }, 438 | AU:{ 439 | name:"黄金", 440 | exchange:"SHF", 441 | AMOpen:"9:00:00", 442 | AMBreak:"10:15:00", 443 | AMResume:"10:30:00", 444 | AMClose:"11:30:00", 445 | PMOpen:"13:30:00", 446 | PMClose:"15:00:00", 447 | NightOpen:"21:00:00", 448 | NightClose:"2:30:00" 449 | }, 450 | BU:{ 451 | name:"沥青", 452 | exchange:"SHF", 453 | AMOpen:"9:00:00", 454 | AMBreak:"10:15:00", 455 | AMResume:"10:30:00", 456 | AMClose:"11:30:00", 457 | PMOpen:"13:30:00", 458 | PMClose:"15:00:00", 459 | NightOpen:"21:00:00", 460 | NightClose:"23:00:00" 461 | }, 462 | CU:{ 463 | name:"沪铜", 464 | exchange:"SHF", 465 | AMOpen:"9:00:00", 466 | AMBreak:"10:15:00", 467 | AMResume:"10:30:00", 468 | AMClose:"11:30:00", 469 | PMOpen:"13:30:00", 470 | PMClose:"15:00:00", 471 | NightOpen:"21:00:00", 472 | NightClose:"1:00:00" 473 | }, 474 | FU:{ 475 | name:"燃油", 476 | exchange:"SHF", 477 | AMOpen:"9:00:00", 478 | AMBreak:"10:15:00", 479 | AMResume:"10:30:00", 480 | AMClose:"11:30:00", 481 | PMOpen:"13:30:00", 482 | PMClose:"15:00:00" 483 | }, 484 | HC:{ 485 | name:"热卷", 486 | exchange:"SHF", 487 | AMOpen:"9:00:00", 488 | AMBreak:"10:15:00", 489 | AMResume:"10:30:00", 490 | AMClose:"11:30:00", 491 | PMOpen:"13:30:00", 492 | PMClose:"15:00:00", 493 | NightOpen:"21:00:00", 494 | NightClose:"23:00:00" 495 | }, 496 | NI:{ 497 | name:"沪镍", 498 | exchange:"SHF", 499 | AMOpen:"9:00:00", 500 | AMBreak:"10:15:00", 501 | AMResume:"10:30:00", 502 | AMClose:"11:30:00", 503 | PMOpen:"13:30:00", 504 | PMClose:"15:00:00", 505 | NightOpen:"21:00:00", 506 | NightClose:"1:00:00" 507 | }, 508 | PB:{ 509 | name:"沪铅", 510 | exchange:"SHF", 511 | AMOpen:"9:00:00", 512 | AMBreak:"10:15:00", 513 | AMResume:"10:30:00", 514 | AMClose:"11:30:00", 515 | PMOpen:"13:30:00", 516 | PMClose:"15:00:00", 517 | NightOpen:"21:00:00", 518 | NightClose:"1:00:00" 519 | }, 520 | RB:{ 521 | name:"螺纹", 522 | exchange:"SHF", 523 | AMOpen:"9:00:00", 524 | AMBreak:"10:15:00", 525 | AMResume:"10:30:00", 526 | AMClose:"11:30:00", 527 | PMOpen:"13:30:00", 528 | PMClose:"15:00:00", 529 | NightOpen:"21:00:00", 530 | NightClose:"23:00:00" 531 | }, 532 | RU:{ 533 | name:"橡胶", 534 | exchange:"SHF", 535 | AMOpen:"9:00:00", 536 | AMBreak:"10:15:00", 537 | AMResume:"10:30:00", 538 | AMClose:"11:30:00", 539 | PMOpen:"13:30:00", 540 | PMClose:"15:00:00", 541 | NightOpen:"21:00:00", 542 | NightClose:"23:00:00" 543 | }, 544 | SN:{ 545 | name:"沪锡", 546 | exchange:"SHF", 547 | AMOpen:"9:00:00", 548 | AMBreak:"10:15:00", 549 | AMResume:"10:30:00", 550 | AMClose:"11:30:00", 551 | PMOpen:"13:30:00", 552 | PMClose:"15:00:00", 553 | NightOpen:"21:00:00", 554 | NightClose:"1:00:00" 555 | }, 556 | ZN:{ 557 | name:"沪锌", 558 | exchange:"SHF", 559 | AMOpen:"9:00:00", 560 | AMBreak:"10:15:00", 561 | AMResume:"10:30:00", 562 | AMClose:"11:30:00", 563 | PMOpen:"13:30:00", 564 | PMClose:"15:00:00", 565 | NightOpen:"21:00:00", 566 | NightClose:"1:00:00" 567 | } 568 | }, 569 | Sgit:{ 570 | "AG(T+D)":{ 571 | name:"白银延期", 572 | exchange:"SGE", 573 | AMOpen:"9:00:00", 574 | AMClose:"11:30:00", 575 | PMOpen:"13:30:00", 576 | PMClose:"15:30:00", 577 | NightOpen:"20:00:00", 578 | NightClose:"2:30:00" 579 | }, 580 | "AU(T+D)":{ 581 | name:"黄金延期", 582 | exchange:"SGE", 583 | AMOpen:"9:00:00", 584 | AMClose:"11:30:00", 585 | PMOpen:"13:30:00", 586 | PMClose:"15:30:00", 587 | NightOpen:"20:00:00", 588 | NightClose:"2:30:00" 589 | }, 590 | "AU(T+N1)":{ 591 | name:"黄金单月延期", 592 | exchange:"SGE", 593 | AMOpen:"9:00:00", 594 | AMClose:"11:30:00", 595 | PMOpen:"13:30:00", 596 | PMClose:"15:30:00", 597 | NightOpen:"20:00:00", 598 | NightClose:"2:30:00" 599 | }, 600 | "AU(T+N2)":{ 601 | name:"黄金双月延期", 602 | exchange:"SGE", 603 | AMOpen:"9:00:00", 604 | AMClose:"11:30:00", 605 | PMOpen:"13:30:00", 606 | PMClose:"15:30:00", 607 | NightOpen:"20:00:00", 608 | NightClose:"2:30:00" 609 | }, 610 | IC:{ 611 | name:"中证500", 612 | exchange:"CFE", 613 | AMOpen:"9:30:00", 614 | AMClose:"11:30:00", 615 | PMOpen:"13:00:00", 616 | PMClose:"15:00:00" 617 | }, 618 | IF:{ 619 | name:"沪深300", 620 | exchange:"CFE", 621 | AMOpen:"9:30:00", 622 | AMClose:"11:30:00", 623 | PMOpen:"13:00:00", 624 | PMClose:"15:00:00" 625 | }, 626 | IH:{ 627 | name:"上证50", 628 | exchange:"CFE", 629 | AMOpen:"9:30:00", 630 | AMClose:"11:30:00", 631 | PMOpen:"13:00:00", 632 | PMClose:"15:00:00" 633 | }, 634 | T:{ 635 | name:"十债", 636 | exchange:"CFE", 637 | AMOpen:"9:15:00", 638 | AMClose:"11:30:00", 639 | PMOpen:"13:00:00", 640 | PMClose:"15:15:00" 641 | }, 642 | TF:{ 643 | name:"五债", 644 | exchange:"CFE", 645 | AMOpen:"9:15:00", 646 | AMClose:"11:30:00", 647 | PMOpen:"13:00:00", 648 | PMClose:"15:15:00" 649 | }, 650 | CF:{ 651 | name:"郑棉", 652 | exchange:"CZC", 653 | AMOpen:"9:00:00", 654 | AMBreak:"10:15:00", 655 | AMResume:"10:30:00", 656 | AMClose:"11:30:00", 657 | PMOpen:"13:30:00", 658 | PMClose:"15:00:00", 659 | NightOpen:"21:00:00", 660 | NightClose:"23:30:00" 661 | }, 662 | FG:{ 663 | name:"玻璃", 664 | exchange:"CZC", 665 | AMOpen:"9:00:00", 666 | AMBreak:"10:15:00", 667 | AMResume:"10:30:00", 668 | AMClose:"11:30:00", 669 | PMOpen:"13:30:00", 670 | PMClose:"15:00:00", 671 | NightOpen:"21:00:00", 672 | NightClose:"23:30:00" 673 | }, 674 | JR:{ 675 | name:"粳稻", 676 | exchange:"CZC", 677 | AMOpen:"9:00:00", 678 | AMBreak:"10:15:00", 679 | AMResume:"10:30:00", 680 | AMClose:"11:30:00", 681 | PMOpen:"13:30:00", 682 | PMClose:"15:00:00" 683 | }, 684 | MA:{ 685 | name:"甲醇", 686 | exchange:"CZC", 687 | AMOpen:"9:00:00", 688 | AMBreak:"10:15:00", 689 | AMResume:"10:30:00", 690 | AMClose:"11:30:00", 691 | PMOpen:"13:30:00", 692 | PMClose:"15:00:00", 693 | NightOpen:"21:00:00", 694 | NightClose:"23:30:00" 695 | }, 696 | OI:{ 697 | name:"郑油", 698 | exchange:"CZC", 699 | AMOpen:"9:00:00", 700 | AMBreak:"10:15:00", 701 | AMResume:"10:30:00", 702 | AMClose:"11:30:00", 703 | PMOpen:"13:30:00", 704 | PMClose:"15:00:00" 705 | }, 706 | PM:{ 707 | name:"普麦", 708 | exchange:"CZC", 709 | AMOpen:"9:00:00", 710 | AMBreak:"10:15:00", 711 | AMResume:"10:30:00", 712 | AMClose:"11:30:00", 713 | PMOpen:"13:30:00", 714 | PMClose:"15:00:00" 715 | }, 716 | RI:{ 717 | name:"早稻", 718 | exchange:"CZC", 719 | AMOpen:"9:00:00", 720 | AMBreak:"10:15:00", 721 | AMResume:"10:30:00", 722 | AMClose:"11:30:00", 723 | PMOpen:"13:30:00", 724 | PMClose:"15:00:00" 725 | }, 726 | RM:{ 727 | name:"菜粕", 728 | exchange:"CZC", 729 | AMOpen:"9:00:00", 730 | AMBreak:"10:15:00", 731 | AMResume:"10:30:00", 732 | AMClose:"11:30:00", 733 | PMOpen:"13:30:00", 734 | PMClose:"15:00:00", 735 | NightOpen:"21:00:00", 736 | NightClose:"23:30:00" 737 | }, 738 | RS:{ 739 | name:"菜籽", 740 | exchange:"CZC", 741 | AMOpen:"9:00:00", 742 | AMBreak:"10:15:00", 743 | AMResume:"10:30:00", 744 | AMClose:"11:30:00", 745 | PMOpen:"13:30:00", 746 | PMClose:"15:00:00" 747 | }, 748 | SF:{ 749 | name:"硅铁", 750 | exchange:"CZC", 751 | AMOpen:"9:00:00", 752 | AMBreak:"10:15:00", 753 | AMResume:"10:30:00", 754 | AMClose:"11:30:00", 755 | PMOpen:"13:30:00", 756 | PMClose:"15:00:00" 757 | }, 758 | SM:{ 759 | name:"锰硅", 760 | exchange:"CZC", 761 | AMOpen:"9:00:00", 762 | AMBreak:"10:15:00", 763 | AMResume:"10:30:00", 764 | AMClose:"11:30:00", 765 | PMOpen:"13:30:00", 766 | PMClose:"15:00:00" 767 | }, 768 | SR:{ 769 | name:"白糖", 770 | exchange:"CZC", 771 | AMOpen:"9:00:00", 772 | AMBreak:"10:15:00", 773 | AMResume:"10:30:00", 774 | AMClose:"11:30:00", 775 | PMOpen:"13:30:00", 776 | PMClose:"15:00:00", 777 | NightOpen:"21:00:00", 778 | NightClose:"23:30:00" 779 | }, 780 | TA:{ 781 | name:"PTA", 782 | exchange:"CZC", 783 | AMOpen:"9:00:00", 784 | AMBreak:"10:15:00", 785 | AMResume:"10:30:00", 786 | AMClose:"11:30:00", 787 | PMOpen:"13:30:00", 788 | PMClose:"15:00:00", 789 | NightOpen:"21:00:00", 790 | NightClose:"23:30:00" 791 | }, 792 | WH:{ 793 | name:"郑麦", 794 | exchange:"CZC", 795 | AMOpen:"9:00:00", 796 | AMBreak:"10:15:00", 797 | AMResume:"10:30:00", 798 | AMClose:"11:30:00", 799 | PMOpen:"13:30:00", 800 | PMClose:"15:00:00" 801 | }, 802 | ZC:{ 803 | name:"郑煤", 804 | exchange:"CZC", 805 | AMOpen:"9:00:00", 806 | AMBreak:"10:15:00", 807 | AMResume:"10:30:00", 808 | AMClose:"11:30:00", 809 | PMOpen:"13:30:00", 810 | PMClose:"15:00:00", 811 | NightOpen:"21:00:00", 812 | NightClose:"23:30:00" 813 | }, 814 | A:{ 815 | name:"豆一", 816 | exchange:"DCE", 817 | AMOpen:"9:00:00", 818 | AMBreak:"10:15:00", 819 | AMResume:"10:30:00", 820 | AMClose:"11:30:00", 821 | PMOpen:"13:30:00", 822 | PMClose:"15:00:00", 823 | NightOpen:"21:00:00", 824 | NightClose:"23:30:00" 825 | }, 826 | B:{ 827 | name:"豆二", 828 | exchange:"DCE", 829 | AMOpen:"9:00:00", 830 | AMBreak:"10:15:00", 831 | AMResume:"10:30:00", 832 | AMClose:"11:30:00", 833 | PMOpen:"13:30:00", 834 | PMClose:"15:00:00", 835 | NightOpen:"21:00:00", 836 | NightClose:"23:30:00" 837 | }, 838 | BB:{ 839 | name:"胶板", 840 | exchange:"DCE", 841 | AMOpen:"9:00:00", 842 | AMBreak:"10:15:00", 843 | AMResume:"10:30:00", 844 | AMClose:"11:30:00", 845 | PMOpen:"13:30:00", 846 | PMClose:"15:00:00" 847 | }, 848 | C:{ 849 | name:"玉米", 850 | exchange:"DCE", 851 | AMOpen:"9:00:00", 852 | AMBreak:"10:15:00", 853 | AMResume:"10:30:00", 854 | AMClose:"11:30:00", 855 | PMOpen:"13:30:00", 856 | PMClose:"15:00:00" 857 | }, 858 | CS:{ 859 | name:"淀粉", 860 | exchange:"DCE", 861 | AMOpen:"9:00:00", 862 | AMBreak:"10:15:00", 863 | AMResume:"10:30:00", 864 | AMClose:"11:30:00", 865 | PMOpen:"13:30:00", 866 | PMClose:"15:00:00" 867 | }, 868 | FB:{ 869 | name:"纤板", 870 | exchange:"DCE", 871 | AMOpen:"9:00:00", 872 | AMBreak:"10:15:00", 873 | AMResume:"10:30:00", 874 | AMClose:"11:30:00", 875 | PMOpen:"13:30:00", 876 | PMClose:"15:00:00" 877 | }, 878 | I:{ 879 | name:"铁矿石", 880 | exchange:"DCE", 881 | AMOpen:"9:00:00", 882 | AMBreak:"10:15:00", 883 | AMResume:"10:30:00", 884 | AMClose:"11:30:00", 885 | PMOpen:"13:30:00", 886 | PMClose:"15:00:00", 887 | NightOpen:"21:00:00", 888 | NightClose:"23:30:00" 889 | }, 890 | J:{ 891 | name:"焦炭", 892 | exchange:"DCE", 893 | AMOpen:"9:00:00", 894 | AMBreak:"10:15:00", 895 | AMResume:"10:30:00", 896 | AMClose:"11:30:00", 897 | PMOpen:"13:30:00", 898 | PMClose:"15:00:00", 899 | NightOpen:"21:00:00", 900 | NightClose:"23:30:00" 901 | }, 902 | JD:{ 903 | name:"鸡蛋", 904 | exchange:"DCE", 905 | AMOpen:"9:00:00", 906 | AMBreak:"10:15:00", 907 | AMResume:"10:30:00", 908 | AMClose:"11:30:00", 909 | PMOpen:"13:30:00", 910 | PMClose:"15:00:00" 911 | }, 912 | JM:{ 913 | name:"焦煤", 914 | exchange:"DCE", 915 | AMOpen:"9:00:00", 916 | AMBreak:"10:15:00", 917 | AMResume:"10:30:00", 918 | AMClose:"11:30:00", 919 | PMOpen:"13:30:00", 920 | PMClose:"15:00:00", 921 | NightOpen:"21:00:00", 922 | NightClose:"23:30:00" 923 | }, 924 | L:{ 925 | name:"塑料", 926 | exchange:"DCE", 927 | AMOpen:"9:00:00", 928 | AMBreak:"10:15:00", 929 | AMResume:"10:30:00", 930 | AMClose:"11:30:00", 931 | PMOpen:"13:30:00", 932 | PMClose:"15:00:00" 933 | }, 934 | M:{ 935 | name:"豆粕", 936 | exchange:"DCE", 937 | AMOpen:"9:00:00", 938 | AMBreak:"10:15:00", 939 | AMResume:"10:30:00", 940 | AMClose:"11:30:00", 941 | PMOpen:"13:30:00", 942 | PMClose:"15:00:00", 943 | NightOpen:"21:00:00", 944 | NightClose:"23:30:00" 945 | }, 946 | P:{ 947 | name:"棕榈", 948 | exchange:"DCE", 949 | AMOpen:"9:00:00", 950 | AMBreak:"10:15:00", 951 | AMResume:"10:30:00", 952 | AMClose:"11:30:00", 953 | PMOpen:"13:30:00", 954 | PMClose:"15:00:00", 955 | NightOpen:"21:00:00", 956 | NightClose:"23:30:00" 957 | }, 958 | PP:{ 959 | name:"丙烯", 960 | exchange:"DCE", 961 | AMOpen:"9:00:00", 962 | AMBreak:"10:15:00", 963 | AMResume:"10:30:00", 964 | AMClose:"11:30:00", 965 | PMOpen:"13:30:00", 966 | PMClose:"15:00:00" 967 | }, 968 | V:{ 969 | name:"PVC", 970 | exchange:"DCE", 971 | AMOpen:"9:00:00", 972 | AMBreak:"10:15:00", 973 | AMResume:"10:30:00", 974 | AMClose:"11:30:00", 975 | PMOpen:"13:30:00", 976 | PMClose:"15:00:00" 977 | }, 978 | Y:{ 979 | name:"豆油", 980 | exchange:"DCE", 981 | AMOpen:"9:00:00", 982 | AMBreak:"10:15:00", 983 | AMResume:"10:30:00", 984 | AMClose:"11:30:00", 985 | PMOpen:"13:30:00", 986 | PMClose:"15:00:00", 987 | NightOpen:"21:00:00", 988 | NightClose:"23:30:00" 989 | }, 990 | AG:{ 991 | name:"白银", 992 | exchange:"SHF", 993 | AMOpen:"9:00:00", 994 | AMBreak:"10:15:00", 995 | AMResume:"10:30:00", 996 | AMClose:"11:30:00", 997 | PMOpen:"13:30:00", 998 | PMClose:"15:00:00", 999 | NightOpen:"21:00:00", 1000 | NightClose:"2:30:00" 1001 | }, 1002 | AL:{ 1003 | name:"沪铝", 1004 | exchange:"SHF", 1005 | AMOpen:"9:00:00", 1006 | AMBreak:"10:15:00", 1007 | AMResume:"10:30:00", 1008 | AMClose:"11:30:00", 1009 | PMOpen:"13:30:00", 1010 | PMClose:"15:00:00", 1011 | NightOpen:"21:00:00", 1012 | NightClose:"1:00:00" 1013 | }, 1014 | AU:{ 1015 | name:"黄金", 1016 | exchange:"SHF", 1017 | AMOpen:"9:00:00", 1018 | AMBreak:"10:15:00", 1019 | AMResume:"10:30:00", 1020 | AMClose:"11:30:00", 1021 | PMOpen:"13:30:00", 1022 | PMClose:"15:00:00", 1023 | NightOpen:"21:00:00", 1024 | NightClose:"2:30:01" 1025 | }, 1026 | BU:{ 1027 | name:"沥青", 1028 | exchange:"SHF", 1029 | AMOpen:"9:00:00", 1030 | AMBreak:"10:15:00", 1031 | AMResume:"10:30:00", 1032 | AMClose:"11:30:00", 1033 | PMOpen:"13:30:00", 1034 | PMClose:"15:00:00", 1035 | NightOpen:"21:00:00", 1036 | NightClose:"23:00:00" 1037 | }, 1038 | CU:{ 1039 | name:"沪铜", 1040 | exchange:"SHF", 1041 | AMOpen:"9:00:00", 1042 | AMBreak:"10:15:00", 1043 | AMResume:"10:30:00", 1044 | AMClose:"11:30:00", 1045 | PMOpen:"13:30:00", 1046 | PMClose:"15:00:00", 1047 | NightOpen:"21:00:00", 1048 | NightClose:"1:00:00" 1049 | }, 1050 | FU:{ 1051 | name:"燃油", 1052 | exchange:"SHF", 1053 | AMOpen:"9:00:00", 1054 | AMBreak:"10:15:00", 1055 | AMResume:"10:30:00", 1056 | AMClose:"11:30:00", 1057 | PMOpen:"13:30:00", 1058 | PMClose:"15:00:00" 1059 | }, 1060 | HC:{ 1061 | name:"热卷", 1062 | exchange:"SHF", 1063 | AMOpen:"9:00:00", 1064 | AMBreak:"10:15:00", 1065 | AMResume:"10:30:00", 1066 | AMClose:"11:30:00", 1067 | PMOpen:"13:30:00", 1068 | PMClose:"15:00:00", 1069 | NightOpen:"21:00:00", 1070 | NightClose:"23:00:00" 1071 | }, 1072 | NI:{ 1073 | name:"沪镍", 1074 | exchange:"SHF", 1075 | AMOpen:"9:00:00", 1076 | AMBreak:"10:15:00", 1077 | AMResume:"10:30:00", 1078 | AMClose:"11:30:00", 1079 | PMOpen:"13:30:00", 1080 | PMClose:"15:00:00", 1081 | NightOpen:"21:00:00", 1082 | NightClose:"1:00:00" 1083 | }, 1084 | PB:{ 1085 | name:"沪铅", 1086 | exchange:"SHF", 1087 | AMOpen:"9:00:00", 1088 | AMBreak:"10:15:00", 1089 | AMResume:"10:30:00", 1090 | AMClose:"11:30:00", 1091 | PMOpen:"13:30:00", 1092 | PMClose:"15:00:00", 1093 | NightOpen:"21:00:00", 1094 | NightClose:"1:00:00" 1095 | }, 1096 | RB:{ 1097 | name:"螺纹", 1098 | exchange:"SHF", 1099 | AMOpen:"9:00:00", 1100 | AMBreak:"10:15:00", 1101 | AMResume:"10:30:00", 1102 | AMClose:"11:30:00", 1103 | PMOpen:"13:30:00", 1104 | PMClose:"15:00:00", 1105 | NightOpen:"21:00:00", 1106 | NightClose:"23:00:00" 1107 | }, 1108 | RU:{ 1109 | name:"橡胶", 1110 | exchange:"SHF", 1111 | AMOpen:"9:00:00", 1112 | AMBreak:"10:15:00", 1113 | AMResume:"10:30:00", 1114 | AMClose:"11:30:00", 1115 | PMOpen:"13:30:00", 1116 | PMClose:"15:00:00", 1117 | NightOpen:"21:00:00", 1118 | NightClose:"23:00:00" 1119 | }, 1120 | SN:{ 1121 | name:"沪锡", 1122 | exchange:"SHF", 1123 | AMOpen:"9:00:00", 1124 | AMBreak:"10:15:00", 1125 | AMResume:"10:30:00", 1126 | AMClose:"11:30:00", 1127 | PMOpen:"13:30:00", 1128 | PMClose:"15:00:00", 1129 | NightOpen:"21:00:00", 1130 | NightClose:"1:00:00" 1131 | }, 1132 | ZN:{ 1133 | name:"沪锌", 1134 | exchange:"SHF", 1135 | AMOpen:"9:00:00", 1136 | AMBreak:"10:15:00", 1137 | AMResume:"10:30:00", 1138 | AMClose:"11:30:00", 1139 | PMOpen:"13:30:00", 1140 | PMClose:"15:00:00", 1141 | NightOpen:"21:00:00", 1142 | NightClose:"1:00:00" 1143 | } 1144 | } 1145 | }; 1146 | -------------------------------------------------------------------------------- /nodequant/userConfig.js: -------------------------------------------------------------------------------- 1 | require("./common"); 2 | require("./systemConfig"); 3 | 4 | //系统数据库配置 5 | System_DBConfig={ 6 | Host:"127.0.0.1", 7 | Port:6379 8 | }; 9 | 10 | 11 | //行情数据库配置 12 | 13 | MarketData_DBConfig={ 14 | /* 15 | Host:"127.0.0.1", 16 | Port:8888, 17 | Password:"" 18 | */ 19 | }; 20 | 21 | //配置客户端,NodeQuant启动,会连接已经配置的交易客户端 22 | //Simnow 非交易时间可用(交易日下午4:00开始可用或者周末可用) 23 | //mdAddress:"tcp://180.168.146.187:10131" 24 | //tdAddress:"tcp://180.168.146.187:10130" 25 | 26 | //Simnow 交易时间可用 27 | //mdAddress:"tcp://180.168.146.187:10212" 28 | //tdAddress:"tcp://180.168.146.187:10202" 29 | ClientConfig={ 30 | 31 | CTP:{ 32 | userID:"simnow user", 33 | password:"simnow password", 34 | brokerID:"9999", 35 | AppID:"simnow_client_test", 36 | AuthCode:"0000000000000000", 37 | mdAddress:"tcp://180.168.146.187:10131", 38 | tdAddress:"tcp://180.168.146.187:10130" 39 | }, 40 | }; 41 | 42 | StrategyConfig={ 43 | Strategys:[ 44 | { 45 | name:"Demo", 46 | className:"DemoStrategy", 47 | symbols: { 48 | "au2112":{ 49 | //要配置在哪个交易客户端订阅该合约 50 | clientName:SupportClients.CTP 51 | } 52 | }, 53 | BarType:KBarType.Minute, //K线类型是: 分钟 54 | BarInterval:1, //1分钟K线 55 | } 56 | ] 57 | }; 58 | 59 | //该设置配合NodeQuant通知服务可以通过声音,邮件通知用户策略发生的异常 60 | NotifyExceptionConfig={ 61 | ExceptionType:[ 62 | ErrorType.Disconnected, 63 | ErrorType.OperationAfterDisconnected, 64 | ] 65 | }; 66 | -------------------------------------------------------------------------------- /nodequant/util/DateTimeUtil.js: -------------------------------------------------------------------------------- 1 | class DateTimeUtil 2 | { 3 | constructor() 4 | { 5 | 6 | } 7 | 8 | //js Date对象从0开始的月份 9 | static StrToDatetime(dateStr,timeStr) 10 | { 11 | let nowDate=new Date(); 12 | let year= nowDate.getFullYear(); 13 | let month = nowDate.getMonth(); 14 | let day= nowDate.getDate(); 15 | 16 | let hour=0; 17 | let minute=0; 18 | let second=0; 19 | 20 | if(dateStr!==undefined) 21 | { 22 | year=parseInt(dateStr.substring(0,4)); 23 | month=parseInt(dateStr.substring(4,6)); 24 | day=parseInt(dateStr.substring(6,8)); 25 | } 26 | 27 | if(timeStr!==undefined) 28 | { 29 | hour=parseInt(timeStr.substring(0,2)); 30 | minute=parseInt(timeStr.substring(3,5)); 31 | second=parseInt(timeStr.substring(6,8)); 32 | } 33 | 34 | let toDateTime = new Date(year,month-1,day,hour,minute,second); 35 | 36 | return toDateTime; 37 | } 38 | } 39 | 40 | module.exports=DateTimeUtil; -------------------------------------------------------------------------------- /nodequant/util/KBar.js: -------------------------------------------------------------------------------- 1 | 2 | class KBar{ 3 | constructor(BarId,StartDatetime,EndDatetime,Symbol,Open,High,Low,Close,Volume,OpenInterest){ 4 | this.Id = BarId; 5 | this.startDatetime = StartDatetime; 6 | this.endDatetime = EndDatetime; 7 | this.date=EndDatetime.toLocaleDateString();//Kbar的时刻为结束时间 8 | this.timeStr=StartDatetime.toLocaleTimeString();//Kbar的时刻为开始时间 9 | this.symbol=Symbol; 10 | this.openPrice=Open; 11 | this.highPrice=High; 12 | this.lowPrice=Low; 13 | this.closePrice=Close; 14 | this.volume=Volume; 15 | this.openInterest=OpenInterest; 16 | } 17 | } 18 | 19 | module.exports=KBar; -------------------------------------------------------------------------------- /nodequant/util/NodeQuantError.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/7/10. 3 | */ 4 | 5 | class NodeQuantError{ 6 | constructor(source,type,message){ 7 | this.Source=source; 8 | this.Type=type; 9 | this.Message=message; 10 | } 11 | } 12 | 13 | module.exports=NodeQuantError; -------------------------------------------------------------------------------- /nodequant/util/NodeQuantLog.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 2017/7/10. 3 | */ 4 | class NodeQuantLog{ 5 | constructor(source,type,datetimestr,message){ 6 | this.Source=source; 7 | this.Type=type; 8 | this.Message=message; 9 | this.Datetime=datetimestr; 10 | } 11 | } 12 | 13 | module.exports=NodeQuantLog; -------------------------------------------------------------------------------- /nodequant/util/Position.js: -------------------------------------------------------------------------------- 1 | //策略仓位管理器 2 | //一个合约一个仓位对象 3 | class Position { 4 | constructor() { 5 | this.strategyName = ""; 6 | this.symbol = ""; 7 | this.longPositionTradeRecordList = []; 8 | this.shortPositionTradeRecordList = []; 9 | } 10 | 11 | //获取总的 锁仓 12 | GetLockedPosition() 13 | { 14 | let longPosition=this.GetLongPosition(); 15 | let shortPosition=this.GetShortPosition(); 16 | 17 | let lockedPostion=Math.min(longPosition,shortPosition); 18 | 19 | return lockedPostion; 20 | } 21 | 22 | //获取总的 非锁 多仓 23 | GetUnLockLongPosition() 24 | { 25 | let unLockLongPosition=0; 26 | let longPosition=this.GetLongPosition(); 27 | let shortPosition=this.GetShortPosition(); 28 | 29 | if (longPosition > shortPosition) { 30 | unLockLongPosition = longPosition - shortPosition; 31 | } 32 | 33 | return unLockLongPosition; 34 | } 35 | 36 | //获取总的 非锁 空仓 37 | GetUnLockShortPosition() 38 | { 39 | let unLockShortPosition=0; 40 | let longPosition=this.GetLongPosition(); 41 | let shortPosition=this.GetShortPosition(); 42 | 43 | if (shortPosition > longPosition) { 44 | unLockShortPosition = shortPosition - longPosition; 45 | } 46 | 47 | return unLockShortPosition; 48 | } 49 | 50 | //获取总的多仓位 51 | GetLongPosition() 52 | { 53 | let longPosition=0; 54 | for(let index in this.longPositionTradeRecordList) 55 | { 56 | let tradeRecord=this.longPositionTradeRecordList[index]; 57 | longPosition+=tradeRecord.volume; 58 | } 59 | 60 | return longPosition; 61 | } 62 | 63 | //获取总的空仓位 64 | GetShortPosition() 65 | { 66 | let shortPosition=0; 67 | for(let index in this.shortPositionTradeRecordList) 68 | { 69 | let tradeRecord=this.shortPositionTradeRecordList[index]; 70 | shortPosition+=tradeRecord.volume; 71 | } 72 | 73 | return shortPosition; 74 | } 75 | 76 | //获取 多仓 持仓均价 77 | GetLongPostionAveragePrice() 78 | { 79 | let longPositionSumAmount=0; 80 | let longPositionSumVolume=0; 81 | for(let index in this.longPositionTradeRecordList) 82 | { 83 | let tradeRecord=this.longPositionTradeRecordList[index]; 84 | longPositionSumVolume += tradeRecord.volume; 85 | longPositionSumAmount += tradeRecord.price*tradeRecord.volume; 86 | } 87 | let longPositionAveragePrice=0; 88 | if(longPositionSumVolume!==0) 89 | { 90 | longPositionAveragePrice = longPositionSumAmount/longPositionSumVolume; 91 | } 92 | return longPositionAveragePrice; 93 | } 94 | 95 | //获取 空仓 持仓 均价 96 | GetShortPositionAveragePrice() 97 | { 98 | let shortPositionSumAmount=0; 99 | let shortPositionSumVolume=0; 100 | for(let index in this.shortPositionTradeRecordList) 101 | { 102 | let tradeRecord=this.shortPositionTradeRecordList[index]; 103 | shortPositionSumVolume += tradeRecord.volume; 104 | shortPositionSumAmount += tradeRecord.price*tradeRecord.volume; 105 | } 106 | let shortPositionAveragePrice=0; 107 | if(shortPositionSumVolume!==0) 108 | { 109 | shortPositionAveragePrice = shortPositionSumAmount/shortPositionSumVolume; 110 | } 111 | return shortPositionAveragePrice; 112 | } 113 | 114 | 115 | /// 116 | /// 获取合约的今天锁仓数量 117 | /// 118 | /// 今天锁仓数量 119 | GetLockedTodayPosition() 120 | { 121 | let longTdPosition = this.GetLongTodayPosition(); 122 | let shortTdPosition = this.GetShortTodayPosition(); 123 | 124 | let tdLockedPostion=Math.min(longTdPosition,shortTdPosition); 125 | 126 | return tdLockedPostion; 127 | } 128 | 129 | /// 130 | /// 获取非锁,多仓,今仓 131 | /// 132 | /// 获取非锁,多仓,今仓 数量 133 | GetUnLockLongTodayPosition() { 134 | let unLockLongTodayPosition = 0; 135 | 136 | let longTdPosition = this.GetLongTodayPosition(); 137 | let shortTdPosition = this.GetShortTodayPosition(); 138 | 139 | if (longTdPosition > shortTdPosition) { 140 | unLockLongTodayPosition = longTdPosition - shortTdPosition; 141 | } 142 | 143 | return unLockLongTodayPosition; 144 | } 145 | 146 | /// 147 | /// 获取非锁,空仓,今仓 148 | /// 149 | /// 获取非锁,空仓,今仓 数量 150 | GetUnLockShortTodayPosition() 151 | { 152 | let unLockShortTodayPosition = 0; 153 | 154 | let longTdPosition = this.GetLongTodayPosition(); 155 | let shortTdPosition = this.GetShortTodayPosition(); 156 | 157 | if (shortTdPosition > longTdPosition) { 158 | unLockShortTodayPosition = shortTdPosition - longTdPosition; 159 | } 160 | 161 | return unLockShortTodayPosition; 162 | } 163 | 164 | /// 165 | /// 获取多仓,今仓 166 | /// 167 | /// 获取多仓,今仓 数量 168 | GetLongTodayPosition() 169 | { 170 | let longTdPosition=0; 171 | let TodayTradingDay=global.NodeQuant.MainEngine.TradingDay; 172 | for(let index in this.longPositionTradeRecordList) 173 | { 174 | let tradeRecord=this.longPositionTradeRecordList[index]; 175 | if(tradeRecord.tradingDay===TodayTradingDay) 176 | { 177 | longTdPosition+=tradeRecord.volume; 178 | } 179 | } 180 | 181 | return longTdPosition; 182 | } 183 | 184 | /// 185 | /// 获取空仓,今仓 186 | /// 187 | /// 获取空仓,今仓 数量 188 | GetShortTodayPosition() 189 | { 190 | let shortTdPosition=0; 191 | let TodayTradingDay=global.NodeQuant.MainEngine.TradingDay; 192 | for(let index in this.shortPositionTradeRecordList) 193 | { 194 | let tradeRecord=this.shortPositionTradeRecordList[index]; 195 | if(tradeRecord.tradingDay===TodayTradingDay) 196 | { 197 | shortTdPosition+=tradeRecord.volume; 198 | } 199 | } 200 | return shortTdPosition; 201 | } 202 | 203 | /// 204 | /// 获取合约的 昨 锁仓 数量 205 | /// 206 | /// 昨 锁仓 数量 207 | GetLockedYesterdayPosition() 208 | { 209 | let longYdPosition = this.GetLongYesterdayPosition(); 210 | let shortYdPosition = this.GetShortYesterdayPosition(); 211 | 212 | let ydLockedPostion=Math.min(longYdPosition,shortYdPosition); 213 | 214 | return ydLockedPostion; 215 | } 216 | 217 | /// 218 | /// 获取合约的 昨 非锁 多仓 219 | /// 220 | /// 昨 非锁 多仓 数量 221 | GetUnLockLongYesterdayPosition() 222 | { 223 | let unLockLongYesterdayPosition = 0; 224 | 225 | let longYdPosition = this.GetLongYesterdayPosition(); 226 | let shortYdPosition = this.GetShortYesterdayPosition(); 227 | 228 | if (longYdPosition > shortYdPosition) { 229 | unLockLongYesterdayPosition = longYdPosition - shortYdPosition; 230 | } 231 | 232 | return unLockLongYesterdayPosition; 233 | } 234 | 235 | 236 | /// 237 | /// 获取合约的 昨 非锁 空仓 数量 238 | /// 239 | /// 昨 非锁 空仓 数量 240 | GetUnLockShortYesterdayPosition() 241 | { 242 | let unLockShortYesterdayPosition = 0; 243 | 244 | let longYdPosition = this.GetLongYesterdayPosition(); 245 | let shortYdPosition = this.GetShortYesterdayPosition(); 246 | 247 | if (shortYdPosition > longYdPosition) { 248 | unLockShortYesterdayPosition = shortYdPosition - longYdPosition; 249 | } 250 | 251 | return unLockShortYesterdayPosition; 252 | } 253 | 254 | /// 255 | /// 获取多仓,昨仓 256 | /// 257 | /// 获取多仓,昨仓 数量 258 | GetLongYesterdayPosition() 259 | { 260 | let longYdPosition=0; 261 | let TodayTradingDay=global.NodeQuant.MainEngine.TradingDay; 262 | for(let index in this.longPositionTradeRecordList) 263 | { 264 | let tradeRecord=this.longPositionTradeRecordList[index]; 265 | if(tradeRecord.tradingDay!==TodayTradingDay) 266 | { 267 | longYdPosition+=tradeRecord.volume; 268 | } 269 | } 270 | return longYdPosition; 271 | } 272 | 273 | /// 274 | /// 获取空仓,昨仓 275 | /// 276 | /// 获取空仓,昨仓 数量 277 | GetShortYesterdayPosition() 278 | { 279 | let shortYdPosition=0; 280 | let TodayTradingDay=global.NodeQuant.MainEngine.TradingDay; 281 | for(let index in this.shortPositionTradeRecordList) 282 | { 283 | let tradeRecord=this.shortPositionTradeRecordList[index]; 284 | if(tradeRecord.tradingDay!==TodayTradingDay) 285 | { 286 | shortYdPosition+=tradeRecord.volume; 287 | } 288 | } 289 | return shortYdPosition; 290 | } 291 | 292 | UpdatePosition(trade) { 293 | if (this.symbol !== trade.symbol) { 294 | let error = new NodeQuantError("StrategyEngine", ErrorType.StrategyError, "UpdatePosition not contain this symbol:" + trade.symbol); 295 | global.AppEventEmitter.emit(EVENT.OnError, error); 296 | return; 297 | } 298 | 299 | if (trade.direction === Direction.Buy) { 300 | //多方开仓,则对应多头的持仓和今仓增加 301 | if (trade.offset === OpenCloseFlagType.Open) { 302 | this.longPositionTradeRecordList.push(trade); 303 | } else if (trade.offset === OpenCloseFlagType.CloseToday) { 304 | this.CloseBuyTodayPosition(trade); 305 | 306 | if(trade.volume>0) 307 | { 308 | let error=new NodeQuantError(trade.strategyName,ErrorType.StrategyError,trade.symbol+"的 (平今仓买入 CloseToday Buy)手数多于"+trade.strategyName+"策略的( 今空仓 )持仓手数,平了账户其他策略仓位,请检查!!!") 309 | global.AppEventEmitter.emit(EVENT.OnError,error); 310 | } 311 | 312 | } else if (trade.offset === OpenCloseFlagType.CloseYesterday) { 313 | 314 | //买入平昨,对应空头的持仓和昨仓减少 315 | this.CloseBuyYesterDayPosition(trade); 316 | 317 | if(trade.volume>0) 318 | { 319 | let error=new NodeQuantError(trade.strategyName,ErrorType.StrategyError,trade.symbol+"的 (平昨仓买入 CloseYesterday Buy)手数多于"+trade.strategyName+"策略的( 昨空仓 )持仓手数,平了账户其他策略仓位,请检查!!!") 320 | global.AppEventEmitter.emit(EVENT.OnError,error); 321 | } 322 | 323 | } else if (trade.offset === OpenCloseFlagType.Close) { 324 | //买入平仓,默认先平昨天空仓,再平今空仓 325 | //有昨仓先平昨仓 326 | this.CloseBuyYesterDayPosition(trade); 327 | //再平今空仓 328 | this.CloseBuyTodayPosition(trade); 329 | 330 | if(trade.volume>0) 331 | { 332 | let error=new NodeQuantError(trade.strategyName,ErrorType.StrategyError,trade.symbol+"的平仓买入(Close Buy)手数多于"+trade.strategyName+"策略的( 空仓 )持仓手数,平了账户其他策略仓位,请检查!!!") 333 | global.AppEventEmitter.emit(EVENT.OnError,error); 334 | } 335 | 336 | } 337 | }else{ 338 | // 空头,和多头相同 339 | if(trade.offset === OpenCloseFlagType.Open){ 340 | //卖出开仓 341 | //计算开仓均价 342 | this.shortPositionTradeRecordList.push(trade); 343 | }else if(trade.offset === OpenCloseFlagType.CloseToday) 344 | { 345 | //卖出平今 346 | this.CloseSellTodayPosition(trade); 347 | 348 | if(trade.volume>0) 349 | { 350 | let error=new NodeQuantError(trade.strategyName,ErrorType.StrategyError,trade.symbol+"的( 平今仓卖出 CloseToday Sell )手数多于"+trade.strategyName+"策略的( 今多仓 )持仓手数,平了账户其他策略仓位,请检查!!!") 351 | global.AppEventEmitter.emit(EVENT.OnError,error); 352 | } 353 | 354 | }else if(trade.offset === OpenCloseFlagType.CloseYesterday){ 355 | //卖出平昨 356 | this.CloseSellYesterDayPosition(trade); 357 | 358 | if(trade.volume>0) 359 | { 360 | let error=new NodeQuantError(trade.strategyName,ErrorType.StrategyError,trade.symbol+"的( 平昨仓卖出 CloseYesterday Sell )手数多于"+trade.strategyName+"策略的( 昨多仓 )持仓手数,平了账户其他策略仓位,请检查!!!") 361 | global.AppEventEmitter.emit(EVENT.OnError,error); 362 | } 363 | 364 | }else if(trade.offset === OpenCloseFlagType.Close){ 365 | //卖出平仓,默认先平昨天多仓,再平今多仓 366 | this.CloseSellYesterDayPosition(trade); 367 | //再平今空仓 368 | this.CloseSellTodayPosition(trade); 369 | //更新仓位后,trade.volume还不变为0,代表平仓多于策略持仓,平了账户别人的仓位!!! 370 | if(trade.volume>0) 371 | { 372 | let error=new NodeQuantError(trade.strategyName,ErrorType.StrategyError,trade.symbol+"的( 平仓卖出 Close Sell )手数多于"+trade.strategyName+"策略的( 多仓 )持仓手数,平了账户其他策略仓位,请检查!!!") 373 | global.AppEventEmitter.emit(EVENT.OnError,error); 374 | } 375 | } 376 | } 377 | } 378 | 379 | //今仓: 平仓买入->开仓卖出(空仓)的成交记录要更新 380 | CloseBuyTodayPosition(trade) 381 | { 382 | //一共要更新多少手,平今仓的仓位,空仓记录可能有多条今仓记录,需要一直减 383 | if(trade===undefined || trade.volume<=0) 384 | { 385 | return; 386 | } 387 | 388 | for (let index in this.shortPositionTradeRecordList) 389 | { 390 | let tradeRecord=this.shortPositionTradeRecordList[index]; 391 | 392 | //非当天的空仓不做处理 393 | if(tradeRecord.tradingDay !== trade.tradingDay) 394 | { 395 | continue; 396 | } 397 | 398 | let AvaVolume = tradeRecord.volume; 399 | 400 | let CloseVolume= Math.min(AvaVolume, trade.volume); 401 | 402 | tradeRecord.volume -= CloseVolume; 403 | //手数目为0的成交记录,要删除掉 404 | if(tradeRecord.volume===0) 405 | { 406 | delete this.shortPositionTradeRecordList[index]; 407 | } 408 | 409 | //判断是否更新完 410 | trade.volume -= CloseVolume; 411 | 412 | if (trade.volume===0) 413 | { 414 | break; 415 | } 416 | } 417 | } 418 | 419 | //昨仓: 平仓买入->开仓卖出(空仓)的成交记录要更新 420 | CloseBuyYesterDayPosition(trade) 421 | { 422 | //一共要更新多少手,平今仓的仓位,空仓记录可能有多条今仓记录,需要一直减 423 | if(trade===undefined || trade.volume<=0) 424 | { 425 | return; 426 | } 427 | 428 | for (let index in this.shortPositionTradeRecordList) 429 | { 430 | let tradeRecord = this.shortPositionTradeRecordList[index]; 431 | 432 | //当天的空仓不做处理 433 | if(tradeRecord.tradingDay === trade.tradingDay) 434 | { 435 | continue; 436 | } 437 | 438 | let AvaVolume = tradeRecord.volume; 439 | 440 | let CloseVolume= Math.min(AvaVolume, trade.volume); 441 | 442 | tradeRecord.volume -= CloseVolume; 443 | //手数目为0的成交记录,要删除掉 444 | if(tradeRecord.volume===0) 445 | { 446 | delete this.shortPositionTradeRecordList[index]; 447 | } 448 | 449 | //判断是否更新完 450 | trade.volume -= CloseVolume; 451 | 452 | if (trade.volume === 0) 453 | { 454 | break; 455 | } 456 | } 457 | } 458 | 459 | //今仓:平仓卖出 -> 开仓买入(多仓)的成交记录要更新 460 | CloseSellTodayPosition(trade) 461 | { 462 | //一共要更新多少手,平今仓的仓位,空仓记录可能有多条今仓记录,需要一直减 463 | if(trade===undefined || trade.volume<=0) 464 | { 465 | return; 466 | } 467 | 468 | for (let index in this.longPositionTradeRecordList) 469 | { 470 | let tradeRecord=this.longPositionTradeRecordList[index]; 471 | 472 | //非当天的空仓不做处理 473 | if(tradeRecord.tradingDay !== trade.tradingDay) 474 | { 475 | continue; 476 | } 477 | 478 | let AvaVolume = tradeRecord.volume; 479 | 480 | let CloseVolume= Math.min(AvaVolume, trade.volume); 481 | 482 | tradeRecord.volume -= CloseVolume; 483 | //手数目为0的成交记录,要删除掉 484 | if(tradeRecord.volume===0) 485 | { 486 | delete this.longPositionTradeRecordList[index]; 487 | } 488 | 489 | //判断是否更新完 490 | trade.volume -= CloseVolume; 491 | 492 | if (trade.volume===0) 493 | { 494 | break; 495 | } 496 | } 497 | } 498 | 499 | CloseSellYesterDayPosition(trade) 500 | { 501 | //一共要更新多少手,平今仓的仓位,空仓记录可能有多条今仓记录,需要一直减 502 | if(trade===undefined || trade.volume<=0) 503 | { 504 | return; 505 | } 506 | 507 | for (let index in this.longPositionTradeRecordList) 508 | { 509 | let tradeRecord=this.longPositionTradeRecordList[index]; 510 | 511 | //当天的多仓不做处理 512 | if(tradeRecord.tradingDay === trade.tradingDay) 513 | { 514 | continue; 515 | } 516 | 517 | let AvaVolume = tradeRecord.volume; 518 | 519 | let CloseVolume= Math.min(AvaVolume, trade.volume); 520 | 521 | tradeRecord.volume -= CloseVolume; 522 | //手数目为0的成交记录,要删除掉 523 | if(tradeRecord.volume===0) 524 | { 525 | delete this.longPositionTradeRecordList[index]; 526 | } 527 | 528 | //判断是否更新完 529 | trade.volume -= CloseVolume; 530 | 531 | if (trade.volume===0) 532 | { 533 | break; 534 | } 535 | } 536 | } 537 | } 538 | 539 | module.exports=Position; -------------------------------------------------------------------------------- /nodequant/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /nodequant/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 |

<%= title %>

9 |

Welcome to <%= title %>

10 | 11 | 12 | -------------------------------------------------------------------------------- /test/TestNodeQuant.json.postman_collection: -------------------------------------------------------------------------------- 1 | { 2 | "variables": [], 3 | "info": { 4 | "name": "TestNodeQuant copy", 5 | "_postman_id": "d4d80f26-b0fe-62bb-ab49-739d9e9507a5", 6 | "description": "", 7 | "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" 8 | }, 9 | "item": [ 10 | { 11 | "name": "localhost:3000/sendStopLimitOrder?contractName=i1709&openclose=open&direction=buy&volume=50&limitPrice=425&stopPriceCondition=gt&stopPrice=425", 12 | "request": { 13 | "url": "localhost:3000/sendStopLimitOrder?contractName=i1709&openclose=open&direction=buy&volume=1&limitPrice=436&stopPriceCondition=lt&stopPrice=428", 14 | "method": "GET", 15 | "header": [], 16 | "body": {}, 17 | "description": "" 18 | }, 19 | "response": [] 20 | }, 21 | { 22 | "name": "localhost:3000/sendMarketIfTouchedOrder?contractName=i1709&openclose=open&direction=buy&volume=50&stopPriceCondition=gt&stopPrice=425", 23 | "request": { 24 | "url": "localhost:3000/sendMarketIfTouchedOrder?contractName=i1709&openclose=open&direction=buy&volume=1&stopPriceCondition=gt&stopPrice=432", 25 | "method": "GET", 26 | "header": [], 27 | "body": {}, 28 | "description": "" 29 | }, 30 | "response": [] 31 | }, 32 | { 33 | "name": "localhost:3000/sendFillOrKillLimitOrder?contractName=i1706&openclose=open&direction=buy&volume=50&limitPrice=3489", 34 | "request": { 35 | "url": { 36 | "raw": "localhost:3000/sendFillOrKillLimitOrder?contractName=i1901&openclose=open&direction=buy&volume=1&limitPrice=520", 37 | "host": [ 38 | "localhost" 39 | ], 40 | "port": "3000", 41 | "path": [ 42 | "sendFillOrKillLimitOrder" 43 | ], 44 | "query": [ 45 | { 46 | "key": "contractName", 47 | "value": "i1901", 48 | "equals": true, 49 | "description": "" 50 | }, 51 | { 52 | "key": "openclose", 53 | "value": "open", 54 | "equals": true, 55 | "description": "" 56 | }, 57 | { 58 | "key": "direction", 59 | "value": "buy", 60 | "equals": true, 61 | "description": "" 62 | }, 63 | { 64 | "key": "volume", 65 | "value": "1", 66 | "equals": true, 67 | "description": "" 68 | }, 69 | { 70 | "key": "limitPrice", 71 | "value": "520", 72 | "equals": true, 73 | "description": "" 74 | } 75 | ], 76 | "variable": [] 77 | }, 78 | "method": "GET", 79 | "header": [], 80 | "body": {}, 81 | "description": "" 82 | }, 83 | "response": [] 84 | }, 85 | { 86 | "name": "localhost:3000/sendFillAndKillLimitOrder?contractName=i1712&openclose=open&direction=buy&volume=50&limitPrice=3489", 87 | "request": { 88 | "url": { 89 | "raw": "localhost:3000/sendFillAndKillLimitOrder?contractName=i1901&openclose=open&direction=buy&volume=1&limitPrice=517", 90 | "host": [ 91 | "localhost" 92 | ], 93 | "port": "3000", 94 | "path": [ 95 | "sendFillAndKillLimitOrder" 96 | ], 97 | "query": [ 98 | { 99 | "key": "contractName", 100 | "value": "i1901", 101 | "equals": true, 102 | "description": "" 103 | }, 104 | { 105 | "key": "openclose", 106 | "value": "open", 107 | "equals": true, 108 | "description": "" 109 | }, 110 | { 111 | "key": "direction", 112 | "value": "buy", 113 | "equals": true, 114 | "description": "" 115 | }, 116 | { 117 | "key": "volume", 118 | "value": "1", 119 | "equals": true, 120 | "description": "" 121 | }, 122 | { 123 | "key": "limitPrice", 124 | "value": "517", 125 | "equals": true, 126 | "description": "" 127 | } 128 | ], 129 | "variable": [] 130 | }, 131 | "method": "GET", 132 | "header": [], 133 | "body": {}, 134 | "description": "" 135 | }, 136 | "response": [] 137 | }, 138 | { 139 | "name": "localhost:3000/sendMarketOrder?contractName=T1709&openclose=open&direction=buy&volume=1", 140 | "request": { 141 | "url": "localhost:3000/sendMarketOrder?contractName=T1709&openclose=open&direction=buy&volume=1", 142 | "method": "GET", 143 | "header": [], 144 | "body": {}, 145 | "description": "" 146 | }, 147 | "response": [] 148 | }, 149 | { 150 | "name": "localhost:3000/sendLimitOrder?contractName=IF1706&openclose=open&direction=buy&volume=1&limitPrice=3489", 151 | "request": { 152 | "url": { 153 | "raw": "localhost:3000/sendLimitOrder?contractName=au1812&openclose=open&direction=buy&volume=1&limitPrice=278", 154 | "host": [ 155 | "localhost" 156 | ], 157 | "port": "3000", 158 | "path": [ 159 | "sendLimitOrder" 160 | ], 161 | "query": [ 162 | { 163 | "key": "contractName", 164 | "value": "au1812", 165 | "equals": true, 166 | "description": "" 167 | }, 168 | { 169 | "key": "openclose", 170 | "value": "open", 171 | "equals": true, 172 | "description": "" 173 | }, 174 | { 175 | "key": "direction", 176 | "value": "buy", 177 | "equals": true, 178 | "description": "" 179 | }, 180 | { 181 | "key": "volume", 182 | "value": "1", 183 | "equals": true, 184 | "description": "" 185 | }, 186 | { 187 | "key": "limitPrice", 188 | "value": "278", 189 | "equals": true, 190 | "description": "" 191 | } 192 | ], 193 | "variable": [] 194 | }, 195 | "method": "GET", 196 | "header": [], 197 | "body": {}, 198 | "description": "" 199 | }, 200 | "response": [] 201 | }, 202 | { 203 | "name": "localhost:3000/cancelOrder", 204 | "request": { 205 | "url": "localhost:3000/cancelOrder", 206 | "method": "GET", 207 | "header": [], 208 | "body": {}, 209 | "description": "" 210 | }, 211 | "response": [] 212 | }, 213 | { 214 | "name": "localhost:3000/subscribeContract?contractName=i1709", 215 | "request": { 216 | "url": "localhost:3000/subscribeContract?contractName=i1709", 217 | "method": "GET", 218 | "header": [], 219 | "body": {}, 220 | "description": "" 221 | }, 222 | "response": [] 223 | }, 224 | { 225 | "name": "localhost:3000/queryInvestorPosition", 226 | "request": { 227 | "url": "localhost:3000/queryInvestorPosition?clientName=CTP", 228 | "method": "GET", 229 | "header": [], 230 | "body": {}, 231 | "description": "" 232 | }, 233 | "response": [] 234 | }, 235 | { 236 | "name": "localhost:3000/queryCommissionRate?contractName=i1801&clientName=CTP", 237 | "request": { 238 | "url": { 239 | "raw": "localhost:3000/queryCommissionRate?contractName=i1901&clientName=CTP", 240 | "host": [ 241 | "localhost" 242 | ], 243 | "port": "3000", 244 | "path": [ 245 | "queryCommissionRate" 246 | ], 247 | "query": [ 248 | { 249 | "key": "contractName", 250 | "value": "i1901", 251 | "equals": true, 252 | "description": "" 253 | }, 254 | { 255 | "key": "clientName", 256 | "value": "CTP", 257 | "equals": true, 258 | "description": "" 259 | } 260 | ], 261 | "variable": [] 262 | }, 263 | "method": "GET", 264 | "header": [], 265 | "body": {}, 266 | "description": "" 267 | }, 268 | "response": [] 269 | }, 270 | { 271 | "name": "localhost:3000/queryTradingAccount?clientName=CTP", 272 | "request": { 273 | "url": "localhost:3000/queryTradingAccount?clientName=CTP", 274 | "method": "GET", 275 | "header": [], 276 | "body": {}, 277 | "description": "" 278 | }, 279 | "response": [] 280 | }, 281 | { 282 | "name": "localhost:3001/getHourMinChartData?contractName=au0000", 283 | "request": { 284 | "url": "localhost:3001/getHourMinChartData?contractName=au0000", 285 | "method": "GET", 286 | "header": [], 287 | "body": {}, 288 | "description": "" 289 | }, 290 | "response": [] 291 | }, 292 | { 293 | "name": "localhost:3000/getTradingDay", 294 | "request": { 295 | "url": "localhost:3000/getTradingDay", 296 | "method": "GET", 297 | "header": [], 298 | "body": {}, 299 | "description": "" 300 | }, 301 | "response": [] 302 | }, 303 | { 304 | "name": "localhost:3000/querySettlementInfo?clientName=CTP", 305 | "request": { 306 | "url": { 307 | "raw": "localhost:3000/querySettlementInfo?clientName=CTP", 308 | "host": [ 309 | "localhost" 310 | ], 311 | "port": "3000", 312 | "path": [ 313 | "querySettlementInfo" 314 | ], 315 | "query": [ 316 | { 317 | "key": "clientName", 318 | "value": "CTP", 319 | "equals": true, 320 | "description": "" 321 | } 322 | ], 323 | "variable": [] 324 | }, 325 | "method": "GET", 326 | "header": [], 327 | "body": {}, 328 | "description": "" 329 | }, 330 | "response": [] 331 | } 332 | ] 333 | } -------------------------------------------------------------------------------- /test/readme.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangshuiyong/nodequant/408e985555fbcfdb6be220acb4af95e192f27268/test/readme.docx --------------------------------------------------------------------------------