├── .idea
├── .gitignore
├── inspectionProfiles
│ ├── Project_Default.xml
│ └── profiles_settings.xml
├── modules.xml
├── starquant.iml
└── vcs.xml
├── LICENSE
├── README.md
├── data
└── iquantdb.sql
├── log
└── error_2023-03-10.txt
├── quant
├── __init__.py
├── account.py
├── api
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── gmapi.cpython-38.pyc
│ │ ├── singleton.cpython-38.pyc
│ │ └── tdxapi.cpython-38.pyc
│ ├── futuapi.py
│ ├── gmapi.py
│ ├── singleton.py
│ └── tdxapi.py
├── brokers
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── broker.cpython-38.pyc
│ │ └── myquantbroker.cpython-38.pyc
│ ├── broker.py
│ └── myquantbroker.py
├── config.ini
├── empyrical
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── _version.cpython-38.pyc
│ │ ├── stats.cpython-38.pyc
│ │ └── utils.cpython-38.pyc
│ ├── _version.py
│ ├── stats.py
│ └── utils.py
├── enums
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── brokers.cpython-38.pyc
│ │ ├── dfcolumns.cpython-38.pyc
│ │ ├── orderside.cpython-38.pyc
│ │ ├── ratings.cpython-38.pyc
│ │ ├── scopes.cpython-38.pyc
│ │ ├── strategys.cpython-38.pyc
│ │ └── strategytype.cpython-38.pyc
│ ├── brokers.py
│ ├── dfcolumns.py
│ ├── orderside.py
│ ├── ratings.py
│ ├── scopes.py
│ ├── strategys.py
│ └── strategytype.py
├── indicators.py
├── job
│ ├── __init__.py
│ ├── run_bt.bat
│ └── run_live.bat
├── logger.py
├── metric.py
├── model
│ ├── .idea
│ │ ├── inspectionProfiles
│ │ │ └── profiles_settings.xml
│ │ ├── misc.xml
│ │ ├── model.iml
│ │ ├── modules.xml
│ │ └── workspace.xml
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── asset.cpython-38.pyc
│ │ ├── backtestrecord.cpython-38.pyc
│ │ ├── executionreport.cpython-38.pyc
│ │ ├── myorder.cpython-38.pyc
│ │ ├── order.cpython-38.pyc
│ │ ├── position.cpython-38.pyc
│ │ ├── setting.cpython-38.pyc
│ │ ├── stockindicator.cpython-38.pyc
│ │ ├── stockpool.cpython-38.pyc
│ │ └── traderecord.cpython-38.pyc
│ ├── asset.py
│ ├── backtestrecord.py
│ ├── executionreport.py
│ ├── log.py
│ ├── myorder.py
│ ├── order.py
│ ├── position.py
│ ├── setting.py
│ ├── stockindicator.py
│ ├── stockpool.py
│ └── traderecord.py
├── picker.py
├── quantengine.py
├── reporter.py
├── signalengine.py
├── signals.py
├── signals
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── kdjsignal.cpython-38.pyc
│ │ ├── macdsignal.cpython-38.pyc
│ │ └── signalbase.cpython-38.pyc
│ ├── kdjsignal.py
│ ├── macdsignal.py
│ └── signalbase.py
├── startengine.py
├── startengine_bt.py
├── startengine_live.py
├── stockdata.py
├── strategyengine.py
├── strategys
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── gridstrategy.cpython-38.pyc
│ │ ├── meanrevertingstrategy.cpython-38.pyc
│ │ └── strategybase.cpython-38.pyc
│ ├── gridstrategy.py
│ ├── meanrevertingstrategy.py
│ └── strategybase.py
├── trader.py
├── updateposition.py
└── util
│ ├── __init__.py
│ ├── __pycache__
│ ├── __init__.cpython-38.pyc
│ ├── configutil.cpython-38.pyc
│ ├── datetimeutil.cpython-38.pyc
│ ├── fileutil.cpython-38.pyc
│ ├── globalvars.cpython-38.pyc
│ ├── numberutil.cpython-38.pyc
│ ├── pdutil.cpython-38.pyc
│ ├── putil.cpython-38.pyc
│ ├── stockutil.cpython-38.pyc
│ └── stringutil.cpython-38.pyc
│ ├── configutil.py
│ ├── datetimeutil.py
│ ├── enumutil.py
│ ├── fileutil.py
│ ├── globalvars.py
│ ├── numberutil.py
│ ├── pdutil.py
│ ├── putil.py
│ ├── redisutil.py
│ ├── stockutil.py
│ └── stringutil.py
└── requirements.txt
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/starquant.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # starquant<星球量化>
2 |
3 | starquant是基于掘金量化系统进行开发的量化交易框架,支持本地部署,支持断点回测,
4 | 支持自定义交易策略,选股数据及交易数据本地存贮及本地自定义统计。
5 | [图文使用说明](https://articles.zsxq.com/id_dnsu1lupjbr9.html) [视频使用说明](https://www.bilibili.com/video/BV1cb411o7oM/?vd_source=477f34fda05c82844e1e16ac83810323)
6 |
7 | ### 环境说明:
8 |
9 | * 支持的系统版本:Windows 10
10 |
11 | * 支持的Python版本: python 3.8 及以上版本
12 |
13 | * 掘金客户端:v3.16
14 |
15 | * 掘金API版本:3.0.159 (pip install gm==3.0.159)
16 |
17 | * Mysql: v5.0.96
18 |
19 | ### 安装说明
20 |
21 | * 必要库安装:
22 | ```
23 | pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
24 | ```
25 | ### 使用说明
26 | #### 掘金客户端配置:
27 | 1. 下载安装掘金客户端
28 |
29 | 首先从[掘金官网](https://myquant.cn) 下载掘金客户端v3.16,安装并注册帐号,具体可参考[官网指引](https://myquant.cn/docs/guide/35#32961c39feb7af92)
30 |
31 | 2. 注册帐号并登录客户端创建一个模拟的交易帐号。
32 |
33 | 3. 新建一个空策略,并记录策略ID
34 |
35 | 4. 挂接策略到交易帐号
36 |
37 | #### starquant量化交易框架参数设置:
38 |
39 | 1. 修改配置文件
40 |
41 | 配置文件路径:\starquant\quant\config.ini
42 |
43 | ```
44 | [TOKEN]
45 | ##掘金token
46 | gmtoken = xxxxxx
47 | [ACCOUNT]
48 |
49 | ##绑定帐号的交易策略id
50 | strategy_id= 71878222-a222-222-2222-5811220c517b
51 |
52 | ##绑定帐号的交易策略id
53 | backtest_strategy_id= 71878222-a222-222-2222-5811220c517b
54 |
55 | ##指定连接数据库信息
56 | [DATABASE]
57 | tradedb = mysql+mysqlconnector://root:111111@localhost:3306/starquant
58 |
59 | ##掘金客户端安装路径
60 | [GOLDMINER]
61 | path =D:\Goldminer3\Hongshu Goldminer3\goldminer3.exe
62 | ```
63 |
64 | 2. 数据库创建
65 |
66 | - 创建名称为starquantdb的空mysql数据库
67 |
68 | - 运行脚本数据库表:
69 | ```
70 | mysql -uroot -p111111 stockdb < /iqunat/data/iquantdb.sql
71 | ```
72 | 3. 修改setting表中,帐号ID字段值为你掘金创建的模拟交易帐号ID
73 |
74 | 4. 交易标的股票代码设置:
75 |
76 | 修改代码文件 starquant\quant\quantengine.py 可添加交易标的股票代码
77 |
78 | 5. 回测入口
79 |
80 | 运行
81 | ```
82 | python starquant\quant\startengine_bt.py
83 | ```
84 |
85 | 6. 实盘交易入口
86 |
87 | 运行
88 | ```
89 | python starquant\quant\startengine_live.py
90 | ```
91 |
92 |
--------------------------------------------------------------------------------
/log/error_2023-03-10.txt:
--------------------------------------------------------------------------------
1 | 2023-03-10 16:01:00,751 - mylogger - ERROR - 4
2 |
--------------------------------------------------------------------------------
/quant/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/__init__.py
--------------------------------------------------------------------------------
/quant/account.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:帐号类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from gm.enum import MODE_BACKTEST
10 | from quant.metric import Metric
11 |
12 | class Account():
13 | # 初始化交易参数
14 | def __init__(self,context):
15 | # 最大仓位
16 | self.MAX_POSITION=context.setting.max_position
17 | # 单股最大创位
18 | self.MAX_SINGLE_POSITION = context.setting.max_single_position
19 | # 最大持股数
20 | self.MAX_STOCK_NUMBER=context.setting.max_stock_number
21 | # 单股单次购买金额
22 | self.PER_BUY_AMOUNT=context.setting.per_buy_amount
23 | # 经纪商myquant,xtquant
24 | self.broker = context.setting.broker
25 | # 帐号ID
26 | self.account_id = context.setting.account_id
27 | # 帐号名称
28 | self.name=context.setting.name
29 | # 帐号类型
30 | self.account_type=context.setting.account_type
31 | # 初始总资金
32 | self.initial_capital = context.setting.initial_capital
33 | # #
34 | self.context=context
35 | #
36 | # 获取帐号ID
37 | def get_account_id(self):
38 | ret=self.account_id
39 | if self.context.mode==MODE_BACKTEST:
40 | if self.context.backtest_account_id=="":
41 | ret="{}{}".format(self.context.setting.account_id, self.context.backtest_id)
42 | else:
43 | ret=self.context.backtest_account_id
44 | return ret
45 |
46 | #打印显示当前帐号交易设置信息
47 | def display_run_param(self):
48 | print("=========================交易设置参数信息===============================")
49 | func_Account_type=lambda x:"实盘帐号" if x==1 else "测试帐号"
50 | print("当前运行帐号:{},id:{},帐号类型:{}".format(self.name,self.account_id,func_Account_type(self.account_type)))
51 | print("最大持仓位:{:.2f}%".format(self.MAX_POSITION*100))
52 | print("最大持股数:{}".format(self.MAX_STOCK_NUMBER))
53 | print("单股单次购买金额:{}".format(self.PER_BUY_AMOUNT))
54 | print("最大单股持仓位:{:.2f}%".format(self.MAX_SINGLE_POSITION*100))
55 |
56 | # 更新最大仓位设置
57 | def update_position_setting(self):
58 | win_rate= Metric().get_win_rate(account_id=self.get_account_id())
59 | pos=self.get_kelly_position(win_rate[0],win_rate[1],win_rate[2])
60 | # 更新最大仓位设置
61 | self.MAX_POSITION=pos
62 | # 最大持股数
63 | # 单股单次购买金额
64 | if self.PER_BUY_AMOUNT<(self.MAX_POSITION/self.MAX_STOCK_NUMBER)*self.initial_capital:
65 | self.PER_BUY_AMOUNT= (self.MAX_POSITION/self.MAX_STOCK_NUMBER)*self.initial_capital
66 | # 凯利公式计算最大仓位
67 | @classmethod
68 | def get_kelly_position(self,win_rate,exp_win,exp_loss):
69 | ret=0.1
70 | if (win_rate>0) and (exp_win>0):
71 | ret=win_rate-((1-win_rate)/exp_win*abs(exp_loss))
72 | if ret<0:
73 | ret=0
74 | return ret
75 |
76 |
77 | if __name__=="__main__":
78 | pos=Account.get_kelly_position(0.21,0.0363,-0.0032)
79 | print(pos)
80 |
--------------------------------------------------------------------------------
/quant/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/api/__init__.py
--------------------------------------------------------------------------------
/quant/api/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/api/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/api/__pycache__/gmapi.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/api/__pycache__/gmapi.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/api/__pycache__/singleton.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/api/__pycache__/singleton.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/api/__pycache__/tdxapi.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/api/__pycache__/tdxapi.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/api/futuapi.py:
--------------------------------------------------------------------------------
1 | from futu import *
2 | import pandas as pd
3 |
4 | from quant.api.singleton import singleton
5 | from quant.logger import Logger
6 |
7 | pd.set_option('display.max_rows', 5000)
8 | pd.set_option('display.max_columns', 5000)
9 | pd.set_option('display.width', 1000)
10 |
11 | # 富途接口
12 | @singleton
13 | class FutuApi():
14 | # 拙金数据API封装
15 | # quote_ctx = None
16 | def __init__(self):
17 | self.quote_ctx = OpenQuoteContext(host='127.0.0.1', port=11111)
18 |
19 | def get_stock_info(self,code_list):
20 | ret, data =self.quote_ctx.get_stock_basicinfo(market=Market.SH, code_list= code_list)
21 | if ret == RET_OK:
22 | data['code']=data['code'].apply(lambda x:x.split('.')[1])
23 | data.set_index('code',inplace=True)
24 | df=data.loc[:,['name']]
25 | # loginfo(df)
26 | return df
27 | else:
28 | Logger().loginfo('error:', data)
29 | # Logger().loginfo('******************************************')
30 |
31 | #获取资金流入量(单位是万)
32 | def get_stock_capital_in(self,code):
33 | ret, data = self.quote_ctx.get_capital_distribution(code)
34 | inflow=0
35 | if ret == RET_OK:
36 | inflow = round((data['capital_in_big'][0] + data['capital_in_mid'][0] + data['capital_in_small'][0] -
37 | data['capital_out_big'][0] - data['capital_out_mid'][0] - data['capital_out_small'][
38 | 0]) / 10000, 2)
39 | return inflow
40 |
41 | #获取大单资金流入量(单位是万)
42 | def get_stock_capital_in_big(self,code):
43 | ret, data = self.quote_ctx.get_capital_distribution(code)
44 | inflow=0
45 | if ret == RET_OK:
46 | inflow = round((data['capital_in_big'][0] - data['capital_out_big'][0]) / 10000, 2)
47 | return inflow
48 |
49 | #获取大单资金流入量(单位是万)
50 | def get_rt_ticker(self,code):
51 | ret_sub, err_message = self.quote_ctx.subscribe(code, [SubType.TICKER], subscribe_push=False)
52 | # 先订阅逐笔类型。订阅成功后 FutuOpenD 将持续收到服务器的推送,False 代表暂时不需要推送给脚本
53 | if ret_sub == RET_OK: # 订阅成功
54 | ret, data = self.quote_ctx.get_rt_ticker(code, 1000) # 获取港股00700最近2个逐笔
55 | if ret == RET_OK:
56 | # print(data)
57 | # print(data['turnover'][0]) # 取第一条的成交金额
58 | # print(data['turnover'].values.tolist()) # 转为 list
59 | pass
60 |
61 | else:
62 | print('error:', data)
63 | else:
64 | print('subscription failed', err_message)
65 |
66 | return data
67 | # quote_ctx.close() # 关闭当条连接,FutuOpenD 会在1分钟后自动取消相应股票相应类型的订阅
68 |
69 | def get_hostory_kline(self,symbol, start=None, end=None):
70 | ret=self.quote_ctx.request_history_kline(symbol, start=start, end=end, ktype=KLType.K_DAY, autype=AuType.QFQ, fields=[KL_FIELD.ALL],
71 | max_count=1000, page_req_key=None, extended_time=False)
72 | df=ret[1]
73 | return df
74 |
75 | #
76 | def get_board(self,symbol_list):
77 | ret, data = self.quote_ctx.get_owner_plate(symbol_list)
78 | return data
79 |
80 |
81 | if __name__=='__main__':
82 | # df=FutuApi().get_board(['SH.600030'])
83 | # print(df)
84 | # ret, data = self.quote_ctx.get_owner_plate(['SH.600030'])
85 | df=FutuApi().get_hostory_kline(symbol='SH.BK0047')
86 | print(df)
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/quant/api/singleton.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:单例
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | def singleton(cls):
10 | _instance = {} # 新建空字典; 不要_instance = None, 否则 inner 函数中赋值时,_instance 只会在函数作用域搜索,判断时会报错;
11 |
12 | def inner(): # 判断是否已有实例,如无,则新建一个实例并返回
13 | if cls not in _instance:
14 | _instance[cls] = cls()
15 |
16 | return _instance[cls] # 返回的是实例对象
17 |
18 | return inner
19 |
20 |
21 | if __name__=="__main__":
22 | @singleton
23 | class Cls():
24 | def __init__(self):
25 | pass # 可以照常添加属性和方法
26 |
27 |
28 | cls1 = Cls()
29 | cls2 = Cls()
30 | print(cls1 is cls2 ) # True
31 |
--------------------------------------------------------------------------------
/quant/brokers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/brokers/__init__.py
--------------------------------------------------------------------------------
/quant/brokers/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/brokers/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/brokers/__pycache__/broker.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/brokers/__pycache__/broker.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/brokers/__pycache__/myquantbroker.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/brokers/__pycache__/myquantbroker.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/brokers/broker.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:经纪商基类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | import datetime
10 | from abc import abstractmethod
11 | from gm.enum import MODE_BACKTEST
12 | from quant.model.order import Order
13 | from quant.model.position import Position
14 | from quant.util import numberutil
15 |
16 | # 经纪商基类
17 | class Broker(object):
18 | def __init__(self,context):
19 | self.context=context
20 |
21 | #限价买入指定量的股票
22 | @abstractmethod
23 | def buy(self,symbol,volume,price,factor,strategy,indicator):
24 | raise NotImplementedError('Method is required!')
25 |
26 | # 限价买出指定量的股票
27 | # @abstractmethod
28 | def sell(self,symbol,volume,price,factor,strategy,indicator):
29 | raise NotImplementedError('Method is required!')
30 |
31 | # 清仓
32 | @abstractmethod
33 | def sell_out(self,symbol,price,factor,strategy,indicator):
34 | raise NotImplementedError('Method is required!')
35 |
36 | #检查是否还有可买仓位
37 | @abstractmethod
38 | def hasPosition(self):
39 | raise NotImplementedError('Method is required!')
40 |
41 | #检查是否超过持仓股票数限制
42 | @abstractmethod
43 | def isUnderMaxStockNumber(self):
44 | raise NotImplementedError('Method is required!')
45 |
46 | #检查某股票是否超过持仓股票数限制
47 | @abstractmethod
48 | def isUnderMaxStockPosition(self,symbol):
49 | raise NotImplementedError('Method is required!')
50 |
51 | # 判断是否有未完成的订单
52 | @abstractmethod
53 | def has_unfinished_orders(self,symbol):
54 | raise NotImplementedError('Method is required!')
55 |
56 | # 金额转为量,并取整
57 | def get_amount(self,value,price):
58 | volume = int(value / price)
59 | volume = numberutil.get_neat(volume)
60 | return volume
61 |
62 | # 发送订单回回调
63 | def send_order_callback(self, account_id,cl_ord_id, symbol,side, price, volume,factor,strategy,indicator,trade_date):
64 |
65 | if self.send_order_event_handler:
66 | order = Order()
67 | order.account_id=account_id
68 | order.cl_ord_id = cl_ord_id
69 | order.symbol =symbol
70 | if side==2:
71 | side=-1
72 | order.side = side
73 | order.price = price
74 | order.volume = volume
75 | order.amount=price*volume
76 | order.factor = factor
77 | order.indicator = indicator
78 | order.strategy = strategy
79 | order.trade_date = trade_date.strftime('%Y-%m-%d')
80 | order.trade_time = trade_date
81 | order.record_time = datetime.datetime.now()
82 | order.status=1
83 | order.mode=self.context.mode
84 | # 更新持仓股票可用数
85 | if self.context.mode==MODE_BACKTEST:
86 | if order.side == -1:
87 | for i in range(len(self.context.positions)):
88 | if self.context.positions[i].symbol == order.symbol:
89 | self.context.positions[i].volume = self.context.positions[i].volume - order.volume
90 | self.context.positions[i].can_use_volume = self.context.positions[i].can_use_volume - order.volume
91 | if self.context.positions[i].volume<0:
92 | self.context.positions[i].volume=0
93 | if self.context.positions[i].can_use_volume<0:
94 | self.context.positions[i].can_use_volume=0
95 | break
96 | order.insert()
97 | self.send_order_event_handler(order)
98 |
99 | # 屏幕打印帐号持仓信息
100 | def display_account_position_info(self,pos):
101 | print("=========================当前帐号持仓信息===============================")
102 | if len(pos)>0:
103 | df=Position().to_df(pos)
104 | market_value=df['market_value'].sum()
105 | profit=df['profit_amount'].sum()
106 | print("当前持股数:{}".format(len(df)))
107 | print("当前持仓金额:{:.2f}元".format(market_value))
108 | print("当前持仓收益:{:.2f}元".format(profit))
109 | print("当前持仓收益率:{:.2f}%".format(profit/(market_value-profit)*100))
110 | print(df)
111 | else:
112 | print("当前帐号没有持仓")
113 |
114 | # 屏幕打印帐号资金信息
115 | def display_account_asset_info(self):
116 | asset=self.getAsset()
117 | print("=========================资金信息===============================")
118 | print("当前运行帐号:{},id:{}".format(asset.account_name,asset.account_id))
119 | print("初始总资金:{:.2f}元".format(asset.initial_capital))
120 | print("帐号总资产现值:{:.2f}元".format(asset.total_asset))
121 | print("总收益:{:.2f}元".format(asset.total_asset-asset.initial_capital))
122 | print("总收益率:{:.2f}%".format((asset.total_asset-asset.initial_capital)/asset.initial_capital*100))
123 | print("当前持仓市值:{:.2f}元".format(asset.market_value))
124 | print("当前持仓位:{:.2f}%".format(asset.market_value/asset.initial_capital*100))
125 | print("当前可用资金:{:.2f}元".format(asset.cash))
--------------------------------------------------------------------------------
/quant/brokers/myquantbroker.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:掘金经纪商封装
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from gm.api import order_volume, get_unfinished_orders, order_cancel, order_close_all, order_cancel_all, \
10 | order_target_percent
11 | from gm.enum import OrderSide_Sell, OrderType_Limit, PositionEffect_Open, PositionSide_Long, OrderSide_Buy, \
12 | PositionEffect_Close, MODE_LIVE, MODE_BACKTEST, OrderType_Market
13 |
14 | from quant.brokers.broker import Broker
15 | from quant.account import Account
16 | from quant.enums.orderside import OrderSides
17 | from quant.model.asset import Asset
18 | from quant.model.order import Order
19 | from quant.model.position import Position
20 | import pandas as pd
21 | from quant.util import stockutil
22 |
23 | # 掘金经纪商
24 | class MyquantBroker(Broker):
25 | #
26 | def __init__(self,context):
27 | self.trade_records = dict()
28 | self.context = context
29 | self.account = Account(self.context)
30 |
31 | #限价买入指定量的股票
32 | def buy(self,symbol,volume,price,factor,strategy,indicator=''):
33 | ret=order_volume(symbol=symbol, volume=volume, side=OrderSide_Buy, order_type=OrderType_Limit,
34 | position_effect=PositionEffect_Open, price=price)
35 | print(ret)
36 | # 发送订单回回调
37 | self.send_order_callback(self.account.get_account_id(),ret[0].cl_ord_id,symbol,OrderSides.OrderSide_Buy.value, price, volume,factor,strategy,indicator,self.context.now)
38 | return ret
39 |
40 | # 限价买出指定量的股票
41 | def sell(self,symbol,volume,price,factor,strategy,indicator=''):
42 | ret=order_volume(symbol=symbol, volume=volume, side=OrderSide_Sell, order_type=OrderType_Limit,
43 | position_effect=PositionEffect_Close, price=price)
44 | print(ret)
45 | # 发送订单回回调
46 | self.send_order_callback( self.account.get_account_id(),ret[0].cl_ord_id, symbol, OrderSides.OrderSide_Sell.value, price,
47 | volume,factor,strategy,indicator,self.context.now)
48 |
49 | # 清仓某股票仓位
50 | def sell_out(self,symbol,price,factor,strategy,indicator=''):
51 | pos=self.getSymbolPosition(symbol)
52 | if pos.account_id:
53 | if (pos.can_use_volume>0):
54 | volume=pos.can_use_volume
55 | self.sell(symbol,volume=volume,price=price,factor=factor,strategy=strategy,indicator=indicator)
56 |
57 |
58 | #限价买入指定量的股票
59 | def order_target_percent(self,symbol,percent,factor,strategy,indicator=''):
60 | ret=order_target_percent(symbol=symbol, percent=percent,
61 | order_type=OrderType_Market,
62 | position_side=PositionSide_Long)
63 | # 发送订单回回调
64 | if ret[0].side==1:
65 | side=OrderSides.OrderSide_Buy.value
66 | else:
67 | side = OrderSides.OrderSide_Sell.value
68 | self.send_order_callback(self.account.get_account_id(),ret[0].cl_ord_id,symbol,side, ret[0].price, ret[0].volume,factor,strategy,indicator,self.context.now)
69 |
70 | return ret
71 | # 清仓
72 | def close_all(self):
73 | # 先撤消所有委托
74 | order_cancel_all()
75 | # 清仓
76 | order_close_all()
77 |
78 | # 判断是否有未完成的订单
79 | def has_unfinished_orders(self,code,side):
80 | ret=False
81 | orders = get_unfinished_orders()
82 | for i,o in enumerate(orders):
83 | if (o.symbol==code) and (o.side==side):
84 | ret=True
85 | break
86 | return ret
87 |
88 | # 订单取消
89 | def cancel_order(self,code,side):
90 | orders = get_unfinished_orders()
91 | ords=[]
92 | if side==0:
93 | for i, o in enumerate(orders):
94 | order = {'cl_ord_id': o['cl_ord_id'], 'account_id': o['account_id']}
95 | ords.append(order)
96 | else:
97 | for i, o in enumerate(orders):
98 | if (code!='') and (o.symbol != code):
99 | continue
100 | if (side!=0) and (o.side != side):
101 | continue
102 | order = {'cl_ord_id': o['cl_ord_id'], 'account_id': o['account_id']}
103 | ords.append(order)
104 |
105 | order_cancel(wait_cancel_orders=ords)
106 |
107 | # 获取当前资产总金额
108 | def getCapital(self):
109 | account = self.context.account()
110 | capital=account.cash.nav
111 | return capital
112 |
113 | # 获取帐号资产信息
114 | def getAsset(self):
115 | asset=Asset()
116 | account = self.context.account()
117 | asset.account_id=account.cash.account_id
118 | asset.account_name = account.cash.account_name
119 | asset.total_asset=account.cash.nav
120 | asset.cash=account.cash.available
121 | asset.frozen_cash=account.cash.order_frozen
122 | asset.market_value=account.cash.market_value
123 | asset.fpnl=account.cash.fpnl
124 | asset.initial_capital = self.account.initial_capital
125 | return asset
126 |
127 | # 获取当前持仓明细
128 | def getPositions(self,display=False):
129 | positions=[]
130 | pos = self.context.account().positions()
131 | symbols=[]
132 | for i in range(len(pos)):
133 | position=Position()
134 | # position.account_id=pos[i].account_id
135 | position.account_id=self.account.get_account_id()
136 | position.code = pos[i].symbol
137 | position.symbol = pos[i].symbol
138 | position.volume = pos[i].volume
139 | position.amount = pos[i].vwap*pos[i].volume
140 | position.on_road_volume = pos[i].volume_today
141 | position.frozen_volume = pos[i].order_frozen
142 | position.open_price=pos[i].vwap
143 | position.market_value = pos[i].market_value
144 | position.can_use_volume= position.volume-position.on_road_volume-position.frozen_volume
145 | position.price =round(position.market_value / position.volume, 2)
146 | position.profit_amount=position.market_value-position.amount
147 | position.date=pos[i].updated_at
148 | position.update_time=pos[i].updated_at
149 | position.created_at=pos[i].created_at
150 | position.profit_rate=round(position.profit_amount/position.amount*100,2)
151 | positions.append(position)
152 | symbols.append(pos[i].symbol)
153 |
154 | if self.context.mode == MODE_BACKTEST:
155 | pos=self.get_backtest_position(account_id=self.context.backtest_account_id,date=self.context.now.strftime("%Y-%m-%d"))
156 | for p in pos:
157 | if p.symbol not in symbols:
158 | positions.append(p)
159 |
160 | if display==True:
161 | self.display_account_position_info(positions)
162 | return positions
163 |
164 | # 获取某股票当前持仓金额
165 | def getSymbolPosition(self,symbol):
166 | ret=Position()
167 | ret.market_value=0
168 | ret.can_use_volume=0
169 | pos = self.context.account().position(symbol=symbol, side=PositionSide_Long)
170 | if pos!=None:
171 | ret.account_id=pos.account_id
172 | ret.code = pos.symbol
173 | ret.symbol = pos.symbol
174 | ret.volume = pos.volume
175 | ret.amount = pos.cost
176 | ret.on_road_volume = pos.volume_today
177 | ret.frozen_volume = pos.order_frozen
178 | ret.open_price=pos.vwap
179 | ret.market_value = pos.market_value
180 | ret.can_use_volume= ret.volume-ret.on_road_volume-ret.frozen_volume
181 | ret.price =round(ret.market_value / ret.volume, 2)
182 | ret.profit_amount=ret.market_value-ret.amount
183 | ret.date=pos.updated_at
184 | ret.update_time = pos.updated_at
185 | ret.profit_rate=round(ret.profit_amount/ret.amount*100,2)
186 | elif self.context.mode == MODE_BACKTEST:
187 | for p in self.context.positions:
188 | if p.symbol == symbol:
189 | ret=p
190 | break
191 | return ret
192 |
193 | # 获取当前股票持仓比例
194 | def getSymbolPositionRate(self, symbol):
195 | capital = self.getCapital()
196 | pos=self.getSymbolPosition(symbol)
197 | ret = pos.market_value / capital
198 | return ret
199 |
200 | # 获取当前持仓股票数量
201 | def getStockNumber(self):
202 | positions = self.context.account().positions()
203 | ret=len(positions)
204 | return ret
205 |
206 | # 获取当前持仓金额
207 | def getPositionAmount(self):
208 | ret = 0
209 | positions = self.context.account().positions()
210 | if len(positions)>0:
211 | df=pd.DataFrame(positions)
212 | ret = df['amount'].sum()
213 | return ret
214 |
215 | # 获取仓位比例
216 | def getPositionRate(self):
217 | capital=self.getCapital()
218 | amount=self.getPositionAmount()
219 | ret=round((amount/capital),2)
220 | return ret
221 |
222 | #检查是否还有可买仓位
223 | def hasPosition(self):
224 | ret=True
225 | if self.getPositionRate()>=self.account.MAX_POSITION:
226 | ret=False
227 | return ret
228 |
229 | #检查是否超过持仓股票数限制
230 | def isUnderMaxStockNumber(self):
231 | ret=True
232 | if self.getStockNumber()>=self.account.MAX_STOCK_NUMBER:
233 | ret=False
234 | return ret
235 |
236 | #检查某股票是否超过持仓股票数限制
237 | def isUnderMaxStockPosition(self,symbol):
238 | ret=True
239 | if self.getSymbolPositionRate(symbol)>=self.account.MAX_SINGLE_POSITION:
240 | ret=False
241 | return ret
242 |
243 | # 获取回测持仓
244 | def get_backtest_position(self,account_id,date):
245 | df=Order().get_unfinished_order(account_id=account_id,date=date)
246 |
247 | positions=[]
248 | for i,row in df.iterrows():
249 | pos=Position()
250 | pos.account_id = row['account_id']
251 | pos.symbol=row['symbol']
252 | pos.code =stockutil.delSymbolPrefix(row['symbol'])
253 | pos.open_price=row['price']
254 | pos.volume=row['volume']
255 | pos.amount = pos.open_price * pos.volume
256 | pos.can_use_volume = row['volume']
257 | pos.frozen_volume = 0
258 | pos.on_road_volume = 0
259 | pos.price=pos.open_price
260 | pos.market_value=pos.amount
261 | pos.profit=0
262 | pos.profit_rate=0
263 | pos.profit_amount=0
264 | positions.append(pos)
265 | return positions
--------------------------------------------------------------------------------
/quant/config.ini:
--------------------------------------------------------------------------------
1 | [TOKEN]
2 | gmtoken = xxxxx
3 |
4 | [ACCOUNT]
5 | strategy_id=xxxx
6 | backtest_strategy_id=xxx
7 |
8 | [DATABASE]
9 | tradedb = mysql+mysqlconnector://root:111111@localhost:3306/iquant
10 |
11 |
12 | [GOLDMINER]
13 | path =D:\Goldminer3\Hongshu Goldminer3\goldminer3.exe
--------------------------------------------------------------------------------
/quant/empyrical/__init__.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2016 Quantopian, Inc.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | # flake8: noqa
16 |
17 | from ._version import get_versions
18 | __version__ = get_versions()['version']
19 | del get_versions
20 |
21 | from .stats import (
22 | cum_returns,
23 | cum_returns_final,
24 | aggregate_returns,
25 | max_drawdown,
26 | annual_return,
27 | annual_volatility,
28 | calmar_ratio,
29 | omega_ratio,
30 | sharpe_ratio,
31 | sortino_ratio,
32 | downside_risk,
33 | information_ratio,
34 | alpha_beta,
35 | alpha,
36 | beta,
37 | alpha_beta_aligned,
38 | alpha_aligned,
39 | beta_aligned,
40 | stability_of_timeseries,
41 | tail_ratio,
42 | cagr,
43 | DAILY,
44 | WEEKLY,
45 | MONTHLY,
46 | YEARLY
47 | )
48 |
--------------------------------------------------------------------------------
/quant/empyrical/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/empyrical/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/empyrical/__pycache__/_version.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/empyrical/__pycache__/_version.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/empyrical/__pycache__/stats.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/empyrical/__pycache__/stats.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/empyrical/__pycache__/utils.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/empyrical/__pycache__/utils.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/empyrical/_version.py:
--------------------------------------------------------------------------------
1 |
2 | # This file was generated by 'versioneer.py' (0.16) from
3 | # revision-control system data, or from the parent directory name of an
4 | # unpacked source archive. Distribution tarballs contain a pre-generated copy
5 | # of this file.
6 |
7 | import json
8 | import sys
9 |
10 | version_json = '''
11 | {
12 | "dirty": false,
13 | "error": null,
14 | "full-revisionid": "5d38634e0d68e65699383d2a97edaf34ce91fada",
15 | "version": "0.2.1"
16 | }
17 | ''' # END VERSION_JSON
18 |
19 |
20 | def get_versions():
21 | return json.loads(version_json)
22 |
--------------------------------------------------------------------------------
/quant/empyrical/utils.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2016 Quantopian, Inc.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | try:
17 | # fast versions
18 | import bottleneck as bn
19 | nanmean = bn.nanmean
20 | nanstd = bn.nanstd
21 | nansum = bn.nansum
22 | nanmax = bn.nanmax
23 | nanmin = bn.nanmin
24 | nanargmax = bn.nanargmax
25 | nanargmin = bn.nanargmin
26 | except ImportError:
27 | # slower numpy
28 | import numpy as np
29 | nanmean = np.nanmean
30 | nanstd = np.nanstd
31 | nansum = np.nansum
32 | nanmax = np.nanmax
33 | nanmin = np.nanmin
34 | nanargmax = np.nanargmax
35 | nanargmin = np.nanargmin
36 |
--------------------------------------------------------------------------------
/quant/enums/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/enums/__init__.py
--------------------------------------------------------------------------------
/quant/enums/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/enums/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/enums/__pycache__/brokers.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/enums/__pycache__/brokers.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/enums/__pycache__/dfcolumns.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/enums/__pycache__/dfcolumns.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/enums/__pycache__/orderside.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/enums/__pycache__/orderside.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/enums/__pycache__/ratings.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/enums/__pycache__/ratings.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/enums/__pycache__/scopes.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/enums/__pycache__/scopes.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/enums/__pycache__/strategys.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/enums/__pycache__/strategys.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/enums/__pycache__/strategytype.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/enums/__pycache__/strategytype.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/enums/brokers.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:经纪商枚举类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from enum import Enum, unique
10 |
11 | @unique # @unique装饰器可以帮助我们检查保证没有重复值
12 | class Brokers(Enum):
13 | def __new__(cls, value=None):
14 | obj = object.__new__(cls)
15 | # obj.english = value
16 | return obj
17 | # 掘金
18 | myquant = 'myquant'
19 | # 迅投qmt
20 | xtquant = 'xtquant'
21 |
22 | if __name__=='__main__':
23 | # print(Factors.raising_limit.value)
24 | for b in Brokers:
25 | print(b.value)
26 |
--------------------------------------------------------------------------------
/quant/enums/dfcolumns.py:
--------------------------------------------------------------------------------
1 | from enum import Enum, unique
2 |
3 | @unique
4 | class DfColumns(Enum):
5 | def __new__(cls, value=None):
6 | obj = object.__new__(cls)
7 | # obj.english = value
8 | return obj
9 |
10 | close = 'close'
11 | dif = 'dif'
12 | dea = 'dea'
13 | macd = 'macd'
14 | k = 'k'
15 | d = 'd'
16 | j = 'j'
17 | # 分钟均价
18 | map = 'map'
19 |
20 | if __name__=='__main__':
21 | print(DfColumns.macd.value)
--------------------------------------------------------------------------------
/quant/enums/orderside.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:交易方向枚举类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from enum import Enum, unique
10 |
11 | #
12 | @unique
13 | class OrderSides(Enum):
14 | def __new__(cls, value=None):
15 | obj = object.__new__(cls)
16 | # obj.english = value
17 | return obj
18 |
19 | OrderSide_Unknown = 0
20 | OrderSide_Buy = 1 # 买入
21 | OrderSide_Sell = 2 # 卖出
22 |
23 | if __name__=='__main__':
24 | pass
25 |
--------------------------------------------------------------------------------
/quant/enums/ratings.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:股票评级枚举类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from enum import Enum, unique
10 |
11 | @unique
12 | class Ratings(Enum):
13 | def __new__(cls, value=None):
14 | obj = object.__new__(cls)
15 | # obj.english = value
16 | return obj
17 |
18 | BuyIn = '买入'
19 | SellOut = '卖出'
20 | Holding = '持有'
21 | Neutral = '观望'
22 |
23 | if __name__=='__main__':
24 | print(Ratings.raising_limit.value)
--------------------------------------------------------------------------------
/quant/enums/scopes.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:策略作用范围枚举类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from enum import Enum, unique
10 | #
11 | @unique
12 | class Scopes(Enum):
13 | def __new__(cls, value=None):
14 | obj = object.__new__(cls)
15 | return obj
16 |
17 | stock = 'stock'
18 | universal= 'universal'
19 |
--------------------------------------------------------------------------------
/quant/enums/strategys.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:策略类型枚举类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from enum import Enum, unique
10 |
11 | @unique
12 | class Strategys(Enum):
13 | def __new__(cls, value=None):
14 | obj = object.__new__(cls)
15 | # obj.english = value
16 | return obj
17 |
18 | GridStrategy='GridStrategy'
19 | MeanRevertingStrategy = 'MeanRevertingStrategy'
20 | TrendFollowingStrategy = 'TrendFollowingStrategy'
21 | DayTradingStrategy= 'DayTradingStrategy'
22 |
--------------------------------------------------------------------------------
/quant/enums/strategytype.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:策略驱动类型枚举类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from enum import Enum, unique
10 | @unique # @unique装饰器可以帮助我们检查保证没有重复值
11 | class StrategyType(Enum):
12 | def __new__(cls, value=None):
13 | obj = object.__new__(cls)
14 | # obj.english = value
15 | return obj
16 |
17 | bar = 'bar'
18 | tick = 'tick'
19 | schedule = 'schedule'
20 |
--------------------------------------------------------------------------------
/quant/indicators.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:指标类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 |
10 | import numpy as np
11 | import talib
12 | import pandas as pd
13 | from quant.api.gmapi import GmApi
14 |
15 | class Indicator():
16 | def __init__(self):
17 | pass
18 | # sma
19 | @classmethod
20 | def sma(self,df):
21 | df.loc[:, 'ma5'] = talib.MA(df.close, timeperiod=5, matype=0)
22 | df.loc[:, 'ma10'] = talib.MA(df.close, timeperiod=10, matype=0)
23 | df.loc[:, 'ma20'] = talib.MA(df.close, timeperiod=20, matype=0)
24 | df.loc[:, 'ma60'] = talib.MA(df.close, timeperiod=60, matype=0)
25 | df.loc[:, 'ma120'] = talib.MA(df.close, timeperiod=120, matype=0)
26 | df.loc[:, 'ma250'] = talib.MA(df.close, timeperiod=250, matype=0)
27 | return df
28 |
29 | # macd
30 | @classmethod
31 | def macd(self,df):
32 | adjust=True
33 | df['dif'] = df['close'].ewm(span=12, adjust=adjust).mean() - df['close'].ewm(span=26, adjust=adjust).mean()
34 | df['dea'] = df['dif'].ewm(span=9, adjust=adjust).mean()
35 | df['macd'] = 2 * (df['dif'] - df['dea'])
36 | df['short_ema']=np.round(talib.EMA(df['close'].values, 12),3)
37 | df['long_ema'] = np.round(talib.EMA(df['close'].values, 26), 3)
38 | # df['dif']=df['short_ema']-df['long_ema']
39 | # df['dea']=np.round(talib.EMA(df['dif'].values, 9),3)
40 | # df['macd'] = 2 * (np.round(df['dif'] - df['dea'],3))
41 |
42 | return df
43 |
44 | # kdj
45 | @classmethod
46 | def kdj(self,df1):
47 | df=df1.copy()
48 | low_list =df['low'].rolling(window=9,min_periods=1).min()
49 | low_list.fillna(value=df['low'].expanding().min(), inplace=True)
50 | high_list = df['high'].rolling(window=9,min_periods=1).max()
51 | high_list.fillna(value=df['high'].expanding().max(), inplace=True)
52 |
53 | if len(high_list[high_list== low_list])==0:
54 | rsv = ((df['close'] - low_list) / (high_list - low_list) * 100)
55 | else:
56 | arr_t=high_list[high_list== low_list]
57 | arr_f=high_list[high_list!= low_list]
58 | rsv_f = ((df['close'][arr_f.index] - low_list[arr_f.index]) / (high_list[arr_f.index] - low_list[arr_f.index]) * 100)
59 | # rsv_t= ((df.loc[arr_t,['close']] - low_list[arr_t]) / (high_list[arr_t] - low_list[arr_t]) * 100)
60 | data= np.ones(len(arr_t))*100
61 | rsv_t=pd.Series(data,arr_t.index)
62 | rsv=pd.concat([rsv_f,rsv_t],axis=0,ignore_index=False).sort_index()
63 |
64 | df.loc[:,'k'] =np.round(rsv.ewm(com=2).mean(),3)
65 | df.loc[:,'d'] = np.round(df['k'].ewm(com=2).mean(),3)
66 | df.loc[:,'j'] = np.round(3 * df['k'] - 2 * df['d'],3)
67 | return df
68 |
69 | # rsi
70 | @classmethod
71 | def rsi(self,df):
72 | df["rsi_fast"] = talib.RSI(df['close'], timeperiod=9)
73 | df["rsi_slow"] = talib.RSI(df['close'], timeperiod=12)
74 | df["rsi_long"] = talib.RSI(df['close'], timeperiod=72)
75 | return df
76 |
77 | # atr
78 | @classmethod
79 | def atr(self,df):
80 | df['preclose']=df['close'].shift(1)
81 | df['preclose']=df['preclose'].fillna(method='bfill')
82 | df['tr']=df.apply(lambda x:max(x['high']-x['low'], abs(x['high'] - x['preclose']), abs(x['low'] - x['preclose'])),axis=1)
83 | df['atr'] = df['tr'].rolling(window=14, min_periods=1).mean()
84 | return df
85 |
86 | # mtm动量指标
87 | @classmethod
88 | def mtm(self,df,N=12, M=6):
89 | """
90 | MTM动力指标
91 | 算法:
92 | MTM线 当日收盘价与N日前的收盘价的差
93 | MTMMA线 对上面的差值求N日移动平均
94 | 参数:N 间隔天数,也是求移动平均的天数,一般取6
95 | 用法:
96 | 1.MTM从下向上突破MTMMA,买入信号
97 | 2.MTM从上向下跌破MTMMA,卖出信号
98 | 3.股价续创新高,而MTM未配合上升,意味上涨动力减弱
99 | 4.股价续创新低,而MTM未配合下降,意味下跌动力减弱
100 | 5.股价与MTM在低位同步上升,将有反弹行情;反之,从高位同步下降,将有回落走势。
101 | """
102 | df['mtm'] = np.round(df['close'] - df['close'].shift(N),3)
103 | df['mtmma'] =np.round(df['mtm'].rolling(M).mean(),3)
104 | return df
105 |
106 | # boll
107 | @classmethod
108 | def boll(self,df):
109 | df['boll_up'], df['boll_mid'], df['boll_low'] = talib.BBANDS(df['close'], timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
110 | return df
111 |
112 | # bias
113 | @classmethod
114 | def bias(self,df):
115 | df['bias_short'] = (df['close'] - df['close'].rolling(6, min_periods=1).mean()) / df['close'].rolling(6,min_periods=1).mean() * 100
116 | df['bias_mid'] = (df['close'] - df['close'].rolling(12, min_periods=1).mean()) / df['close'].rolling(12,min_periods=1).mean() * 100
117 | df['bias_long'] = (df['close'] - df['close'].rolling(24, min_periods=1).mean()) / df['close'].rolling(24,min_periods=1).mean() * 100
118 | df['bias_short'] = round(df['bias_short'], 2)
119 | df['bias_mid'] = round(df['bias_mid'], 2)
120 | df['bias_long'] = round(df['bias_long'], 2)
121 | return df
122 |
123 | # 计算所有指标
124 | @classmethod
125 | def cal_indicator(self,df):
126 | df= self.macd(df)
127 | df = self.sma(df)
128 | # df = self.kdj(df)
129 | df = self.rsi(df)
130 | df = self.boll(df)
131 | df = self.bias(df)
132 | return df
133 |
134 | # 计算即时kdj
135 | @classmethod
136 | def instant_kdj(self,high,low,close,k_pre,d_pre,df_bar):
137 | if high< df_bar['high'].max():
138 | high=df_bar['high'].max()
139 | if low> df_bar['low'].min():
140 | low=df_bar['low'].min()
141 | if high==low:
142 | rsv=100
143 | else:
144 | rsv=(close-low)/(high-low)*100
145 | k=(2/3*k_pre)+(1/3*rsv)
146 | d=(2/3*d_pre)+(1/3*k)
147 | j=(3*k)-(2*d)
148 | return np.round(k,3),np.round(d,3),np.round(j,3)
149 |
150 | @classmethod
151 | def ema(self,x,n,pre_y):
152 | return (2 * x + (n - 1) * pre_y) / (n + 1)
153 |
154 | # 计算即时macd
155 | @classmethod
156 | def instant_macd(self,price,pre_short_ema,pre_long_ema,pre_dea):
157 | short_ema = self.ema(price, 12, pre_short_ema)
158 | long_ema = self.ema(price, 26, pre_long_ema)
159 | dif = short_ema - long_ema
160 | dea = self.ema(dif, 9, pre_dea)
161 | macd = (dif - dea) * 2
162 | return (dif,dea,macd)
163 |
164 | # 计算即时boll
165 | @classmethod
166 | def instant_boll(self,close_19,price):
167 | close_20=np.append(close_19,price)
168 | boll_mid=np.mean(close_20,axis=0)
169 | # 求标准差
170 | arr_std = np.std(close_20, ddof=0)
171 | boll_up=boll_mid + 2 * arr_std
172 | boll_low = boll_mid - 2 * arr_std
173 | return boll_low,boll_mid,boll_up
174 |
175 | # 计算即时均值
176 | @classmethod
177 | def instant_sma(self,close_array,price):
178 | # arr=close_array[-n+1:]
179 | arr=np.append(close_array,price)
180 | ret=np.mean(arr)
181 | return ret
182 |
183 | # 计算即时mtm
184 | @classmethod
185 | def instant_mtm(self,pren_close,mtm5,price):
186 | mtm=np.round(price-pren_close,3)
187 | mtmma = np.round((mtm5 + mtm) / 6,3)
188 | return mtm,mtmma
189 |
190 | if __name__=="__main__":
191 | # df = pro.query('daily', ts_code='002526.SZ', start_date='20110801', end_date='20200810')
192 | df = GmApi().getSymbolHistoryKdata('SHSE.601318', start_time='2022-04-01', end_time='2022-11-10')
193 | # df1 = Indicator.boll(df.copy())
194 | # df2=df.iloc[-20:-1,:].copy()
195 |
196 | # df['prenclose'] = df['close'].shift(6)
197 | # df['prenclose'].fillna(method='bfill',inplace=True)
198 |
199 |
200 | bol= Indicator.macd(df)
201 | # mtm=15.21-14.78
202 | # mtmma=(bol['mtm'].sum()+mtm)/6
203 | #
204 | print(bol)
205 |
206 | # macd=Indicator().instant_macd(38.46,38.234,39.015,-0.969)
207 | # print(macd)
208 | # bol=bol.tail(5)
209 | # mtmma=Indicator.instant_mtm(19.1739,bol['mtm'].sum(),19.92)
210 | # print(mtmma)
211 | # #
212 | # print(bol)
213 | # print(df)
214 | # df['k_pre'] = df['k'].shift(1)
215 | # df["k_pre"].fillna(method='bfill', inplace=True)
216 | # df['d_pre'] = df['d'].shift(1)
217 | # df["d_pre"].fillna(method='bfill', inplace=True)
218 | # # df=df.loc[(df['k'] >= df['d']) & (df['k_pre'] < df['d_pre'])]
219 | # df=df.loc[(df['k'] < df['d']) & (df['k_pre'] >= df['d_pre'])]
220 | # df.to_csv("e:\\300919.csv")
221 | # df.loc[(df['dif'] < df['dea']) & (df['dif_pre'] >= df['dea_pre']), ['signal']] = 0
222 |
223 | # 上向下突破上轨,买出
224 | # df.loc[(df['close_pre'] >= df['boll_up_pre']) & (df['close'] < df['boll_up']), ['signal']] = 0
225 | # print(df1.tail(10))
226 | # df['pre_close'] = df['close'].shift()
227 | # df["pre_close"].fillna(method='bfill', inplace=True)
228 | # df['change'] = (df['close'] - df['pre_close']) / df['pre_close']
229 | # df=Indicator.cal_indicator(df)
230 | # df.index=pd.to_datetime(df.index)
231 | # figure = mpyplot.figure(figsize=(20, 8), facecolor='#ffffff', edgecolor='#000000', linewidth=1)
232 | # plt= Plot_OldSync()
233 | # plt.plotfig(df=df,fig=figure)
234 | # plt.show()
235 | pass
236 | # print(df)
237 |
--------------------------------------------------------------------------------
/quant/job/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/job/__init__.py
--------------------------------------------------------------------------------
/quant/job/run_bt.bat:
--------------------------------------------------------------------------------
1 | D:\ProgramData\Anaconda3\envs\py38\python.exe E:/code/python/myquant/quant/startengine_live.py --mode 2
2 |
3 |
--------------------------------------------------------------------------------
/quant/job/run_live.bat:
--------------------------------------------------------------------------------
1 | D:\ProgramData\Anaconda3\envs\py38\python.exe E:/code/python/myquant/quant/startengine_live.py --mode 1
2 |
3 |
4 |
--------------------------------------------------------------------------------
/quant/logger.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:日志类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | import datetime
10 | import sys,os
11 | base_dir=os.path.dirname(os.path.dirname(__file__))#获取pathtest的绝对路径
12 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
13 | import logging
14 | from quant.api.singleton import singleton
15 |
16 | @singleton
17 | class Logger():
18 | # 拙金数据API封装
19 | # quote_ctx = None
20 | def __init__(self):
21 | self.logger = logging.getLogger("mylogger")
22 | # StreamHandler对象自定义日志级别
23 | self.logger_leverl=logging.DEBUG
24 | self.logger.setLevel(self.logger_leverl)
25 | # 日志格式
26 | self.formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
27 |
28 | # info handler类型 ,0:stream,1:file,2:both
29 | self.handler_type=0
30 |
31 |
32 | # 获取流handler
33 | def get_stream_handler(self,level):
34 | stream_handler = logging.StreamHandler()
35 | # StreamHandler对象自定义日志格式
36 | stream_handler.setFormatter(self.formatter)
37 | stream_handler.setLevel(level)
38 | return stream_handler
39 |
40 | # 获取流handler
41 | def get_file_handler(self,type,level):
42 | log_file = "{}/log/{}_{}.txt".format(base_dir,type, datetime.datetime.now().strftime("%Y-%m-%d"))
43 | file_handler = logging.FileHandler(log_file)
44 | file_handler.setLevel(level)
45 | file_handler.setFormatter(self.formatter)
46 | return file_handler
47 |
48 | # 信息日志
49 | def loginfo(self,message):
50 | if self.handler_type==0:
51 | stream_handler=self.get_stream_handler(logging.INFO)
52 | self.logger.addHandler(stream_handler)
53 | elif self.handler_type==1:
54 | file_handler = self.get_file_handler('info',logging.INFO)
55 | self.logger.addHandler(file_handler)
56 | else:
57 | stream_handler = self.get_stream_handler(logging.INFO)
58 | self.logger.addHandler(stream_handler)
59 |
60 | file_handler = self.get_file_handler('info',logging.INFO)
61 | self.logger.addHandler(file_handler)
62 |
63 | self.logger.info(message)
64 | for handler in self.logger.handlers:
65 | self.logger.removeHandler(handler)
66 |
67 | #错误日志
68 | def logerror(self,message):
69 | handler = self.get_file_handler('error',logging.ERROR)
70 | self.logger.addHandler(handler)
71 | self.logger.error(message)
72 | self.logger.removeHandler(handler)
73 |
74 | # 调试日志
75 | def logdebug(self,message):
76 | handler = self.get_stream_handler(logging.DEBUG)
77 | self.logger.addHandler(handler)
78 | self.logger.debug(message)
79 | self.logger.removeHandler(handler)
80 |
81 | if __name__=="__main__":
82 | Logger().loginfo("test done")
83 |
--------------------------------------------------------------------------------
/quant/model/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/quant/model/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/quant/model/.idea/model.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/quant/model/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/quant/model/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 1650266167878
27 |
28 |
29 | 1650266167878
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/quant/model/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__init__.py
--------------------------------------------------------------------------------
/quant/model/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/__pycache__/asset.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/asset.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/__pycache__/backtestrecord.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/backtestrecord.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/__pycache__/executionreport.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/executionreport.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/__pycache__/myorder.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/myorder.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/__pycache__/order.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/order.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/__pycache__/position.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/position.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/__pycache__/setting.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/setting.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/__pycache__/stockindicator.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/stockindicator.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/__pycache__/stockpool.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/stockpool.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/__pycache__/traderecord.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/model/__pycache__/traderecord.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/model/asset.py:
--------------------------------------------------------------------------------
1 | class Asset():
2 | def __init__(self, account_id="", account_type=0, cash=-1, frozen_cash=-1, market_value=-1, total_asset=-1):
3 | self.account_id = account_id
4 | self.account_name = ""
5 | self.account_type = account_type
6 | self.cash = cash
7 | self.frozen_cash = frozen_cash
8 | self.market_value = market_value
9 | self.total_asset = total_asset
10 | self.initial_capital = -1
11 | self.fpnl=0
--------------------------------------------------------------------------------
/quant/model/backtestrecord.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:回归记录
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 |
10 | from sqlalchemy import Column, Integer, String, create_engine,DateTime,Float,DECIMAL
11 | from sqlalchemy.ext.declarative import declarative_base
12 | from sqlalchemy.orm import sessionmaker
13 |
14 | from quant.util.configutil import get_config
15 |
16 | Base = declarative_base()
17 | # 定义映射类User,其继承上一步创建的Base
18 | class BacktestRecord(Base):
19 | # 指定本类映射到users表
20 | __tablename__ = 'backtest_record'
21 | __connectString__=get_config('DATABASE','tradedb')
22 | id = Column(Integer, primary_key=True, autoincrement=True)
23 | # 指定name映射到name字段; name字段为字符串类形,
24 | account_id = Column(String(50))
25 | start = Column(String(20))
26 | symbols = Column(String(200))
27 | current= Column(String(20))
28 | end = Column(String(20))
29 | status = Column(String(20))
30 | date = Column(String(20))
31 |
32 | def insert(self):
33 | # 初始化数据库连接:
34 | engine = create_engine(self.__connectString__)
35 | # 创建DBSession类型:
36 | DBSession = sessionmaker(bind=engine)
37 |
38 | # 创建session对象:
39 | session = DBSession()
40 | session.add(self)
41 | # 提交即保存到数据库:
42 | session.commit()
43 | # 关闭session:
44 | session.close()
45 |
46 | def get_backtest_record(self,account_id):
47 | engine = create_engine(self.__connectString__)
48 | DBSession = sessionmaker(bind=engine, expire_on_commit=False)
49 | session = DBSession()
50 | ret = session.query(BacktestRecord).filter(BacktestRecord.account_id == account_id).one()
51 | # session.commit()
52 | engine.dispose()
53 | return ret
54 |
55 | # 获取未完成的回测
56 | def get_suspend_record(self):
57 | engine = create_engine(self.__connectString__)
58 | DBSession = sessionmaker(bind=engine)
59 | session = DBSession()
60 | ret = session.query(BacktestRecord).filter(BacktestRecord.status != 'finished').order_by(BacktestRecord.date.desc()).first()
61 | # session.commit()
62 | engine.dispose()
63 | return ret
64 | # 更新股票评级
65 | def update_backtest_record(self,account_id,date,status):
66 | engine = create_engine(self.__connectString__)
67 | DBSession = sessionmaker(bind=engine)
68 | session = DBSession()
69 | js={"current": date,"status": status}
70 | session.query(BacktestRecord).filter(BacktestRecord.account_id == account_id).update(js)
71 | session.commit()
72 | engine.dispose()
73 |
74 | if __name__ == '__main__':
75 | # b= BacktestRecord()
76 | # b.status="suspend"
77 | # b.account_id="dsssss"
78 | # b.date="2022-10-10"
79 | # b.start = "2021-10-10"
80 | # b.current = "2021-12-10"
81 | # b.end = "2022-10-10"
82 | # b.insert()
83 | b=BacktestRecord().get_suspend_record()
84 | # b=BacktestRecord().update_backtest_record(account_id='dsssss',date='2021-12-11',status='finished')
85 | if b==None:
86 | print(None)
87 | else:
88 | print(b.account_id)
89 | pass
90 |
--------------------------------------------------------------------------------
/quant/model/executionreport.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:交易事件回报
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from datetime import datetime
10 | from sqlalchemy import Column, Integer, String, create_engine,DateTime,Float,DECIMAL
11 | from sqlalchemy.ext.declarative import declarative_base
12 | from sqlalchemy.orm import sessionmaker
13 | import pandas as pd
14 | from quant.util.configutil import get_config
15 |
16 | Base = declarative_base()
17 | # 定义映射类User,其继承上一步创建的Base
18 | class ExecutionReport(Base):
19 | # 指定本类映射到users表
20 | __tablename__ = 'execution_report'
21 | __connectString__=get_config('DATABASE','tradedb')
22 | id = Column(Integer, primary_key=True, autoincrement=True)
23 | # 指定name映射到name字段; name字段为字符串类形,
24 | account_id = Column(String(50))
25 | cl_ord_id = Column(String(50))
26 | symbol = Column(String(20))
27 | side = Column(Integer)
28 | mode = Column(Integer)
29 | price = Column(Float(10))
30 | volume = Column(Integer)
31 | amount = Column(Float(20))
32 | strategy=Column(String(50))
33 | trade_date = Column(String(50))
34 | trade_time = Column(DateTime,default=datetime.now())
35 | record_time= Column(DateTime,default=datetime.now())
36 |
37 | # __repr__方法用于输出该类的对象被print()时输出的字符串,如果不想写可以不写
38 | def __repr__(self):
39 | return "" % (
40 | self.account_id,self.symbol,self.name,self.side,self.price,self.volume,self.amount,self.strategy,self.trade_date,self.trade_time)
41 |
42 | def insert(self):
43 | # 初始化数据库连接:
44 | engine = create_engine(self.__connectString__)
45 | # 创建DBSession类型:
46 | DBSession = sessionmaker(bind=engine)
47 |
48 | # 创建session对象:
49 | session = DBSession()
50 | # 创建新User对象:
51 | # 添加到session:
52 | session.add(self)
53 | # 提交即保存到数据库:
54 | session.commit()
55 | # 关闭session:
56 | session.close()
57 |
58 | def getRecord(self,start=None,end=None,acount='d28f4b30-4bf9-11ec-ad1b-00163e0a4100'):
59 | engine = create_engine(self.__connectString__)
60 | where=''
61 | if start!=None:
62 | where="trade_date>='{}'".format(start)
63 | if end!=None:
64 | if where!='':
65 | where=where+' and '
66 | where=where+"trade_date<='{}'".format(end)
67 | if acount!="":
68 | if where != '':
69 | where = where + ' and '
70 | where = where + " account_id='{}'".format(acount)
71 | if where!='':
72 | where=" where {}".format(where)
73 | df=pd.read_sql("select * from {} {}".format(self.__tablename__,where),con=engine)
74 | engine.dispose()
75 | return df
76 | # 获取单个股票交易记录
77 | def getStockRecord(self,symbol,start=None,end=None):
78 | engine = create_engine(self.__connectString__)
79 | where=" symbol='{}'".format(symbol)
80 | if start!=None:
81 | where=where+" and trade_time>='{}'".format(start)
82 | if end!=None:
83 | where=where+" and trade_time<='{}'".format(end)
84 | if where!='':
85 | where=' where {}'.format(where)
86 | df=pd.read_sql("select * from {} {}".format(self.__tablename__,where),con=engine)
87 | engine.dispose()
88 | return df
89 | # 获取单个股票交易记录
90 | def hasExistTradeRecord(self,symbol,trade_time,amount):
91 | ret=False
92 | engine = create_engine(self.__connectString__)
93 | where="where symbol='{}' and trade_time='{}' and amount={} ".format(symbol,trade_time,amount)
94 | df=pd.read_sql("select * from trade_record {}".format(where),con=engine)
95 | engine.dispose()
96 | if len(df)>0:
97 | ret=True
98 | return ret
99 | # 获取单个股票交易记录
100 | def getAccount(self):
101 | engine = create_engine(self.__connectString__)
102 | # where=" "
103 | # if start!=None:
104 | # where=where+" trade_time>='{}'".format(start)
105 | # if end!=None:
106 | # where=where+" and trade_time<='{}'".format(end)
107 | # if where!='':
108 | # where=' where {}'.format(where)
109 | # df=pd.read_sql("SELECT account_id,COUNT(*) AS cnt FROM trade_record GROUP BY account_id {}".format(where),con=engine)
110 | df = pd.read_sql("SELECT account_id,COUNT(*) AS cnt FROM trade_record GROUP BY account_id ",
111 | con=engine)
112 | engine.dispose()
113 | return df
114 |
115 | # 获取最多交易记录帐号
116 | def getAccountId(self):
117 | ret=''
118 | df=self.getAccount()
119 | if len(df)>0:
120 | df.sort_values(by=['cnt'],ascending=False,inplace=True)
121 | ret=df.at[0,'account_id']
122 | return ret
123 | # 获取单个股票交易记录
124 | def getTradeRecord(self,symbol,acount,start=None,end=None):
125 | engine = create_engine(self.__connectString__)
126 | where=" symbol='{}' and account_id='{}'".format(symbol,acount)
127 | if start!=None:
128 | where=where+" and trade_time>='{}'".format(start)
129 | if end!=None:
130 | where=where+" and trade_time<='{}'".format(end)
131 | if where!='':
132 | where=' where {}'.format(where)
133 | df=pd.read_sql("select * from trade_record {}".format(where),con=engine)
134 | engine.dispose()
135 | df_buy=df.loc[df['side']==1,:]
136 | df_buy=df_buy.groupby(['trade_date']).sum(['amount', 'volume'])
137 | df_buy['buy']=round((df_buy['amount']/df_buy['volume']),2)
138 |
139 | df_sell = df.loc[df['side'] == -1, :]
140 | df_sell = df_sell.groupby(['trade_date']).sum(['amount', 'volume'])
141 | df_sell['sell'] = round((df_sell['amount'] / df_sell['volume']), 2)
142 | df_trade = df.groupby(['trade_date']).sum(['amount', 'volume'])
143 | df_trade['buy']=df_buy['buy']
144 | df_trade['sell'] = df_sell['sell']
145 | df_trade.index=pd.to_datetime(df_trade.index)
146 | # print(df_trade)
147 | return df_trade
148 |
149 | # def batch_insert(self,df):
150 | # for i,execrpt in df.iterrows():
151 | # if execrpt.exec_type == 15:
152 | # trade_record = ExecutionReport()
153 | # trade_record.account_id = execrpt.account_id
154 | # trade_record.cl_ord_id = execrpt.cl_ord_id
155 | # trade_record.symbol = execrpt.symbol
156 | # if execrpt.side == 1:
157 | # trade_record.side = 1
158 | # elif execrpt.side == 2:
159 | # trade_record.side = -1
160 | # trade_record.price = execrpt.price
161 | # trade_record.volume = execrpt.volume
162 | # trade_record.amount =Decimal(execrpt.amount).quantize(Decimal('0.00'))
163 | # trade_record.trade_date = execrpt.created_at.strftime('%Y-%m-%d')
164 | # trade_record.record_time =datetime.now()
165 | # trade_record.trade_time=pd.to_datetime(execrpt.created_at).strftime('%Y-%m-%d %H:%M:%S')
166 | # if ExecutionReport().hasExistTradeRecord(trade_record.symbol, trade_record.trade_time,
167 | # trade_record.amount) == False:
168 | # try:
169 | # trade_record.insert()
170 | # except Exception as e:
171 | # logerror('交易记录保存错误:{}'.format(e))
172 | if __name__ == '__main__':
173 |
174 | # StockPool().insert()
175 | # record=TradeRecord()
176 | # record.symbol='SHSE.600030'
177 | # record.side=1
178 | # record.price=11
179 | # record.volume=1000
180 | # record.amount=10000
181 | # record.trade_time=datetime.now()
182 | # record.record_time = datetime.now()
183 | # record.insert()
184 | print(ExecutionReport().getAccountId())
185 | df= ExecutionReport().hasExistTradeRecord('SHSE.600743','2022-05-17 13:52:07.693056',33120)
186 | # df=TradeRecord().getAccount()
187 | print(df)
188 | # loginfo(TradeRecord().getRecord())
189 | pass
190 |
--------------------------------------------------------------------------------
/quant/model/log.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:日志类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | import datetime
10 | from sqlalchemy import Column, Integer, String, create_engine,DateTime,Float,DECIMAL
11 | from sqlalchemy.ext.declarative import declarative_base
12 | from sqlalchemy.orm import sessionmaker
13 | from quant.util.configutil import get_config
14 |
15 | Base = declarative_base()
16 | # 定义映射类User,其继承上一步创建的Base
17 | class Log(Base):
18 | # 指定本类映射到users表
19 | __tablename__ = 'log'
20 | __connectString__=get_config('DATABASE','Tradedb')
21 | id = Column(Integer, primary_key=True, autoincrement=True)
22 | # 指定name映射到name字段; name字段为字符串类形,
23 | date = Column(String(50))
24 | record_time =Column(DateTime)
25 | result = Column(Integer)
26 | type = Column(Integer)
27 | memo = Column(String(500))
28 |
29 | # 获取股票dataframe
30 | def getdata(self,date,type):
31 | # 初始化数据库连接:
32 | engine = create_engine(self.__connectString__)
33 | DBSession = sessionmaker(bind=engine)
34 | session = DBSession()
35 | datacrawlLog = session.query(Log).filter(Log.date == date).filter(Log.type == type).filter(Log.result == 0).first()
36 | session.close()
37 | return datacrawlLog
38 | # 新增数据
39 | def insert(self):
40 | # 初始化数据库连接:
41 | engine = create_engine(self.__connectString__)
42 | # 创建DBSession类型:
43 | DBSession = sessionmaker(bind=engine)
44 | # 创建session对象:
45 | session = DBSession()
46 | # 添加到session:
47 | session.add(self)
48 | # 提交即保存到数据库:
49 | session.commit()
50 | # 关闭session:
51 | session.close()
52 |
53 | #删除数据
54 | def delete(self,date,type):
55 | engine = create_engine(self.__connectString__)
56 | DBSession = sessionmaker(bind=engine)
57 | # 创建session对象
58 | session = DBSession()
59 | session.query(Log).filter(Log.date==date).filter(Log.type==type).delete(synchronize_session=False)
60 | session.commit()
61 | engine.dispose()
62 |
63 | # log操作
64 | def log_operation(self,type,result,memo):
65 | dl = Log()
66 | dl.record_time = datetime.datetime.now()
67 | dl.type=type
68 | dl.result = result
69 | dl.date=datetime.datetime.now().strftime("%Y-%m-%d")
70 | dl.memo = memo
71 | dl.insert()
72 |
73 | # 每天数据更新结果检查
74 | def check_log(self,date,type):
75 | engine = create_engine(self.__connectString__)
76 | DBSession = sessionmaker(bind=engine)
77 |
78 | dl = Log()
79 | dl.record_time = datetime.datetime.now()
80 | dl.type=1
81 | # if len(result)==0:
82 | # dl.result = 0
83 | # dl.memo = '成功更新所有数据'
84 | # else:
85 | # dl.result = 1
86 | # str2 = ','
87 | # memo = str2.join(result)
88 | # dl.memo = memo
89 | dl.date=date
90 | Log().delete(date, 1)
91 | dl.insert()
92 |
93 | if __name__ == '__main__':
94 | Log().delete('2022-05-06',0)
95 | d= Log().getdata('2022-05-06',0)
96 | if d!=None:
97 | print(d.date)
98 |
--------------------------------------------------------------------------------
/quant/model/myorder.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:掘金订单类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from datetime import datetime
10 | from sqlalchemy import Column, Integer, String, create_engine,DateTime,Float,DECIMAL
11 | from sqlalchemy.ext.declarative import declarative_base
12 | from sqlalchemy.orm import sessionmaker
13 | import pandas as pd
14 |
15 | from quant.util import stringutil
16 | from quant.util.configutil import get_config
17 |
18 | pd.set_option('display.max_rows', 5000)
19 | pd.set_option('display.max_columns', 5000)
20 | pd.set_option('display.width', 1000)
21 |
22 | Base = declarative_base()
23 | # 定义映射类User,其继承上一步创建的Base
24 | class MyOrder(Base):
25 | # 指定本类映射到users表
26 | __tablename__ = 'myorder'
27 | __connectString__=get_config('DATABASE','tradedb')
28 | id = Column(Integer, primary_key=True, autoincrement=True)
29 | # 指定name映射到name字段; name字段为字符串类形,
30 | account_id = Column(String(50))
31 | symbol = Column(String(20))
32 | side = Column(Integer)
33 | price = Column(Float(10))
34 | volume = Column(Integer)
35 | amount = Column(Float(20))
36 | strategy=Column(String(50))
37 | trade_date = Column(String(50))
38 | trade_time = Column(DateTime,default=datetime.now())
39 | record_time= Column(DateTime,default=datetime.now())
40 | status = Column(Integer)
41 | mode = Column(Integer)
42 | cl_ord_id = Column(String(50))
43 | order_id = Column(String(50))
44 | ex_ord_id = Column(String(50))
45 | created_at = Column(DateTime, default=datetime.now())
46 | updated_at = Column(DateTime, default=datetime.now())
47 |
48 | def tojson(self):
49 | js = {'account_id': self.account_id, 'symbol': self.symbol,
50 | 'side': self.side,'price': self.price,'volume': self.volume,'amount': self.amount,'strategy': self.strategy,'trade_date': self.trade_date
51 | ,'trade_time': self.trade_time,'record_time': self.record_time,'status': self.status,'cl_ord_id': self.cl_ord_id,'order_id': self.order_id
52 | ,'ex_ord_id': self.ex_ord_id,'created_at': self.created_at,'updated_at': self.updated_at}
53 | return js
54 |
55 | # 获取股票dataframe
56 | def getdata(self,order_id):
57 | # 初始化数据库连接:
58 | engine = create_engine(self.__connectString__)
59 | DBSession = sessionmaker(bind=engine)
60 | session = DBSession()
61 | order = session.query(MyOrder).filter(MyOrder.order_id == order_id).first()
62 | return order
63 |
64 | # 新增数据
65 | def insert(self):
66 | # 初始化数据库连接:
67 | engine = create_engine(self.__connectString__)
68 | # 创建DBSession类型:
69 | DBSession = sessionmaker(bind=engine)
70 | # 创建session对象:
71 | session = DBSession()
72 | # 添加到session:
73 | session.add(self)
74 | # 提交即保存到数据库:
75 | session.commit()
76 | # 关闭session:
77 | session.close()
78 |
79 | # 修改更新数据
80 | def update(self):
81 | json=self.tojson()
82 | print(json)
83 | engine = create_engine(self.__connectString__)
84 | DBSession = sessionmaker(bind=engine)
85 | session = DBSession()
86 | session.query(MyOrder).filter(MyOrder.order_id == self.order_id).update(json)
87 | session.commit()
88 | engine.dispose()
89 |
90 | # 保存
91 | def save(self):
92 | order=self.getdata(self.order_id)
93 | if order==None:
94 | self.insert()
95 | else:
96 | self.update()
97 |
98 | # 获取清仓股票
99 | def get_sellout_profit(self,account_id='59ae65ad-c062-11ec-bde8-00163e0a4100',start='2022-09-27',end='2022-09-29'):
100 | engine = create_engine(self.__connectString__)
101 | df_sellout = pd.read_sql("SELECT symbol,cast(SUM(side*volume) AS signed) as volume FROM `myorder` WHERE STATUS=3 AND account_id='{}' AND trade_date>='{}' AND trade_date<='{}' GROUP BY symbol HAVING volume=0 order BY volume;".format(account_id,start,end), con=engine)
102 |
103 | df=pd.read_sql("SELECT * FROM `myorder` WHERE STATUS=3 AND account_id='{}' AND trade_date>='{}' AND trade_date<='{}'".format(account_id,start,end), con=engine)
104 | df_stock= df.loc[df['symbol'].isin(df_sellout['symbol'].values), :].copy()
105 | # engine.dispose()
106 | df_stock=df_stock.groupby(by=['symbol']).apply(lambda x:-(x['side']*x['volume']*x['price']).sum())
107 | # print(df_stock)
108 |
109 | df_profit = pd.DataFrame(data=df_stock)
110 | df_profit.reset_index(inplace=True)
111 | df_profit.columns=['symbol','profit']
112 | # print(df_gb.columns)
113 | # return df_profit['profit'].sum()
114 | return df_profit
115 |
116 | def get_day_sellout_stock(self,account_id='59ae65ad-c062-11ec-bde8-00163e0a4100',date=datetime.now().strftime('%Y-%m-%d')):
117 | engine = create_engine(self.__connectString__)
118 | df_sellout = pd.read_sql("SELECT symbol,cast(SUM(side*volume) AS signed) as volume FROM `myorder` WHERE STATUS=3 AND account_id='{}' AND symbol IN (SELECT symbol FROM myorder WHERE STATUS=3 AND side=-1 AND account_id='{}' AND trade_date='{}') GROUP BY symbol HAVING volume=0 order BY volume;".format(account_id,account_id,date), con=engine)
119 | return df_sellout
120 | # print(df_sellout)
121 |
122 | def get_day_sellout_profit(self,account_id='59ae65ad-c062-11ec-bde8-00163e0a4100',date=datetime.now().strftime('%Y-%m-%d')):
123 | df_sellout = self.get_day_sellout_stock(account_id=account_id, date=date)
124 | engine = create_engine(self.__connectString__)
125 | df = pd.read_sql("SELECT * FROM `myorder` WHERE STATUS=3 AND account_id='{}' AND trade_date<='{}'".format(account_id, date), con=engine)
126 | symbols=stringutil.array_to_string(df_sellout['symbol'].values)
127 | symbols="'{}'".format(symbols.replace(',',"','"))
128 | df_sell=pd.read_sql("SELECT symbol,cast(SUM(side*volume) AS signed) as volume FROM `myorder` WHERE STATUS=3 AND side=-1 AND account_id='{}' AND symbol IN ({}) AND trade_date='{}' GROUP BY symbol order BY volume;".format(account_id,symbols, date), con=engine)
129 | engine.dispose()
130 |
131 | df_out = df.loc[df.symbol.isin(df_sellout.symbol) & (df['side'] == 1), :].copy()
132 | df_ret = pd.DataFrame(columns=df_out.columns)
133 |
134 | df_ret=pd.concat([df_ret,df.loc[(df['trade_date']== date) & (df['side'] == -1) & (df['symbol'].isin(df_sell['symbol'])), :].copy()],axis=0,ignore_index=True)
135 |
136 | df_sell.set_index('symbol', inplace=True)
137 |
138 | for i,row in df_sellout.iterrows():
139 | df_symbol=df_out.loc[(df_out['symbol']==row['symbol']) ,:].copy()
140 | df_symbol.sort_values(by='updated_at', ascending=False,inplace=True)
141 | df_symbol['volume_cumsum']=df_symbol['volume'].cumsum()
142 |
143 | df_ret=pd.concat([df_ret,df_symbol.loc[df_symbol['volume_cumsum']<=-df_sell.at[row['symbol'],'volume'],:].copy()],axis=0,ignore_index=True)
144 |
145 | return df_ret
146 |
147 | # 获取单个股票交易记录
148 | def getAccount(self):
149 | engine = create_engine(self.__connectString__)
150 | # where=" "
151 | # if start!=None:
152 | # where=where+" trade_time>='{}'".format(start)
153 | # if end!=None:
154 | # where=where+" and trade_time<='{}'".format(end)
155 | # if where!='':
156 | # where=' where {}'.format(where)
157 | # df=pd.read_sql("SELECT account_id,COUNT(*) AS cnt FROM trade_record GROUP BY account_id {}".format(where),con=engine)
158 | df = pd.read_sql("SELECT account_id,COUNT(*) AS cnt FROM {} GROUP BY account_id ".format(self.__tablename__),
159 | con=engine)
160 | engine.dispose()
161 | return df
162 |
163 | if __name__ == '__main__':
164 |
165 | # StockPool().insert()
166 | # record=TradeRecord()
167 | # record.symbol='SHSE.600030'
168 | # record.side=1
169 | # record.price=11
170 | # record.volume=1000
171 | # record.amount=10000
172 | # record.trade_time=datetime.now()
173 | # record.record_time = datetime.now()
174 | # record.insert()
175 | # print(MyOrder().getAccountId())
176 | # df= MyOrder().get_day_sellout_stock(date='2022-09-29')
177 | df = MyOrder().get_day_sellout_profit(date='2022-09-29')
178 | # df=TradeRecord().getAccount()
179 | print(df)
180 | # loginfo(TradeRecord().getRecord())
181 | pass
182 |
--------------------------------------------------------------------------------
/quant/model/position.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:持仓类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 |
10 | from datetime import datetime
11 | from sqlalchemy import Column, Integer, String, create_engine, DateTime, Float, DECIMAL, text
12 | from sqlalchemy.ext.declarative import declarative_base
13 | from sqlalchemy.orm import sessionmaker
14 | import pandas as pd
15 | import datacompy
16 | from quant.util.configutil import get_config
17 | Base = declarative_base()
18 |
19 | pd.set_option('display.max_rows', 5000)
20 | pd.set_option('display.max_columns', 5000)
21 | pd.set_option('display.width', 1000)
22 | # 定义映射类User,其继承上一步创建的Base
23 | class Position(Base):
24 | # 指定本类映射到users表
25 | __tablename__ = 'position'
26 | __connectString__=get_config('DATABASE','tradedb')
27 | id = Column(Integer, primary_key=True, autoincrement=True)
28 | account_id= Column(String(50))
29 | # 指定name映射到name字段; name字段为字符串类形,
30 | code = Column(String(20))
31 | symbol= Column(String(20))
32 | name = Column(String(20))
33 | #平均建仓成本
34 | open_price=Column(Float(10))
35 | # 持仓总数量
36 | volume=Column(Integer)
37 | amount=Column(Float(10))
38 | #可用数量
39 | can_use_volume=Column(Integer)
40 | # 冻结量
41 | frozen_volume = Column(Integer)
42 | # 昨量
43 | yesterday_volume= Column(Integer)
44 | # 当天量
45 | on_road_volume= Column(Integer)
46 | # 现价
47 | price = Column(Float(10))
48 | # 持仓市值(现值)
49 | market_value= Column(Float(10))
50 |
51 | # 持仓盈利
52 | profit_amount = Column(Float(20))
53 | profit_rate = Column(Float(10))
54 |
55 | # ------------------------------------
56 | inflow = Column(Float(20))
57 | factor = Column(String(30))
58 | rating = Column(String(30))
59 | strategy = Column(String(20))
60 |
61 | # pe = Column(Float(20))
62 | # holderchange = Column(Float(20))
63 | # # 公司利润
64 | # profit = Column(Float(20))
65 | # industry = Column(String(30))
66 | # industry_name = Column(String(30))
67 | date = Column(String(20))
68 | # seldate = Column(DateTime, default=datetime.now())
69 | update_time= Column(DateTime, default=datetime.now())
70 | created_at= Column(DateTime, default=datetime.now())
71 | def insert(self):
72 | # 初始化数据库连接:
73 | engine = create_engine(self.__connectString__)
74 | # 创建DBSession类型:
75 | DBSession = sessionmaker(bind=engine)
76 | # 创建session对象:
77 | session = DBSession()
78 | # 添加到session:
79 | session.add(self)
80 | # 提交即保存到数据库:
81 | session.commit()
82 | # 关闭session:
83 | session.close()
84 |
85 | # 批量插入股票池股票
86 | def batch_insert(self,df):
87 | engine = create_engine(self.__connectString__)
88 | df.to_sql(Position.__tablename__, engine, index=False, if_exists='append')
89 | engine.dispose()
90 |
91 | # 获取股票池股票
92 | def get_all_stock(self):
93 | engine = create_engine(self.__connectString__)
94 | df=pd.read_sql('select * from {}'.format(self.__tablename__),con=engine)
95 | engine.dispose()
96 | return df
97 |
98 | # 获取股票池股票
99 | def get_stock_by_code(self,code):
100 | engine = create_engine(self.__connectString__)
101 | df=pd.read_sql("SELECT * FROM {} WHERE CODE='{}' ".format(self.__tablename__,code),con=engine)
102 | engine.dispose()
103 | return df
104 |
105 | # 获取股票池股票
106 | def get_stock(self,start=None,end=None,factor=None):
107 | engine = create_engine(self.__connectString__)
108 | where=''
109 | if factor!=None:
110 | where = " factor='{}'".format(factor)
111 | if start != None:
112 | if where !='':
113 | where=where+" and "
114 | where = where + " seldate>='{}'".format(start)
115 | if end != None:
116 | if where != '':
117 | where = where + " and "
118 | where = where + " seldate<='{}'".format(end)
119 | if where != '':
120 | where = ' where {}'.format(where)
121 | df = pd.read_sql("select * from {} {}".format(self.__tablename__,where), con=engine)
122 | engine.dispose()
123 | return df
124 |
125 | # 删除代码列数据
126 | def delete(self,symbols=[]):
127 | engine = create_engine(self.__connectString__)
128 | DBSession = sessionmaker(bind=engine)
129 | # 创建session对象
130 | session = DBSession()
131 | session.query(Position).filter(Position.code.in_(symbols)).delete(synchronize_session=False)
132 | session.commit()
133 | engine.dispose()
134 |
135 | # 删除所有数据
136 | def delete_all(self,account_id):
137 | engine = create_engine(self.__connectString__)
138 | DBSession = sessionmaker(bind=engine)
139 | # 创建session对象
140 | session = DBSession()
141 | # session.query(Position).filter(text('1=1')).delete(synchronize_session=False)
142 | session.query(Position).filter(Position.account_id==account_id).delete(synchronize_session=False)
143 | session.commit()
144 | engine.dispose()
145 |
146 | # 查询并删除某策略的数据
147 | def delete_strategy(self,strategy):
148 | engine = create_engine(self.__connectString__)
149 | DBSession = sessionmaker(bind=engine)
150 | # 创建session对象
151 | session = DBSession()
152 | session.query(Position).filter(Position.strategy==strategy).delete(synchronize_session=False)
153 | session.commit()
154 | engine.dispose()
155 |
156 | # 更新股票评级
157 | def update(self,code,js):
158 | engine = create_engine(self.__connectString__)
159 | DBSession = sessionmaker(bind=engine)
160 | session = DBSession()
161 | session.query(Position).filter(Position.code == code).update(js)
162 | session.commit()
163 | engine.dispose()
164 |
165 | def get_position(self):
166 | engine = create_engine(self.__connectString__)
167 | DBSession = sessionmaker(bind=engine)
168 | session = DBSession()
169 | ret = session.query(Position).all()
170 | session.commit()
171 | engine.dispose()
172 | return ret
173 |
174 | #对象转json
175 | # @classmethod
176 | def to_json(self):
177 | js = {'account_id': self.account_id, 'code': self.code ,'symbol': self.symbol ,'profit_amount': self.profit_amount,'profit_rate': self.profit_rate ,
178 | 'name': self.name ,'price': self.price ,'open_price': self.open_price,'volume': self.volume,'amount': self.amount,'can_use_volume': self.can_use_volume
179 | ,'frozen_volume': self.frozen_volume ,'on_road_volume': self.on_road_volume,'market_value': self.market_value,'update_time': self.update_time,'created_at': self.created_at }
180 | return js
181 |
182 | # 对象数组转dataframe
183 | @classmethod
184 | def to_df(self,pos):
185 | df=pd.DataFrame()
186 | if len(pos)>0:
187 | df=pd.DataFrame(pos[0].to_json(),index=[0])
188 | for i in range(len(pos)):
189 | if i>0:
190 | # df=df.append(pd.DataFrame(pos[i].to_json(),index=[i]))
191 | df=pd.concat([df,pd.DataFrame(pos[i].to_json(),index=[i])],axis=0,ignore_index=True)
192 | # df['symbol']=df['code'].apply(lambda x:stockutil.xtToGmSymbol(x))
193 | return df
194 |
195 | # 获取持仓策略及评级
196 | def get_symbols_strategy(self,account_id,symbols=[]):
197 | engine = create_engine(self.__connectString__)
198 | DBSession = sessionmaker(bind=engine)
199 | # 创建session对象
200 | session = DBSession()
201 | query = session.query(Position).filter(Position.account_id==account_id).filter(Position.symbol.in_(symbols))
202 | df = pd.read_sql(query.statement, engine)
203 | session.commit()
204 | engine.dispose()
205 | df.set_index('symbol',inplace=True)
206 | df['stock_type']='holding'
207 | df=df.loc[:,['strategy','rating','stock_type','open_price']]
208 | return df
209 |
210 | # 获取本地数据持仓数据
211 | def get_local_position(self,account_id='59ae65ad-c062-11ec-bde8-00163e0a4100'):
212 | engine = create_engine(self.__connectString__)
213 | df_pos = pd.read_sql("SELECT * FROM `position` WHERE account_id='{}'".format(account_id), con=engine)
214 | engine.dispose()
215 | return df_pos
216 |
217 | # 检查持仓是否和交易记录一致
218 | def check_position_data(self,account_id='59ae65ad-c062-11ec-bde8-00163e0a4100'):
219 | engine = create_engine(self.__connectString__)
220 | df_ord = pd.read_sql("SELECT symbol,cast(SUM(side*volume) AS signed) AS volume FROM `myorder` WHERE STATUS=3 AND account_id='{}' GROUP BY symbol HAVING volume>0 order BY volume".format(account_id), con=engine)
221 | df_pos = pd.read_sql("SELECT * FROM `position` WHERE account_id='{}'".format(account_id), con=engine)
222 | engine.dispose()
223 |
224 | # df_ord = df_ord.loc[:, ['symbol', 'volume']]
225 | print(df_ord)
226 |
227 | df_pos=df_pos.loc[:,['symbol','volume']]
228 | df_pos.sort_values(by='volume',ascending=True,inplace=True)
229 | print(df_pos)
230 | compare = datacompy.Compare(df_ord, df_pos, join_columns='volume')
231 |
232 | return compare.matches()
233 | # print(compare.matches()) # 最后判断是否相等,返回 bool
234 | # print(compare.report()) # 打印报告详情,返回 string
235 |
236 | # 获取缓存持仓信息
237 | def get_cache_position(self, positions, symbol):
238 | ret = None
239 | for pos in positions:
240 | if pos.symbol == symbol:
241 | ret = pos
242 | break
243 | return ret
244 |
245 |
246 |
247 | if __name__ == '__main__':
248 | b= Position().get_symbols_strategy('62bbe5fb-3f95-11ed-976c-00163e18a8b3',['SZSE.002265','SZSE.002850','SHSE.601633'])
249 | print(b)
250 | # Position().delete(['002781', '002437', '002679', '300782', '000987', '600661', '000333', '600690', '601231', '600410', '603808', '399001', '000001'])
251 | # loginfo(StockPool().delete())
252 | pass
253 |
--------------------------------------------------------------------------------
/quant/model/setting.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:帐号交易参数设置类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | import pandas as pd
10 | from gm.enum import MODE_LIVE, MODE_BACKTEST
11 | from sqlalchemy import Column, Integer, String, create_engine,DateTime,Float,DECIMAL
12 | from sqlalchemy.ext.declarative import declarative_base
13 | from sqlalchemy.orm import sessionmaker
14 |
15 | from quant.util.configutil import get_config
16 |
17 | Base = declarative_base()
18 | # 定义映射类User,其继承上一步创建的Base
19 | class Setting(Base):
20 | # 指定本类映射到users表
21 | __tablename__ = 'setting'
22 | __connectString__=get_config('DATABASE','Tradedb')
23 | id = Column(Integer, primary_key=True, autoincrement=True)
24 | # 指定name映射到name字段; name字段为字符串类形,
25 | account_id = Column(String(50))
26 | max_position =Column(Float(5))
27 | max_single_position = Column(Float(5))
28 | max_stock_number=Column(Integer)
29 | per_buy_amount = Column(Float(10))
30 | memo = Column(String(200))
31 | name = Column(String(50))
32 | broker = Column(String(50))
33 | initial_capital=Column(Float(10))
34 | account_type=Column(Integer)
35 |
36 |
37 | def tojson(self):
38 | js = {'account_id': self.account_id, 'max_position': self.max_position,
39 | 'max_single_position': self.max_single_position,'per_buy_amount': self.per_buy_amount,'memo': self.memo,'name': self.name,'broker': self.broker,'account_type': self.account_type}
40 | return js
41 |
42 | # 获取所有帐号设置
43 | def get_all_data(self):
44 | engine = create_engine(self.__connectString__)
45 | df=pd.read_sql("select * from setting",con=engine)
46 | engine.dispose()
47 | return df
48 |
49 | # 获取股票dataframe
50 | def getdata(self,account_id):
51 | # 初始化数据库连接:
52 | engine = create_engine(self.__connectString__)
53 | DBSession = sessionmaker(bind=engine)
54 | session = DBSession()
55 | setting = session.query(Setting).filter(Setting.account_id == account_id).first()
56 | return setting
57 | # 新增数据
58 | def insert(self):
59 | # 初始化数据库连接:
60 | engine = create_engine(self.__connectString__)
61 | # 创建DBSession类型:
62 | DBSession = sessionmaker(bind=engine)
63 | # 创建session对象:
64 | session = DBSession()
65 | # 添加到session:
66 | session.add(self)
67 | # 提交即保存到数据库:
68 | session.commit()
69 | # 关闭session:
70 | session.close()
71 |
72 | def update(self):
73 | json=self.tojson()
74 | engine = create_engine(self.__connectString__)
75 | DBSession = sessionmaker(bind=engine)
76 | session = DBSession()
77 | session.query(Setting).filter(Setting.account_id == self.account_id).update(json)
78 | session.commit()
79 | engine.dispose()
80 |
81 | def save(self):
82 | setting=self.getdata(self.account_id)
83 | if setting==None:
84 | self.insert()
85 | else:
86 | self.update()
87 |
88 | # 止损
89 | @classmethod
90 | def get_setting_accountid(self,context):
91 | ret = ""
92 | if context.mode==MODE_BACKTEST:
93 | ret="back_test"
94 | else:
95 | ret=context.account().id
96 | return ret
97 |
98 | # 获取当天发生的交易帐号
99 | def get_trade_account(self,date):
100 | engine = create_engine(self.__connectString__)
101 | # df=pd.read_sql("SELECT * FROM setting WHERE account_id IN (SELECT DISTINCT account_id FROM `order` WHERE trade_date='{}')".format(date),con=engine)
102 | df = pd.read_sql("SELECT * FROM setting ", con=engine)
103 | engine.dispose()
104 | return df
105 | if __name__ == '__main__':
106 | s=Setting()
107 | # s=Setting().getdata("back_test")
108 | s.account_id="live"
109 | s.max_position = 0.5
110 | s.max_single_position = 0.5
111 | s.per_buy_amount = 0.5
112 | s.memo = "实时"
113 | print(s.tojson())
114 | df=pd.DataFrame( s.tojson(), )
115 | # df = json_normalize(s)
116 |
117 | print(df)
118 | # s.per_buy_amount=10000
119 | # # s.save()
120 | # setting = Setting().getdata("live")
121 | # print(setting.max_position)
122 | pass
123 | # Log().delete('2022-05-06',0)
124 | # d= Log().getdata('2022-05-06',0)
125 | # if d!=None:
126 | # print(d.date)
127 |
--------------------------------------------------------------------------------
/quant/model/stockindicator.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:股票
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | import datetime
10 |
11 | class StockIndicator():
12 | def __init__(self):
13 | self.symbol = ''
14 | self.open = ''
15 | self.close = ''
16 | self.low = ''
17 | self.high = ''
18 | self.volume = ''
19 | self.amount = ''
20 | self.eob = ''
21 | self.pre_close = ''
22 | self.k_pre = ''
23 | self.d_pre = ''
24 | self.j_pre = ''
25 | self.date = ''
26 | self.inflow = 0
27 | self.strategy=''
28 | self.stock_type=''
29 | self.minute_kdj=[]
30 | self.inflow_time = datetime.datetime.strptime("{} {}".format(datetime.datetime.now().strftime("%Y-%m-%d"),"09:30:00"),"%Y-%m-%d %H:%M:%S")
31 |
32 | # 'minute_kdj': self.minute_kdj,
33 | def to_json(self):
34 | js = {'symbol': self.symbol, 'open': self.open, 'close': self.close,
35 | 'low': self.low, 'high': self.high,
36 | 'volume': self.volume, 'amount': self.amount, 'eob': self.eob, 'pre_close': self.pre_close,
37 | 'k_pre': self.k_pre,'d_pre': self.d_pre, 'j_pre': self.j_pre, 'date': self.date, 'inflow': self.inflow, 'strategy': self.strategy,'stock_type': self.stock_type, 'inflow_time': self.inflow_time}
38 | return js
--------------------------------------------------------------------------------
/quant/model/stockpool.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:股票池类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | import pandas as pd
10 | from sqlalchemy import Column, Integer, String, create_engine, DateTime, Float, DECIMAL
11 | from sqlalchemy.ext.declarative import declarative_base
12 | from sqlalchemy.orm import sessionmaker
13 | from quant.util.configutil import get_config
14 |
15 | Base = declarative_base()
16 | # 定义映射类User,其继承上一步创建的Base
17 | class StockPool(Base):
18 | # 指定本类映射到users表
19 | __tablename__ = 'stock_pool'
20 | __connectString__ = get_config('DATABASE', 'tradedb')
21 |
22 | id = Column(Integer, primary_key=True, autoincrement=True)
23 | # 指定name映射到name字段; name字段为字符串类形,
24 | code = Column(String(20))
25 | symbol = Column(String(20))
26 | name = Column(String(20))
27 | change = Column(DECIMAL(6, 2))
28 | price = Column(Float(10))
29 | close = Column(Float(10))
30 | date = Column(String(20))
31 | strategy = Column(String(30))
32 | rating = Column(String(30))
33 |
34 | def insert(self):
35 | # 初始化数据库连接:
36 | engine = create_engine(self.__connectString__)
37 | # 创建DBSession类型:
38 | DBSession = sessionmaker(bind=engine)
39 |
40 | # 创建session对象:
41 | session = DBSession()
42 | # 添加到session:
43 | session.add(self)
44 | # 提交即保存到数据库:
45 | session.commit()
46 | # 关闭session:
47 | session.close()
48 |
49 | # 获取股票dataframe
50 | def getPositionStock(self):
51 | engine = create_engine(self.__connectString__)
52 | df = pd.read_sql("SELECT * FROM pick_time WHERE strategy='by_holding'", con=engine)
53 | engine.dispose()
54 | return df
55 |
56 | # 获取股票dataframe
57 | def getStock(self):
58 | engine = create_engine(self.__connectString__)
59 | df = pd.read_sql('select * from pick_time', con=engine)
60 | engine.dispose()
61 | return df
62 |
63 |
64 | # 获取股票通过代码
65 | def get_stock_by_code(self, code):
66 | engine = create_engine(self.__connectString__)
67 | df = pd.read_sql("select * from pick_time WHERE CODE='{}'".format(code), con=engine)
68 | engine.dispose()
69 | return df
70 |
71 | # 获取股票价格
72 | @classmethod
73 | def get_price(self, df, code):
74 | price = 0
75 | for i, row in df.iterrows():
76 | if row.code == code:
77 | price = row.price
78 | return price
79 |
80 | # 条件删除数据
81 | def delete(self, symbols=[]):
82 | engine = create_engine(self.__connectString__)
83 | DBSession = sessionmaker(bind=engine)
84 | # 创建session对象
85 | session = DBSession()
86 | session.query(StockPool).filter(StockPool.code.in_(symbols)).delete()
87 | session.commit()
88 | engine.dispose()
89 |
90 | # 条件删除数据
91 | def delete_all(self):
92 | engine = create_engine(self.__connectString__)
93 | DBSession = sessionmaker(bind=engine)
94 | # 创建session对象
95 | session = DBSession()
96 | # session.query(PickTime).filter(Teacher.id > 4).delete()
97 | session.query(StockPool).delete()
98 | session.commit()
99 | engine.dispose()
100 |
101 | def update(self, code, js):
102 | engine = create_engine(self.__connectString__)
103 | DBSession = sessionmaker(bind=engine)
104 | session = DBSession()
105 | session.query(StockPool).filter(StockPool.code == code).update(js)
106 | session.commit()
107 | engine.dispose()
108 |
109 | # 批量插入股票池股票
110 | def batch_insert(self, df):
111 | engine = create_engine(self.__connectString__)
112 | df.to_sql(StockPool.__tablename__, engine, index=False, if_exists='append')
113 | engine.dispose()
114 |
115 | # 批量插入股票池股票
116 | def row_insert(self, row):
117 | engine = create_engine(self.__connectString__)
118 | row.to_sql(StockPool.__tablename__, engine, index=False, if_exists='append')
119 | engine.dispose()
120 |
121 | def get_picktime(self):
122 | engine = create_engine(self.__connectString__)
123 | df = pd.read_sql("select * from {} WHERE rating='{}'".format(self.__tablename__, "买入"), con=engine)
124 | engine.dispose()
125 | return df
126 |
127 | # 获取订单买入策略
128 | def get_symbols_strategy(self, symbols=[]):
129 | engine = create_engine(self.__connectString__)
130 | DBSession = sessionmaker(bind=engine)
131 | # 创建session对象
132 | session = DBSession()
133 | query = session.query(StockPool).filter(StockPool.symbol.in_(symbols))
134 | df = pd.read_sql(query.statement, engine)
135 | session.commit()
136 | engine.dispose()
137 | df.sort_values(by='date')
138 | df.drop_duplicates(subset=['symbol'], keep='last', inplace=True)
139 | df.set_index('symbol', inplace=True)
140 | df['stock_type'] = 'buyin'
141 | df['open_price'] = 0
142 | df = df.loc[:, ['strategy', 'rating', 'stock_type', 'open_price']]
143 | return df
144 |
145 |
146 | if __name__ == '__main__':
147 | b = StockPool().get_symbols_strategy(['SZSE.000059'])
148 | print(b)
149 |
--------------------------------------------------------------------------------
/quant/model/traderecord.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:交易记录类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 |
10 | from datetime import datetime
11 | from decimal import Decimal
12 |
13 | from sqlalchemy import Column, Integer, String, create_engine,DateTime,Float,DECIMAL
14 | from sqlalchemy.ext.declarative import declarative_base
15 | from sqlalchemy.orm import sessionmaker
16 | import pandas as pd
17 |
18 | from quant.logger import Logger
19 | from quant.util.configutil import get_config
20 |
21 | Base = declarative_base()
22 | # 定义映射类User,其继承上一步创建的Base
23 | class TradeRecord(Base):
24 | # 指定本类映射到users表
25 | __tablename__ = 'trade_record'
26 | __connectString__=get_config('DATABASE','tradedb')
27 | id = Column(Integer, primary_key=True, autoincrement=True)
28 | # 指定name映射到name字段; name字段为字符串类形,
29 | account_id = Column(String(50))
30 | symbol = Column(String(20))
31 | side = Column(Integer)
32 | price = Column(Float(10))
33 | volume = Column(Integer)
34 | amount = Column(Float(20))
35 | strategy=Column(String(50))
36 | cl_ord_id = Column(String(50))
37 | mode = Column(Integer)
38 | trade_date = Column(String(50))
39 | trade_time = Column(DateTime,default=datetime.now())
40 | record_time= Column(DateTime,default=datetime.now())
41 |
42 | # __repr__方法用于输出该类的对象被print()时输出的字符串,如果不想写可以不写
43 | def __repr__(self):
44 | return "" % (
45 | self.account_id,self.symbol,self.name,self.side,self.price,self.volume,self.amount,self.strategy,self.trade_date,self.trade_time)
46 |
47 | def insert(self):
48 | # 初始化数据库连接:
49 | engine = create_engine(self.__connectString__)
50 | # 创建DBSession类型:
51 | DBSession = sessionmaker(bind=engine)
52 |
53 | # 创建session对象:
54 | session = DBSession()
55 | # 创建新User对象:
56 | # 添加到session:
57 | session.add(self)
58 | # 提交即保存到数据库:
59 | session.commit()
60 | # 关闭session:
61 | session.close()
62 |
63 | def getRecord(self,start=None,end=None,acount='d28f4b30-4bf9-11ec-ad1b-00163e0a4100'):
64 | engine = create_engine(self.__connectString__)
65 | where=''
66 | if start!=None:
67 | where="trade_date>='{}'".format(start)
68 | if end!=None:
69 | if where!='':
70 | where=where+' and '
71 | where=where+"trade_date<='{}'".format(end)
72 | if acount!="":
73 | if where != '':
74 | where = where + ' and '
75 | where = where + " account_id='{}'".format(acount)
76 | if where!='':
77 | where=" where {}".format(where)
78 | df=pd.read_sql("select * from trade_record {}".format(where),con=engine)
79 | engine.dispose()
80 | return df
81 | # 获取单个股票交易记录
82 | def getStockRecord(self,symbol,start=None,end=None):
83 | engine = create_engine(self.__connectString__)
84 | where=" symbol='{}'".format(symbol)
85 | if start!=None:
86 | where=where+" and trade_time>='{}'".format(start)
87 | if end!=None:
88 | where=where+" and trade_time<='{}'".format(end)
89 | if where!='':
90 | where=' where {}'.format(where)
91 | df=pd.read_sql("select * from trade_record {}".format(where),con=engine)
92 | engine.dispose()
93 | return df
94 | # 获取单个股票交易记录
95 | def hasExistTradeRecord(self,symbol,trade_time,amount):
96 | ret=False
97 | engine = create_engine(self.__connectString__)
98 | where="where symbol='{}' and trade_time='{}' and amount={} ".format(symbol,trade_time,amount)
99 | df=pd.read_sql("select * from trade_record {}".format(where),con=engine)
100 | engine.dispose()
101 | if len(df)>0:
102 | ret=True
103 | return ret
104 | # 获取单个股票交易记录
105 | def getAccount(self):
106 | engine = create_engine(self.__connectString__)
107 | df = pd.read_sql("SELECT account_id,COUNT(*) AS cnt FROM trade_record GROUP BY account_id ",
108 | con=engine)
109 | engine.dispose()
110 | return df
111 |
112 | # 获取最多交易记录帐号
113 | def getAccountId(self):
114 | ret=''
115 | df=self.getAccount()
116 | if len(df)>0:
117 | df.sort_values(by=['cnt'],ascending=False,inplace=True)
118 | ret=df.at[0,'account_id']
119 | return ret
120 | # 获取单个股票交易记录
121 | def getTradeRecord(self,symbol,acount,start=None,end=None):
122 | engine = create_engine(self.__connectString__)
123 | where=" symbol='{}' and account_id='{}'".format(symbol,acount)
124 | if start!=None:
125 | where=where+" and trade_time>='{}'".format(start)
126 | if end!=None:
127 | where=where+" and trade_time<='{}'".format(end)
128 | if where!='':
129 | where=' where {}'.format(where)
130 | df=pd.read_sql("select * from trade_record {}".format(where),con=engine)
131 | engine.dispose()
132 | df_buy=df.loc[df['side']==1,:]
133 | df_buy=df_buy.groupby(['trade_date']).sum(['amount', 'volume'])
134 | df_buy['buy']=round((df_buy['amount']/df_buy['volume']),2)
135 |
136 | df_sell = df.loc[df['side'] == -1, :]
137 | df_sell = df_sell.groupby(['trade_date']).sum(['amount', 'volume'])
138 | df_sell['sell'] = round((df_sell['amount'] / df_sell['volume']), 2)
139 | df_trade = df.groupby(['trade_date']).sum(['amount', 'volume'])
140 | df_trade['buy']=df_buy['buy']
141 | df_trade['sell'] = df_sell['sell']
142 | df_trade.index=pd.to_datetime(df_trade.index)
143 | # print(df_trade)
144 | return df_trade
145 |
146 | def batch_insert(self,df):
147 | for i,execrpt in df.iterrows():
148 | if execrpt.exec_type == 15:
149 | trade_record = TradeRecord()
150 | trade_record.account_id = execrpt.account_id
151 | trade_record.symbol = execrpt.symbol
152 | if execrpt.side == 1:
153 | trade_record.side = 1
154 | elif execrpt.side == 2:
155 | trade_record.side = -1
156 | trade_record.price = execrpt.price
157 | trade_record.volume = execrpt.volume
158 | trade_record.cl_ord_id = execrpt.cl_ord_id
159 | trade_record.mode = execrpt.mode
160 | trade_record.amount =Decimal(execrpt.amount).quantize(Decimal('0.00'))
161 | trade_record.trade_date = execrpt.created_at.strftime('%Y-%m-%d')
162 | trade_record.record_time =datetime.now()
163 | trade_record.trade_time=pd.to_datetime(execrpt.created_at).strftime('%Y-%m-%d %H:%M:%S')
164 | if TradeRecord().hasExistTradeRecord(trade_record.symbol, trade_record.trade_time,
165 | trade_record.amount) == False:
166 | try:
167 | trade_record.insert()
168 | except Exception as e:
169 | Logger().logerror('交易记录保存错误:{}'.format(e))
170 | if __name__ == '__main__':
171 |
172 | # StockPool().insert()
173 | # record=TradeRecord()
174 | # record.symbol='SHSE.600030'
175 | # record.side=1
176 | # record.price=11
177 | # record.volume=1000
178 | # record.amount=10000
179 | # record.trade_time=datetime.now()
180 | # record.record_time = datetime.now()
181 | # record.insert()
182 | print(TradeRecord().getAccountId())
183 | df= TradeRecord().hasExistTradeRecord('SHSE.600743','2022-05-17 13:52:07.693056',33120)
184 | # df=TradeRecord().getAccount()
185 | print(df)
186 | # loginfo(TradeRecord().getRecord())
187 | pass
188 |
--------------------------------------------------------------------------------
/quant/picker.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:选股类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 |
10 | import sys,os
11 | import pandas as pd
12 | from quant.api.gmapi import GmApi
13 | from quant.enums.ratings import Ratings
14 | from quant.enums.strategys import Strategys
15 | from quant.util import stockutil
16 |
17 | base_dir=os.path.dirname(os.path.dirname(__file__))#获取pathtest的绝对路径
18 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
19 | from gm.enum import MODE_BACKTEST
20 | from quant.model.stockpool import StockPool
21 | # 择时
22 | class Picker():
23 | # 获取择时股票列表
24 | def get_picktime(self,context,codes=[]):
25 | account_type=context.setting.account_type
26 | count=50-len(context.positions)
27 | if len(codes)==0:
28 | if context.mode==MODE_BACKTEST:
29 | df = StockPool().get_picktime()
30 | df = df.head(count)
31 |
32 | df.drop_duplicates(subset='code', keep='first', inplace=True)
33 |
34 | else:
35 | df=Picker().picktime_by_codes(codes)
36 | df.reset_index(drop=True,inplace=True)
37 | df = df.head(count)
38 | return df
39 | #
40 | def picktime_by_codes(self,codes=[]):
41 | df=pd.DataFrame(columns=['symbol','code','name','change','price','close', 'strategy', 'rating', 'date'])
42 | for code in codes:
43 | df_symbol= GmApi().getHistoryNKdata(symbol=stockutil.getGmSymbol(code),count=1)
44 | if len(df_symbol)>0:
45 | df_symbol['code'] = df_symbol['symbol'].apply(lambda x: stockutil.delSymbolPrefix(x))
46 | df_symbol['name'] = ''
47 | df_symbol['change'] = 0
48 | df_symbol['price'] = df_symbol['close']
49 | df_symbol['strategy'] = Strategys.MeanRevertingStrategy.value
50 | df_symbol['rating'] = Ratings.BuyIn.value
51 | df_symbol['date'] = df_symbol['eob']
52 | df_symbol = df_symbol.loc[:,
53 | ['symbol', 'code', 'name', 'change', 'price', 'close', 'strategy', 'rating', 'date']]
54 | df=pd.concat([df,df_symbol],ignore_index=True,axis=0)
55 |
56 | df.drop_duplicates(subset=['symbol'],keep='first',inplace=True)
57 | return df
58 |
59 | if __name__=='__main__':
60 | df=Picker().picktime_by_codes(codes=['002281','002272'])
61 | print(df)
62 | pass
--------------------------------------------------------------------------------
/quant/reporter.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:盘后发送报告
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | import sys,os
10 | base_dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取pathtest的绝对路径
11 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
12 | from quant.model.order import Order
13 | from datetime import datetime
14 | import pandas as pd
15 | from quant.model.position import Position
16 | from quant.model.setting import Setting
17 |
18 | # 盘后发送报告
19 | class Reporter():
20 |
21 | def __init__(self):
22 | pass
23 |
24 | def get_report(self,date=datetime.now().strftime('%Y-%m-%d')):
25 |
26 | df_acnt=Setting().get_trade_account(date=date)
27 | trade_html=""
28 | for i,row in df_acnt.iterrows():
29 | account_id= row['account_id']
30 | account_name= row['name']
31 | date= date
32 | df_dt=self.get_day_trade_record(account_id= row['account_id'],account_name= row['name'],date= date)
33 | if len(df_dt)>0:
34 | trade_html +="帐号:{},帐号名称:{},日期:{},交易记录:
{}
".format(account_id, account_name,date,df_dt.to_html())
35 |
36 | trade_html +=self.get_position_report(account_id= row['account_id'],account_name= row['name'])
37 |
38 | htm="每天数据更新结果:
{}".format(trade_html)
39 |
40 | # 发送邮件
41 | # mailutil.send_mail(htm)
42 | print(htm)
43 |
44 | def get_day_trade_record(self,account_id='59ae65ad-c062-11ec-bde8-00163e0a4100',account_name='',date=datetime.now().strftime('%Y-%m-%d')):
45 | ret = Order().get_day_trade_record(account_id, date)
46 | return ret
47 |
48 | def get_day_trade_report(self,account_id='59ae65ad-c062-11ec-bde8-00163e0a4100',account_name='',date=datetime.now().strftime('%Y-%m-%d')):
49 | # 当天清仓盈利
50 | df_prof=Order().get_day_sellout_profit(account_id=account_id,date=date)
51 | if len(df_prof)==0:
52 | return ''
53 | df_profit=pd.DataFrame(data=df_prof.groupby("symbol").apply(lambda x:-(x['side']*x['price']*x['volume']).sum()),columns=['盈利'])
54 | # print(df_profit)
55 | # df_name=StockInfo().get_stock_by_symbols(df_profit.index.values)
56 | # df_profit['name']=df_name['name']
57 | df_profit['name']=''
58 | df_factor=Order().get_day_sellout_orders(date=date,account_id=account_id)
59 |
60 | if len(df_factor)>0:
61 | df_buy = Order().get_order_by_symbols(df_factor.index.values, account_id=account_id)
62 | df_buy=df_buy.loc[df_buy['side']==1,:]
63 | df_buy.sort_values(by='trade_time',ascending=False,inplace=True)
64 | df_buy.drop_duplicates(subset='symbol',keep='first',inplace=True)
65 | df_buy.set_index('symbol',inplace=True)
66 |
67 | df_profit['buy_strategy'] = df_buy['strategy']
68 | df_profit['buy_factor'] = df_buy['factor']
69 | df_profit['buy_date'] = df_buy['trade_date']
70 | df_profit['buy_price'] = df_buy['price']
71 |
72 | df_profit['sell_strategy'] = df_factor['strategy']
73 | df_profit['sell_factor'] = df_factor['factor']
74 | df_profit['sell_price'] = df_factor['price']
75 | df_profit['sell_date'] = df_factor['trade_date']
76 |
77 | htm_profit= df_profit.to_html()
78 | profit=round(df_profit['盈利'].sum(),2)
79 |
80 | else:
81 | htm_profit="当天没有清仓的交易"
82 | profit=0
83 |
84 | # df_picktime=Picker().get_live_picktime(export_eastmoney=True)
85 | account = "{},ID:{}".format(account_name, account_id)
86 |
87 | ret="帐号:{}
每天清仓盈利(总计:{}元):
{}
".format(account,profit,htm_profit)
88 | return ret
89 |
90 | def get_position_report(self,account_id,account_name):
91 | # 当天持仓明细
92 | ret=''
93 | df_pos=Position().get_local_position(account_id=account_id)
94 | if len(df_pos)==0:
95 | return ret
96 | df_pos=df_pos.loc[:,['symbol','name','volume','open_price','price','amount','market_value','profit_amount','profit_rate','date']]
97 | pos_profit=round(df_pos['profit_amount'].sum(),2)
98 | htm_pos=df_pos.to_html()
99 | account="{},ID:{}".format(account_name,account_id)
100 | ret = "帐号:{}
每天持仓明细(持仓盈利总计:{}元):
{}
".format(account,pos_profit,htm_pos)
101 | return ret
102 | if __name__=="__main__":
103 | Reporter().get_report(date=datetime.now().strftime('%Y-%m-%d'))
104 | # Reporter().get_report(date='2023-01-10')
--------------------------------------------------------------------------------
/quant/signalengine.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:信号类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 |
10 | import importlib
11 | from quant.util import stringutil
12 |
13 | class SignalEngine():
14 |
15 | def __init__(self,signal_list):
16 | self.signal_list =signal_list
17 | self.signals=dict()
18 | for s in self.signal_list:
19 | s=s.strip()
20 | if s!='':
21 | self.signals[s]=self.init_signals(s)
22 |
23 | # 初始化信息对象
24 | def init_signals(self,signal_name):
25 | imp_class = getattr(importlib.import_module("quant.signals.{}".format(signal_name.lower())),
26 | signal_name)
27 | cls_obj = imp_class()
28 | return cls_obj
29 |
30 | # @classmethod
31 | def fit_buy(self, stock_indicator):
32 | ret = False
33 | fit_name=[]
34 | fit_indicator=[]
35 | for sig_name in self.signals:
36 | ret_fit=self.signals[sig_name].fit_buy(stock_indicator)
37 | if ret_fit[0]:
38 | fit_name.append("{}_fit_buy".format(ret_fit[1]))
39 | fit_indicator.append(ret_fit[2])
40 | ret=True
41 |
42 | return (ret,stringutil.array_to_string(fit_name),stringutil.array_to_string(fit_indicator))
43 |
44 | # @classmethod
45 | def fit_sell(self, stock_indicator):
46 | ret = False
47 | fit_name = []
48 | fit_indicator = []
49 | for sig_name in self.signals:
50 | ret_fit=self.signals[sig_name].fit_sell(stock_indicator)
51 | if ret_fit[0]:
52 | fit_name.append("{}_fit_sell".format(ret_fit[1]))
53 | fit_indicator.append(ret_fit[2])
54 | ret=True
55 | return (ret,stringutil.array_to_string(fit_name),stringutil.array_to_string(fit_indicator))
56 |
57 | if __name__=="__main__":
58 | pass
59 | # df = PickTime().getStock()
60 | # df.set_index('code',inplace=True)
61 | # if FactorEngine().fit(df.loc['600056'],10)==True:
62 | # print('fddddddddddddddd')
63 | # else:
64 | # print('111111111111111111')
--------------------------------------------------------------------------------
/quant/signals.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:交易信号类
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from quant.indicators import Indicator
10 | class Signal():
11 | def __init__(self):
12 | pass
13 | # 止损
14 | @classmethod
15 | def buy_signal(self,df):
16 | df=self.kdj_signal(df)
17 | # df=self.boll_signal(df)
18 | return df
19 |
20 | # macd金叉
21 | @classmethod
22 | def macd_signal(self,df):
23 | df=Indicator.macd(df)
24 | df['dif_pre'] = df['dif'].shift(1)
25 | df["dif_pre"].fillna(method='bfill', inplace=True)
26 | df['dea_pre'] = df['dea'].shift(1)
27 | df["dea_pre"].fillna(method='bfill', inplace=True)
28 | df.loc[(df['dif'] >= df['dea']) & (df['dif_pre'] < df['dea_pre']), ['signal']] = 1
29 | df.loc[(df['dif'] < df['dea']) & (df['dif_pre'] >= df['dea_pre']), ['signal']] = 0
30 | df["signal"].fillna(method='ffill', inplace=True)
31 | return df
32 |
33 | # kdj金叉
34 | @classmethod
35 | def kdj_signal(self,df):
36 | df=Indicator.kdj(df)
37 | # print(df)
38 | df['k_pre'] = df['k'].shift(1)
39 | df["k_pre"].fillna(method='bfill', inplace=True)
40 | df['d_pre'] = df['d'].shift(1)
41 | df["d_pre"].fillna(method='bfill', inplace=True)
42 | df['j_pre'] = df['j'].shift(1)
43 | df["j_pre"].fillna(method='bfill', inplace=True)
44 |
45 | df.loc[(df['k'] >= df['d']) & (df['k_pre'] < df['d_pre']), ['signal']] = 1
46 | df.loc[(df['k'] < df['d']) & (df['k_pre'] >= df['d_pre']), ['signal']] = 0
47 | # df.loc[df['j']>100, ['signal']] = 0
48 | df["signal"].fillna(method='ffill', inplace=True)
49 | # print(df)
50 | return df
51 |
52 | if __name__=="__main__":
53 |
54 | df=Signal().bt(symbol='SHSE.601318', start = '2022-03-01',end = '2022-11-05')
55 |
--------------------------------------------------------------------------------
/quant/signals/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/signals/__init__.py
--------------------------------------------------------------------------------
/quant/signals/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/signals/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/signals/__pycache__/kdjsignal.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/signals/__pycache__/kdjsignal.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/signals/__pycache__/macdsignal.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/signals/__pycache__/macdsignal.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/signals/__pycache__/signalbase.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/signals/__pycache__/signalbase.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/signals/kdjsignal.py:
--------------------------------------------------------------------------------
1 | from abc import abstractmethod
2 | from quant.signals.signalbase import SignalBase
3 |
4 | # kdj
5 | class KdjSignal(SignalBase):
6 |
7 | def __init__(self):
8 | self.name="KdjSignal"
9 |
10 | # 匹配买入信息
11 | # @classmethod
12 | def fit_buy(self, stock_indicator):
13 | ret = False
14 | indicator=''
15 | if (stock_indicator.k_pre < stock_indicator.d_pre) and (stock_indicator.k >= stock_indicator.d) :
16 | indicator="k_pre:{},d_pre:{},k:{},j:{}".format(stock_indicator.k_pre,
17 | stock_indicator.d_pre, stock_indicator.k,
18 | stock_indicator.d)
19 | ret=True
20 | return (ret,self.name,indicator)
21 |
22 | # 匹配卖出信息
23 | # @abstractmethod
24 | def fit_sell(self, stock_indicator):
25 | ret = False
26 | indicator = ''
27 | if(stock_indicator.k_pre > stock_indicator.d_pre) and (stock_indicator.k <= stock_indicator.d):
28 | indicator="k_pre:{},d_pre:{},j_pre:{},k:{},d:{},j:{}".format(stock_indicator.k_pre,
29 | stock_indicator.d_pre, stock_indicator.j_pre,stock_indicator.k,
30 | stock_indicator.d,stock_indicator.j)
31 | ret = True
32 | return (ret,self.name,indicator)
33 |
--------------------------------------------------------------------------------
/quant/signals/macdsignal.py:
--------------------------------------------------------------------------------
1 | from abc import abstractmethod
2 | from quant.signals.signalbase import SignalBase
3 |
4 | # macd
5 | class MacdSignal(SignalBase):
6 |
7 | def __init__(self):
8 | self.name="MacdSignal"
9 |
10 | # 匹配买入信息
11 | # @classmethod
12 | def fit_buy(self, stock_indicator):
13 | ret = False
14 | indicator=''
15 |
16 | if (stock_indicator.pre_dif < stock_indicator.pre_dea) and (stock_indicator.dif >= stock_indicator.dea):
17 | indicator="pre_dif:{},pre_dif:{},dif:{},dea:{}".format(stock_indicator.pre_dif,
18 | stock_indicator.pre_dea, stock_indicator.dif,
19 | stock_indicator.dea)
20 | ret=True
21 | return (ret,self.name,indicator)
22 |
23 | # 匹配卖出信息
24 | # @abstractmethod
25 | def fit_sell(self, stock_indicator):
26 | ret = False
27 | indicator = ''
28 | if(stock_indicator.pre_dif > stock_indicator.pre_dea) and (stock_indicator.dif <= stock_indicator.dea):
29 | indicator="pre_dif:{},pre_dea:{},dif:{},dea:{}".format(stock_indicator.pre_dif,
30 | stock_indicator.pre_dea, stock_indicator.dif,
31 | stock_indicator.dea)
32 | ret = True
33 | return (ret,self.name,indicator)
34 |
--------------------------------------------------------------------------------
/quant/signals/signalbase.py:
--------------------------------------------------------------------------------
1 | from abc import abstractmethod
2 | # 信号基类
3 | class SignalBase():
4 |
5 | def __init__(self):
6 | pass
7 |
8 | # 匹配买入信息
9 | @abstractmethod
10 | def fit_buy(self, stock_indicator):
11 | ret = False
12 | return ret
13 |
14 | # 匹配卖出信息
15 | @abstractmethod
16 | def fit_sell(self, stock_indicator):
17 | ret = False
18 | return ret
19 |
--------------------------------------------------------------------------------
/quant/startengine.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # from __future__ import print_function, absolute_import
3 | # -*- encoding: utf-8 -*-
4 | ###############################################################################
5 | # @author : vamed
6 | # @time :2021-02-02
7 | # @function:入口
8 | # Copyright (C) 2021-2023
9 | ###############################################################################
10 | #
11 | import sys,os
12 | base_dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取pathtest的绝对路径
13 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
14 |
15 | import datetime
16 | from quant.logger import Logger
17 | from quant.util.configutil import get_config
18 | from gm.api import *
19 | from quant.quantengine import QuantEngine
20 | from quant.model.backtestrecord import BacktestRecord
21 | from quant.util import globalvars
22 |
23 | # 回测帐号ID
24 |
25 | # 策略中必须有init方法
26 | def init(context):
27 | subscribe(symbols='SHSE.000001', frequency='60s', count=1)
28 | # subscribe(symbols='SHSE.600030,SHSE.600050,SHSE.600007', frequency='60s', count=200)
29 | # 设置回测模式帐号ID
30 | print(globalvars.hand_trade)
31 | if (context.mode==MODE_BACKTEST):
32 | context.backtest_id=globalvars.backtest_id
33 | context.backtest_account_id=''
34 | if globalvars.backtest_account_id!='':
35 | context.backtest_account_id = globalvars.backtest_account_id
36 |
37 | context.quantengine=QuantEngine(context)
38 |
39 | def on_bar(context, bars):
40 | # print(bars)
41 | context.quantengine.on_bar(context,bars)
42 |
43 | def on_tick(context, tick):
44 | # print(tick)
45 | # logdebug(tick)
46 | context.quantengine.on_tick(context,tick)
47 |
48 | # 响应委托状态更新事情,下单后及委托状态更新时被触发。
49 | def on_order_status(context, order):
50 | context.quantengine.on_order_status(context, order)
51 |
52 | # 响应委托被执行事件,委托成交或者撤单拒绝后被触发。
53 | def on_execution_report(context, execrpt):
54 | context.quantengine.on_execution_report(context, execrpt)
55 |
56 | # 响应交易账户状态更新事件,交易账户状态变化时被触发。
57 | def on_account_status(context, account):
58 | # Logger().logdebug(account)
59 | pass
60 |
61 | #当发生异常情况,比如断网时、终端服务崩溃是会触发
62 | def on_error(context, code, info):
63 | # print(context.now)
64 | # Logger().loginfo(code)
65 | Logger().logerror(info)
66 |
67 | # 在回测模式下,回测结束后会触发该事件,并返回回测得到的绩效指标对象
68 | def on_backtest_finished(context, indicator):
69 | BacktestRecord().update_backtest_record(account_id=context.backtest_account_id,date=context.now.strftime("%Y-%m-%d %H:%M:%S"), status="finished")
70 | Logger().loginfo(indicator)
71 |
72 | def run_engine(mode):
73 | # globalvars.hand_trade=ht
74 | if mode=='1':
75 | strategy_id = get_config('ACCOUNT', 'strategy_id')
76 | run_mode=MODE_LIVE
77 | Logger().loginfo("mode运行模式, 实时模式:MODE_LIVE:1")
78 | else:
79 | strategy_id = get_config('ACCOUNT', 'backtest_strategy_id')
80 | run_mode = MODE_BACKTEST
81 | Logger().loginfo("mode运行模式, 回测模式:MODE_BACKTEST:2")
82 |
83 | # backtest_start_time = '2022-03-03 08:00:00'
84 | #backtest_start_time = '2022-05-27 08:00:00'
85 | #backtest_end_time = '2023-02-27 16:00:00'
86 | # 掘金只提供最近一个月的免费数据,默认回测时间为最近20天
87 | current_date = datetime.datetime.now().date()
88 | backtest_start_time = (current_date + datetime.timedelta(days=-20)).strftime("%Y-%m-%d %H:%M:%S")
89 | backtest_end_time = current_date.strftime("%Y-%m-%d %H:%M:%S")
90 |
91 | if run_mode==MODE_BACKTEST and mode=='2':
92 | br=BacktestRecord().get_suspend_record()
93 | if br== None:
94 | # 回测模式帐号ID
95 | globalvars.backtest_account_id = "{}{}".format("back_test", globalvars.backtest_id)
96 | br=BacktestRecord()
97 | br.account_id=globalvars.backtest_account_id
98 | br.start=backtest_start_time
99 | br.current = backtest_start_time
100 | br.end = backtest_end_time
101 | br.date=datetime.datetime.now().strftime("%Y-%m-%d")
102 | br.status="start"
103 | br.symbols=""
104 | br.insert()
105 | else:
106 | globalvars.backtest_account_id=br.account_id
107 | backtest_start_time=br.current
108 | # pass
109 | try:
110 | run(strategy_id=strategy_id,
111 | filename='startengine.py',
112 | mode=run_mode,
113 | token=get_config('TOKEN','gmtoken'),
114 | backtest_start_time=backtest_start_time,
115 | backtest_end_time=backtest_end_time,
116 | backtest_adjust=ADJUST_PREV,
117 | backtest_initial_cash=1000000,
118 | backtest_commission_ratio=0.0001,
119 | backtest_slippage_ratio=0.0001)
120 | except Exception as e:
121 | Logger().loginfo(e)
122 | Logger().logerror(e)
123 |
124 |
125 | # if __name__ == '__main__':
126 | #
127 | # # start_up(["--mode=3"])
128 | # # start_up(["--mode=2","--ht=1"])
129 | # # run_engine(["--mode=1"])
130 | # # start_up()
131 | # pass
132 |
133 |
--------------------------------------------------------------------------------
/quant/startengine_bt.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # from __future__ import print_function, absolute_import
3 | # -*- encoding: utf-8 -*-
4 | ###############################################################################
5 | # @author : vamed
6 | # @time :2021-02-02
7 | # @function:回测入口,支持断点回测
8 | # Copyright (C) 2021-2023
9 | ###############################################################################
10 | #
11 | import multiprocessing
12 | import sys,os
13 | base_dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取pathtest的绝对路径
14 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
15 |
16 | import time
17 | import argparse
18 | import ctypes
19 | import datetime
20 | import inspect
21 | from quant.logger import Logger
22 | from quant.util.putil import check_run_gm
23 | from gm.api import *
24 | from quant.startengine import run_engine
25 |
26 | # 强制杀死线程
27 | def _async_raise(tid, exctype):
28 | """raises the exception, performs cleanup if needed"""
29 | tid = ctypes.c_long(tid)
30 | if not inspect.isclass(exctype):
31 | exctype = type(exctype)
32 | res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
33 | if res == 0:
34 | raise ValueError("invalid thread id")
35 | elif res != 1:
36 | # """if it returns a number greater than one, you're in trouble,
37 | # and you should call it again with exc=NULL to revert the effect"""
38 | ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
39 | raise SystemError("PyThreadState_SetAsyncExc failed")
40 |
41 | # 停止线程
42 | def stop_thread(thread):
43 | if thread.ident:
44 | _async_raise(thread.ident, SystemExit)
45 |
46 | # 解析参数
47 | def parse_args(pargs=None):
48 | parser = argparse.ArgumentParser(
49 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
50 | description=(
51 | 'Sample Skeleton'
52 | )
53 | )
54 |
55 | parser.add_argument('--mode', default='2',
56 | required=False, help='mode运行模式, 实时模式:MODE_LIVE:1回测模式:MODE_BACKTEST:2')
57 |
58 |
59 | return parser.parse_args(pargs)
60 |
61 | # 启动交易程序
62 | def start_up(args=None):
63 | args = parse_args(args)
64 | Logger().logerror(args.mode)
65 | # run_strategy(args.mode)
66 | if args.mode=='2':
67 | check_run_gm()
68 | Logger().loginfo("-------------start--------------------")
69 | Logger().loginfo("-------------正在检测掘金客户端是否打开--------------------")
70 | time.sleep(30)
71 | stoped="stoped"
72 | running="running"
73 | status=stoped
74 | show_tip=True
75 | process_run_engine = multiprocessing.Process(target=run_engine,args=(args.mode,))
76 | while True:
77 | now=datetime.datetime.now()
78 | # 8:50和20:50掘金服务器暂停
79 | if (now.hour<8) or ((now.hour>8) and (now.hour<20)) or (now.hour>20):
80 | if status!=running:
81 | Logger().loginfo(datetime.datetime.now())
82 | process_run_engine = multiprocessing.Process(target=run_engine,args=(args.mode,))
83 | Logger().loginfo("-------------策略正在初始化-------------")
84 | process_run_engine.start()
85 | status=running
86 | Logger().loginfo("-------------策略正在运行-------------")
87 | elif (now.hour==8) or ((now.hour==20)):
88 | if show_tip:
89 | Logger().loginfo("-------------当前时间服务有问题,回测程序停止-----------------")
90 | show_tip=False
91 | # if status!=stoped:
92 | else:
93 | # stop()
94 | stop_thread(process_run_engine)
95 | status=stoped
96 | Logger().loginfo("-------------策略已停止-------------")
97 | for i in range(10):
98 | print(i)
99 | time.sleep(1)
100 | sys.exit()
101 | time.sleep(1)
102 |
103 | elif args.mode == '4':
104 | run_engine('2')
105 | else:
106 | process_run_engine = multiprocessing.Process(target=run_engine,args=(args.mode,))
107 | check_run_gm()
108 | Logger().loginfo("-------------start--------------------")
109 | Logger().loginfo("-------------正在检测掘金客户端是否打开--------------------")
110 | # time.sleep(30)
111 | stoped="stoped"
112 | running="running"
113 | status=stoped
114 | show_tip=True
115 |
116 | while True:
117 | now=datetime.datetime.now()
118 | if (now.hour>=9) and (now.hour<16):
119 | if status!=running:
120 | Logger().loginfo(datetime.datetime.now())
121 | process_run_engine = multiprocessing.Process(target=run_engine,args=(args.mode,))
122 | Logger().loginfo("-------------策略正在初始化-------------")
123 | process_run_engine.start()
124 | status=running
125 | Logger().loginfo("-------------策略正在运行-------------")
126 | else:
127 | if show_tip:
128 | Logger().loginfo("-------------当前非交易时间,交易程序停止-----------------")
129 | show_tip=False
130 | if status!=stoped:
131 | # stop()
132 | stop_thread(process_run_engine)
133 | status=stoped
134 | Logger().loginfo("-------------策略已停止-------------")
135 | for i in range(10):
136 | print(i)
137 | time.sleep(1)
138 | sys.exit()
139 | time.sleep(1)
140 |
141 | if __name__ == '__main__':
142 |
143 | start_up(["--mode=4"])
144 | # start_up(["--mode=2"])
145 | # start_up(["--mode=1"])
146 | # start_up()
147 |
148 |
--------------------------------------------------------------------------------
/quant/startengine_live.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # from __future__ import print_function, absolute_import
3 | # -*- encoding: utf-8 -*-
4 | ###############################################################################
5 | # @author : vamed
6 | # @time :2021-02-02
7 | # @function:实盘入口
8 | # Copyright (C) 2021-2023
9 | ###############################################################################
10 | #
11 | import multiprocessing
12 | import sys,os
13 | base_dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取pathtest的绝对路径
14 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
15 |
16 | import time
17 | import argparse
18 | import ctypes
19 | import datetime
20 | import inspect
21 | from quant.logger import Logger
22 | from quant.util.putil import check_run_gm
23 | from gm.api import *
24 | from quant.startengine import run_engine
25 |
26 | # 强制杀死线程
27 | def _async_raise(tid, exctype):
28 | """raises the exception, performs cleanup if needed"""
29 | tid = ctypes.c_long(tid)
30 | if not inspect.isclass(exctype):
31 | exctype = type(exctype)
32 | res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
33 | if res == 0:
34 | raise ValueError("invalid thread id")
35 | elif res != 1:
36 | # """if it returns a number greater than one, you're in trouble,
37 | # and you should call it again with exc=NULL to revert the effect"""
38 | ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
39 | raise SystemError("PyThreadState_SetAsyncExc failed")
40 |
41 | # 停止线程
42 | def stop_thread(thread):
43 | if thread.ident:
44 | _async_raise(thread.ident, SystemExit)
45 |
46 | # 解析参数
47 | def parse_args(pargs=None):
48 | parser = argparse.ArgumentParser(
49 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
50 | description=(
51 | 'Sample Skeleton'
52 | )
53 | )
54 |
55 | parser.add_argument('--mode', default='2',
56 | required=False, help='mode运行模式, 实时模式:MODE_LIVE:1回测模式:MODE_BACKTEST:2')
57 |
58 |
59 | return parser.parse_args(pargs)
60 |
61 | # 启动交易程序
62 | def start_up(args=None):
63 | args = parse_args(args)
64 | Logger().logerror(args.mode)
65 | # run_strategy(args.mode)
66 | if args.mode=='2':
67 | check_run_gm()
68 | Logger().loginfo("-------------start--------------------")
69 | Logger().loginfo("-------------正在检测掘金客户端是否打开--------------------")
70 | time.sleep(30)
71 | stoped="stoped"
72 | running="running"
73 | status=stoped
74 | show_tip=True
75 | process_run_engine = multiprocessing.Process(target=run_engine,args=(args.mode,))
76 | while True:
77 | now=datetime.datetime.now()
78 | # 8:50和20:50掘金服务器暂停
79 | if (now.hour<8) or ((now.hour>8) and (now.hour<20)) or (now.hour>20):
80 | if status!=running:
81 | Logger().loginfo(datetime.datetime.now())
82 | process_run_engine = multiprocessing.Process(target=run_engine,args=(args.mode,))
83 | Logger().loginfo("-------------策略正在初始化-------------")
84 | process_run_engine.start()
85 | status=running
86 | Logger().loginfo("-------------策略正在运行-------------")
87 | elif (now.hour==8) or ((now.hour==20)):
88 | if show_tip:
89 | Logger().loginfo("-------------当前时间服务有问题,回测程序停止-----------------")
90 | show_tip=False
91 | # if status!=stoped:
92 | else:
93 | # stop()
94 | stop_thread(process_run_engine)
95 | status=stoped
96 | Logger().loginfo("-------------策略已停止-------------")
97 | for i in range(10):
98 | print(i)
99 | time.sleep(1)
100 | sys.exit()
101 | time.sleep(1)
102 |
103 | elif args.mode == '3':
104 | run_engine('3')
105 |
106 | else:
107 | process_run_engine = multiprocessing.Process(target=run_engine,args=(args.mode,))
108 | check_run_gm()
109 | Logger().loginfo("-------------start--------------------")
110 | Logger().loginfo("-------------正在检测掘金客户端是否打开--------------------")
111 | time.sleep(30)
112 | stoped="stoped"
113 | running="running"
114 | status=stoped
115 | show_tip=True
116 |
117 | while True:
118 | now=datetime.datetime.now()
119 | if (now.hour>=9) and (now.hour<16):
120 | if status!=running:
121 | Logger().loginfo(datetime.datetime.now())
122 | process_run_engine = multiprocessing.Process(target=run_engine,args=(args.mode,))
123 | Logger().loginfo("-------------策略正在初始化-------------")
124 | process_run_engine.start()
125 | status=running
126 | Logger().loginfo("-------------策略正在运行-------------")
127 | else:
128 | if show_tip:
129 | Logger().loginfo("-------------当前非交易时间,交易程序停止-----------------")
130 | show_tip=False
131 | if status!=stoped:
132 | # stop()
133 | stop_thread(process_run_engine)
134 | status=stoped
135 | Logger().loginfo("-------------策略已停止-------------")
136 | for i in range(10):
137 | print(i)
138 | time.sleep(1)
139 | sys.exit()
140 | time.sleep(1)
141 |
142 | if __name__ == '__main__':
143 |
144 | # start_up(["--mode=3"])
145 | # start_up(["--mode=2"])
146 | start_up(["--mode=1"])
147 | # start_up()
148 |
149 |
--------------------------------------------------------------------------------
/quant/strategyengine.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function:策略调用引擎
6 | # Copyright (C) 2021-2023
7 | ###############################################################################
8 | #
9 | from __future__ import print_function, absolute_import
10 | import sys,os
11 | base_dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取pathtest的绝对路径
12 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
13 | from quant.enums.strategytype import StrategyType
14 |
15 | import importlib
16 |
17 | # 策略引擎
18 | class StrategyEngine():
19 |
20 | def __init__(self,context,strategy_list,broker,send_order_event_handler):
21 | # strategy_names= enumutil.getItems(Strategys)
22 | self.strategy_list =strategy_list
23 | self.context=context
24 | # strategy_names = ['MeanRevertingStrategy', 'RiskStrategy', 'TrendFollowingStrategy', 'DayTradingStrategy']
25 | # strategy_names = ['RiskStrategy']
26 | self.strategys=dict()
27 | for s in self.strategy_list:
28 | s=s.strip()
29 | if s!='':
30 | self.strategys[s]=self.init_strategy(s,context,broker,send_order_event_handler)
31 |
32 |
33 | def init_strategy(self,strategy_name,context,broker,send_order_event_handler):
34 | imp_class = getattr(importlib.import_module("quant.strategys.{}".format(strategy_name.lower())),
35 | strategy_name)
36 | cls_obj = imp_class(context,broker,send_order_event_handler)
37 | return cls_obj
38 |
39 | def run(self,context,stock_indicator,bar=None,tick=None):
40 | if stock_indicator.strategy!="":
41 | # 运行股票选定策略
42 | if stock_indicator.strategy in self.strategys.keys():
43 | if bar and self.strategys[stock_indicator.strategy].drive_type == StrategyType.bar.value:
44 | self.strategys[stock_indicator.strategy].run(context, bar, stock_indicator)
45 | elif tick and (self.strategys[stock_indicator.strategy].drive_type==StrategyType.tick.value):
46 | self.strategys[stock_indicator.strategy].run(context, tick, stock_indicator)
47 |
48 | # 运行全域策略
49 | for key in self.strategys:
50 | if key!=stock_indicator.strategy:
51 | if bar and (self.strategys[key].drive_type == StrategyType.bar.value):
52 | self.strategys[key].run(context, bar, stock_indicator)
53 | elif tick and (self.strategys[key].drive_type == StrategyType.tick.value):
54 | self.strategys[key].run(context, tick, stock_indicator)
55 | else:
56 | for key in self.strategys:
57 | if bar and (self.strategys[key].drive_type==StrategyType.bar.value):
58 | self.strategys[key].run(context,bar,stock_indicator)
59 | elif tick and (self.strategys[key].drive_type==StrategyType.tick.value):
60 | self.strategys[key].run(context, tick, stock_indicator)
61 |
62 | # 运行定时策略
63 | def run_schedule(self):
64 | for key in self.strategys:
65 | if self.strategys[key].drive_type == StrategyType.schedule.value:
66 | self.strategys[key].run_schedule()
67 |
68 | def update_order_symbols(self):
69 | for key in self.strategys:
70 | self.strategys[key].update_order_symbols()
71 |
72 | if __name__=="__main__":
73 |
74 | # df = StockPool().get_picktime()
75 |
76 | # stock_indicator = get_obj_from_df(df, StockIndicator)
77 | # StrategyEngine().run('','',stock_indicator)
78 | pass
79 |
--------------------------------------------------------------------------------
/quant/strategys/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/strategys/__init__.py
--------------------------------------------------------------------------------
/quant/strategys/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/strategys/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/strategys/__pycache__/gridstrategy.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/strategys/__pycache__/gridstrategy.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/strategys/__pycache__/meanrevertingstrategy.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/strategys/__pycache__/meanrevertingstrategy.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/strategys/__pycache__/strategybase.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/strategys/__pycache__/strategybase.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/strategys/gridstrategy.py:
--------------------------------------------------------------------------------
1 | from quant.enums.scopes import Scopes
2 | from quant.enums.strategytype import StrategyType
3 | from quant.logger import Logger
4 | from quant.model.position import Position
5 | from quant.strategys.strategybase import StrategyBase
6 | from quant.util import stockutil
7 |
8 | # 网格交易策略
9 | class GridStrategy(StrategyBase):
10 |
11 | def __init__(self,context,broker,send_order_event_handler=None):
12 | # 初始化父类
13 | super().__init__(context,broker,send_order_event_handler)
14 | # 策略名称
15 | self.strategy_name='GridStrategy'
16 | # 行情推送数据类型bar,tick
17 | self.drive_type = StrategyType.bar.value
18 | self.scope = Scopes.universal
19 | # 获取当天已交易订单信息
20 | self.get_order_symbols()
21 | #日内交易已卖出股票代码数组
22 | self.daytrading_sell_symbols=self.strategy_sell_symbols
23 | # 日内交易已买入股票代码数组
24 | self.daytrading_buy_symbols=self.strategy_buy_symbols
25 | # 偏离分时均价阀值
26 | self.threshold=0.02
27 | # 日内交易截止时间
28 | self.daytradeing_time="14:55"
29 |
30 | # 从最后买单中找出当前股票的订单
31 | def get_last_order(self, context,symbol):
32 | return context.last_orders[context.last_orders['symbol']==symbol]
33 |
34 | # context.last_orders.at[context.last_orders[context.last_orders['symbol'] == symbol].index[0], 'price'
35 | # 执行买入操作
36 | def do_buy(self, context, tick, stock_indicator):
37 | # ma_price=stock_indicator.map
38 | symbol=tick['symbol']
39 | price=tick['close']
40 | factor = ""
41 | time_now = context.now.strftime('%H:%M')
42 |
43 | df=self.get_last_order(context,symbol)
44 | if len(df)==0:
45 | return
46 | last_price=df.at[df.index[0], 'price']
47 | # 如果在日内交易时间设置前,则进行日内交易
48 | if (time_now =m_kdj[4])):
52 | if (last_price-price)/last_price > self.threshold :
53 | factor = "fit_buy_gridstrategy"
54 |
55 | # 如果在日内交易时间设置后,如果有卖出交易,格价在分时均线之后,买回
56 | else:
57 | # if (symbol in context.daytrading_sell_symbols) and (symbol not in context.daytrading_buy_symbols):
58 | # factor="DayTradingStrategy_buy"
59 | pass
60 | if factor!="":
61 |
62 | if symbol not in self.daytrading_buy_symbols:
63 | print("------------------buy-------------------")
64 | self.daytrading_buy_symbols.append(symbol)
65 | self.trader.buy(symbol= symbol,price= price,factor= factor,strategy=self.strategy_name)
66 |
67 | # 执行卖出操作
68 | def do_sell(self,context,tick,stock_indicator):
69 | # ma_price=stock_indicator.map
70 | symbol = tick['symbol']
71 | price = tick['close']
72 | if (symbol not in context.holding_symbols):
73 | return
74 |
75 |
76 | pos=Position().get_cache_position(context.positions,symbol)
77 | # print(obj_to_series(pos))
78 | # 如果没有持仓,则终止
79 | if pos.can_use_volume==0:
80 | return
81 |
82 | volume=stockutil.value_to_volume(context.setting.per_buy_amount,price)
83 |
84 | if volume>pos.can_use_volume:
85 | volume=pos.can_use_volume
86 | factor=""
87 |
88 | # 如果在日内交易时间设置前,则进行日内交易
89 | time_now = context.now.strftime('%H:%M')
90 | df=self.get_last_order(context,symbol)
91 | if len(df)==0:
92 | return
93 | last_price=df.at[df.index[0], 'price']
94 | if (time_now self.threshold and (symbol not in self.daytrading_sell_symbols):
96 | factor = "GridStrategy_sell"
97 | # 如果在日内交易时间设置后,如果有买入交易,格价在分时均线之上,卖出
98 | # elif (symbol in self.daytrading_buy_symbols) and (symbol not in self.daytrading_sell_symbols):
99 | # order=self.get_symbol_orders(symbol,1)
100 | # if order[0].price0):
49 | price = bar['close']
50 | fit_signal = self.signals.fit_sell(stock_indicator)
51 | if fit_signal[0]:
52 | factor = fit_signal[1]
53 | indicator = fit_signal[2]
54 | if symbol not in StrategyBase.sell_symbols:
55 | print("------------------sell-------------------")
56 | StrategyBase.sell_symbols.append(symbol)
57 | self.trader.sell_out(symbol,price,factor=factor,strategy=self.strategy_name,indicator=indicator)
58 |
59 | def run(self,context,bar,stock_indicator):
60 | Logger().logdebug('正在运行策略:{}'.format(self.strategy_name))
61 | symbol=bar['symbol']
62 | # self.fit_money_flow(stock_indicator)
63 | if symbol in context.holding_symbols:
64 | self.do_sell(context,bar,stock_indicator)
65 |
66 | if symbol in context.buy_symbols:
67 | self.do_buy(context, bar, stock_indicator)
68 |
69 | #更新当天订单股票代码
70 | def update_order_symbols(self):
71 | # 获取当天已交易订单信息
72 | self.get_order_symbols()
73 | # # 日内交易已卖出股票代码数组
74 | StrategyBase.sell_symbols= []
75 | # # 日内交易已买入股票代码数组
76 | StrategyBase.buy_symbols= []
77 | # 日内交易已卖出股票代码数组
78 | self.meanreverting_sell_symbols = []
79 | # 日内交易已买入股票代码数组
80 | self.meanreverting_buy_symbols = []
--------------------------------------------------------------------------------
/quant/strategys/strategybase.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from quant.trader import Trader
3 |
4 | # 策略基类
5 | class StrategyBase():
6 | # 日内交易已卖出股票代码数组
7 | sell_symbols = []
8 | # 日内交易已买入股票代码数组
9 | buy_symbols = []
10 |
11 | def __init__(self, context, broker, send_order_event_handler=None):
12 | self.context = context
13 | # 经纪商
14 | self.broker = broker
15 | # 订单回调函数
16 | self.broker.send_order_event_handler = send_order_event_handler
17 | # 订单交易对象
18 | self.trader = Trader(context, broker)
19 | # 策略名称
20 | self.strategy_name = ''
21 |
22 | # 策略交易已卖出股票代码数组
23 | self.strategy_sell_symbols = []
24 | # 策略交易已买入股票代码数组
25 | self.strategy_buy_symbols = []
26 | # self.get_order_symbols()
27 | # 上次检查资金流时间
28 | self.money_flow_time = datetime.datetime.strptime(
29 | "{} {}".format(datetime.datetime.now().strftime("%Y-%m-%d"), "09:30:00"), "%Y-%m-%d %H:%M:%S")
30 |
31 | # # 获取缓存持仓信息
32 | def get_cache_position(self, positions, symbol):
33 | ret = None
34 | for pos in positions:
35 | if pos.symbol == symbol:
36 | ret = pos
37 | break
38 | return ret
39 |
40 | # 获取订单股票代码
41 | def get_order_symbols(self):
42 | for o in self.context.orders:
43 | # 所有交易代码
44 | if o.status == 5:
45 | continue
46 | if o.side == 1:
47 | if o.symbol not in self.buy_symbols:
48 | self.buy_symbols.append(o.symbol)
49 | else:
50 | if o.symbol not in self.sell_symbols:
51 | self.sell_symbols.append(o.symbol)
52 |
53 | # 略策交易代码
54 | if o.strategy == self.strategy_name:
55 | if o.side == 1:
56 | if o.symbol not in self.strategy_buy_symbols:
57 | self.strategy_buy_symbols.append(o.symbol)
58 | else:
59 | if o.symbol not in self.strategy_sell_symbols:
60 | self.strategy_sell_symbols.append(o.symbol)
61 |
62 |
63 | # # 获取股票代码的订单
64 | def get_symbol_orders(self,symbol,side):
65 | orders=[]
66 | for o in self.context.orders:
67 | if (o.symbol==symbol) and (o.side==side):
68 | orders.append(o)
69 |
70 | return orders
--------------------------------------------------------------------------------
/quant/trader.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | ###############################################################################
3 | # @author : vamed
4 | # @time :2021-02-02
5 | # @function: 交易类
6 | # Copyright (C) 2015-2020
7 | ###############################################################################
8 |
9 | from gm.api import *
10 | from quant.account import Account
11 | from quant.logger import Logger
12 | from quant.util import stockutil
13 |
14 | # 交易类
15 | class Trader():
16 | def __init__(self,context,broker):
17 | self.trade_records = dict()
18 | self.context=context
19 | self.account=Account(self.context)
20 | self.broker=broker
21 |
22 | def buy(self,symbol,price,value=None,factor='',strategy= 'strategy',indicator='',checkPos=True):
23 | ret=None
24 | if self.broker.hasPosition() == False:
25 | Logger().loginfo('已经超过设置最大仓位')
26 | return
27 | if (self.broker.isUnderMaxStockNumber() == False) and checkPos:
28 | Logger().loginfo('已经超过设置最大持仓股数据')
29 | return
30 | if (self.broker.isUnderMaxStockPosition(symbol) == False):
31 | Logger().loginfo('已经超过设置单股最大持仓金额')
32 | return
33 | if value==None:
34 | value=self.account.PER_BUY_AMOUNT
35 | if self.has_unfinished_orders(symbol)==False:
36 | self.trade_records[symbol] = factor
37 | volume=stockutil.value_to_volume(value,price)
38 | ret=self.broker.buy(symbol=symbol, volume=volume, price=price,factor=factor,indicator=indicator,strategy= strategy)
39 | Logger().loginfo('----------buy--------')
40 | return ret
41 |
42 |
43 | def order_target_percent(self,symbol,percent,factor='',strategy= 'strategy',indicator='',checkPos=True):
44 | ret = self.broker.order_target_percent(symbol=symbol, percent=percent, factor=factor, indicator=indicator,
45 | strategy=strategy)
46 | Logger().loginfo(ret)
47 | return ret
48 | # 清仓股票
49 | def sell_out(self,symbol,price,factor='',indicator='',strategy= 'strategy'):
50 | self.broker.sell_out(symbol,price,factor=factor,indicator=indicator,strategy= strategy)
51 | self.trade_records[symbol] =factor
52 |
53 | #卖出股票
54 | def sell(self,symbol,price,volume,factor='',indicator='',strategy= 'strategy'):
55 | self.broker.sell(symbol,volume,price,factor=factor,indicator=indicator,strategy= strategy)
56 | self.trade_records[symbol] =factor
57 |
58 | # 清仓所有仓位
59 | def close_all(self):
60 | self.broker.close_all()
61 | # 止损
62 | def stop_lost(self,symbol,cost,price):
63 | if cost*0.9>price:
64 | self.sell_target_volume(symbol, price, 0, "stop_lost")
65 | print("stop_lost")
66 |
67 | def has_unfinished_orders(self,code):
68 | ret=False
69 | orders = get_unfinished_orders()
70 | for i,o in enumerate(orders):
71 | if o.symbol==code:
72 | ret=True
73 | break
74 | return ret
75 |
76 | # 订单取消
77 | def cancel_order(self,code,side):
78 | self.broker.cancel_order(code,side)
79 |
--------------------------------------------------------------------------------
/quant/updateposition.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | # -*- encoding: utf-8 -*-
3 | ###############################################################################
4 | # @author : vamed
5 | # @time :2021-02-02
6 | # @function:盘后更新持仓数据到本地数据库
7 | # Copyright (C) 2021-2023
8 | ###############################################################################
9 | #
10 | from __future__ import print_function, absolute_import
11 |
12 | import datetime
13 | import sys,os
14 | import time
15 |
16 | base_dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取pathtest的绝对路径
17 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
18 |
19 | from quant.stockdata import StockData
20 | from quant.brokers.myquantbroker import MyquantBroker
21 | from quant.enums.brokers import Brokers
22 | from quant.model.setting import Setting
23 |
24 | from quant.util.configutil import get_config
25 |
26 | from gm.api import *
27 |
28 | # 盘后更新持仓数据到本地数据库
29 | def init(context):
30 | # 订阅浦发银行, bar频率为一天和一分钟
31 | # 订阅订阅多个频率的数据,可多次调用subscribe
32 | # self.context = context
33 | # 获取当前setting帐号id
34 | if len(context.accounts) == 0:
35 | print("当然设置的帐号交易配置文件错误:略策文件没有挂接交易帐号")
36 | stop()
37 |
38 | # 获取交易帐号设置参数
39 | setting_account_id = Setting.get_setting_accountid(context)
40 | setting = Setting().getdata(setting_account_id)
41 | if setting == None:
42 | print(
43 | "当然设置的帐号:{},交易配置文件错误:数据库setting表没有配置交易设置信息".format(context.setting_account_id))
44 | stop()
45 | else:
46 | context.setting = setting
47 |
48 | # context.backtest_id="{}{}".format(context.setting.account_id, context.backtest_id)
49 | # # 设置当然帐号对象
50 | # self.account = Account(self.context)
51 | #
52 | # # 显示输出交易帐号设置参数
53 | # self.account.display_run_param()
54 |
55 | # 设定经纪商
56 | # broker = get_borker(context=context, broker_type=setting.broker)
57 | # # 打印显示当前帐号资金信息
58 | # broker.display_account_asset_info()
59 | #
60 | # # 获取持仓信息并打印输出
61 | # positions = broker.getPositions(display=True)
62 | # print(positions)
63 | broker=get_borker(context,setting.broker)
64 | StockData.update_position(broker)
65 | # GmApi().update_execution_reports()
66 | # pos= Account(context).getPositions()
67 | # print(pos)
68 | print("-------------end update_position--------------------")
69 | print(datetime.datetime.now())
70 | time.sleep(30)
71 | stop()
72 | print("-------------stop--------------------")
73 | print(datetime.datetime.now())
74 |
75 | #获取经纪商
76 | def get_borker(context,broker_type):
77 | if broker_type==Brokers.xtquant.value:
78 | # broker=XtBroker(context=context)
79 | pass
80 | else:
81 | broker = MyquantBroker(context=context)
82 | return broker
83 |
84 |
85 | def run_start():
86 | # check_run_gm()
87 | print("-------------start--------------------")
88 | print(datetime.datetime.now())
89 | # time.sleep(30)
90 | print(datetime.datetime.now())
91 | run_context()
92 | print("-------------end update_position--------------------")
93 |
94 | def run_context():
95 | run(
96 | strategy_id=get_config('ACCOUNT','strategy_id'),
97 | # filename="{}\\quant\\{}".format(base_dir,'updateposition.py'),
98 | filename='updateposition.py',
99 | mode=MODE_LIVE,
100 | token=get_config('TOKEN','gmtoken'),
101 | # backtest_start_time='2020-11-01 08:00:00',
102 | # backtest_end_time='2020-11-10 16:00:00',
103 | # backtest_adjust=ADJUST_PREV,
104 | # backtest_initial_cash=10000000,
105 | # backtest_commission_ratio=0.0001,
106 | # backtest_slippage_ratio=0.0001
107 | )
108 |
109 | if __name__ == '__main__':
110 | run_start()
111 |
112 |
--------------------------------------------------------------------------------
/quant/util/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__init__.py
--------------------------------------------------------------------------------
/quant/util/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/util/__pycache__/configutil.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__pycache__/configutil.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/util/__pycache__/datetimeutil.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__pycache__/datetimeutil.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/util/__pycache__/fileutil.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__pycache__/fileutil.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/util/__pycache__/globalvars.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__pycache__/globalvars.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/util/__pycache__/numberutil.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__pycache__/numberutil.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/util/__pycache__/pdutil.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__pycache__/pdutil.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/util/__pycache__/putil.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__pycache__/putil.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/util/__pycache__/stockutil.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__pycache__/stockutil.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/util/__pycache__/stringutil.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vamed/starquant/90390f1496821eeedb42ace62582c31fbe158b6d/quant/util/__pycache__/stringutil.cpython-38.pyc
--------------------------------------------------------------------------------
/quant/util/configutil.py:
--------------------------------------------------------------------------------
1 | import configparser
2 | import sys,os
3 | base_dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取pathtest的绝对路径
4 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
5 | # loginfo(base_dir)
6 | def get_config(section,key):
7 | config = configparser.ConfigParser()
8 | # loginfo('{}/{}'.format(base_dir,"config.ini"))
9 | config.read('{}/{}'.format(base_dir,"config.ini"))
10 | return config[section][key]
11 |
12 | def set_config(file,section,key,value):
13 | # coding=utf-8
14 | import sys
15 | cf = configparser.ConfigParser()
16 | cf.read(file)
17 | cf.set(section, key, value)
18 | cf.write(open(file, "w"))
19 | pass
20 | if __name__=='__main__':
21 | # str=get_config('TOKEN','gmtoken')
22 | set_config("E:/temp/StockwayStock.ini", "\SelfSelect", "择时", "0.000333,0.002248,0.000905,1.600030,1.600050,")
23 | # loginfo(str)
--------------------------------------------------------------------------------
/quant/util/datetimeutil.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | base_dir=os.path.dirname(os.path.dirname(__file__))#获取pathtest的绝对路径
5 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
6 | # 时间比较 格式hh:mm
7 | import datetime
8 |
9 | # 日期大小比较
10 | def time_compare(time1,timestr):
11 | ret=False
12 | t1=float(time_compare(get_time_string(time1).replace(':','.')))
13 | t2 = float(timestr.replace(':', '.'))
14 | if t1>=t2 :
15 | ret=True
16 | return ret
17 | # 格式化日期输出时间部分'%H:%M'
18 | def get_time_string(date):
19 | return date.strftime('%H:%M')
20 |
21 | # 计算交易时间过去的分钟数
22 | def get_pass_minutes(date):
23 | hour=date.hour
24 | minute=date.minute
25 | ret=0
26 | if hour<9:
27 | ret=0
28 | elif hour>=15:
29 | ret=240
30 | elif hour >=9 and hour<12:
31 | ret = (hour-9)*60+minute-30
32 | elif hour == 12:
33 | ret = 120
34 | elif hour >12 and hour<15:
35 | ret = (hour-11)*60+minute
36 | return ret-1
37 | pass
38 |
39 | def add_minutes(stime,mins):
40 | time= stime.split(':')
41 | hour= int(time[0])
42 | minute=int(time[1])
43 | minute=minute + mins
44 | if minute>=60:
45 | minute=minute%60
46 | hour=hour+1
47 | if hour>=24:
48 | hour=hour%24
49 | return "{}:{}".format(str(hour).rjust(2, '0'),str(minute).rjust(2, '0'))
50 |
51 | def count_differ_days(time_a, time_b):
52 | """
53 | 计算日期相差天数
54 | """
55 | # 因为得到的是UTC时间,所以需要UTC时间+8
56 | time_a = time_a + datetime.timedelta(hours=8)
57 | time_b = time_b + datetime.timedelta(hours=8)
58 |
59 | d1 = datetime.date(time_a.year, time_a.month, time_a.day)
60 | d2 = datetime.date(time_b.year, time_b.month, time_b.day)
61 |
62 | return (d1 - d2).days
63 |
64 | if __name__=='__main__':
65 | print(add_minutes("23:55",5))
--------------------------------------------------------------------------------
/quant/util/enumutil.py:
--------------------------------------------------------------------------------
1 |
2 | def getItems(Enum):
3 | items=['']
4 | for enum in Enum:
5 | items.append(enum.value)
6 | return items
7 |
8 |
9 | if __name__=="__main__":
10 | # print(getItems(Factors))
11 | pass
--------------------------------------------------------------------------------
/quant/util/fileutil.py:
--------------------------------------------------------------------------------
1 | import sys,os
2 | base_dir=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取pathtest的绝对路径
3 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
4 | # loginfo(base_dir)
5 | import configparser
6 | import subprocess
7 |
8 | def get_config(file=None,section=None,key=None):
9 | config = configparser.ConfigParser()
10 | if file==None:
11 | file='{}/{}'.format(base_dir,"config.ini")
12 | try:
13 | config.read(file,encoding=check_charset(file))
14 | except:
15 | config.read(file)
16 | return config[section][key]
17 | def check_charset(file_path):
18 | import chardet
19 | with open(file_path, "rb") as f:
20 | data = f.read(4)
21 | charset = chardet.detect(data)['encoding']
22 | return charset
23 |
24 | def set_config(file,section,key,value):
25 | # coding=utf-8
26 | cf = configparser.ConfigParser()
27 | try:
28 | cf.read(file,encoding=check_charset(file))
29 | except:
30 | cf.read(file)
31 | # cf.read(file)
32 | cf.set(section, key, value)
33 | cf.write(open(file, "w"))
34 | pass
35 |
36 | def runfile(file):
37 | if os.path.exists(file):
38 | rc, out = subprocess.getstatusoutput(file)
39 | # loginfo(rc)
40 | # loginfo('*' * 10)
41 | # loginfo(out)
42 | # pass
43 |
44 | if __name__=='__main__':
45 |
46 | # file=get_config(section='GOLDMINER',key='path')
47 | # runfile(file)
48 |
49 | file=get_config(section='FUTU',key='path')
50 | runfile(file)
51 | # file="E:/temp/StockwayStock.ini"
52 | # loginfo(file)
53 | # str=get_config(file,'\SelfSelect','自选股')
54 | # # set_config("E:/temp/StockwayStock.ini", "\SelfSelect", "择时", "0.000333,0.002248,0.000905,1.600030,1.600050,")
55 | # loginfo(str)
--------------------------------------------------------------------------------
/quant/util/globalvars.py:
--------------------------------------------------------------------------------
1 | # 全局变量
2 | import datetime
3 |
4 | backtest_account_id=""
5 | backtest_id=datetime.datetime.now().strftime("%Y%m%d%H%M%S")
6 | # backtest_id='20230206164646'
7 | hand_trade=1
--------------------------------------------------------------------------------
/quant/util/numberutil.py:
--------------------------------------------------------------------------------
1 | import math
2 | import numpy as np
3 |
4 | def get_ticks(max,n):
5 | pos=0
6 | if max<0:
7 | pos=-1
8 | max=abs(max)
9 | step = max / n
10 | st_n = int(math.log10(step))
11 | if st_n>0:
12 | step = round((step / math.pow(10, st_n)), 0) * math.pow(10, st_n)
13 | else:
14 | step =round(round((step / math.pow(10, st_n)),1) * math.pow(10, st_n),1-st_n)
15 | end=step*n
16 | if (max>end):
17 | n=n+1
18 | end=step*n
19 | if pos==0:
20 | end = end + step
21 | ret=np.arange(0,end,step)
22 | else:
23 | ret=np.arange(-end,0,step)
24 | return ret
25 |
26 |
27 | def get_step(max):
28 | max_m=math.log10(max)
29 | st=max/2
30 | st_n=int(math.log10(st))
31 | st=int(st/math.pow(10, st_n))*10
32 | # st=math.pow(10, zero_m-1)*5
33 |
34 | return st
35 |
36 | # 浮点数转换
37 | def parse_float(number):
38 | ret=number
39 | if number!=None:
40 | if np.isnan(number):
41 | ret=None
42 | return ret
43 |
44 | #取整百
45 | def get_neat(i):
46 | ret=0
47 |
48 | if i<100:
49 | ret=0
50 | else:
51 | p=pow(10,len(str(i))-1)
52 | ret=int(i/p)*p
53 | return ret
54 |
55 | #取整百
56 | def get_n_number(d,i):
57 | ret=d
58 | if d>pow(10,i+1):
59 | log=np.log10(d)
60 | ret=d/pow(10,int(int(log)))*pow(10,i)
61 | return int(ret)
62 |
63 | if __name__=="__main__":
64 | # print(get_neat(239487))
65 | # d=len(search("\.(0*)", "5.00060030").group(1))
66 | # dist = int(math.log10(abs(0.000060030)))
67 | # s= get_ticks(100,2)
68 | # s2 = get_ticks(-0.03, 2)
69 | # s1 = get_ticks(-110,2)
70 | # # s = get_ticks(310, 2)
71 | #
72 | # s3 = get_ticks(0.15,2)
73 | # s4 = get_ticks(0.0006,4)
74 | # s5 = get_ticks(6,3)
75 | # s5 = get_ticks(4,2)
76 | # s5 = get_ticks(9,2)
77 | # s5 = get_ticks(20,2)
78 | # s5 = get_ticks(60,2)
79 |
80 | # p= np.log10(23000)
81 | # print(p)
82 | # print(int(p))
83 | #
84 | # p10=pow(10,int(p))
85 | # print(p10)
86 | p=get_n_number(999999,4)
87 | print(p)
88 | pass
89 | # findzero(0.004)
90 |
--------------------------------------------------------------------------------
/quant/util/pdutil.py:
--------------------------------------------------------------------------------
1 | import sys,os
2 | import pandas as pd
3 | base_dir=os.path.dirname(os.path.dirname(os.path.dirname(__file__)))#获取pathtest的绝对路径
4 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
5 |
6 | from quant.logger import Logger
7 | # 单步插入数据
8 | def to_sql_by_step(df, tablename,engine):
9 | df.reset_index(drop=True,inplace=True)
10 | for i, r in df.iterrows():
11 | row = df.loc[i:i, :]
12 | try:
13 | row.to_sql(tablename, engine, index=False, if_exists='append')
14 | except Exception as e:
15 | # logerror('to_sql_by_step(self,df,tablename):{}'.format(e))
16 | Logger().logerror(e)
17 |
18 |
19 | # 保存数据到本地文件
20 | def to_csv(df,filename):
21 | try:
22 | file="{}/data/{}".format(base_dir,filename)
23 | df.to_csv(file)
24 | except Exception as e:
25 | Logger().logerror('获取北向个股资金流:{}'.format(e))
26 |
27 | # 生成数据文件名
28 | def get_filename(table_name,date,squence=None):
29 | if squence==None:
30 | file="{}-{}.csv".format(table_name,date)
31 | else:
32 | file="{}-{}-{}.csv".format(table_name,squence,date)
33 | return file
34 |
35 | # 添加对象实例到dataframe
36 | def append_object_instance(df,obj):
37 | ls = dir(obj)
38 | vals = []
39 | keys = []
40 | for attr in ls:
41 | if attr[0:2] != '__':
42 | keys.append(attr)
43 | vals.append(getattr(obj, attr))
44 | srs = pd.Series(data=vals, index=keys)
45 | df=df.append(srs, ignore_index=True)
46 | return df
47 |
48 | # 添加对象实例到dataframe
49 | def append_from_dic(df,dict):
50 | df_dict=pd.DataFrame(dict,index=[0])
51 | df=pd.concat([df,df_dict],ignore_index=True)
52 | return df
53 |
54 | # 添加对象实例到dataframe
55 | def obj_to_series(obj):
56 | ls = dir(obj)
57 | vals = []
58 | keys = []
59 | for attr in ls:
60 | if (attr[0:2] != '__') and (attr !='data'):
61 | keys.append(attr)
62 | vals.append(getattr(obj, attr))
63 | series = pd.Series(data=vals, index=keys)
64 | return series
65 | # 获取某列第一个值
66 | def get_df_col_value(df,col_judge,value,column):
67 | ret=0
68 | for i,row in df.iterrows():
69 | if row[col_judge]==value:
70 | ret=row[column]
71 | break
72 | return ret
73 |
74 | # dict转类对象
75 | def dict_to_object(cls,dict):
76 | obj=cls()
77 | obj.__dict__=dict
78 | return obj
79 |
80 | # 获取 df中某行记录并转实类实例
81 | def get_obj_from_df(df,cls):
82 | # df=df.loc[df[col]==val,:]
83 | records=df.to_dict(orient ='records')
84 | obj=dict_to_object(cls,records[0])
85 | return obj
86 |
87 | def list_to_df(v):
88 | if isinstance(v, list):
89 | return pd.DataFrame(data=v)
90 | elif isinstance(v, dict):
91 | return pd.DataFrame(data=[v, ])
92 | else:
93 | return pd.DataFrame(data=[{'value': v}])
94 |
95 | # if __name__=="__main__":
96 | # to_csv("","")
--------------------------------------------------------------------------------
/quant/util/putil.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from threading import Thread
3 | import psutil
4 | import subprocess
5 | import os
6 |
7 | # 运行程序
8 | from quant.util import fileutil
9 |
10 | def asyn(f):
11 | def wrapper(*args, **kwargs):
12 | thr = Thread(target=f, args=args, kwargs=kwargs)
13 | thr.start()
14 |
15 | return wrapper
16 | @asyn
17 | def run(file):
18 | if os.path.exists(file):
19 | rc,out= subprocess.getstatusoutput(file)
20 |
21 | # 检测某程序是否已运行
22 | def check_process(name):
23 | pl = psutil.pids()
24 | ret=0
25 | pname=""
26 | for pid in pl:
27 | if psutil.pid_exists(pid):
28 | try:
29 | pname=psutil.Process(pid).name()
30 | except:
31 | pass
32 | if pname == name:
33 | ret=pid
34 | # print(pid)
35 | break
36 | return ret
37 |
38 | def check_run_gm():
39 | ret=0
40 | file = fileutil.get_config(section='GOLDMINER', key='path')
41 | fname=file.split('/')[-1]
42 | if check_process(fname)==0:
43 | run(file)
44 | else:
45 | ret=1
46 | return ret
47 |
48 |
49 | if __name__=='__main__':
50 | check_run_gm()
--------------------------------------------------------------------------------
/quant/util/redisutil.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from redis import StrictRedis
4 |
5 | # 数据库连接方式 因为就算我自己使用的,所有没有设置密码
6 | def getredis():
7 | redis = StrictRedis(host='localhost', port=6379, db=0, password='foobared')
8 | return redis
9 |
10 | rd=getredis()
11 | # rd.set('logging_level',logging.DEBUG)
12 | rd.set('logging_level',logging.INFO)
13 | dd=rd.get('logging_level')
14 | # loginfo(dd)
--------------------------------------------------------------------------------
/quant/util/stockutil.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | from quant.util import numberutil
5 |
6 | base_dir=os.path.dirname(os.path.dirname(__file__))#获取pathtest的绝对路径
7 | sys.path.append(base_dir)#将pathtest的绝对路径加入到sys.path中
8 | # 转换东方财富网数据的股票代码为掘金代码格式
9 | from datetime import datetime
10 | import pandas as pd
11 | # 交易日
12 | trade_dates =['2022-01-04', '2022-01-05', '2022-01-06', '2022-01-07', '2022-01-10', '2022-01-11', '2022-01-12', '2022-01-13', '2022-01-14', '2022-01-17', '2022-01-18', '2022-01-19', '2022-01-20', '2022-01-21', '2022-01-24', '2022-01-25', '2022-01-26', '2022-01-27', '2022-01-28', '2022-02-07', '2022-02-08', '2022-02-09', '2022-02-10', '2022-02-11', '2022-02-14', '2022-02-15', '2022-02-16', '2022-02-17', '2022-02-18', '2022-02-21', '2022-02-22', '2022-02-23', '2022-02-24', '2022-02-25', '2022-02-28', '2022-03-01', '2022-03-02', '2022-03-03', '2022-03-04', '2022-03-07', '2022-03-08', '2022-03-09', '2022-03-10', '2022-03-11', '2022-03-14', '2022-03-15', '2022-03-16', '2022-03-17', '2022-03-18', '2022-03-21', '2022-03-22', '2022-03-23', '2022-03-24', '2022-03-25', '2022-03-28', '2022-03-29', '2022-03-30', '2022-03-31', '2022-04-01', '2022-04-06', '2022-04-07', '2022-04-08', '2022-04-11', '2022-04-12', '2022-04-13', '2022-04-14', '2022-04-15', '2022-04-18', '2022-04-19', '2022-04-20', '2022-04-21', '2022-04-22', '2022-04-25', '2022-04-26', '2022-04-27', '2022-04-28', '2022-04-29', '2022-05-05', '2022-05-06', '2022-05-09', '2022-05-10', '2022-05-11', '2022-05-12', '2022-05-13', '2022-05-16', '2022-05-17', '2022-05-18', '2022-05-19', '2022-05-20', '2022-05-23', '2022-05-24', '2022-05-25', '2022-05-26', '2022-05-27', '2022-05-30', '2022-05-31', '2022-06-01', '2022-06-02', '2022-06-06', '2022-06-07', '2022-06-08', '2022-06-09', '2022-06-10', '2022-06-13', '2022-06-14', '2022-06-15', '2022-06-16', '2022-06-17', '2022-06-20', '2022-06-21', '2022-06-22', '2022-06-23', '2022-06-24', '2022-06-27', '2022-06-28', '2022-06-29', '2022-06-30', '2022-07-01', '2022-07-04', '2022-07-05', '2022-07-06', '2022-07-07', '2022-07-08', '2022-07-11', '2022-07-12', '2022-07-13', '2022-07-14', '2022-07-15', '2022-07-18', '2022-07-19', '2022-07-20', '2022-07-21', '2022-07-22', '2022-07-25', '2022-07-26', '2022-07-27', '2022-07-28', '2022-07-29', '2022-08-01', '2022-08-02', '2022-08-03', '2022-08-04', '2022-08-05', '2022-08-08', '2022-08-09', '2022-08-10', '2022-08-11', '2022-08-12', '2022-08-15', '2022-08-16', '2022-08-17', '2022-08-18', '2022-08-19', '2022-08-22', '2022-08-23', '2022-08-24', '2022-08-25', '2022-08-26', '2022-08-29', '2022-08-30', '2022-08-31', '2022-09-01', '2022-09-02', '2022-09-05', '2022-09-06', '2022-09-07', '2022-09-08', '2022-09-09', '2022-09-13', '2022-09-14', '2022-09-15', '2022-09-16', '2022-09-19', '2022-09-20', '2022-09-21', '2022-09-22', '2022-09-23', '2022-09-26', '2022-09-27', '2022-09-28', '2022-09-29', '2022-09-30', '2022-10-10', '2022-10-11', '2022-10-12', '2022-10-13', '2022-10-14', '2022-10-17', '2022-10-18', '2022-10-19', '2022-10-20', '2022-10-21', '2022-10-24', '2022-10-25', '2022-10-26', '2022-10-27', '2022-10-28', '2022-10-31', '2022-11-01', '2022-11-02', '2022-11-03', '2022-11-04', '2022-11-07', '2022-11-08', '2022-11-09', '2022-11-10', '2022-11-11', '2022-11-14', '2022-11-15', '2022-11-16', '2022-11-17', '2022-11-18', '2022-11-21', '2022-11-22', '2022-11-23', '2022-11-24', '2022-11-25', '2022-11-28', '2022-11-29', '2022-11-30', '2022-12-01', '2022-12-02', '2022-12-05', '2022-12-06', '2022-12-07', '2022-12-08', '2022-12-09', '2022-12-12', '2022-12-13', '2022-12-14', '2022-12-15', '2022-12-16', '2022-12-19', '2022-12-20', '2022-12-21', '2022-12-22', '2022-12-23', '2022-12-26', '2022-12-27', '2022-12-28', '2022-12-29', '2022-12-30', '2023-01-03', '2023-01-04', '2023-01-05', '2023-01-06', '2023-01-09', '2023-01-10', '2023-01-11', '2023-01-12', '2023-01-13', '2023-01-16', '2023-01-17', '2023-01-18', '2023-01-19', '2023-01-20', '2023-01-30', '2023-01-31', '2023-02-01', '2023-02-02', '2023-02-03', '2023-02-06', '2023-02-07', '2023-02-08', '2023-02-09', '2023-02-10', '2023-02-13', '2023-02-14', '2023-02-15', '2023-02-16', '2023-02-17', '2023-02-20', '2023-02-21', '2023-02-22', '2023-02-23', '2023-02-24', '2023-02-27', '2023-02-28', '2023-03-01', '2023-03-02', '2023-03-03', '2023-03-06', '2023-03-07', '2023-03-08', '2023-03-09', '2023-03-10', '2023-03-13', '2023-03-14', '2023-03-15', '2023-03-16', '2023-03-17', '2023-03-20', '2023-03-21', '2023-03-22', '2023-03-23', '2023-03-24', '2023-03-27', '2023-03-28', '2023-03-29', '2023-03-30', '2023-03-31', '2023-04-03', '2023-04-04', '2023-04-06', '2023-04-07', '2023-04-10', '2023-04-11', '2023-04-12', '2023-04-13', '2023-04-14', '2023-04-17', '2023-04-18', '2023-04-19', '2023-04-20', '2023-04-21', '2023-04-24', '2023-04-25', '2023-04-26', '2023-04-27', '2023-04-28', '2023-05-04', '2023-05-05', '2023-05-08', '2023-05-09', '2023-05-10', '2023-05-11', '2023-05-12', '2023-05-15', '2023-05-16', '2023-05-17', '2023-05-18', '2023-05-19', '2023-05-22', '2023-05-23', '2023-05-24', '2023-05-25', '2023-05-26', '2023-05-29', '2023-05-30', '2023-05-31', '2023-06-01', '2023-06-02', '2023-06-05', '2023-06-06', '2023-06-07', '2023-06-08', '2023-06-09', '2023-06-12', '2023-06-13', '2023-06-14', '2023-06-15', '2023-06-16', '2023-06-19', '2023-06-20', '2023-06-21', '2023-06-26', '2023-06-27', '2023-06-28', '2023-06-29', '2023-06-30', '2023-07-03', '2023-07-04', '2023-07-05', '2023-07-06', '2023-07-07', '2023-07-10', '2023-07-11', '2023-07-12', '2023-07-13', '2023-07-14', '2023-07-17', '2023-07-18', '2023-07-19', '2023-07-20', '2023-07-21', '2023-07-24', '2023-07-25', '2023-07-26', '2023-07-27', '2023-07-28', '2023-07-31', '2023-08-01', '2023-08-02', '2023-08-03', '2023-08-04', '2023-08-07', '2023-08-08', '2023-08-09', '2023-08-10', '2023-08-11', '2023-08-14', '2023-08-15', '2023-08-16', '2023-08-17', '2023-08-18', '2023-08-21', '2023-08-22', '2023-08-23', '2023-08-24', '2023-08-25', '2023-08-28', '2023-08-29', '2023-08-30', '2023-08-31', '2023-09-01', '2023-09-04', '2023-09-05', '2023-09-06', '2023-09-07', '2023-09-08', '2023-09-11', '2023-09-12', '2023-09-13', '2023-09-14', '2023-09-15', '2023-09-18', '2023-09-19', '2023-09-20', '2023-09-21', '2023-09-22', '2023-09-25', '2023-09-26', '2023-09-27', '2023-09-28', '2023-10-09', '2023-10-10', '2023-10-11', '2023-10-12', '2023-10-13', '2023-10-16', '2023-10-17', '2023-10-18', '2023-10-19', '2023-10-20', '2023-10-23', '2023-10-24', '2023-10-25', '2023-10-26', '2023-10-27', '2023-10-30', '2023-10-31', '2023-11-01', '2023-11-02', '2023-11-03', '2023-11-06', '2023-11-07', '2023-11-08', '2023-11-09', '2023-11-10', '2023-11-13', '2023-11-14', '2023-11-15', '2023-11-16', '2023-11-17', '2023-11-20', '2023-11-21', '2023-11-22', '2023-11-23', '2023-11-24', '2023-11-27', '2023-11-28', '2023-11-29', '2023-11-30', '2023-12-01', '2023-12-04', '2023-12-05', '2023-12-06', '2023-12-07', '2023-12-08', '2023-12-11', '2023-12-12', '2023-12-13', '2023-12-14', '2023-12-15', '2023-12-18', '2023-12-19', '2023-12-20', '2023-12-21', '2023-12-22', '2023-12-25', '2023-12-26', '2023-12-27', '2023-12-28', '2023-12-29']
13 |
14 | # 从东方财富通代码转到掘金代码
15 | def getSymbolFromEastMoney(code):
16 | ret = ''
17 | prec = code[0:2]
18 | if prec == '60':
19 | ret = '{0}.{1}'.format('SHSE', code)
20 | elif prec == '30' or prec == '00':
21 | ret = '{0}.{1}'.format('SZSE', code)
22 | return ret
23 |
24 | # 从复合股票代码分割出股票代码和市场代码
25 | def getSymbol(code,pos):
26 | ret=''
27 | if pos==0:
28 | ret = code.split('.')[pos]
29 | if ret == 'SHSE':
30 | ret = 'sh'
31 | else:
32 | ret = 'sz'
33 | elif pos==1:
34 | ret = code.split('.')[pos]
35 | return ret
36 | # pass
37 |
38 | # 拼奏掘金格式股票代码包括市场代码
39 | def getGmStyleSymbol(code,market):
40 | pref='SHSE'
41 | if market=='sz':
42 | pref='SZSE'
43 | return '{0}.{1}'.format(pref,code )
44 |
45 | # 讯投代码转掘金
46 | def xtToGmSymbol(code):
47 | symbol=code.split('.')[0]
48 | symbol=getGmSymbol(symbol)
49 | return symbol
50 |
51 | # 掘金代码转讯投
52 | def GmToXtSymbol(code):
53 | symbol=code.split('.')[1]
54 | symbol=getXtSymbol(symbol)
55 | return symbol
56 |
57 | # 拼奏掘金格式股票代码包括市场代码
58 | def getGmSymbol(code):
59 | if len(code)<6:
60 | code= code.zfill(6)
61 | ret = ''
62 | prec = code[0:1]
63 | if prec == '6' or prec == '5':
64 | ret = '{0}.{1}'.format('SHSE', code)
65 | # elif prec == '30' or prec == '00':
66 | else:
67 | ret = '{0}.{1}'.format('SZSE', code)
68 | return ret
69 |
70 | # 拼奏掘金格式股票代码包括市场代码
71 | def getXtSymbol(code):
72 | if len(code)<6:
73 | code= code.zfill(6)
74 | ret = ''
75 | prec = code[0:1]
76 | if prec == '6':
77 | ret = '{0}.{1}'.format(code,'SH')
78 | # elif prec == '30' or prec == '00':
79 | else:
80 | ret = '{0}.{1}'.format(code,'SZ')
81 | return ret
82 | # 传入普通代码数组生成掘金代码字符串
83 | def getGmSymbolStr(codes):
84 | ret=''
85 | i=0
86 | for code in codes:
87 | if i>0:
88 | ret=ret+','
89 | ret=ret+getGmSymbol(code)
90 | i=i+1
91 | return ret
92 |
93 | # 删除股票代码前缀
94 | def delSymbolPrefix(symbol):
95 | ret=symbol
96 | if symbol.find('.')>=0:
97 | ret=symbol.split('.')[1]
98 | return ret
99 |
100 | # 从股票代码获取市场代码{适用东方财富网获取数据}
101 | def getMarketFromGmSymbol(code):
102 | pref=code.split('.')[0]
103 | ret=0
104 | if pref=="SHSE":
105 | ret=1
106 | elif pref=="SZSE":
107 | ret=0
108 | return ret
109 |
110 | # 掘金格式转普通格式
111 | def gmToNormalCode(symbols):
112 | ret=[]
113 | for s in symbols:
114 | ret.append(delSymbolPrefix(s))
115 | return ret
116 | # 拼奏掘金格式股票代码包括市场代码
117 | def getGmToEastmoneySymbol(code):
118 | code=code.replace('SHSE',"1")
119 | code = code.replace('SZSE', "0")
120 | return code
121 |
122 | # 拼奏富途格式股票代码包括市场代码
123 | def getFutuSymbol(code):
124 | if len(code)<6:
125 | code= code.zfill(6)
126 | ret = ''
127 | prec = code[0:1]
128 | if prec == '6':
129 | ret = '{0}.{1}'.format('SH', code)
130 | # elif prec == '30' or prec == '00':
131 | else:
132 | ret = '{0}.{1}'.format('SZ', code)
133 | return ret
134 |
135 | # 拼奏富途格式股票代码包括市场代码
136 | def getFutuSymbols(codes):
137 | ret=[]
138 | for i, v in codes.items():
139 | ret.append(getFutuSymbol(v))
140 | return ret
141 |
142 | # 判断是否交易日
143 | def isTradeDate(date):
144 | ret=False
145 | if date in trade_dates:
146 | ret=True
147 | return ret
148 |
149 | # 获取最近的交易日期
150 | def getLatelyTradeDate():
151 | dates = pd.Series(trade_dates)
152 | df= pd.DataFrame(dates,columns=['date'])
153 | df['date']=pd.to_datetime(df['date'])
154 | df=df.loc[df['date']<=datetime.now(),:]
155 | date=df.at[len(df)-1,'date']
156 | return date
157 |
158 | #获取上一交易日
159 | def get_last_trade_date():
160 | dates = pd.Series(trade_dates)
161 | df= pd.DataFrame(dates,columns=['date'])
162 | df['date']=pd.to_datetime(df['date'])
163 | now=datetime.strptime(datetime.now().strftime('%Y-%m-%d'), '%Y-%m-%d')
164 | df=df.loc[df['date']=begin) & (df['date']<=end),:]
203 | return df
204 |
205 | # 获取报表日期
206 | def get_reporter_date():
207 | rpt_dates=["2023-12-31","2023-09-30","2023-06-30","2023-03-31","2022-12-31","2022-09-30","2022-06-30","2022-03-31","2021-12-31","2021-09-30","2021-06-30","2021-03-31"]
208 | dates = pd.Series(rpt_dates)
209 | df = pd.DataFrame(dates, columns=['date'])
210 | df['date'] = pd.to_datetime(df['date'])
211 | now = datetime.strptime(datetime.now().strftime('%Y-%m-%d'), '%Y-%m-%d')
212 | df = df.loc[df['date'] < now, :]
213 | df.reset_index(drop=True,inplace=True)
214 | date = df.at[0, 'date']
215 | return date.strftime('%Y-%m-%d')
216 | # date=GmApi().get_last_trade_date()
217 | # return pd.to_datetime(date)
218 | # loginfo(date)
219 |
220 | def value_to_volume(value,price):
221 | volume = int(value / price)
222 | volume = numberutil.get_neat(volume)
223 | return volume
224 |
225 | if __name__=='__main__':
226 | if isTradeDate('2023-01-08'):
227 | print('yes')
228 | else:
229 | print('no')
230 | # getLatelyTradeDate()
231 | # date=get_reporter_date()
232 | # code=gmToNormalCode(['SHSE.600031','SHSE.600032','SZSE.000031'])
233 | # loginfo(code)
234 | # df=get_period_trade_date('2021-10-19','2022-02-22')
235 | # date= get_date_pren_trade(5)
236 | # code=GmToXtSymbol("SZSE.600222")
237 | # print(code)
238 | date=get_date_pren_trade(20)
239 | print(date)
--------------------------------------------------------------------------------
/quant/util/stringutil.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import ast
3 | import re
4 |
5 | # 数组转字符串
6 | def array_to_string(array):
7 | ret=''
8 | i=0
9 | for s in array:
10 | if i>0:
11 | ret=ret+','
12 | ret=ret+str(s)
13 | i=i+1
14 | return ret
15 |
16 | # 字符串转数组
17 |
18 | # arr = [1,2,3,4,5,6]
19 | # #求方差
20 | # arr_var = np.var(arr)
21 | # #求标准差
22 | # arr_std = np.std(arr,ddof=1)
23 | # print("方差为:%f" % arr_var)
24 | # print("标准差为:%f" % arr_std)
25 |
26 | def string_to_nparray(text):
27 | # 将,替换为空格
28 | text = text.replace(",", " ")
29 | # 去除换行
30 | text = text.replace('\n', '')
31 | text = re.sub('\[\s+', '[', text)
32 | text = re.sub('\s+]', ']', text)
33 | # 添加 ','
34 | xs = re.sub('\s+', ',', text)
35 | # 转换回numpy.array
36 | return np.array(ast.literal_eval(xs))
37 |
38 |
39 | if __name__=='__main__':
40 | a= string_to_nparray('[ 16.6206 16.8443 16.9422 16.9282 16.7814 16.3409 16.0193 15.8375 16.9213 16.6486 16.3619 16.1451 16.1941 16.25 17.194 16.6835 16.6556 16.1032 16.1171 ]')
41 | print(a)
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy==1.22.0
2 | gm==3.0.159
3 | matplotlib==3.5.1
4 | statsmodels==0.13.1
5 | scipy==1.7.3
6 | requests==2.27.0
7 | demjson==1.6
8 | mysql-connector==2.2.9
9 | typing==3.7.4.3
10 | mpl_finance==0.10.1
11 | SQLAlchemy==1.4.27
12 | psutil
13 | urllib3==1.26.4
14 | mplfinance==0.12.9b1
15 | pytdx==1.72
16 |
--------------------------------------------------------------------------------