Repository: drlgistics/Wt4ElegantRL
Branch: main
Commit: cc9f327e3461
Files: 188
Total size: 3.7 MB
Directory structure:
gitextract_wz5tjxvh/
├── .gitignore
├── LICENSE
├── README.md
├── analysts.py
├── assessments.py
├── compare_elegantrl.py
├── compare_rllib.py
├── compare_sb3.py
├── config/
│ ├── 01commom/
│ │ ├── actpolicy.json
│ │ ├── commodities.json
│ │ ├── contracts.json
│ │ ├── fees.json
│ │ ├── holidays.json
│ │ ├── hots.json
│ │ └── sessions.json
│ ├── 03research/
│ │ ├── cta.json
│ │ ├── hft.json
│ │ ├── log_debugger.json
│ │ ├── log_evaluator.json
│ │ └── log_trainer.json
│ └── 04realtime/
│ └── log.json
├── dataset/
│ └── his/
│ ├── day/
│ │ ├── CFFEX/
│ │ │ ├── CFFEX.IC_HOT.dsb
│ │ │ ├── CFFEX.IF_HOT.dsb
│ │ │ ├── CFFEX.IH_HOT.dsb
│ │ │ ├── CFFEX.TF_HOT.dsb
│ │ │ └── CFFEX.T_HOT.dsb
│ │ ├── CZCE/
│ │ │ ├── CZCE.AP_HOT.dsb
│ │ │ ├── CZCE.CF_HOT.dsb
│ │ │ ├── CZCE.JR_HOT.dsb
│ │ │ ├── CZCE.MA_HOT.dsb
│ │ │ ├── CZCE.RM_HOT.dsb
│ │ │ ├── CZCE.SF_HOT.dsb
│ │ │ ├── CZCE.SR_HOT.dsb
│ │ │ ├── CZCE.TA_HOT.dsb
│ │ │ └── CZCE.ZC_HOT.dsb
│ │ ├── DCE/
│ │ │ ├── DCE.a_HOT.dsb
│ │ │ ├── DCE.c_HOT.dsb
│ │ │ ├── DCE.cs_HOT.dsb
│ │ │ ├── DCE.i_HOT.dsb
│ │ │ ├── DCE.jd_HOT.dsb
│ │ │ ├── DCE.l_HOT.dsb
│ │ │ ├── DCE.m_HOT.dsb
│ │ │ ├── DCE.p_HOT.dsb
│ │ │ ├── DCE.pp_HOT.dsb
│ │ │ ├── DCE.rr_HOT.dsb
│ │ │ └── DCE.y_HOT.dsb
│ │ ├── INE/
│ │ │ ├── INE.lu_HOT.dsb
│ │ │ └── INE.sc_HOT.dsb
│ │ └── SHFE/
│ │ ├── SHFE.ag_HOT.dsb
│ │ ├── SHFE.al_HOT.dsb
│ │ ├── SHFE.bu_HOT.dsb
│ │ ├── SHFE.cu_HOT.dsb
│ │ ├── SHFE.fu_HOT.dsb
│ │ ├── SHFE.hc_HOT.dsb
│ │ ├── SHFE.ni_HOT.dsb
│ │ ├── SHFE.pb_HOT.dsb
│ │ ├── SHFE.rb_HOT.dsb
│ │ └── SHFE.zn_HOT.dsb
│ └── min5/
│ ├── CFFEX/
│ │ ├── CFFEX.IC_HOT.dsb
│ │ ├── CFFEX.IF_HOT.dsb
│ │ ├── CFFEX.IH_HOT.dsb
│ │ ├── CFFEX.TF_HOT.dsb
│ │ └── CFFEX.T_HOT.dsb
│ ├── CZCE/
│ │ ├── CZCE.AP_HOT.dsb
│ │ ├── CZCE.CF_HOT.dsb
│ │ ├── CZCE.JR_HOT.dsb
│ │ ├── CZCE.MA_HOT.dsb
│ │ ├── CZCE.RM_HOT.dsb
│ │ ├── CZCE.SF_HOT.dsb
│ │ ├── CZCE.SR_HOT.dsb
│ │ ├── CZCE.TA_HOT.dsb
│ │ └── CZCE.ZC_HOT.dsb
│ ├── DCE/
│ │ ├── DCE.a_HOT.dsb
│ │ ├── DCE.c_HOT.dsb
│ │ ├── DCE.cs_HOT.dsb
│ │ ├── DCE.i_HOT.dsb
│ │ ├── DCE.jd_HOT.dsb
│ │ ├── DCE.l_HOT.dsb
│ │ ├── DCE.m_HOT.dsb
│ │ ├── DCE.p_HOT.dsb
│ │ ├── DCE.pp_HOT.dsb
│ │ ├── DCE.rr_HOT.dsb
│ │ └── DCE.y_HOT.dsb
│ ├── INE/
│ │ ├── INE.lu_HOT.dsb
│ │ └── INE.sc_HOT.dsb
│ └── SHFE/
│ ├── SHFE.ag_HOT.dsb
│ ├── SHFE.al_HOT.dsb
│ ├── SHFE.bu_HOT.dsb
│ ├── SHFE.cu_HOT.dsb
│ ├── SHFE.fu_HOT.dsb
│ ├── SHFE.hc_HOT.dsb
│ ├── SHFE.ni_HOT.dsb
│ ├── SHFE.pb_HOT.dsb
│ ├── SHFE.rb_HOT.dsb
│ └── SHFE.zn_HOT.dsb
├── dataset_from_storage.py
├── elegantrl/
│ ├── __init__.py
│ ├── agent.py
│ ├── demo.py
│ ├── env.py
│ ├── evaluator.py
│ ├── net.py
│ ├── replay.py
│ └── run.py
├── envs.py
├── envs_simple_cta.py
├── features.py
├── reprocess.py
├── requirements/
│ ├── full_with_cuda.yaml
│ └── full_without_cuda.yaml
├── run_toy.py
├── runner.py
├── stoppers.py
├── strategies.py
└── wtpy/
├── CodeHelper.py
├── ContractMgr.py
├── CtaContext.py
├── ExtModuleDefs.py
├── ExtToolDefs.py
├── HftContext.py
├── ProductMgr.py
├── SelContext.py
├── SessionMgr.py
├── StrategyDefs.py
├── WtBtEngine.py
├── WtCoreDefs.py
├── WtDataDefs.py
├── WtDtEngine.py
├── WtDtServo.py
├── WtEngine.py
├── WtMsgQue.py
├── WtUtilDefs.py
├── __init__.py
├── apps/
│ ├── WtBtAnalyst.py
│ ├── WtCtaOptimizer.py
│ ├── WtHotPicker.py
│ ├── __init__.py
│ └── datahelper/
│ ├── DHBaostock.py
│ ├── DHDefs.py
│ ├── DHFactory.py
│ ├── DHRqData.py
│ ├── DHTushare.py
│ ├── __init__.py
│ └── db/
│ ├── MysqlHelper.py
│ ├── __init__.py
│ └── initdb_mysql.sql
├── monitor/
│ ├── DataMgr.py
│ ├── EventReceiver.py
│ ├── PushSvr.py
│ ├── WatchDog.py
│ ├── WtBtMon.py
│ ├── WtLogger.py
│ ├── WtMonSvr.py
│ ├── __init__.py
│ └── static/
│ ├── __init__.py
│ └── console/
│ ├── __init__.py
│ ├── index.html
│ └── static/
│ ├── __init__.py
│ ├── css/
│ │ ├── __init__.py
│ │ ├── app.7b6729291f05cec91b99cfd44c17df6b.css
│ │ └── static/
│ │ ├── __init__.py
│ │ └── fonts/
│ │ └── __init__.py
│ └── js/
│ ├── __init__.py
│ ├── app.d3652914f874ea570f72.js
│ ├── manifest.3ad1d5771e9b13dbdad2.js
│ └── vendor.cd39a6c53a9d7599c08c.js
└── wrapper/
├── ContractLoader.py
├── PlatformHelper.py
├── WtBtWrapper.py
├── WtDtHelper.py
├── WtDtServoApi.py
├── WtDtWrapper.py
├── WtExecApi.py
├── WtMQWrapper.py
├── WtWrapper.py
├── __init__.py
├── linux/
│ ├── __init__.py
│ ├── executer/
│ │ └── __init__.py
│ ├── parsers/
│ │ └── __init__.py
│ └── traders/
│ └── __init__.py
├── x64/
│ ├── __init__.py
│ ├── executer/
│ │ └── __init__.py
│ ├── parsers/
│ │ └── __init__.py
│ └── traders/
│ └── __init__.py
└── x86/
├── __init__.py
├── executer/
│ └── __init__.py
├── parsers/
│ └── __init__.py
└── traders/
└── __init__.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# Wt4ElegantRL
Wt4ElegantRL = [WonderTrader](https://github.com/wondertrader/wtpy) + [ElegantRL](https://github.com/AI4Finance-Foundation/ElegantRL)
# Document by panyunan
https://gitee.com/panyunan/wt4elegantrl-doc
## INSTALL
clone
```
git clone https://github.com/drlgistics/Wt4ElegantRL.git
cd Wt4ElegantRL
```
with cuda
```
conda env create -n wt4elegantrl --file ./requirements/full_with_cuda.yaml
```
or without cuda
```
conda env create -n wt4elegantrl --file ./requirements/full_without_cuda.yaml
```
activate
```
conda activate wt4elegantrl
```
## RLLIB
demo
```
python ./compare_rllib.py test -p ./trained/rllib/TD3_2021-10-29_11-21-37/TD3_SimpleCTAEnv_536e7_00000_0_2021-10-29_11-21-37/checkpoint_000023/checkpoint-23
```
train
```
python ./compare_rllib.py train
```
## SB3
demo
```
python ./compare_sb3.py test -p ./trained/sb3/td3/best_model
```
train
```
python ./compare_sb3.py train
```
## ELEGANTRL
train
```
python ./compare_elegantrl.py train
```
================================================
FILE: analysts.py
================================================
from ntpath import realpath
from wtpy.apps import WtBtAnalyst
from glob import glob
from tqdm.auto import tqdm
from os.path import dirname, basename
from click import command, option
# for i in tqdm(glob('./outputs_bt/*/funds.csv')):
# folder = dirname(i)
# name = folder[13:]
# analyst = WtBtAnalyst()
# folder = "./outputs_bt/%s/" % name
# analyst.add_strategy(
# name, folder=folder, init_capital=500000, rf=0.04, annual_trading_days=240)
# try:
# analyst.run_new('%s/%s_PnLAnalyzing.xlsx' % (folder, name))
# # analyst.run('%s/%s_PnLAnalyzing.xlsx' % (folder, name))
# except:
# analyst.run('%s/%s_PnLAnalyzing.xlsx' % (folder, name))
@command()
@option('--path', '-p', 'path', default='./outputs_bt/*/')
def run(path):
for i in tqdm(glob('%s/funds.csv'%path)):
folder = dirname(i)
name = basename(folder)
analyst = WtBtAnalyst()
analyst.add_strategy(
name, folder='%s/'%folder, init_capital=500000, rf=0.04, annual_trading_days=240)
try:
analyst.run_new('%s/%s_PnLAnalyzing.xlsx' % (folder, name))
# analyst.run('%s/%s_PnLAnalyzing.xlsx' % (folder, name))
except:
analyst.run('%s/%s_PnLAnalyzing.xlsx' % (folder, name))
if __name__ == '__main__':
run()
================================================
FILE: assessments.py
================================================
from abc import abstractmethod
import numpy as np
from wtpy.StrategyDefs import CtaContext, HftContext
class Assessment():
def __init__(self, init_assets=1000000):
self._init_assets_ = init_assets
self.reset()
@abstractmethod
def reset(self):
raise NotImplementedError
@abstractmethod
def calculate(self, context: CtaContext):
raise NotImplementedError
@abstractmethod
def finish(self):
raise NotImplementedError
@property
@abstractmethod
def reward(self) -> float:
raise NotImplementedError
@property
@abstractmethod
def done(self) -> float:
raise NotImplementedError
@property
@abstractmethod
def curr_assets(self) -> float:
raise NotImplementedError
@property
def init_assets(self) -> float:
return self._init_assets_
class SimpleAssessment(Assessment): # 借鉴了neofinrl
gamma = 0.99
def reset(self):
self.__assets__: list = [self._init_assets_]
self.__reward__: list = [0]
self.__done__: bool = False
self.__successive__: int = 1
# self.__gamma__ = 1-self.gamma
def calculate(self, context: CtaContext):
if self.__done__:
return
# 动态权益
dynbalance = context.stra_get_fund_data(0)
# 总平仓盈亏
# closeprofit = context.stra_get_fund_data(1)
# 总浮动盈亏
# positionprofit = context.stra_get_fund_data(2)
# 总手续费
# fee = context.stra_get_fund_data(3)
self.__assets__.append(self._init_assets_+dynbalance) # 账户实时的动态权益
if len(self.__reward__) > 1:
reward = (self.__assets__[-1]-self.__assets__[-2]) \
/ self._init_assets_ * 12 #* 0.382
# if (reward < 0 and self.__reward__[-1] < 0) or \
# (reward > 0 and self.__reward__[-1] > 0):
# self.__successive__ += 1
# else:
# self.__successive__ = 1
# reward *= self.__successive__
# if self.__assets__[-1] > self.__assets__[-2]: #
# reward += 0.0001*self.__successive__
# else:
# reward -= 0.0001*self.__successive__
# reward += (self.__assets__[-1]-max(self.__assets__[:-1])) \
# / self._init_assets_ * 0.1
# reward += (self.__assets__[-1]-min(self.__assets__[:-1])) \
# / self._init_assets_ * 0.1
# reward = 0
# returns = np.diff(np.array(self.__assets__))
# reward = (np.where(returns < 0, 0, returns).sum()-1e-5) \
# / abs(np.where(returns > 0, 0, returns).sum()+1e-5) \
# - 1
# reward *= 0.1
# print(np.where(returns < 0, 0, returns).sum(), np.where(returns > 0, 0, returns).sum(), dynbalance, reward)
# 情绪奖励
# if (reward < 0 and self.__reward__[-1] < 0) or \
# (reward > 0 and self.__reward__[-1] > 0):
# self.__successive__ += 1
# else:
# self.__successive__ = 1
# #资金成本
# if self.__assets__[-1] > self.__assets__[-2]: #
# reward += 0.0001*self.__successive__
# else:
# reward -= 0.0001*self.__successive__
# reward *= 0.01
# #近期奖励
# if self.__assets__[-1] > max(self.__assets__[:-1]):
# reward += 0.02
# if self.__assets__[-1] < min(self.__assets__[:-1]):
# reward -= 0.01
# 长期奖励
# reward += (self.__assets__[-1]-max(self.__assets__[:-1])) \
# / self._init_assets_ * self.__successive__ * 0.382
# reward += (self.__assets__[-1]-min(self.__assets__[:-1])) \
# / self._init_assets_ * self.__successive__ * 0.382
# if (reward < 0 and self.__reward__[-1] < 0) or \
# (reward > 0 and self.__reward__[-1] > 0):
# reward *= self.__successive__
# self.__successive__ += 1
# else:
# self.__successive__ = 1
# reward += (self.__assets__[-1]-max(self.__assets__[:-1])) \
# / self._init_assets_ * 0.382
# reward += (self.__assets__[-1]-min(self.__assets__[:-1])) \
# / self._init_assets_ * 0.382
'''
2021/11/01
if self.__assets__[-1] > self.__assets__[-2]:
self.__successive__ += 1
else:
self.__successive__ = 1
reward = -0.00001
reward += (self.__assets__[-1] /
self.__assets__[-2] - 1) * 0.382 * self.__successive__
max_assets = (self.__assets__[-1]/max(self.__assets__[:-1])-1) * 0.382
reward += max_assets * (5 if max_assets > 0 else 1)
min_assets = (self.__assets__[-1]/min(self.__assets__[:-1])-1) * 0.382
reward += min_assets * (5 if min_assets < 0 else 1)
'''
else:
reward = -0.01
self.__reward__.append(reward) # 以动态权益差分设计reward
self.__done__ = False # 此处可以根据控制任务结束状态
def finish(self):
if self.__done__:
return
# returns = np.add(1, self.__reward__).cumprod()
# np.subtract(returns, 1, out=returns)
# gamma = 0
# for reward in np.diff(np.log(self.__assets__)):
# gamma *= self.gamma
# gamma += reward
gamma = 0
for reward in self.__reward__:
gamma *= self.gamma
gamma += reward
# gamma = np.diff(np.array(self.__assets__))
# gamma = (np.where(gamma < 0, 0, gamma).sum()-1e-5) \
# / abs(np.where(gamma > 0, 0, gamma).sum()+1e-5) \
# - 1
# gamma = np.round(np.nanprod(np.array(self.__reward__)+1, axis=0)-1, 5)
# gamma = self.__assets__[-1]/max(self.__assets__)-1
# gamma = self.__assets__[-1]/self.init_assets-1
self.__reward__.append(gamma) # 在结束的时候把过程奖励做处理,作为整个训练的奖励
self.__done__ = True
@property
def reward(self) -> float:
# return self.__reward__[-1]
return float(self.__reward__[-1])
@property
def rewards(self) -> float:
return self.__reward__
@property
def done(self) -> float:
return self.__done__
@property
def curr_assets(self) -> float:
return self.__assets__[-1]
================================================
FILE: compare_elegantrl.py
================================================
from click import command, group, option
from elegantrl.agent import AgentPPO as Agent
# from elegantrl.agent import AgentSAC as Agent
# from elegantrl.agent import AgentModSAC as Agent
# from elegantrl.agent import AgentTD3 as Agent
# from elegantrl.agent import AgentDoubleDQN as Agent
# from elegantrl.agent import AgentDQN as Agent
from elegantrl.run import Arguments, train_and_evaluate
from envs_simple_cta import SimpleCTASubProcessEnv
from gym import make, register
from numpy import inf
from os import getpid
class Wt4RLSimpleTrainer(SimpleCTASubProcessEnv):
env_num = 1
max_step = 1500
if_discrete = False
@property
def state_dim(self):
return self.observation_space.shape[0]
@property
def action_dim(self):
# if len(self.action_space.shape) > 0 else 10
return self.action_space.shape[0]
def __init__(self):
super().__init__(**{
# 'time_start': 202108301600,
# 'time_end': 202108311600,
'time_range': (
# (201901011600, 202101011600),
# (201901011600, 201906301600),
# (201906301600, 202001011600),
# (202001011600, 202006301600),
# (202006301600, 202101011600),
(201812311600, 201901311600),
(201901311600, 201902311600),
(201902311600, 201903311600),
(201903311600, 201904311600),
(201904311600, 201905311600),
(201905311600, 201906311600),
(201906311600, 201907311600),
(201907311600, 201908311600),
(201908311600, 201909311600),
(201909311600, 201910311600),
(201910311600, 201911311600),
(201911311600, 201912311600),
(201912311600, 202001311600),
(202001311600, 202002311600),
(202002311600, 202003311600),
(202003311600, 202004311600),
(202004311600, 202005311600),
(202005311600, 202006311600),
(202006311600, 202007311600),
(202007311600, 202008311600),
(202008311600, 202009311600),
(202009311600, 202010311600),
(202010311600, 202011311600),
(202011311600, 202012311600),
),
'slippage': 0,
'mode': 1
})
class Wt4RLSimpleEvaluator(SimpleCTASubProcessEnv):
env_num = 1
max_step = 1500
if_discrete = False
@property
def state_dim(self):
return self.observation_space.shape[0]
@property
def action_dim(self):
# if len(self.action_space.shape) > 0 else 10
return self.action_space.shape[0]
def __init__(self): # mode=3可以打开详细调试模式
super().__init__(**{
'time_range': (
# (202101011600, 202106301600),
# (201701011600, 201706301600),
# (201706301600, 201801011600),
# (201801011600, 201806301600),
# (201806301600, 201901011600),
(202012311600, 202101311600),
(202101311600, 202102311600),
(202102311600, 202103311600),
(202103311600, 202104311600),
(202104311600, 202105311600),
(202105311600, 202106311600),
(201612311600, 201701311600),
(201701311600, 201702311600),
(201702311600, 201703311600),
(201703311600, 201704311600),
(201704311600, 201705311600),
(201705311600, 201706311600),
(201706311600, 201707311600),
(201707311600, 201708311600),
(201708311600, 201709311600),
(201709311600, 201710311600),
(201710311600, 201711311600),
(201711311600, 201712311600),
(201712311600, 201801311600),
(201801311600, 201802311600),
(201802311600, 201803311600),
(201803311600, 201804311600),
(201804311600, 201805311600),
(201805311600, 201806311600),
(201806311600, 201807311600),
(201807311600, 201808311600),
(201808311600, 201809311600),
(201809311600, 201810311600),
(201810311600, 201811311600),
(201811311600, 201812311600),
),
'slippage': 0,
'mode': 1
})
register('wt4rl-simplecta-trainer-v0', entry_point=Wt4RLSimpleTrainer)
register('wt4rl-simplecta-evaluator-v0', entry_point=Wt4RLSimpleEvaluator)
if __name__ == '__main__':
@group()
def run():
pass
@command()
@option('--count', default=24)
def debug(count):
env: SimpleCTASubProcessEnv = make('wt4rl-simplecta-trainer-v0')
print('状态空间', env.observation_space.shape)
print('动作空间', env.action_space.shape)
a = 0
for i in range(1, int(count)+1): # 模拟训练10次
obs = env.reset()
done = False
n = 0
while not done:
action = env.action_space.sample() # 模拟智能体产生动作
obs, reward, done, info = env.step(action)
n += 1
a += 1
# print('action:', action, 'obs:', obs, 'reward:', reward, 'done:', done)
print('第%s次训练完成,执行%s步, 奖励%s, 盈亏%s。' % (i, n, reward, env.assets))
env.close()
print(a)
@command()
def train():
args = Arguments(
env='wt4rl-simplecta-trainer-v0',
# env='wt4rl-simplecta-evaluator-v0',
agent=Agent()
)
# args必须设置的参数
args.eval_env = 'wt4rl-simplecta-evaluator-v0'
args.max_step = 3000
args.state_dim = 126
args.action_dim = 3
args.if_discrete = False
args.target_return = 5 # inf
args.if_overwrite = False
args.eval_times1 = 15 # 待查明:为啥td3的评估器结果完全一致
args.eval_times2 = 30 # 待查明:为啥td3的评估器结果完全一致
args.worker_num = 1 # 内存小的注意别爆内存
args.break_step = inf
args.if_allow_break = True
#
args.gamma = 0.96 # 8小时会跨过一次隔夜风险,既96个bar
args.learning_rate = 1e-5
# args.gamma = 0.1 ** (1/12/8) # 8小时会跨过一次隔夜风险,既96个bar
# args.learning_rate = 1e-3 # N15:294 Y14:292
args.eval_gap = 2 ** 8
args.net_dim = 2 ** 6
args.batch_size = args.net_dim * 2
args.max_memo = 2 ** 20
args.target_step = args.max_step * 2
args.if_per_or_gae = True
# args.agent.if_use_cri_target = True
# args.agent.if_use_dueling = True
args.env_num = 1
args.learner_gpus = (0,)
args.workers_gpus = args.learner_gpus
args.eval_gpu_id = 0
args.cwd = './outputs_bt/elegantrl/%s_%s_%s' % (
args.agent.__class__.__name__, args.gamma, args.learning_rate)
# args.repeat_times = 0.01
#args.net_dim = 2**9
# args.net_dim = 2 ** 8
#args.max_memo = 2 ** 22
# args.break_step = args.max_step*1000
# args.batch_size = 2 ** 11 # args.net_dim * 2
# args.repeat_times = 1.5
# args.eval_gap = 2 ** 9
# args.eval_times1 = 2 ** 2
# args.eval_times2 = 2 ** 5
# args.worker_num = 4
# args.target_step = args.env.max_step * 1
# train_and_evaluate(args)
train_and_evaluate(args)
@command()
def test():
env = Wt4RLSimpleEvaluator(mode=3)
agent = Agent()
agent.init(net_dim=2 ** 8, state_dim=380, action_dim=10,
learning_rate=0.1 ** (1/12/8), if_per_or_gae=True, env_num=1, gpu_id=0)
agent.save_or_load_agent(
cwd='./outputs_bt/elegantrl/AgentTD3_6.103515625e-05_8', if_save=False)
# for i in range(10): # 模拟训练10次
# obs = env.reset()
# done = False
# n = 0
# while not done:
# action = agent.select_action(obs) # 模拟智能体产生动作
# obs, reward, done, info = env.step(action)
# n += 1
# # print('action:', action, 'obs:', obs,
# # 'reward:', reward, 'done:', done)
# print('第%s次训练完成,执行%s步, 盈亏%s。' % (i+1, n, env.assets))
# env.close()
run.add_command(debug)
run.add_command(train)
run.add_command(test)
# run.add_command(eval)
run()
================================================
FILE: compare_rllib.py
================================================
from ray import tune, init
# from ray.rllib.agents.sac import SACTrainer as Trainer
# from ray.rllib.agents.ddpg import TD3Trainer as Trainer
# from ray.rllib.agents.ddpg import ApexDDPGTrainer as Trainer
# from ray.rllib.agents.a3c import A3CTrainer as Trainer
from ray.rllib.agents.ppo import PPOTrainer as Trainer
# from ray.rllib.agents.ppo import APPOTrainer as Trainer
# from ray.rllib.agents.marwil import MARWILTrainer as Trainer
# from ray.rllib.agents.impala import ImpalaTrainer as Trainer
# from ray.rllib.agents.mbmpo import MBMPOTrainer as Trainer
# from ray.rllib.agents.dreamer import DREAMERTrainer as Trainer
# from ray.rllib.agents.pg import PGTrainer as Trainer
# from ray.rllib.agents.pg import PGTrainer as Trainer
# from ray.rllib.agents.dqn import R2D2Trainer as Trainer
# from ray.rllib.agents.dqn import ApexTrainer as Trainer
# from ray.tune.schedulers.pb2 import PB2
from envs_simple_cta import SimpleCTAEnv
import click
tune.register_env('SimpleCTAEnv',
lambda env_config: SimpleCTAEnv(**env_config))
if __name__ == '__main__':
@click.group()
def run():
pass
@run.command()
def train():
# pb2 = PB2(
# time_attr="training_iteration",
# metric="episode_reward_mean",
# mode="max",
# perturbation_interval=50000,
# quantile_fraction=0.25, # copy bottom % with top %
# # Specifies the hyperparam search space
# hyperparam_bounds={
# "lr": [1e-3, 1e-5],
# "gamma": [0.96, 0.99],
# })
nums_subproc = 5
nums_gpu = 0.92/(nums_subproc+2)
config = {
'env': 'SimpleCTAEnv',
'env_config': {
# 'time_start': 202108301600,
# 'time_end': 202108311600,
'time_range': (
# (201901011600, 202101011600),
# (201901011600, 201906301600),
# (201906301600, 202001011600),
# (202001011600, 202006301600),
# (202006301600, 202101011600),
(201812311600, 201901311600),
(201901311600, 201902311600),
(201902311600, 201903311600),
(201903311600, 201904311600),
(201904311600, 201905311600),
(201905311600, 201906311600),
(201906311600, 201907311600),
(201907311600, 201908311600),
(201908311600, 201909311600),
(201909311600, 201910311600),
(201910311600, 201911311600),
(201911311600, 201912311600),
(201912311600, 202001311600),
(202001311600, 202002311600),
(202002311600, 202003311600),
(202003311600, 202004311600),
(202004311600, 202005311600),
(202005311600, 202006311600),
(202006311600, 202007311600),
(202007311600, 202008311600),
(202008311600, 202009311600),
(202009311600, 202010311600),
(202010311600, 202011311600),
(202011311600, 202012311600),
),
'slippage': 0,
'mode': 1
},
# 'rollout_fragment_length': 10156,
'framework': 'torch',
'num_workers': nums_subproc,
'num_gpus': nums_gpu,
'num_gpus_per_worker': nums_gpu,
'gamma': 0.99,
'lr': 1e-5,
'train_batch_size': 30465,
# "actor_lr": 1e-5,
# "critic_lr": 1e-5,
# "l2_reg": 1e-6,
# 'model': {
# 'use_lstm': True,
# # 'fcnet_hiddens': [64],
# # 'fcnet_activation': 'linear',
# # 'lstm_cell_size': 64,
# # 'max_seq_len': 2,
# },
'evaluation_interval': 24,
"evaluation_num_episodes": 30,
'evaluation_parallel_to_training': False,
'evaluation_num_workers': 1,
"evaluation_config": {
"env_config": {
'time_range': (
# (202101011600, 202106301600),
# (201701011600, 201706301600),
# (201706301600, 201801011600),
# (201801011600, 201806301600),
# (201806301600, 201901011600),
(202012311600, 202101311600),
(202101311600, 202102311600),
(202102311600, 202103311600),
(202103311600, 202104311600),
(202104311600, 202105311600),
(202105311600, 202106311600),
(201612311600, 201701311600),
(201701311600, 201702311600),
(201702311600, 201703311600),
(201703311600, 201704311600),
(201704311600, 201705311600),
(201705311600, 201706311600),
(201706311600, 201707311600),
(201707311600, 201708311600),
(201708311600, 201709311600),
(201709311600, 201710311600),
(201710311600, 201711311600),
(201711311600, 201712311600),
(201712311600, 201801311600),
(201801311600, 201802311600),
(201802311600, 201803311600),
(201803311600, 201804311600),
(201804311600, 201805311600),
(201805311600, 201806311600),
(201806311600, 201807311600),
(201807311600, 201808311600),
(201808311600, 201809311600),
(201809311600, 201810311600),
(201810311600, 201811311600),
(201811311600, 201812311600),
),
'slippage': 0,
'mode': 1
},
},
# 'train_batch_size': 10156,
"batch_mode": "complete_episodes",
}
# training and saving
analysis = tune.run(
Trainer,
stop={
"timesteps_total": 1e10,
'episode_reward_mean': 0.48,
# 'episode_reward_min': 50,
},
# scheduler=pb2,
# num_samples=nums_subproc,
config=config,
keep_checkpoints_num=10,
checkpoint_freq=24,
checkpoint_score_attr='episode_reward_mean',
checkpoint_at_end=True,
local_dir="./outputs_bt/rllib",
)
# retrieve the checkpoint path
analysis.default_metric = "episode_reward_mean"
analysis.default_mode = "max"
checkpoint_path = analysis.get_best_checkpoint(
trial=analysis.get_best_trial())
print(f"Trained model saved at {checkpoint_path}")
@run.command()
@click.option('--path', '-p', 'path')
def test(path):
config = {
'env': 'SimpleCTAEnv',
'env_config': {
'time_range': (
(202106311600, 202107311600),
(202107311600, 202108311600),
(202108311600, 202109311600),
(202109311600, 202110311600),
(202110311600, 202111311600),
(202106311600, 202111311600),
),
'slippage': 0,
'mode': 2
},
'framework': 'torch',
'num_workers': 1,
'num_gpus': 1,
'num_gpus_per_worker': 1,
}
agent = Trainer(config=config)
agent.restore(path)
print(f"Agent loaded from saved model at {path}")
env = SimpleCTAEnv(**config['env_config'])
for i in range(len(config['env_config']['time_range'])*10): # 模拟训练10次
obs = env.reset()
done = False
n = 0
while not done:
# action = env.action_space.sample() # 模拟智能体产生动作
action = agent.compute_single_action(obs)
obs, reward, done, info = env.step(action)
n += 1
# print(
# # 'action:', action,
# # 'obs:', obs,
# 'reward:', reward,
# # 'done:', done
# )
# break
# break
print('第%s次测试完成,执行%s步, 市值%s。' % (i+1, n, env.assets))
env.close()
del env
run()
================================================
FILE: compare_sb3.py
================================================
# from stable_baselines3 import SAC as Trainer
from stable_baselines3 import TD3 as Trainer
# from stable_baselines3 import PPO as Trainer
# from stable_baselines3 import A2C as Trainer
from envs_simple_cta import SimpleCTASubProcessEnv, SimpleCTAEnv
from stable_baselines3.common.callbacks import EvalCallback, StopTrainingOnRewardThreshold
from stable_baselines3.common.monitor import Monitor
import click
@click.group()
def run():
pass
@run.command()
def debug():
learner = SimpleCTASubProcessEnv(**{
'time_range': (
# (201901011600, 202101011600),
# (201901011600, 201906301600),
# (201906301600, 202001011600),
# (202001011600, 202006301600),
# (202006301600, 202101011600),
(201812311600, 201901311600),
(201901311600, 201902311600),
(201902311600, 201903311600),
(201903311600, 201904311600),
(201904311600, 201905311600),
(201905311600, 201906311600),
(201906311600, 201907311600),
(201907311600, 201908311600),
(201908311600, 201909311600),
(201909311600, 201910311600),
(201910311600, 201911311600),
(201911311600, 201912311600),
(201912311600, 202001311600),
(202001311600, 202002311600),
(202002311600, 202003311600),
(202003311600, 202004311600),
(202004311600, 202005311600),
(202005311600, 202006311600),
(202006311600, 202007311600),
(202007311600, 202008311600),
(202008311600, 202009311600),
(202009311600, 202010311600),
(202010311600, 202011311600),
(202011311600, 202012311600),
),
'slippage': 0,
'mode': 1
})
evaluator = SimpleCTASubProcessEnv(**{
'time_range': (
# (202101011600, 202106301600),
# (201701011600, 201706301600),
# (201706301600, 201801011600),
# (201801011600, 201806301600),
# (201806301600, 201901011600),
(202012311600, 202101311600),
(202101311600, 202102311600),
(202102311600, 202103311600),
(202103311600, 202104311600),
(202104311600, 202105311600),
(202105311600, 202106311600),
(201612311600, 201701311600),
(201701311600, 201702311600),
(201702311600, 201703311600),
(201703311600, 201704311600),
(201704311600, 201705311600),
(201705311600, 201706311600),
(201706311600, 201707311600),
(201707311600, 201708311600),
(201708311600, 201709311600),
(201709311600, 201710311600),
(201710311600, 201711311600),
(201711311600, 201712311600),
(201712311600, 201801311600),
(201801311600, 201802311600),
(201802311600, 201803311600),
(201803311600, 201804311600),
(201804311600, 201805311600),
(201805311600, 201806311600),
(201806311600, 201807311600),
(201807311600, 201808311600),
(201808311600, 201809311600),
(201809311600, 201810311600),
(201810311600, 201811311600),
(201811311600, 201812311600),
),
'slippage': 0,
'mode': 1
})
env = learner
for i in range(1): # 模拟训练10次
obs = env.reset()
done = False
n = 0
while not done:
action = env.action_space.sample() # 模拟智能体产生动作
obs, reward, done, info = env.step(action)
n += 1
# print('action:', action, 'obs:', obs,
# 'reward:', reward, 'done:', done)
print('第%s次训练完成,执行%s步, 市值%s。' % (i+1, n, env.assets))
learner.close()
@run.command()
def train():
learner = SimpleCTASubProcessEnv(**{
'time_range': (
# (201901011600, 202101011600),
# (201901011600, 201906301600),
# (201906301600, 202001011600),
# (202001011600, 202006301600),
# (202006301600, 202101011600),
(201812311600, 201901311600),
(201901311600, 201902311600),
(201902311600, 201903311600),
(201903311600, 201904311600),
(201904311600, 201905311600),
(201905311600, 201906311600),
(201906311600, 201907311600),
(201907311600, 201908311600),
(201908311600, 201909311600),
(201909311600, 201910311600),
(201910311600, 201911311600),
(201911311600, 201912311600),
(201912311600, 202001311600),
(202001311600, 202002311600),
(202002311600, 202003311600),
(202003311600, 202004311600),
(202004311600, 202005311600),
(202005311600, 202006311600),
(202006311600, 202007311600),
(202007311600, 202008311600),
(202008311600, 202009311600),
(202009311600, 202010311600),
(202010311600, 202011311600),
(202011311600, 202012311600),
),
'slippage': 0,
'mode': 1
})
evaluator = SimpleCTASubProcessEnv(**{
'time_range': (
# (202101011600, 202106301600),
# (201701011600, 201706301600),
# (201706301600, 201801011600),
# (201801011600, 201806301600),
# (201806301600, 201901011600),
(202012311600, 202101311600),
(202101311600, 202102311600),
(202102311600, 202103311600),
(202103311600, 202104311600),
(202104311600, 202105311600),
(202105311600, 202106311600),
(201612311600, 201701311600),
(201701311600, 201702311600),
(201702311600, 201703311600),
(201703311600, 201704311600),
(201704311600, 201705311600),
(201705311600, 201706311600),
(201706311600, 201707311600),
(201707311600, 201708311600),
(201708311600, 201709311600),
(201709311600, 201710311600),
(201710311600, 201711311600),
(201711311600, 201712311600),
(201712311600, 201801311600),
(201801311600, 201802311600),
(201802311600, 201803311600),
(201803311600, 201804311600),
(201804311600, 201805311600),
(201805311600, 201806311600),
(201806311600, 201807311600),
(201807311600, 201808311600),
(201808311600, 201809311600),
(201809311600, 201810311600),
(201810311600, 201811311600),
(201811311600, 201812311600),
),
'slippage': 0,
'mode': 2
})
n = 1500
eval_callback = EvalCallback(
eval_env=Monitor(evaluator),
callback_on_new_best=StopTrainingOnRewardThreshold(
reward_threshold=5, verbose=1),
n_eval_episodes=30,
eval_freq=24,
log_path='./outputs_bt/sb3/%s'%Trainer.__name__,
best_model_save_path='./outputs_bt/sb3/%s'%Trainer.__name__,
verbose=1)
model: Trainer = Trainer('MlpPolicy', learner,
# gamma=0.1 ** (1/12/8),
gamma=0.96,
# learning_rate=2 ** -14, # 15: 167, 14:
learning_rate=1e-5,
# learning_starts=100,
# batch_size=128,
# ent_coef='auto_0.1',
# policy_kwargs=dict(net_arch=[128, 128, 128]),
tensorboard_log='./outputs_bt/sb3/%s'%Trainer.__name__,
verbose=1,
# device='cpu',
)
model.learn(
total_timesteps=10000,
callback=eval_callback,
log_interval=1
)
model.save('SimpleTrainer')
@run.command()
@click.option('--path', '-p', 'path')
def test(path):
env = SimpleCTAEnv(**{
# 'time_start': 201701011600,
# 'time_end': 201901011600,
# 'time_start': 202001011600,
# 'time_end': 202108311600,
'time_start': 202108311600,
'time_end': 202110131600,
# 'time_end': 202110281600,
'slippage': 0,
'mode': 2,
'id': 2,
})
model = Trainer.load(path)
for i in range(1): # 模拟训练10次
obs = env.reset()
done = False
n = 0
while not done:
action = model.predict(obs)[0]
obs, reward, done, info = env.step(action)
n += 1
# print(
# # 'action:', action,
# # 'obs:', obs,
# 'reward:', reward,
# # 'done:', done
# )
# break
# break
print('第%s次测试完成,执行%s步, 市值%s。' % (i+1, n, env.assets))
env.close()
if __name__ == '__main__':
run()
================================================
FILE: config/01commom/actpolicy.json
================================================
{
"default":{
"order":[
{
"action":"close",
"limit":0
},
{
"action":"open",
"limit":0
}
]
},
"stockindex":{
"order":[
{
"action":"closeyestoday",
"limit":0
},
{
"action":"open",
"limit":500
},
{
"action":"closetoday",
"limit":0
}
],
"filters":["CFFEX.IF","CFFEX.IC","CFFEX.IH"]
},
"lowinnerdayfee":{
"order":[
{
"action":"closetoday",
"limit":0
},
{
"action":"closeyestoday",
"limit":0
},
{
"action":"open",
"limit":0
}
],
"filters":["DCE.a","DCE.cs","DCE.i","DCE.j","DCE.l","DCE.m","DCE.p","DCE.pp","SHFE.au","SHFE.hc","CZCE.ZC","CZCE.CF","CZCE.MA","CZCE.SR","CZCE.AP"]
}
}
================================================
FILE: config/01commom/commodities.json
================================================
{
"CFFEX": {
"IC": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 1,
"pricetick": 0.2,
"volscale": 200,
"name": "֤",
"exchg": "CFFEX",
"session": "SD0930",
"holiday": "CHINA"
},
"IF": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 1,
"pricetick": 0.2,
"volscale": 300,
"name": "",
"exchg": "CFFEX",
"session": "SD0930",
"holiday": "CHINA"
},
"IH": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 1,
"pricetick": 0.2,
"volscale": 300,
"name": "֤",
"exchg": "CFFEX",
"session": "SD0930",
"holiday": "CHINA"
},
"T": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 3,
"pricetick": 0.005,
"volscale": 10000,
"name": "10ծ",
"exchg": "CFFEX",
"session": "FD0915",
"holiday": "CHINA"
},
"TF": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 3,
"pricetick": 0.005,
"volscale": 10000,
"name": "5ծ",
"exchg": "CFFEX",
"session": "FD0915",
"holiday": "CHINA"
},
"TS": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 3,
"pricetick": 0.005,
"volscale": 20000,
"name": "2ծ",
"exchg": "CFFEX",
"session": "FD0915",
"holiday": "CHINA"
}
},
"CZCE": {
"AP": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "ƻ",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"CF": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 5,
"name": "֣",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
},
"CJ": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 5,
"name": "",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"CY": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 5,
"name": "ɴ",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
},
"FG": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 20,
"name": "",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
},
"JR": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 20,
"name": "",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"LR": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 20,
"name": "̵",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"MA": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "֣",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
},
"OI": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
},
"PF": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 2.0,
"volscale": 5,
"name": "",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
},
"PK": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 2.0,
"volscale": 5,
"name": "",
"exchg": "CZCE",
"session": "",
"holiday": "CHINA"
},
"PM": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 50,
"name": "",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"RI": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 20,
"name": "̵",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"RM": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
},
"RS": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"SA": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 20,
"name": "",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
},
"SF": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 2.0,
"volscale": 5,
"name": "",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"SM": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 2.0,
"volscale": 5,
"name": "̹",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"SR": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
},
"TA": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 2.0,
"volscale": 5,
"name": "PTA",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
},
"UR": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 20,
"name": "",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"WH": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 20,
"name": "֣",
"exchg": "CZCE",
"session": "FD0900",
"holiday": "CHINA"
},
"ZC": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 1,
"pricetick": 0.2,
"volscale": 100,
"name": "֣ú",
"exchg": "CZCE",
"session": "FN2300",
"holiday": "CHINA"
}
},
"DCE": {
"a": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "һ",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"b": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"bb": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 2,
"pricetick": 0.05,
"volscale": 500,
"name": "",
"exchg": "DCE",
"session": "FD0900",
"holiday": "CHINA"
},
"c": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"cs": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"eb": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 5,
"name": "ϩ",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"eg": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "Ҷ",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"fb": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 1,
"pricetick": 0.5,
"volscale": 10,
"name": "˰",
"exchg": "DCE",
"session": "FD0900",
"holiday": "CHINA"
},
"i": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 1,
"pricetick": 0.5,
"volscale": 100,
"name": "ʯ",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"j": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 1,
"pricetick": 0.5,
"volscale": 100,
"name": "̿",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"jd": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "DCE",
"session": "FD0900",
"holiday": "CHINA"
},
"jm": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 1,
"pricetick": 0.5,
"volscale": 60,
"name": "ú",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"l": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 5,
"name": "",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"lh": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 16,
"name": "",
"exchg": "DCE",
"session": "FD0900",
"holiday": "CHINA"
},
"m": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"p": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 2.0,
"volscale": 10,
"name": "",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"pg": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 20,
"name": "Һ",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"pp": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 5,
"name": "۱ϩ",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"rr": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"v": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 5,
"name": "PVC",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
},
"y": {
"covermode": 0,
"pricemode": 0,
"category": 1,
"precision": 0,
"pricetick": 2.0,
"volscale": 10,
"name": "",
"exchg": "DCE",
"session": "FN2300",
"holiday": "CHINA"
}
},
"INE": {
"bc": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 10.0,
"volscale": 5,
"name": "ͭ",
"exchg": "INE",
"session": "FN0100",
"holiday": "CHINA"
},
"lu": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "INE",
"session": "FN2300",
"holiday": "CHINA"
},
"nr": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 10,
"name": "20",
"exchg": "INE",
"session": "FN0230",
"holiday": "CHINA"
},
"sc": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 1,
"pricetick": 0.1,
"volscale": 1000,
"name": "ԭ",
"exchg": "INE",
"session": "FN0230",
"holiday": "CHINA"
}
},
"SHFE": {
"ag": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 15,
"name": "",
"exchg": "SHFE",
"session": "FN0230",
"holiday": "CHINA"
},
"al": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 5,
"name": "",
"exchg": "SHFE",
"session": "FN0100",
"holiday": "CHINA"
},
"au": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 2,
"pricetick": 0.02,
"volscale": 1000,
"name": "",
"exchg": "SHFE",
"session": "FN0230",
"holiday": "CHINA"
},
"bu": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 2.0,
"volscale": 10,
"name": "",
"exchg": "SHFE",
"session": "FN2300",
"holiday": "CHINA"
},
"cu": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 10.0,
"volscale": 5,
"name": "ͭ",
"exchg": "SHFE",
"session": "FN0100",
"holiday": "CHINA"
},
"fu": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "ȼ",
"exchg": "SHFE",
"session": "FN2300",
"holiday": "CHINA"
},
"hc": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "Ⱦ",
"exchg": "SHFE",
"session": "FN2300",
"holiday": "CHINA"
},
"ni": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 10.0,
"volscale": 1,
"name": "",
"exchg": "SHFE",
"session": "FN0100",
"holiday": "CHINA"
},
"pb": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 5,
"name": "Ǧ",
"exchg": "SHFE",
"session": "FN0100",
"holiday": "CHINA"
},
"rb": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "",
"exchg": "SHFE",
"session": "FN2300",
"holiday": "CHINA"
},
"ru": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 10,
"name": "",
"exchg": "SHFE",
"session": "FN2300",
"holiday": "CHINA"
},
"sn": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 10.0,
"volscale": 1,
"name": "",
"exchg": "SHFE",
"session": "FN0100",
"holiday": "CHINA"
},
"sp": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 2.0,
"volscale": 10,
"name": "ֽ",
"exchg": "SHFE",
"session": "FN2300",
"holiday": "CHINA"
},
"ss": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 5,
"name": "",
"exchg": "SHFE",
"session": "FN0100",
"holiday": "CHINA"
},
"wr": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 1.0,
"volscale": 10,
"name": "߲",
"exchg": "SHFE",
"session": "FD0900",
"holiday": "CHINA"
},
"zn": {
"covermode": 1,
"pricemode": 1,
"category": 1,
"precision": 0,
"pricetick": 5.0,
"volscale": 5,
"name": "п",
"exchg": "SHFE",
"session": "FN0100",
"holiday": "CHINA"
}
}
}
================================================
FILE: config/01commom/contracts.json
================================================
{
"CFFEX": {
"IC2110": {
"name": "֤2110",
"code": "IC2110",
"exchg": "CFFEX",
"product": "IC",
"maxlimitqty": 20,
"maxmarketqty": 10
},
"IC2111": {
"name": "֤2111",
"code": "IC2111",
"exchg": "CFFEX",
"product": "IC",
"maxlimitqty": 20,
"maxmarketqty": 10
},
"IC2112": {
"name": "֤2112",
"code": "IC2112",
"exchg": "CFFEX",
"product": "IC",
"maxlimitqty": 20,
"maxmarketqty": 0
},
"IC2203": {
"name": "֤2203",
"code": "IC2203",
"exchg": "CFFEX",
"product": "IC",
"maxlimitqty": 20,
"maxmarketqty": 0
},
"IF2110": {
"name": "2110",
"code": "IF2110",
"exchg": "CFFEX",
"product": "IF",
"maxlimitqty": 20,
"maxmarketqty": 10
},
"IF2111": {
"name": "2111",
"code": "IF2111",
"exchg": "CFFEX",
"product": "IF",
"maxlimitqty": 20,
"maxmarketqty": 10
},
"IF2112": {
"name": "2112",
"code": "IF2112",
"exchg": "CFFEX",
"product": "IF",
"maxlimitqty": 20,
"maxmarketqty": 0
},
"IF2203": {
"name": "2203",
"code": "IF2203",
"exchg": "CFFEX",
"product": "IF",
"maxlimitqty": 20,
"maxmarketqty": 0
},
"IH2110": {
"name": "֤2110",
"code": "IH2110",
"exchg": "CFFEX",
"product": "IH",
"maxlimitqty": 20,
"maxmarketqty": 10
},
"IH2111": {
"name": "֤2111",
"code": "IH2111",
"exchg": "CFFEX",
"product": "IH",
"maxlimitqty": 20,
"maxmarketqty": 10
},
"IH2112": {
"name": "֤2112",
"code": "IH2112",
"exchg": "CFFEX",
"product": "IH",
"maxlimitqty": 20,
"maxmarketqty": 0
},
"IH2203": {
"name": "֤2203",
"code": "IH2203",
"exchg": "CFFEX",
"product": "IH",
"maxlimitqty": 20,
"maxmarketqty": 0
},
"T2112": {
"name": "10ծ2112",
"code": "T2112",
"exchg": "CFFEX",
"product": "T",
"maxlimitqty": 50,
"maxmarketqty": 0
},
"T2203": {
"name": "10ծ2203",
"code": "T2203",
"exchg": "CFFEX",
"product": "T",
"maxlimitqty": 50,
"maxmarketqty": 0
},
"T2206": {
"name": "10ծ2206",
"code": "T2206",
"exchg": "CFFEX",
"product": "T",
"maxlimitqty": 50,
"maxmarketqty": 0
},
"TF2112": {
"name": "5ծ2112",
"code": "TF2112",
"exchg": "CFFEX",
"product": "TF",
"maxlimitqty": 50,
"maxmarketqty": 0
},
"TF2203": {
"name": "5ծ2203",
"code": "TF2203",
"exchg": "CFFEX",
"product": "TF",
"maxlimitqty": 50,
"maxmarketqty": 0
},
"TF2206": {
"name": "5ծ2206",
"code": "TF2206",
"exchg": "CFFEX",
"product": "TF",
"maxlimitqty": 50,
"maxmarketqty": 0
},
"TS2112": {
"name": "2ծ2112",
"code": "TS2112",
"exchg": "CFFEX",
"product": "TS",
"maxlimitqty": 50,
"maxmarketqty": 0
},
"TS2203": {
"name": "2ծ2203",
"code": "TS2203",
"exchg": "CFFEX",
"product": "TS",
"maxlimitqty": 50,
"maxmarketqty": 0
},
"TS2206": {
"name": "2ծ2206",
"code": "TS2206",
"exchg": "CFFEX",
"product": "TS",
"maxlimitqty": 50,
"maxmarketqty": 0
}
},
"CZCE": {
"AP110": {
"name": "ƻ110",
"code": "AP110",
"exchg": "CZCE",
"product": "AP",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"AP111": {
"name": "ƻ111",
"code": "AP111",
"exchg": "CZCE",
"product": "AP",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"AP112": {
"name": "ƻ112",
"code": "AP112",
"exchg": "CZCE",
"product": "AP",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"AP201": {
"name": "ƻ201",
"code": "AP201",
"exchg": "CZCE",
"product": "AP",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"AP203": {
"name": "ƻ203",
"code": "AP203",
"exchg": "CZCE",
"product": "AP",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"AP204": {
"name": "ƻ204",
"code": "AP204",
"exchg": "CZCE",
"product": "AP",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"AP205": {
"name": "ƻ205",
"code": "AP205",
"exchg": "CZCE",
"product": "AP",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CF111": {
"name": "֣111",
"code": "CF111",
"exchg": "CZCE",
"product": "CF",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"CF201": {
"name": "֣201",
"code": "CF201",
"exchg": "CZCE",
"product": "CF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CF203": {
"name": "֣203",
"code": "CF203",
"exchg": "CZCE",
"product": "CF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CF205": {
"name": "֣205",
"code": "CF205",
"exchg": "CZCE",
"product": "CF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CF207": {
"name": "֣207",
"code": "CF207",
"exchg": "CZCE",
"product": "CF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CF209": {
"name": "֣209",
"code": "CF209",
"exchg": "CZCE",
"product": "CF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CJ112": {
"name": "112",
"code": "CJ112",
"exchg": "CZCE",
"product": "CJ",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"CJ201": {
"name": "201",
"code": "CJ201",
"exchg": "CZCE",
"product": "CJ",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CJ203": {
"name": "203",
"code": "CJ203",
"exchg": "CZCE",
"product": "CJ",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CJ205": {
"name": "205",
"code": "CJ205",
"exchg": "CZCE",
"product": "CJ",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CJ207": {
"name": "207",
"code": "CJ207",
"exchg": "CZCE",
"product": "CJ",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CJ209": {
"name": "209",
"code": "CJ209",
"exchg": "CZCE",
"product": "CJ",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CY110": {
"name": "ɴ110",
"code": "CY110",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"CY111": {
"name": "ɴ111",
"code": "CY111",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"CY112": {
"name": "ɴ112",
"code": "CY112",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"CY201": {
"name": "ɴ201",
"code": "CY201",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CY202": {
"name": "ɴ202",
"code": "CY202",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CY203": {
"name": "ɴ203",
"code": "CY203",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CY204": {
"name": "ɴ204",
"code": "CY204",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CY205": {
"name": "ɴ205",
"code": "CY205",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CY206": {
"name": "ɴ206",
"code": "CY206",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CY207": {
"name": "ɴ207",
"code": "CY207",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CY208": {
"name": "ɴ208",
"code": "CY208",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"CY209": {
"name": "ɴ209",
"code": "CY209",
"exchg": "CZCE",
"product": "CY",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"FG110": {
"name": "110",
"code": "FG110",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"FG111": {
"name": "111",
"code": "FG111",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"FG112": {
"name": "112",
"code": "FG112",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"FG201": {
"name": "201",
"code": "FG201",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"FG202": {
"name": "202",
"code": "FG202",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"FG203": {
"name": "203",
"code": "FG203",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"FG204": {
"name": "204",
"code": "FG204",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"FG205": {
"name": "205",
"code": "FG205",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"FG206": {
"name": "206",
"code": "FG206",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"FG207": {
"name": "207",
"code": "FG207",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"FG208": {
"name": "208",
"code": "FG208",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"FG209": {
"name": "209",
"code": "FG209",
"exchg": "CZCE",
"product": "FG",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"JR111": {
"name": "111",
"code": "JR111",
"exchg": "CZCE",
"product": "JR",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"JR201": {
"name": "201",
"code": "JR201",
"exchg": "CZCE",
"product": "JR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"JR203": {
"name": "203",
"code": "JR203",
"exchg": "CZCE",
"product": "JR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"JR205": {
"name": "205",
"code": "JR205",
"exchg": "CZCE",
"product": "JR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"JR207": {
"name": "207",
"code": "JR207",
"exchg": "CZCE",
"product": "JR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"JR209": {
"name": "209",
"code": "JR209",
"exchg": "CZCE",
"product": "JR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"LR111": {
"name": "̵111",
"code": "LR111",
"exchg": "CZCE",
"product": "LR",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"LR201": {
"name": "̵201",
"code": "LR201",
"exchg": "CZCE",
"product": "LR",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"LR203": {
"name": "̵203",
"code": "LR203",
"exchg": "CZCE",
"product": "LR",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"LR205": {
"name": "̵205",
"code": "LR205",
"exchg": "CZCE",
"product": "LR",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"LR207": {
"name": "̵207",
"code": "LR207",
"exchg": "CZCE",
"product": "LR",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"LR209": {
"name": "̵209",
"code": "LR209",
"exchg": "CZCE",
"product": "LR",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"MA110": {
"name": "֣110",
"code": "MA110",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"MA111": {
"name": "֣111",
"code": "MA111",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"MA112": {
"name": "֣112",
"code": "MA112",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"MA201": {
"name": "֣201",
"code": "MA201",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"MA202": {
"name": "֣202",
"code": "MA202",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"MA203": {
"name": "֣203",
"code": "MA203",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"MA204": {
"name": "֣204",
"code": "MA204",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"MA205": {
"name": "֣205",
"code": "MA205",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"MA206": {
"name": "֣206",
"code": "MA206",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"MA207": {
"name": "֣207",
"code": "MA207",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"MA208": {
"name": "֣208",
"code": "MA208",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"MA209": {
"name": "֣209",
"code": "MA209",
"exchg": "CZCE",
"product": "MA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"OI111": {
"name": "111",
"code": "OI111",
"exchg": "CZCE",
"product": "OI",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"OI201": {
"name": "201",
"code": "OI201",
"exchg": "CZCE",
"product": "OI",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"OI203": {
"name": "203",
"code": "OI203",
"exchg": "CZCE",
"product": "OI",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"OI205": {
"name": "205",
"code": "OI205",
"exchg": "CZCE",
"product": "OI",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"OI207": {
"name": "207",
"code": "OI207",
"exchg": "CZCE",
"product": "OI",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"OI209": {
"name": "209",
"code": "OI209",
"exchg": "CZCE",
"product": "OI",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"PF110": {
"name": "110",
"code": "PF110",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"PF111": {
"name": "111",
"code": "PF111",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"PF112": {
"name": "112",
"code": "PF112",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"PF201": {
"name": "201",
"code": "PF201",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PF202": {
"name": "202",
"code": "PF202",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PF203": {
"name": "203",
"code": "PF203",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PF204": {
"name": "204",
"code": "PF204",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PF205": {
"name": "205",
"code": "PF205",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PF206": {
"name": "206",
"code": "PF206",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PF207": {
"name": "207",
"code": "PF207",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PF208": {
"name": "208",
"code": "PF208",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PF209": {
"name": "209",
"code": "PF209",
"exchg": "CZCE",
"product": "PF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PK110": {
"name": "110",
"code": "PK110",
"exchg": "CZCE",
"product": "PK",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PK111": {
"name": "111",
"code": "PK111",
"exchg": "CZCE",
"product": "PK",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PK112": {
"name": "112",
"code": "PK112",
"exchg": "CZCE",
"product": "PK",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PK201": {
"name": "201",
"code": "PK201",
"exchg": "CZCE",
"product": "PK",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PK203": {
"name": "203",
"code": "PK203",
"exchg": "CZCE",
"product": "PK",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PK204": {
"name": "204",
"code": "PK204",
"exchg": "CZCE",
"product": "PK",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PM111": {
"name": "111",
"code": "PM111",
"exchg": "CZCE",
"product": "PM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"PM201": {
"name": "201",
"code": "PM201",
"exchg": "CZCE",
"product": "PM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PM203": {
"name": "203",
"code": "PM203",
"exchg": "CZCE",
"product": "PM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PM205": {
"name": "205",
"code": "PM205",
"exchg": "CZCE",
"product": "PM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PM207": {
"name": "207",
"code": "PM207",
"exchg": "CZCE",
"product": "PM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"PM209": {
"name": "209",
"code": "PM209",
"exchg": "CZCE",
"product": "PM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"RI111": {
"name": "̵111",
"code": "RI111",
"exchg": "CZCE",
"product": "RI",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RI201": {
"name": "̵201",
"code": "RI201",
"exchg": "CZCE",
"product": "RI",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"RI203": {
"name": "̵203",
"code": "RI203",
"exchg": "CZCE",
"product": "RI",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"RI205": {
"name": "̵205",
"code": "RI205",
"exchg": "CZCE",
"product": "RI",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"RI207": {
"name": "̵207",
"code": "RI207",
"exchg": "CZCE",
"product": "RI",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"RI209": {
"name": "̵209",
"code": "RI209",
"exchg": "CZCE",
"product": "RI",
"maxlimitqty": 1000,
"maxmarketqty": 500
},
"RM111": {
"name": "111",
"code": "RM111",
"exchg": "CZCE",
"product": "RM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RM201": {
"name": "201",
"code": "RM201",
"exchg": "CZCE",
"product": "RM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RM203": {
"name": "203",
"code": "RM203",
"exchg": "CZCE",
"product": "RM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RM205": {
"name": "205",
"code": "RM205",
"exchg": "CZCE",
"product": "RM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RM207": {
"name": "207",
"code": "RM207",
"exchg": "CZCE",
"product": "RM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RM208": {
"name": "208",
"code": "RM208",
"exchg": "CZCE",
"product": "RM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RM209": {
"name": "209",
"code": "RM209",
"exchg": "CZCE",
"product": "RM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RS111": {
"name": "111",
"code": "RS111",
"exchg": "CZCE",
"product": "RS",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RS207": {
"name": "207",
"code": "RS207",
"exchg": "CZCE",
"product": "RS",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RS208": {
"name": "208",
"code": "RS208",
"exchg": "CZCE",
"product": "RS",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"RS209": {
"name": "209",
"code": "RS209",
"exchg": "CZCE",
"product": "RS",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SA110": {
"name": "110",
"code": "SA110",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SA111": {
"name": "111",
"code": "SA111",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SA112": {
"name": "112",
"code": "SA112",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SA201": {
"name": "201",
"code": "SA201",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SA202": {
"name": "202",
"code": "SA202",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SA203": {
"name": "203",
"code": "SA203",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SA204": {
"name": "204",
"code": "SA204",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SA205": {
"name": "205",
"code": "SA205",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SA206": {
"name": "206",
"code": "SA206",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SA207": {
"name": "207",
"code": "SA207",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SA208": {
"name": "208",
"code": "SA208",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SA209": {
"name": "209",
"code": "SA209",
"exchg": "CZCE",
"product": "SA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SF110": {
"name": "110",
"code": "SF110",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SF111": {
"name": "111",
"code": "SF111",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SF112": {
"name": "112",
"code": "SF112",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SF201": {
"name": "201",
"code": "SF201",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SF202": {
"name": "202",
"code": "SF202",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SF203": {
"name": "203",
"code": "SF203",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SF204": {
"name": "204",
"code": "SF204",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SF205": {
"name": "205",
"code": "SF205",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SF206": {
"name": "206",
"code": "SF206",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SF207": {
"name": "207",
"code": "SF207",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SF208": {
"name": "208",
"code": "SF208",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SF209": {
"name": "209",
"code": "SF209",
"exchg": "CZCE",
"product": "SF",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SM110": {
"name": "̹110",
"code": "SM110",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SM111": {
"name": "̹111",
"code": "SM111",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SM112": {
"name": "̹112",
"code": "SM112",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SM201": {
"name": "̹201",
"code": "SM201",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SM202": {
"name": "̹202",
"code": "SM202",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SM203": {
"name": "̹203",
"code": "SM203",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SM204": {
"name": "̹204",
"code": "SM204",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SM205": {
"name": "̹205",
"code": "SM205",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SM206": {
"name": "̹206",
"code": "SM206",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SM207": {
"name": "̹207",
"code": "SM207",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SM208": {
"name": "̹208",
"code": "SM208",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SM209": {
"name": "̹209",
"code": "SM209",
"exchg": "CZCE",
"product": "SM",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SR111": {
"name": "111",
"code": "SR111",
"exchg": "CZCE",
"product": "SR",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"SR201": {
"name": "201",
"code": "SR201",
"exchg": "CZCE",
"product": "SR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SR203": {
"name": "203",
"code": "SR203",
"exchg": "CZCE",
"product": "SR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SR205": {
"name": "205",
"code": "SR205",
"exchg": "CZCE",
"product": "SR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SR207": {
"name": "207",
"code": "SR207",
"exchg": "CZCE",
"product": "SR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"SR209": {
"name": "209",
"code": "SR209",
"exchg": "CZCE",
"product": "SR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"TA110": {
"name": "PTA110",
"code": "TA110",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"TA111": {
"name": "PTA111",
"code": "TA111",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"TA112": {
"name": "PTA112",
"code": "TA112",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"TA201": {
"name": "PTA201",
"code": "TA201",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"TA202": {
"name": "PTA202",
"code": "TA202",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"TA203": {
"name": "PTA203",
"code": "TA203",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"TA204": {
"name": "PTA204",
"code": "TA204",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"TA205": {
"name": "PTA205",
"code": "TA205",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"TA206": {
"name": "PTA206",
"code": "TA206",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"TA207": {
"name": "PTA207",
"code": "TA207",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"TA208": {
"name": "PTA208",
"code": "TA208",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"TA209": {
"name": "PTA209",
"code": "TA209",
"exchg": "CZCE",
"product": "TA",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"UR110": {
"name": "110",
"code": "UR110",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"UR111": {
"name": "111",
"code": "UR111",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"UR112": {
"name": "112",
"code": "UR112",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"UR201": {
"name": "201",
"code": "UR201",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"UR202": {
"name": "202",
"code": "UR202",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"UR203": {
"name": "203",
"code": "UR203",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"UR204": {
"name": "204",
"code": "UR204",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"UR205": {
"name": "205",
"code": "UR205",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"UR206": {
"name": "206",
"code": "UR206",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"UR207": {
"name": "207",
"code": "UR207",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"UR208": {
"name": "208",
"code": "UR208",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"UR209": {
"name": "209",
"code": "UR209",
"exchg": "CZCE",
"product": "UR",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"WH111": {
"name": "֣111",
"code": "WH111",
"exchg": "CZCE",
"product": "WH",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"WH201": {
"name": "֣201",
"code": "WH201",
"exchg": "CZCE",
"product": "WH",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"WH203": {
"name": "֣203",
"code": "WH203",
"exchg": "CZCE",
"product": "WH",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"WH205": {
"name": "֣205",
"code": "WH205",
"exchg": "CZCE",
"product": "WH",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"WH207": {
"name": "֣207",
"code": "WH207",
"exchg": "CZCE",
"product": "WH",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"WH209": {
"name": "֣209",
"code": "WH209",
"exchg": "CZCE",
"product": "WH",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"ZC110": {
"name": "֣ú110",
"code": "ZC110",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"ZC111": {
"name": "֣ú111",
"code": "ZC111",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"ZC112": {
"name": "֣ú112",
"code": "ZC112",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"ZC201": {
"name": "֣ú201",
"code": "ZC201",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"ZC202": {
"name": "֣ú202",
"code": "ZC202",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"ZC203": {
"name": "֣ú203",
"code": "ZC203",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"ZC204": {
"name": "֣ú204",
"code": "ZC204",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"ZC205": {
"name": "֣ú205",
"code": "ZC205",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"ZC206": {
"name": "֣ú206",
"code": "ZC206",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"ZC207": {
"name": "֣ú207",
"code": "ZC207",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"ZC208": {
"name": "֣ú208",
"code": "ZC208",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 200
},
"ZC209": {
"name": "֣ú209",
"code": "ZC209",
"exchg": "CZCE",
"product": "ZC",
"maxlimitqty": 1000,
"maxmarketqty": 200
}
},
"DCE": {
"a2111": {
"name": "һ2111",
"code": "a2111",
"exchg": "DCE",
"product": "a",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"a2201": {
"name": "һ2201",
"code": "a2201",
"exchg": "DCE",
"product": "a",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"a2203": {
"name": "һ2203",
"code": "a2203",
"exchg": "DCE",
"product": "a",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"a2205": {
"name": "һ2205",
"code": "a2205",
"exchg": "DCE",
"product": "a",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"a2207": {
"name": "һ2207",
"code": "a2207",
"exchg": "DCE",
"product": "a",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"a2209": {
"name": "һ2209",
"code": "a2209",
"exchg": "DCE",
"product": "a",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2110": {
"name": "2110",
"code": "b2110",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2111": {
"name": "2111",
"code": "b2111",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2112": {
"name": "2112",
"code": "b2112",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2201": {
"name": "2201",
"code": "b2201",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2202": {
"name": "2202",
"code": "b2202",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2203": {
"name": "2203",
"code": "b2203",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2204": {
"name": "2204",
"code": "b2204",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2205": {
"name": "2205",
"code": "b2205",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2206": {
"name": "2206",
"code": "b2206",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2207": {
"name": "2207",
"code": "b2207",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2208": {
"name": "2208",
"code": "b2208",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"b2209": {
"name": "2209",
"code": "b2209",
"exchg": "DCE",
"product": "b",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2110": {
"name": "2110",
"code": "bb2110",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2111": {
"name": "2111",
"code": "bb2111",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2112": {
"name": "2112",
"code": "bb2112",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2201": {
"name": "2201",
"code": "bb2201",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2202": {
"name": "2202",
"code": "bb2202",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2203": {
"name": "2203",
"code": "bb2203",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2204": {
"name": "2204",
"code": "bb2204",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2205": {
"name": "2205",
"code": "bb2205",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2206": {
"name": "2206",
"code": "bb2206",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2207": {
"name": "2207",
"code": "bb2207",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2208": {
"name": "2208",
"code": "bb2208",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"bb2209": {
"name": "2209",
"code": "bb2209",
"exchg": "DCE",
"product": "bb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"c2111": {
"name": "2111",
"code": "c2111",
"exchg": "DCE",
"product": "c",
"maxlimitqty": 2000,
"maxmarketqty": 2000
},
"c2201": {
"name": "2201",
"code": "c2201",
"exchg": "DCE",
"product": "c",
"maxlimitqty": 2000,
"maxmarketqty": 2000
},
"c2203": {
"name": "2203",
"code": "c2203",
"exchg": "DCE",
"product": "c",
"maxlimitqty": 2000,
"maxmarketqty": 2000
},
"c2205": {
"name": "2205",
"code": "c2205",
"exchg": "DCE",
"product": "c",
"maxlimitqty": 2000,
"maxmarketqty": 2000
},
"c2207": {
"name": "2207",
"code": "c2207",
"exchg": "DCE",
"product": "c",
"maxlimitqty": 2000,
"maxmarketqty": 2000
},
"c2209": {
"name": "2209",
"code": "c2209",
"exchg": "DCE",
"product": "c",
"maxlimitqty": 2000,
"maxmarketqty": 2000
},
"cs2111": {
"name": "2111",
"code": "cs2111",
"exchg": "DCE",
"product": "cs",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"cs2201": {
"name": "2201",
"code": "cs2201",
"exchg": "DCE",
"product": "cs",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"cs2203": {
"name": "2203",
"code": "cs2203",
"exchg": "DCE",
"product": "cs",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"cs2205": {
"name": "2205",
"code": "cs2205",
"exchg": "DCE",
"product": "cs",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"cs2207": {
"name": "2207",
"code": "cs2207",
"exchg": "DCE",
"product": "cs",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"cs2209": {
"name": "2209",
"code": "cs2209",
"exchg": "DCE",
"product": "cs",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2110": {
"name": "ϩ2110",
"code": "eb2110",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2111": {
"name": "ϩ2111",
"code": "eb2111",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2112": {
"name": "ϩ2112",
"code": "eb2112",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2201": {
"name": "ϩ2201",
"code": "eb2201",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2202": {
"name": "ϩ2202",
"code": "eb2202",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2203": {
"name": "ϩ2203",
"code": "eb2203",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2204": {
"name": "ϩ2204",
"code": "eb2204",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2205": {
"name": "ϩ2205",
"code": "eb2205",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2206": {
"name": "ϩ2206",
"code": "eb2206",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2207": {
"name": "ϩ2207",
"code": "eb2207",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2208": {
"name": "ϩ2208",
"code": "eb2208",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eb2209": {
"name": "ϩ2209",
"code": "eb2209",
"exchg": "DCE",
"product": "eb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2110": {
"name": "Ҷ2110",
"code": "eg2110",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2111": {
"name": "Ҷ2111",
"code": "eg2111",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2112": {
"name": "Ҷ2112",
"code": "eg2112",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2201": {
"name": "Ҷ2201",
"code": "eg2201",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2202": {
"name": "Ҷ2202",
"code": "eg2202",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2203": {
"name": "Ҷ2203",
"code": "eg2203",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2204": {
"name": "Ҷ2204",
"code": "eg2204",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2205": {
"name": "Ҷ2205",
"code": "eg2205",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2206": {
"name": "Ҷ2206",
"code": "eg2206",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2207": {
"name": "Ҷ2207",
"code": "eg2207",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2208": {
"name": "Ҷ2208",
"code": "eg2208",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"eg2209": {
"name": "Ҷ2209",
"code": "eg2209",
"exchg": "DCE",
"product": "eg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2110": {
"name": "˰2110",
"code": "fb2110",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2111": {
"name": "˰2111",
"code": "fb2111",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2112": {
"name": "˰2112",
"code": "fb2112",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2201": {
"name": "˰2201",
"code": "fb2201",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2202": {
"name": "˰2202",
"code": "fb2202",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2203": {
"name": "˰2203",
"code": "fb2203",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2204": {
"name": "˰2204",
"code": "fb2204",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2205": {
"name": "˰2205",
"code": "fb2205",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2206": {
"name": "˰2206",
"code": "fb2206",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2207": {
"name": "˰2207",
"code": "fb2207",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2208": {
"name": "˰2208",
"code": "fb2208",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"fb2209": {
"name": "˰2209",
"code": "fb2209",
"exchg": "DCE",
"product": "fb",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2110": {
"name": "ʯ2110",
"code": "i2110",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2111": {
"name": "ʯ2111",
"code": "i2111",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2112": {
"name": "ʯ2112",
"code": "i2112",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2201": {
"name": "ʯ2201",
"code": "i2201",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2202": {
"name": "ʯ2202",
"code": "i2202",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2203": {
"name": "ʯ2203",
"code": "i2203",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2204": {
"name": "ʯ2204",
"code": "i2204",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2205": {
"name": "ʯ2205",
"code": "i2205",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2206": {
"name": "ʯ2206",
"code": "i2206",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2207": {
"name": "ʯ2207",
"code": "i2207",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2208": {
"name": "ʯ2208",
"code": "i2208",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"i2209": {
"name": "ʯ2209",
"code": "i2209",
"exchg": "DCE",
"product": "i",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"j2110": {
"name": "̿2110",
"code": "j2110",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2111": {
"name": "̿2111",
"code": "j2111",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2112": {
"name": "̿2112",
"code": "j2112",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2201": {
"name": "̿2201",
"code": "j2201",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2202": {
"name": "̿2202",
"code": "j2202",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2203": {
"name": "̿2203",
"code": "j2203",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2204": {
"name": "̿2204",
"code": "j2204",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2205": {
"name": "̿2205",
"code": "j2205",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2206": {
"name": "̿2206",
"code": "j2206",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2207": {
"name": "̿2207",
"code": "j2207",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2208": {
"name": "̿2208",
"code": "j2208",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"j2209": {
"name": "̿2209",
"code": "j2209",
"exchg": "DCE",
"product": "j",
"maxlimitqty": 500,
"maxmarketqty": 500
},
"jd2110": {
"name": "2110",
"code": "jd2110",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2111": {
"name": "2111",
"code": "jd2111",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2112": {
"name": "2112",
"code": "jd2112",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2201": {
"name": "2201",
"code": "jd2201",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2202": {
"name": "2202",
"code": "jd2202",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2203": {
"name": "2203",
"code": "jd2203",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2204": {
"name": "2204",
"code": "jd2204",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2205": {
"name": "2205",
"code": "jd2205",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2206": {
"name": "2206",
"code": "jd2206",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2207": {
"name": "2207",
"code": "jd2207",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2208": {
"name": "2208",
"code": "jd2208",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jd2209": {
"name": "2209",
"code": "jd2209",
"exchg": "DCE",
"product": "jd",
"maxlimitqty": 300,
"maxmarketqty": 300
},
"jm2110": {
"name": "ú2110",
"code": "jm2110",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2111": {
"name": "ú2111",
"code": "jm2111",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2112": {
"name": "ú2112",
"code": "jm2112",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2201": {
"name": "ú2201",
"code": "jm2201",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2202": {
"name": "ú2202",
"code": "jm2202",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2203": {
"name": "ú2203",
"code": "jm2203",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2204": {
"name": "ú2204",
"code": "jm2204",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2205": {
"name": "ú2205",
"code": "jm2205",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2206": {
"name": "ú2206",
"code": "jm2206",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2207": {
"name": "ú2207",
"code": "jm2207",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2208": {
"name": "ú2208",
"code": "jm2208",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"jm2209": {
"name": "ú2209",
"code": "jm2209",
"exchg": "DCE",
"product": "jm",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2110": {
"name": "2110",
"code": "l2110",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2111": {
"name": "2111",
"code": "l2111",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2112": {
"name": "2112",
"code": "l2112",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2201": {
"name": "2201",
"code": "l2201",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2202": {
"name": "2202",
"code": "l2202",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2203": {
"name": "2203",
"code": "l2203",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2204": {
"name": "2204",
"code": "l2204",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2205": {
"name": "2205",
"code": "l2205",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2206": {
"name": "2206",
"code": "l2206",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2207": {
"name": "2207",
"code": "l2207",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2208": {
"name": "2208",
"code": "l2208",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"l2209": {
"name": "2209",
"code": "l2209",
"exchg": "DCE",
"product": "l",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"lh2111": {
"name": "2111",
"code": "lh2111",
"exchg": "DCE",
"product": "lh",
"maxlimitqty": 50,
"maxmarketqty": 50
},
"lh2201": {
"name": "2201",
"code": "lh2201",
"exchg": "DCE",
"product": "lh",
"maxlimitqty": 50,
"maxmarketqty": 50
},
"lh2203": {
"name": "2203",
"code": "lh2203",
"exchg": "DCE",
"product": "lh",
"maxlimitqty": 50,
"maxmarketqty": 50
},
"lh2205": {
"name": "2205",
"code": "lh2205",
"exchg": "DCE",
"product": "lh",
"maxlimitqty": 50,
"maxmarketqty": 50
},
"lh2207": {
"name": "2207",
"code": "lh2207",
"exchg": "DCE",
"product": "lh",
"maxlimitqty": 50,
"maxmarketqty": 50
},
"lh2209": {
"name": "2209",
"code": "lh2209",
"exchg": "DCE",
"product": "lh",
"maxlimitqty": 50,
"maxmarketqty": 50
},
"m2111": {
"name": "2111",
"code": "m2111",
"exchg": "DCE",
"product": "m",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"m2112": {
"name": "2112",
"code": "m2112",
"exchg": "DCE",
"product": "m",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"m2201": {
"name": "2201",
"code": "m2201",
"exchg": "DCE",
"product": "m",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"m2203": {
"name": "2203",
"code": "m2203",
"exchg": "DCE",
"product": "m",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"m2205": {
"name": "2205",
"code": "m2205",
"exchg": "DCE",
"product": "m",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"m2207": {
"name": "2207",
"code": "m2207",
"exchg": "DCE",
"product": "m",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"m2208": {
"name": "2208",
"code": "m2208",
"exchg": "DCE",
"product": "m",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"m2209": {
"name": "2209",
"code": "m2209",
"exchg": "DCE",
"product": "m",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2110": {
"name": "2110",
"code": "p2110",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2111": {
"name": "2111",
"code": "p2111",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2112": {
"name": "2112",
"code": "p2112",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2201": {
"name": "2201",
"code": "p2201",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2202": {
"name": "2202",
"code": "p2202",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2203": {
"name": "2203",
"code": "p2203",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2204": {
"name": "2204",
"code": "p2204",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2205": {
"name": "2205",
"code": "p2205",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2206": {
"name": "2206",
"code": "p2206",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2207": {
"name": "2207",
"code": "p2207",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2208": {
"name": "2208",
"code": "p2208",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"p2209": {
"name": "2209",
"code": "p2209",
"exchg": "DCE",
"product": "p",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2110": {
"name": "Һ2110",
"code": "pg2110",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2111": {
"name": "Һ2111",
"code": "pg2111",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2112": {
"name": "Һ2112",
"code": "pg2112",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2201": {
"name": "Һ2201",
"code": "pg2201",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2202": {
"name": "Һ2202",
"code": "pg2202",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2203": {
"name": "Һ2203",
"code": "pg2203",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2204": {
"name": "Һ2204",
"code": "pg2204",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2205": {
"name": "Һ2205",
"code": "pg2205",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2206": {
"name": "Һ2206",
"code": "pg2206",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2207": {
"name": "Һ2207",
"code": "pg2207",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2208": {
"name": "Һ2208",
"code": "pg2208",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pg2209": {
"name": "Һ2209",
"code": "pg2209",
"exchg": "DCE",
"product": "pg",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2110": {
"name": "۱ϩ2110",
"code": "pp2110",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2111": {
"name": "۱ϩ2111",
"code": "pp2111",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2112": {
"name": "۱ϩ2112",
"code": "pp2112",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2201": {
"name": "۱ϩ2201",
"code": "pp2201",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2202": {
"name": "۱ϩ2202",
"code": "pp2202",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2203": {
"name": "۱ϩ2203",
"code": "pp2203",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2204": {
"name": "۱ϩ2204",
"code": "pp2204",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2205": {
"name": "۱ϩ2205",
"code": "pp2205",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2206": {
"name": "۱ϩ2206",
"code": "pp2206",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2207": {
"name": "۱ϩ2207",
"code": "pp2207",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2208": {
"name": "۱ϩ2208",
"code": "pp2208",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"pp2209": {
"name": "۱ϩ2209",
"code": "pp2209",
"exchg": "DCE",
"product": "pp",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2110": {
"name": "2110",
"code": "rr2110",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2111": {
"name": "2111",
"code": "rr2111",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2112": {
"name": "2112",
"code": "rr2112",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2201": {
"name": "2201",
"code": "rr2201",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2202": {
"name": "2202",
"code": "rr2202",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2203": {
"name": "2203",
"code": "rr2203",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2204": {
"name": "2204",
"code": "rr2204",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2205": {
"name": "2205",
"code": "rr2205",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2206": {
"name": "2206",
"code": "rr2206",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2207": {
"name": "2207",
"code": "rr2207",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2208": {
"name": "2208",
"code": "rr2208",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"rr2209": {
"name": "2209",
"code": "rr2209",
"exchg": "DCE",
"product": "rr",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2110": {
"name": "PVC2110",
"code": "v2110",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2111": {
"name": "PVC2111",
"code": "v2111",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2112": {
"name": "PVC2112",
"code": "v2112",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2201": {
"name": "PVC2201",
"code": "v2201",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2202": {
"name": "PVC2202",
"code": "v2202",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2203": {
"name": "PVC2203",
"code": "v2203",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2204": {
"name": "PVC2204",
"code": "v2204",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2205": {
"name": "PVC2205",
"code": "v2205",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2206": {
"name": "PVC2206",
"code": "v2206",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2207": {
"name": "PVC2207",
"code": "v2207",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2208": {
"name": "PVC2208",
"code": "v2208",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"v2209": {
"name": "PVC2209",
"code": "v2209",
"exchg": "DCE",
"product": "v",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"y2111": {
"name": "2111",
"code": "y2111",
"exchg": "DCE",
"product": "y",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"y2112": {
"name": "2112",
"code": "y2112",
"exchg": "DCE",
"product": "y",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"y2201": {
"name": "2201",
"code": "y2201",
"exchg": "DCE",
"product": "y",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"y2203": {
"name": "2203",
"code": "y2203",
"exchg": "DCE",
"product": "y",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"y2205": {
"name": "2205",
"code": "y2205",
"exchg": "DCE",
"product": "y",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"y2207": {
"name": "2207",
"code": "y2207",
"exchg": "DCE",
"product": "y",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"y2208": {
"name": "2208",
"code": "y2208",
"exchg": "DCE",
"product": "y",
"maxlimitqty": 1000,
"maxmarketqty": 1000
},
"y2209": {
"name": "2209",
"code": "y2209",
"exchg": "DCE",
"product": "y",
"maxlimitqty": 1000,
"maxmarketqty": 1000
}
},
"INE": {
"bc2110": {
"name": "ͭ2110",
"code": "bc2110",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2111": {
"name": "ͭ2111",
"code": "bc2111",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2112": {
"name": "ͭ2112",
"code": "bc2112",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2201": {
"name": "ͭ2201",
"code": "bc2201",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2202": {
"name": "ͭ2202",
"code": "bc2202",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2203": {
"name": "ͭ2203",
"code": "bc2203",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2204": {
"name": "ͭ2204",
"code": "bc2204",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2205": {
"name": "ͭ2205",
"code": "bc2205",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2206": {
"name": "ͭ2206",
"code": "bc2206",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2207": {
"name": "ͭ2207",
"code": "bc2207",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2208": {
"name": "ͭ2208",
"code": "bc2208",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bc2209": {
"name": "ͭ2209",
"code": "bc2209",
"exchg": "INE",
"product": "bc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2111": {
"name": "2111",
"code": "lu2111",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2112": {
"name": "2112",
"code": "lu2112",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2201": {
"name": "2201",
"code": "lu2201",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2202": {
"name": "2202",
"code": "lu2202",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2203": {
"name": "2203",
"code": "lu2203",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2204": {
"name": "2204",
"code": "lu2204",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2205": {
"name": "2205",
"code": "lu2205",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2206": {
"name": "2206",
"code": "lu2206",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2207": {
"name": "2207",
"code": "lu2207",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2208": {
"name": "2208",
"code": "lu2208",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2209": {
"name": "2209",
"code": "lu2209",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"lu2210": {
"name": "2210",
"code": "lu2210",
"exchg": "INE",
"product": "lu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2110": {
"name": "202110",
"code": "nr2110",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2111": {
"name": "202111",
"code": "nr2111",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2112": {
"name": "202112",
"code": "nr2112",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2201": {
"name": "202201",
"code": "nr2201",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2202": {
"name": "202202",
"code": "nr2202",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2203": {
"name": "202203",
"code": "nr2203",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2204": {
"name": "202204",
"code": "nr2204",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2205": {
"name": "202205",
"code": "nr2205",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2206": {
"name": "202206",
"code": "nr2206",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2207": {
"name": "202207",
"code": "nr2207",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2208": {
"name": "202208",
"code": "nr2208",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"nr2209": {
"name": "202209",
"code": "nr2209",
"exchg": "INE",
"product": "nr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2111": {
"name": "ԭ2111",
"code": "sc2111",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2112": {
"name": "ԭ2112",
"code": "sc2112",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2201": {
"name": "ԭ2201",
"code": "sc2201",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2202": {
"name": "ԭ2202",
"code": "sc2202",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2203": {
"name": "ԭ2203",
"code": "sc2203",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2204": {
"name": "ԭ2204",
"code": "sc2204",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2205": {
"name": "ԭ2205",
"code": "sc2205",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2206": {
"name": "ԭ2206",
"code": "sc2206",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2207": {
"name": "ԭ2207",
"code": "sc2207",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2208": {
"name": "ԭ2208",
"code": "sc2208",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2209": {
"name": "ԭ2209",
"code": "sc2209",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2210": {
"name": "ԭ2210",
"code": "sc2210",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2212": {
"name": "ԭ2212",
"code": "sc2212",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2303": {
"name": "ԭ2303",
"code": "sc2303",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2306": {
"name": "ԭ2306",
"code": "sc2306",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2309": {
"name": "ԭ2309",
"code": "sc2309",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2312": {
"name": "ԭ2312",
"code": "sc2312",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2403": {
"name": "ԭ2403",
"code": "sc2403",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2406": {
"name": "ԭ2406",
"code": "sc2406",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sc2409": {
"name": "ԭ2409",
"code": "sc2409",
"exchg": "INE",
"product": "sc",
"maxlimitqty": 500,
"maxmarketqty": 30
}
},
"SHFE": {
"ag2110": {
"name": "2110",
"code": "ag2110",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2111": {
"name": "2111",
"code": "ag2111",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2112": {
"name": "2112",
"code": "ag2112",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2201": {
"name": "2201",
"code": "ag2201",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2202": {
"name": "2202",
"code": "ag2202",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2203": {
"name": "2203",
"code": "ag2203",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2204": {
"name": "2204",
"code": "ag2204",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2205": {
"name": "2205",
"code": "ag2205",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2206": {
"name": "2206",
"code": "ag2206",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2207": {
"name": "2207",
"code": "ag2207",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2208": {
"name": "2208",
"code": "ag2208",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ag2209": {
"name": "2209",
"code": "ag2209",
"exchg": "SHFE",
"product": "ag",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2110": {
"name": "2110",
"code": "al2110",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2111": {
"name": "2111",
"code": "al2111",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2112": {
"name": "2112",
"code": "al2112",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2201": {
"name": "2201",
"code": "al2201",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2202": {
"name": "2202",
"code": "al2202",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2203": {
"name": "2203",
"code": "al2203",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2204": {
"name": "2204",
"code": "al2204",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2205": {
"name": "2205",
"code": "al2205",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2206": {
"name": "2206",
"code": "al2206",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2207": {
"name": "2207",
"code": "al2207",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2208": {
"name": "2208",
"code": "al2208",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"al2209": {
"name": "2209",
"code": "al2209",
"exchg": "SHFE",
"product": "al",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"au2110": {
"name": "2110",
"code": "au2110",
"exchg": "SHFE",
"product": "au",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"au2111": {
"name": "2111",
"code": "au2111",
"exchg": "SHFE",
"product": "au",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"au2112": {
"name": "2112",
"code": "au2112",
"exchg": "SHFE",
"product": "au",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"au2202": {
"name": "2202",
"code": "au2202",
"exchg": "SHFE",
"product": "au",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"au2204": {
"name": "2204",
"code": "au2204",
"exchg": "SHFE",
"product": "au",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"au2206": {
"name": "2206",
"code": "au2206",
"exchg": "SHFE",
"product": "au",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"au2208": {
"name": "2208",
"code": "au2208",
"exchg": "SHFE",
"product": "au",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"au2210": {
"name": "2210",
"code": "au2210",
"exchg": "SHFE",
"product": "au",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2110": {
"name": "2110",
"code": "bu2110",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2111": {
"name": "2111",
"code": "bu2111",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2112": {
"name": "2112",
"code": "bu2112",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2201": {
"name": "2201",
"code": "bu2201",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2202": {
"name": "2202",
"code": "bu2202",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2203": {
"name": "2203",
"code": "bu2203",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2206": {
"name": "2206",
"code": "bu2206",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2209": {
"name": "2209",
"code": "bu2209",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2212": {
"name": "2212",
"code": "bu2212",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2303": {
"name": "2303",
"code": "bu2303",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2306": {
"name": "2306",
"code": "bu2306",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"bu2309": {
"name": "2309",
"code": "bu2309",
"exchg": "SHFE",
"product": "bu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2110": {
"name": "ͭ2110",
"code": "cu2110",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2111": {
"name": "ͭ2111",
"code": "cu2111",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2112": {
"name": "ͭ2112",
"code": "cu2112",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2201": {
"name": "ͭ2201",
"code": "cu2201",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2202": {
"name": "ͭ2202",
"code": "cu2202",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2203": {
"name": "ͭ2203",
"code": "cu2203",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2204": {
"name": "ͭ2204",
"code": "cu2204",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2205": {
"name": "ͭ2205",
"code": "cu2205",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2206": {
"name": "ͭ2206",
"code": "cu2206",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2207": {
"name": "ͭ2207",
"code": "cu2207",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2208": {
"name": "ͭ2208",
"code": "cu2208",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"cu2209": {
"name": "ͭ2209",
"code": "cu2209",
"exchg": "SHFE",
"product": "cu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2111": {
"name": "ȼ2111",
"code": "fu2111",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2112": {
"name": "ȼ2112",
"code": "fu2112",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2201": {
"name": "ȼ2201",
"code": "fu2201",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2202": {
"name": "ȼ2202",
"code": "fu2202",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2203": {
"name": "ȼ2203",
"code": "fu2203",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2204": {
"name": "ȼ2204",
"code": "fu2204",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2205": {
"name": "ȼ2205",
"code": "fu2205",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2206": {
"name": "ȼ2206",
"code": "fu2206",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2207": {
"name": "ȼ2207",
"code": "fu2207",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2208": {
"name": "ȼ2208",
"code": "fu2208",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2209": {
"name": "ȼ2209",
"code": "fu2209",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"fu2210": {
"name": "ȼ2210",
"code": "fu2210",
"exchg": "SHFE",
"product": "fu",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2110": {
"name": "Ⱦ2110",
"code": "hc2110",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2111": {
"name": "Ⱦ2111",
"code": "hc2111",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2112": {
"name": "Ⱦ2112",
"code": "hc2112",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2201": {
"name": "Ⱦ2201",
"code": "hc2201",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2202": {
"name": "Ⱦ2202",
"code": "hc2202",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2203": {
"name": "Ⱦ2203",
"code": "hc2203",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2204": {
"name": "Ⱦ2204",
"code": "hc2204",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2205": {
"name": "Ⱦ2205",
"code": "hc2205",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2206": {
"name": "Ⱦ2206",
"code": "hc2206",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2207": {
"name": "Ⱦ2207",
"code": "hc2207",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2208": {
"name": "Ⱦ2208",
"code": "hc2208",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"hc2209": {
"name": "Ⱦ2209",
"code": "hc2209",
"exchg": "SHFE",
"product": "hc",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2110": {
"name": "2110",
"code": "ni2110",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2111": {
"name": "2111",
"code": "ni2111",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2112": {
"name": "2112",
"code": "ni2112",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2201": {
"name": "2201",
"code": "ni2201",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2202": {
"name": "2202",
"code": "ni2202",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2203": {
"name": "2203",
"code": "ni2203",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2204": {
"name": "2204",
"code": "ni2204",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2205": {
"name": "2205",
"code": "ni2205",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2206": {
"name": "2206",
"code": "ni2206",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2207": {
"name": "2207",
"code": "ni2207",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2208": {
"name": "2208",
"code": "ni2208",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ni2209": {
"name": "2209",
"code": "ni2209",
"exchg": "SHFE",
"product": "ni",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2110": {
"name": "Ǧ2110",
"code": "pb2110",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2111": {
"name": "Ǧ2111",
"code": "pb2111",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2112": {
"name": "Ǧ2112",
"code": "pb2112",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2201": {
"name": "Ǧ2201",
"code": "pb2201",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2202": {
"name": "Ǧ2202",
"code": "pb2202",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2203": {
"name": "Ǧ2203",
"code": "pb2203",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2204": {
"name": "Ǧ2204",
"code": "pb2204",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2205": {
"name": "Ǧ2205",
"code": "pb2205",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2206": {
"name": "Ǧ2206",
"code": "pb2206",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2207": {
"name": "Ǧ2207",
"code": "pb2207",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2208": {
"name": "Ǧ2208",
"code": "pb2208",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"pb2209": {
"name": "Ǧ2209",
"code": "pb2209",
"exchg": "SHFE",
"product": "pb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2110": {
"name": "2110",
"code": "rb2110",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2111": {
"name": "2111",
"code": "rb2111",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2112": {
"name": "2112",
"code": "rb2112",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2201": {
"name": "2201",
"code": "rb2201",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2202": {
"name": "2202",
"code": "rb2202",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2203": {
"name": "2203",
"code": "rb2203",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2204": {
"name": "2204",
"code": "rb2204",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2205": {
"name": "2205",
"code": "rb2205",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2206": {
"name": "2206",
"code": "rb2206",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2207": {
"name": "2207",
"code": "rb2207",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2208": {
"name": "2208",
"code": "rb2208",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"rb2209": {
"name": "2209",
"code": "rb2209",
"exchg": "SHFE",
"product": "rb",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ru2110": {
"name": "2110",
"code": "ru2110",
"exchg": "SHFE",
"product": "ru",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ru2111": {
"name": "2111",
"code": "ru2111",
"exchg": "SHFE",
"product": "ru",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ru2201": {
"name": "2201",
"code": "ru2201",
"exchg": "SHFE",
"product": "ru",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ru2203": {
"name": "2203",
"code": "ru2203",
"exchg": "SHFE",
"product": "ru",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ru2204": {
"name": "2204",
"code": "ru2204",
"exchg": "SHFE",
"product": "ru",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ru2205": {
"name": "2205",
"code": "ru2205",
"exchg": "SHFE",
"product": "ru",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ru2206": {
"name": "2206",
"code": "ru2206",
"exchg": "SHFE",
"product": "ru",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ru2207": {
"name": "2207",
"code": "ru2207",
"exchg": "SHFE",
"product": "ru",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ru2208": {
"name": "2208",
"code": "ru2208",
"exchg": "SHFE",
"product": "ru",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ru2209": {
"name": "2209",
"code": "ru2209",
"exchg": "SHFE",
"product": "ru",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2110": {
"name": "2110",
"code": "sn2110",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2111": {
"name": "2111",
"code": "sn2111",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2112": {
"name": "2112",
"code": "sn2112",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2201": {
"name": "2201",
"code": "sn2201",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2202": {
"name": "2202",
"code": "sn2202",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2203": {
"name": "2203",
"code": "sn2203",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2204": {
"name": "2204",
"code": "sn2204",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2205": {
"name": "2205",
"code": "sn2205",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2206": {
"name": "2206",
"code": "sn2206",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2207": {
"name": "2207",
"code": "sn2207",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2208": {
"name": "2208",
"code": "sn2208",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sn2209": {
"name": "2209",
"code": "sn2209",
"exchg": "SHFE",
"product": "sn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2110": {
"name": "ֽ2110",
"code": "sp2110",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2111": {
"name": "ֽ2111",
"code": "sp2111",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2112": {
"name": "ֽ2112",
"code": "sp2112",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2201": {
"name": "ֽ2201",
"code": "sp2201",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2202": {
"name": "ֽ2202",
"code": "sp2202",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2203": {
"name": "ֽ2203",
"code": "sp2203",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2204": {
"name": "ֽ2204",
"code": "sp2204",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2205": {
"name": "ֽ2205",
"code": "sp2205",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2206": {
"name": "ֽ2206",
"code": "sp2206",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2207": {
"name": "ֽ2207",
"code": "sp2207",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2208": {
"name": "ֽ2208",
"code": "sp2208",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"sp2209": {
"name": "ֽ2209",
"code": "sp2209",
"exchg": "SHFE",
"product": "sp",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2110": {
"name": "2110",
"code": "ss2110",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2111": {
"name": "2111",
"code": "ss2111",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2112": {
"name": "2112",
"code": "ss2112",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2201": {
"name": "2201",
"code": "ss2201",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2202": {
"name": "2202",
"code": "ss2202",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2203": {
"name": "2203",
"code": "ss2203",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2204": {
"name": "2204",
"code": "ss2204",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2205": {
"name": "2205",
"code": "ss2205",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2206": {
"name": "2206",
"code": "ss2206",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2207": {
"name": "2207",
"code": "ss2207",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2208": {
"name": "2208",
"code": "ss2208",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"ss2209": {
"name": "2209",
"code": "ss2209",
"exchg": "SHFE",
"product": "ss",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2110": {
"name": "߲2110",
"code": "wr2110",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2111": {
"name": "߲2111",
"code": "wr2111",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2112": {
"name": "߲2112",
"code": "wr2112",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2201": {
"name": "߲2201",
"code": "wr2201",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2202": {
"name": "߲2202",
"code": "wr2202",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2203": {
"name": "߲2203",
"code": "wr2203",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2204": {
"name": "߲2204",
"code": "wr2204",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2205": {
"name": "߲2205",
"code": "wr2205",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2206": {
"name": "߲2206",
"code": "wr2206",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2207": {
"name": "߲2207",
"code": "wr2207",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2208": {
"name": "߲2208",
"code": "wr2208",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"wr2209": {
"name": "߲2209",
"code": "wr2209",
"exchg": "SHFE",
"product": "wr",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2110": {
"name": "п2110",
"code": "zn2110",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2111": {
"name": "п2111",
"code": "zn2111",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2112": {
"name": "п2112",
"code": "zn2112",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2201": {
"name": "п2201",
"code": "zn2201",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2202": {
"name": "п2202",
"code": "zn2202",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2203": {
"name": "п2203",
"code": "zn2203",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2204": {
"name": "п2204",
"code": "zn2204",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2205": {
"name": "п2205",
"code": "zn2205",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2206": {
"name": "п2206",
"code": "zn2206",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2207": {
"name": "п2207",
"code": "zn2207",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2208": {
"name": "п2208",
"code": "zn2208",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
},
"zn2209": {
"name": "п2209",
"code": "zn2209",
"exchg": "SHFE",
"product": "zn",
"maxlimitqty": 500,
"maxmarketqty": 30
}
}
}
================================================
FILE: config/01commom/fees.json
================================================
{
"CFFEX.T":
{
"open":3.0,
"close":3.0,
"closetoday":0.0,
"byvolume":true
},
"CFFEX.TF":
{
"open":3.0,
"close":3.0,
"closetoday":0.0,
"byvolume":true
},
"CFFEX.IF":
{
"open":0.000023,
"close":0.000023,
"closetoday":0.00023,
"byvolume":false
},
"DCE.a":
{
"open":2.01,
"close":2.01,
"closetoday":2.01,
"byvolume":true
},
"DCE.b":
{
"open":1.01,
"close":1.01,
"closetoday":1.01,
"byvolume":true
},
"DCE.c":
{
"open":1.21,
"close":1.21,
"closetoday":0.0,
"byvolume":true
},
"DCE.cs":
{
"open":1.51,
"close":1.51,
"closetoday":0.0,
"byvolume":true
},
"DCE.i":
{
"open":0.00006,
"close":0.00006,
"closetoday":0.00003,
"byvolume":false
},
"DCE.j":
{
"open":0.00006,
"close":0.00006,
"closetoday":0.00003,
"byvolume":false
},
"DCE.jd":
{
"open":0.00015,
"close":0.00015,
"closetoday":0.00015,
"byvolume":false
},
"DCE.l":
{
"open":2.0,
"close":2.0,
"closetoday":0.0,
"byvolume":true
},
"DCE.y":
{
"open":2.51,
"close":2.51,
"closetoday":0.0,
"byvolume":true
},
"DCE.m":
{
"open":1.51,
"close":1.51,
"closetoday":0.0,
"byvolume":true
},
"DCE.p":
{
"open":2.5,
"close":2.5,
"closetoday":0.0,
"byvolume":true
},
"DCE.pp":
{
"open":0.00005,
"close":0.00005,
"closetoday":0.000025,
"byvolume":false
},
"INE.nr":
{
"open":0.000021,
"close":0.000021,
"closetoday":0,
"byvolume":false
},
"SHFE.ag":
{
"open":0.00005,
"close":0.00005,
"closetoday":0.00005,
"byvolume":false
},
"SHFE.au":
{
"open":10.0,
"close":10.0,
"closetoday":0.0,
"byvolume":true
},
"SHFE.bu":
{
"open":0.0001,
"close":0.0001,
"closetoday":0.0001,
"byvolume":false
},
"SHFE.cu":
{
"open":0.0001,
"close":0.0001,
"closetoday":0.0001,
"byvolume":false
},
"SHFE.hc":
{
"open":0.00005,
"close":0.00005,
"closetoday":0.0,
"byvolume":false
},
"SHFE.ni":
{
"open":6.0,
"close":6.0,
"closetoday":6.0,
"byvolume":true
},
"SHFE.rb":
{
"open":0.0001,
"close":0.0001,
"closetoday":0.0001,
"byvolume":false
},
"SHFE.ru":
{
"open":3.01,
"close":3.01,
"closetoday":0,
"byvolume":true
},
"SHFE.sp":
{
"open":0.000051,
"close":0.000051,
"closetoday":0,
"byvolume":false
},
"CZCE.AP":
{
"open":0.5,
"close":0.5,
"closetoday":0.0,
"byvolume":true
},
"CZCE.CF":
{
"open":4.31,
"close":4.31,
"closetoday":0.0,
"byvolume":true
},
"CZCE.MA":
{
"open":1.4,
"close":1.4,
"closetoday":0,
"byvolume":true
},
"CZCE.TA":
{
"open":3.01,
"close":3.01,
"closetoday":0,
"byvolume":true
},
"CZCE.SR":
{
"open":3.0,
"close":3.0,
"closetoday":0.0,
"byvolume":true
},
"CZCE.ZC":
{
"open":4.0,
"close":4.0,
"closetoday":0.0,
"byvolume":true
},
"CZCE.RM":
{
"open":1.51,
"close":1.51,
"closetoday":0.0,
"byvolume":true
}
}
================================================
FILE: config/01commom/holidays.json
================================================
{
"CHINA" : [
20180101,20180215,20181005,20181231,
20190101,20190913,20191001,20191002,20191003,20191004,20191007,
20200101,20200124,20200127,20200128,20200129,20200130,20200131,20200406,20200501,20200504,20200505,20200625,20200626,20201001,20201002,20201005,20201006,20201007,20201008,
20210101,20210211,20210212,20210215,20210216,20210217,20210405,20210503,20210504,20210505,20210614,20210920,20210921,20211001,20211004,20211005,20211006,20211007]
}
================================================
FILE: config/01commom/hots.json
================================================
{
"CFFEX": {
"IC": [
{
"date": 20160104,
"from": "",
"to": "IC1601",
"newClose": 6881.2001953125,
"oldClose": 0
},
{
"date": 20160115,
"from": "IC1601",
"to": "IC1602",
"newClose": 5570.0,
"oldClose": 5942.7998046875
},
{
"date": 20160218,
"from": "IC1602",
"to": "IC1603",
"newClose": 5746.0,
"oldClose": 5950.2001953125
},
{
"date": 20160318,
"from": "IC1603",
"to": "IC1604",
"newClose": 5799.60009765625,
"oldClose": 5944.60009765625
},
{
"date": 20160415,
"from": "IC1604",
"to": "IC1605",
"newClose": 6214.60009765625,
"oldClose": 6376.60009765625
},
{
"date": 20160518,
"from": "IC1605",
"to": "IC1606",
"newClose": 5460.2001953125,
"oldClose": 5632.7998046875
},
{
"date": 20160617,
"from": "IC1606",
"to": "IC1607",
"newClose": 5810.60009765625,
"oldClose": 5962.39990234375
},
{
"date": 20160715,
"from": "IC1607",
"to": "IC1608",
"newClose": 6319.0,
"oldClose": 6439.7998046875
},
{
"date": 20160817,
"from": "IC1608",
"to": "IC1609",
"newClose": 6379.2001953125,
"oldClose": 6470.7998046875
},
{
"date": 20160914,
"from": "IC1609",
"to": "IC1610",
"newClose": 6157.39990234375,
"oldClose": 6270.0
},
{
"date": 20161020,
"from": "IC1610",
"to": "IC1611",
"newClose": 6418.0,
"oldClose": 6499.0
},
{
"date": 20161115,
"from": "IC1611",
"to": "IC1612",
"newClose": 6519.2001953125,
"oldClose": 6601.7998046875
},
{
"date": 20161215,
"from": "IC1612",
"to": "IC1701",
"newClose": 6150.2001953125,
"oldClose": 6262.7998046875
},
{
"date": 20170119,
"from": "IC1701",
"to": "IC1702",
"newClose": 5995.0,
"oldClose": 6047.0
},
{
"date": 20170214,
"from": "IC1702",
"to": "IC1703",
"newClose": 6253.0,
"oldClose": 6332.0
},
{
"date": 20170316,
"from": "IC1703",
"to": "IC1704",
"newClose": 6469.39990234375,
"oldClose": 6539.0
},
{
"date": 20170420,
"from": "IC1704",
"to": "IC1705",
"newClose": 6312.2001953125,
"oldClose": 6347.7998046875
},
{
"date": 20170516,
"from": "IC1705",
"to": "IC1706",
"newClose": 5973.60009765625,
"oldClose": 6009.7998046875
},
{
"date": 20170615,
"from": "IC1706",
"to": "IC1707",
"newClose": 5964.2001953125,
"oldClose": 6008.39990234375
},
{
"date": 20170721,
"from": "IC1707",
"to": "IC1708",
"newClose": 6041.2001953125,
"oldClose": 6123.0
},
{
"date": 20170816,
"from": "IC1708",
"to": "IC1709",
"newClose": 6259.7998046875,
"oldClose": 6309.60009765625
},
{
"date": 20170915,
"from": "IC1709",
"to": "IC1710",
"newClose": 6588.0,
"oldClose": 6629.2001953125
},
{
"date": 20171020,
"from": "IC1710",
"to": "IC1711",
"newClose": 6530.0,
"oldClose": 6548.0
},
{
"date": 20171115,
"from": "IC1711",
"to": "IC1712",
"newClose": 6481.7998046875,
"oldClose": 6527.0
},
{
"date": 20171214,
"from": "IC1712",
"to": "IC1801",
"newClose": 6251.7998046875,
"oldClose": 6284.0
},
{
"date": 20180119,
"from": "IC1801",
"to": "IC1802",
"newClose": 6232.0,
"oldClose": 6281.0
},
{
"date": 20180209,
"from": "IC1802",
"to": "IC1803",
"newClose": 5486.2001953125,
"oldClose": 5504.0
},
{
"date": 20180316,
"from": "IC1803",
"to": "IC1804",
"newClose": 6084.60009765625,
"oldClose": 6174.2001953125
},
{
"date": 20180420,
"from": "IC1804",
"to": "IC1805",
"newClose": 5837.0,
"oldClose": 5908.0
},
{
"date": 20180517,
"from": "IC1805",
"to": "IC1806",
"newClose": 5925.0,
"oldClose": 5975.0
},
{
"date": 20180615,
"from": "IC1806",
"to": "IC1807",
"newClose": 5436.60009765625,
"oldClose": 5482.60009765625
},
{
"date": 20180719,
"from": "IC1807",
"to": "IC1808",
"newClose": 5067.60009765625,
"oldClose": 5119.39990234375
},
{
"date": 20180816,
"from": "IC1808",
"to": "IC1809",
"newClose": 4820.39990234375,
"oldClose": 4871.0
},
{
"date": 20180921,
"from": "IC1809",
"to": "IC1810",
"newClose": 4827.7998046875,
"oldClose": 4788.0
},
{
"date": 20181019,
"from": "IC1810",
"to": "IC1811",
"newClose": 4080.39990234375,
"oldClose": 4091.39990234375
},
{
"date": 20181115,
"from": "IC1811",
"to": "IC1812",
"newClose": 4619.0,
"oldClose": 4620.0
},
{
"date": 20181220,
"from": "IC1812",
"to": "IC1901",
"newClose": 4232.60009765625,
"oldClose": 4264.0
},
{
"date": 20190118,
"from": "IC1901",
"to": "IC1902",
"newClose": 4341.0,
"oldClose": 4330.0
},
{
"date": 20190214,
"from": "IC1902",
"to": "IC1903",
"newClose": 4537.0,
"oldClose": 4533.7998046875
},
{
"date": 20190315,
"from": "IC1903",
"to": "IC1904",
"newClose": 5330.60009765625,
"oldClose": 5351.2001953125
},
{
"date": 20190419,
"from": "IC1904",
"to": "IC1905",
"newClose": 5775.7998046875,
"oldClose": 5781.2001953125
},
{
"date": 20190516,
"from": "IC1905",
"to": "IC1906",
"newClose": 4993.0,
"oldClose": 5091.39990234375
},
{
"date": 20190620,
"from": "IC1906",
"to": "IC1907",
"newClose": 4901.7998046875,
"oldClose": 4954.2001953125
},
{
"date": 20190719,
"from": "IC1907",
"to": "IC1908",
"newClose": 4823.39990234375,
"oldClose": 4881.39990234375
},
{
"date": 20190815,
"from": "IC1908",
"to": "IC1909",
"newClose": 4652.60009765625,
"oldClose": 4701.7998046875
},
{
"date": 20190919,
"from": "IC1909",
"to": "IC1910",
"newClose": 5150.60009765625,
"oldClose": 5185.60009765625
},
{
"date": 20191018,
"from": "IC1910",
"to": "IC1911",
"newClose": 4893.2001953125,
"oldClose": 4976.2001953125
},
{
"date": 20191114,
"from": "IC1911",
"to": "IC1912",
"newClose": 4839.2001953125,
"oldClose": 4903.0
},
{
"date": 20191219,
"from": "IC1912",
"to": "IC2001",
"newClose": 5251.0,
"oldClose": 5250.60009765625
},
{
"date": 20200117,
"from": "IC2001",
"to": "IC2002",
"newClose": 5501.2001953125,
"oldClose": 5515.0
},
{
"date": 20200220,
"from": "IC2002",
"to": "IC2003",
"newClose": 5667.7998046875,
"oldClose": 5704.7998046875
},
{
"date": 20200319,
"from": "IC2003",
"to": "IC2004",
"newClose": 5099.60009765625,
"oldClose": 5147.39990234375
},
{
"date": 20200416,
"from": "IC2004",
"to": "IC2005",
"newClose": 5247.0,
"oldClose": 5308.39990234375
},
{
"date": 20200514,
"from": "IC2005",
"to": "IC2006",
"newClose": 5408.60009765625,
"oldClose": 5490.39990234375
},
{
"date": 20200618,
"from": "IC2006",
"to": "IC2007",
"newClose": 5659.60009765625,
"oldClose": 5743.2001953125
},
{
"date": 20200716,
"from": "IC2007",
"to": "IC2008",
"newClose": 6265.7998046875,
"oldClose": 6309.2001953125
},
{
"date": 20200820,
"from": "IC2008",
"to": "IC2009",
"newClose": 6495.7998046875,
"oldClose": 6587.2001953125
},
{
"date": 20200917,
"from": "IC2009",
"to": "IC2010",
"newClose": 6291.7998046875,
"oldClose": 6374.0
},
{
"date": 20201016,
"from": "IC2010",
"to": "IC2011",
"newClose": 6356.0,
"oldClose": 6419.60009765625
},
{
"date": 20201119,
"from": "IC2011",
"to": "IC2012",
"newClose": 6320.60009765625,
"oldClose": 6373.0
},
{
"date": 20201217,
"from": "IC2012",
"to": "IC2101",
"newClose": 6277.7998046875,
"oldClose": 6313.7998046875
},
{
"date": 20210115,
"from": "IC2101",
"to": "IC2102",
"newClose": 6351.0,
"oldClose": 6390.2001953125
},
{
"date": 20210218,
"from": "IC2102",
"to": "IC2103",
"newClose": 6505.0,
"oldClose": 6579.60009765625
},
{
"date": 20210319,
"from": "IC2103",
"to": "IC2104",
"newClose": 6169.0,
"oldClose": 6196.7998046875
},
{
"date": 20210416,
"from": "IC2104",
"to": "IC2105",
"newClose": 6325.39990234375,
"oldClose": 6332.60009765625
},
{
"date": 20210520,
"from": "IC2105",
"to": "IC2106",
"newClose": 6524.0,
"oldClose": 6556.39990234375
},
{
"date": 20210618,
"from": "IC2106",
"to": "IC2107",
"newClose": 6614.2001953125,
"oldClose": 6648.60009765625
},
{
"date": 20210715,
"from": "IC2107",
"to": "IC2108",
"newClose": 6873.7998046875,
"oldClose": 6920.0
},
{
"date": 20210819,
"from": "IC2108",
"to": "IC2109",
"newClose": 6885.0,
"oldClose": 6952.39990234375
},
{
"date": 20210916,
"from": "IC2109",
"to": "IC2110",
"newClose": 7363.39990234375,
"oldClose": 7442.39990234375
}
],
"IF": [
{
"date": 20160104,
"from": "",
"to": "IF1601",
"newClose": 3425.0,
"oldClose": 0
},
{
"date": 20160115,
"from": "IF1601",
"to": "IF1602",
"newClose": 3027.800048828125,
"oldClose": 3132.800048828125
},
{
"date": 20160217,
"from": "IF1602",
"to": "IF1603",
"newClose": 2987.800048828125,
"oldClose": 3045.60009765625
},
{
"date": 20160318,
"from": "IF1603",
"to": "IF1604",
"newClose": 3125.0,
"oldClose": 3176.800048828125
},
{
"date": 20160415,
"from": "IF1604",
"to": "IF1605",
"newClose": 3237.60009765625,
"oldClose": 3270.199951171875
},
{
"date": 20160518,
"from": "IF1605",
"to": "IF1606",
"newClose": 3019.0,
"oldClose": 3058.60009765625
},
{
"date": 20160616,
"from": "IF1606",
"to": "IF1607",
"newClose": 3028.800048828125,
"oldClose": 3088.0
},
{
"date": 20160714,
"from": "IF1607",
"to": "IF1608",
"newClose": 3241.0,
"oldClose": 3270.800048828125
},
{
"date": 20160817,
"from": "IF1608",
"to": "IF1609",
"newClose": 3341.800048828125,
"oldClose": 3365.800048828125
},
{
"date": 20160914,
"from": "IF1609",
"to": "IF1610",
"newClose": 3187.800048828125,
"oldClose": 3235.0
},
{
"date": 20161020,
"from": "IF1610",
"to": "IF1611",
"newClose": 3289.60009765625,
"oldClose": 3317.39990234375
},
{
"date": 20161116,
"from": "IF1611",
"to": "IF1612",
"newClose": 3398.199951171875,
"oldClose": 3423.60009765625
},
{
"date": 20161215,
"from": "IF1612",
"to": "IF1701",
"newClose": 3299.60009765625,
"oldClose": 3328.0
},
{
"date": 20170119,
"from": "IF1701",
"to": "IF1702",
"newClose": 3322.199951171875,
"oldClose": 3326.0
},
{
"date": 20170214,
"from": "IF1702",
"to": "IF1703",
"newClose": 3402.60009765625,
"oldClose": 3419.39990234375
},
{
"date": 20170316,
"from": "IF1703",
"to": "IF1704",
"newClose": 3456.800048828125,
"oldClose": 3481.39990234375
},
{
"date": 20170420,
"from": "IF1704",
"to": "IF1705",
"newClose": 3441.60009765625,
"oldClose": 3458.60009765625
},
{
"date": 20170517,
"from": "IF1705",
"to": "IF1706",
"newClose": 3391.0,
"oldClose": 3411.0
},
{
"date": 20170615,
"from": "IF1706",
"to": "IF1707",
"newClose": 3504.800048828125,
"oldClose": 3524.0
},
{
"date": 20170721,
"from": "IF1707",
"to": "IF1708",
"newClose": 3716.0,
"oldClose": 3738.0
},
{
"date": 20170815,
"from": "IF1708",
"to": "IF1709",
"newClose": 3680.60009765625,
"oldClose": 3694.39990234375
},
{
"date": 20170915,
"from": "IF1709",
"to": "IF1710",
"newClose": 3823.0,
"oldClose": 3834.199951171875
},
{
"date": 20171020,
"from": "IF1710",
"to": "IF1711",
"newClose": 3911.800048828125,
"oldClose": 3924.39990234375
},
{
"date": 20171114,
"from": "IF1711",
"to": "IF1712",
"newClose": 4098.7998046875,
"oldClose": 4101.0
},
{
"date": 20171214,
"from": "IF1712",
"to": "IF1801",
"newClose": 4027.199951171875,
"oldClose": 4030.0
},
{
"date": 20180119,
"from": "IF1801",
"to": "IF1802",
"newClose": 4303.60009765625,
"oldClose": 4290.7998046875
},
{
"date": 20180208,
"from": "IF1802",
"to": "IF1803",
"newClose": 4000.60009765625,
"oldClose": 3996.800048828125
},
{
"date": 20180316,
"from": "IF1803",
"to": "IF1804",
"newClose": 4027.199951171875,
"oldClose": 4082.0
},
{
"date": 20180420,
"from": "IF1804",
"to": "IF1805",
"newClose": 3732.60009765625,
"oldClose": 3760.199951171875
},
{
"date": 20180517,
"from": "IF1805",
"to": "IF1806",
"newClose": 3840.800048828125,
"oldClose": 3861.39990234375
},
{
"date": 20180615,
"from": "IF1806",
"to": "IF1807",
"newClose": 3722.0,
"oldClose": 3752.800048828125
},
{
"date": 20180720,
"from": "IF1807",
"to": "IF1808",
"newClose": 3482.39990234375,
"oldClose": 3473.39990234375
},
{
"date": 20180816,
"from": "IF1808",
"to": "IF1809",
"newClose": 3249.0,
"oldClose": 3271.39990234375
},
{
"date": 20180920,
"from": "IF1809",
"to": "IF1810",
"newClose": 3312.0,
"oldClose": 3315.0
},
{
"date": 20181019,
"from": "IF1810",
"to": "IF1811",
"newClose": 3128.60009765625,
"oldClose": 3108.199951171875
},
{
"date": 20181115,
"from": "IF1811",
"to": "IF1812",
"newClose": 3247.39990234375,
"oldClose": 3241.199951171875
},
{
"date": 20181220,
"from": "IF1812",
"to": "IF1901",
"newClose": 3075.800048828125,
"oldClose": 3073.199951171875
},
{
"date": 20190118,
"from": "IF1901",
"to": "IF1902",
"newClose": 3170.0,
"oldClose": 3158.199951171875
},
{
"date": 20190214,
"from": "IF1902",
"to": "IF1903",
"newClose": 3411.800048828125,
"oldClose": 3402.39990234375
},
{
"date": 20190315,
"from": "IF1903",
"to": "IF1904",
"newClose": 3751.60009765625,
"oldClose": 3741.199951171875
},
{
"date": 20190418,
"from": "IF1904",
"to": "IF1905",
"newClose": 4075.0,
"oldClose": 4070.60009765625
},
{
"date": 20190516,
"from": "IF1905",
"to": "IF1906",
"newClose": 3703.0,
"oldClose": 3732.800048828125
},
{
"date": 20190620,
"from": "IF1906",
"to": "IF1907",
"newClose": 3805.60009765625,
"oldClose": 3821.60009765625
},
{
"date": 20190719,
"from": "IF1907",
"to": "IF1908",
"newClose": 3799.39990234375,
"oldClose": 3805.800048828125
},
{
"date": 20190815,
"from": "IF1908",
"to": "IF1909",
"newClose": 3674.60009765625,
"oldClose": 3688.39990234375
},
{
"date": 20190920,
"from": "IF1909",
"to": "IF1910",
"newClose": 3926.0,
"oldClose": 3932.0
},
{
"date": 20191018,
"from": "IF1910",
"to": "IF1911",
"newClose": 3854.199951171875,
"oldClose": 3880.60009765625
},
{
"date": 20191114,
"from": "IF1911",
"to": "IF1912",
"newClose": 3895.39990234375,
"oldClose": 3900.0
},
{
"date": 20191219,
"from": "IF1912",
"to": "IF2001",
"newClose": 4045.199951171875,
"oldClose": 4028.800048828125
},
{
"date": 20200117,
"from": "IF2001",
"to": "IF2002",
"newClose": 4172.60009765625,
"oldClose": 4151.60009765625
},
{
"date": 20200219,
"from": "IF2002",
"to": "IF2003",
"newClose": 4052.60009765625,
"oldClose": 4049.199951171875
},
{
"date": 20200319,
"from": "IF2003",
"to": "IF2004",
"newClose": 3561.800048828125,
"oldClose": 3584.0
},
{
"date": 20200417,
"from": "IF2004",
"to": "IF2005",
"newClose": 3796.60009765625,
"oldClose": 3850.39990234375
},
{
"date": 20200514,
"from": "IF2005",
"to": "IF2006",
"newClose": 3882.39990234375,
"oldClose": 3928.0
},
{
"date": 20200618,
"from": "IF2006",
"to": "IF2007",
"newClose": 3992.39990234375,
"oldClose": 4042.0
},
{
"date": 20200716,
"from": "IF2007",
"to": "IF2008",
"newClose": 4495.0,
"oldClose": 4496.0
},
{
"date": 20200820,
"from": "IF2008",
"to": "IF2009",
"newClose": 4659.60009765625,
"oldClose": 4682.7998046875
},
{
"date": 20200917,
"from": "IF2009",
"to": "IF2010",
"newClose": 4593.39990234375,
"oldClose": 4625.60009765625
},
{
"date": 20201016,
"from": "IF2010",
"to": "IF2011",
"newClose": 4767.39990234375,
"oldClose": 4787.60009765625
},
{
"date": 20201119,
"from": "IF2011",
"to": "IF2012",
"newClose": 4925.7998046875,
"oldClose": 4933.60009765625
},
{
"date": 20201217,
"from": "IF2012",
"to": "IF2101",
"newClose": 5025.60009765625,
"oldClose": 5029.7998046875
},
{
"date": 20210115,
"from": "IF2101",
"to": "IF2102",
"newClose": 5448.0,
"oldClose": 5438.60009765625
},
{
"date": 20210218,
"from": "IF2102",
"to": "IF2103",
"newClose": 5735.7998046875,
"oldClose": 5747.2001953125
},
{
"date": 20210319,
"from": "IF2103",
"to": "IF2104",
"newClose": 4941.0,
"oldClose": 5006.0
},
{
"date": 20210416,
"from": "IF2104",
"to": "IF2105",
"newClose": 4938.39990234375,
"oldClose": 4963.7998046875
},
{
"date": 20210520,
"from": "IF2105",
"to": "IF2106",
"newClose": 5172.2001953125,
"oldClose": 5183.7998046875
},
{
"date": 20210618,
"from": "IF2106",
"to": "IF2107",
"newClose": 5062.7998046875,
"oldClose": 5094.60009765625
},
{
"date": 20210715,
"from": "IF2107",
"to": "IF2108",
"newClose": 5129.0,
"oldClose": 5145.7998046875
},
{
"date": 20210819,
"from": "IF2108",
"to": "IF2109",
"newClose": 4814.2001953125,
"oldClose": 4853.0
},
{
"date": 20210916,
"from": "IF2109",
"to": "IF2110",
"newClose": 4799.39990234375,
"oldClose": 4812.60009765625
}
],
"TF": [
{
"date": 20160104,
"from": "",
"to": "TF1603",
"newClose": 100.59500122070312,
"oldClose": 0
},
{
"date": 20160205,
"from": "TF1603",
"to": "TF1606",
"newClose": 100.40499877929688,
"oldClose": 100.7750015258789
},
{
"date": 20160428,
"from": "TF1606",
"to": "TF1609",
"newClose": 99.83499908447266,
"oldClose": 100.66999816894531
},
{
"date": 20160811,
"from": "TF1609",
"to": "TF1612",
"newClose": 101.58499908447266,
"oldClose": 101.875
},
{
"date": 20161110,
"from": "TF1612",
"to": "TF1703",
"newClose": 101.04499816894531,
"oldClose": 101.58499908447266
},
{
"date": 20170124,
"from": "TF1703",
"to": "TF1706",
"newClose": 98.08999633789062,
"oldClose": 99.09500122070312
},
{
"date": 20170511,
"from": "TF1706",
"to": "TF1709",
"newClose": 97.41000366210938,
"oldClose": 97.19999694824219
},
{
"date": 20170810,
"from": "TF1709",
"to": "TF1712",
"newClose": 97.30500030517578,
"oldClose": 97.16000366210938
},
{
"date": 20171110,
"from": "TF1712",
"to": "TF1803",
"newClose": 96.2750015258789,
"oldClose": 95.98999786376953
},
{
"date": 20180212,
"from": "TF1803",
"to": "TF1806",
"newClose": 96.0999984741211,
"oldClose": 96.18499755859375
},
{
"date": 20180516,
"from": "TF1806",
"to": "TF1809",
"newClose": 97.59500122070312,
"oldClose": 97.69000244140625
},
{
"date": 20180814,
"from": "TF1809",
"to": "TF1812",
"newClose": 98.05500030517578,
"oldClose": 97.95999908447266
},
{
"date": 20181119,
"from": "TF1812",
"to": "TF1903",
"newClose": 98.83499908447266,
"oldClose": 98.83499908447266
},
{
"date": 20190221,
"from": "TF1903",
"to": "TF1906",
"newClose": 99.69000244140625,
"oldClose": 99.8949966430664
},
{
"date": 20190522,
"from": "TF1906",
"to": "TF1909",
"newClose": 98.79499816894531,
"oldClose": 99.1500015258789
},
{
"date": 20190815,
"from": "TF1909",
"to": "TF1912",
"newClose": 100.05999755859375,
"oldClose": 100.20500183105469
},
{
"date": 20191121,
"from": "TF1912",
"to": "TF2003",
"newClose": 99.74500274658203,
"oldClose": 100.07499694824219
},
{
"date": 20200220,
"from": "TF2003",
"to": "TF2006",
"newClose": 100.83000183105469,
"oldClose": 101.30000305175781
},
{
"date": 20200514,
"from": "TF2006",
"to": "TF2009",
"newClose": 103.38999938964844,
"oldClose": 104.17500305175781
},
{
"date": 20200819,
"from": "TF2009",
"to": "TF2012",
"newClose": 100.1050033569336,
"oldClose": 100.52999877929688
},
{
"date": 20201119,
"from": "TF2012",
"to": "TF2103",
"newClose": 98.94000244140625,
"oldClose": 99.01000213623047
},
{
"date": 20210219,
"from": "TF2103",
"to": "TF2106",
"newClose": 99.08999633789062,
"oldClose": 99.41999816894531
},
{
"date": 20210520,
"from": "TF2106",
"to": "TF2109",
"newClose": 100.05999755859375,
"oldClose": 100.41500091552734
},
{
"date": 20210818,
"from": "TF2109",
"to": "TF2112",
"newClose": 101.01000213623047,
"oldClose": 101.29000091552734
}
],
"IH": [
{
"date": 20160104,
"from": "",
"to": "IH1601",
"newClose": 2254.0,
"oldClose": 0
},
{
"date": 20160115,
"from": "IH1601",
"to": "IH1602",
"newClose": 2031.0,
"oldClose": 2079.0
},
{
"date": 20160217,
"from": "IH1602",
"to": "IH1603",
"newClose": 1974.5999755859375,
"oldClose": 2000.800048828125
},
{
"date": 20160318,
"from": "IH1603",
"to": "IH1604",
"newClose": 2120.0,
"oldClose": 2146.60009765625
},
{
"date": 20160415,
"from": "IH1604",
"to": "IH1605",
"newClose": 2173.0,
"oldClose": 2181.800048828125
},
{
"date": 20160518,
"from": "IH1605",
"to": "IH1606",
"newClose": 2049.0,
"oldClose": 2068.800048828125
},
{
"date": 20160616,
"from": "IH1606",
"to": "IH1607",
"newClose": 2053.60009765625,
"oldClose": 2092.60009765625
},
{
"date": 20160714,
"from": "IH1607",
"to": "IH1608",
"newClose": 2186.800048828125,
"oldClose": 2192.39990234375
},
{
"date": 20160816,
"from": "IH1608",
"to": "IH1609",
"newClose": 2257.60009765625,
"oldClose": 2258.60009765625
},
{
"date": 20160914,
"from": "IH1609",
"to": "IH1610",
"newClose": 2154.0,
"oldClose": 2175.0
},
{
"date": 20161020,
"from": "IH1610",
"to": "IH1611",
"newClose": 2212.199951171875,
"oldClose": 2221.199951171875
},
{
"date": 20161115,
"from": "IH1611",
"to": "IH1612",
"newClose": 2300.39990234375,
"oldClose": 2309.0
},
{
"date": 20161215,
"from": "IH1612",
"to": "IH1701",
"newClose": 2302.60009765625,
"oldClose": 2304.0
},
{
"date": 20170119,
"from": "IH1701",
"to": "IH1702",
"newClose": 2330.199951171875,
"oldClose": 2334.0
},
{
"date": 20170214,
"from": "IH1702",
"to": "IH1703",
"newClose": 2365.0,
"oldClose": 2368.0
},
{
"date": 20170316,
"from": "IH1703",
"to": "IH1704",
"newClose": 2364.0,
"oldClose": 2368.800048828125
},
{
"date": 20170420,
"from": "IH1704",
"to": "IH1705",
"newClose": 2325.199951171875,
"oldClose": 2332.60009765625
},
{
"date": 20170517,
"from": "IH1705",
"to": "IH1706",
"newClose": 2351.0,
"oldClose": 2362.800048828125
},
{
"date": 20170615,
"from": "IH1706",
"to": "IH1707",
"newClose": 2439.0,
"oldClose": 2458.0
},
{
"date": 20170721,
"from": "IH1707",
"to": "IH1708",
"newClose": 2640.199951171875,
"oldClose": 2648.199951171875
},
{
"date": 20170816,
"from": "IH1708",
"to": "IH1709",
"newClose": 2593.0,
"oldClose": 2591.199951171875
},
{
"date": 20170915,
"from": "IH1709",
"to": "IH1710",
"newClose": 2681.0,
"oldClose": 2669.0
},
{
"date": 20171020,
"from": "IH1710",
"to": "IH1711",
"newClose": 2740.800048828125,
"oldClose": 2740.39990234375
},
{
"date": 20171115,
"from": "IH1711",
"to": "IH1712",
"newClose": 2858.199951171875,
"oldClose": 2848.0
},
{
"date": 20171215,
"from": "IH1712",
"to": "IH1801",
"newClose": 2825.60009765625,
"oldClose": 2829.199951171875
},
{
"date": 20180119,
"from": "IH1801",
"to": "IH1802",
"newClose": 3133.800048828125,
"oldClose": 3123.60009765625
},
{
"date": 20180213,
"from": "IH1802",
"to": "IH1803",
"newClose": 2837.199951171875,
"oldClose": 2844.0
},
{
"date": 20180316,
"from": "IH1803",
"to": "IH1804",
"newClose": 2864.60009765625,
"oldClose": 2889.0
},
{
"date": 20180420,
"from": "IH1804",
"to": "IH1805",
"newClose": 2636.199951171875,
"oldClose": 2641.800048828125
},
{
"date": 20180517,
"from": "IH1805",
"to": "IH1806",
"newClose": 2687.199951171875,
"oldClose": 2697.39990234375
},
{
"date": 20180615,
"from": "IH1806",
"to": "IH1807",
"newClose": 2640.0,
"oldClose": 2666.60009765625
},
{
"date": 20180720,
"from": "IH1807",
"to": "IH1808",
"newClose": 2499.199951171875,
"oldClose": 2473.800048828125
},
{
"date": 20180816,
"from": "IH1808",
"to": "IH1809",
"newClose": 2405.0,
"oldClose": 2411.0
},
{
"date": 20180920,
"from": "IH1809",
"to": "IH1810",
"newClose": 2496.800048828125,
"oldClose": 2491.0
},
{
"date": 20181019,
"from": "IH1810",
"to": "IH1811",
"newClose": 2439.199951171875,
"oldClose": 2425.0
},
{
"date": 20181115,
"from": "IH1811",
"to": "IH1812",
"newClose": 2464.199951171875,
"oldClose": 2459.60009765625
},
{
"date": 20181220,
"from": "IH1812",
"to": "IH1901",
"newClose": 2342.0,
"oldClose": 2336.199951171875
},
{
"date": 20190118,
"from": "IH1901",
"to": "IH1902",
"newClose": 2415.199951171875,
"oldClose": 2412.0
},
{
"date": 20190214,
"from": "IH1902",
"to": "IH1903",
"newClose": 2585.60009765625,
"oldClose": 2578.800048828125
},
{
"date": 20190315,
"from": "IH1903",
"to": "IH1904",
"newClose": 2768.0,
"oldClose": 2753.199951171875
},
{
"date": 20190419,
"from": "IH1904",
"to": "IH1905",
"newClose": 3050.60009765625,
"oldClose": 3024.0
},
{
"date": 20190516,
"from": "IH1905",
"to": "IH1906",
"newClose": 2769.39990234375,
"oldClose": 2785.60009765625
},
{
"date": 20190620,
"from": "IH1906",
"to": "IH1907",
"newClose": 2918.60009765625,
"oldClose": 2939.60009765625
},
{
"date": 20190719,
"from": "IH1907",
"to": "IH1908",
"newClose": 2898.0,
"oldClose": 2892.199951171875
},
{
"date": 20190815,
"from": "IH1908",
"to": "IH1909",
"newClose": 2794.800048828125,
"oldClose": 2810.800048828125
},
{
"date": 20190920,
"from": "IH1909",
"to": "IH1910",
"newClose": 2961.39990234375,
"oldClose": 2964.0
},
{
"date": 20191018,
"from": "IH1910",
"to": "IH1911",
"newClose": 2956.800048828125,
"oldClose": 2974.199951171875
},
{
"date": 20191114,
"from": "IH1911",
"to": "IH1912",
"newClose": 2965.199951171875,
"oldClose": 2965.60009765625
},
{
"date": 20191220,
"from": "IH1912",
"to": "IH2001",
"newClose": 3024.60009765625,
"oldClose": 3018.39990234375
},
{
"date": 20200117,
"from": "IH2001",
"to": "IH2002",
"newClose": 3063.0,
"oldClose": 3048.60009765625
},
{
"date": 20200220,
"from": "IH2002",
"to": "IH2003",
"newClose": 2973.60009765625,
"oldClose": 2971.60009765625
},
{
"date": 20200320,
"from": "IH2003",
"to": "IH2004",
"newClose": 2606.60009765625,
"oldClose": 2608.0
},
{
"date": 20200417,
"from": "IH2004",
"to": "IH2005",
"newClose": 2771.800048828125,
"oldClose": 2814.60009765625
},
{
"date": 20200514,
"from": "IH2005",
"to": "IH2006",
"newClose": 2796.0,
"oldClose": 2833.199951171875
},
{
"date": 20200618,
"from": "IH2006",
"to": "IH2007",
"newClose": 2844.199951171875,
"oldClose": 2886.39990234375
},
{
"date": 20200717,
"from": "IH2007",
"to": "IH2008",
"newClose": 3182.39990234375,
"oldClose": 3174.39990234375
},
{
"date": 20200820,
"from": "IH2008",
"to": "IH2009",
"newClose": 3258.39990234375,
"oldClose": 3263.39990234375
},
{
"date": 20200918,
"from": "IH2009",
"to": "IH2010",
"newClose": 3347.800048828125,
"oldClose": 3326.0
},
{
"date": 20201016,
"from": "IH2010",
"to": "IH2011",
"newClose": 3368.0,
"oldClose": 3380.0
},
{
"date": 20201119,
"from": "IH2011",
"to": "IH2012",
"newClose": 3411.199951171875,
"oldClose": 3415.39990234375
},
{
"date": 20201218,
"from": "IH2012",
"to": "IH2101",
"newClose": 3516.0,
"oldClose": 3505.199951171875
},
{
"date": 20210115,
"from": "IH2101",
"to": "IH2102",
"newClose": 3817.0,
"oldClose": 3815.800048828125
},
{
"date": 20210218,
"from": "IH2102",
"to": "IH2103",
"newClose": 3997.0,
"oldClose": 4005.0
},
{
"date": 20210319,
"from": "IH2103",
"to": "IH2104",
"newClose": 3472.60009765625,
"oldClose": 3512.199951171875
},
{
"date": 20210416,
"from": "IH2104",
"to": "IH2105",
"newClose": 3436.39990234375,
"oldClose": 3444.800048828125
},
{
"date": 20210520,
"from": "IH2105",
"to": "IH2106",
"newClose": 3507.39990234375,
"oldClose": 3520.199951171875
},
{
"date": 20210618,
"from": "IH2106",
"to": "IH2107",
"newClose": 3423.39990234375,
"oldClose": 3446.800048828125
},
{
"date": 20210716,
"from": "IH2107",
"to": "IH2108",
"newClose": 3364.39990234375,
"oldClose": 3378.199951171875
},
{
"date": 20210819,
"from": "IH2108",
"to": "IH2109",
"newClose": 3126.39990234375,
"oldClose": 3139.199951171875
},
{
"date": 20210916,
"from": "IH2109",
"to": "IH2110",
"newClose": 3132.800048828125,
"oldClose": 3128.199951171875
}
],
"T": [
{
"date": 20160104,
"from": "",
"to": "T1603",
"newClose": 99.86499786376953,
"oldClose": 0
},
{
"date": 20160204,
"from": "T1603",
"to": "T1606",
"newClose": 99.53500366210938,
"oldClose": 100.25
},
{
"date": 20160505,
"from": "T1606",
"to": "T1609",
"newClose": 98.40499877929688,
"oldClose": 99.8499984741211
},
{
"date": 20160809,
"from": "T1609",
"to": "T1612",
"newClose": 101.19999694824219,
"oldClose": 101.58999633789062
},
{
"date": 20161109,
"from": "T1612",
"to": "T1703",
"newClose": 100.41500091552734,
"oldClose": 101.26000213623047
},
{
"date": 20170118,
"from": "T1703",
"to": "T1706",
"newClose": 95.23999786376953,
"oldClose": 96.6500015258789
},
{
"date": 20170509,
"from": "T1706",
"to": "T1709",
"newClose": 94.01499938964844,
"oldClose": 94.69999694824219
},
{
"date": 20170808,
"from": "T1709",
"to": "T1712",
"newClose": 94.3550033569336,
"oldClose": 94.45500183105469
},
{
"date": 20171109,
"from": "T1712",
"to": "T1803",
"newClose": 92.80500030517578,
"oldClose": 92.73500061035156
},
{
"date": 20180207,
"from": "T1803",
"to": "T1806",
"newClose": 92.1449966430664,
"oldClose": 92.08999633789062
},
{
"date": 20180517,
"from": "T1806",
"to": "T1809",
"newClose": 93.8949966430664,
"oldClose": 93.91500091552734
},
{
"date": 20180814,
"from": "T1809",
"to": "T1812",
"newClose": 95.30500030517578,
"oldClose": 95.32499694824219
},
{
"date": 20181114,
"from": "T1812",
"to": "T1903",
"newClose": 96.44000244140625,
"oldClose": 96.4800033569336
},
{
"date": 20190220,
"from": "T1903",
"to": "T1906",
"newClose": 97.6500015258789,
"oldClose": 98.08999633789062
},
{
"date": 20190521,
"from": "T1906",
"to": "T1909",
"newClose": 96.63999938964844,
"oldClose": 97.20999908447266
},
{
"date": 20190814,
"from": "T1909",
"to": "T1912",
"newClose": 99.0199966430664,
"oldClose": 99.30500030517578
},
{
"date": 20191119,
"from": "T1912",
"to": "T2003",
"newClose": 98.16500091552734,
"oldClose": 98.50499725341797
},
{
"date": 20200218,
"from": "T2003",
"to": "T2006",
"newClose": 100.11000061035156,
"oldClose": 100.6449966430664
},
{
"date": 20200514,
"from": "T2006",
"to": "T2009",
"newClose": 100.98500061035156,
"oldClose": 101.69499969482422
},
{
"date": 20200819,
"from": "T2009",
"to": "T2012",
"newClose": 98.42500305175781,
"oldClose": 99.05500030517578
},
{
"date": 20201119,
"from": "T2012",
"to": "T2103",
"newClose": 96.95999908447266,
"oldClose": 97.1449966430664
},
{
"date": 20210218,
"from": "T2103",
"to": "T2106",
"newClose": 96.44499969482422,
"oldClose": 96.76499938964844
},
{
"date": 20210519,
"from": "T2106",
"to": "T2109",
"newClose": 98.12000274658203,
"oldClose": 98.51000213623047
},
{
"date": 20210818,
"from": "T2109",
"to": "T2112",
"newClose": 100.04499816894531,
"oldClose": 100.30500030517578
}
],
"TS": [
{
"date": 20180817,
"from": "",
"to": "TS1812",
"newClose": 99.16000366210938,
"oldClose": 0
},
{
"date": 20181116,
"from": "TS1812",
"to": "TS1903",
"newClose": 100.05999755859375,
"oldClose": 100.07499694824219
},
{
"date": 20190222,
"from": "TS1903",
"to": "TS1906",
"newClose": 100.51000213623047,
"oldClose": 100.5199966430664
},
{
"date": 20190521,
"from": "TS1906",
"to": "TS1912",
"newClose": 99.75499725341797,
"oldClose": 100.05999755859375
},
{
"date": 20190627,
"from": "TS1912",
"to": "TS2003",
"newClose": 99.7249984741211,
"oldClose": 99.88999938964844
},
{
"date": 20190926,
"from": "TS2003",
"to": "TS2006",
"newClose": 99.98999786376953,
"oldClose": 100.12000274658203
},
{
"date": 20200515,
"from": "TS2006",
"to": "TS2009",
"newClose": 102.13999938964844,
"oldClose": 102.44499969482422
},
{
"date": 20200821,
"from": "TS2009",
"to": "TS2012",
"newClose": 100.36000061035156,
"oldClose": 100.54499816894531
},
{
"date": 20201123,
"from": "TS2012",
"to": "TS2103",
"newClose": 100.01000213623047,
"oldClose": 99.8550033569336
},
{
"date": 20210223,
"from": "TS2103",
"to": "TS2106",
"newClose": 100.03500366210938,
"oldClose": 100.3949966430664
},
{
"date": 20210519,
"from": "TS2106",
"to": "TS2109",
"newClose": 100.30000305175781,
"oldClose": 100.54499816894531
},
{
"date": 20210819,
"from": "TS2109",
"to": "TS2112",
"newClose": 100.76000213623047,
"oldClose": 100.87000274658203
}
]
},
"DCE": {
"cs": [
{
"date": 20160104,
"from": "",
"to": "cs1605",
"newClose": 2050.0,
"oldClose": 0
},
{
"date": 20160321,
"from": "cs1605",
"to": "cs1609",
"newClose": 2001.0,
"oldClose": 2085.0
},
{
"date": 20160628,
"from": "cs1609",
"to": "cs1701",
"newClose": 1989.0,
"oldClose": 2217.0
},
{
"date": 20161122,
"from": "cs1701",
"to": "cs1705",
"newClose": 1891.0,
"oldClose": 1938.0
},
{
"date": 20170322,
"from": "cs1705",
"to": "cs1709",
"newClose": 2120.0,
"oldClose": 1911.0
},
{
"date": 20170808,
"from": "cs1709",
"to": "cs1801",
"newClose": 1952.0,
"oldClose": 1971.0
},
{
"date": 20171204,
"from": "cs1801",
"to": "cs1805",
"newClose": 2106.0,
"oldClose": 2071.0
},
{
"date": 20180402,
"from": "cs1805",
"to": "cs1809",
"newClose": 2137.0,
"oldClose": 2138.0
},
{
"date": 20180809,
"from": "cs1809",
"to": "cs1901",
"newClose": 2367.0,
"oldClose": 2318.0
},
{
"date": 20181211,
"from": "cs1901",
"to": "cs1905",
"newClose": 2335.0,
"oldClose": 2351.0
},
{
"date": 20190415,
"from": "cs1905",
"to": "cs1909",
"newClose": 2365.0,
"oldClose": 2305.0
},
{
"date": 20190823,
"from": "cs1909",
"to": "cs2001",
"newClose": 2331.0,
"oldClose": 2324.0
},
{
"date": 20191218,
"from": "cs2001",
"to": "cs2005",
"newClose": 2257.0,
"oldClose": 2194.0
},
{
"date": 20200414,
"from": "cs2005",
"to": "cs2009",
"newClose": 2390.0,
"oldClose": 2282.0
},
{
"date": 20200824,
"from": "cs2009",
"to": "cs2101",
"newClose": 2571.0,
"oldClose": 2508.0
},
{
"date": 20201211,
"from": "cs2101",
"to": "cs2105",
"newClose": 2917.0,
"oldClose": 2844.0
},
{
"date": 20210420,
"from": "cs2105",
"to": "cs2107",
"newClose": 3295.0,
"oldClose": 3255.0
},
{
"date": 20210622,
"from": "cs2107",
"to": "cs2109",
"newClose": 2981.0,
"oldClose": 3048.0
},
{
"date": 20210825,
"from": "cs2109",
"to": "cs2111",
"newClose": 2913.0,
"oldClose": 2918.0
}
],
"fb": [
{
"date": 20160104,
"from": "",
"to": "fb1601",
"newClose": 49.349998474121094,
"oldClose": 0
},
{
"date": 20160118,
"from": "fb1601",
"to": "fb1604",
"newClose": 61.599998474121094,
"oldClose": 45.900001525878906
},
{
"date": 20160408,
"from": "fb1604",
"to": "fb1701",
"newClose": 39.5,
"oldClose": 82.5
},
{
"date": 20170110,
"from": "fb1705",
"to": "fb1709",
"newClose": 85.05000305175781,
"oldClose": 0
},
{
"date": 20170711,
"from": "fb1709",
"to": "fb1801",
"newClose": 75.55000305175781,
"oldClose": 76.80000305175781
},
{
"date": 20180103,
"from": "fb1801",
"to": "fb1808",
"newClose": 73.5999984741211,
"oldClose": 109.80000305175781
},
{
"date": 20180508,
"from": "fb1808",
"to": "fb1901",
"newClose": 88.80000305175781,
"oldClose": 73.5999984741211
},
{
"date": 20190102,
"from": "fb1901",
"to": "fb1903",
"newClose": 76.6500015258789,
"oldClose": 106.55000305175781
},
{
"date": 20190301,
"from": "fb1903",
"to": "fb1904",
"newClose": 61.29999923706055,
"oldClose": 50.75
},
{
"date": 20190328,
"from": "fb1904",
"to": "fb1905",
"newClose": 65.9000015258789,
"oldClose": 68.4000015258789
},
{
"date": 20190506,
"from": "fb1905",
"to": "fb1906",
"newClose": 60.599998474121094,
"oldClose": 58.5
},
{
"date": 20190603,
"from": "fb1906",
"to": "fb1907",
"newClose": 55.900001525878906,
"oldClose": 53.54999923706055
},
{
"date": 20190701,
"from": "fb1907",
"to": "fb1908",
"newClose": 61.900001525878906,
"oldClose": 47.29999923706055
},
{
"date": 20190801,
"from": "fb1908",
"to": "fb1909",
"newClose": 64.0,
"oldClose": 63.70000076293945
},
{
"date": 20190902,
"from": "fb1909",
"to": "fb1910",
"newClose": 58.79999923706055,
"oldClose": 57.0
},
{
"date": 20191008,
"from": "fb1910",
"to": "fb1911",
"newClose": 66.6500015258789,
"oldClose": 76.8499984741211
},
{
"date": 20191202,
"from": "fb1911",
"to": "fb2005",
"newClose": 1478.5,
"oldClose": 85.0
},
{
"date": 20200506,
"from": "fb2005",
"to": "fb2009",
"newClose": 1353.0,
"oldClose": 1175.0
},
{
"date": 20200902,
"from": "fb2009",
"to": "fb2010",
"newClose": 1313.5,
"oldClose": 1110.0
},
{
"date": 20201009,
"from": "fb2010",
"to": "fb2011",
"newClose": 1228.5,
"oldClose": 1199.0
},
{
"date": 20201102,
"from": "fb2011",
"to": "fb2101",
"newClose": 1294.0,
"oldClose": 1265.5
},
{
"date": 20210104,
"from": "fb2101",
"to": "fb2102",
"newClose": 1320.5,
"oldClose": 1335.0
},
{
"date": 20210201,
"from": "fb2102",
"to": "fb2103",
"newClose": 1282.0,
"oldClose": 1240.0
},
{
"date": 20210301,
"from": "fb2103",
"to": "fb2105",
"newClose": 1325.0,
"oldClose": 1156.0
},
{
"date": 20210506,
"from": "fb2105",
"to": "fb2106",
"newClose": 1348.0,
"oldClose": 1309.0
},
{
"date": 20210601,
"from": "fb2106",
"to": "fb2109",
"newClose": 1417.0,
"oldClose": 1334.5
},
{
"date": 20210901,
"from": "fb2109",
"to": "fb2110",
"newClose": 1355.0,
"oldClose": 1262.0
},
{
"date": 20211008,
"from": "fb2110",
"to": "fb2201",
"newClose": 1438.5,
"oldClose": 1440.0
}
],
"i": [
{
"date": 20160104,
"from": "",
"to": "i1605",
"newClose": 321.5,
"oldClose": 0
},
{
"date": 20160321,
"from": "i1605",
"to": "i1609",
"newClose": 422.5,
"oldClose": 456.0
},
{
"date": 20160811,
"from": "i1609",
"to": "i1701",
"newClose": 431.0,
"oldClose": 487.0
},
{
"date": 20161125,
"from": "i1701",
"to": "i1705",
"newClose": 613.0,
"oldClose": 653.5
},
{
"date": 20170322,
"from": "i1705",
"to": "i1709",
"newClose": 577.0,
"oldClose": 665.5
},
{
"date": 20170808,
"from": "i1709",
"to": "i1801",
"newClose": 549.0,
"oldClose": 585.0
},
{
"date": 20171123,
"from": "i1801",
"to": "i1805",
"newClose": 507.5,
"oldClose": 491.5
},
{
"date": 20180404,
"from": "i1805",
"to": "i1809",
"newClose": 438.5,
"oldClose": 431.0
},
{
"date": 20180807,
"from": "i1809",
"to": "i1901",
"newClose": 515.0,
"oldClose": 503.5
},
{
"date": 20181203,
"from": "i1901",
"to": "i1905",
"newClose": 463.0,
"oldClose": 495.5
},
{
"date": 20190408,
"from": "i1905",
"to": "i1909",
"newClose": 653.5,
"oldClose": 712.0
},
{
"date": 20190725,
"from": "i1909",
"to": "i2001",
"newClose": 739.0,
"oldClose": 871.5
},
{
"date": 20191204,
"from": "i2001",
"to": "i2005",
"newClose": 619.5,
"oldClose": 662.0
},
{
"date": 20200331,
"from": "i2005",
"to": "i2009",
"newClose": 574.5,
"oldClose": 650.5
},
{
"date": 20200810,
"from": "i2009",
"to": "i2101",
"newClose": 822.5,
"oldClose": 894.0
},
{
"date": 20201207,
"from": "i2101",
"to": "i2105",
"newClose": 913.5,
"oldClose": 967.5
},
{
"date": 20210401,
"from": "i2105",
"to": "i2109",
"newClose": 975.5,
"oldClose": 1104.0
},
{
"date": 20210804,
"from": "i2109",
"to": "i2201",
"newClose": 941.0,
"oldClose": 1067.0
}
],
"j": [
{
"date": 20160104,
"from": "",
"to": "j1605",
"newClose": 636.0,
"oldClose": 0
},
{
"date": 20160325,
"from": "j1605",
"to": "j1609",
"newClose": 764.0,
"oldClose": 767.0
},
{
"date": 20160816,
"from": "j1609",
"to": "j1701",
"newClose": 1226.5,
"oldClose": 1261.0
},
{
"date": 20161202,
"from": "j1701",
"to": "j1705",
"newClose": 1728.0,
"oldClose": 2064.0
},
{
"date": 20170331,
"from": "j1705",
"to": "j1709",
"newClose": 1777.0,
"oldClose": 1900.5
},
{
"date": 20170808,
"from": "j1709",
"to": "j1801",
"newClose": 2081.0,
"oldClose": 2050.5
},
{
"date": 20171129,
"from": "j1801",
"to": "j1805",
"newClose": 2079.5,
"oldClose": 1997.0
},
{
"date": 20180411,
"from": "j1805",
"to": "j1809",
"newClose": 1745.5,
"oldClose": 1814.0
},
{
"date": 20180809,
"from": "j1809",
"to": "j1901",
"newClose": 2450.0,
"oldClose": 2469.0
},
{
"date": 20181205,
"from": "j1901",
"to": "j1905",
"newClose": 1986.0,
"oldClose": 2337.0
},
{
"date": 20190404,
"from": "j1905",
"to": "j1909",
"newClose": 2036.0,
"oldClose": 2026.5
},
{
"date": 20190806,
"from": "j1909",
"to": "j2001",
"newClose": 2003.0,
"oldClose": 2036.5
},
{
"date": 20191211,
"from": "j2001",
"to": "j2005",
"newClose": 1863.0,
"oldClose": 1918.0
},
{
"date": 20200331,
"from": "j2005",
"to": "j2009",
"newClose": 1655.5,
"oldClose": 1763.5
},
{
"date": 20200819,
"from": "j2009",
"to": "j2101",
"newClose": 2010.5,
"oldClose": 2008.5
},
{
"date": 20201119,
"from": "j2101",
"to": "j2102",
"newClose": 2335.0,
"oldClose": 2417.5
},
{
"date": 20201207,
"from": "j2102",
"to": "j2105",
"newClose": 2443.5,
"oldClose": 2532.5
},
{
"date": 20210416,
"from": "j2105",
"to": "j2109",
"newClose": 2540.0,
"oldClose": 2472.5
},
{
"date": 20210812,
"from": "j2109",
"to": "j2201",
"newClose": 2869.0,
"oldClose": 3103.5
}
],
"jd": [
{
"date": 20160104,
"from": "",
"to": "jd1605",
"newClose": 3188.0,
"oldClose": 0
},
{
"date": 20160324,
"from": "jd1605",
"to": "jd1609",
"newClose": 3787.0,
"oldClose": 3226.0
},
{
"date": 20160801,
"from": "jd1609",
"to": "jd1701",
"newClose": 3433.0,
"oldClose": 3820.0
},
{
"date": 20161129,
"from": "jd1701",
"to": "jd1705",
"newClose": 3521.0,
"oldClose": 3586.0
},
{
"date": 20170412,
"from": "jd1705",
"to": "jd1709",
"newClose": 3926.0,
"oldClose": 2878.0
},
{
"date": 20170808,
"from": "jd1709",
"to": "jd1801",
"newClose": 4345.0,
"oldClose": 4059.0
},
{
"date": 20171201,
"from": "jd1801",
"to": "jd1805",
"newClose": 3987.0,
"oldClose": 4497.0
},
{
"date": 20180409,
"from": "jd1805",
"to": "jd1809",
"newClose": 4044.0,
"oldClose": 3489.0
},
{
"date": 20180808,
"from": "jd1809",
"to": "jd1901",
"newClose": 3868.0,
"oldClose": 4230.0
},
{
"date": 20181211,
"from": "jd1901",
"to": "jd1905",
"newClose": 3511.0,
"oldClose": 4140.0
},
{
"date": 20190404,
"from": "jd1905",
"to": "jd1909",
"newClose": 4209.0,
"oldClose": 3499.0
},
{
"date": 20190807,
"from": "jd1909",
"to": "jd2001",
"newClose": 4271.0,
"oldClose": 4413.0
},
{
"date": 20191203,
"from": "jd2001",
"to": "jd2005",
"newClose": 4148.0,
"oldClose": 4415.0
},
{
"date": 20200203,
"from": "jd2005",
"to": "jd2009",
"newClose": 3832.0,
"oldClose": 3203.0
},
{
"date": 20200819,
"from": "jd2009",
"to": "jd2010",
"newClose": 3698.0,
"oldClose": 3739.0
},
{
"date": 20200916,
"from": "jd2010",
"to": "jd2011",
"newClose": 3509.0,
"oldClose": 3404.0
},
{
"date": 20201022,
"from": "jd2011",
"to": "jd2101",
"newClose": 3998.0,
"oldClose": 3520.0
},
{
"date": 20201209,
"from": "jd2101",
"to": "jd2105",
"newClose": 3674.0,
"oldClose": 3800.0
},
{
"date": 20210414,
"from": "jd2105",
"to": "jd2109",
"newClose": 4774.0,
"oldClose": 4295.0
},
{
"date": 20210818,
"from": "jd2109",
"to": "jd2201",
"newClose": 4324.0,
"oldClose": 4396.0
}
],
"jm": [
{
"date": 20160104,
"from": "",
"to": "jm1605",
"newClose": 555.0,
"oldClose": 0
},
{
"date": 20160331,
"from": "jm1605",
"to": "jm1609",
"newClose": 642.5,
"oldClose": 616.5
},
{
"date": 20160815,
"from": "jm1609",
"to": "jm1701",
"newClose": 865.0,
"oldClose": 766.5
},
{
"date": 20161130,
"from": "jm1701",
"to": "jm1705",
"newClose": 1294.5,
"oldClose": 1510.5
},
{
"date": 20170331,
"from": "jm1705",
"to": "jm1709",
"newClose": 1235.0,
"oldClose": 1271.0
},
{
"date": 20170808,
"from": "jm1709",
"to": "jm1801",
"newClose": 1281.5,
"oldClose": 1252.5
},
{
"date": 20171201,
"from": "jm1801",
"to": "jm1805",
"newClose": 1403.0,
"oldClose": 1363.0
},
{
"date": 20180411,
"from": "jm1805",
"to": "jm1809",
"newClose": 1149.5,
"oldClose": 1264.5
},
{
"date": 20180808,
"from": "jm1809",
"to": "jm1901",
"newClose": 1273.5,
"oldClose": 1208.5
},
{
"date": 20181212,
"from": "jm1901",
"to": "jm1905",
"newClose": 1204.0,
"oldClose": 1433.5
},
{
"date": 20190408,
"from": "jm1905",
"to": "jm1909",
"newClose": 1360.0,
"oldClose": 1258.5
},
{
"date": 20190820,
"from": "jm1909",
"to": "jm2001",
"newClose": 1338.0,
"oldClose": 1405.0
},
{
"date": 20191219,
"from": "jm2001",
"to": "jm2005",
"newClose": 1184.5,
"oldClose": 1236.0
},
{
"date": 20200403,
"from": "jm2005",
"to": "jm2009",
"newClose": 1075.0,
"oldClose": 1210.0
},
{
"date": 20200821,
"from": "jm2009",
"to": "jm2101",
"newClose": 1198.5,
"oldClose": 1217.5
},
{
"date": 20201029,
"from": "jm2101",
"to": "jm2102",
"newClose": 1322.5,
"oldClose": 1357.5
},
{
"date": 20201209,
"from": "jm2102",
"to": "jm2105",
"newClose": 1529.5,
"oldClose": 1596.5
},
{
"date": 20210420,
"from": "jm2105",
"to": "jm2109",
"newClose": 1739.5,
"oldClose": 1643.5
},
{
"date": 20210810,
"from": "jm2109",
"to": "jm2201",
"newClose": 2198.0,
"oldClose": 2344.0
}
],
"l": [
{
"date": 20160104,
"from": "",
"to": "l1605",
"newClose": 8030.0,
"oldClose": 0
},
{
"date": 20160325,
"from": "l1605",
"to": "l1609",
"newClose": 8725.0,
"oldClose": 9430.0
},
{
"date": 20160725,
"from": "l1609",
"to": "l1701",
"newClose": 8575.0,
"oldClose": 8865.0
},
{
"date": 20161128,
"from": "l1701",
"to": "l1705",
"newClose": 9655.0,
"oldClose": 9565.0
},
{
"date": 20170407,
"from": "l1705",
"to": "l1709",
"newClose": 9370.0,
"oldClose": 9485.0
},
{
"date": 20170808,
"from": "l1709",
"to": "l1801",
"newClose": 9780.0,
"oldClose": 9575.0
},
{
"date": 20171128,
"from": "l1801",
"to": "l1805",
"newClose": 9375.0,
"oldClose": 9510.0
},
{
"date": 20180404,
"from": "l1805",
"to": "l1809",
"newClose": 9145.0,
"oldClose": 9195.0
},
{
"date": 20180806,
"from": "l1809",
"to": "l1901",
"newClose": 9630.0,
"oldClose": 9630.0
},
{
"date": 20181129,
"from": "l1901",
"to": "l1905",
"newClose": 8270.0,
"oldClose": 8685.0
},
{
"date": 20190403,
"from": "l1905",
"to": "l1909",
"newClose": 8375.0,
"oldClose": 8505.0
},
{
"date": 20190809,
"from": "l1909",
"to": "l2001",
"newClose": 7560.0,
"oldClose": 7535.0
},
{
"date": 20191206,
"from": "l2001",
"to": "l2005",
"newClose": 7270.0,
"oldClose": 7255.0
},
{
"date": 20200331,
"from": "l2005",
"to": "l2009",
"newClose": 5505.0,
"oldClose": 5565.0
},
{
"date": 20200818,
"from": "l2009",
"to": "l2101",
"newClose": 7165.0,
"oldClose": 7175.0
},
{
"date": 20201215,
"from": "l2101",
"to": "l2105",
"newClose": 7815.0,
"oldClose": 7800.0
},
{
"date": 20210416,
"from": "l2105",
"to": "l2109",
"newClose": 8345.0,
"oldClose": 8355.0
},
{
"date": 20210817,
"from": "l2109",
"to": "l2201",
"newClose": 8415.0,
"oldClose": 8430.0
}
],
"m": [
{
"date": 20160104,
"from": "",
"to": "m1605",
"newClose": 2326.0,
"oldClose": 0
},
{
"date": 20160303,
"from": "m1605",
"to": "m1609",
"newClose": 2304.0,
"oldClose": 2328.0
},
{
"date": 20160715,
"from": "m1609",
"to": "m1701",
"newClose": 3155.0,
"oldClose": 3200.0
},
{
"date": 20161114,
"from": "m1701",
"to": "m1705",
"newClose": 2753.0,
"oldClose": 2868.0
},
{
"date": 20170316,
"from": "m1705",
"to": "m1709",
"newClose": 2889.0,
"oldClose": 2864.0
},
{
"date": 20170726,
"from": "m1709",
"to": "m1801",
"newClose": 2824.0,
"oldClose": 2808.0
},
{
"date": 20171122,
"from": "m1801",
"to": "m1805",
"newClose": 2826.0,
"oldClose": 2902.0
},
{
"date": 20180315,
"from": "m1805",
"to": "m1809",
"newClose": 2995.0,
"oldClose": 3036.0
},
{
"date": 20180723,
"from": "m1809",
"to": "m1901",
"newClose": 3154.0,
"oldClose": 3124.0
},
{
"date": 20181207,
"from": "m1901",
"to": "m1905",
"newClose": 2711.0,
"oldClose": 2911.0
},
{
"date": 20190402,
"from": "m1905",
"to": "m1909",
"newClose": 2634.0,
"oldClose": 2541.0
},
{
"date": 20190808,
"from": "m1909",
"to": "m2001",
"newClose": 2873.0,
"oldClose": 2883.0
},
{
"date": 20191126,
"from": "m2001",
"to": "m2005",
"newClose": 2761.0,
"oldClose": 2880.0
},
{
"date": 20200311,
"from": "m2005",
"to": "m2009",
"newClose": 2733.0,
"oldClose": 2681.0
},
{
"date": 20200804,
"from": "m2009",
"to": "m2101",
"newClose": 2927.0,
"oldClose": 2907.0
},
{
"date": 20201112,
"from": "m2101",
"to": "m2105",
"newClose": 3185.0,
"oldClose": 3204.0
},
{
"date": 20210331,
"from": "m2105",
"to": "m2109",
"newClose": 3383.0,
"oldClose": 3279.0
},
{
"date": 20210810,
"from": "m2109",
"to": "m2201",
"newClose": 3591.0,
"oldClose": 3628.0
}
],
"p": [
{
"date": 20160104,
"from": "",
"to": "p1605",
"newClose": 4804.0,
"oldClose": 0
},
{
"date": 20160315,
"from": "p1605",
"to": "p1609",
"newClose": 5178.0,
"oldClose": 5116.0
},
{
"date": 20160706,
"from": "p1609",
"to": "p1701",
"newClose": 5032.0,
"oldClose": 5080.0
},
{
"date": 20161123,
"from": "p1701",
"to": "p1705",
"newClose": 6092.0,
"oldClose": 6204.0
},
{
"date": 20170309,
"from": "p1705",
"to": "p1709",
"newClose": 5664.0,
"oldClose": 5882.0
},
{
"date": 20170705,
"from": "p1709",
"to": "p1801",
"newClose": 5144.0,
"oldClose": 5338.0
},
{
"date": 20171120,
"from": "p1801",
"to": "p1805",
"newClose": 5526.0,
"oldClose": 5434.0
},
{
"date": 20180321,
"from": "p1805",
"to": "p1809",
"newClose": 5090.0,
"oldClose": 5114.0
},
{
"date": 20180726,
"from": "p1809",
"to": "p1901",
"newClose": 4802.0,
"oldClose": 4692.0
},
{
"date": 20181204,
"from": "p1901",
"to": "p1905",
"newClose": 4514.0,
"oldClose": 4224.0
},
{
"date": 20190410,
"from": "p1905",
"to": "p1909",
"newClose": 4692.0,
"oldClose": 4466.0
},
{
"date": 20190807,
"from": "p1909",
"to": "p2001",
"newClose": 4630.0,
"oldClose": 4456.0
},
{
"date": 20191203,
"from": "p2001",
"to": "p2005",
"newClose": 5718.0,
"oldClose": 5594.0
},
{
"date": 20200407,
"from": "p2005",
"to": "p2009",
"newClose": 4738.0,
"oldClose": 4910.0
},
{
"date": 20200813,
"from": "p2009",
"to": "p2101",
"newClose": 5694.0,
"oldClose": 5876.0
},
{
"date": 20201209,
"from": "p2101",
"to": "p2102",
"newClose": 6808.0,
"oldClose": 6738.0
},
{
"date": 20201211,
"from": "p2102",
"to": "p2105",
"newClose": 6568.0,
"oldClose": 7022.0
},
{
"date": 20210412,
"from": "p2105",
"to": "p2109",
"newClose": 6716.0,
"oldClose": 7434.0
},
{
"date": 20210817,
"from": "p2109",
"to": "p2201",
"newClose": 8418.0,
"oldClose": 8948.0
}
],
"pp": [
{
"date": 20160104,
"from": "",
"to": "pp1605",
"newClose": 5792.0,
"oldClose": 0
},
{
"date": 20160328,
"from": "pp1605",
"to": "pp1609",
"newClose": 6836.0,
"oldClose": 7355.0
},
{
"date": 20160811,
"from": "pp1609",
"to": "pp1701",
"newClose": 7365.0,
"oldClose": 8212.0
},
{
"date": 20161201,
"from": "pp1701",
"to": "pp1705",
"newClose": 8330.0,
"oldClose": 8370.0
},
{
"date": 20170411,
"from": "pp1705",
"to": "pp1709",
"newClose": 7996.0,
"oldClose": 8100.0
},
{
"date": 20170809,
"from": "pp1709",
"to": "pp1801",
"newClose": 8779.0,
"oldClose": 8540.0
},
{
"date": 20171201,
"from": "pp1801",
"to": "pp1805",
"newClose": 8903.0,
"oldClose": 9030.0
},
{
"date": 20180409,
"from": "pp1805",
"to": "pp1809",
"newClose": 9062.0,
"oldClose": 8948.0
},
{
"date": 20180808,
"from": "pp1809",
"to": "pp1901",
"newClose": 10121.0,
"oldClose": 10037.0
},
{
"date": 20181211,
"from": "pp1901",
"to": "pp1905",
"newClose": 8287.0,
"oldClose": 8870.0
},
{
"date": 20190408,
"from": "pp1905",
"to": "pp1909",
"newClose": 8810.0,
"oldClose": 9002.0
},
{
"date": 20190808,
"from": "pp1909",
"to": "pp2001",
"newClose": 8055.0,
"oldClose": 8472.0
},
{
"date": 20191211,
"from": "pp2001",
"to": "pp2005",
"newClose": 7549.0,
"oldClose": 7844.0
},
{
"date": 20200402,
"from": "pp2005",
"to": "pp2009",
"newClose": 6151.0,
"oldClose": 6265.0
},
{
"date": 20200819,
"from": "pp2009",
"to": "pp2101",
"newClose": 7601.0,
"oldClose": 7692.0
},
{
"date": 20201215,
"from": "pp2101",
"to": "pp2105",
"newClose": 8284.0,
"oldClose": 8319.0
},
{
"date": 20210421,
"from": "pp2105",
"to": "pp2109",
"newClose": 8309.0,
"oldClose": 8514.0
},
{
"date": 20210819,
"from": "pp2109",
"to": "pp2201",
"newClose": 8271.0,
"oldClose": 8420.0
}
],
"v": [
{
"date": 20160104,
"from": "",
"to": "v1605",
"newClose": 4820.0,
"oldClose": 0
},
{
"date": 20160325,
"from": "v1605",
"to": "v1609",
"newClose": 5190.0,
"oldClose": 5290.0
},
{
"date": 20160818,
"from": "v1609",
"to": "v1701",
"newClose": 5655.0,
"oldClose": 5845.0
},
{
"date": 20161202,
"from": "v1701",
"to": "v1705",
"newClose": 6750.0,
"oldClose": 6950.0
},
{
"date": 20170407,
"from": "v1705",
"to": "v1709",
"newClose": 6035.0,
"oldClose": 6030.0
},
{
"date": 20170809,
"from": "v1709",
"to": "v1801",
"newClose": 7400.0,
"oldClose": 7540.0
},
{
"date": 20171129,
"from": "v1801",
"to": "v1805",
"newClose": 6260.0,
"oldClose": 6155.0
},
{
"date": 20180409,
"from": "v1805",
"to": "v1809",
"newClose": 6680.0,
"oldClose": 6635.0
},
{
"date": 20180809,
"from": "v1809",
"to": "v1901",
"newClose": 7370.0,
"oldClose": 7340.0
},
{
"date": 20181210,
"from": "v1901",
"to": "v1905",
"newClose": 6265.0,
"oldClose": 6535.0
},
{
"date": 20190403,
"from": "v1905",
"to": "v1909",
"newClose": 6700.0,
"oldClose": 6670.0
},
{
"date": 20190813,
"from": "v1909",
"to": "v2001",
"newClose": 6610.0,
"oldClose": 6790.0
},
{
"date": 20191211,
"from": "v2001",
"to": "v2005",
"newClose": 6630.0,
"oldClose": 6745.0
},
{
"date": 20200331,
"from": "v2005",
"to": "v2009",
"newClose": 5040.0,
"oldClose": 5015.0
},
{
"date": 20200817,
"from": "v2009",
"to": "v2101",
"newClose": 6580.0,
"oldClose": 6665.0
},
{
"date": 20201015,
"from": "v2101",
"to": "v2102",
"newClose": 6750.0,
"oldClose": 6860.0
},
{
"date": 20201104,
"from": "v2102",
"to": "v2105",
"newClose": 6740.0,
"oldClose": 6965.0
},
{
"date": 20210414,
"from": "v2105",
"to": "v2109",
"newClose": 8640.0,
"oldClose": 8715.0
},
{
"date": 20210819,
"from": "v2109",
"to": "v2201",
"newClose": 9140.0,
"oldClose": 9375.0
}
],
"y": [
{
"date": 20160104,
"from": "",
"to": "y1605",
"newClose": 5658.0,
"oldClose": 0
},
{
"date": 20160308,
"from": "y1605",
"to": "y1609",
"newClose": 5776.0,
"oldClose": 5708.0
},
{
"date": 20160706,
"from": "y1609",
"to": "y1701",
"newClose": 6246.0,
"oldClose": 6098.0
},
{
"date": 20161123,
"from": "y1701",
"to": "y1705",
"newClose": 6824.0,
"oldClose": 6730.0
},
{
"date": 20170321,
"from": "y1705",
"to": "y1709",
"newClose": 6498.0,
"oldClose": 6380.0
},
{
"date": 20170808,
"from": "y1709",
"to": "y1801",
"newClose": 6272.0,
"oldClose": 6136.0
},
{
"date": 20171201,
"from": "y1801",
"to": "y1805",
"newClose": 6046.0,
"oldClose": 5868.0
},
{
"date": 20180326,
"from": "y1805",
"to": "y1809",
"newClose": 5856.0,
"oldClose": 5680.0
},
{
"date": 20180726,
"from": "y1809",
"to": "y1901",
"newClose": 5754.0,
"oldClose": 5568.0
},
{
"date": 20181204,
"from": "y1901",
"to": "y1905",
"newClose": 5534.0,
"oldClose": 5400.0
},
{
"date": 20190403,
"from": "y1905",
"to": "y1909",
"newClose": 5598.0,
"oldClose": 5440.0
},
{
"date": 20190802,
"from": "y1909",
"to": "y2001",
"newClose": 5718.0,
"oldClose": 5558.0
},
{
"date": 20191128,
"from": "y2001",
"to": "y2005",
"newClose": 6214.0,
"oldClose": 6228.0
},
{
"date": 20200325,
"from": "y2005",
"to": "y2009",
"newClose": 5670.0,
"oldClose": 5602.0
},
{
"date": 20200805,
"from": "y2009",
"to": "y2101",
"newClose": 6282.0,
"oldClose": 6332.0
},
{
"date": 20201211,
"from": "y2101",
"to": "y2105",
"newClose": 7422.0,
"oldClose": 7954.0
},
{
"date": 20210409,
"from": "y2105",
"to": "y2109",
"newClose": 8054.0,
"oldClose": 8612.0
},
{
"date": 20210816,
"from": "y2109",
"to": "y2201",
"newClose": 9076.0,
"oldClose": 9320.0
}
],
"c": [
{
"date": 20160104,
"from": "",
"to": "c1605",
"newClose": 1893.0,
"oldClose": 0
},
{
"date": 20160222,
"from": "c1605",
"to": "c1609",
"newClose": 1643.0,
"oldClose": 1906.0
},
{
"date": 20160321,
"from": "c1609",
"to": "c1701",
"newClose": 1518.0,
"oldClose": 1631.0
},
{
"date": 20161114,
"from": "c1701",
"to": "c1705",
"newClose": 1567.0,
"oldClose": 1648.0
},
{
"date": 20170309,
"from": "c1705",
"to": "c1709",
"newClose": 1688.0,
"oldClose": 1609.0
},
{
"date": 20170808,
"from": "c1709",
"to": "c1801",
"newClose": 1680.0,
"oldClose": 1667.0
},
{
"date": 20171205,
"from": "c1801",
"to": "c1805",
"newClose": 1777.0,
"oldClose": 1722.0
},
{
"date": 20180315,
"from": "c1805",
"to": "c1809",
"newClose": 1764.0,
"oldClose": 1806.0
},
{
"date": 20180606,
"from": "c1809",
"to": "c1811",
"newClose": 1795.0,
"oldClose": 1763.0
},
{
"date": 20180608,
"from": "c1811",
"to": "c1901",
"newClose": 1809.0,
"oldClose": 1791.0
},
{
"date": 20181119,
"from": "c1901",
"to": "c1905",
"newClose": 1982.0,
"oldClose": 1898.0
},
{
"date": 20190326,
"from": "c1905",
"to": "c1909",
"newClose": 1847.0,
"oldClose": 1822.0
},
{
"date": 20190805,
"from": "c1909",
"to": "c2001",
"newClose": 1979.0,
"oldClose": 1922.0
},
{
"date": 20191202,
"from": "c2001",
"to": "c2005",
"newClose": 1892.0,
"oldClose": 1823.0
},
{
"date": 20200310,
"from": "c2005",
"to": "c2009",
"newClose": 2001.0,
"oldClose": 1947.0
},
{
"date": 20200806,
"from": "c2009",
"to": "c2101",
"newClose": 2252.0,
"oldClose": 2284.0
},
{
"date": 20201130,
"from": "c2101",
"to": "c2105",
"newClose": 2681.0,
"oldClose": 2601.0
},
{
"date": 20210412,
"from": "c2105",
"to": "c2109",
"newClose": 2617.0,
"oldClose": 2674.0
},
{
"date": 20210813,
"from": "c2109",
"to": "c2201",
"newClose": 2600.0,
"oldClose": 2631.0
}
],
"bb": [
{
"date": 20160104,
"from": "",
"to": "bb1601",
"newClose": 83.4000015258789,
"oldClose": 0
},
{
"date": 20160118,
"from": "bb1601",
"to": "bb1609",
"newClose": 81.25,
"oldClose": 72.1500015258789
},
{
"date": 20160405,
"from": "bb1609",
"to": "bb1701",
"newClose": 76.30000305175781,
"oldClose": 81.0999984741211
},
{
"date": 20161118,
"from": "bb1705",
"to": "bb1711",
"newClose": 107.80000305175781,
"oldClose": 94.6500015258789
},
{
"date": 20170206,
"from": "bb1711",
"to": "bb1712",
"newClose": 105.44999694824219,
"oldClose": 96.0
},
{
"date": 20170516,
"from": "bb1712",
"to": "bb1804",
"newClose": 99.05000305175781,
"oldClose": 106.8499984741211
},
{
"date": 20180115,
"from": "bb1804",
"to": "bb1805",
"newClose": 107.55000305175781,
"oldClose": 131.5
},
{
"date": 20180514,
"from": "bb1805",
"to": "bb1809",
"newClose": 130.8000030517578,
"oldClose": 148.0
},
{
"date": 20180726,
"from": "bb1809",
"to": "bb1901",
"newClose": 136.35000610351562,
"oldClose": 147.0
},
{
"date": 20180912,
"from": "bb1901",
"to": "bb1903",
"newClose": 142.89999389648438,
"oldClose": 136.35000610351562
},
{
"date": 20181128,
"from": "bb1903",
"to": "bb1906",
"newClose": 121.19999694824219,
"oldClose": 130.0
},
{
"date": 20190312,
"from": "bb1906",
"to": "bb1910",
"newClose": 170.10000610351562,
"oldClose": 139.35000610351562
},
{
"date": 20190909,
"from": "bb1910",
"to": "bb2001",
"newClose": 161.35000610351562,
"oldClose": 156.64999389648438
},
{
"date": 20191024,
"from": "bb2001",
"to": "bb2003",
"newClose": 157.0,
"oldClose": 153.85000610351562
},
{
"date": 20200305,
"from": "bb2003",
"to": "bb2004",
"newClose": 167.75,
"oldClose": 247.3000030517578
},
{
"date": 20200407,
"from": "bb2004",
"to": "bb2005",
"newClose": 167.9499969482422,
"oldClose": 230.5
},
{
"date": 20200506,
"from": "bb2005",
"to": "bb2006",
"newClose": 185.89999389648438,
"oldClose": 226.5
},
{
"date": 20200511,
"from": "bb2006",
"to": "bb2009",
"newClose": 178.8000030517578,
"oldClose": 185.0500030517578
},
{
"date": 20200907,
"from": "bb2009",
"to": "bb2012",
"newClose": 337.95001220703125,
"oldClose": 224.39999389648438
},
{
"date": 20201125,
"from": "bb2012",
"to": "bb2101",
"newClose": 249.0,
"oldClose": 300.0
},
{
"date": 20210507,
"from": "bb2105",
"to": "bb2106",
"newClose": 210.60000610351562,
"oldClose": 348.29998779296875
},
{
"date": 20210604,
"from": "bb2106",
"to": "bb2109",
"newClose": 228.25,
"oldClose": 210.60000610351562
},
{
"date": 20210909,
"from": "bb2109",
"to": "bb2110",
"newClose": 230.64999389648438,
"oldClose": 228.25
}
],
"b": [
{
"date": 20160104,
"from": "",
"to": "b1605",
"newClose": 3275.0,
"oldClose": 0
},
{
"date": 20160107,
"from": "b1605",
"to": "b1611",
"newClose": 3235.0,
"oldClose": 3178.0
},
{
"date": 20160126,
"from": "b1611",
"to": "b1701",
"newClose": 3324.0,
"oldClose": 3240.0
},
{
"date": 20160411,
"from": "b1701",
"to": "b1703",
"newClose": 3296.0,
"oldClose": 3241.0
},
{
"date": 20160713,
"from": "b1703",
"to": "b1705",
"newClose": 3815.0,
"oldClose": 3960.0
},
{
"date": 20161031,
"from": "b1705",
"to": "b1709",
"newClose": 3801.0,
"oldClose": 3821.0
},
{
"date": 20170317,
"from": "b1709",
"to": "b1711",
"newClose": 4000.0,
"oldClose": 3950.0
},
{
"date": 20170327,
"from": "b1711",
"to": "b1801",
"newClose": 3970.0,
"oldClose": 4000.0
},
{
"date": 20170512,
"from": "b1801",
"to": "b1803",
"newClose": 3851.0,
"oldClose": 3825.0
},
{
"date": 20170601,
"from": "b1803",
"to": "b1805",
"newClose": 3180.0,
"oldClose": 3851.0
},
{
"date": 20180208,
"from": "b1805",
"to": "b1809",
"newClose": 3364.0,
"oldClose": 3371.0
},
{
"date": 20180504,
"from": "b1809",
"to": "b1901",
"newClose": 3643.0,
"oldClose": 3554.0
},
{
"date": 20180718,
"from": "b1901",
"to": "b1905",
"newClose": 3316.0,
"oldClose": 3494.0
},
{
"date": 20181210,
"from": "b1905",
"to": "b1909",
"newClose": 3125.0,
"oldClose": 3047.0
},
{
"date": 20190801,
"from": "b1909",
"to": "b1910",
"newClose": 3030.0,
"oldClose": 3021.0
},
{
"date": 20190902,
"from": "b1910",
"to": "b1911",
"newClose": 3420.0,
"oldClose": 3410.0
},
{
"date": 20191008,
"from": "b1911",
"to": "b1912",
"newClose": 3218.0,
"oldClose": 3221.0
},
{
"date": 20191101,
"from": "b1912",
"to": "b2001",
"newClose": 3343.0,
"oldClose": 3373.0
},
{
"date": 20191202,
"from": "b2001",
"to": "b2002",
"newClose": 3142.0,
"oldClose": 3149.0
},
{
"date": 20200102,
"from": "b2002",
"to": "b2003",
"newClose": 3292.0,
"oldClose": 3298.0
},
{
"date": 20200206,
"from": "b2003",
"to": "b2005",
"newClose": 3083.0,
"oldClose": 3093.0
},
{
"date": 20200415,
"from": "b2005",
"to": "b2006",
"newClose": 3059.0,
"oldClose": 3081.0
},
{
"date": 20200522,
"from": "b2006",
"to": "b2007",
"newClose": 2912.0,
"oldClose": 2903.0
},
{
"date": 20200619,
"from": "b2007",
"to": "b2008",
"newClose": 3065.0,
"oldClose": 2974.0
},
{
"date": 20200707,
"from": "b2008",
"to": "b2009",
"newClose": 3206.0,
"oldClose": 3157.0
},
{
"date": 20200810,
"from": "b2009",
"to": "b2010",
"newClose": 3261.0,
"oldClose": 3247.0
},
{
"date": 20200908,
"from": "b2010",
"to": "b2011",
"newClose": 3555.0,
"oldClose": 3520.0
},
{
"date": 20201016,
"from": "b2011",
"to": "b2012",
"newClose": 3753.0,
"oldClose": 3749.0
},
{
"date": 20201113,
"from": "b2012",
"to": "b2102",
"newClose": 3816.0,
"oldClose": 3789.0
},
{
"date": 20210115,
"from": "b2102",
"to": "b2103",
"newClose": 4326.0,
"oldClose": 4340.0
},
{
"date": 20210223,
"from": "b2103",
"to": "b2104",
"newClose": 4452.0,
"oldClose": 4423.0
},
{
"date": 20210308,
"from": "b2104",
"to": "b2105",
"newClose": 4359.0,
"oldClose": 4518.0
},
{
"date": 20210414,
"from": "b2105",
"to": "b2106",
"newClose": 4046.0,
"oldClose": 4008.0
},
{
"date": 20210517,
"from": "b2106",
"to": "b2107",
"newClose": 4326.0,
"oldClose": 4266.0
},
{
"date": 20210617,
"from": "b2107",
"to": "b2108",
"newClose": 4040.0,
"oldClose": 3986.0
},
{
"date": 20210713,
"from": "b2108",
"to": "b2109",
"newClose": 4393.0,
"oldClose": 4330.0
},
{
"date": 20210817,
"from": "b2109",
"to": "b2110",
"newClose": 4559.0,
"oldClose": 4525.0
},
{
"date": 20210917,
"from": "b2110",
"to": "b2111",
"newClose": 4624.0,
"oldClose": 4544.0
}
],
"a": [
{
"date": 20160104,
"from": "",
"to": "a1605",
"newClose": 3603.0,
"oldClose": 0
},
{
"date": 20160309,
"from": "a1605",
"to": "a1609",
"newClose": 3471.0,
"oldClose": 3580.0
},
{
"date": 20160714,
"from": "a1609",
"to": "a1701",
"newClose": 3912.0,
"oldClose": 3871.0
},
{
"date": 20161129,
"from": "a1701",
"to": "a1705",
"newClose": 4089.0,
"oldClose": 3960.0
},
{
"date": 20170323,
"from": "a1705",
"to": "a1709",
"newClose": 3871.0,
"oldClose": 3842.0
},
{
"date": 20170808,
"from": "a1709",
"to": "a1801",
"newClose": 3951.0,
"oldClose": 3863.0
},
{
"date": 20171211,
"from": "a1801",
"to": "a1805",
"newClose": 3621.0,
"oldClose": 3457.0
},
{
"date": 20180409,
"from": "a1805",
"to": "a1809",
"newClose": 4031.0,
"oldClose": 3913.0
},
{
"date": 20180801,
"from": "a1809",
"to": "a1901",
"newClose": 3628.0,
"oldClose": 3514.0
},
{
"date": 20181221,
"from": "a1901",
"to": "a1905",
"newClose": 3400.0,
"oldClose": 3144.0
},
{
"date": 20190422,
"from": "a1905",
"to": "a1909",
"newClose": 3412.0,
"oldClose": 3309.0
},
{
"date": 20190815,
"from": "a1909",
"to": "a2001",
"newClose": 3424.0,
"oldClose": 3383.0
},
{
"date": 20191204,
"from": "a2001",
"to": "a2005",
"newClose": 3848.0,
"oldClose": 3478.0
},
{
"date": 20200408,
"from": "a2005",
"to": "a2009",
"newClose": 4519.0,
"oldClose": 4836.0
},
{
"date": 20200819,
"from": "a2009",
"to": "a2101",
"newClose": 4482.0,
"oldClose": 4640.0
},
{
"date": 20201217,
"from": "a2101",
"to": "a2105",
"newClose": 5289.0,
"oldClose": 5272.0
},
{
"date": 20210421,
"from": "a2105",
"to": "a2109",
"newClose": 5771.0,
"oldClose": 5712.0
},
{
"date": 20210816,
"from": "a2109",
"to": "a2111",
"newClose": 5839.0,
"oldClose": 5879.0
}
],
"eg": [
{
"date": 20181211,
"from": "",
"to": "eg1906",
"newClose": 5541.0,
"oldClose": 0
},
{
"date": 20190507,
"from": "eg1906",
"to": "eg1909",
"newClose": 4712.0,
"oldClose": 4578.0
},
{
"date": 20190808,
"from": "eg1909",
"to": "eg2001",
"newClose": 4373.0,
"oldClose": 4396.0
},
{
"date": 20191210,
"from": "eg2001",
"to": "eg2005",
"newClose": 4645.0,
"oldClose": 4849.0
},
{
"date": 20200413,
"from": "eg2005",
"to": "eg2009",
"newClose": 3504.0,
"oldClose": 3339.0
},
{
"date": 20200817,
"from": "eg2009",
"to": "eg2101",
"newClose": 3986.0,
"oldClose": 3805.0
},
{
"date": 20201211,
"from": "eg2101",
"to": "eg2105",
"newClose": 4038.0,
"oldClose": 3907.0
},
{
"date": 20210416,
"from": "eg2105",
"to": "eg2109",
"newClose": 4842.0,
"oldClose": 5077.0
},
{
"date": 20210824,
"from": "eg2109",
"to": "eg2201",
"newClose": 4941.0,
"oldClose": 5030.0
}
],
"rr": [
{
"date": 20190816,
"from": "",
"to": "rr2001",
"newClose": 3685.0,
"oldClose": 0
},
{
"date": 20191212,
"from": "rr2001",
"to": "rr2005",
"newClose": 3459.0,
"oldClose": 3396.0
},
{
"date": 20200325,
"from": "rr2005",
"to": "rr2009",
"newClose": 3454.0,
"oldClose": 3404.0
},
{
"date": 20200702,
"from": "rr2009",
"to": "rr2010",
"newClose": 3429.0,
"oldClose": 3422.0
},
{
"date": 20200706,
"from": "rr2010",
"to": "rr2012",
"newClose": 3455.0,
"oldClose": 3434.0
},
{
"date": 20200901,
"from": "rr2012",
"to": "rr2102",
"newClose": 3519.0,
"oldClose": 3487.0
},
{
"date": 20201105,
"from": "rr2102",
"to": "rr2103",
"newClose": 3655.0,
"oldClose": 3630.0
},
{
"date": 20201201,
"from": "rr2103",
"to": "rr2104",
"newClose": 3647.0,
"oldClose": 3609.0
},
{
"date": 20210301,
"from": "rr2104",
"to": "rr2105",
"newClose": 3650.0,
"oldClose": 3618.0
},
{
"date": 20210401,
"from": "rr2105",
"to": "rr2108",
"newClose": 3615.0,
"oldClose": 3540.0
},
{
"date": 20210506,
"from": "rr2108",
"to": "rr2110",
"newClose": 3659.0,
"oldClose": 3645.0
},
{
"date": 20210705,
"from": "rr2110",
"to": "rr2111",
"newClose": 3565.0,
"oldClose": 3548.0
},
{
"date": 20210802,
"from": "rr2111",
"to": "rr2112",
"newClose": 3550.0,
"oldClose": 3538.0
},
{
"date": 20210902,
"from": "rr2112",
"to": "rr2201",
"newClose": 3558.0,
"oldClose": 3534.0
},
{
"date": 20211011,
"from": "rr2201",
"to": "rr2202",
"newClose": 3466.0,
"oldClose": 3459.0
}
],
"eb": [
{
"date": 20190926,
"from": "",
"to": "eb2005",
"newClose": 8056.0,
"oldClose": 0
},
{
"date": 20200414,
"from": "eb2005",
"to": "eb2009",
"newClose": 5400.0,
"oldClose": 5220.0
},
{
"date": 20200821,
"from": "eb2009",
"to": "eb2101",
"newClose": 5650.0,
"oldClose": 5299.0
},
{
"date": 20201221,
"from": "eb2101",
"to": "eb2102",
"newClose": 6698.0,
"oldClose": 6674.0
},
{
"date": 20210125,
"from": "eb2102",
"to": "eb2103",
"newClose": 6916.0,
"oldClose": 6884.0
},
{
"date": 20210226,
"from": "eb2103",
"to": "eb2105",
"newClose": 9760.0,
"oldClose": 9900.0
},
{
"date": 20210426,
"from": "eb2105",
"to": "eb2106",
"newClose": 9048.0,
"oldClose": 9240.0
},
{
"date": 20210526,
"from": "eb2106",
"to": "eb2107",
"newClose": 8976.0,
"oldClose": 9179.0
},
{
"date": 20210622,
"from": "eb2107",
"to": "eb2108",
"newClose": 8535.0,
"oldClose": 8625.0
},
{
"date": 20210721,
"from": "eb2108",
"to": "eb2109",
"newClose": 8910.0,
"oldClose": 8995.0
},
{
"date": 20210817,
"from": "eb2109",
"to": "eb2110",
"newClose": 8529.0,
"oldClose": 8570.0
},
{
"date": 20210927,
"from": "eb2110",
"to": "eb2111",
"newClose": 8985.0,
"oldClose": 9000.0
}
],
"pg": [
{
"date": 20200330,
"from": "",
"to": "pg2011",
"newClose": 2366.0,
"oldClose": 0
},
{
"date": 20201023,
"from": "pg2011",
"to": "pg2012",
"newClose": 3774.0,
"oldClose": 3724.0
},
{
"date": 20201123,
"from": "pg2012",
"to": "pg2101",
"newClose": 3556.0,
"oldClose": 3544.0
},
{
"date": 20201218,
"from": "pg2101",
"to": "pg2102",
"newClose": 3993.0,
"oldClose": 3951.0
},
{
"date": 20210122,
"from": "pg2102",
"to": "pg2103",
"newClose": 3490.0,
"oldClose": 3658.0
},
{
"date": 20210223,
"from": "pg2103",
"to": "pg2104",
"newClose": 3927.0,
"oldClose": 3506.0
},
{
"date": 20210323,
"from": "pg2104",
"to": "pg2105",
"newClose": 3920.0,
"oldClose": 4019.0
},
{
"date": 20210416,
"from": "pg2105",
"to": "pg2106",
"newClose": 3810.0,
"oldClose": 3909.0
},
{
"date": 20210521,
"from": "pg2106",
"to": "pg2107",
"newClose": 4090.0,
"oldClose": 4078.0
},
{
"date": 20210610,
"from": "pg2107",
"to": "pg2108",
"newClose": 4394.0,
"oldClose": 4392.0
},
{
"date": 20210715,
"from": "pg2108",
"to": "pg2109",
"newClose": 4819.0,
"oldClose": 4825.0
},
{
"date": 20210816,
"from": "pg2109",
"to": "pg2110",
"newClose": 5158.0,
"oldClose": 5161.0
},
{
"date": 20210915,
"from": "pg2110",
"to": "pg2111",
"newClose": 5564.0,
"oldClose": 5550.0
}
]
},
"SHFE": {
"ag": [
{
"date": 20160104,
"from": "",
"to": "ag1606",
"newClose": 3305.0,
"oldClose": 0
},
{
"date": 20160509,
"from": "ag1606",
"to": "ag1612",
"newClose": 3828.0,
"oldClose": 3723.0
},
{
"date": 20161111,
"from": "ag1612",
"to": "ag1706",
"newClose": 4452.0,
"oldClose": 4313.0
},
{
"date": 20170510,
"from": "ag1706",
"to": "ag1712",
"newClose": 4002.0,
"oldClose": 3899.0
},
{
"date": 20171121,
"from": "ag1712",
"to": "ag1806",
"newClose": 3988.0,
"oldClose": 3873.0
},
{
"date": 20180507,
"from": "ag1806",
"to": "ag1812",
"newClose": 3754.0,
"oldClose": 3656.0
},
{
"date": 20181107,
"from": "ag1812",
"to": "ag1906",
"newClose": 3575.0,
"oldClose": 3545.0
},
{
"date": 20190508,
"from": "ag1906",
"to": "ag1912",
"newClose": 3607.0,
"oldClose": 3549.0
},
{
"date": 20191113,
"from": "ag1912",
"to": "ag2002",
"newClose": 4150.0,
"oldClose": 4123.0
},
{
"date": 20200103,
"from": "ag2002",
"to": "ag2006",
"newClose": 4452.0,
"oldClose": 4395.0
},
{
"date": 20200518,
"from": "ag2006",
"to": "ag2012",
"newClose": 4248.0,
"oldClose": 4201.0
},
{
"date": 20201126,
"from": "ag2012",
"to": "ag2102",
"newClose": 4938.0,
"oldClose": 4895.0
},
{
"date": 20210126,
"from": "ag2102",
"to": "ag2106",
"newClose": 5293.0,
"oldClose": 5222.0
},
{
"date": 20210526,
"from": "ag2106",
"to": "ag2112",
"newClose": 5861.0,
"oldClose": 5754.0
}
],
"al": [
{
"date": 20160104,
"from": "",
"to": "al1603",
"newClose": 10630.0,
"oldClose": 0
},
{
"date": 20160128,
"from": "al1603",
"to": "al1604",
"newClose": 10760.0,
"oldClose": 10745.0
},
{
"date": 20160229,
"from": "al1604",
"to": "al1605",
"newClose": 11300.0,
"oldClose": 11265.0
},
{
"date": 20160331,
"from": "al1605",
"to": "al1606",
"newClose": 11750.0,
"oldClose": 11775.0
},
{
"date": 20160428,
"from": "al1606",
"to": "al1607",
"newClose": 12480.0,
"oldClose": 12485.0
},
{
"date": 20160602,
"from": "al1607",
"to": "al1608",
"newClose": 12145.0,
"oldClose": 12305.0
},
{
"date": 20160704,
"from": "al1608",
"to": "al1609",
"newClose": 12535.0,
"oldClose": 12685.0
},
{
"date": 20160802,
"from": "al1609",
"to": "al1610",
"newClose": 12205.0,
"oldClose": 12315.0
},
{
"date": 20160902,
"from": "al1610",
"to": "al1611",
"newClose": 12205.0,
"oldClose": 12345.0
},
{
"date": 20160929,
"from": "al1611",
"to": "al1612",
"newClose": 12350.0,
"oldClose": 12590.0
},
{
"date": 20161103,
"from": "al1612",
"to": "al1701",
"newClose": 13410.0,
"oldClose": 13665.0
},
{
"date": 20161123,
"from": "al1701",
"to": "al1702",
"newClose": 13860.0,
"oldClose": 14100.0
},
{
"date": 20170103,
"from": "al1702",
"to": "al1703",
"newClose": 12630.0,
"oldClose": 12590.0
},
{
"date": 20170208,
"from": "al1703",
"to": "al1704",
"newClose": 13775.0,
"oldClose": 13695.0
},
{
"date": 20170306,
"from": "al1704",
"to": "al1705",
"newClose": 13830.0,
"oldClose": 13730.0
},
{
"date": 20170412,
"from": "al1705",
"to": "al1706",
"newClose": 13970.0,
"oldClose": 13865.0
},
{
"date": 20170509,
"from": "al1706",
"to": "al1707",
"newClose": 13855.0,
"oldClose": 13790.0
},
{
"date": 20170608,
"from": "al1707",
"to": "al1708",
"newClose": 13825.0,
"oldClose": 13785.0
},
{
"date": 20170706,
"from": "al1708",
"to": "al1709",
"newClose": 14210.0,
"oldClose": 14125.0
},
{
"date": 20170808,
"from": "al1709",
"to": "al1710",
"newClose": 15435.0,
"oldClose": 15335.0
},
{
"date": 20170825,
"from": "al1710",
"to": "al1711",
"newClose": 16705.0,
"oldClose": 16620.0
},
{
"date": 20170927,
"from": "al1711",
"to": "al1712",
"newClose": 16600.0,
"oldClose": 16490.0
},
{
"date": 20171030,
"from": "al1712",
"to": "al1801",
"newClose": 16380.0,
"oldClose": 16275.0
},
{
"date": 20171201,
"from": "al1801",
"to": "al1802",
"newClose": 14705.0,
"oldClose": 14600.0
},
{
"date": 20180102,
"from": "al1802",
"to": "al1803",
"newClose": 15175.0,
"oldClose": 15075.0
},
{
"date": 20180206,
"from": "al1803",
"to": "al1804",
"newClose": 14225.0,
"oldClose": 14140.0
},
{
"date": 20180301,
"from": "al1804",
"to": "al1805",
"newClose": 14460.0,
"oldClose": 14390.0
},
{
"date": 20180409,
"from": "al1805",
"to": "al1806",
"newClose": 14270.0,
"oldClose": 14200.0
},
{
"date": 20180508,
"from": "al1806",
"to": "al1807",
"newClose": 14755.0,
"oldClose": 14690.0
},
{
"date": 20180608,
"from": "al1807",
"to": "al1808",
"newClose": 14910.0,
"oldClose": 14835.0
},
{
"date": 20180706,
"from": "al1808",
"to": "al1809",
"newClose": 14080.0,
"oldClose": 14010.0
},
{
"date": 20180808,
"from": "al1809",
"to": "al1810",
"newClose": 14495.0,
"oldClose": 14420.0
},
{
"date": 20180905,
"from": "al1810",
"to": "al1811",
"newClose": 14670.0,
"oldClose": 14610.0
},
{
"date": 20181016,
"from": "al1811",
"to": "al1812",
"newClose": 14245.0,
"oldClose": 14250.0
},
{
"date": 20181106,
"from": "al1812",
"to": "al1901",
"newClose": 13915.0,
"oldClose": 13945.0
},
{
"date": 20181212,
"from": "al1901",
"to": "al1902",
"newClose": 13615.0,
"oldClose": 13575.0
},
{
"date": 20190108,
"from": "al1902",
"to": "al1903",
"newClose": 13390.0,
"oldClose": 13350.0
},
{
"date": 20190220,
"from": "al1903",
"to": "al1904",
"newClose": 13520.0,
"oldClose": 13480.0
},
{
"date": 20190226,
"from": "al1904",
"to": "al1905",
"newClose": 13690.0,
"oldClose": 13700.0
},
{
"date": 20190418,
"from": "al1905",
"to": "al1906",
"newClose": 14055.0,
"oldClose": 14025.0
},
{
"date": 20190515,
"from": "al1906",
"to": "al1907",
"newClose": 14340.0,
"oldClose": 14335.0
},
{
"date": 20190617,
"from": "al1907",
"to": "al1908",
"newClose": 13810.0,
"oldClose": 13855.0
},
{
"date": 20190717,
"from": "al1908",
"to": "al1909",
"newClose": 13885.0,
"oldClose": 13865.0
},
{
"date": 20190809,
"from": "al1909",
"to": "al1910",
"newClose": 13915.0,
"oldClose": 13910.0
},
{
"date": 20190909,
"from": "al1910",
"to": "al1911",
"newClose": 14385.0,
"oldClose": 14420.0
},
{
"date": 20191015,
"from": "al1911",
"to": "al1912",
"newClose": 13765.0,
"oldClose": 13790.0
},
{
"date": 20191115,
"from": "al1912",
"to": "al2001",
"newClose": 13670.0,
"oldClose": 13825.0
},
{
"date": 20191212,
"from": "al2001",
"to": "al2002",
"newClose": 13880.0,
"oldClose": 13955.0
},
{
"date": 20200110,
"from": "al2002",
"to": "al2003",
"newClose": 14110.0,
"oldClose": 14165.0
},
{
"date": 20200207,
"from": "al2003",
"to": "al2004",
"newClose": 13745.0,
"oldClose": 13725.0
},
{
"date": 20200304,
"from": "al2004",
"to": "al2005",
"newClose": 13250.0,
"oldClose": 13205.0
},
{
"date": 20200330,
"from": "al2005",
"to": "al2006",
"newClose": 11540.0,
"oldClose": 11515.0
},
{
"date": 20200508,
"from": "al2006",
"to": "al2007",
"newClose": 12430.0,
"oldClose": 12620.0
},
{
"date": 20200622,
"from": "al2007",
"to": "al2008",
"newClose": 13695.0,
"oldClose": 13865.0
},
{
"date": 20200727,
"from": "al2008",
"to": "al2009",
"newClose": 14220.0,
"oldClose": 14440.0
},
{
"date": 20200826,
"from": "al2009",
"to": "al2010",
"newClose": 14665.0,
"oldClose": 14765.0
},
{
"date": 20200925,
"from": "al2010",
"to": "al2011",
"newClose": 13945.0,
"oldClose": 14090.0
},
{
"date": 20201028,
"from": "al2011",
"to": "al2012",
"newClose": 14400.0,
"oldClose": 14585.0
},
{
"date": 20201126,
"from": "al2012",
"to": "al2101",
"newClose": 15870.0,
"oldClose": 16005.0
},
{
"date": 20201223,
"from": "al2101",
"to": "al2102",
"newClose": 15705.0,
"oldClose": 15890.0
},
{
"date": 20210115,
"from": "al2102",
"to": "al2103",
"newClose": 14875.0,
"oldClose": 14865.0
},
{
"date": 20210219,
"from": "al2103",
"to": "al2104",
"newClose": 16490.0,
"oldClose": 16485.0
},
{
"date": 20210315,
"from": "al2104",
"to": "al2105",
"newClose": 17605.0,
"oldClose": 17650.0
},
{
"date": 20210421,
"from": "al2105",
"to": "al2106",
"newClose": 18000.0,
"oldClose": 18010.0
},
{
"date": 20210520,
"from": "al2106",
"to": "al2107",
"newClose": 19140.0,
"oldClose": 19125.0
},
{
"date": 20210623,
"from": "al2107",
"to": "al2108",
"newClose": 18705.0,
"oldClose": 18725.0
},
{
"date": 20210721,
"from": "al2108",
"to": "al2109",
"newClose": 18885.0,
"oldClose": 18875.0
},
{
"date": 20210820,
"from": "al2109",
"to": "al2110",
"newClose": 20110.0,
"oldClose": 20115.0
},
{
"date": 20210922,
"from": "al2110",
"to": "al2111",
"newClose": 23080.0,
"oldClose": 23080.0
}
],
"au": [
{
"date": 20160104,
"from": "",
"to": "au1606",
"newClose": 228.1999969482422,
"oldClose": 0
},
{
"date": 20160504,
"from": "au1606",
"to": "au1612",
"newClose": 270.6499938964844,
"oldClose": 268.3999938964844
},
{
"date": 20161109,
"from": "au1612",
"to": "au1706",
"newClose": 291.6499938964844,
"oldClose": 288.70001220703125
},
{
"date": 20170502,
"from": "au1706",
"to": "au1712",
"newClose": 283.8999938964844,
"oldClose": 280.75
},
{
"date": 20171110,
"from": "au1712",
"to": "au1806",
"newClose": 280.8500061035156,
"oldClose": 276.5
},
{
"date": 20180507,
"from": "au1806",
"to": "au1812",
"newClose": 275.1499938964844,
"oldClose": 271.3500061035156
},
{
"date": 20181112,
"from": "au1812",
"to": "au1906",
"newClose": 276.54998779296875,
"oldClose": 272.20001220703125
},
{
"date": 20190510,
"from": "au1906",
"to": "au1912",
"newClose": 287.25,
"oldClose": 284.45001220703125
},
{
"date": 20191119,
"from": "au1912",
"to": "au2002",
"newClose": 335.3999938964844,
"oldClose": 334.04998779296875
},
{
"date": 20191226,
"from": "au2002",
"to": "au2006",
"newClose": 344.1199951171875,
"oldClose": 340.9200134277344
},
{
"date": 20200515,
"from": "au2006",
"to": "au2012",
"newClose": 391.8999938964844,
"oldClose": 390.1600036621094
},
{
"date": 20201126,
"from": "au2012",
"to": "au2102",
"newClose": 381.2200012207031,
"oldClose": 380.2200012207031
},
{
"date": 20210114,
"from": "au2102",
"to": "au2106",
"newClose": 386.5,
"oldClose": 385.29998779296875
},
{
"date": 20210527,
"from": "au2106",
"to": "au2112",
"newClose": 394.2799987792969,
"oldClose": 390.9599914550781
}
],
"bu": [
{
"date": 20160104,
"from": "",
"to": "bu1606",
"newClose": 1810.0,
"oldClose": 0
},
{
"date": 20160411,
"from": "bu1606",
"to": "bu1609",
"newClose": 1882.0,
"oldClose": 1776.0
},
{
"date": 20160802,
"from": "bu1609",
"to": "bu1612",
"newClose": 1966.0,
"oldClose": 1886.0
},
{
"date": 20161111,
"from": "bu1612",
"to": "bu1706",
"newClose": 2248.0,
"oldClose": 1912.0
},
{
"date": 20170425,
"from": "bu1706",
"to": "bu1709",
"newClose": 2568.0,
"oldClose": 2508.0
},
{
"date": 20170808,
"from": "bu1709",
"to": "bu1712",
"newClose": 2678.0,
"oldClose": 2558.0
},
{
"date": 20171121,
"from": "bu1712",
"to": "bu1806",
"newClose": 2696.0,
"oldClose": 2538.0
},
{
"date": 20180504,
"from": "bu1806",
"to": "bu1812",
"newClose": 3004.0,
"oldClose": 2850.0
},
{
"date": 20181123,
"from": "bu1812",
"to": "bu1906",
"newClose": 2780.0,
"oldClose": 3002.0
},
{
"date": 20190506,
"from": "bu1906",
"to": "bu1912",
"newClose": 3540.0,
"oldClose": 3540.0
},
{
"date": 20191129,
"from": "bu1912",
"to": "bu2006",
"newClose": 2968.0,
"oldClose": 3060.0
},
{
"date": 20200513,
"from": "bu2006",
"to": "bu2012",
"newClose": 2358.0,
"oldClose": 2084.0
},
{
"date": 20201130,
"from": "bu2012",
"to": "bu2106",
"newClose": 2612.0,
"oldClose": 2410.0
},
{
"date": 20210526,
"from": "bu2106",
"to": "bu2109",
"newClose": 3272.0,
"oldClose": 3186.0
},
{
"date": 20210816,
"from": "bu2109",
"to": "bu2112",
"newClose": 3170.0,
"oldClose": 3136.0
}
],
"cu": [
{
"date": 20160104,
"from": "",
"to": "cu1603",
"newClose": 35970.0,
"oldClose": 0
},
{
"date": 20160201,
"from": "cu1603",
"to": "cu1604",
"newClose": 35540.0,
"oldClose": 35450.0
},
{
"date": 20160229,
"from": "cu1604",
"to": "cu1605",
"newClose": 35880.0,
"oldClose": 35840.0
},
{
"date": 20160329,
"from": "cu1605",
"to": "cu1606",
"newClose": 37590.0,
"oldClose": 37570.0
},
{
"date": 20160426,
"from": "cu1606",
"to": "cu1607",
"newClose": 37710.0,
"oldClose": 37700.0
},
{
"date": 20160527,
"from": "cu1607",
"to": "cu1608",
"newClose": 35840.0,
"oldClose": 35910.0
},
{
"date": 20160628,
"from": "cu1608",
"to": "cu1609",
"newClose": 37200.0,
"oldClose": 37190.0
},
{
"date": 20160801,
"from": "cu1609",
"to": "cu1610",
"newClose": 38280.0,
"oldClose": 38170.0
},
{
"date": 20160902,
"from": "cu1610",
"to": "cu1611",
"newClose": 36670.0,
"oldClose": 36640.0
},
{
"date": 20160928,
"from": "cu1611",
"to": "cu1612",
"newClose": 37520.0,
"oldClose": 37500.0
},
{
"date": 20161101,
"from": "cu1612",
"to": "cu1701",
"newClose": 38740.0,
"oldClose": 38720.0
},
{
"date": 20161128,
"from": "cu1701",
"to": "cu1702",
"newClose": 49580.0,
"oldClose": 49380.0
},
{
"date": 20161228,
"from": "cu1702",
"to": "cu1703",
"newClose": 45620.0,
"oldClose": 45540.0
},
{
"date": 20170209,
"from": "cu1703",
"to": "cu1704",
"newClose": 47590.0,
"oldClose": 47410.0
},
{
"date": 20170308,
"from": "cu1704",
"to": "cu1705",
"newClose": 47300.0,
"oldClose": 47140.0
},
{
"date": 20170412,
"from": "cu1705",
"to": "cu1706",
"newClose": 46230.0,
"oldClose": 46150.0
},
{
"date": 20170509,
"from": "cu1706",
"to": "cu1707",
"newClose": 44730.0,
"oldClose": 44730.0
},
{
"date": 20170601,
"from": "cu1707",
"to": "cu1708",
"newClose": 45350.0,
"oldClose": 45340.0
},
{
"date": 20170703,
"from": "cu1708",
"to": "cu1709",
"newClose": 47600.0,
"oldClose": 47500.0
},
{
"date": 20170808,
"from": "cu1709",
"to": "cu1710",
"newClose": 50450.0,
"oldClose": 50280.0
},
{
"date": 20170906,
"from": "cu1710",
"to": "cu1711",
"newClose": 53290.0,
"oldClose": 53160.0
},
{
"date": 20170929,
"from": "cu1711",
"to": "cu1712",
"newClose": 51880.0,
"oldClose": 51840.0
},
{
"date": 20171107,
"from": "cu1712",
"to": "cu1801",
"newClose": 54600.0,
"oldClose": 54500.0
},
{
"date": 20171201,
"from": "cu1801",
"to": "cu1802",
"newClose": 52950.0,
"oldClose": 52900.0
},
{
"date": 20171228,
"from": "cu1802",
"to": "cu1803",
"newClose": 55920.0,
"oldClose": 55770.0
},
{
"date": 20180202,
"from": "cu1803",
"to": "cu1804",
"newClose": 53670.0,
"oldClose": 53520.0
},
{
"date": 20180301,
"from": "cu1804",
"to": "cu1805",
"newClose": 52470.0,
"oldClose": 52290.0
},
{
"date": 20180413,
"from": "cu1805",
"to": "cu1806",
"newClose": 50520.0,
"oldClose": 50410.0
},
{
"date": 20180508,
"from": "cu1806",
"to": "cu1807",
"newClose": 51370.0,
"oldClose": 51170.0
},
{
"date": 20180607,
"from": "cu1807",
"to": "cu1808",
"newClose": 54160.0,
"oldClose": 53930.0
},
{
"date": 20180706,
"from": "cu1808",
"to": "cu1809",
"newClose": 49160.0,
"oldClose": 49080.0
},
{
"date": 20180807,
"from": "cu1809",
"to": "cu1810",
"newClose": 49550.0,
"oldClose": 49480.0
},
{
"date": 20180907,
"from": "cu1810",
"to": "cu1811",
"newClose": 47560.0,
"oldClose": 47680.0
},
{
"date": 20181017,
"from": "cu1811",
"to": "cu1812",
"newClose": 50160.0,
"oldClose": 50290.0
},
{
"date": 20181106,
"from": "cu1812",
"to": "cu1901",
"newClose": 49660.0,
"oldClose": 49740.0
},
{
"date": 20181204,
"from": "cu1901",
"to": "cu1902",
"newClose": 49660.0,
"oldClose": 49660.0
},
{
"date": 20190103,
"from": "cu1902",
"to": "cu1903",
"newClose": 47310.0,
"oldClose": 47330.0
},
{
"date": 20190213,
"from": "cu1903",
"to": "cu1904",
"newClose": 48280.0,
"oldClose": 48120.0
},
{
"date": 20190305,
"from": "cu1904",
"to": "cu1905",
"newClose": 49500.0,
"oldClose": 49880.0
},
{
"date": 20190408,
"from": "cu1905",
"to": "cu1906",
"newClose": 49310.0,
"oldClose": 49260.0
},
{
"date": 20190506,
"from": "cu1906",
"to": "cu1907",
"newClose": 47990.0,
"oldClose": 48010.0
},
{
"date": 20190605,
"from": "cu1907",
"to": "cu1908",
"newClose": 46550.0,
"oldClose": 46490.0
},
{
"date": 20190708,
"from": "cu1908",
"to": "cu1909",
"newClose": 46390.0,
"oldClose": 46320.0
},
{
"date": 20190806,
"from": "cu1909",
"to": "cu1910",
"newClose": 46190.0,
"oldClose": 46150.0
},
{
"date": 20190906,
"from": "cu1910",
"to": "cu1911",
"newClose": 47450.0,
"oldClose": 47430.0
},
{
"date": 20191010,
"from": "cu1911",
"to": "cu1912",
"newClose": 46700.0,
"oldClose": 46690.0
},
{
"date": 20191107,
"from": "cu1912",
"to": "cu2001",
"newClose": 47250.0,
"oldClose": 47150.0
},
{
"date": 20191209,
"from": "cu2001",
"to": "cu2002",
"newClose": 48440.0,
"oldClose": 48340.0
},
{
"date": 20200103,
"from": "cu2002",
"to": "cu2003",
"newClose": 48900.0,
"oldClose": 48780.0
},
{
"date": 20200205,
"from": "cu2003",
"to": "cu2004",
"newClose": 45690.0,
"oldClose": 45550.0
},
{
"date": 20200304,
"from": "cu2004",
"to": "cu2005",
"newClose": 45420.0,
"oldClose": 45280.0
},
{
"date": 20200410,
"from": "cu2005",
"to": "cu2006",
"newClose": 41720.0,
"oldClose": 41790.0
},
{
"date": 20200515,
"from": "cu2006",
"to": "cu2007",
"newClose": 42940.0,
"oldClose": 43130.0
},
{
"date": 20200619,
"from": "cu2007",
"to": "cu2008",
"newClose": 47680.0,
"oldClose": 47780.0
},
{
"date": 20200721,
"from": "cu2008",
"to": "cu2009",
"newClose": 51870.0,
"oldClose": 51900.0
},
{
"date": 20200819,
"from": "cu2009",
"to": "cu2010",
"newClose": 52260.0,
"oldClose": 52150.0
},
{
"date": 20200922,
"from": "cu2010",
"to": "cu2011",
"newClose": 51810.0,
"oldClose": 51780.0
},
{
"date": 20201022,
"from": "cu2011",
"to": "cu2012",
"newClose": 52340.0,
"oldClose": 52280.0
},
{
"date": 20201118,
"from": "cu2012",
"to": "cu2101",
"newClose": 53000.0,
"oldClose": 52950.0
},
{
"date": 20201217,
"from": "cu2101",
"to": "cu2102",
"newClose": 58730.0,
"oldClose": 58620.0
},
{
"date": 20210111,
"from": "cu2102",
"to": "cu2103",
"newClose": 58670.0,
"oldClose": 58580.0
},
{
"date": 20210218,
"from": "cu2103",
"to": "cu2104",
"newClose": 63340.0,
"oldClose": 63210.0
},
{
"date": 20210309,
"from": "cu2104",
"to": "cu2105",
"newClose": 66120.0,
"oldClose": 66000.0
},
{
"date": 20210414,
"from": "cu2105",
"to": "cu2106",
"newClose": 66330.0,
"oldClose": 66190.0
},
{
"date": 20210520,
"from": "cu2106",
"to": "cu2107",
"newClose": 73690.0,
"oldClose": 73450.0
},
{
"date": 20210623,
"from": "cu2107",
"to": "cu2108",
"newClose": 68590.0,
"oldClose": 68480.0
},
{
"date": 20210723,
"from": "cu2108",
"to": "cu2109",
"newClose": 69760.0,
"oldClose": 69670.0
},
{
"date": 20210820,
"from": "cu2109",
"to": "cu2110",
"newClose": 67050.0,
"oldClose": 67200.0
},
{
"date": 20210927,
"from": "cu2110",
"to": "cu2111",
"newClose": 69270.0,
"oldClose": 69340.0
}
],
"fu": [
{
"date": 20160104,
"from": "",
"to": "fu1612",
"newClose": 2438.0,
"oldClose": 0
},
{
"date": 20160229,
"from": "fu1612",
"to": "fu1702",
"newClose": 2456.0,
"oldClose": 2438.0
},
{
"date": 20160606,
"from": "fu1703",
"to": "fu1704",
"newClose": 2664.0,
"oldClose": 2341.0
},
{
"date": 20170331,
"from": "fu1704",
"to": "fu1711",
"newClose": 3658.0,
"oldClose": 3915.0
},
{
"date": 20170531,
"from": "fu1711",
"to": "fu1804",
"newClose": 4188.0,
"oldClose": 3874.0
},
{
"date": 20170808,
"from": "fu1804",
"to": "fu1805",
"newClose": 3473.0,
"oldClose": 3790.0
},
{
"date": 20180717,
"from": "fu1807",
"to": "fu1901",
"newClose": 2869.0,
"oldClose": 0
},
{
"date": 20181210,
"from": "fu1901",
"to": "fu1905",
"newClose": 2760.0,
"oldClose": 2776.0
},
{
"date": 20190415,
"from": "fu1905",
"to": "fu1909",
"newClose": 2851.0,
"oldClose": 2834.0
},
{
"date": 20190805,
"from": "fu1909",
"to": "fu2001",
"newClose": 2399.0,
"oldClose": 2981.0
},
{
"date": 20191128,
"from": "fu2001",
"to": "fu2005",
"newClose": 1932.0,
"oldClose": 1825.0
},
{
"date": 20200407,
"from": "fu2005",
"to": "fu2009",
"newClose": 1741.0,
"oldClose": 1579.0
},
{
"date": 20200810,
"from": "fu2009",
"to": "fu2101",
"newClose": 1897.0,
"oldClose": 1659.0
},
{
"date": 20201207,
"from": "fu2101",
"to": "fu2105",
"newClose": 2116.0,
"oldClose": 1896.0
},
{
"date": 20210413,
"from": "fu2105",
"to": "fu2109",
"newClose": 2344.0,
"oldClose": 2333.0
},
{
"date": 20210812,
"from": "fu2109",
"to": "fu2201",
"newClose": 2520.0,
"oldClose": 2535.0
}
],
"wr": [
{
"date": 20161226,
"from": "wr1705",
"to": "wr1712",
"newClose": 2516.0,
"oldClose": 0
},
{
"date": 20180319,
"from": "wr1804",
"to": "wr1805",
"newClose": 3131.0,
"oldClose": 0
},
{
"date": 20180423,
"from": "wr1805",
"to": "wr1812",
"newClose": 3234.0,
"oldClose": 3401.0
},
{
"date": 20181017,
"from": "wr1812",
"to": "wr1905",
"newClose": 4087.0,
"oldClose": 3327.0
},
{
"date": 20190513,
"from": "wr1905",
"to": "wr1907",
"newClose": 4051.0,
"oldClose": 4843.0
},
{
"date": 20190702,
"from": "wr1907",
"to": "wr1910",
"newClose": 4240.0,
"oldClose": 4814.0
},
{
"date": 20190830,
"from": "wr1910",
"to": "wr1912",
"newClose": 3999.0,
"oldClose": 3894.0
},
{
"date": 20190906,
"from": "wr1912",
"to": "wr2003",
"newClose": 3630.0,
"oldClose": 3999.0
},
{
"date": 20190916,
"from": "wr2003",
"to": "wr2005",
"newClose": 3938.0,
"oldClose": 3630.0
},
{
"date": 20191009,
"from": "wr2005",
"to": "wr2009",
"newClose": 3847.0,
"oldClose": 3938.0
},
{
"date": 20191211,
"from": "wr2009",
"to": "wr2010",
"newClose": 3932.0,
"oldClose": 4050.0
},
{
"date": 20200108,
"from": "wr2010",
"to": "wr2011",
"newClose": 3860.0,
"oldClose": 3849.0
},
{
"date": 20200409,
"from": "wr2011",
"to": "wr2103",
"newClose": 3810.0,
"oldClose": 3860.0
},
{
"date": 20200417,
"from": "wr2103",
"to": "wr2104",
"newClose": 4050.0,
"oldClose": 3851.0
},
{
"date": 20200518,
"from": "wr2104",
"to": "wr2105",
"newClose": 3751.0,
"oldClose": 4050.0
},
{
"date": 20200730,
"from": "wr2106",
"to": "wr2107",
"newClose": 3869.0,
"oldClose": 0
},
{
"date": 20200820,
"from": "wr2107",
"to": "wr2108",
"newClose": 3990.0,
"oldClose": 3911.0
},
{
"date": 20201228,
"from": "wr2108",
"to": "wr2110",
"newClose": 4560.0,
"oldClose": 3888.0
},
{
"date": 20210108,
"from": "wr2110",
"to": "wr2111",
"newClose": 4366.0,
"oldClose": 4529.0
},
{
"date": 20210514,
"from": "wr2111",
"to": "wr2201",
"newClose": 5615.0,
"oldClose": 5916.0
},
{
"date": 20210521,
"from": "wr2201",
"to": "wr2205",
"newClose": 5799.0,
"oldClose": 5790.0
},
{
"date": 20210823,
"from": "wr2205",
"to": "wr2207",
"newClose": 5249.0,
"oldClose": 5277.0
}
],
"ru": [
{
"date": 20160104,
"from": "",
"to": "ru1605",
"newClose": 10160.0,
"oldClose": 0
},
{
"date": 20160317,
"from": "ru1605",
"to": "ru1609",
"newClose": 11775.0,
"oldClose": 11460.0
},
{
"date": 20160803,
"from": "ru1609",
"to": "ru1701",
"newClose": 12665.0,
"oldClose": 10870.0
},
{
"date": 20161121,
"from": "ru1701",
"to": "ru1705",
"newClose": 17645.0,
"oldClose": 17130.0
},
{
"date": 20170324,
"from": "ru1705",
"to": "ru1709",
"newClose": 16865.0,
"oldClose": 16435.0
},
{
"date": 20170808,
"from": "ru1709",
"to": "ru1801",
"newClose": 16070.0,
"oldClose": 12815.0
},
{
"date": 20171128,
"from": "ru1801",
"to": "ru1805",
"newClose": 13980.0,
"oldClose": 13545.0
},
{
"date": 20180329,
"from": "ru1805",
"to": "ru1809",
"newClose": 11475.0,
"oldClose": 11100.0
},
{
"date": 20180806,
"from": "ru1809",
"to": "ru1901",
"newClose": 12150.0,
"oldClose": 10325.0
},
{
"date": 20181204,
"from": "ru1901",
"to": "ru1905",
"newClose": 11160.0,
"oldClose": 10915.0
},
{
"date": 20190401,
"from": "ru1905",
"to": "ru1909",
"newClose": 11715.0,
"oldClose": 11365.0
},
{
"date": 20190806,
"from": "ru1909",
"to": "ru2001",
"newClose": 11555.0,
"oldClose": 10565.0
},
{
"date": 20191202,
"from": "ru2001",
"to": "ru2005",
"newClose": 12640.0,
"oldClose": 12400.0
},
{
"date": 20200323,
"from": "ru2005",
"to": "ru2009",
"newClose": 9630.0,
"oldClose": 9370.0
},
{
"date": 20200804,
"from": "ru2009",
"to": "ru2101",
"newClose": 12140.0,
"oldClose": 10825.0
},
{
"date": 20201201,
"from": "ru2101",
"to": "ru2105",
"newClose": 15385.0,
"oldClose": 15180.0
},
{
"date": 20210402,
"from": "ru2105",
"to": "ru2109",
"newClose": 14405.0,
"oldClose": 14150.0
},
{
"date": 20210809,
"from": "ru2109",
"to": "ru2201",
"newClose": 14835.0,
"oldClose": 13620.0
}
],
"hc": [
{
"date": 20160104,
"from": "",
"to": "hc1605",
"newClose": 1929.0,
"oldClose": 0
},
{
"date": 20160413,
"from": "hc1605",
"to": "hc1610",
"newClose": 2558.0,
"oldClose": 2662.0
},
{
"date": 20160901,
"from": "hc1610",
"to": "hc1701",
"newClose": 2633.0,
"oldClose": 2741.0
},
{
"date": 20161130,
"from": "hc1701",
"to": "hc1705",
"newClose": 3247.0,
"oldClose": 3295.0
},
{
"date": 20170328,
"from": "hc1705",
"to": "hc1710",
"newClose": 3086.0,
"oldClose": 3217.0
},
{
"date": 20170810,
"from": "hc1710",
"to": "hc1801",
"newClose": 4028.0,
"oldClose": 4073.0
},
{
"date": 20171127,
"from": "hc1801",
"to": "hc1805",
"newClose": 3840.0,
"oldClose": 4002.0
},
{
"date": 20180409,
"from": "hc1805",
"to": "hc1810",
"newClose": 3516.0,
"oldClose": 3782.0
},
{
"date": 20180817,
"from": "hc1810",
"to": "hc1901",
"newClose": 4162.0,
"oldClose": 4261.0
},
{
"date": 20181203,
"from": "hc1901",
"to": "hc1905",
"newClose": 3235.0,
"oldClose": 3537.0
},
{
"date": 20190404,
"from": "hc1905",
"to": "hc1910",
"newClose": 3567.0,
"oldClose": 3846.0
},
{
"date": 20190829,
"from": "hc1910",
"to": "hc2001",
"newClose": 3345.0,
"oldClose": 3563.0
},
{
"date": 20191216,
"from": "hc2001",
"to": "hc2005",
"newClose": 3523.0,
"oldClose": 3708.0
},
{
"date": 20200327,
"from": "hc2005",
"to": "hc2010",
"newClose": 3171.0,
"oldClose": 3306.0
},
{
"date": 20200902,
"from": "hc2010",
"to": "hc2101",
"newClose": 3934.0,
"oldClose": 4033.0
},
{
"date": 20201208,
"from": "hc2101",
"to": "hc2105",
"newClose": 4193.0,
"oldClose": 4313.0
},
{
"date": 20210409,
"from": "hc2105",
"to": "hc2110",
"newClose": 5355.0,
"oldClose": 5460.0
},
{
"date": 20210816,
"from": "hc2110",
"to": "hc2201",
"newClose": 5629.0,
"oldClose": 5703.0
}
],
"zn": [
{
"date": 20160104,
"from": "",
"to": "zn1603",
"newClose": 13165.0,
"oldClose": 0
},
{
"date": 20160202,
"from": "zn1603",
"to": "zn1604",
"newClose": 13340.0,
"oldClose": 13295.0
},
{
"date": 20160302,
"from": "zn1604",
"to": "zn1605",
"newClose": 14370.0,
"oldClose": 14310.0
},
{
"date": 20160329,
"from": "zn1605",
"to": "zn1606",
"newClose": 14180.0,
"oldClose": 14135.0
},
{
"date": 20160503,
"from": "zn1606",
"to": "zn1607",
"newClose": 15345.0,
"oldClose": 15295.0
},
{
"date": 20160601,
"from": "zn1607",
"to": "zn1608",
"newClose": 14970.0,
"oldClose": 14940.0
},
{
"date": 20160630,
"from": "zn1608",
"to": "zn1609",
"newClose": 16450.0,
"oldClose": 16410.0
},
{
"date": 20160729,
"from": "zn1609",
"to": "zn1610",
"newClose": 16930.0,
"oldClose": 16870.0
},
{
"date": 20160831,
"from": "zn1610",
"to": "zn1611",
"newClose": 17955.0,
"oldClose": 17990.0
},
{
"date": 20161010,
"from": "zn1611",
"to": "zn1612",
"newClose": 18390.0,
"oldClose": 18360.0
},
{
"date": 20161031,
"from": "zn1612",
"to": "zn1701",
"newClose": 19320.0,
"oldClose": 19330.0
},
{
"date": 20161130,
"from": "zn1701",
"to": "zn1702",
"newClose": 22590.0,
"oldClose": 22545.0
},
{
"date": 20161228,
"from": "zn1702",
"to": "zn1703",
"newClose": 20650.0,
"oldClose": 20600.0
},
{
"date": 20170213,
"from": "zn1703",
"to": "zn1704",
"newClose": 24000.0,
"oldClose": 23955.0
},
{
"date": 20170306,
"from": "zn1704",
"to": "zn1705",
"newClose": 22295.0,
"oldClose": 22295.0
},
{
"date": 20170407,
"from": "zn1705",
"to": "zn1706",
"newClose": 22150.0,
"oldClose": 22195.0
},
{
"date": 20170508,
"from": "zn1706",
"to": "zn1707",
"newClose": 21230.0,
"oldClose": 21470.0
},
{
"date": 20170607,
"from": "zn1707",
"to": "zn1708",
"newClose": 20540.0,
"oldClose": 21115.0
},
{
"date": 20170705,
"from": "zn1708",
"to": "zn1709",
"newClose": 22780.0,
"oldClose": 22915.0
},
{
"date": 20170808,
"from": "zn1709",
"to": "zn1710",
"newClose": 23845.0,
"oldClose": 23860.0
},
{
"date": 20170905,
"from": "zn1710",
"to": "zn1711",
"newClose": 26070.0,
"oldClose": 26160.0
},
{
"date": 20171011,
"from": "zn1711",
"to": "zn1712",
"newClose": 26135.0,
"oldClose": 26450.0
},
{
"date": 20171102,
"from": "zn1712",
"to": "zn1801",
"newClose": 25845.0,
"oldClose": 26000.0
},
{
"date": 20171205,
"from": "zn1801",
"to": "zn1802",
"newClose": 25100.0,
"oldClose": 25200.0
},
{
"date": 20180104,
"from": "zn1802",
"to": "zn1803",
"newClose": 25990.0,
"oldClose": 25980.0
},
{
"date": 20180206,
"from": "zn1803",
"to": "zn1804",
"newClose": 26535.0,
"oldClose": 26555.0
},
{
"date": 20180305,
"from": "zn1804",
"to": "zn1805",
"newClose": 25650.0,
"oldClose": 25660.0
},
{
"date": 20180403,
"from": "zn1805",
"to": "zn1806",
"newClose": 24710.0,
"oldClose": 24765.0
},
{
"date": 20180508,
"from": "zn1806",
"to": "zn1807",
"newClose": 23815.0,
"oldClose": 23885.0
},
{
"date": 20180611,
"from": "zn1807",
"to": "zn1808",
"newClose": 24445.0,
"oldClose": 24580.0
},
{
"date": 20180711,
"from": "zn1808",
"to": "zn1809",
"newClose": 20545.0,
"oldClose": 20695.0
},
{
"date": 20180731,
"from": "zn1809",
"to": "zn1810",
"newClose": 21210.0,
"oldClose": 21405.0
},
{
"date": 20180912,
"from": "zn1810",
"to": "zn1811",
"newClose": 20425.0,
"oldClose": 20920.0
},
{
"date": 20181022,
"from": "zn1811",
"to": "zn1812",
"newClose": 22035.0,
"oldClose": 22455.0
},
{
"date": 20181108,
"from": "zn1812",
"to": "zn1901",
"newClose": 20780.0,
"oldClose": 21360.0
},
{
"date": 20181207,
"from": "zn1901",
"to": "zn1902",
"newClose": 21440.0,
"oldClose": 21695.0
},
{
"date": 20190110,
"from": "zn1902",
"to": "zn1903",
"newClose": 20415.0,
"oldClose": 20670.0
},
{
"date": 20190218,
"from": "zn1903",
"to": "zn1904",
"newClose": 21425.0,
"oldClose": 21575.0
},
{
"date": 20190308,
"from": "zn1904",
"to": "zn1905",
"newClose": 21190.0,
"oldClose": 21590.0
},
{
"date": 20190411,
"from": "zn1905",
"to": "zn1906",
"newClose": 22395.0,
"oldClose": 22620.0
},
{
"date": 20190515,
"from": "zn1906",
"to": "zn1907",
"newClose": 20965.0,
"oldClose": 21270.0
},
{
"date": 20190613,
"from": "zn1907",
"to": "zn1908",
"newClose": 20285.0,
"oldClose": 20675.0
},
{
"date": 20190710,
"from": "zn1908",
"to": "zn1909",
"newClose": 19180.0,
"oldClose": 19190.0
},
{
"date": 20190807,
"from": "zn1909",
"to": "zn1910",
"newClose": 18765.0,
"oldClose": 18850.0
},
{
"date": 20190909,
"from": "zn1910",
"to": "zn1911",
"newClose": 19100.0,
"oldClose": 19115.0
},
{
"date": 20191015,
"from": "zn1911",
"to": "zn1912",
"newClose": 19070.0,
"oldClose": 19055.0
},
{
"date": 20191112,
"from": "zn1912",
"to": "zn2001",
"newClose": 18805.0,
"oldClose": 18810.0
},
{
"date": 20191209,
"from": "zn2001",
"to": "zn2002",
"newClose": 17900.0,
"oldClose": 18065.0
},
{
"date": 20200107,
"from": "zn2002",
"to": "zn2003",
"newClose": 18175.0,
"oldClose": 18190.0
},
{
"date": 20200210,
"from": "zn2003",
"to": "zn2004",
"newClose": 17155.0,
"oldClose": 17140.0
},
{
"date": 20200306,
"from": "zn2004",
"to": "zn2005",
"newClose": 16055.0,
"oldClose": 16020.0
},
{
"date": 20200408,
"from": "zn2005",
"to": "zn2006",
"newClose": 15650.0,
"oldClose": 15675.0
},
{
"date": 20200508,
"from": "zn2006",
"to": "zn2007",
"newClose": 16940.0,
"oldClose": 16985.0
},
{
"date": 20200619,
"from": "zn2007",
"to": "zn2008",
"newClose": 16935.0,
"oldClose": 16985.0
},
{
"date": 20200721,
"from": "zn2008",
"to": "zn2009",
"newClose": 17840.0,
"oldClose": 17870.0
},
{
"date": 20200821,
"from": "zn2009",
"to": "zn2010",
"newClose": 20005.0,
"oldClose": 20060.0
},
{
"date": 20200923,
"from": "zn2010",
"to": "zn2011",
"newClose": 19250.0,
"oldClose": 19520.0
},
{
"date": 20201027,
"from": "zn2011",
"to": "zn2012",
"newClose": 19610.0,
"oldClose": 19680.0
},
{
"date": 20201124,
"from": "zn2012",
"to": "zn2101",
"newClose": 20930.0,
"oldClose": 20960.0
},
{
"date": 20201221,
"from": "zn2101",
"to": "zn2102",
"newClose": 22180.0,
"oldClose": 22200.0
},
{
"date": 20210115,
"from": "zn2102",
"to": "zn2103",
"newClose": 20290.0,
"oldClose": 20310.0
},
{
"date": 20210223,
"from": "zn2103",
"to": "zn2104",
"newClose": 21840.0,
"oldClose": 21850.0
},
{
"date": 20210319,
"from": "zn2104",
"to": "zn2105",
"newClose": 21420.0,
"oldClose": 21460.0
},
{
"date": 20210420,
"from": "zn2105",
"to": "zn2106",
"newClose": 21745.0,
"oldClose": 21750.0
},
{
"date": 20210521,
"from": "zn2106",
"to": "zn2107",
"newClose": 22435.0,
"oldClose": 22425.0
},
{
"date": 20210624,
"from": "zn2107",
"to": "zn2108",
"newClose": 21700.0,
"oldClose": 21690.0
},
{
"date": 20210726,
"from": "zn2108",
"to": "zn2109",
"newClose": 22380.0,
"oldClose": 22375.0
},
{
"date": 20210823,
"from": "zn2109",
"to": "zn2110",
"newClose": 22230.0,
"oldClose": 22240.0
},
{
"date": 20210924,
"from": "zn2110",
"to": "zn2111",
"newClose": 22980.0,
"oldClose": 22985.0
}
],
"sn": [
{
"date": 20160104,
"from": "",
"to": "sn1605",
"newClose": 91710.0,
"oldClose": 0
},
{
"date": 20160504,
"from": "sn1605",
"to": "sn1609",
"newClose": 112040.0,
"oldClose": 110240.0
},
{
"date": 20160831,
"from": "sn1609",
"to": "sn1701",
"newClose": 123750.0,
"oldClose": 123200.0
},
{
"date": 20161230,
"from": "sn1701",
"to": "sn1705",
"newClose": 146820.0,
"oldClose": 146080.0
},
{
"date": 20170426,
"from": "sn1705",
"to": "sn1709",
"newClose": 138290.0,
"oldClose": 137610.0
},
{
"date": 20170830,
"from": "sn1709",
"to": "sn1801",
"newClose": 146270.0,
"oldClose": 143540.0
},
{
"date": 20171225,
"from": "sn1801",
"to": "sn1805",
"newClose": 141300.0,
"oldClose": 138200.0
},
{
"date": 20180418,
"from": "sn1805",
"to": "sn1809",
"newClose": 148230.0,
"oldClose": 145690.0
},
{
"date": 20180821,
"from": "sn1809",
"to": "sn1901",
"newClose": 144940.0,
"oldClose": 142870.0
},
{
"date": 20181211,
"from": "sn1901",
"to": "sn1905",
"newClose": 146070.0,
"oldClose": 144500.0
},
{
"date": 20190416,
"from": "sn1905",
"to": "sn1909",
"newClose": 148690.0,
"oldClose": 146870.0
},
{
"date": 20190819,
"from": "sn1909",
"to": "sn2001",
"newClose": 130520.0,
"oldClose": 130210.0
},
{
"date": 20191217,
"from": "sn2001",
"to": "sn2005",
"newClose": 138850.0,
"oldClose": 140580.0
},
{
"date": 20191227,
"from": "sn2005",
"to": "sn2006",
"newClose": 137530.0,
"oldClose": 137840.0
},
{
"date": 20200428,
"from": "sn2006",
"to": "sn2007",
"newClose": 128090.0,
"oldClose": 129850.0
},
{
"date": 20200528,
"from": "sn2007",
"to": "sn2008",
"newClose": 133670.0,
"oldClose": 134520.0
},
{
"date": 20200701,
"from": "sn2008",
"to": "sn2009",
"newClose": 138430.0,
"oldClose": 138610.0
},
{
"date": 20200727,
"from": "sn2009",
"to": "sn2010",
"newClose": 146790.0,
"oldClose": 146510.0
},
{
"date": 20200826,
"from": "sn2010",
"to": "sn2011",
"newClose": 141580.0,
"oldClose": 141420.0
},
{
"date": 20200929,
"from": "sn2011",
"to": "sn2012",
"newClose": 143300.0,
"oldClose": 143400.0
},
{
"date": 20201027,
"from": "sn2012",
"to": "sn2101",
"newClose": 144110.0,
"oldClose": 143830.0
},
{
"date": 20201201,
"from": "sn2101",
"to": "sn2102",
"newClose": 146000.0,
"oldClose": 145570.0
},
{
"date": 20201228,
"from": "sn2102",
"to": "sn2103",
"newClose": 153490.0,
"oldClose": 153320.0
},
{
"date": 20210205,
"from": "sn2103",
"to": "sn2104",
"newClose": 163660.0,
"oldClose": 163550.0
},
{
"date": 20210308,
"from": "sn2104",
"to": "sn2105",
"newClose": 170850.0,
"oldClose": 170460.0
},
{
"date": 20210401,
"from": "sn2105",
"to": "sn2106",
"newClose": 176550.0,
"oldClose": 176330.0
},
{
"date": 20210507,
"from": "sn2106",
"to": "sn2107",
"newClose": 198160.0,
"oldClose": 197800.0
},
{
"date": 20210617,
"from": "sn2107",
"to": "sn2108",
"newClose": 205630.0,
"oldClose": 206150.0
},
{
"date": 20210723,
"from": "sn2108",
"to": "sn2109",
"newClose": 233530.0,
"oldClose": 235400.0
},
{
"date": 20210827,
"from": "sn2109",
"to": "sn2110",
"newClose": 240810.0,
"oldClose": 243520.0
},
{
"date": 20210927,
"from": "sn2110",
"to": "sn2111",
"newClose": 268810.0,
"oldClose": 270340.0
}
],
"rb": [
{
"date": 20160104,
"from": "",
"to": "rb1605",
"newClose": 1777.0,
"oldClose": 0
},
{
"date": 20160314,
"from": "rb1605",
"to": "rb1610",
"newClose": 1986.0,
"oldClose": 2041.0
},
{
"date": 20160818,
"from": "rb1610",
"to": "rb1701",
"newClose": 2549.0,
"oldClose": 2575.0
},
{
"date": 20161128,
"from": "rb1701",
"to": "rb1705",
"newClose": 3332.0,
"oldClose": 3188.0
},
{
"date": 20170323,
"from": "rb1705",
"to": "rb1710",
"newClose": 3127.0,
"oldClose": 3417.0
},
{
"date": 20170808,
"from": "rb1710",
"to": "rb1801",
"newClose": 3884.0,
"oldClose": 4046.0
},
{
"date": 20171109,
"from": "rb1801",
"to": "rb1805",
"newClose": 3760.0,
"oldClose": 3760.0
},
{
"date": 20180328,
"from": "rb1805",
"to": "rb1810",
"newClose": 3212.0,
"oldClose": 3424.0
},
{
"date": 20180815,
"from": "rb1810",
"to": "rb1901",
"newClose": 4163.0,
"oldClose": 4345.0
},
{
"date": 20181130,
"from": "rb1901",
"to": "rb1905",
"newClose": 3256.0,
"oldClose": 3587.0
},
{
"date": 20190402,
"from": "rb1905",
"to": "rb1910",
"newClose": 3560.0,
"oldClose": 3838.0
},
{
"date": 20190823,
"from": "rb1910",
"to": "rb2001",
"newClose": 3446.0,
"oldClose": 3715.0
},
{
"date": 20191205,
"from": "rb2001",
"to": "rb2005",
"newClose": 3404.0,
"oldClose": 3585.0
},
{
"date": 20200325,
"from": "rb2005",
"to": "rb2010",
"newClose": 3365.0,
"oldClose": 3463.0
},
{
"date": 20200825,
"from": "rb2010",
"to": "rb2101",
"newClose": 3704.0,
"oldClose": 3773.0
},
{
"date": 20201207,
"from": "rb2101",
"to": "rb2105",
"newClose": 3912.0,
"oldClose": 3980.0
},
{
"date": 20210406,
"from": "rb2105",
"to": "rb2110",
"newClose": 5147.0,
"oldClose": 5180.0
},
{
"date": 20210810,
"from": "rb2110",
"to": "rb2201",
"newClose": 5497.0,
"oldClose": 5437.0
}
],
"pb": [
{
"date": 20160104,
"from": "",
"to": "pb1602",
"newClose": 13145.0,
"oldClose": 0
},
{
"date": 20160112,
"from": "pb1602",
"to": "pb1603",
"newClose": 12555.0,
"oldClose": 12615.0
},
{
"date": 20160217,
"from": "pb1603",
"to": "pb1604",
"newClose": 13510.0,
"oldClose": 13610.0
},
{
"date": 20160308,
"from": "pb1604",
"to": "pb1605",
"newClose": 13755.0,
"oldClose": 13730.0
},
{
"date": 20160414,
"from": "pb1605",
"to": "pb1606",
"newClose": 13350.0,
"oldClose": 13395.0
},
{
"date": 20160509,
"from": "pb1606",
"to": "pb1607",
"newClose": 12970.0,
"oldClose": 12955.0
},
{
"date": 20160614,
"from": "pb1607",
"to": "pb1608",
"newClose": 12755.0,
"oldClose": 12730.0
},
{
"date": 20160707,
"from": "pb1608",
"to": "pb1609",
"newClose": 13015.0,
"oldClose": 12920.0
},
{
"date": 20160822,
"from": "pb1609",
"to": "pb1610",
"newClose": 13900.0,
"oldClose": 13890.0
},
{
"date": 20160920,
"from": "pb1610",
"to": "pb1611",
"newClose": 14475.0,
"oldClose": 14420.0
},
{
"date": 20161020,
"from": "pb1611",
"to": "pb1612",
"newClose": 15410.0,
"oldClose": 15450.0
},
{
"date": 20161108,
"from": "pb1612",
"to": "pb1701",
"newClose": 16690.0,
"oldClose": 16650.0
},
{
"date": 20161208,
"from": "pb1701",
"to": "pb1702",
"newClose": 20775.0,
"oldClose": 20965.0
},
{
"date": 20170116,
"from": "pb1702",
"to": "pb1703",
"newClose": 18900.0,
"oldClose": 18845.0
},
{
"date": 20170221,
"from": "pb1703",
"to": "pb1704",
"newClose": 19150.0,
"oldClose": 19075.0
},
{
"date": 20170316,
"from": "pb1704",
"to": "pb1705",
"newClose": 18540.0,
"oldClose": 18430.0
},
{
"date": 20170417,
"from": "pb1705",
"to": "pb1706",
"newClose": 16510.0,
"oldClose": 16445.0
},
{
"date": 20170523,
"from": "pb1706",
"to": "pb1707",
"newClose": 15865.0,
"oldClose": 15795.0
},
{
"date": 20170622,
"from": "pb1707",
"to": "pb1708",
"newClose": 17535.0,
"oldClose": 17660.0
},
{
"date": 20170721,
"from": "pb1708",
"to": "pb1709",
"newClose": 17430.0,
"oldClose": 17460.0
},
{
"date": 20170816,
"from": "pb1709",
"to": "pb1710",
"newClose": 19440.0,
"oldClose": 19430.0
},
{
"date": 20170918,
"from": "pb1710",
"to": "pb1711",
"newClose": 19830.0,
"oldClose": 20350.0
},
{
"date": 20171020,
"from": "pb1711",
"to": "pb1712",
"newClose": 19270.0,
"oldClose": 19580.0
},
{
"date": 20171116,
"from": "pb1712",
"to": "pb1801",
"newClose": 18495.0,
"oldClose": 18515.0
},
{
"date": 20171220,
"from": "pb1801",
"to": "pb1802",
"newClose": 19335.0,
"oldClose": 19330.0
},
{
"date": 20180117,
"from": "pb1802",
"to": "pb1803",
"newClose": 19195.0,
"oldClose": 19135.0
},
{
"date": 20180214,
"from": "pb1803",
"to": "pb1804",
"newClose": 19315.0,
"oldClose": 19320.0
},
{
"date": 20180314,
"from": "pb1804",
"to": "pb1805",
"newClose": 18385.0,
"oldClose": 18540.0
},
{
"date": 20180417,
"from": "pb1805",
"to": "pb1806",
"newClose": 18155.0,
"oldClose": 18395.0
},
{
"date": 20180523,
"from": "pb1806",
"to": "pb1807",
"newClose": 19750.0,
"oldClose": 20200.0
},
{
"date": 20180625,
"from": "pb1807",
"to": "pb1808",
"newClose": 19550.0,
"oldClose": 19980.0
},
{
"date": 20180724,
"from": "pb1808",
"to": "pb1809",
"newClose": 18575.0,
"oldClose": 18940.0
},
{
"date": 20180816,
"from": "pb1809",
"to": "pb1810",
"newClose": 17505.0,
"oldClose": 17760.0
},
{
"date": 20180919,
"from": "pb1810",
"to": "pb1811",
"newClose": 18480.0,
"oldClose": 18885.0
},
{
"date": 20181023,
"from": "pb1811",
"to": "pb1812",
"newClose": 18125.0,
"oldClose": 18305.0
},
{
"date": 20181115,
"from": "pb1812",
"to": "pb1901",
"newClose": 18250.0,
"oldClose": 18575.0
},
{
"date": 20181220,
"from": "pb1901",
"to": "pb1902",
"newClose": 18045.0,
"oldClose": 18405.0
},
{
"date": 20190123,
"from": "pb1902",
"to": "pb1903",
"newClose": 17555.0,
"oldClose": 17675.0
},
{
"date": 20190221,
"from": "pb1903",
"to": "pb1904",
"newClose": 16765.0,
"oldClose": 16770.0
},
{
"date": 20190320,
"from": "pb1904",
"to": "pb1905",
"newClose": 16875.0,
"oldClose": 16880.0
},
{
"date": 20190415,
"from": "pb1905",
"to": "pb1906",
"newClose": 16420.0,
"oldClose": 16440.0
},
{
"date": 20190520,
"from": "pb1906",
"to": "pb1907",
"newClose": 16065.0,
"oldClose": 16080.0
},
{
"date": 20190621,
"from": "pb1907",
"to": "pb1908",
"newClose": 15965.0,
"oldClose": 16010.0
},
{
"date": 20190716,
"from": "pb1908",
"to": "pb1909",
"newClose": 16275.0,
"oldClose": 16285.0
},
{
"date": 20190820,
"from": "pb1909",
"to": "pb1910",
"newClose": 16975.0,
"oldClose": 16950.0
},
{
"date": 20190918,
"from": "pb1910",
"to": "pb1911",
"newClose": 16870.0,
"oldClose": 16925.0
},
{
"date": 20191022,
"from": "pb1911",
"to": "pb1912",
"newClose": 16840.0,
"oldClose": 16945.0
},
{
"date": 20191112,
"from": "pb1912",
"to": "pb2001",
"newClose": 16000.0,
"oldClose": 16025.0
},
{
"date": 20191216,
"from": "pb2001",
"to": "pb2002",
"newClose": 14975.0,
"oldClose": 15070.0
},
{
"date": 20200110,
"from": "pb2002",
"to": "pb2003",
"newClose": 14975.0,
"oldClose": 14975.0
},
{
"date": 20200212,
"from": "pb2003",
"to": "pb2004",
"newClose": 14200.0,
"oldClose": 14170.0
},
{
"date": 20200311,
"from": "pb2004",
"to": "pb2005",
"newClose": 14315.0,
"oldClose": 14365.0
},
{
"date": 20200416,
"from": "pb2005",
"to": "pb2006",
"newClose": 13680.0,
"oldClose": 13810.0
},
{
"date": 20200520,
"from": "pb2006",
"to": "pb2007",
"newClose": 14090.0,
"oldClose": 14200.0
},
{
"date": 20200622,
"from": "pb2007",
"to": "pb2008",
"newClose": 14560.0,
"oldClose": 14645.0
},
{
"date": 20200723,
"from": "pb2008",
"to": "pb2009",
"newClose": 14850.0,
"oldClose": 14865.0
},
{
"date": 20200820,
"from": "pb2009",
"to": "pb2010",
"newClose": 16015.0,
"oldClose": 16025.0
},
{
"date": 20200924,
"from": "pb2010",
"to": "pb2011",
"newClose": 15100.0,
"oldClose": 15415.0
},
{
"date": 20201026,
"from": "pb2011",
"to": "pb2012",
"newClose": 14105.0,
"oldClose": 14160.0
},
{
"date": 20201120,
"from": "pb2012",
"to": "pb2101",
"newClose": 15320.0,
"oldClose": 15280.0
},
{
"date": 20201223,
"from": "pb2101",
"to": "pb2102",
"newClose": 14520.0,
"oldClose": 14500.0
},
{
"date": 20210115,
"from": "pb2102",
"to": "pb2103",
"newClose": 14905.0,
"oldClose": 14905.0
},
{
"date": 20210222,
"from": "pb2103",
"to": "pb2104",
"newClose": 15840.0,
"oldClose": 15810.0
},
{
"date": 20210319,
"from": "pb2104",
"to": "pb2105",
"newClose": 14830.0,
"oldClose": 14820.0
},
{
"date": 20210423,
"from": "pb2105",
"to": "pb2106",
"newClose": 15320.0,
"oldClose": 15300.0
},
{
"date": 20210525,
"from": "pb2106",
"to": "pb2107",
"newClose": 15255.0,
"oldClose": 15260.0
},
{
"date": 20210624,
"from": "pb2107",
"to": "pb2108",
"newClose": 15465.0,
"oldClose": 15390.0
},
{
"date": 20210722,
"from": "pb2108",
"to": "pb2109",
"newClose": 16005.0,
"oldClose": 15960.0
},
{
"date": 20210825,
"from": "pb2109",
"to": "pb2110",
"newClose": 15470.0,
"oldClose": 15415.0
},
{
"date": 20210928,
"from": "pb2110",
"to": "pb2111",
"newClose": 14515.0,
"oldClose": 14470.0
}
],
"ni": [
{
"date": 20160104,
"from": "",
"to": "ni1605",
"newClose": 68930.0,
"oldClose": 0
},
{
"date": 20160408,
"from": "ni1605",
"to": "ni1609",
"newClose": 67430.0,
"oldClose": 66520.0
},
{
"date": 20160801,
"from": "ni1609",
"to": "ni1701",
"newClose": 83930.0,
"oldClose": 82430.0
},
{
"date": 20161125,
"from": "ni1701",
"to": "ni1705",
"newClose": 96700.0,
"oldClose": 95200.0
},
{
"date": 20170405,
"from": "ni1705",
"to": "ni1709",
"newClose": 86000.0,
"oldClose": 84230.0
},
{
"date": 20170808,
"from": "ni1709",
"to": "ni1801",
"newClose": 84300.0,
"oldClose": 82730.0
},
{
"date": 20171113,
"from": "ni1801",
"to": "ni1805",
"newClose": 100610.0,
"oldClose": 99650.0
},
{
"date": 20180329,
"from": "ni1805",
"to": "ni1807",
"newClose": 98810.0,
"oldClose": 98360.0
},
{
"date": 20180606,
"from": "ni1807",
"to": "ni1809",
"newClose": 118470.0,
"oldClose": 118760.0
},
{
"date": 20180801,
"from": "ni1809",
"to": "ni1811",
"newClose": 113050.0,
"oldClose": 113290.0
},
{
"date": 20181015,
"from": "ni1811",
"to": "ni1901",
"newClose": 104200.0,
"oldClose": 104590.0
},
{
"date": 20181210,
"from": "ni1901",
"to": "ni1905",
"newClose": 89310.0,
"oldClose": 89770.0
},
{
"date": 20190408,
"from": "ni1905",
"to": "ni1906",
"newClose": 102800.0,
"oldClose": 103100.0
},
{
"date": 20190515,
"from": "ni1906",
"to": "ni1907",
"newClose": 97230.0,
"oldClose": 97220.0
},
{
"date": 20190619,
"from": "ni1907",
"to": "ni1908",
"newClose": 99060.0,
"oldClose": 99790.0
},
{
"date": 20190722,
"from": "ni1908",
"to": "ni1910",
"newClose": 113490.0,
"oldClose": 113170.0
},
{
"date": 20190828,
"from": "ni1910",
"to": "ni1911",
"newClose": 125870.0,
"oldClose": 125740.0
},
{
"date": 20191014,
"from": "ni1911",
"to": "ni1912",
"newClose": 135770.0,
"oldClose": 136970.0
},
{
"date": 20191111,
"from": "ni1912",
"to": "ni2002",
"newClose": 124470.0,
"oldClose": 130400.0
},
{
"date": 20191220,
"from": "ni2002",
"to": "ni2003",
"newClose": 111710.0,
"oldClose": 111740.0
},
{
"date": 20200121,
"from": "ni2003",
"to": "ni2004",
"newClose": 107410.0,
"oldClose": 107480.0
},
{
"date": 20200225,
"from": "ni2004",
"to": "ni2006",
"newClose": 102970.0,
"oldClose": 102670.0
},
{
"date": 20200422,
"from": "ni2006",
"to": "ni2007",
"newClose": 98130.0,
"oldClose": 98050.0
},
{
"date": 20200525,
"from": "ni2007",
"to": "ni2008",
"newClose": 100770.0,
"oldClose": 100650.0
},
{
"date": 20200624,
"from": "ni2008",
"to": "ni2010",
"newClose": 102040.0,
"oldClose": 101640.0
},
{
"date": 20200818,
"from": "ni2010",
"to": "ni2011",
"newClose": 116290.0,
"oldClose": 116150.0
},
{
"date": 20200917,
"from": "ni2011",
"to": "ni2012",
"newClose": 115680.0,
"oldClose": 115560.0
},
{
"date": 20201102,
"from": "ni2012",
"to": "ni2102",
"newClose": 115690.0,
"oldClose": 115510.0
},
{
"date": 20201218,
"from": "ni2102",
"to": "ni2103",
"newClose": 131800.0,
"oldClose": 131530.0
},
{
"date": 20210126,
"from": "ni2103",
"to": "ni2104",
"newClose": 133820.0,
"oldClose": 134000.0
},
{
"date": 20210302,
"from": "ni2104",
"to": "ni2106",
"newClose": 138200.0,
"oldClose": 138080.0
},
{
"date": 20210524,
"from": "ni2106",
"to": "ni2107",
"newClose": 123880.0,
"oldClose": 123970.0
},
{
"date": 20210629,
"from": "ni2107",
"to": "ni2108",
"newClose": 135560.0,
"oldClose": 135560.0
},
{
"date": 20210727,
"from": "ni2108",
"to": "ni2109",
"newClose": 144760.0,
"oldClose": 144840.0
},
{
"date": 20210827,
"from": "ni2109",
"to": "ni2110",
"newClose": 141840.0,
"oldClose": 143480.0
},
{
"date": 20210929,
"from": "ni2110",
"to": "ni2111",
"newClose": 139430.0,
"oldClose": 141960.0
}
],
"sp": [
{
"date": 20190102,
"from": "",
"to": "sp1906",
"newClose": 5082.0,
"oldClose": 0
},
{
"date": 20190510,
"from": "sp1906",
"to": "sp1909",
"newClose": 5080.0,
"oldClose": 5134.0
},
{
"date": 20190809,
"from": "sp1909",
"to": "sp2001",
"newClose": 4664.0,
"oldClose": 4550.0
},
{
"date": 20191217,
"from": "sp2001",
"to": "sp2005",
"newClose": 4560.0,
"oldClose": 4434.0
},
{
"date": 20200402,
"from": "sp2005",
"to": "sp2009",
"newClose": 4624.0,
"oldClose": 4500.0
},
{
"date": 20200811,
"from": "sp2009",
"to": "sp2012",
"newClose": 4570.0,
"oldClose": 4466.0
},
{
"date": 20201111,
"from": "sp2012",
"to": "sp2101",
"newClose": 4730.0,
"oldClose": 4664.0
},
{
"date": 20201207,
"from": "sp2101",
"to": "sp2103",
"newClose": 5100.0,
"oldClose": 5026.0
},
{
"date": 20210208,
"from": "sp2103",
"to": "sp2105",
"newClose": 6626.0,
"oldClose": 6702.0
},
{
"date": 20210414,
"from": "sp2105",
"to": "sp2106",
"newClose": 7234.0,
"oldClose": 7328.0
},
{
"date": 20210520,
"from": "sp2106",
"to": "sp2107",
"newClose": 6850.0,
"oldClose": 6954.0
},
{
"date": 20210615,
"from": "sp2107",
"to": "sp2109",
"newClose": 5872.0,
"oldClose": 6050.0
},
{
"date": 20210820,
"from": "sp2109",
"to": "sp2110",
"newClose": 6192.0,
"oldClose": 6282.0
},
{
"date": 20210924,
"from": "sp2110",
"to": "sp2111",
"newClose": 5786.0,
"oldClose": 6194.0
}
],
"ss": [
{
"date": 20190925,
"from": "",
"to": "ss2002",
"newClose": 15575.0,
"oldClose": 0
},
{
"date": 20191230,
"from": "ss2002",
"to": "ss2006",
"newClose": 14160.0,
"oldClose": 14345.0
},
{
"date": 20200429,
"from": "ss2006",
"to": "ss2007",
"newClose": 12760.0,
"oldClose": 12820.0
},
{
"date": 20200528,
"from": "ss2007",
"to": "ss2008",
"newClose": 12980.0,
"oldClose": 13060.0
},
{
"date": 20200702,
"from": "ss2008",
"to": "ss2009",
"newClose": 13210.0,
"oldClose": 13355.0
},
{
"date": 20200803,
"from": "ss2009",
"to": "ss2010",
"newClose": 13895.0,
"oldClose": 13995.0
},
{
"date": 20200827,
"from": "ss2010",
"to": "ss2011",
"newClose": 15110.0,
"oldClose": 15090.0
},
{
"date": 20201009,
"from": "ss2011",
"to": "ss2012",
"newClose": 14540.0,
"oldClose": 14690.0
},
{
"date": 20201027,
"from": "ss2012",
"to": "ss2101",
"newClose": 14290.0,
"oldClose": 14210.0
},
{
"date": 20201127,
"from": "ss2101",
"to": "ss2102",
"newClose": 13370.0,
"oldClose": 13355.0
},
{
"date": 20201229,
"from": "ss2102",
"to": "ss2103",
"newClose": 13320.0,
"oldClose": 13295.0
},
{
"date": 20210203,
"from": "ss2103",
"to": "ss2104",
"newClose": 14225.0,
"oldClose": 14185.0
},
{
"date": 20210301,
"from": "ss2104",
"to": "ss2105",
"newClose": 15105.0,
"oldClose": 15060.0
},
{
"date": 20210401,
"from": "ss2105",
"to": "ss2106",
"newClose": 14250.0,
"oldClose": 14285.0
},
{
"date": 20210507,
"from": "ss2106",
"to": "ss2107",
"newClose": 14900.0,
"oldClose": 14980.0
},
{
"date": 20210622,
"from": "ss2107",
"to": "ss2108",
"newClose": 16230.0,
"oldClose": 16435.0
},
{
"date": 20210727,
"from": "ss2108",
"to": "ss2109",
"newClose": 19045.0,
"oldClose": 19190.0
},
{
"date": 20210825,
"from": "ss2109",
"to": "ss2110",
"newClose": 17725.0,
"oldClose": 18000.0
},
{
"date": 20210928,
"from": "ss2110",
"to": "ss2111",
"newClose": 20040.0,
"oldClose": 20520.0
}
]
},
"CZCE": {
"CF": [
{
"date": 20160104,
"from": "",
"to": "CF605",
"newClose": 11280.0,
"oldClose": 0
},
{
"date": 20160203,
"from": "CF605",
"to": "CF609",
"newClose": 10715.0,
"oldClose": 11460.0
},
{
"date": 20160621,
"from": "CF609",
"to": "CF701",
"newClose": 13155.0,
"oldClose": 13030.0
},
{
"date": 20161122,
"from": "CF701",
"to": "CF705",
"newClose": 16320.0,
"oldClose": 16265.0
},
{
"date": 20170331,
"from": "CF705",
"to": "CF709",
"newClose": 15350.0,
"oldClose": 14840.0
},
{
"date": 20170808,
"from": "CF709",
"to": "CF801",
"newClose": 15330.0,
"oldClose": 15150.0
},
{
"date": 20171128,
"from": "CF801",
"to": "CF805",
"newClose": 15195.0,
"oldClose": 15050.0
},
{
"date": 20180326,
"from": "CF805",
"to": "CF809",
"newClose": 15495.0,
"oldClose": 14995.0
},
{
"date": 20180522,
"from": "CF809",
"to": "CF901",
"newClose": 17865.0,
"oldClose": 17085.0
},
{
"date": 20181126,
"from": "CF901",
"to": "CF905",
"newClose": 14970.0,
"oldClose": 14440.0
},
{
"date": 20190404,
"from": "CF905",
"to": "CF909",
"newClose": 15630.0,
"oldClose": 15160.0
},
{
"date": 20190809,
"from": "CF909",
"to": "CF001",
"newClose": 12720.0,
"oldClose": 12175.0
},
{
"date": 20191202,
"from": "CF001",
"to": "CF005",
"newClose": 13085.0,
"oldClose": 12615.0
},
{
"date": 20200331,
"from": "CF005",
"to": "CF009",
"newClose": 10940.0,
"oldClose": 10540.0
},
{
"date": 20200810,
"from": "CF009",
"to": "CF101",
"newClose": 12735.0,
"oldClose": 12140.0
},
{
"date": 20201208,
"from": "CF101",
"to": "CF105",
"newClose": 14570.0,
"oldClose": 14485.0
},
{
"date": 20210408,
"from": "CF105",
"to": "CF109",
"newClose": 15120.0,
"oldClose": 14880.0
},
{
"date": 20210806,
"from": "CF109",
"to": "CF201",
"newClose": 17705.0,
"oldClose": 17410.0
}
],
"FG": [
{
"date": 20160104,
"from": "",
"to": "FG605",
"newClose": 823.0,
"oldClose": 0
},
{
"date": 20160407,
"from": "FG605",
"to": "FG609",
"newClose": 970.0,
"oldClose": 919.0
},
{
"date": 20160812,
"from": "FG609",
"to": "FG701",
"newClose": 1182.0,
"oldClose": 1155.0
},
{
"date": 20161205,
"from": "FG701",
"to": "FG705",
"newClose": 1219.0,
"oldClose": 1245.0
},
{
"date": 20170411,
"from": "FG705",
"to": "FG709",
"newClose": 1232.0,
"oldClose": 1256.0
},
{
"date": 20170810,
"from": "FG709",
"to": "FG801",
"newClose": 1424.0,
"oldClose": 1409.0
},
{
"date": 20171129,
"from": "FG801",
"to": "FG805",
"newClose": 1463.0,
"oldClose": 1475.0
},
{
"date": 20180410,
"from": "FG805",
"to": "FG809",
"newClose": 1343.0,
"oldClose": 1386.0
},
{
"date": 20180808,
"from": "FG809",
"to": "FG901",
"newClose": 1420.0,
"oldClose": 1469.0
},
{
"date": 20181214,
"from": "FG901",
"to": "FG905",
"newClose": 1297.0,
"oldClose": 1298.0
},
{
"date": 20190416,
"from": "FG905",
"to": "FG909",
"newClose": 1341.0,
"oldClose": 1286.0
},
{
"date": 20190808,
"from": "FG909",
"to": "FG001",
"newClose": 1377.0,
"oldClose": 1483.0
},
{
"date": 20191209,
"from": "FG001",
"to": "FG005",
"newClose": 1451.0,
"oldClose": 1472.0
},
{
"date": 20200401,
"from": "FG005",
"to": "FG009",
"newClose": 1274.0,
"oldClose": 1269.0
},
{
"date": 20200811,
"from": "FG009",
"to": "FG101",
"newClose": 1851.0,
"oldClose": 1950.0
},
{
"date": 20201211,
"from": "FG101",
"to": "FG105",
"newClose": 1910.0,
"oldClose": 1988.0
},
{
"date": 20210407,
"from": "FG105",
"to": "FG109",
"newClose": 2142.0,
"oldClose": 2123.0
},
{
"date": 20210722,
"from": "FG109",
"to": "FG201",
"newClose": 2998.0,
"oldClose": 3057.0
}
],
"JR": [
{
"date": 20161207,
"from": "JR705",
"to": "JR711",
"newClose": 3189.0,
"oldClose": 3375.0
},
{
"date": 20170214,
"from": "JR711",
"to": "JR801",
"newClose": 3028.0,
"oldClose": 3202.0
},
{
"date": 20170921,
"from": "JR801",
"to": "JR805",
"newClose": 3211.0,
"oldClose": 3278.0
},
{
"date": 20171016,
"from": "JR805",
"to": "JR807",
"newClose": 3228.0,
"oldClose": 3287.0
},
{
"date": 20180713,
"from": "JR807",
"to": "JR809",
"newClose": 3240.0,
"oldClose": 3166.0
},
{
"date": 20180718,
"from": "JR809",
"to": "JR907",
"newClose": 3240.0,
"oldClose": 3240.0
},
{
"date": 20190307,
"from": "JR907",
"to": "JR911",
"newClose": 3068.0,
"oldClose": 2858.0
},
{
"date": 20190315,
"from": "JR911",
"to": "JR001",
"newClose": 2711.0,
"oldClose": 3040.0
},
{
"date": 20190820,
"from": "JR001",
"to": "JR007",
"newClose": 3001.0,
"oldClose": 2963.0
},
{
"date": 20200210,
"from": "JR007",
"to": "JR009",
"newClose": 3206.0,
"oldClose": 2970.0
},
{
"date": 20200211,
"from": "JR009",
"to": "JR011",
"newClose": 3061.0,
"oldClose": 3206.0
},
{
"date": 20200304,
"from": "JR011",
"to": "JR101",
"newClose": 3029.0,
"oldClose": 2981.0
},
{
"date": 20200611,
"from": "JR101",
"to": "JR105",
"newClose": 3032.0,
"oldClose": 2991.0
},
{
"date": 20210128,
"from": "JR105",
"to": "JR109",
"newClose": 2860.0,
"oldClose": 2894.0
},
{
"date": 20210907,
"from": "JR109",
"to": "JR111",
"newClose": 2779.0,
"oldClose": 2389.0
},
{
"date": 20210928,
"from": "JR111",
"to": "JR203",
"newClose": 2800.0,
"oldClose": 2819.0
}
],
"LR": [
{
"date": 20160104,
"from": "",
"to": "LR609",
"newClose": 2548.0,
"oldClose": 0
},
{
"date": 20161025,
"from": "LR705",
"to": "LR709",
"newClose": 2544.0,
"oldClose": 0
},
{
"date": 20161223,
"from": "LR709",
"to": "LR711",
"newClose": 2775.0,
"oldClose": 2544.0
},
{
"date": 20170714,
"from": "LR711",
"to": "LR805",
"newClose": 2901.0,
"oldClose": 2789.0
},
{
"date": 20180330,
"from": "LR805",
"to": "LR809",
"newClose": 3229.0,
"oldClose": 3089.0
},
{
"date": 20180503,
"from": "LR809",
"to": "LR903",
"newClose": 3200.0,
"oldClose": 3229.0
},
{
"date": 20190301,
"from": "LR903",
"to": "LR905",
"newClose": 2586.0,
"oldClose": 2660.0
},
{
"date": 20190305,
"from": "LR905",
"to": "LR907",
"newClose": 2643.0,
"oldClose": 2577.0
},
{
"date": 20190311,
"from": "LR907",
"to": "LR911",
"newClose": 2630.0,
"oldClose": 2643.0
},
{
"date": 20190531,
"from": "LR911",
"to": "LR005",
"newClose": 2749.0,
"oldClose": 2537.0
},
{
"date": 20190828,
"from": "LR005",
"to": "LR007",
"newClose": 2810.0,
"oldClose": 2702.0
},
{
"date": 20200715,
"from": "LR007",
"to": "LR011",
"newClose": 2819.0,
"oldClose": 2650.0
},
{
"date": 20210520,
"from": "LR105",
"to": "LR107",
"newClose": 2858.0,
"oldClose": 0
}
],
"MA": [
{
"date": 20160104,
"from": "",
"to": "MA605",
"newClose": 1720.0,
"oldClose": 0
},
{
"date": 20160408,
"from": "MA605",
"to": "MA609",
"newClose": 1898.0,
"oldClose": 1839.0
},
{
"date": 20160809,
"from": "MA609",
"to": "MA701",
"newClose": 2046.0,
"oldClose": 1918.0
},
{
"date": 20161202,
"from": "MA701",
"to": "MA705",
"newClose": 2580.0,
"oldClose": 2582.0
},
{
"date": 20170407,
"from": "MA705",
"to": "MA709",
"newClose": 2596.0,
"oldClose": 2684.0
},
{
"date": 20170808,
"from": "MA709",
"to": "MA801",
"newClose": 2670.0,
"oldClose": 2519.0
},
{
"date": 20171211,
"from": "MA801",
"to": "MA805",
"newClose": 2877.0,
"oldClose": 3140.0
},
{
"date": 20180412,
"from": "MA805",
"to": "MA809",
"newClose": 2637.0,
"oldClose": 2750.0
},
{
"date": 20180808,
"from": "MA809",
"to": "MA901",
"newClose": 3320.0,
"oldClose": 3193.0
},
{
"date": 20181211,
"from": "MA901",
"to": "MA905",
"newClose": 2428.0,
"oldClose": 2397.0
},
{
"date": 20190412,
"from": "MA905",
"to": "MA909",
"newClose": 2530.0,
"oldClose": 2469.0
},
{
"date": 20190816,
"from": "MA909",
"to": "MA001",
"newClose": 2194.0,
"oldClose": 2078.0
},
{
"date": 20191213,
"from": "MA001",
"to": "MA005",
"newClose": 2117.0,
"oldClose": 2014.0
},
{
"date": 20200413,
"from": "MA005",
"to": "MA009",
"newClose": 1837.0,
"oldClose": 1753.0
},
{
"date": 20200817,
"from": "MA009",
"to": "MA101",
"newClose": 1904.0,
"oldClose": 1744.0
},
{
"date": 20201211,
"from": "MA101",
"to": "MA105",
"newClose": 2475.0,
"oldClose": 2435.0
},
{
"date": 20210416,
"from": "MA105",
"to": "MA109",
"newClose": 2457.0,
"oldClose": 2480.0
},
{
"date": 20210813,
"from": "MA109",
"to": "MA201",
"newClose": 2841.0,
"oldClose": 2687.0
}
],
"OI": [
{
"date": 20160104,
"from": "",
"to": "OI605",
"newClose": 5656.0,
"oldClose": 0
},
{
"date": 20160308,
"from": "OI605",
"to": "OI609",
"newClose": 5644.0,
"oldClose": 5540.0
},
{
"date": 20160704,
"from": "OI609",
"to": "OI701",
"newClose": 6676.0,
"oldClose": 6522.0
},
{
"date": 20161122,
"from": "OI701",
"to": "OI705",
"newClose": 7086.0,
"oldClose": 6910.0
},
{
"date": 20170315,
"from": "OI705",
"to": "OI709",
"newClose": 6812.0,
"oldClose": 6514.0
},
{
"date": 20170705,
"from": "OI709",
"to": "OI801",
"newClose": 6532.0,
"oldClose": 6372.0
},
{
"date": 20171117,
"from": "OI801",
"to": "OI805",
"newClose": 6870.0,
"oldClose": 6680.0
},
{
"date": 20180321,
"from": "OI805",
"to": "OI809",
"newClose": 6514.0,
"oldClose": 6326.0
},
{
"date": 20180730,
"from": "OI809",
"to": "OI901",
"newClose": 6527.0,
"oldClose": 6319.0
},
{
"date": 20181204,
"from": "OI901",
"to": "OI905",
"newClose": 6585.0,
"oldClose": 6407.0
},
{
"date": 20190410,
"from": "OI905",
"to": "OI909",
"newClose": 7095.0,
"oldClose": 7049.0
},
{
"date": 20190805,
"from": "OI909",
"to": "OI001",
"newClose": 7061.0,
"oldClose": 7006.0
},
{
"date": 20191210,
"from": "OI001",
"to": "OI005",
"newClose": 7363.0,
"oldClose": 7525.0
},
{
"date": 20200402,
"from": "OI005",
"to": "OI007",
"newClose": 6681.0,
"oldClose": 6749.0
},
{
"date": 20200429,
"from": "OI007",
"to": "OI009",
"newClose": 6655.0,
"oldClose": 6865.0
},
{
"date": 20200818,
"from": "OI009",
"to": "OI101",
"newClose": 8634.0,
"oldClose": 8991.0
},
{
"date": 20201215,
"from": "OI101",
"to": "OI105",
"newClose": 9245.0,
"oldClose": 9612.0
},
{
"date": 20210416,
"from": "OI105",
"to": "OI109",
"newClose": 10431.0,
"oldClose": 10712.0
},
{
"date": 20210817,
"from": "OI109",
"to": "OI201",
"newClose": 10815.0,
"oldClose": 10831.0
}
],
"PM": [
{
"date": 20160104,
"from": "",
"to": "PM601",
"newClose": 2350.0,
"oldClose": 0
},
{
"date": 20160118,
"from": "PM601",
"to": "PM605",
"newClose": 2768.0,
"oldClose": 2350.0
},
{
"date": 20160426,
"from": "PM605",
"to": "PM607",
"newClose": 2699.0,
"oldClose": 2133.0
},
{
"date": 20160427,
"from": "PM607",
"to": "PM611",
"newClose": 2516.0,
"oldClose": 2512.0
},
{
"date": 20160906,
"from": "PM611",
"to": "PM701",
"newClose": 2486.0,
"oldClose": 2356.0
},
{
"date": 20161216,
"from": "PM701",
"to": "PM705",
"newClose": 2273.0,
"oldClose": 2460.0
},
{
"date": 20170405,
"from": "PM705",
"to": "PM709",
"newClose": 2487.0,
"oldClose": 2431.0
},
{
"date": 20170503,
"from": "PM709",
"to": "PM801",
"newClose": 2515.0,
"oldClose": 2451.0
},
{
"date": 20171226,
"from": "PM801",
"to": "PM805",
"newClose": 2640.0,
"oldClose": 2410.0
},
{
"date": 20180702,
"from": "PM807",
"to": "PM809",
"newClose": 2480.0,
"oldClose": 0
},
{
"date": 20180709,
"from": "PM809",
"to": "PM905",
"newClose": 2458.0,
"oldClose": 2445.0
},
{
"date": 20181119,
"from": "PM905",
"to": "PM907",
"newClose": 2300.0,
"oldClose": 2388.0
},
{
"date": 20181211,
"from": "PM907",
"to": "PM911",
"newClose": 2485.0,
"oldClose": 2304.0
},
{
"date": 20190520,
"from": "PM911",
"to": "PM003",
"newClose": 2280.0,
"oldClose": 2388.0
},
{
"date": 20190826,
"from": "PM003",
"to": "PM007",
"newClose": 2398.0,
"oldClose": 2280.0
},
{
"date": 20200324,
"from": "PM007",
"to": "PM101",
"newClose": 2250.0,
"oldClose": 2255.0
},
{
"date": 20200325,
"from": "PM101",
"to": "PM103",
"newClose": 2283.0,
"oldClose": 2250.0
},
{
"date": 20200609,
"from": "PM103",
"to": "PM105",
"newClose": 2335.0,
"oldClose": 2235.0
},
{
"date": 20200728,
"from": "PM105",
"to": "PM107",
"newClose": 2377.0,
"oldClose": 2343.0
},
{
"date": 20210225,
"from": "PM107",
"to": "PM111",
"newClose": 2489.0,
"oldClose": 2520.0
},
{
"date": 20210706,
"from": "PM111",
"to": "PM201",
"newClose": 2716.0,
"oldClose": 2525.0
},
{
"date": 20210913,
"from": "PM201",
"to": "PM207",
"newClose": 2486.0,
"oldClose": 2500.0
}
],
"RI": [
{
"date": 20160104,
"from": "",
"to": "RI605",
"newClose": 2568.0,
"oldClose": 0
},
{
"date": 20160121,
"from": "RI605",
"to": "RI609",
"newClose": 2531.0,
"oldClose": 2568.0
},
{
"date": 20160518,
"from": "RI609",
"to": "RI703",
"newClose": 2790.0,
"oldClose": 2679.0
},
{
"date": 20160705,
"from": "RI703",
"to": "RI705",
"newClose": 2853.0,
"oldClose": 2638.0
},
{
"date": 20160920,
"from": "RI705",
"to": "RI709",
"newClose": 2461.0,
"oldClose": 2640.0
},
{
"date": 20170905,
"from": "RI709",
"to": "RI801",
"newClose": 2745.0,
"oldClose": 2897.0
},
{
"date": 20171031,
"from": "RI801",
"to": "RI807",
"newClose": 2759.0,
"oldClose": 2741.0
},
{
"date": 20180227,
"from": "RI807",
"to": "RI901",
"newClose": 2854.0,
"oldClose": 2806.0
},
{
"date": 20181010,
"from": "RI901",
"to": "RI903",
"newClose": 2450.0,
"oldClose": 2350.0
},
{
"date": 20181023,
"from": "RI903",
"to": "RI909",
"newClose": 2516.0,
"oldClose": 2450.0
},
{
"date": 20190227,
"from": "RI909",
"to": "RI911",
"newClose": 2400.0,
"oldClose": 2411.0
},
{
"date": 20191009,
"from": "RI911",
"to": "RI005",
"newClose": 2662.0,
"oldClose": 2523.0
},
{
"date": 20200318,
"from": "RI005",
"to": "RI009",
"newClose": 2582.0,
"oldClose": 2629.0
},
{
"date": 20200721,
"from": "RI009",
"to": "RI101",
"newClose": 2701.0,
"oldClose": 2604.0
},
{
"date": 20210108,
"from": "RI101",
"to": "RI111",
"newClose": 2809.0,
"oldClose": 2788.0
},
{
"date": 20210806,
"from": "RI111",
"to": "RI201",
"newClose": 2635.0,
"oldClose": 2699.0
}
],
"RS": [
{
"date": 20160104,
"from": "",
"to": "RS607",
"newClose": 3914.0,
"oldClose": 0
},
{
"date": 20160715,
"from": "RS607",
"to": "RS609",
"newClose": 4175.0,
"oldClose": 4070.0
},
{
"date": 20160919,
"from": "RS609",
"to": "RS611",
"newClose": 4203.0,
"oldClose": 4265.0
},
{
"date": 20161115,
"from": "RS611",
"to": "RS707",
"newClose": 4208.0,
"oldClose": 4203.0
},
{
"date": 20170703,
"from": "RS707",
"to": "RS709",
"newClose": 5220.0,
"oldClose": 5530.0
},
{
"date": 20170915,
"from": "RS709",
"to": "RS711",
"newClose": 4990.0,
"oldClose": 5444.0
},
{
"date": 20171009,
"from": "RS711",
"to": "RS807",
"newClose": 5307.0,
"oldClose": 5400.0
},
{
"date": 20180130,
"from": "RS807",
"to": "RS809",
"newClose": 5318.0,
"oldClose": 5287.0
},
{
"date": 20180815,
"from": "RS809",
"to": "RS907",
"newClose": 4850.0,
"oldClose": 5458.0
},
{
"date": 20180829,
"from": "RS907",
"to": "RS908",
"newClose": 5270.0,
"oldClose": 4850.0
},
{
"date": 20180918,
"from": "RS908",
"to": "RS909",
"newClose": 5463.0,
"oldClose": 5465.0
},
{
"date": 20181214,
"from": "RS909",
"to": "RS911",
"newClose": 5420.0,
"oldClose": 5473.0
},
{
"date": 20191104,
"from": "RS911",
"to": "RS007",
"newClose": 4426.0,
"oldClose": 3685.0
},
{
"date": 20191112,
"from": "RS007",
"to": "RS008",
"newClose": 4135.0,
"oldClose": 4436.0
},
{
"date": 20200103,
"from": "RS008",
"to": "RS009",
"newClose": 4205.0,
"oldClose": 4135.0
},
{
"date": 20200831,
"from": "RS009",
"to": "RS011",
"newClose": 5574.0,
"oldClose": 5689.0
},
{
"date": 20201116,
"from": "RS011",
"to": "RS107",
"newClose": 5447.0,
"oldClose": 5999.0
},
{
"date": 20210203,
"from": "RS107",
"to": "RS109",
"newClose": 5745.0,
"oldClose": 5978.0
},
{
"date": 20210906,
"from": "RS109",
"to": "RS111",
"newClose": 6100.0,
"oldClose": 6200.0
}
],
"SF": [
{
"date": 20160104,
"from": "",
"to": "SF605",
"newClose": 3704.0,
"oldClose": 0
},
{
"date": 20160503,
"from": "SF605",
"to": "SF609",
"newClose": 4352.0,
"oldClose": 4130.0
},
{
"date": 20160711,
"from": "SF609",
"to": "SF706",
"newClose": 4160.0,
"oldClose": 4222.0
},
{
"date": 20161108,
"from": "SF706",
"to": "SF709",
"newClose": 5576.0,
"oldClose": 5200.0
},
{
"date": 20170221,
"from": "SF709",
"to": "SF801",
"newClose": 5248.0,
"oldClose": 5192.0
},
{
"date": 20170314,
"from": "SF801",
"to": "SF802",
"newClose": 5028.0,
"oldClose": 5590.0
},
{
"date": 20170822,
"from": "SF802",
"to": "SF801",
"newClose": 7800.0,
"oldClose": 7496.0
},
{
"date": 20170914,
"from": "SF801",
"to": "SF805",
"newClose": 6248.0,
"oldClose": 6390.0
},
{
"date": 20170919,
"from": "SF805",
"to": "SF801",
"newClose": 6208.0,
"oldClose": 6108.0
},
{
"date": 20171218,
"from": "SF801",
"to": "SF805",
"newClose": 6770.0,
"oldClose": 8418.0
},
{
"date": 20180420,
"from": "SF805",
"to": "SF809",
"newClose": 6110.0,
"oldClose": 6086.0
},
{
"date": 20180815,
"from": "SF809",
"to": "SF901",
"newClose": 7208.0,
"oldClose": 7028.0
},
{
"date": 20181221,
"from": "SF901",
"to": "SF905",
"newClose": 5926.0,
"oldClose": 6060.0
},
{
"date": 20190422,
"from": "SF905",
"to": "SF909",
"newClose": 5854.0,
"oldClose": 5748.0
},
{
"date": 20190827,
"from": "SF909",
"to": "SF001",
"newClose": 5752.0,
"oldClose": 5992.0
},
{
"date": 20191220,
"from": "SF001",
"to": "SF005",
"newClose": 5796.0,
"oldClose": 5794.0
},
{
"date": 20200403,
"from": "SF005",
"to": "SF007",
"newClose": 5552.0,
"oldClose": 5490.0
},
{
"date": 20200410,
"from": "SF007",
"to": "SF009",
"newClose": 5590.0,
"oldClose": 5548.0
},
{
"date": 20200601,
"from": "SF009",
"to": "SF010",
"newClose": 5736.0,
"oldClose": 5780.0
},
{
"date": 20200928,
"from": "SF010",
"to": "SF011",
"newClose": 5880.0,
"oldClose": 5696.0
},
{
"date": 20200929,
"from": "SF011",
"to": "SF101",
"newClose": 5730.0,
"oldClose": 5862.0
},
{
"date": 20201221,
"from": "SF101",
"to": "SF105",
"newClose": 6812.0,
"oldClose": 6800.0
},
{
"date": 20210416,
"from": "SF105",
"to": "SF109",
"newClose": 7444.0,
"oldClose": 7258.0
},
{
"date": 20210817,
"from": "SF109",
"to": "SF201",
"newClose": 9048.0,
"oldClose": 9192.0
}
],
"SM": [
{
"date": 20160906,
"from": "SM705",
"to": "SM708",
"newClose": 5106.0,
"oldClose": 5668.0
},
{
"date": 20160923,
"from": "SM708",
"to": "SM709",
"newClose": 6284.0,
"oldClose": 5288.0
},
{
"date": 20161227,
"from": "SM709",
"to": "SM705",
"newClose": 7184.0,
"oldClose": 7014.0
},
{
"date": 20161228,
"from": "SM705",
"to": "SM709",
"newClose": 7066.0,
"oldClose": 7168.0
},
{
"date": 20170116,
"from": "SM709",
"to": "SM705",
"newClose": 6800.0,
"oldClose": 6554.0
},
{
"date": 20170417,
"from": "SM705",
"to": "SM709",
"newClose": 5768.0,
"oldClose": 5738.0
},
{
"date": 20170811,
"from": "SM709",
"to": "SM801",
"newClose": 6884.0,
"oldClose": 6952.0
},
{
"date": 20171222,
"from": "SM801",
"to": "SM805",
"newClose": 7688.0,
"oldClose": 8504.0
},
{
"date": 20180425,
"from": "SM805",
"to": "SM809",
"newClose": 7462.0,
"oldClose": 7720.0
},
{
"date": 20180815,
"from": "SM809",
"to": "SM901",
"newClose": 8624.0,
"oldClose": 8620.0
},
{
"date": 20181221,
"from": "SM901",
"to": "SM905",
"newClose": 7280.0,
"oldClose": 7982.0
},
{
"date": 20190422,
"from": "SM905",
"to": "SM909",
"newClose": 7236.0,
"oldClose": 7390.0
},
{
"date": 20190826,
"from": "SM909",
"to": "SM001",
"newClose": 6970.0,
"oldClose": 7334.0
},
{
"date": 20191217,
"from": "SM001",
"to": "SM005",
"newClose": 6274.0,
"oldClose": 6340.0
},
{
"date": 20200413,
"from": "SM005",
"to": "SM009",
"newClose": 7020.0,
"oldClose": 7016.0
},
{
"date": 20200825,
"from": "SM009",
"to": "SM101",
"newClose": 6262.0,
"oldClose": 6194.0
},
{
"date": 20201216,
"from": "SM101",
"to": "SM105",
"newClose": 6636.0,
"oldClose": 6576.0
},
{
"date": 20210420,
"from": "SM105",
"to": "SM109",
"newClose": 7184.0,
"oldClose": 7128.0
},
{
"date": 20210818,
"from": "SM109",
"to": "SM201",
"newClose": 7950.0,
"oldClose": 7946.0
}
],
"SR": [
{
"date": 20160104,
"from": "",
"to": "SR605",
"newClose": 5613.0,
"oldClose": 0
},
{
"date": 20160301,
"from": "SR605",
"to": "SR609",
"newClose": 5466.0,
"oldClose": 5348.0
},
{
"date": 20160720,
"from": "SR609",
"to": "SR701",
"newClose": 6247.0,
"oldClose": 5860.0
},
{
"date": 20161207,
"from": "SR701",
"to": "SR705",
"newClose": 7144.0,
"oldClose": 7054.0
},
{
"date": 20170331,
"from": "SR705",
"to": "SR709",
"newClose": 6573.0,
"oldClose": 6500.0
},
{
"date": 20170717,
"from": "SR709",
"to": "SR801",
"newClose": 6187.0,
"oldClose": 6211.0
},
{
"date": 20171214,
"from": "SR801",
"to": "SR805",
"newClose": 5959.0,
"oldClose": 6250.0
},
{
"date": 20180330,
"from": "SR805",
"to": "SR809",
"newClose": 5551.0,
"oldClose": 5659.0
},
{
"date": 20180730,
"from": "SR809",
"to": "SR901",
"newClose": 5157.0,
"oldClose": 4989.0
},
{
"date": 20181203,
"from": "SR901",
"to": "SR905",
"newClose": 4936.0,
"oldClose": 4948.0
},
{
"date": 20190328,
"from": "SR905",
"to": "SR909",
"newClose": 5001.0,
"oldClose": 5035.0
},
{
"date": 20190731,
"from": "SR909",
"to": "SR001",
"newClose": 5366.0,
"oldClose": 5322.0
},
{
"date": 20191206,
"from": "SR001",
"to": "SR005",
"newClose": 5402.0,
"oldClose": 5526.0
},
{
"date": 20200326,
"from": "SR005",
"to": "SR009",
"newClose": 5444.0,
"oldClose": 5491.0
},
{
"date": 20200807,
"from": "SR009",
"to": "SR101",
"newClose": 5105.0,
"oldClose": 5132.0
},
{
"date": 20201204,
"from": "SR101",
"to": "SR105",
"newClose": 5005.0,
"oldClose": 5057.0
},
{
"date": 20210326,
"from": "SR105",
"to": "SR109",
"newClose": 5350.0,
"oldClose": 5272.0
},
{
"date": 20210812,
"from": "SR109",
"to": "SR201",
"newClose": 5920.0,
"oldClose": 5654.0
}
],
"TA": [
{
"date": 20160104,
"from": "",
"to": "TA605",
"newClose": 4440.0,
"oldClose": 0
},
{
"date": 20160330,
"from": "TA605",
"to": "TA609",
"newClose": 4768.0,
"oldClose": 4642.0
},
{
"date": 20160805,
"from": "TA609",
"to": "TA701",
"newClose": 4770.0,
"oldClose": 4590.0
},
{
"date": 20161115,
"from": "TA701",
"to": "TA705",
"newClose": 4916.0,
"oldClose": 4812.0
},
{
"date": 20170407,
"from": "TA705",
"to": "TA709",
"newClose": 5126.0,
"oldClose": 4984.0
},
{
"date": 20170808,
"from": "TA709",
"to": "TA801",
"newClose": 5220.0,
"oldClose": 5112.0
},
{
"date": 20171123,
"from": "TA801",
"to": "TA805",
"newClose": 5440.0,
"oldClose": 5410.0
},
{
"date": 20180327,
"from": "TA805",
"to": "TA809",
"newClose": 5464.0,
"oldClose": 5462.0
},
{
"date": 20180808,
"from": "TA809",
"to": "TA901",
"newClose": 6794.0,
"oldClose": 6950.0
},
{
"date": 20181129,
"from": "TA901",
"to": "TA905",
"newClose": 5808.0,
"oldClose": 5994.0
},
{
"date": 20190410,
"from": "TA905",
"to": "TA909",
"newClose": 6250.0,
"oldClose": 6480.0
},
{
"date": 20190815,
"from": "TA909",
"to": "TA001",
"newClose": 5176.0,
"oldClose": 5290.0
},
{
"date": 20191210,
"from": "TA001",
"to": "TA005",
"newClose": 4850.0,
"oldClose": 4762.0
},
{
"date": 20200331,
"from": "TA005",
"to": "TA009",
"newClose": 3342.0,
"oldClose": 3222.0
},
{
"date": 20200811,
"from": "TA009",
"to": "TA101",
"newClose": 3816.0,
"oldClose": 3656.0
},
{
"date": 20201202,
"from": "TA101",
"to": "TA105",
"newClose": 3540.0,
"oldClose": 3416.0
},
{
"date": 20210401,
"from": "TA105",
"to": "TA109",
"newClose": 4462.0,
"oldClose": 4374.0
},
{
"date": 20210805,
"from": "TA109",
"to": "TA201",
"newClose": 5198.0,
"oldClose": 5172.0
}
],
"WH": [
{
"date": 20160104,
"from": "",
"to": "WH605",
"newClose": 2917.0,
"oldClose": 0
},
{
"date": 20160418,
"from": "WH605",
"to": "WH609",
"newClose": 2661.0,
"oldClose": 2688.0
},
{
"date": 20160719,
"from": "WH609",
"to": "WH701",
"newClose": 2614.0,
"oldClose": 2581.0
},
{
"date": 20161222,
"from": "WH701",
"to": "WH705",
"newClose": 3058.0,
"oldClose": 3100.0
},
{
"date": 20170426,
"from": "WH705",
"to": "WH709",
"newClose": 2651.0,
"oldClose": 3060.0
},
{
"date": 20170710,
"from": "WH709",
"to": "WH801",
"newClose": 2625.0,
"oldClose": 2571.0
},
{
"date": 20171228,
"from": "WH801",
"to": "WH805",
"newClose": 2575.0,
"oldClose": 2511.0
},
{
"date": 20180416,
"from": "WH805",
"to": "WH809",
"newClose": 2515.0,
"oldClose": 2548.0
},
{
"date": 20180625,
"from": "WH809",
"to": "WH901",
"newClose": 2649.0,
"oldClose": 2561.0
},
{
"date": 20181228,
"from": "WH901",
"to": "WH905",
"newClose": 2428.0,
"oldClose": 2421.0
},
{
"date": 20190212,
"from": "WH905",
"to": "WH909",
"newClose": 2420.0,
"oldClose": 2423.0
},
{
"date": 20190308,
"from": "WH909",
"to": "WH911",
"newClose": 2400.0,
"oldClose": 2408.0
},
{
"date": 20190313,
"from": "WH911",
"to": "WH001",
"newClose": 2440.0,
"oldClose": 2400.0
},
{
"date": 20190705,
"from": "WH001",
"to": "WH005",
"newClose": 2510.0,
"oldClose": 2449.0
},
{
"date": 20190826,
"from": "WH005",
"to": "WH007",
"newClose": 2549.0,
"oldClose": 2524.0
},
{
"date": 20190924,
"from": "WH007",
"to": "WH009",
"newClose": 2426.0,
"oldClose": 2402.0
},
{
"date": 20200210,
"from": "WH009",
"to": "WH101",
"newClose": 2600.0,
"oldClose": 2566.0
},
{
"date": 20200320,
"from": "WH101",
"to": "WH103",
"newClose": 2589.0,
"oldClose": 2538.0
},
{
"date": 20200527,
"from": "WH103",
"to": "WH105",
"newClose": 2612.0,
"oldClose": 2550.0
},
{
"date": 20200728,
"from": "WH105",
"to": "WH107",
"newClose": 2758.0,
"oldClose": 2650.0
},
{
"date": 20201013,
"from": "WH107",
"to": "WH109",
"newClose": 2700.0,
"oldClose": 2780.0
},
{
"date": 20210907,
"from": "WH109",
"to": "WH205",
"newClose": 2906.0,
"oldClose": 2699.0
}
],
"ZC": [
{
"date": 20160104,
"from": "",
"to": "ZC605",
"newClose": 304.3999938964844,
"oldClose": 0
},
{
"date": 20160407,
"from": "ZC605",
"to": "ZC609",
"newClose": 362.6000061035156,
"oldClose": 367.0
},
{
"date": 20160816,
"from": "ZC609",
"to": "ZC701",
"newClose": 516.2000122070312,
"oldClose": 454.6000061035156
},
{
"date": 20161206,
"from": "ZC701",
"to": "ZC705",
"newClose": 525.4000244140625,
"oldClose": 597.0
},
{
"date": 20170419,
"from": "ZC705",
"to": "ZC709",
"newClose": 531.5999755859375,
"oldClose": 622.5999755859375
},
{
"date": 20170810,
"from": "ZC709",
"to": "ZC801",
"newClose": 605.4000244140625,
"oldClose": 608.7999877929688
},
{
"date": 20171220,
"from": "ZC801",
"to": "ZC805",
"newClose": 636.5999755859375,
"oldClose": 687.5999755859375
},
{
"date": 20180413,
"from": "ZC805",
"to": "ZC809",
"newClose": 556.4000244140625,
"oldClose": 558.5999755859375
},
{
"date": 20180814,
"from": "ZC809",
"to": "ZC901",
"newClose": 617.4000244140625,
"oldClose": 627.7999877929688
},
{
"date": 20181210,
"from": "ZC901",
"to": "ZC905",
"newClose": 576.7999877929688,
"oldClose": 615.4000244140625
},
{
"date": 20190422,
"from": "ZC905",
"to": "ZC909",
"newClose": 596.7999877929688,
"oldClose": 611.0
},
{
"date": 20190822,
"from": "ZC909",
"to": "ZC911",
"newClose": 587.0,
"oldClose": 589.7999877929688
},
{
"date": 20190920,
"from": "ZC911",
"to": "ZC001",
"newClose": 576.4000244140625,
"oldClose": 581.4000244140625
},
{
"date": 20191211,
"from": "ZC001",
"to": "ZC005",
"newClose": 535.5999755859375,
"oldClose": 543.2000122070312
},
{
"date": 20200330,
"from": "ZC005",
"to": "ZC009",
"newClose": 501.6000061035156,
"oldClose": 517.7999877929688
},
{
"date": 20200818,
"from": "ZC009",
"to": "ZC011",
"newClose": 561.4000244140625,
"oldClose": 570.0
},
{
"date": 20201020,
"from": "ZC011",
"to": "ZC101",
"newClose": 576.4000244140625,
"oldClose": 590.0
},
{
"date": 20201215,
"from": "ZC101",
"to": "ZC105",
"newClose": 668.5999755859375,
"oldClose": 721.0
},
{
"date": 20210420,
"from": "ZC105",
"to": "ZC109",
"newClose": 712.5999755859375,
"oldClose": 782.2000122070312
},
{
"date": 20210813,
"from": "ZC109",
"to": "ZC201",
"newClose": 778.5999755859375,
"oldClose": 889.7999877929688
}
],
"RM": [
{
"date": 20160104,
"from": "",
"to": "RM605",
"newClose": 1870.0,
"oldClose": 0
},
{
"date": 20160314,
"from": "RM605",
"to": "RM609",
"newClose": 1915.0,
"oldClose": 1915.0
},
{
"date": 20160805,
"from": "RM609",
"to": "RM701",
"newClose": 2370.0,
"oldClose": 2502.0
},
{
"date": 20161128,
"from": "RM701",
"to": "RM705",
"newClose": 2534.0,
"oldClose": 2469.0
},
{
"date": 20170320,
"from": "RM705",
"to": "RM709",
"newClose": 2434.0,
"oldClose": 2419.0
},
{
"date": 20170808,
"from": "RM709",
"to": "RM801",
"newClose": 2229.0,
"oldClose": 2318.0
},
{
"date": 20171206,
"from": "RM801",
"to": "RM805",
"newClose": 2394.0,
"oldClose": 2355.0
},
{
"date": 20180327,
"from": "RM805",
"to": "RM809",
"newClose": 2497.0,
"oldClose": 2481.0
},
{
"date": 20180806,
"from": "RM809",
"to": "RM901",
"newClose": 2480.0,
"oldClose": 2486.0
},
{
"date": 20181211,
"from": "RM901",
"to": "RM905",
"newClose": 2216.0,
"oldClose": 2227.0
},
{
"date": 20190410,
"from": "RM905",
"to": "RM909",
"newClose": 2207.0,
"oldClose": 2179.0
},
{
"date": 20190816,
"from": "RM909",
"to": "RM001",
"newClose": 2302.0,
"oldClose": 2444.0
},
{
"date": 20191212,
"from": "RM001",
"to": "RM005",
"newClose": 2308.0,
"oldClose": 2244.0
},
{
"date": 20200401,
"from": "RM005",
"to": "RM009",
"newClose": 2373.0,
"oldClose": 2391.0
},
{
"date": 20200817,
"from": "RM009",
"to": "RM101",
"newClose": 2297.0,
"oldClose": 2329.0
},
{
"date": 20201214,
"from": "RM101",
"to": "RM105",
"newClose": 2540.0,
"oldClose": 2482.0
},
{
"date": 20210416,
"from": "RM105",
"to": "RM109",
"newClose": 2896.0,
"oldClose": 2798.0
},
{
"date": 20210813,
"from": "RM109",
"to": "RM201",
"newClose": 2929.0,
"oldClose": 3025.0
}
],
"CY": [
{
"date": 20171018,
"from": "",
"to": "CY801",
"newClose": 23015.0,
"oldClose": 0
},
{
"date": 20171201,
"from": "CY801",
"to": "CY809",
"newClose": 23575.0,
"oldClose": 23200.0
},
{
"date": 20180424,
"from": "CY809",
"to": "CY810",
"newClose": 24020.0,
"oldClose": 23495.0
},
{
"date": 20181008,
"from": "CY810",
"to": "CY901",
"newClose": 24520.0,
"oldClose": 24200.0
},
{
"date": 20190102,
"from": "CY901",
"to": "CY905",
"newClose": 23855.0,
"oldClose": 23600.0
},
{
"date": 20190410,
"from": "CY905",
"to": "CY909",
"newClose": 25090.0,
"oldClose": 24405.0
},
{
"date": 20190415,
"from": "CY909",
"to": "CY001",
"newClose": 24215.0,
"oldClose": 25070.0
},
{
"date": 20191203,
"from": "CY001",
"to": "CY005",
"newClose": 20970.0,
"oldClose": 20545.0
},
{
"date": 20200407,
"from": "CY005",
"to": "CY009",
"newClose": 18700.0,
"oldClose": 18470.0
},
{
"date": 20200810,
"from": "CY009",
"to": "CY101",
"newClose": 19910.0,
"oldClose": 18680.0
},
{
"date": 20201207,
"from": "CY101",
"to": "CY105",
"newClose": 21475.0,
"oldClose": 20655.0
},
{
"date": 20210409,
"from": "CY105",
"to": "CY109",
"newClose": 22410.0,
"oldClose": 22475.0
},
{
"date": 20210812,
"from": "CY109",
"to": "CY201",
"newClose": 26510.0,
"oldClose": 26685.0
}
],
"AP": [
{
"date": 20171222,
"from": "",
"to": "AP805",
"newClose": 8117.0,
"oldClose": 0
},
{
"date": 20180412,
"from": "AP805",
"to": "AP810",
"newClose": 7083.0,
"oldClose": 7221.0
},
{
"date": 20180716,
"from": "AP810",
"to": "AP901",
"newClose": 10061.0,
"oldClose": 9247.0
},
{
"date": 20181114,
"from": "AP901",
"to": "AP905",
"newClose": 12298.0,
"oldClose": 11402.0
},
{
"date": 20190412,
"from": "AP905",
"to": "AP910",
"newClose": 7684.0,
"oldClose": 11825.0
},
{
"date": 20190919,
"from": "AP910",
"to": "AP001",
"newClose": 7877.0,
"oldClose": 8387.0
},
{
"date": 20191217,
"from": "AP001",
"to": "AP005",
"newClose": 8248.0,
"oldClose": 8563.0
},
{
"date": 20200410,
"from": "AP005",
"to": "AP010",
"newClose": 8325.0,
"oldClose": 6720.0
},
{
"date": 20200917,
"from": "AP010",
"to": "AP101",
"newClose": 7372.0,
"oldClose": 6705.0
},
{
"date": 20201215,
"from": "AP101",
"to": "AP105",
"newClose": 6530.0,
"oldClose": 6462.0
},
{
"date": 20210319,
"from": "AP105",
"to": "AP110",
"newClose": 7026.0,
"oldClose": 5173.0
},
{
"date": 20210831,
"from": "AP110",
"to": "AP201",
"newClose": 5775.0,
"oldClose": 5963.0
}
],
"CJ": [
{
"date": 20190430,
"from": "",
"to": "CJ912",
"newClose": 8840.0,
"oldClose": 0
},
{
"date": 20191107,
"from": "CJ912",
"to": "CJ001",
"newClose": 10880.0,
"oldClose": 10855.0
},
{
"date": 20191205,
"from": "CJ001",
"to": "CJ005",
"newClose": 10795.0,
"oldClose": 10840.0
},
{
"date": 20200408,
"from": "CJ005",
"to": "CJ009",
"newClose": 10235.0,
"oldClose": 10060.0
},
{
"date": 20200820,
"from": "CJ009",
"to": "CJ101",
"newClose": 9690.0,
"oldClose": 8930.0
},
{
"date": 20201218,
"from": "CJ101",
"to": "CJ105",
"newClose": 9770.0,
"oldClose": 10035.0
},
{
"date": 20210412,
"from": "CJ105",
"to": "CJ109",
"newClose": 10075.0,
"oldClose": 9715.0
},
{
"date": 20210721,
"from": "CJ109",
"to": "CJ201",
"newClose": 12400.0,
"oldClose": 8980.0
}
],
"UR": [
{
"date": 20190809,
"from": "",
"to": "UR001",
"newClose": 1702.0,
"oldClose": 0
},
{
"date": 20191213,
"from": "UR001",
"to": "UR005",
"newClose": 1736.0,
"oldClose": 1710.0
},
{
"date": 20200409,
"from": "UR005",
"to": "UR009",
"newClose": 1578.0,
"oldClose": 1676.0
},
{
"date": 20200811,
"from": "UR009",
"to": "UR101",
"newClose": 1704.0,
"oldClose": 1727.0
},
{
"date": 20201218,
"from": "UR101",
"to": "UR105",
"newClose": 1867.0,
"oldClose": 1807.0
},
{
"date": 20210420,
"from": "UR105",
"to": "UR107",
"newClose": 2040.0,
"oldClose": 2050.0
},
{
"date": 20210607,
"from": "UR107",
"to": "UR109",
"newClose": 2383.0,
"oldClose": 2417.0
},
{
"date": 20210812,
"from": "UR109",
"to": "UR201",
"newClose": 2358.0,
"oldClose": 2465.0
}
],
"SA": [
{
"date": 20191206,
"from": "",
"to": "SA005",
"newClose": 1561.0,
"oldClose": 0
},
{
"date": 20200417,
"from": "SA005",
"to": "SA009",
"newClose": 1472.0,
"oldClose": 1356.0
},
{
"date": 20200819,
"from": "SA009",
"to": "SA101",
"newClose": 1569.0,
"oldClose": 1409.0
},
{
"date": 20201218,
"from": "SA101",
"to": "SA105",
"newClose": 1573.0,
"oldClose": 1416.0
},
{
"date": 20210408,
"from": "SA105",
"to": "SA109",
"newClose": 1967.0,
"oldClose": 1842.0
},
{
"date": 20210728,
"from": "SA109",
"to": "SA201",
"newClose": 2584.0,
"oldClose": 2312.0
}
],
"PF": [
{
"date": 20201014,
"from": "",
"to": "PF105",
"newClose": 6104.0,
"oldClose": 0
},
{
"date": 20210416,
"from": "PF105",
"to": "PF107",
"newClose": 7234.0,
"oldClose": 6986.0
},
{
"date": 20210513,
"from": "PF107",
"to": "PF109",
"newClose": 7152.0,
"oldClose": 7064.0
},
{
"date": 20210823,
"from": "PF109",
"to": "PF201",
"newClose": 7012.0,
"oldClose": 6894.0
}
]
},
"INE": {
"sc": [
{
"date": 20180326,
"from": "",
"to": "sc1809",
"newClose": 429.8999938964844,
"oldClose": 0
},
{
"date": 20180815,
"from": "sc1809",
"to": "sc1812",
"newClose": 510.1000061035156,
"oldClose": 516.7999877929688
},
{
"date": 20181120,
"from": "sc1812",
"to": "sc1901",
"newClose": 465.0,
"oldClose": 455.79998779296875
},
{
"date": 20181218,
"from": "sc1901",
"to": "sc1903",
"newClose": 415.79998779296875,
"oldClose": 404.70001220703125
},
{
"date": 20190218,
"from": "sc1903",
"to": "sc1904",
"newClose": 461.1000061035156,
"oldClose": 453.0
},
{
"date": 20190319,
"from": "sc1904",
"to": "sc1905",
"newClose": 457.29998779296875,
"oldClose": 468.20001220703125
},
{
"date": 20190418,
"from": "sc1905",
"to": "sc1906",
"newClose": 472.6000061035156,
"oldClose": 474.70001220703125
},
{
"date": 20190521,
"from": "sc1906",
"to": "sc1907",
"newClose": 508.8999938964844,
"oldClose": 509.6000061035156
},
{
"date": 20190618,
"from": "sc1907",
"to": "sc1908",
"newClose": 413.5,
"oldClose": 408.1000061035156
},
{
"date": 20190719,
"from": "sc1908",
"to": "sc1909",
"newClose": 431.3999938964844,
"oldClose": 425.8999938964844
},
{
"date": 20190820,
"from": "sc1909",
"to": "sc1910",
"newClose": 427.0,
"oldClose": 431.3999938964844
},
{
"date": 20190911,
"from": "sc1910",
"to": "sc1911",
"newClose": 448.8999938964844,
"oldClose": 458.0
},
{
"date": 20191021,
"from": "sc1911",
"to": "sc1912",
"newClose": 446.1000061035156,
"oldClose": 441.3999938964844
},
{
"date": 20191119,
"from": "sc1912",
"to": "sc2001",
"newClose": 453.1000061035156,
"oldClose": 453.3999938964844
},
{
"date": 20191210,
"from": "sc2001",
"to": "sc2002",
"newClose": 466.70001220703125,
"oldClose": 470.20001220703125
},
{
"date": 20200106,
"from": "sc2002",
"to": "sc2003",
"newClose": 516.0999755859375,
"oldClose": 509.20001220703125
},
{
"date": 20200203,
"from": "sc2003",
"to": "sc2004",
"newClose": 413.6000061035156,
"oldClose": 415.5
},
{
"date": 20200304,
"from": "sc2004",
"to": "sc2005",
"newClose": 375.1000061035156,
"oldClose": 367.70001220703125
},
{
"date": 20200330,
"from": "sc2005",
"to": "sc2006",
"newClose": 252.0,
"oldClose": 245.6999969482422
},
{
"date": 20200512,
"from": "sc2006",
"to": "sc2007",
"newClose": 253.1999969482422,
"oldClose": 238.39999389648438
},
{
"date": 20200616,
"from": "sc2007",
"to": "sc2008",
"newClose": 290.20001220703125,
"oldClose": 275.5
},
{
"date": 20200716,
"from": "sc2008",
"to": "sc2009",
"newClose": 301.5,
"oldClose": 288.70001220703125
},
{
"date": 20200817,
"from": "sc2009",
"to": "sc2010",
"newClose": 289.29998779296875,
"oldClose": 279.20001220703125
},
{
"date": 20200911,
"from": "sc2010",
"to": "sc2011",
"newClose": 259.79998779296875,
"oldClose": 243.3000030517578
},
{
"date": 20201015,
"from": "sc2011",
"to": "sc2012",
"newClose": 272.8999938964844,
"oldClose": 260.20001220703125
},
{
"date": 20201113,
"from": "sc2012",
"to": "sc2101",
"newClose": 257.20001220703125,
"oldClose": 248.5
},
{
"date": 20201211,
"from": "sc2101",
"to": "sc2102",
"newClose": 301.5,
"oldClose": 288.29998779296875
},
{
"date": 20210113,
"from": "sc2102",
"to": "sc2103",
"newClose": 350.29998779296875,
"oldClose": 342.3999938964844
},
{
"date": 20210209,
"from": "sc2103",
"to": "sc2104",
"newClose": 378.20001220703125,
"oldClose": 376.0
},
{
"date": 20210312,
"from": "sc2104",
"to": "sc2105",
"newClose": 423.3999938964844,
"oldClose": 418.79998779296875
},
{
"date": 20210416,
"from": "sc2105",
"to": "sc2106",
"newClose": 416.8999938964844,
"oldClose": 412.79998779296875
},
{
"date": 20210517,
"from": "sc2106",
"to": "sc2107",
"newClose": 426.70001220703125,
"oldClose": 427.1000061035156
},
{
"date": 20210616,
"from": "sc2107",
"to": "sc2108",
"newClose": 461.29998779296875,
"oldClose": 458.5
},
{
"date": 20210719,
"from": "sc2108",
"to": "sc2109",
"newClose": 435.1000061035156,
"oldClose": 427.8999938964844
},
{
"date": 20210817,
"from": "sc2109",
"to": "sc2110",
"newClose": 419.8999938964844,
"oldClose": 422.0
},
{
"date": 20210909,
"from": "sc2110",
"to": "sc2111",
"newClose": 451.3999938964844,
"oldClose": 457.1000061035156
}
],
"nr": [
{
"date": 20190812,
"from": "",
"to": "nr2002",
"newClose": 9820.0,
"oldClose": 0
},
{
"date": 20191028,
"from": "nr2002",
"to": "nr2004",
"newClose": 10010.0,
"oldClose": 9920.0
},
{
"date": 20200226,
"from": "nr2004",
"to": "nr2005",
"newClose": 9620.0,
"oldClose": 9535.0
},
{
"date": 20200414,
"from": "nr2005",
"to": "nr2006",
"newClose": 8285.0,
"oldClose": 8130.0
},
{
"date": 20200508,
"from": "nr2006",
"to": "nr2007",
"newClose": 8445.0,
"oldClose": 8355.0
},
{
"date": 20200529,
"from": "nr2007",
"to": "nr2009",
"newClose": 8525.0,
"oldClose": 8340.0
},
{
"date": 20200729,
"from": "nr2009",
"to": "nr2010",
"newClose": 8990.0,
"oldClose": 8880.0
},
{
"date": 20200827,
"from": "nr2010",
"to": "nr2011",
"newClose": 9675.0,
"oldClose": 9550.0
},
{
"date": 20200928,
"from": "nr2011",
"to": "nr2012",
"newClose": 9705.0,
"oldClose": 9520.0
},
{
"date": 20201027,
"from": "nr2012",
"to": "nr2101",
"newClose": 11725.0,
"oldClose": 11600.0
},
{
"date": 20201130,
"from": "nr2101",
"to": "nr2102",
"newClose": 11025.0,
"oldClose": 10895.0
},
{
"date": 20201228,
"from": "nr2102",
"to": "nr2103",
"newClose": 10250.0,
"oldClose": 10165.0
},
{
"date": 20210129,
"from": "nr2103",
"to": "nr2104",
"newClose": 10750.0,
"oldClose": 10660.0
},
{
"date": 20210226,
"from": "nr2104",
"to": "nr2105",
"newClose": 12045.0,
"oldClose": 11950.0
},
{
"date": 20210331,
"from": "nr2105",
"to": "nr2106",
"newClose": 10890.0,
"oldClose": 10710.0
},
{
"date": 20210506,
"from": "nr2106",
"to": "nr2107",
"newClose": 11585.0,
"oldClose": 11470.0
},
{
"date": 20210602,
"from": "nr2107",
"to": "nr2108",
"newClose": 10690.0,
"oldClose": 10530.0
},
{
"date": 20210701,
"from": "nr2108",
"to": "nr2109",
"newClose": 10290.0,
"oldClose": 10150.0
},
{
"date": 20210803,
"from": "nr2109",
"to": "nr2110",
"newClose": 11160.0,
"oldClose": 11000.0
},
{
"date": 20210902,
"from": "nr2110",
"to": "nr2111",
"newClose": 10735.0,
"oldClose": 10610.0
},
{
"date": 20210928,
"from": "nr2111",
"to": "nr2112",
"newClose": 11175.0,
"oldClose": 11280.0
}
],
"lu": [
{
"date": 20201009,
"from": "",
"to": "lu2101",
"newClose": 2314.0,
"oldClose": 0
},
{
"date": 20201120,
"from": "lu2101",
"to": "lu2103",
"newClose": 2430.0,
"oldClose": 2346.0
},
{
"date": 20210105,
"from": "lu2103",
"to": "lu2104",
"newClose": 2606.0,
"oldClose": 2595.0
},
{
"date": 20210202,
"from": "lu2104",
"to": "lu2105",
"newClose": 2939.0,
"oldClose": 2942.0
},
{
"date": 20210310,
"from": "lu2105",
"to": "lu2106",
"newClose": 3296.0,
"oldClose": 3278.0
},
{
"date": 20210412,
"from": "lu2106",
"to": "lu2107",
"newClose": 3107.0,
"oldClose": 3106.0
},
{
"date": 20210506,
"from": "lu2107",
"to": "lu2108",
"newClose": 3291.0,
"oldClose": 3286.0
},
{
"date": 20210602,
"from": "lu2108",
"to": "lu2109",
"newClose": 3318.0,
"oldClose": 3304.0
},
{
"date": 20210707,
"from": "lu2109",
"to": "lu2110",
"newClose": 3417.0,
"oldClose": 3398.0
},
{
"date": 20210805,
"from": "lu2110",
"to": "lu2111",
"newClose": 3259.0,
"oldClose": 3261.0
},
{
"date": 20210903,
"from": "lu2111",
"to": "lu2112",
"newClose": 3463.0,
"oldClose": 3481.0
},
{
"date": 20211008,
"from": "lu2112",
"to": "lu2201",
"newClose": 3866.0,
"oldClose": 3880.0
}
],
"bc": [
{
"date": 20210104,
"from": "",
"to": "bc2103",
"newClose": 51790.0,
"oldClose": 0
},
{
"date": 20210202,
"from": "bc2103",
"to": "bc2105",
"newClose": 51000.0,
"oldClose": 51040.0
},
{
"date": 20210406,
"from": "bc2105",
"to": "bc2106",
"newClose": 59680.0,
"oldClose": 59500.0
},
{
"date": 20210507,
"from": "bc2106",
"to": "bc2107",
"newClose": 66840.0,
"oldClose": 66720.0
},
{
"date": 20210603,
"from": "bc2107",
"to": "bc2108",
"newClose": 65070.0,
"oldClose": 64960.0
},
{
"date": 20210702,
"from": "bc2108",
"to": "bc2109",
"newClose": 60850.0,
"oldClose": 60830.0
},
{
"date": 20210730,
"from": "bc2109",
"to": "bc2110",
"newClose": 64100.0,
"oldClose": 63980.0
},
{
"date": 20210827,
"from": "bc2110",
"to": "bc2111",
"newClose": 61280.0,
"oldClose": 61380.0
},
{
"date": 20210928,
"from": "bc2111",
"to": "bc2112",
"newClose": 61350.0,
"oldClose": 61350.0
}
]
}
}
================================================
FILE: config/01commom/sessions.json
================================================
{
"SD0930":{
"name":"SD0930",
"offset": 0,
"auction":{
"from": 929,
"to": 930
},
"sections":[
{
"from": 930,
"to": 1130
},
{
"from": 1300,
"to": 1500
}
]
},
"FD0915":{
"name":"FD0915",
"offset": 0,
"auction":{
"from": 929,
"to": 930
},
"sections":[
{
"from": 930,
"to": 1130
},
{
"from": 1300,
"to": 1515
}
]
},
"FN0100":{
"name":"FN0100",
"offset": 300,
"auction":{
"from": 2059,
"to": 2100
},
"sections":[
{
"from": 2100,
"to": 100
},
{
"from": 900,
"to": 1015
},
{
"from": 1030,
"to": 1130
},
{
"from": 1330,
"to": 1500
}
]
},
"FN0230":{
"name":"FN0230",
"offset": 300,
"auction":{
"from": 2059,
"to": 2100
},
"sections":[
{
"from": 2100,
"to": 230
},
{
"from": 900,
"to": 1015
},
{
"from": 1030,
"to": 1130
},
{
"from": 1330,
"to": 1500
}
]
},
"FN2300":{
"name":"FN2300",
"offset": 300,
"auction":{
"from": 2059,
"to": 2100
},
"sections":[
{
"from": 2100,
"to": 2300
},
{
"from": 900,
"to": 1015
},
{
"from": 1030,
"to": 1130
},
{
"from": 1330,
"to": 1500
}
]
},
"FD0900":{
"name":"FD0900",
"offset": 0,
"auction":{
"from": 859,
"to": 900
},
"sections":[
{
"from": 900,
"to": 1015
},
{
"from": 1030,
"to": 1130
},
{
"from": 1330,
"to": 1500
}
]
},
"FN2330":{
"name":"FN2330",
"offset": 300,
"auction":{
"from": 2059,
"to": 2100
},
"sections":[
{
"from": 2100,
"to": 2330
},
{
"from": 900,
"to": 1015
},
{
"from": 1030,
"to": 1130
},
{
"from": 1330,
"to": 1500
}
]
},
"TRADING":{
"name":"TRADING",
"offset": 300,
"auction":{
"from": 2059,
"to": 2100
},
"sections":[
{
"from": 2100,
"to": 230
},
{
"from": 900,
"to": 1130
},
{
"from": 1300,
"to": 1515
}
]
}
}
================================================
FILE: config/03research/cta.json
================================================
{
"replayer":{
"mode":"bin",
"path":"./dataset/",
"path1":"D:/Github/test/Storage",
"tick":false,
"basefiles":{
"session":"./config/01commom/sessions.json",
"commodity":"./config/01commom/commodities.json",
"contract":"./config/01commom/contracts.json",
"holiday":"./config/01commom/holidays.json",
"hot":"./config/01commom/hots.json"
},
"fees":"./config/01commom/fees.json"
},
"env":{
"mocker":"cta"
},
"bspolicy":"./config/01commom/actpolicy.json"
}
================================================
FILE: config/03research/hft.json
================================================
{
"replayer":{
"mode":"bin",
"path":"./dataset/",
"tick":true,
"basefiles":{
"session":"./config/01commom/sessions.json",
"commodity":"./config/01commom/commodities.json",
"contract":"./config/01commom/contracts.json",
"holiday":"./config/01commom/holidays.json",
"hot":"./config/01commom/hots.json"
},
"fees":"./config/01commom/fees.json"
},
"env":{
"mocker":"hft"
},
"bspolicy":"./config/01commom/actpolicy.json"
}
================================================
FILE: config/03research/log_debugger.json
================================================
{
"root":{
"level":"debug",
"async":false,
"sinks":[
{
"type":"basic_file_sink",
"filename":"outputs_bt/logs/runner.log",
"pattern":"[%Y.%m.%d %H:%M:%S,%f - %-5l] %v",
"truncate":true
},
{
"type":"ostream_sink",
"pattern":"[%Y.%m.%d %H:%M:%S,%f - %-5l] %v"
}
]
},
"dyn_pattern":{
"strategy":{
"level":"debug",
"async":false,
"sinks":[
{
"type":"basic_file_sink",
"filename":"outputs_bt/logs/strategy/%s.log",
"pattern":"[%Y.%m.%d %H:%M:%S,%f - %-5l] %v",
"truncate":true
}
]
}
}
}
================================================
FILE: config/03research/log_evaluator.json
================================================
{
"root":{
"level":"info",
"async":false,
"sinks":[]
},
"dyn_pattern":{
"strategy":{
"level":"info",
"async":false,
"sinks":[
{
"type":"basic_file_sink",
"filename":"outputs_bt/logs/strategy/%s.log",
"pattern":"[%Y.%m.%d %H:%M:%S,%f - %-5l] %v",
"truncate":true
}
]
}
}
}
================================================
FILE: config/03research/log_trainer.json
================================================
{
"root":{
"level":"info",
"async":false,
"sinks":[]
},
"dyn_pattern":{
"strategy":{
"level":"info",
"async":false,
"sinks":[]
}
}
}
================================================
FILE: config/04realtime/log.json
================================================
{
"root":{
"level":"debug",
"async":false,
"sinks":[
{
"type":"daily_file_sink",
"filename":"Logs/Runner.log",
"pattern":"[%Y.%m.%d %H:%M:%S - %-5l] %v",
"truncate":true
},
{
"type":"ostream_sink",
"pattern":"[%m.%d %H:%M:%S - %^%-5l%$] %v"
}
]
},
"dyn_pattern":{
"strategy":{
"level":"debug",
"async":false,
"sinks":[
{
"type":"daily_file_sink",
"filename":"Logs/Strategy/%s.log",
"pattern":"[%Y.%m.%d %H:%M:%S - %-5l] %v",
"truncate":true
}
]
},
"parser":{
"level":"debug",
"async":false,
"sinks":[
{
"type":"daily_file_sink",
"filename":"Logs/Parser/%s.log",
"pattern":"[%Y.%m.%d %H:%M:%S - %-5l] %v",
"truncate":true
}
]
},
"trader":{
"level":"debug",
"async":false,
"sinks":[
{
"type":"daily_file_sink",
"filename":"Logs/Trader/%s.log",
"pattern":"[%Y.%m.%d %H:%M:%S - %-5l] %v",
"truncate":true
}
]
},
"executer":{
"level":"debug",
"async":false,
"sinks":[
{
"type":"daily_file_sink",
"filename":"Logs/Executer/%s.log",
"pattern":"[%Y.%m.%d %H:%M:%S - %-5l] %v",
"truncate":true
}
]
}
},
"risk":{
"level":"debug",
"async":false,
"sinks":[
{
"type":"daily_file_sink",
"filename":"Logs/Riskmon/Riskmon.log",
"pattern":"[%Y.%m.%d %H:%M:%S - %-5l] %v",
"truncate":true
}
]
}
}
================================================
FILE: dataset_from_storage.py
================================================
import pandas as pd
from ctypes import POINTER
from os import makedirs
from wtpy.wrapper.WtDtHelper import WtDataHelper
from wtpy.WtCoreDefs import WTSBarStruct
from wtpy import WtDtServo
class DataReader:
df: pd.DataFrame
def __len__(self) -> int:
return len(self.df)
def set_bars(self, data: pd.DataFrame) -> tuple:
dtype = {
'date': int,
'time': int,
'open': float,
'high': float,
'low': float,
'close': float,
'money': float,
'vol': int,
'hold': int,
'diff': int,
}
self.df = data[dtype.keys()].astype(dtype)
def get_bars(self, curBar: POINTER(WTSBarStruct), idx: int) -> bool:
curBar.contents.date = self.df['date'].iloc[idx]
curBar.contents.time = self.df['time'].iloc[idx]
curBar.contents.open = self.df['open'].iloc[idx]
curBar.contents.high = self.df['high'].iloc[idx]
curBar.contents.low = self.df['low'].iloc[idx]
curBar.contents.close = self.df['close'].iloc[idx]
curBar.contents.money = self.df['money'].iloc[idx]
curBar.contents.vol = self.df['vol'].iloc[idx]
curBar.contents.hold = self.df['hold'].iloc[idx]
curBar.contents.diff = self.df['diff'].iloc[idx]
return True
reader: DataReader = DataReader()
helper: WtDataHelper = WtDataHelper()
dt: WtDtServo = WtDtServo()
dt.setBasefiles(
commfile='./config/01commom/commodities.json',
contractfile='./config/01commom/contracts.json',
holidayfile='./config/01commom/holidays.json',
sessionfile='./config/01commom/sessions.json',
hotfile='./config/01commom/hots.json'
)
dt.setStorage('D:/Github/test/Storage/')
dt.commitConfig()
securities: tuple = (
'CFFEX.IC.HOT',
'CFFEX.IF.HOT',
'CFFEX.IH.HOT',
'CFFEX.T.HOT',
'CFFEX.TF.HOT',
'CZCE.RM.HOT',#
'CZCE.JR.HOT',#
'CZCE.AP.HOT',
'CZCE.CF.HOT',
'CZCE.MA.HOT',
'CZCE.SF.HOT',
'CZCE.SR.HOT',
'CZCE.TA.HOT',#
'CZCE.ZC.HOT',
'DCE.a.HOT',
'DCE.c.HOT',#
'DCE.cs.HOT',#
'DCE.i.HOT',
'DCE.jd.HOT',
'DCE.l.HOT',
'DCE.m.HOT',#
'DCE.p.HOT',
'DCE.pp.HOT',
'DCE.y.HOT',
'DCE.rr.HOT',#
'INE.sc.HOT',
'INE.lu.HOT',
'SHFE.ag.HOT',
'SHFE.al.HOT',
'SHFE.bu.HOT',
'SHFE.cu.HOT',
'SHFE.fu.HOT',
'SHFE.hc.HOT',
'SHFE.ni.HOT',
'SHFE.pb.HOT',
'SHFE.rb.HOT',
'SHFE.zn.HOT',
)
for period, name in {'d1': 'day', 'm5': 'min5', }.items():#, 'm1': 'min1'
for code in securities:
print(period, code)
path = './dataset/his/%s/%s' % (name, code.split('.')[0])
makedirs(path, exist_ok=True)
df = dt.get_bars(code, period=period, fromTime=201601011600,
endTime=202110131600).to_pandas()
reader.set_bars(df)
helper.trans_bars(barFile='%s/%s.dsb' % (path, code.replace('.HOT', '_HOT')), getter=reader.get_bars,
count=len(reader), period='d' if period == 'd1' else period)
# break
# break
================================================
FILE: elegantrl/__init__.py
================================================
================================================
FILE: elegantrl/agent.py
================================================
import os
import torch
import numpy as np
import numpy.random as rd
from copy import deepcopy
from torch.nn.utils import clip_grad_norm_
from elegantrl.net import QNet, QNetDuel, QNetTwin, QNetTwinDuel
from elegantrl.net import Actor, Critic, ShareDPG
from elegantrl.net import ActorSAC, CriticTwin, ShareSPG
from elegantrl.net import ActorPPO, ActorDiscretePPO, CriticPPO, SharePPO
from elegantrl.net import ActorBiConv, CriticBiConv, ShareBiConv
"""[ElegantRL.2021.11.05](https://github.com/AI4Finance-Foundation/ElegantRL)"""
class AgentBase:
def __init__(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
"""initialize
replace by different DRL algorithms
explict call self.init() for multiprocessing.
:param net_dim: the dimension of networks (the width of neural networks)
:param state_dim: the dimension of state (the number of state vector)
:param action_dim: the dimension of action (the number of discrete action)
:param reward_scale: scale the reward to get a appropriate scale Q value
:param gamma: the discount factor of Reinforcement Learning
:param learning_rate: learning rate of optimizer
:param if_per_or_gae: PER (off-policy) or GAE (on-policy) for sparse reward
:param env_num: the env number of VectorEnv. env_num == 1 means don't use VectorEnv
:param gpu_id: the gpu_id of the training device. Use CPU when cuda is not available.
"""
self.gamma = None
self.states = None
self.device = None
self.traj_list = None
self.action_dim = None
self.reward_scale = None
self.if_off_policy = True
self.env_num = env_num
self.explore_rate = 1.0
self.explore_noise = 0.1
self.clip_grad_norm = 4.0
# self.amp_scale = None # automatic mixed precision
'''attribute'''
self.explore_env = None
self.get_obj_critic = None
self.criterion = torch.nn.SmoothL1Loss()
self.cri = self.cri_target = self.if_use_cri_target = self.cri_optim = self.ClassCri = None
self.act = self.act_target = self.if_use_act_target = self.act_optim = self.ClassAct = None
assert isinstance(gpu_id, int)
assert isinstance(env_num, int)
assert isinstance(net_dim, int)
assert isinstance(state_dim, int)
assert isinstance(action_dim, int)
assert isinstance(if_per_or_gae, bool)
assert isinstance(gamma, float)
assert isinstance(reward_scale, float)
assert isinstance(learning_rate, float)
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
"""initialize the self.object in `__init__()`
replace by different DRL algorithms
explict call self.init() for multiprocessing.
:param net_dim: the dimension of networks (the width of neural networks)
:param state_dim: the dimension of state (the number of state vector)
:param action_dim: the dimension of action (the number of discrete action)
:param reward_scale: scale the reward to get a appropriate scale Q value
:param gamma: the discount factor of Reinforcement Learning
:param learning_rate: learning rate of optimizer
:param if_per_or_gae: PER (off-policy) or GAE (on-policy) for sparse reward
:param env_num: the env number of VectorEnv. env_num == 1 means don't use VectorEnv
:param gpu_id: the gpu_id of the training device. Use CPU when cuda is not available.
"""
self.gamma = gamma
self.action_dim = action_dim
self.reward_scale = reward_scale
# self.amp_scale = torch.cuda.amp.GradScaler()
self.traj_list = [list() for _ in range(env_num)]
self.device = torch.device(f"cuda:{gpu_id}" if (torch.cuda.is_available() and (gpu_id >= 0)) else "cpu")
self.cri = self.ClassCri(int(net_dim * 1.25), state_dim, action_dim).to(self.device)
self.act = self.ClassAct(net_dim, state_dim, action_dim).to(self.device) if self.ClassAct else self.cri
self.cri_target = deepcopy(self.cri) if self.if_use_cri_target else self.cri
self.act_target = deepcopy(self.act) if self.if_use_act_target else self.act
self.cri_optim = torch.optim.Adam(self.cri.parameters(), learning_rate)
self.act_optim = torch.optim.Adam(self.act.parameters(), learning_rate) if self.ClassAct else self.cri
# del self.ClassCri, self.ClassAct
assert isinstance(if_per_or_gae, bool)
if env_num == 1:
self.explore_env = self.explore_one_env
else:
self.explore_env = self.explore_vec_env
def select_action(self, state: np.ndarray) -> np.ndarray:
s_tensor = torch.as_tensor(state[np.newaxis], device=self.device)
a_tensor = self.act(s_tensor)
action = a_tensor.detach().cpu().numpy()
return action
def select_actions(self, state: torch.Tensor) -> torch.Tensor:
"""Select continuous actions for exploration
:param state: states.shape==(batch_size, state_dim, )
:return: actions.shape==(batch_size, action_dim, ), -1 < action < +1
"""
action = self.act(state.to(self.device))
if rd.rand() < self.explore_rate: # epsilon-greedy
action = (action + torch.randn_like(action) * self.explore_noise).clamp(-1, 1)
return action.detach().cpu()
def explore_one_env(self, env, target_step: int) -> list:
"""actor explores in single Env, then returns the trajectory (env transitions) for ReplayBuffer
:param env: RL training environment. env.reset() env.step()
:param target_step: explored target_step number of step in env
:return: `[traj_env_0, ]`
`traj_env_0 = [(state, reward, mask, action, noise), ...]` for on-policy
`traj_env_0 = [(state, other), ...]` for off-policy
"""
state = self.states[0]
traj = list()
for _ in range(target_step):
ten_state = torch.as_tensor(state, dtype=torch.float32)
ten_action = self.select_actions(ten_state.unsqueeze(0))[0]
action = ten_action.numpy()
next_s, reward, done, _ = env.step(action)
ten_other = torch.empty(2 + self.action_dim)
ten_other[0] = reward
ten_other[1] = done
ten_other[2:] = ten_action
traj.append((ten_state, ten_other))
state = env.reset() if done else next_s
self.states[0] = state
traj_state = torch.stack([item[0] for item in traj])
traj_other = torch.stack([item[1] for item in traj])
traj_list = [(traj_state, traj_other), ]
return self.convert_trajectory(traj_list) # [traj_env_0, ]
def explore_vec_env(self, env, target_step: int) -> list:
"""actor explores in VectorEnv, then returns the trajectory (env transitions) for ReplayBuffer
:param env: RL training environment. env.reset() env.step(). It should be a vector env.
:param target_step: explored target_step number of step in env
:return: `[traj_env_0, ]`
`traj_env_0 = [(state, reward, mask, action, noise), ...]` for on-policy
`traj_env_0 = [(state, other), ...]` for off-policy
"""
ten_states = self.states
traj = list()
for _ in range(target_step):
ten_actions = self.select_actions(ten_states)
ten_next_states, ten_rewards, ten_dones = env.step(ten_actions)
ten_others = torch.cat((ten_rewards.unsqueeze(0), ten_dones.unsqueeze(0), ten_actions))
traj.append((ten_states, ten_others))
ten_states = ten_next_states
self.states = ten_states
# traj = [(env_ten, ...), ...], env_ten = (env1_ten, env2_ten, ...)
traj_state = torch.stack([item[0] for item in traj])
traj_other = torch.stack([item[1] for item in traj])
traj_list = [(traj_state[:, env_i, :], traj_other[:, env_i, :])
for env_i in range(len(self.states))]
# traj_list = [traj_env_0, ...], traj_env_0 = (ten_state, ten_other)
return self.convert_trajectory(traj_list) # [traj_env_0, ...]
def update_net(self, buffer, batch_size: int, repeat_times: float, soft_update_tau: float) -> tuple:
"""update the neural network by sampling batch data from ReplayBuffer
:param buffer: Experience replay buffer
:param batch_size: sample batch_size of data for Stochastic Gradient Descent
:param repeat_times: `batch_sampling_times = int(target_step * repeat_times / batch_size)`
:param soft_update_tau: soft target update: `target_net = target_net * (1-tau) + current_net * tau`,
"""
def optim_update(self, optimizer, objective, params): # plan todo params generator -> list
"""minimize the optimization objective via update the network parameters
:param optimizer: `optimizer = torch.optim.SGD(net.parameters(), learning_rate)`
:param objective: `objective = net(...)` the optimization objective, sometimes is a loss function.
:param params: `params = net.parameters()` the network parameters which need to be updated.
"""
optimizer.zero_grad()
objective.backward()
clip_grad_norm_(params, max_norm=self.clip_grad_norm)
optimizer.step()
# def optim_update_amp(self, optimizer, objective): # automatic mixed precision
# """minimize the optimization objective via update the network parameters
#
# amp: Automatic Mixed Precision
#
# :param optimizer: `optimizer = torch.optim.SGD(net.parameters(), learning_rate)`
# :param objective: `objective = net(...)` the optimization objective, sometimes is a loss function.
# :param params: `params = net.parameters()` the network parameters which need to be updated.
# """
# # self.amp_scale = torch.cuda.amp.GradScaler()
#
# optimizer.zero_grad()
# self.amp_scale.scale(objective).backward() # loss.backward()
# self.amp_scale.unscale_(optimizer) # amp
#
# # from torch.nn.utils import clip_grad_norm_
# # clip_grad_norm_(model.parameters(), max_norm=3.0) # amp, clip_grad_norm_
# self.amp_scale.step(optimizer) # optimizer.step()
# self.amp_scale.update() # optimizer.step()
@staticmethod
def soft_update(target_net, current_net, tau):
"""soft update target network via current network
:param target_net: update target network via current network to make training more stable.
:param current_net: current network update via an optimizer
:param tau: tau of soft target update: `target_net = target_net * (1-tau) + current_net * tau`
"""
for tar, cur in zip(target_net.parameters(), current_net.parameters()):
tar.data.copy_(cur.data * tau + tar.data * (1.0 - tau))
def save_or_load_agent(self, cwd: str, if_save: bool):
"""save or load training files for Agent
:param cwd: Current Working Directory. ElegantRL save training files in CWD.
:param if_save: True: save files. False: load files.
"""
def load_torch_file(model_or_optim, _path):
state_dict = torch.load(_path, map_location=lambda storage, loc: storage)
model_or_optim.load_state_dict(state_dict)
name_obj_list = [('actor', self.act), ('act_target', self.act_target), ('act_optim', self.act_optim),
('critic', self.cri), ('cri_target', self.cri_target), ('cri_optim', self.cri_optim), ]
name_obj_list = [(name, obj) for name, obj in name_obj_list if obj is not None]
if if_save:
for name, obj in name_obj_list:
save_path = f"{cwd}/{name}.pth"
torch.save(obj.state_dict(), save_path)
else:
for name, obj in name_obj_list:
save_path = f"{cwd}/{name}.pth"
load_torch_file(obj, save_path) if os.path.isfile(save_path) else None
def convert_trajectory(self, traj_list: list) -> list: # off-policy
"""convert trajectory (env exploration type) to trajectory (replay buffer type)
convert `other = concat(( reward, done, ...))`
to `other = concat((scale_reward, mask, ...))`
:param traj_list: `traj_list = [(tensor_state, other_state), ...]`
:return: `traj_list = [(tensor_state, other_state), ...]`
"""
for ten_state, ten_other in traj_list:
ten_other[:, 0] = ten_other[:, 0] * self.reward_scale # ten_reward
ten_other[:, 1] = (1.0 - ten_other[:, 1]) * self.gamma # ten_mask = (1.0 - ary_done) * gamma
return traj_list
"""Value-based Methods (Q network)"""
class AgentDQN(AgentBase): # [ElegantRL.2021.10.25]
def __init__(self):
AgentBase.__init__(self)
self.ClassCri = None # self.ClassCri = QNetDuel if self.if_use_dueling else QNet
self.if_use_dueling = True # self.ClassCri = QNetDuel if self.if_use_dueling else QNet
self.explore_rate = 0.25 # the probability of choosing action randomly in epsilon-greedy
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
self.ClassCri = QNetDuel if self.if_use_dueling else QNet
AgentBase.init(self, net_dim=net_dim, state_dim=state_dim, action_dim=action_dim,
reward_scale=reward_scale, gamma=gamma,
learning_rate=learning_rate, if_per_or_gae=if_per_or_gae,
env_num=env_num, gpu_id=gpu_id, )
if if_per_or_gae: # if_use_per
self.criterion = torch.nn.SmoothL1Loss(reduction='none')
self.get_obj_critic = self.get_obj_critic_per
else:
self.criterion = torch.nn.SmoothL1Loss(reduction='mean')
self.get_obj_critic = self.get_obj_critic_raw
def select_actions(self, states: torch.Tensor) -> torch.Tensor: # for discrete action space
"""Select discrete actions for exploration
`tensor states` states.shape==(batch_size, state_dim, )
return `tensor a_ints` a_ints.shape==(batch_size, )
"""
if rd.rand() < self.explore_rate: # epsilon-greedy
a_ints = torch.randint(self.action_dim, size=states.shape[0]) # choosing action randomly
else:
actions = self.act(states.to(self.device))
a_ints = actions.argmax(dim=1)
return a_ints.detach().cpu()
def explore_one_env(self, env, target_step) -> list:
traj = list()
state = self.states[0]
for _ in range(target_step):
ten_state = torch.as_tensor(state, dtype=torch.float32)
ten_action = self.select_actions(ten_state.unsqueeze(0))[0]
action = ten_action.numpy() # isinstance(action, int)
next_s, reward, done, _ = env.step(action)
ten_other = torch.empty(2 + 1)
ten_other[0] = reward
ten_other[1] = done
ten_other[2] = ten_action
traj.append((ten_state, ten_other))
state = env.reset() if done else next_s
self.states[0] = state
traj_state = torch.stack([item[0] for item in traj])
traj_other = torch.stack([item[1] for item in traj])
traj_list = [(traj_state, traj_other), ]
return self.convert_trajectory(traj_list) # [traj_env_0, ...]
def explore_vec_env(self, env, target_step) -> list:
ten_states = self.states
traj = list()
for _ in range(target_step):
ten_actions = self.select_actions(ten_states)
ten_next_states, ten_rewards, ten_dones = env.step(ten_actions)
ten_others = torch.cat((ten_rewards.unsqueeze(0),
ten_dones.unsqueeze(0),
ten_actions.unsqueeze(0)))
traj.append((ten_states, ten_others))
ten_states = ten_next_states
self.states = ten_states
traj_state = torch.stack([item[0] for item in traj])
traj_other = torch.stack([item[1] for item in traj])
traj_list = [(traj_state[:, env_i, :], traj_other[:, env_i, :])
for env_i in range(len(self.states))]
return self.convert_trajectory(traj_list) # [traj_env_0, ...]
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau) -> tuple:
buffer.update_now_len()
obj_critic = q_value = None
for _ in range(int(buffer.now_len / batch_size * repeat_times)):
obj_critic, q_value = self.get_obj_critic(buffer, batch_size)
self.optim_update(self.cri_optim, obj_critic, self.cri.parameters())
if self.if_use_cri_target:
self.soft_update(self.cri_target, self.cri, soft_update_tau)
return obj_critic.item(), q_value.mean().item()
def get_obj_critic_raw(self, buffer, batch_size):
with torch.no_grad():
reward, mask, action, state, next_s = buffer.sample_batch(batch_size)
next_q = self.cri_target(next_s).max(dim=1, keepdim=True)[0]
q_label = reward + mask * next_q
q_value = self.cri(state).gather(1, action.long())
obj_critic = self.criterion(q_value, q_label)
return obj_critic, q_value
def get_obj_critic_per(self, buffer, batch_size):
with torch.no_grad():
reward, mask, action, state, next_s, is_weights = buffer.sample_batch(batch_size)
next_q = self.cri_target(next_s).max(dim=1, keepdim=True)[0]
q_label = reward + mask * next_q
q_value = self.cri(state).gather(1, action.long())
td_error = self.criterion(q_value, q_label) # or td_error = (q_value - q_label).abs()
obj_critic = (td_error * is_weights).mean()
buffer.td_error_update(td_error.detach())
return obj_critic, q_value
class AgentDoubleDQN(AgentDQN): # [ElegantRL.2021.10.25]
def __init__(self):
AgentDQN.__init__(self)
self.soft_max = torch.nn.Softmax(dim=1)
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
self.ClassCri = QNetTwinDuel if self.if_use_dueling else QNetTwin
AgentDQN.init(self, net_dim, state_dim, action_dim, learning_rate, reward_scale, gamma,
if_per_or_gae, env_num, gpu_id)
if if_per_or_gae: # if_use_per
self.criterion = torch.nn.SmoothL1Loss(reduction='none')
self.get_obj_critic = self.get_obj_critic_per
else:
self.criterion = torch.nn.SmoothL1Loss(reduction='mean')
self.get_obj_critic = self.get_obj_critic_raw
def select_actions(self, states: torch.Tensor) -> torch.Tensor: # for discrete action space
actions = self.act(states.to(self.device))
if rd.rand() < self.explore_rate: # epsilon-greedy
a_prob = self.soft_max(actions)
a_ints = torch.multinomial(a_prob, num_samples=1, replacement=True)[:, 0]
# a_int = rd.choice(self.action_dim, prob=a_prob) # numpy version
else:
a_ints = actions.argmax(dim=1)
return a_ints.detach().cpu()
def get_obj_critic_raw(self, buffer, batch_size) -> (torch.Tensor, torch.Tensor):
with torch.no_grad():
reward, mask, action, state, next_s = buffer.sample_batch(batch_size)
next_q = torch.min(*self.cri_target.get_q1_q2(next_s)).max(dim=1, keepdim=True)[0]
q_label = reward + mask * next_q
q1, q2 = [qs.gather(1, action.long()) for qs in self.act.get_q1_q2(state)]
obj_critic = self.criterion(q1, q_label) + self.criterion(q2, q_label)
return obj_critic, q1
def get_obj_critic_per(self, buffer, batch_size):
with torch.no_grad():
reward, mask, action, state, next_s, is_weights = buffer.sample_batch(batch_size)
next_q = torch.min(*self.cri_target.get_q1_q2(next_s)).max(dim=1, keepdim=True)[0]
q_label = reward + mask * next_q
q1, q2 = [qs.gather(1, action.long()) for qs in self.act.get_q1_q2(state)]
td_error = self.criterion(q1, q_label) + self.criterion(q2, q_label)
obj_critic = (td_error * is_weights).mean()
buffer.td_error_update(td_error.detach())
return obj_critic, q1
'''Actor-Critic Methods (Policy Gradient)'''
class AgentDDPG(AgentBase):
def __init__(self):
AgentBase.__init__(self)
self.ClassAct = Actor
self.ClassCri = Critic
self.if_use_cri_target = True
self.if_use_act_target = True
self.explore_noise = 0.3 # explore noise of action (OrnsteinUhlenbeckNoise)
self.ou_noise = None
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
AgentBase.init(self, net_dim=net_dim, state_dim=state_dim, action_dim=action_dim,
reward_scale=reward_scale, gamma=gamma,
learning_rate=learning_rate, if_per_or_gae=if_per_or_gae,
env_num=env_num, gpu_id=gpu_id, )
self.ou_noise = OrnsteinUhlenbeckNoise(size=action_dim, sigma=self.explore_noise)
if if_per_or_gae:
self.criterion = torch.nn.SmoothL1Loss(reduction='none' if if_per_or_gae else 'mean')
self.get_obj_critic = self.get_obj_critic_per
else:
self.criterion = torch.nn.SmoothL1Loss(reduction='none' if if_per_or_gae else 'mean')
self.get_obj_critic = self.get_obj_critic_raw
def select_actions(self, state: torch.Tensor) -> torch.Tensor:
action = self.act(state.to(self.device))
if rd.rand() < self.explore_rate: # epsilon-greedy
ou_noise = torch.as_tensor(self.ou_noise(), dtype=torch.float32, device=self.device).unsqueeze(0)
action = (action + ou_noise).clamp(-1, 1)
return action.detach().cpu()
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau) -> (float, float):
buffer.update_now_len()
obj_critic = None
obj_actor = None
for _ in range(int(buffer.now_len / batch_size * repeat_times)):
obj_critic, state = self.get_obj_critic(buffer, batch_size)
self.optim_update(self.cri_optim, obj_critic, self.cri.parameters())
if self.if_use_cri_target:
self.soft_update(self.cri_target, self.cri, soft_update_tau)
action_pg = self.act(state) # policy gradient
obj_actor = -self.cri(state, action_pg).mean()
self.optim_update(self.act_optim, obj_actor, self.act.parameters())
if self.if_use_act_target:
self.soft_update(self.act_target, self.act, soft_update_tau)
return obj_critic.item(), obj_actor.item()
def get_obj_critic_raw(self, buffer, batch_size):
with torch.no_grad():
reward, mask, action, state, next_s = buffer.sample_batch(batch_size)
next_q = self.cri_target(next_s, self.act_target(next_s))
q_label = reward + mask * next_q
q_value = self.cri(state, action)
obj_critic = self.criterion(q_value, q_label)
return obj_critic, state
def get_obj_critic_per(self, buffer, batch_size):
with torch.no_grad():
reward, mask, action, state, next_s, is_weights = buffer.sample_batch(batch_size)
next_q = self.cri_target(next_s, self.act_target(next_s))
q_label = reward + mask * next_q
q_value = self.cri(state, action)
td_error = self.criterion(q_value, q_label) # or td_error = (q_value - q_label).abs()
obj_critic = (td_error * is_weights).mean()
buffer.td_error_update(td_error.detach())
return obj_critic, state
class AgentTD3(AgentBase):
def __init__(self):
AgentBase.__init__(self)
self.ClassAct = Actor
self.ClassCri = CriticTwin
self.if_use_cri_target = True
self.if_use_act_target = True
self.explore_noise = 0.1 # standard deviation of exploration noise
self.policy_noise = 0.2 # standard deviation of policy noise
self.update_freq = 2 # delay update frequency
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
AgentBase.init(self, net_dim=net_dim, state_dim=state_dim, action_dim=action_dim,
reward_scale=reward_scale, gamma=gamma,
learning_rate=learning_rate, if_per_or_gae=if_per_or_gae,
env_num=env_num, gpu_id=gpu_id, )
if if_per_or_gae: # if_use_per
self.criterion = torch.nn.SmoothL1Loss(reduction='none')
self.get_obj_critic = self.get_obj_critic_per
else:
self.criterion = torch.nn.SmoothL1Loss(reduction='mean')
self.get_obj_critic = self.get_obj_critic_raw
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau) -> tuple:
buffer.update_now_len()
obj_critic = None
obj_actor = None
for update_c in range(int(buffer.now_len / batch_size * repeat_times)):
obj_critic, state = self.get_obj_critic(buffer, batch_size)
self.optim_update(self.cri_optim, obj_critic, self.cri.parameters())
if update_c % self.update_freq == 0: # delay update
action_pg = self.act(state) # policy gradient
obj_actor = -self.cri_target(state, action_pg).mean() # use cri_target is more stable than cri
self.optim_update(self.act_optim, obj_actor, self.act.parameters())
if self.if_use_cri_target:
self.soft_update(self.cri_target, self.cri, soft_update_tau)
if self.if_use_act_target:
self.soft_update(self.act_target, self.act, soft_update_tau)
return obj_critic.item() / 2, obj_actor.item()
def get_obj_critic_raw(self, buffer, batch_size):
with torch.no_grad():
reward, mask, action, state, next_s = buffer.sample_batch(batch_size)
next_a = self.act_target.get_action(next_s, self.policy_noise) # policy noise
next_q = torch.min(*self.cri_target.get_q1_q2(next_s, next_a)) # twin critics
q_label = reward + mask * next_q
q1, q2 = self.cri.get_q1_q2(state, action)
obj_critic = self.criterion(q1, q_label) + self.criterion(q2, q_label) # twin critics
return obj_critic, state
def get_obj_critic_per(self, buffer, batch_size):
"""Prioritized Experience Replay
Contributor: Github GyChou
"""
with torch.no_grad():
reward, mask, action, state, next_s, is_weights = buffer.sample_batch(batch_size)
next_a = self.act_target.get_action(next_s, self.policy_noise) # policy noise
next_q = torch.min(*self.cri_target.get_q1_q2(next_s, next_a)) # twin critics
q_label = reward + mask * next_q
q1, q2 = self.cri.get_q1_q2(state, action)
td_error = self.criterion(q1, q_label) + self.criterion(q2, q_label)
obj_critic = (td_error * is_weights).mean()
buffer.td_error_update(td_error.detach())
return obj_critic, state
class AgentSAC(AgentBase): # [ElegantRL.2021.10.25]
def __init__(self):
AgentBase.__init__(self)
self.ClassCri = CriticTwin
self.ClassAct = ActorSAC
self.if_use_cri_target = True
self.if_use_act_target = False
self.alpha_log = None
self.alpha_optim = None
self.target_entropy = None
self.obj_critic = (-np.log(0.5)) ** 0.5 # for reliable_lambda
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
AgentBase.init(self, net_dim=net_dim, state_dim=state_dim, action_dim=action_dim,
reward_scale=reward_scale, gamma=gamma,
learning_rate=learning_rate, if_per_or_gae=if_per_or_gae,
env_num=env_num, gpu_id=gpu_id, )
self.alpha_log = torch.tensor((-np.log(action_dim) * np.e,), dtype=torch.float32,
requires_grad=True, device=self.device) # trainable parameter
self.alpha_optim = torch.optim.Adam((self.alpha_log,), lr=learning_rate)
self.target_entropy = np.log(action_dim)
if if_per_or_gae: # if_use_per
self.criterion = torch.nn.SmoothL1Loss(reduction='none')
self.get_obj_critic = self.get_obj_critic_per
else:
self.criterion = torch.nn.SmoothL1Loss(reduction='mean')
self.get_obj_critic = self.get_obj_critic_raw
def select_actions(self, state: torch.Tensor) -> torch.Tensor:
state = state.to(self.device)
if rd.rand() < self.explore_rate: # epsilon-greedy
actions = self.act.get_action(state)
else:
actions = self.act(state)
return actions.detach().cpu()
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau):
buffer.update_now_len()
obj_actor = None
alpha = None
for _ in range(int(buffer.now_len * repeat_times / batch_size)):
alpha = self.alpha_log.exp()
'''objective of critic (loss function of critic)'''
obj_critic, state = self.get_obj_critic(buffer, batch_size, alpha)
self.obj_critic = 0.995 * self.obj_critic + 0.0025 * obj_critic.item() # for reliable_lambda
self.optim_update(self.cri_optim, obj_critic, self.cri.parameters())
if self.if_use_cri_target:
self.soft_update(self.cri_target, self.cri, soft_update_tau)
'''objective of alpha (temperature parameter automatic adjustment)'''
action_pg, logprob = self.act.get_action_logprob(state) # policy gradient
obj_alpha = (self.alpha_log * (logprob - self.target_entropy).detach()).mean()
self.optim_update(self.alpha_optim, obj_alpha, self.alpha_log)
'''objective of actor'''
with torch.no_grad():
self.alpha_log[:] = self.alpha_log.clamp(-20, 2).detach()
obj_actor = -(torch.min(*self.cri.get_q1_q2(state, action_pg)) + logprob * alpha).mean()
# use self.cri_target.get_q1_q2 in above code for more stable training.
self.optim_update(self.act_optim, obj_actor, self.act.parameters())
if self.if_use_act_target:
self.soft_update(self.act_target, self.act, soft_update_tau)
return self.obj_critic, obj_actor.item(), alpha.item()
def get_obj_critic_raw(self, buffer, batch_size, alpha):
with torch.no_grad():
reward, mask, action, state, next_s = buffer.sample_batch(batch_size)
next_a, next_log_prob = self.act_target.get_action_logprob(next_s) # stochastic policy
next_q = torch.min(*self.cri_target.get_q1_q2(next_s, next_a)) # twin critics
q_label = reward + mask * (next_q + next_log_prob * alpha)
q1, q2 = self.cri.get_q1_q2(state, action)
obj_critic = self.criterion(q1, q_label) + self.criterion(q2, q_label)
return obj_critic, state
def get_obj_critic_per(self, buffer, batch_size, alpha):
with torch.no_grad():
reward, mask, action, state, next_s, is_weights = buffer.sample_batch(batch_size)
next_a, next_log_prob = self.act_target.get_action_logprob(next_s) # stochastic policy
next_q = torch.min(*self.cri_target.get_q1_q2(next_s, next_a)) # twin critics
q_label = reward + mask * (next_q + next_log_prob * alpha)
q1, q2 = self.cri.get_q1_q2(state, action)
td_error = self.criterion(q1, q_label) + self.criterion(q2, q_label)
obj_critic = (td_error * is_weights).mean()
buffer.td_error_update(td_error.detach())
return obj_critic, state
class AgentModSAC(AgentSAC): # Modified SAC using reliable_lambda and TTUR (Two Time-scale Update Rule)
def __init__(self):
AgentSAC.__init__(self)
self.if_use_act_target = True
self.if_use_cri_target = True
self.obj_critic = (-np.log(0.5)) ** 0.5 # for reliable_lambda
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau):
buffer.update_now_len()
obj_actor = None
update_a = 0
alpha = None
for update_c in range(1, int(buffer.now_len * repeat_times / batch_size)):
alpha = self.alpha_log.exp()
'''objective of critic (loss function of critic)'''
obj_critic, state = self.get_obj_critic(buffer, batch_size, alpha)
self.obj_critic = 0.995 * self.obj_critic + 0.0025 * obj_critic.item() # for reliable_lambda
self.optim_update(self.cri_optim, obj_critic, self.cri.parameters())
if self.if_use_cri_target:
self.soft_update(self.cri_target, self.cri, soft_update_tau)
a_noise_pg, logprob = self.act.get_action_logprob(state) # policy gradient
'''objective of alpha (temperature parameter automatic adjustment)'''
obj_alpha = (self.alpha_log * (logprob - self.target_entropy).detach()).mean()
self.optim_update(self.alpha_optim, obj_alpha, self.alpha_log)
with torch.no_grad():
self.alpha_log[:] = self.alpha_log.clamp(-16, 2).detach()
'''objective of actor using reliable_lambda and TTUR (Two Time-scales Update Rule)'''
reliable_lambda = np.exp(-self.obj_critic ** 2) # for reliable_lambda
if_update_a = update_a / update_c < 1 / (2 - reliable_lambda)
if if_update_a: # auto TTUR
update_a += 1
q_value_pg = torch.min(*self.cri.get_q1_q2(state, a_noise_pg))
obj_actor = -(q_value_pg + logprob * alpha).mean()
self.optim_update(self.act_optim, obj_actor, self.act.parameters())
if self.if_use_act_target:
self.soft_update(self.act_target, self.act, soft_update_tau)
return self.obj_critic, obj_actor.item(), alpha.item()
class AgentPPO(AgentBase):
def __init__(self):
AgentBase.__init__(self)
self.ClassAct = ActorPPO
self.ClassCri = CriticPPO
self.if_off_policy = False
self.ratio_clip = 0.2 # could be 0.00 ~ 0.50 ratio.clamp(1 - clip, 1 + clip)
self.lambda_entropy = 0.02 # could be 0.00~0.10
self.lambda_a_value = 1.00 # could be 0.25~8.00, the lambda of advantage value
self.lambda_gae_adv = 0.98 # could be 0.95~0.99, GAE (Generalized Advantage Estimation. ICLR.2016.)
self.get_reward_sum = None # self.get_reward_sum_gae if if_use_gae else self.get_reward_sum_raw
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
AgentBase.init(self, net_dim=net_dim, state_dim=state_dim, action_dim=action_dim,
reward_scale=reward_scale, gamma=gamma,
learning_rate=learning_rate, if_per_or_gae=if_per_or_gae,
env_num=env_num, gpu_id=gpu_id, )
self.traj_list = [list() for _ in range(env_num)]
self.env_num = env_num
if if_per_or_gae: # if_use_gae
self.get_reward_sum = self.get_reward_sum_gae
else:
self.get_reward_sum = self.get_reward_sum_raw
if env_num == 1:
self.explore_env = self.explore_one_env
else:
self.explore_env = self.explore_vec_env
def select_action(self, state: np.ndarray) -> np.ndarray:
s_tensor = torch.as_tensor(state[np.newaxis], device=self.device)
a_tensor = self.act(s_tensor)
action = a_tensor.detach().cpu().numpy()
return action
def select_actions(self, state: torch.Tensor) -> tuple:
"""
`tensor state` state.shape = (batch_size, state_dim)
return `tensor action` action.shape = (batch_size, action_dim)
return `tensor noise` noise.shape = (batch_size, action_dim)
"""
state = state.to(self.device)
action, noise = self.act.get_action(state)
return action.detach().cpu(), noise.detach().cpu()
def explore_one_env(self, env, target_step):
state = self.states[0]
last_done = 0
traj = list()
for step_i in range(target_step):
ten_states = torch.as_tensor(state, dtype=torch.float32).unsqueeze(0)
ten_actions, ten_noises = self.select_actions(ten_states)
action = ten_actions[0].numpy()
next_s, reward, done, _ = env.step(np.tanh(action))
traj.append((ten_states, reward, done, ten_actions, ten_noises))
if done:
state = env.reset()
last_done = step_i
else:
state = next_s
self.states[0] = state
traj_list = self.splice_trajectory([traj, ], [last_done, ])
return self.convert_trajectory(traj_list) # [traj_env_0, ]
def explore_vec_env(self, env, target_step):
ten_states = self.states
env_num = len(self.traj_list)
traj_list = [list() for _ in range(env_num)] # [traj_env_0, ..., traj_env_i]
last_done_list = [0 for _ in range(env_num)]
for step_i in range(target_step):
ten_actions, ten_noises = self.select_actions(ten_states)
tem_next_states, ten_rewards, ten_dones = env.step(ten_actions.tanh())
for env_i in range(env_num):
traj_list[env_i].append((ten_states[env_i], ten_rewards[env_i], ten_dones[env_i],
ten_actions[env_i], ten_noises[env_i]))
if ten_dones[env_i]:
last_done_list[env_i] = step_i
ten_states = tem_next_states
self.states = ten_states
traj_list = self.splice_trajectory(traj_list, last_done_list)
return self.convert_trajectory(traj_list) # [traj_env_0, ...]
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau):
with torch.no_grad():
buf_len = buffer[0].shape[0]
buf_state, buf_reward, buf_mask, buf_action, buf_noise = [ten.to(self.device) for ten in buffer]
'''get buf_r_sum, buf_logprob'''
bs = 2 ** 10 # set a smaller 'BatchSize' when out of GPU memory.
buf_value = [self.cri_target(buf_state[i:i + bs]) for i in range(0, buf_len, bs)]
buf_value = torch.cat(buf_value, dim=0)
buf_logprob = self.act.get_old_logprob(buf_action, buf_noise)
buf_r_sum, buf_adv_v = self.get_reward_sum(buf_len, buf_reward, buf_mask, buf_value) # detach()
buf_adv_v = (buf_adv_v - buf_adv_v.mean()) * (self.lambda_a_value / (buf_adv_v.std() + 1e-5))
# buf_adv_v: buffer data of adv_v value
del buf_noise, buffer[:]
obj_critic = None
obj_actor = None
update_times = int(buf_len / batch_size * repeat_times)
for update_i in range(1, update_times + 1):
indices = torch.randint(buf_len, size=(batch_size,), requires_grad=False, device=self.device)
state = buf_state[indices]
r_sum = buf_r_sum[indices]
adv_v = buf_adv_v[indices]
action = buf_action[indices]
logprob = buf_logprob[indices]
'''PPO: Surrogate objective of Trust Region'''
new_logprob, obj_entropy = self.act.get_logprob_entropy(state, action) # it is obj_actor
ratio = (new_logprob - logprob.detach()).exp()
surrogate1 = adv_v * ratio
surrogate2 = adv_v * ratio.clamp(1 - self.ratio_clip, 1 + self.ratio_clip)
obj_surrogate = -torch.min(surrogate1, surrogate2).mean()
obj_actor = obj_surrogate + obj_entropy * self.lambda_entropy
self.optim_update(self.act_optim, obj_actor, self.act.parameters())
value = self.cri(state).squeeze(1) # critic network predicts the reward_sum (Q value) of state
obj_critic = self.criterion(value, r_sum) / (r_sum.std() + 1e-6)
self.optim_update(self.cri_optim, obj_critic, self.cri.parameters())
if self.if_use_cri_target:
self.soft_update(self.cri_target, self.cri, soft_update_tau)
a_std_log = getattr(self.act, 'a_std_log', torch.zeros(1)).mean()
return obj_critic.item(), obj_actor.item(), a_std_log.item() # logging_tuple
def get_reward_sum_raw(self, buf_len, buf_reward, buf_mask, buf_value) -> (torch.Tensor, torch.Tensor):
buf_r_sum = torch.empty(buf_len, dtype=torch.float32, device=self.device) # reward sum
pre_r_sum = 0
for i in range(buf_len - 1, -1, -1):
buf_r_sum[i] = buf_reward[i] + buf_mask[i] * pre_r_sum
pre_r_sum = buf_r_sum[i]
buf_adv_v = buf_r_sum - buf_value[:, 0]
return buf_r_sum, buf_adv_v
def get_reward_sum_gae(self, buf_len, ten_reward, ten_mask, ten_value) -> (torch.Tensor, torch.Tensor):
buf_r_sum = torch.empty(buf_len, dtype=torch.float32, device=self.device) # old policy value
buf_adv_v = torch.empty(buf_len, dtype=torch.float32, device=self.device) # advantage value
pre_r_sum = 0
pre_adv_v = 0 # advantage value of previous step
ten_bool = torch.not_equal(ten_mask, 0).float()
for i in range(buf_len - 1, -1, -1):
buf_r_sum[i] = ten_reward[i] + ten_mask[i] * pre_r_sum
pre_r_sum = buf_r_sum[i]
buf_adv_v[i] = ten_reward[i] + ten_bool[i] * (pre_adv_v - ten_value[i]) # todo need to check
pre_adv_v = ten_value[i] + buf_adv_v[i] * self.lambda_gae_adv
return buf_r_sum, buf_adv_v
def splice_trajectory(self, traj_list, last_done_list):
for env_i in range(self.env_num):
last_done = last_done_list[env_i]
traj_temp = traj_list[env_i]
traj_list[env_i] = self.traj_list[env_i] + traj_temp[:last_done + 1]
self.traj_list[env_i] = traj_temp[last_done:]
return traj_list
def convert_trajectory(self, traj_list):
for traj in traj_list:
temp = list(map(list, zip(*traj))) # 2D-list transpose
ten_state = torch.stack(temp[0])
ten_reward = torch.as_tensor(temp[1], dtype=torch.float32) * self.reward_scale
ten_mask = (1.0 - torch.as_tensor(temp[2], dtype=torch.float32)) * self.gamma
ten_action = torch.stack(temp[3])
ten_noise = torch.stack(temp[4])
traj[:] = (ten_state, ten_reward, ten_mask, ten_action, ten_noise)
return traj_list
class AgentDiscretePPO(AgentPPO):
def __init__(self):
AgentPPO.__init__(self)
self.ClassAct = ActorDiscretePPO
def explore_one_env(self, env, target_step):
state = self.states[0]
last_done = 0
traj = list()
for step_i in range(target_step):
ten_states = torch.as_tensor(state, dtype=torch.float32).unsqueeze(0)
ten_a_ints, ten_probs = self.select_actions(ten_states)
a_int = ten_a_ints[0].numpy()
next_s, reward, done, _ = env.step(a_int) # only different
traj.append((ten_states, reward, done, ten_a_ints, ten_probs))
if done:
state = env.reset()
last_done = step_i
else:
state = next_s
self.states[0] = state
traj_list = self.splice_trajectory([traj, ], [last_done, ])
return self.convert_trajectory(traj_list)
def explore_vec_env(self, env, target_step):
ten_states = self.states
env_num = len(self.traj_list)
traj_list = [list() for _ in range(env_num)] # [traj_env_0, ..., traj_env_i]
last_done_list = [0 for _ in range(env_num)]
for step_i in range(target_step):
ten_a_ints, ten_probs = self.select_actions(ten_states)
tem_next_states, ten_rewards, ten_dones = env.step(ten_a_ints.numpy())
for env_i in range(env_num):
traj_list[env_i].append((ten_states[env_i], ten_rewards[env_i], ten_dones[env_i],
ten_a_ints[env_i], ten_probs[env_i]))
if ten_dones[env_i]:
last_done_list[env_i] = step_i
ten_states = tem_next_states
self.states = ten_states
traj_list = self.splice_trajectory(traj_list, last_done_list)
return self.convert_trajectory(traj_list) # [traj_env_0, ...]
class AgentA2C(AgentPPO): # A2C.2015, PPO.2016
def __init__(self):
AgentPPO.__init__(self)
print('| AgentA2C: A2C or A3C is worse than PPO. We provide AgentA2C code just for teaching.'
'| Without TrustRegion, A2C needs special hyper-parameters, such as smaller repeat_times.')
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau):
with torch.no_grad():
buf_len = buffer[0].shape[0]
buf_state, buf_reward, buf_mask, buf_action, buf_noise = [ten.to(self.device) for ten in buffer]
'''get buf_r_sum, buf_logprob'''
bs = 2 ** 10 # set a smaller 'BatchSize' when out of GPU memory.
buf_value = [self.cri_target(buf_state[i:i + bs]) for i in range(0, buf_len, bs)]
buf_value = torch.cat(buf_value, dim=0)
# buf_logprob = self.act.get_old_logprob(buf_action, buf_noise)
buf_r_sum, buf_adv_v = self.get_reward_sum(buf_len, buf_reward, buf_mask, buf_value) # detach()
buf_adv_v = (buf_adv_v - buf_adv_v.mean()) * (self.lambda_a_value / (buf_adv_v.std() + 1e-5))
# buf_adv_v: advantage_value in ReplayBuffer
del buf_noise, buffer[:]
obj_critic = None
obj_actor = None
update_times = int(buf_len / batch_size * repeat_times)
for update_i in range(1, update_times + 1):
indices = torch.randint(buf_len, size=(batch_size,), requires_grad=False, device=self.device)
state = buf_state[indices]
r_sum = buf_r_sum[indices]
adv_v = buf_adv_v[indices]
action = buf_action[indices]
# logprob = buf_logprob[indices]
'''A2C: Advantage function'''
new_logprob, obj_entropy = self.act.get_logprob_entropy(state, action) # it is obj_actor
obj_actor = -(adv_v * new_logprob.exp()).mean() + obj_entropy * self.lambda_entropy
self.optim_update(self.act_optim, obj_actor, self.act.parameters())
value = self.cri(state).squeeze(1) # critic network predicts the reward_sum (Q value) of state
obj_critic = self.criterion(value, r_sum) / (r_sum.std() + 1e-6)
self.optim_update(self.cri_optim, obj_critic, self.cri.parameters())
if self.if_use_cri_target:
self.soft_update(self.cri_target, self.cri, soft_update_tau)
a_std_log = getattr(self.act, 'a_std_log', torch.zeros(1)).mean()
return obj_critic.item(), obj_actor.item(), a_std_log.item() # logging_tuple
class AgentDiscreteA2C(AgentA2C):
def __init__(self):
AgentA2C.__init__(self)
self.ClassAct = ActorDiscretePPO
def explore_one_env(self, env, target_step):
state = self.states[0]
last_done = 0
traj = list()
for step_i in range(target_step):
ten_states = torch.as_tensor(state, dtype=torch.float32).unsqueeze(0)
ten_a_ints, ten_probs = self.select_actions(ten_states)
a_int = ten_a_ints[0].numpy()
next_s, reward, done, _ = env.step(a_int) # only different
traj.append((ten_states, reward, done, ten_a_ints, ten_probs))
if done:
state = env.reset()
last_done = step_i
else:
state = next_s
self.states[0] = state
traj_list = self.splice_trajectory([traj, ], [last_done, ])
return self.convert_trajectory(traj_list)
def explore_vec_env(self, env, target_step):
ten_states = self.states
env_num = len(self.traj_list)
traj_list = [list() for _ in range(env_num)] # [traj_env_0, ..., traj_env_i]
last_done_list = [0 for _ in range(env_num)]
for step_i in range(target_step):
ten_a_ints, ten_probs = self.select_actions(ten_states)
tem_next_states, ten_rewards, ten_dones = env.step(ten_a_ints.numpy())
for env_i in range(env_num):
traj_list[env_i].append((ten_states[env_i], ten_rewards[env_i], ten_dones[env_i],
ten_a_ints[env_i], ten_probs[env_i]))
if ten_dones[env_i]:
last_done_list[env_i] = step_i
ten_states = tem_next_states
self.states = ten_states
traj_list = self.splice_trajectory(traj_list, last_done_list)
return self.convert_trajectory(traj_list) # [traj_env_0, ...]
class AgentStep1AC(AgentBase):
def __init__(self):
AgentBase.__init__(self)
self.ClassAct = ActorBiConv
self.ClassCri = CriticBiConv
self.if_use_cri_target = False
self.if_use_act_target = False
self.explore_noise = 2 ** -8
self.obj_critic = (-np.log(0.5)) ** 0.5 # for reliable_lambda
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
AgentBase.init(self, net_dim=net_dim, state_dim=state_dim, action_dim=action_dim,
reward_scale=reward_scale, gamma=gamma,
learning_rate=learning_rate, if_per_or_gae=if_per_or_gae,
env_num=env_num, gpu_id=gpu_id, )
if if_per_or_gae: # if_use_per
self.criterion = torch.nn.MSELoss(reduction='none')
self.get_obj_critic = self.get_obj_critic_per
else:
self.criterion = torch.nn.MSELoss(reduction='mean')
self.get_obj_critic = self.get_obj_critic_raw
self.get_obj_critic = self.get_obj_critic_raw
def select_actions(self, state: torch.Tensor) -> torch.Tensor:
action = self.act.get_action(state.to(self.device), self.explore_noise)
return action.detach().cpu()
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau) -> (float, float):
buffer.update_now_len()
obj_actor = None
update_a = 0
for update_c in range(1, int(buffer.now_len / batch_size * repeat_times)):
'''objective of critic (loss function of critic)'''
obj_critic, state = self.get_obj_critic(buffer, batch_size)
self.obj_critic = 0.99 * self.obj_critic + 0.01 * obj_critic.item() # for reliable_lambda
self.optim_update(self.cri_optim, obj_critic, self.cri.parameters())
if self.if_use_cri_target:
self.soft_update(self.cri_target, self.cri, soft_update_tau)
'''objective of actor using reliable_lambda and TTUR (Two Time-scales Update Rule)'''
reliable_lambda = np.exp(-self.obj_critic ** 2) # for reliable_lambda
if_update_a = update_a / update_c < 1 / (2 - reliable_lambda)
if if_update_a: # auto TTUR
update_a += 1
obj_actor = -self.cri(state, self.act(state)).mean() # policy gradient
self.optim_update(self.act_optim, obj_actor, self.act.parameters())
if self.if_use_act_target:
self.soft_update(self.act_target, self.act, soft_update_tau)
return self.obj_critic, obj_actor.item()
def get_obj_critic_raw(self, buffer, batch_size):
with torch.no_grad():
# reward, mask, action, state, next_s = buffer.sample_batch(batch_size)
q_label, action, state = buffer.sample_batch_one_step(batch_size)
q_value = self.cri(state, action)
obj_critic = self.criterion(q_value, q_label)
return obj_critic, state
def get_obj_critic_per(self, buffer, batch_size):
with torch.no_grad():
# reward, mask, action, state, next_s, is_weights = buffer.sample_batch(batch_size)
q_label, action, state, is_weights = buffer.sample_batch_one_step(batch_size)
q_value = self.cri(state, action)
td_error = self.criterion(q_value, q_label) # or td_error = (q_value - q_label).abs()
obj_critic = (td_error * is_weights).mean()
buffer.td_error_update(td_error.detach())
return obj_critic, q_value
'''Actor-Critic Methods (Parameter Sharing)'''
class AgentShareAC(AgentBase): # IAC (InterAC) waiting for check
def __init__(self):
AgentBase.__init__(self)
self.ClassCri = ShareDPG # self.Act = None
self.explore_noise = 0.2 # standard deviation of explore noise
self.policy_noise = 0.4 # standard deviation of policy noise
self.update_freq = 2 ** 7 # delay update frequency, for hard target update
self.avg_loss_c = (-np.log(0.5)) ** 0.5 # old version reliable_lambda
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau) -> tuple:
buffer.update_now_len()
obj_critic = None
obj_actor = None
reliable_lambda = None
k = 1.0 + buffer.now_len / buffer.max_len
batch_size_ = int(batch_size * k)
for i in range(int(buffer.now_len / batch_size * repeat_times)):
with torch.no_grad():
reward, mask, action, state, next_state = buffer.sample_batch(batch_size_)
next_q_label, next_action = self.cri_target.next_q_action(state, next_state, self.policy_noise)
q_label = reward + mask * next_q_label
"""obj_critic"""
q_eval = self.cri.critic(state, action)
obj_critic = self.criterion(q_eval, q_label)
'''auto reliable lambda'''
self.avg_loss_c = 0.995 * self.avg_loss_c + 0.005 * obj_critic.item() / 2 # soft update, twin critics
reliable_lambda = np.exp(-self.avg_loss_c ** 2)
'''actor correction term'''
actor_term = self.criterion(self.cri(next_state), next_action)
if i % repeat_times == 0:
'''actor obj'''
action_pg = self.cri(state) # policy gradient
obj_actor = -self.cri_target.critic(state, action_pg).mean() # policy gradient
# NOTICE! It is very important to use act_target.critic here instead act.critic
# Or you can use act.critic.deepcopy(). Whatever you cannot use act.critic directly.
obj_united = obj_critic + actor_term * (1 - reliable_lambda) + obj_actor * (reliable_lambda * 0.5)
else:
obj_united = obj_critic + actor_term * (1 - reliable_lambda)
"""united loss"""
self.optim_update(self.cri_optim, obj_united, self.cri.parameters())
if i % self.update_freq == self.update_freq and reliable_lambda > 0.1:
self.cri_target.load_state_dict(self.cri.state_dict()) # Hard Target Update
return obj_critic.item(), obj_actor.item(), reliable_lambda
class AgentShareSAC(AgentSAC): # Integrated Soft Actor-Critic
def __init__(self):
AgentSAC.__init__(self)
self.obj_critic = (-np.log(0.5)) ** 0.5 # for reliable_lambda
self.cri_optim = None
self.target_entropy = None
self.alpha_log = None
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
self.device = torch.device(f"cuda:{gpu_id}" if torch.cuda.is_available() else "cpu")
self.alpha_log = torch.tensor((-np.log(action_dim) * np.e,), dtype=torch.float32,
requires_grad=True, device=self.device) # trainable parameter
self.target_entropy = np.log(action_dim)
self.act = self.cri = ShareSPG(net_dim, state_dim, action_dim).to(self.device)
self.act_target = self.cri_target = deepcopy(self.act)
self.cri_optim = torch.optim.Adam(
[{'params': self.act.enc_s.parameters(), 'lr': learning_rate * 1.5},
{'params': self.act.enc_a.parameters(), },
{'params': self.act.net.parameters(), 'lr': learning_rate * 1.5},
{'params': self.act.dec_a.parameters(), },
{'params': self.act.dec_d.parameters(), },
{'params': self.act.dec_q1.parameters(), },
{'params': self.act.dec_q2.parameters(), },
{'params': (self.alpha_log,)}], lr=learning_rate)
if if_per_or_gae: # if_use_per
self.criterion = torch.nn.SmoothL1Loss(reduction='none')
self.get_obj_critic = self.get_obj_critic_per
else:
self.criterion = torch.nn.SmoothL1Loss(reduction='mean')
self.get_obj_critic = self.get_obj_critic_raw
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau) -> tuple: # 1111
buffer.update_now_len()
obj_actor = None
update_a = 0
alpha = None
for update_c in range(1, int(buffer.now_len / batch_size * repeat_times)):
alpha = self.alpha_log.exp()
'''objective of critic'''
obj_critic, state = self.get_obj_critic(buffer, batch_size, alpha)
self.obj_critic = 0.995 * self.obj_critic + 0.0025 * obj_critic.item() # for reliable_lambda
reliable_lambda = np.exp(-self.obj_critic ** 2) # for reliable_lambda
'''objective of alpha (temperature parameter automatic adjustment)'''
a_noise_pg, logprob = self.act.get_action_logprob(state) # policy gradient
obj_alpha = (self.alpha_log * (logprob - self.target_entropy).detach() * reliable_lambda).mean()
with torch.no_grad():
self.alpha_log[:] = self.alpha_log.clamp(-16, 2).detach()
'''objective of actor using reliable_lambda and TTUR (Two Time-scales Update Rule)'''
if_update_a = update_a / update_c < 1 / (2 - reliable_lambda)
if if_update_a: # auto TTUR
update_a += 1
q_value_pg = torch.min(*self.act_target.get_q1_q2(state, a_noise_pg)).mean() # twin critics
obj_actor = -(q_value_pg + logprob * alpha.detach()).mean() # policy gradient
obj_united = obj_critic + obj_alpha + obj_actor * reliable_lambda
else:
obj_united = obj_critic + obj_alpha
self.optim_update(self.cri_optim, obj_united, self.cri.parameters())
if self.if_use_act_target:
self.soft_update(self.act_target, self.act, soft_update_tau)
return self.obj_critic, obj_actor.item(), alpha.item()
class AgentSharePPO(AgentPPO):
def __init__(self):
AgentPPO.__init__(self)
self.obj_c = (-np.log(0.5)) ** 0.5 # for reliable_lambda
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
self.device = torch.device(f"cuda:{gpu_id}" if torch.cuda.is_available() else "cpu")
if if_per_or_gae:
self.get_reward_sum = self.get_reward_sum_gae
else:
self.get_reward_sum = self.get_reward_sum_raw
self.act = self.cri = SharePPO(state_dim, action_dim, net_dim).to(self.device)
self.cri_optim = torch.optim.Adam([
{'params': self.act.enc_s.parameters(), 'lr': learning_rate * 0.9},
{'params': self.act.dec_a.parameters(), },
{'params': self.act.a_std_log, },
{'params': self.act.dec_q1.parameters(), },
{'params': self.act.dec_q2.parameters(), },
], lr=learning_rate)
self.criterion = torch.nn.SmoothL1Loss()
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau):
with torch.no_grad():
buf_len = buffer[0].shape[0]
buf_state, buf_action, buf_noise, buf_reward, buf_mask = [ten.to(self.device) for ten in buffer]
# (ten_state, ten_action, ten_noise, ten_reward, ten_mask) = buffer
'''get buf_r_sum, buf_logprob'''
bs = 2 ** 10 # set a smaller 'BatchSize' when out of GPU memory.
buf_value = [self.cri_target(buf_state[i:i + bs]) for i in range(0, buf_len, bs)]
buf_value = torch.cat(buf_value, dim=0)
buf_logprob = self.act.get_old_logprob(buf_action, buf_noise)
buf_r_sum, buf_adv_v = self.get_reward_sum(buf_len, buf_reward, buf_mask, buf_value) # detach()
buf_adv_v = (buf_adv_v - buf_adv_v.mean()) * (self.lambda_a_value / torch.std(buf_adv_v) + 1e-5)
# buf_adv_v: buffer data of adv_v value
del buf_noise, buffer[:]
obj_critic = obj_actor = None
for _ in range(int(buf_len / batch_size * repeat_times)):
indices = torch.randint(buf_len, size=(batch_size,), requires_grad=False, device=self.device)
state = buf_state[indices]
r_sum = buf_r_sum[indices]
adv_v = buf_adv_v[indices] # advantage value
action = buf_action[indices]
logprob = buf_logprob[indices]
'''PPO: Surrogate objective of Trust Region'''
new_logprob, obj_entropy = self.act.get_logprob_entropy(state, action) # it is obj_actor
ratio = (new_logprob - logprob.detach()).exp()
surrogate1 = adv_v * ratio
surrogate2 = adv_v * ratio.clamp(1 - self.ratio_clip, 1 + self.ratio_clip)
obj_surrogate = -torch.min(surrogate1, surrogate2).mean()
obj_actor = obj_surrogate + obj_entropy * self.lambda_entropy
value = self.cri(state).squeeze(1) # critic network predicts the reward_sum (Q value) of state
obj_critic = self.criterion(value, r_sum) / (r_sum.std() + 1e-6)
obj_united = obj_critic + obj_actor
self.optim_update(self.cri_optim, obj_united, self.cri.parameters())
if self.if_use_cri_target:
self.soft_update(self.cri_target, self.cri, soft_update_tau)
a_std_log = getattr(self.act, 'a_std_log', torch.zeros(1)).mean()
return obj_critic.item(), obj_actor.item(), a_std_log.item() # logging_tuple
class AgentShareA2C(AgentSharePPO):
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau):
with torch.no_grad():
buf_len = buffer[0].shape[0]
buf_state, buf_action, buf_noise, buf_reward, buf_mask = [ten.to(self.device) for ten in buffer]
# (ten_state, ten_action, ten_noise, ten_reward, ten_mask) = buffer
'''get buf_r_sum, buf_logprob'''
bs = 2 ** 10 # set a smaller 'BatchSize' when out of GPU memory.
buf_value = [self.cri_target(buf_state[i:i + bs]) for i in range(0, buf_len, bs)]
buf_value = torch.cat(buf_value, dim=0)
# buf_logprob = self.act.get_old_logprob(buf_action, buf_noise)
buf_r_sum, buf_adv_v = self.get_reward_sum(buf_len, buf_reward, buf_mask, buf_value) # detach()
buf_adv_v = (buf_adv_v - buf_adv_v.mean()) * (self.lambda_a_value / torch.std(buf_adv_v) + 1e-5)
# buf_adv_v: buffer data of adv_v value
del buf_noise, buffer[:]
obj_critic = obj_actor = None
for _ in range(int(buf_len / batch_size * repeat_times)):
indices = torch.randint(buf_len, size=(batch_size,), requires_grad=False, device=self.device)
state = buf_state[indices]
r_sum = buf_r_sum[indices]
adv_v = buf_adv_v[indices] # advantage value
action = buf_action[indices]
# logprob = buf_logprob[indices]
'''A2C: Advantage function'''
new_logprob, obj_entropy = self.act.get_logprob_entropy(state, action) # it is obj_actor
obj_actor = -(adv_v * new_logprob.exp()).mean() + obj_entropy * self.lambda_entropy
self.optim_update(self.act_optim, obj_actor, self.act.parameters())
value = self.cri(state).squeeze(1) # critic network predicts the reward_sum (Q value) of state
obj_critic = self.criterion(value, r_sum) / (r_sum.std() + 1e-6)
obj_united = obj_critic + obj_actor
self.optim_update(self.cri_optim, obj_united, self.cri.parameters())
if self.if_use_cri_target:
self.soft_update(self.cri_target, self.cri, soft_update_tau)
a_std_log = getattr(self.act, 'a_std_log', torch.zeros(1)).mean()
return obj_critic.item(), obj_actor.item(), a_std_log.item() # logging_tuple
class AgentShareStep1AC(AgentBase):
def __init__(self):
AgentBase.__init__(self)
self.ClassAct = ShareBiConv
self.ClassCri = self.ClassAct
self.if_use_cri_target = True
self.if_use_act_target = True
self.obj_critic = (-np.log(0.5)) ** 0.5 # for reliable_lambda
def init(self, net_dim=256, state_dim=8, action_dim=2, reward_scale=1.0, gamma=0.99,
learning_rate=1e-4, if_per_or_gae=False, env_num=1, gpu_id=0):
AgentBase.init(self, net_dim=net_dim, state_dim=state_dim, action_dim=action_dim,
reward_scale=reward_scale, gamma=gamma,
learning_rate=learning_rate, if_per_or_gae=if_per_or_gae,
env_num=env_num, gpu_id=gpu_id, )
self.act = self.cri = self.ClassAct(net_dim, state_dim, action_dim).to(self.device)
if self.if_use_act_target:
self.act_target = self.cri_target = deepcopy(self.act)
else:
self.act_target = self.cri_target = self.act
self.cri_optim = torch.optim.Adam(
[{'params': self.act.enc_s.parameters(), 'lr': learning_rate * 1.5},
{'params': self.act.enc_a.parameters(), },
{'params': self.act.mid_n.parameters(), 'lr': learning_rate * 1.5},
{'params': self.act.dec_a.parameters(), },
{'params': self.act.dec_q.parameters(), },
], lr=learning_rate)
self.act_optim = self.cri_optim
if if_per_or_gae: # if_use_per
self.criterion = torch.nn.MSELoss(reduction='none')
self.get_obj_critic = self.get_obj_critic_per
else:
self.criterion = torch.nn.MSELoss(reduction='mean')
self.get_obj_critic = self.get_obj_critic_raw
def select_actions(self, state: torch.Tensor) -> torch.Tensor:
action = self.act.get_action(state.to(self.device), self.explore_noise)
return action.detach().cpu()
def update_net(self, buffer, batch_size, repeat_times, soft_update_tau) -> (float, float):
buffer.update_now_len()
obj_critic = None
obj_actor = None
update_a = 0
for update_c in range(1, int(buffer.now_len / batch_size * repeat_times)):
'''objective of critic'''
obj_critic, state = self.get_obj_critic(buffer, batch_size)
self.obj_critic = 0.995 * self.obj_critic + 0.005 * obj_critic.item() # for reliable_lambda
reliable_lambda = np.exp(-self.obj_critic ** 2) # for reliable_lambda
'''objective of actor using reliable_lambda and TTUR (Two Time-scales Update Rule)'''
if_update_a = update_a / update_c < 1 / (2 - reliable_lambda)
if if_update_a: # auto TTUR
update_a += 1
action_pg = self.act(state) # policy gradient
obj_actor = -self.act_target.critic(state, action_pg).mean()
obj_united = obj_critic + obj_actor * reliable_lambda
else:
obj_united = obj_critic
self.optim_update(self.act_optim, obj_united, self.act.parameters())
if self.if_use_act_target:
self.soft_update(self.act_target, self.act, soft_update_tau)
return obj_critic.item(), obj_actor.item()
def get_obj_critic_raw(self, buffer, batch_size):
with torch.no_grad():
# reward, mask, action, state, next_s = buffer.sample_batch(batch_size)
q_label, action, state = buffer.sample_batch_one_step(batch_size)
q_value = self.act.critic(state, action)
obj_critic = self.criterion(q_value, q_label)
return obj_critic, state
def get_obj_critic_per(self, buffer, batch_size):
with torch.no_grad():
# reward, mask, action, state, next_s, is_weights = buffer.sample_batch(batch_size)
q_label, action, state, is_weights = buffer.sample_batch_one_step(batch_size)
q_value = self.act.critic(state, action)
td_error = self.criterion(q_value, q_label) # or td_error = (q_value - q_label).abs()
obj_critic = (td_error * is_weights).mean()
buffer.td_error_update(td_error.detach())
return obj_critic, q_value
'''Utils'''
class OrnsteinUhlenbeckNoise: # NOT suggest to use it
def __init__(self, size, theta=0.15, sigma=0.3, ou_noise=0.0, dt=1e-2):
"""The noise of Ornstein-Uhlenbeck Process
Source: https://github.com/slowbull/DDPG/blob/master/src/explorationnoise.py
It makes Zero-mean Gaussian Noise more stable.
It helps agent explore better in a inertial system.
Don't abuse OU Process. OU process has too much hyper-parameters and over fine-tuning make no sense.
:int size: the size of noise, noise.shape==(-1, action_dim)
:float theta: related to the not independent of OU-noise
:float sigma: related to action noise std
:float ou_noise: initialize OU-noise
:float dt: derivative
"""
self.theta = theta
self.sigma = sigma
self.ou_noise = ou_noise
self.dt = dt
self.size = size
def __call__(self) -> float:
"""output a OU-noise
:return array ou_noise: a noise generated by Ornstein-Uhlenbeck Process
"""
noise = self.sigma * np.sqrt(self.dt) * rd.normal(size=self.size)
self.ou_noise -= self.theta * self.ou_noise * self.dt + noise
return self.ou_noise
================================================
FILE: elegantrl/demo.py
================================================
from elegantrl.agent import *
from elegantrl.env import build_env
from elegantrl.run import Arguments, train_and_evaluate, train_and_evaluate_mp
"""[ElegantRL.2021.10.10](https://github.com/AI4Finance-Foundation/ElegantRL)"""
'''train'''
def demo_continuous_action_off_policy(): # [ElegantRL.2021.10.10]
env_name = ['Pendulum-v1', 'LunarLanderContinuous-v2',
'BipedalWalker-v3', 'BipedalWalkerHardcore-v3'][0]
agent_class = [AgentModSAC, AgentSAC,
AgentTD3, AgentDDPG][0]
args = Arguments(env=build_env(env_name), agent=agent_class())
if env_name in {'Pendulum-v1', 'Pendulum-v0'}:
"""EpisodeReturn: (-1800) -1000 ~ -200 (-50)
Step 2e5, Reward -200, UsedTime 200s ModSAC
"""
# args = Arguments(env=build_env(env_name), agent=agent_class()) # One way to build env
# args = Arguments(env=env_name, agent=agent_class()) # Another way to build env
# args.env_num = 1
# args.max_step = 200
# args.state_dim = 3
# args.action_dim = 1
# args.if_discrete = False
# args.target_return = -200
args.gamma = 0.97
args.net_dim = 2 ** 7
args.worker_num = 2
args.reward_scale = 2 ** -2
args.target_step = 200 * 4 # max_step = 200
if env_name in {'LunarLanderContinuous-v2', 'LunarLanderContinuous-v1'}:
"""EpisodeReturn: (-800) -200 ~ 200 (302)
Step 4e5, Reward 200, UsedTime 900s, TD3
Step 5e5, Reward 200, UsedTime 1500s, ModSAC
"""
args.eval_times1 = 2 ** 4
args.eval_times2 = 2 ** 6
args.target_step = args.env.max_step
if env_name in {'BipedalWalker-v3', 'BipedalWalker-v2'}:
"""EpisodeReturn: (-200) -140 ~ 300 (341)
Step 08e5, Reward 300, UsedTime 1800s TD3
Step 11e5, Reward 329, UsedTime 6000s TD3
Step 4e5, Reward 300, UsedTime 2000s ModSAC
Step 8e5, Reward 330, UsedTime 5000s ModSAC
"""
args.eval_times1 = 2 ** 3
args.eval_times2 = 2 ** 5
args.gamma = 0.98
args.target_step = args.env.max_step
if env_name in {'BipedalWalkerHardcore-v3', 'BipedalWalkerHardcore-v2'}:
'''EpisodeReturn: (-200) -150 ~ 300 (334)
TotalStep (2e6) 4e6
Step 12e5, Reward 20
Step 18e5, Reward 135
Step 25e5, Reward 202
Step 43e5, Reward 309, UsedTime 68ks, ModSAC, worker_num=4
Step 14e5, Reward 15
Step 18e5, Reward 117
Step 28e5, Reward 212
Step 45e5, Reward 306, UsedTime 67ks, ModSAC, worker_num=4
Step 8e5, Reward 13
Step 16e5, Reward 136
Step 23e5, Reward 219
Step 38e5, Reward 302
UsedTime 99ks ModSAC, worker_num=2
'''
args.gamma = 0.98
args.net_dim = 2 ** 8
args.max_memo = 2 ** 22
args.break_step = int(80e6)
args.batch_size = args.net_dim * 2
args.repeat_times = 1.5
args.learning_rate = 2 ** -15
args.eval_gap = 2 ** 9
args.eval_times1 = 2 ** 2
args.eval_times2 = 2 ** 5
args.worker_num = 4
args.target_step = args.env.max_step * 1
# args.learner_gpus = (0, ) # single GPU
# args.learner_gpus = (0, 1) # multiple GPUs
# train_and_evaluate(args) # single process
train_and_evaluate_mp(args) # multiple process
def demo_continuous_action_on_policy(): # [ElegantRL.2021.10.13]
env_name = ['Pendulum-v1', 'LunarLanderContinuous-v2',
'BipedalWalker-v3', 'BipedalWalkerHardcore-v3'][ENV_ID]
agent_class = [AgentPPO, AgentA2C][0]
args = Arguments(env=build_env(env_name), agent=agent_class())
# args.if_per_or_gae = True
if env_name in {'Pendulum-v1', 'Pendulum-v0'}:
"""
Step 45e4, Reward -138, UsedTime 373s PPO
Step 40e4, Reward -200, UsedTime 400s PPO
Step 46e4, Reward -213, UsedTime 300s PPO
"""
# args = Arguments(env=build_env(env_name), agent=agent_class()) # One way to build env
# args = Arguments(env=env_name, agent=agent_class()) # Another way to build env
# args.env_num = 1
# args.max_step = 200
# args.state_dim = 3
# args.action_dim = 1
# args.if_discrete = False
# args.target_return = -200
args.gamma = 0.97
args.net_dim = 2 ** 8
args.worker_num = 2
args.reward_scale = 2 ** -2
args.target_step = 200 * 16 # max_step = 200
args.eval_gap = 2 ** 5
if env_name in {'LunarLanderContinuous-v2', 'LunarLanderContinuous-v1'}:
"""
Step 9e5, Reward 210, UsedTime 1127s PPO
Step 13e5, Reward 223, UsedTime 1416s PPO
Step 15e5, Reward 250, UsedTime 1648s PPO
Step 19e5, Reward 201, UsedTime 1880s PPO
Step 43e5, Reward 224, UsedTime 3738s PPO
Step 14e5, Reward 213, UsedTime 1654s PPO GAE
Step 12e5, Reward 216, UsedTime 1710s PPO GAE
"""
args.eval_times1 = 2 ** 4
args.eval_times2 = 2 ** 6
args.target_step = args.env.max_step * 8
if env_name in {'BipedalWalker-v3', 'BipedalWalker-v2'}:
"""
Step 51e5, Reward 300, UsedTime 2827s PPO
Step 78e5, Reward 304, UsedTime 4747s PPO
Step 61e5, Reward 300, UsedTime 3977s PPO GAE
Step 95e5, Reward 291, UsedTime 6193s PPO GAE
"""
args.eval_times1 = 2 ** 3
args.eval_times2 = 2 ** 5
args.gamma = 0.98
args.target_step = args.env.max_step * 16
if env_name in {'BipedalWalkerHardcore-v3', 'BipedalWalkerHardcore-v2'}:
"""
Step 57e5, Reward 295, UsedTime 17ks PPO
Step 70e5, Reward 300, UsedTime 21ks PPO
"""
args.gamma = 0.98
args.net_dim = 2 ** 8
args.max_memo = 2 ** 22
args.batch_size = args.net_dim * 4
args.repeat_times = 2 ** 4
args.learning_rate = 2 ** -16
args.eval_gap = 2 ** 8
args.eval_times1 = 2 ** 2
args.eval_times2 = 2 ** 5
# args.break_step = int(80e5)
args.worker_num = 4
args.target_step = args.env.max_step * 16
args.learner_gpus = (GPU_ID,) # single GPU
# args.learner_gpus = (0, 1) # multiple GPUs
# train_and_evaluate(args) # single process
train_and_evaluate_mp(args) # multiple process
def demo_discrete_action_off_policy(): # [ElegantRL.2021.10.10]
env_name = ['CartPole-v0', 'LunarLander-v2',
'SlimeVolley-v0', ][0]
agent_class = [AgentDoubleDQN, AgentDQN][0]
args = Arguments(env=build_env(env_name), agent=agent_class())
args.agent.if_use_dueling = True # DuelingDQN
if env_name in {'CartPole-v0', }:
"Step 1e5, Reward 200, UsedTime 40s, AgentD3QN"
args.target_return = 195
args.reward_scale = 2 ** -1
args.target_step = args.env.max_step * 4
args.eval_gap = 2 ** 5
if env_name in {'LunarLander-v2', }:
"Step 29e4, Reward 222, UsedTime 5811s D3QN"
args.max_memo = 2 ** 19
args.reward_scale = 2 ** -1
args.target_step = args.env.max_step
# args.learner_gpus = (0, ) # single GPU
# args.learner_gpus = (0, 1) # multiple GPUs
# train_and_evaluate(args) # single process
train_and_evaluate_mp(args) # multiple process
def demo_discrete_action_on_policy(): # [ElegantRL.2021.10.12]
env_name = ['CartPole-v0', 'LunarLander-v2',
'SlimeVolley-v0', ][0]
agent_class = [AgentDiscretePPO, AgentDiscreteA2C][0]
args = Arguments(env=build_env(env_name), agent=agent_class())
if env_name in {'CartPole-v0', }:
"Step 1e5, Reward 200, UsedTime 40s, DiscretePPO"
args.target_return = 195
args.reward_scale = 2 ** -1
args.target_step = args.env.max_step * 8
args.eval_gap = 2 ** 5
train_and_evaluate(args) # single process
if env_name in {'LunarLander-v2', }:
'''
Step 70e5, Reward 110, UsedTime 9961s DiscretePPO, repeat_times = 2 ** 4
Step 10e5, Reward 218, UsedTime 1336s DiscretePPO, repeat_times = 2 ** 5
'''
args.reward_scale = 2 ** -1
args.repeat_times = 2 ** 5
args.worker_num = 2
args.target_step = args.env.max_step * 4
train_and_evaluate_mp(args) # multiple process
def demo_pixel_level_on_policy(): # 2021-09-07
env_name = ['CarRacingFix', ][ENV_ID]
agent_class = [AgentPPO, AgentSharePPO, AgentShareA2C][0]
# args = Arguments(env=build_env(env_name, if_print=True), agent=agent_class())
args = Arguments(env=env_name, agent=agent_class())
if env_name == 'CarRacingFix':
args.state_dim = (112, 112, 6)
args.action_dim = 6
args.max_step = 512
args.if_discrete = False
args.target_return = 950
"Step 12e5, Reward 300, UsedTime 10ks PPO"
"Step 20e5, Reward 700, UsedTime 25ks PPO"
"Step 40e5, Reward 800, UsedTime 50ks PPO"
args.agent.ratio_clip = 0.5
args.agent.explore_rate = 0.75
args.agent.if_use_cri_target = True
args.gamma = 0.98
args.net_dim = 2 ** 8
args.repeat_times = 2 ** 4
args.learning_rate = 2 ** -17
args.soft_update_tau = 2 ** -11
args.batch_size = args.net_dim * 4
args.if_per_or_gae = True
args.agent.lambda_gae_adv = 0.96
args.eval_gap = 2 ** 9
args.eval_times1 = 2 ** 2
args.eval_times1 = 2 ** 4
args.if_allow_break = False
args.break_step = int(2 ** 22)
# args.worker_num = 6 # about 96 cores
args.worker_num = 2 # about 32 cores
args.target_step = int(args.max_step * 12 / args.worker_num)
args.learner_gpus = (GPU_ID,) # single GPU
args.eval_gpu_id = GPU_ID
train_and_evaluate_mp(args)
def demo_isaac_gym_on_policy():
env_name = ['IsaacVecEnvAnt', 'IsaacVecEnvHumanoid'][0]
args = Arguments(env=env_name, agent=AgentPPO())
args.learner_gpus = (0,)
args.eval_gpu_id = 1
if env_name in {'IsaacVecEnvAnt', 'IsaacOneEnvAnt'}:
'''
Step 21e7, Reward 8350, UsedTime 35ks
Step 484e7, Reward 16206, UsedTime 960ks PPO
Step 20e7, Reward 9196, UsedTime 35ks
Step 471e7, Reward 15021, UsedTime 960ks PPO, if_use_cri_target = True
Step 23e7, Reward 7111, UsedTime 12ks PPO
Step 22e7, Reward 5412, UsedTime 12ks PPO, max_step * 2
'''
args.eval_env = 'IsaacOneEnvAnt'
args.env = f'IsaacVecEnvAnt'
args.env_num = 4096
args.max_step = 1000
args.state_dim = 60
args.action_dim = 8
args.if_discrete = False
args.target_return = 8000
args.agent.lambda_entropy = 0.05
args.agent.lambda_gae_adv = 0.97
args.agent.if_use_cri_target = False
args.if_per_or_gae = True
args.learning_rate = 2 ** -14
args.net_dim = int(2 ** 8 * 1.5)
args.batch_size = args.net_dim * 2 ** 4
args.target_step = args.max_step * 1
args.repeat_times = 2 ** 4
args.reward_scale = 2 ** -2 # (-50) 0 ~ 2500 (3340)
args.break_step = int(8e14)
args.if_allow_break = False
args.eval_times1 = 2 ** 1
args.eval_times1 = 2 ** 3
if env_name in {'IsaacVecEnvHumanoid', 'IsaacOneEnvHumanoid'}:
'''
Step 126e7, Reward 8021
Step 216e7, Reward 9517
Step 283e7, Reward 9998
Step 438e7, Reward 10749, UsedTime 960ks PPO, env_num = 4096
Step 71e7, Reward 7800
Step 215e7, Reward 9794, UsedTime 465ks PPO, env_num = 2048
Step 1e7, Reward 117
Step 16e7, Reward 538
Step 21e7, Reward 3044
Step 38e7, Reward 5015
Step 65e7, Reward 6010
Step 72e7, Reward 6257, UsedTime 129ks PPO, if_use_cri_target = True
Step 77e7, Reward 5399, UsedTime 143ks PPO
Step 86e7, Reward 5822, UsedTime 157ks PPO, max_step * 2
'''
args.eval_env = 'IsaacOneEnvHumanoid'
args.env = f'IsaacVecEnvHumanoid'
args.env_num = 4096
args.max_step = 1000
args.state_dim = 108
args.action_dim = 21
args.if_discrete = False
args.target_return = 7000
args.agent.lambda_entropy = 0.05
args.agent.lambda_gae_adv = 0.97
args.agent.if_use_cri_target = True
args.net_dim = int(2 ** 8 * 1.5)
args.batch_size = args.net_dim * 2 ** 5
args.target_step = args.max_step * 1
args.repeat_times = 2 ** 4
args.reward_scale = 2 ** -2 # (-50) 0 ~ 2500 (3340)
args.if_per_or_gae = True
args.learning_rate = 2 ** -15
args.break_step = int(8e14)
args.if_allow_break = False
args.eval_times1 = 2 ** 1
args.eval_times1 = 2 ** 3
args.worker_num = 1
args.workers_gpus = args.learner_gpus
train_and_evaluate_mp(args) # train_and_evaluate(args)
def demo_pybullet_off_policy():
env_name = ['AntBulletEnv-v0', 'HumanoidBulletEnv-v0',
'ReacherBulletEnv-v0', 'MinitaurBulletEnv-v0', ][0]
agent_class = [AgentModSAC, AgentTD3,
AgentShareSAC, AgentShareAC][0]
args = Arguments(env=build_env(env_name, if_print=True), agent=agent_class())
if env_name == 'AntBulletEnv-v0':
"""EpisodeReturn (-50) 0 ~ 2500 (3340)
TotalStep (8e5) 10e5
0 4.29e+06 2446.47 | 431.34 82.1 999 0 | 0.08 1.65-275.32 0.26 | UsedTime 14393 |
0 1.41e+07 3499.37 | 3317.42 5.9 999 0 | 0.24 0.06 -49.94 0.03 | UsedTime 70020 |
0 3.54e+06 2875.30 | 888.67 4.7 999 0 | 0.19 0.11 -69.10 0.05 | UsedTime 54701 |
0 2.00e+07 2960.38 | 698.58 42.5 999 0 | 0.08 0.05 -39.44 0.03 | UsedTime 53545 |
"""
args.agent.if_use_act_target = False
args.net_dim = 2 ** 9
args.max_memo = 2 ** 22
args.repeat_times = 2 ** 1
args.reward_scale = 2 ** -2
args.batch_size = args.net_dim * 2
args.target_step = args.env.max_step * 2
args.eval_gap = 2 ** 8
args.eval_times1 = 2 ** 1
args.eval_times2 = 2 ** 4
args.break_step = int(8e7)
args.if_allow_break = False
if env_name == 'HumanoidBulletEnv-v0':
"""
0 1.50e+07 2571.46 | 53.63 66.8 128 58 | 0.04 0.96-153.29 0.06 | UsedTime 74470 |
0 1.51e+07 2822.93 | -1.51 27.1 99 36 | 0.03 0.58 -96.48 0.04 | UsedTime 74480 |
0 1.09e+06 66.96 | 58.69 8.2 58 12 | 0.22 0.28 -22.92 0.00
0 3.01e+06 129.69 | 101.39 40.6 96 33 | 0.14 0.28 -20.16 0.03
0 5.02e+06 263.13 | 208.69 122.6 195 59 | 0.11 0.29 -32.71 0.03
0 6.03e+06 791.89 | 527.79 282.7 360 144 | 0.21 0.26 -36.51 0.03
0 8.00e+06 2432.21 | 35.78 49.3 113 54 | -0.08 1.30-168.28 0.05
0 8.13e+06 2432.21 | 907.28 644.9 606 374 | 0.11 0.72-134.01 0.05
0 8.29e+06 2432.21 | 2341.30 39.4 999 0 | 0.41 0.41 -96.96 0.03
0 1.09e+07 2936.10 | 2936.10 24.8 999 0 | 0.60 0.13 -68.74 0.02
0 2.83e+07 2968.08 | 2737.18 15.9 999 0 | 0.57 0.21 -81.07 0.03 | UsedTime 74512 |
"""
args.net_dim = 2 ** 9
args.reward_scale = 2 ** -2
args.batch_size = args.net_dim * 2
args.target_step = args.env.max_step * 2
args.break_step = int(8e7)
args.if_allow_break = False
if env_name == 'ReacherBulletEnv-v0':
"""EpisodeReturn (-37) 0 ~ 18 (29)
TotalStep: (4e4) 5e4 # low eval_times
"""
args.explore_rate = 0.9
args.learning_rate = 2 ** -15
args.gamma = 0.99
args.net_dim = 2 ** 8
args.break_step = int(4e7)
args.batch_size = args.net_dim * 2
args.repeat_times = 2 ** 0
args.reward_scale = 2 ** 2
args.target_step = args.env.max_step * 4
args.eval_gap = 2 ** 8
args.eval_times1 = 2 ** 3
args.eval_times2 = 2 ** 5
if env_name == 'MinitaurBulletEnv-v0':
"""EpisodeReturn (-2) 0 ~ 16 (20)
TotalStep (2e6) 4e6
0 1.00e+06 0.46 | 0.24 0.0 98 37 | 0.06 0.06 -7.64 0.02
0 1.26e+06 1.36 | 1.36 0.7 731 398 | 0.10 0.08 -10.40 0.02
0 1.30e+06 3.18 | 3.18 0.8 999 0 | 0.13 0.08 -10.99 0.02
0 2.00e+06 3.18 | 0.04 0.0 28 0 | 0.13 0.09 -16.02 0.02
0 4.04e+06 7.11 | 6.68 0.6 999 0 | 0.17 0.08 -19.67 0.02
0 5.72e+06 9.79 | 9.28 0.1 999 0 | 0.22 0.03 -23.89 0.01
0 6.01e+06 10.69 | 10.09 0.8 999 0 | 0.22 0.03 -24.98 0.01
"""
args.net_dim = 2 ** 9
args.reward_scale = 2 ** 5 # (-2) 0 ~ 16 (20)
args.learning_rate = 2 ** -16
args.batch_size = args.net_dim * 2
args.target_step = args.env.max_step * 2
args.eval_gap = 2 ** 8
args.eval_times1 = 2 ** 2
args.eval_times2 = 2 ** 4
args.break_step = int(8e7)
args.if_allow_break = False
args.worker_num = 4
args.learner_gpus = (0,)
train_and_evaluate_mp(args)
def demo_pybullet_on_policy():
env_name = ['AntBulletEnv-v0', 'HumanoidBulletEnv-v0',
'ReacherBulletEnv-v0', 'MinitaurBulletEnv-v0', ][0]
agent_class = [AgentPPO, AgentSharePPO][0]
args = Arguments(env=build_env(env_name, if_print=True), agent=agent_class())
if env_name == 'AntBulletEnv-v0':
"""
0 1.98e+07 3322.16 | 3322.16 48.7 999 0 | 0.78 0.48 -0.01 -0.80 | UsedTime 12380 PPO
0 1.99e+07 3104.05 | 3071.44 14.5 999 0 | 0.74 0.47 0.01 -0.79 | UsedTime 12976
0 1.98e+07 3246.79 | 3245.98 25.3 999 0 | 0.75 0.48 -0.02 -0.81 | UsedTime 13170
0 1.97e+07 3345.48 | 3345.48 29.0 999 0 | 0.80 0.49 -0.01 -0.81 | UsedTime 8169 PPO 2GPU
0 1.98e+07 3028.69 | 3004.67 10.3 999 0 | 0.72 0.48 0.05 -0.82 | UsedTime 8734 PPO 2GPU
"""
args.agent.lambda_entropy = 0.05
args.agent.lambda_gae_adv = 0.97
args.net_dim = 2 ** 9
args.reward_scale = 2 ** -2 # (-50) 0 ~ 2500 (3340)
args.repeat_times = 2 ** 3
args.learning_rate = 2 ** -15
args.if_per_or_gae = True
args.batch_size = args.net_dim * 2 ** 3
args.target_step = args.env.max_step * 2
args.break_step = int(8e7)
args.if_allow_break = False
if env_name == 'HumanoidBulletEnv-v0':
"""
0 2.00e+07 2049.87 | 1905.57 686.5 883 308 | 0.93 0.42 -0.02 -1.14 | UsedTime 15292
0 3.99e+07 2977.80 | 2611.64 979.6 879 317 | 1.29 0.46 -0.01 -1.16 | UsedTime 19685
0 7.99e+07 3047.88 | 3041.95 41.1 999 0 | 1.37 0.46 -0.04 -1.15 | UsedTime 38693
"""
args.agent.lambda_entropy = 0.02
args.agent.lambda_gae_adv = 0.97
args.net_dim = 2 ** 9
args.batch_size = args.net_dim * 2 ** 3
args.target_step = args.env.max_step * 4
args.reward_scale = 2 ** -1
args.repeat_times = 2 ** 3
args.if_per_or_gae = True
args.learning_rate = 2 ** -14
args.break_step = int(8e7)
args.if_allow_break = False
if env_name == 'ReacherBulletEnv-v0':
'''eval_times = 4
Step 1e5, Return: 18, UsedTime 3ks, PPO eval_times =< 4
Step 1e6, Return: 18, UsedTime 30ks, PPO eval_times =< 4
eval_times = 64
The probability of the following results is only 25%.
0 5.00e+05 3.23 | 3.23 12.6 149 0 | -0.03 0.64 -0.03 -0.51
0 3.55e+06 7.69 | 7.69 10.3 149 0 | -0.19 0.56 -0.04 -0.59
0 5.07e+06 9.72 | 7.89 7.6 149 0 | 0.27 0.24 0.02 -0.71
0 6.85e+06 11.89 | 6.52 12.3 149 0 | 0.22 0.18 -0.06 -0.85
0 7.87e+06 18.59 | 18.59 9.4 149 0 | 0.39 0.18 -0.01 -0.94
0 1.01e+06 -2.19 | -7.30 10.9 149 0 | -0.05 0.70 0.03 -0.52
0 4.05e+06 9.29 | -1.86 15.0 149 0 | 0.08 0.28 -0.05 -0.65
0 4.82e+06 11.12 | 11.12 12.4 149 0 | 0.13 0.26 -0.07 -0.71
0 6.07e+06 15.66 | 15.66 11.1 149 0 | 0.16 0.14 0.00 -0.81
0 9.46e+06 18.58 | 18.58 8.2 149 0 | 0.19 0.10 -0.06 -1.09
0 2.20e+06 3.63 | 3.26 7.3 149 0 | -0.05 0.43 -0.01 -0.55
0 4.19e+06 5.24 | 4.60 9.2 149 0 | 0.04 0.24 0.00 -0.66
0 6.16e+06 5.24 | 4.80 9.2 149 0 | 0.03 0.15 -0.00 -0.81
0 7.40e+06 12.99 | 12.99 13.2 149 0 | 0.07 0.19 -0.03 -0.91
0 1.01e+07 18.09 | 18.09 7.6 149 0 | 0.18 0.16 -0.00 -1.09
0 1.06e+06 3.25 | 3.25 7.6 149 0 | -0.21 0.72 -0.05 -0.51
0 2.13e+06 3.56 | 0.94 6.1 149 0 | 0.08 0.54 0.02 -0.56
0 5.85e+06 11.61 | 11.61 11.0 149 0 | 0.13 0.22 -0.05 -0.78
0 9.04e+06 14.07 | 13.57 10.5 149 0 | 0.27 0.17 0.01 -1.05
0 1.01e+07 16.16 | 16.16 10.8 149 0 | 0.29 0.19 -0.08 -1.13
0 1.14e+07 21.33 | 21.33 7.8 149 0 | 0.21 0.24 -0.06 -1.21
0 1.02e+07 4.06 | -3.27 11.1 149 0 | -0.01 0.34 -0.03 -0.88
0 2.00e+07 9.23 | -1.57 7.6 149 0 | 0.06 0.12 -0.08 -1.26
0 3.00e+07 11.78 | 11.78 7.6 149 0 | 0.05 0.08 -0.04 -1.40
0 4.01e+07 13.20 | 12.35 7.8 149 0 | 0.14 0.08 0.01 -1.42
0 5.02e+07 14.13 | 11.53 6.5 149 0 | 0.10 0.03 0.03 -1.42
0 6.00e+07 15.75 | 6.33 6.1 149 0 | 0.18 0.13 -0.03 -1.43
0 7.29e+07 20.71 | 20.71 8.1 149 0 | 0.16 0.03 -0.00 -1.41
'''
args.agent.ratio_clip = 0.5
args.agent.lambda_gae_adv = 0.97
args.agent.if_use_cri_target = True
args.gamma = 0.99
args.reward_scale = 2 ** 1
args.if_per_or_gae = True
args.break_step = int(8e7)
args.explore_rate = 0.9
args.learning_rate = 2 ** -16
args.net_dim = 2 ** 8
args.batch_size = args.net_dim * 4
args.repeat_times = 2 ** 4
args.target_step = args.env.max_step * 4
args.eval_gap = 2 ** 8
args.eval_times1 = 2 ** 3
args.eval_times2 = 2 ** 5
if env_name == 'MinitaurBulletEnv-v0':
"""EpisodeReturn (-2) 0 ~ 16 (PPO 34)
TotalStep (PPO 3e5) 5e5
0 5.91e+05 10.59 | 10.59 3.9 727 282 | 0.27 0.69 -0.03 -0.52
0 1.15e+06 14.91 | 12.48 2.2 860 158 | 0.40 0.65 -0.02 -0.55
0 2.27e+06 25.38 | 22.54 4.7 968 54 | 0.75 0.61 -0.06 -0.60
0 4.13e+06 29.05 | 28.33 1.0 999 0 | 0.89 0.51 -0.07 -0.65
0 8.07e+06 32.66 | 32.17 0.9 999 0 | 0.97 0.45 -0.06 -0.73
0 1.10e+07 32.66 | 32.33 1.3 999 0 | 0.94 0.40 -0.07 -0.80 | UsedTime 20208 |
0 5.91e+05 5.48 | 5.48 1.5 781 219 | 0.24 0.66 -0.04 -0.52
0 1.01e+06 12.35 | 9.77 2.9 754 253 | 0.34 0.74 -0.05 -0.54
0 2.10e+06 12.35 | 12.21 4.8 588 285 | 0.60 0.65 -0.01 -0.58
0 4.09e+06 28.31 | 22.88 12.6 776 385 | 0.88 0.51 -0.03 -0.66
0 8.03e+06 30.96 | 28.32 6.8 905 163 | 0.93 0.52 -0.05 -0.76
0 1.09e+07 32.07 | 31.29 0.9 999 0 | 0.95 0.47 -0.07 -0.82 | UsedTime 20238 |
"""
args.agent.lambda_entropy = 0.05
args.agent.lambda_gae_adv = 0.97
args.net_dim = 2 ** 9
args.reward_scale = 2 ** 5 # (-2) 0 ~ 16 (20)
args.repeat_times = 2 ** 4
args.batch_size = args.net_dim * 4
args.target_step = args.env.max_step * 2
args.if_per_or_gae = True
args.learning_rate = 2 ** -15
args.break_step = int(8e7)
args.if_allow_break = False
args.eval_gap = 2 ** 8
args.eval_times1 = 2 ** 2
args.eval_times2 = 2 ** 4
args.worker_num = 4
args.learner_gpus = (0,)
train_and_evaluate_mp(args)
def demo_step1_off_policy():
env_name = ['DownLinkEnv-v0', 'DownLinkEnv-v1'][ENV_ID]
agent_class = [AgentStep1AC, AgentShareStep1AC][1]
args = Arguments(env=build_env(env_name), agent=agent_class())
args.random_seed += GPU_ID
args.net_dim = 2 ** 8
args.batch_size = int(args.net_dim * 2 ** -1)
args.max_memo = 2 ** 17
args.target_step = int(args.max_memo * 2 ** -4)
args.repeat_times = 0.75
args.reward_scale = 2 ** 2
args.agent.exploration_noise = 2 ** -5
args.eval_gpu_id = GPU_ID
args.eval_gap = 2 ** 9
args.eval_times1 = 2 ** 0
args.eval_times2 = 2 ** 1
args.learner_gpus = (GPU_ID,)
if_use_single_process = 0
if if_use_single_process:
train_and_evaluate(args, )
else:
args.worker_num = 4
train_and_evaluate_mp(args, )
'''train and watch'''
if __name__ == '__main__':
GPU_ID = 0 # eval(sys.argv[1])
ENV_ID = 0 # eval(sys.argv[2])
# demo_continuous_action_off_policy()
# demo_continuous_action_on_policy()
# demo_discrete_action_off_policy()
# demo_discrete_action_on_policy()
# demo_pixel_level_on_policy()
# demo_pybullet_off_policy()
# demo_pybullet_on_policy()
# demo_step1_off_policy()
pass
================================================
FILE: elegantrl/env.py
================================================
import os
import gym # not necessary
import numpy as np
from copy import deepcopy
"""[ElegantRL.2021.11.08](https://github.com/AI4Finance-Foundation/ElegantRL)"""
gym.logger.set_level(40) # Block warning
"""register your custom env here."""
def build_env(env, if_print=False, env_num=1, device_id=None, args=None, ):
if isinstance(env, str):
env_name = env
else:
env_name = env.env_name
env = None
'''OpenAI gym classical control'''
if env_name in {'CartPole-v0', 'CartPole-v1'}:
env = gym.make(env_name)
env = PreprocessEnv(env, if_print=if_print)
elif env_name in {'Pendulum-v1', 'Pendulum-v0'}:
env = PendulumEnv(env_name)
'''OpenAI gym Box2D'''
# pip3 install Box2D==2.3.8 or 2.3.10
if env_name in {'LunarLander-v2', 'LunarLanderContinuous-v2',
'BipedalWalker-v3', 'BipedalWalkerHardcore-v3', }:
env = gym.make(env_name)
env = PreprocessEnv(env, if_print=if_print) # todo plan to be elegant
elif env_name == 'CarRacingFix': # Box2D
from envs.CarRacingFix import CarRacingFix
env = CarRacingFix()
if if_print: # todo plan to be elegant
print(f"\n| env_name: {env.env_name}, action if_discrete: {env.if_discrete}"
f"\n| state_dim: {env.state_dim}, action_dim: {env.action_dim}"
f"\n| max_step: {env.max_step:4}, target_return: {env.target_return}")
'''PyBullet gym'''
if env_name in {'ReacherBulletEnv-v0', 'AntBulletEnv-v0',
'HumanoidBulletEnv-v0', 'MinitaurBulletEnv-v0'}:
import pybullet_envs
dir(pybullet_envs)
env = gym.make(env_name)
env = PreprocessEnv(env, if_print=if_print)
'''NVIDIA Isaac gym'''
if env_name.find('Isaac') >= 0:
from envs.IsaacGym import PreprocessIsaacOneEnv, PreprocessIsaacVecEnv
env_last_name = env_name[11:]
assert env_last_name in {'Ant', 'Humanoid'}
if env_name.find('IsaacOneEnv') >= 0:
env = PreprocessIsaacOneEnv(env_last_name, if_print=if_print, env_num=1, device_id=device_id)
elif env_name.find('IsaacVecEnv') >= 0:
env = PreprocessIsaacVecEnv(env_last_name, if_print=if_print, env_num=env_num, device_id=device_id)
else:
raise ValueError(f'| build_env_from_env_name: need register: {env_name}')
return env
# elif env_name[:10] in {'StockDOW5', 'StockDOW30', 'StockNAS74', 'StockNAS89'}:
# if_eval = env_name.find('eval') != -1
# gamma = 0.993
# from elegantrl.envs.FinRL.StockTradingEnv import StockEnvDOW5, StockEnvDOW30, StockEnvNAS74, StockEnvNAS89
# env_class = {'StockDOW5': StockEnvDOW5,
# 'StockDOW30': StockEnvDOW30,
# 'StockNAS74': StockEnvNAS74,
# 'StockNAS89': StockEnvNAS89,
# }[env_name[:10]]
# env = env_class(if_eval=if_eval, gamma=gamma)
if env_name in {'DownLinkEnv-v0', 'DownLinkEnv-v1'}:
if env_name == 'DownLinkEnv-v0':
from envs.DownLink import DownLinkEnv
env = DownLinkEnv(bs_n=4, ur_n=8, power=1.0, csi_noise_var=0.1, csi_clip=3.0)
elif env_name == 'DownLinkEnv-v1':
from envs.DownLink import DownLinkEnv1
env = DownLinkEnv1(bs_n=4, ur_n=8, power=1.0, csi_noise_var=0.1, csi_clip=3.0,
env_cwd=getattr(args, 'cwd', '.'))
else:
raise ValueError("| env.py, build_env(), DownLinkEnv")
if env_name[:6]=='wt4rl-':
env = gym.make(env_name)
if env is None:
try:
env = deepcopy(env)
print(f"| build_env(): Warning. NOT suggest to use `deepcopy(env)`. env_name: {env_name}")
except Exception as error:
print(f"| build_env(): Error. {error}")
raise ValueError("| build_env(): register your custom env in this function.")
return env
def build_eval_env(eval_env, env, env_num, eval_gpu_id, args, ):
if isinstance(eval_env, str):
eval_env = build_env(env=eval_env, if_print=False, env_num=env_num, device_id=eval_gpu_id, args=args, )
elif eval_env is None:
eval_env = build_env(env=env, if_print=False, env_num=env_num, device_id=eval_gpu_id, args=args, )
else:
assert hasattr(eval_env, 'reset')
assert hasattr(eval_env, 'step')
return eval_env
"""a demo tell you how to build a custom env"""
class PendulumEnv: # [ElegantRL.2021.10.10]
def __init__(self, env_name):
assert env_name in {'Pendulum-v1', 'Pendulum-v0'}
try:
env_name = 'Pendulum-v0' # gym.__version__ == 0.17.0
self.env = gym.make(env_name)
except KeyError:
env_name = 'Pendulum-v1' # gym.__version__ == 0.21.0
self.env = gym.make(env_name)
self.env_name = env_name # assert isinstance(env_name, str)
# from elegantrl.env import get_gym_env_info
# get_gym_env_info(env, if_print=True) # use this function to see the env information
self.env_num = 1 # the env number of VectorEnv is greater than 1
self.max_step = 200 # the max step of each episode
self.state_dim = 3 # feature number of state
self.action_dim = 1 # feature number of action
self.if_discrete = False # discrete action or continuous action
self.target_return = -200 # episode return is between (-1600, 0)
def reset(self):
return self.env.reset()
def step(self, action):
# PendulumEnv set its action space as (-2, +2). It is bad. # https://github.com/openai/gym/wiki/Pendulum-v0
# I suggest you to set action space as (-1, +1) when you design your own env.
return self.env.step(action * 2) # state, reward, done, info_dict
def render(self):
self.env.render()
"""Utils"""
class PreprocessEnv(gym.Wrapper): # environment wrapper
def __init__(self, env, if_print=True, if_norm=False):
"""Preprocess a standard OpenAI gym environment for training.
`object env` a standard OpenAI gym environment, it has env.reset() and env.step()
`bool if_print` print the information of environment. Such as env_name, state_dim ...
`object data_type` convert state (sometimes float64) to data_type (float32).
"""
self.env = gym.make(env) if isinstance(env, str) else env
super().__init__(self.env)
(self.env_name, self.state_dim, self.action_dim, self.max_step,
self.if_discrete, self.target_return) = get_gym_env_info(self.env, if_print)
self.env.env_num = getattr(self.env, 'env_num', 1)
self.env_num = 1
if if_norm:
state_avg, state_std = get_avg_std__for_state_norm(self.env_name)
self.neg_state_avg = -state_avg
self.div_state_std = 1 / (state_std + 1e-4)
self.reset = self.reset_norm
self.step = self.step_norm
else:
self.reset = self.reset_type
self.step = self.step_type
def reset_type(self):
return self.env.reset()
def step_type(self, action) -> (np.ndarray, float, bool, dict):
return self.env.step(action)
def reset_norm(self):
""" convert the data type of state from float64 to float32
do normalization on state
return `array state` state.shape==(state_dim, )
"""
state = self.env.reset()
return (state + self.neg_state_avg) * self.div_state_std
def step_norm(self, action) -> (np.ndarray, float, bool, dict):
"""convert the data type of state from float64 to float32,
do normalization on state
return `array state` state.shape==(state_dim, )
return `float reward` reward of one step
return `bool done` the terminal of an training episode
return `dict info` the information save in a dict. OpenAI gym standard. Send a `None` is OK
"""
state, reward, done, info = self.env.step(action)
state = (state + self.neg_state_avg) * self.div_state_std
return state, reward, done, info
def get_gym_env_info(env, if_print) -> (str, int, int, int, bool, float): # [ElegantRL.2021.10.10]
"""get information of a standard OpenAI gym env.
The DRL algorithm AgentXXX need these env information for building networks and training.
`object env` a standard OpenAI gym environment, it has env.reset() and env.step()
`bool if_print` print the information of environment. Such as env_name, state_dim ...
return `env_name` the environment name, such as XxxXxx-v0
return `state_dim` the dimension of state
return `action_dim` the dimension of continuous action; Or the number of discrete action
return `max_step` the steps in an episode. (from env.reset to done). It breaks an episode when it reach max_step
return `if_discrete` Is this env a discrete action space?
return `target_return` the target episode return, if agent reach this score, then it pass this game (env).
"""
if isinstance(env, gym.Env):
env_name = getattr(env, 'env_name', None)
env_name = env.unwrapped.spec.id if env_name is None else env_name
state_shape = env.observation_space.shape
state_dim = state_shape[0] if len(state_shape) == 1 else state_shape # sometimes state_dim is a list
target_return = getattr(env, 'target_return', None)
target_return_default = getattr(env.spec, 'reward_threshold', None)
if target_return is None:
target_return = target_return_default
if target_return is None:
target_return = 2 ** 16
max_step = getattr(env, 'max_step', None)
max_step_default = getattr(env, '_max_episode_steps', None)
if max_step is None:
max_step = max_step_default
if max_step is None:
max_step = 2 ** 10
if_discrete = isinstance(env.action_space, gym.spaces.Discrete)
if if_discrete: # make sure it is discrete action space
action_dim = env.action_space.bs_n
elif isinstance(env.action_space, gym.spaces.Box): # make sure it is continuous action space
action_dim = env.action_space.shape[0]
assert not any(env.action_space.high - 1)
assert not any(env.action_space.low + 1)
else:
raise RuntimeError('\n| Error in get_gym_env_info()'
'\n Please set these value manually: if_discrete=bool, action_dim=int.'
'\n And keep action_space in (-1, 1).')
else:
env_name = env.env_name
state_dim = env.state_dim
action_dim = env.action_dim
max_step = env.max_step
if_discrete = env.if_discrete
target_return = env.target_return
if if_print:
print(f"\n| env_name: {env_name}, action if_discrete: {if_discrete}"
f"\n| state_dim: {state_dim:4}, action_dim: {action_dim}"
f"\n| max_step: {max_step:4}, target_return: {target_return}")
return env_name, state_dim, action_dim, max_step, if_discrete, target_return
def get_avg_std__for_state_norm(env_name) -> (np.ndarray, np.ndarray):
"""return the state normalization data: neg_avg and div_std
ReplayBuffer.print_state_norm() will print `neg_avg` and `div_std`
You can save these array to here. And PreprocessEnv will load them automatically.
eg. `state = (state + self.neg_state_avg) * self.div_state_std` in `PreprocessEnv.step_norm()`
neg_avg = -states.mean()
div_std = 1/(states.std()+1e-5) or 6/(states.max()-states.min())
`str env_name` the name of environment that helps to find neg_avg and div_std
return `array avg` neg_avg.shape=(state_dim)
return `array std` div_std.shape=(state_dim)
"""
avg = 0
std = 1
if env_name == 'LunarLanderContinuous-v2':
avg = np.array([1.65470898e-02, -1.29684399e-01, 4.26883133e-03, -3.42124557e-02,
-7.39076972e-03, -7.67103031e-04, 1.12640885e+00, 1.12409466e+00])
std = np.array([0.15094465, 0.29366297, 0.23490797, 0.25931464, 0.21603736,
0.25886878, 0.277233, 0.27771219])
elif env_name == "BipedalWalker-v3":
avg = np.array([1.42211734e-01, -2.74547996e-03, 1.65104509e-01, -1.33418152e-02,
-2.43243194e-01, -1.73886203e-02, 4.24114229e-02, -6.57800099e-02,
4.53460692e-01, 6.08022244e-01, -8.64884810e-04, -2.08789053e-01,
-2.92092949e-02, 5.04791247e-01, 3.33571745e-01, 3.37325723e-01,
3.49106580e-01, 3.70363115e-01, 4.04074671e-01, 4.55838055e-01,
5.36685407e-01, 6.70771701e-01, 8.80356865e-01, 9.97987386e-01])
std = np.array([0.84419678, 0.06317835, 0.16532085, 0.09356959, 0.486594,
0.55477525, 0.44076614, 0.85030824, 0.29159821, 0.48093035,
0.50323634, 0.48110776, 0.69684234, 0.29161077, 0.06962932,
0.0705558, 0.07322677, 0.07793258, 0.08624322, 0.09846895,
0.11752805, 0.14116005, 0.13839757, 0.07760469])
elif env_name == 'ReacherBulletEnv-v0':
avg = np.array([0.03149641, 0.0485873, -0.04949671, -0.06938662, -0.14157104,
0.02433294, -0.09097818, 0.4405931, 0.10299437], dtype=np.float32)
std = np.array([0.12277275, 0.1347579, 0.14567468, 0.14747661, 0.51311225,
0.5199606, 0.2710207, 0.48395795, 0.40876198], dtype=np.float32)
elif env_name == 'AntBulletEnv-v0':
avg = np.array([-1.4400886e-01, -4.5074993e-01, 8.5741436e-01, 4.4249415e-01,
-3.1593361e-01, -3.4174921e-03, -6.1666980e-02, -4.3752361e-03,
-8.9226037e-02, 2.5108769e-03, -4.8667483e-02, 7.4835382e-03,
3.6160579e-01, 2.6877613e-03, 4.7474738e-02, -5.0628246e-03,
-2.5761038e-01, 5.9789192e-04, -2.1119279e-01, -6.6801407e-03,
2.5196713e-01, 1.6556121e-03, 1.0365561e-01, 1.0219718e-02,
5.8209229e-01, 7.7563477e-01, 4.8815918e-01, 4.2498779e-01],
dtype=np.float32)
std = np.array([0.04128463, 0.19463477, 0.15422264, 0.16463493, 0.16640785,
0.08266512, 0.10606721, 0.07636797, 0.7229637, 0.52585346,
0.42947173, 0.20228386, 0.44787514, 0.33257666, 0.6440182,
0.38659114, 0.6644085, 0.5352245, 0.45194066, 0.20750992,
0.4599643, 0.3846344, 0.651452, 0.39733195, 0.49320385,
0.41713253, 0.49984455, 0.4943505], dtype=np.float32)
elif env_name == 'HumanoidBulletEnv-v0':
avg = np.array([-1.25880212e-01, -8.51390958e-01, 7.07488894e-01, -5.72232604e-01,
-8.76260102e-01, -4.07587215e-02, 7.27005303e-04, 1.23370838e+00,
-3.68912554e+00, -4.75829793e-03, -7.42472351e-01, -8.94218776e-03,
1.29535913e+00, 3.16205365e-03, 9.13809776e-01, -6.42679911e-03,
8.90435696e-01, -7.92571157e-03, 6.54826105e-01, 1.82383414e-02,
1.20868635e+00, 2.90832808e-03, -9.96598601e-03, -1.87555347e-02,
1.66691601e+00, 7.45300390e-03, -5.63859344e-01, 5.48619963e-03,
1.33900166e+00, 1.05895223e-02, -8.30249667e-01, 1.57017610e-03,
1.92912612e-02, 1.55787319e-02, -1.19833803e+00, -8.22103582e-03,
-6.57119334e-01, -2.40323972e-02, -1.05282271e+00, -1.41856335e-02,
8.53593826e-01, -1.73063378e-03, 5.46878874e-01, 5.43514848e-01],
dtype=np.float32)
std = np.array([0.08138401, 0.41358876, 0.33958328, 0.17817754, 0.17003846,
0.15247536, 0.690917, 0.481272, 0.40543965, 0.6078898,
0.46960834, 0.4825346, 0.38099176, 0.5156369, 0.6534775,
0.45825616, 0.38340876, 0.89671516, 0.14449312, 0.47643778,
0.21150663, 0.56597894, 0.56706554, 0.49014297, 0.30507362,
0.6868296, 0.25598812, 0.52973163, 0.14948095, 0.49912784,
0.42137524, 0.42925757, 0.39722264, 0.54846555, 0.5816031,
1.139402, 0.29807225, 0.27311933, 0.34721208, 0.38530213,
0.4897849, 1.0748593, 0.30166605, 0.30824476], dtype=np.float32)
elif env_name == 'MinitaurBulletEnv-v0': # need check
avg = np.array([0.90172989, 1.54730119, 1.24560906, 1.97365306, 1.9413892,
1.03866835, 1.69646277, 1.18655352, -0.45842347, 0.17845232,
0.38784456, 0.58572877, 0.91414561, -0.45410697, 0.7591031,
-0.07008998, 3.43842258, 0.61032482, 0.86689961, -0.33910894,
0.47030415, 4.5623528, -2.39108079, 3.03559422, -0.36328256,
-0.20753499, -0.47758384, 0.86756409])
std = np.array([0.34192648, 0.51169916, 0.39370621, 0.55568461, 0.46910769,
0.28387504, 0.51807949, 0.37723445, 13.16686185, 17.51240024,
14.80264211, 16.60461412, 15.72930229, 11.38926597, 15.40598346,
13.03124941, 2.47718145, 2.55088804, 2.35964651, 2.51025567,
2.66379017, 2.37224904, 2.55892521, 2.41716885, 0.07529733,
0.05903034, 0.1314812, 0.0221248])
return avg, std
def demo_get_video_to_watch_gym_render():
import cv2 # pip3 install opencv-python
# import gym # pip3 install gym==0.17 pyglet==1.5.0 # env.render() bug in gym==0.18, pyglet==1.6
import torch
"""init env"""
env = build_env(env='CarRacingFix')
'''init agent'''
# agent = None # means use random action
from elegantrl.agent import AgentPPO
agent = AgentPPO() # means use the policy network which saved in cwd
agent_cwd = '/mnt/sdb1/Yonv/code/ElegantRL/AgentPPO_CarRacingFix_3'
net_dim = 2 ** 8
state_dim = env.state_dim
action_dim = env.action_dim
os.environ['CUDA_VISIBLE_DEVICES'] = '1'
agent.init(net_dim, state_dim, action_dim)
agent.save_or_load_agent(cwd=agent_cwd, if_save=False)
device = agent.device
'''initialize evaluete and env.render()'''
save_frame_dir = '' # means don't save video, just open the env.render()
# save_frame_dir = 'frames' # means save video in this directory
if save_frame_dir:
os.makedirs(save_frame_dir, exist_ok=True)
state = env.reset()
episode_return = 0
step = 0
for i in range(2 ** 10):
print(i) if i % 128 == 0 else None
for j in range(1):
if agent is None:
action = env.action_space.sample()
else:
s_tensor = torch.as_tensor((state,), dtype=torch.float32, device=device)
a_tensor = agent.act(s_tensor)
action = a_tensor.detach().cpu().numpy()[0] # if use 'with torch.no_grad()', then '.detach()' not need.
next_state, reward, done, _ = env.step(action)
episode_return += reward
step += 1
if done:
print(f'\t'
f'TotalStep {i:>6}, epiStep {step:6.0f}, '
f'Reward_T {reward:8.3f}, epiReward {episode_return:8.3f}')
state = env.reset()
episode_return = 0
step = 0
else:
state = next_state
if save_frame_dir:
frame = env.render('rgb_array')
cv2.imwrite(f'{save_frame_dir}/{i:06}.png', frame)
cv2.imshow('OpenCV Window', frame)
cv2.waitKey(1)
else:
env.render()
env.close()
'''convert frames png/jpg to video mp4/avi using ffmpeg'''
if save_frame_dir:
frame_shape = cv2.imread(f'{save_frame_dir}/{3:06}.png').shape
print(f"frame_shape: {frame_shape}")
save_video = 'gym_render.mp4'
os.system(f"| Convert frames to video using ffmpeg. Save in {save_video}")
os.system(f'ffmpeg -r 60 -f image2 -s {frame_shape[0]}x{frame_shape[1]} '
f'-i ./{save_frame_dir}/%06d.png '
f'-crf 25 -vb 20M -pix_fmt yuv420p {save_video}')
def train_save_eval_watch():
# from elegantrl.env import build_env
from elegantrl.run import Arguments, train_and_evaluate
from elegantrl.agent import AgentDoubleDQN
env = build_env('CartPole-v0')
agent = AgentDoubleDQN(if_dueling=True)
cwd = 'demo_CartPole_D3QN'
print('train and save')
args = Arguments(env=env, agent=agent)
args.eval_gap = 2 ** 5
args.target_return = 195
train_and_evaluate(args) # single process
print('evaluate and watch')
agent.init(args.net_dim, args.state_dim, args.action_dim, gpu_id=0)
agent.save_or_load_agent(cwd=cwd, if_save=False)
state = env.reset()
for i in range(2 ** 10):
action = agent.select_action(state)
next_state, reward, done, _ = env.step(action)
state = env.reset() if done else next_state
env.render()
================================================
FILE: elegantrl/evaluator.py
================================================
import os
import time
import torch
import numpy as np
"""[ElegantRL.2021.11.10](https://github.com/AI4Finance-Foundation/ElegantRL)"""
class Evaluator: # [ElegantRL.2021.10.13]
def __init__(self, cwd, agent_id, eval_env, eval_gap, eval_times1, eval_times2, target_return, if_overwrite):
self.recorder = list() # total_step, r_avg, r_std, obj_c, ...
self.recorder_path = f'{cwd}/recorder.npy'
self.cwd = cwd
self.agent_id = agent_id
self.eval_env = eval_env
self.eval_gap = eval_gap
self.eval_times1 = eval_times1
self.eval_times2 = eval_times2
self.if_overwrite = if_overwrite
self.target_return = target_return
self.r_max = -np.inf
self.eval_time = 0
self.used_time = 0
self.total_step = 0
self.start_time = time.time()
print(f"{'#' * 80}\n"
f"{'ID':<3}{'Step':>8}{'maxR':>8} |"
f"{'avgR':>8}{'stdR':>7}{'avgS':>7}{'stdS':>6} |"
f"{'expR':>8}{'objC':>7}{'etc.':>7}")
def evaluate_and_save(self, act, steps, r_exp, log_tuple) -> (bool, bool): # 2021-09-09
self.total_step += steps # update total training steps
if time.time() - self.eval_time < self.eval_gap:
if_reach_goal = False
if_save = False
else:
self.eval_time = time.time()
'''evaluate first time'''
rewards_steps_list = [get_episode_return_and_step(self.eval_env, act)
for _ in range(self.eval_times1)]
r_avg, r_std, s_avg, s_std = self.get_r_avg_std_s_avg_std(rewards_steps_list)
'''evaluate second time'''
if r_avg > self.r_max: # evaluate actor twice to save CPU Usage and keep precision
rewards_steps_list += [get_episode_return_and_step(self.eval_env, act)
for _ in range(self.eval_times2 - self.eval_times1)]
r_avg, r_std, s_avg, s_std = self.get_r_avg_std_s_avg_std(rewards_steps_list)
'''save the policy network'''
if_save = r_avg > self.r_max
if if_save: # save checkpoint with highest episode return
self.r_max = r_avg # update max reward (episode return)
act_name = 'actor' if self.if_overwrite else f'actor.{self.r_max:08.2f}'
act_path = f"{self.cwd}/{act_name}.pth"
torch.save(act.state_dict(), act_path) # save policy network in *.pth
print(f"{self.agent_id:<3}{self.total_step:8.2e}{self.r_max:8.2f} |") # save policy and print
self.recorder.append((self.total_step, r_avg, r_std, r_exp, *log_tuple)) # update recorder
'''print some information to Terminal'''
if_reach_goal = bool(self.r_max > self.target_return) # check if_reach_goal
if if_reach_goal and self.used_time is None:
self.used_time = int(time.time() - self.start_time)
print(f"{'ID':<3}{'Step':>8}{'TargetR':>8} |"
f"{'avgR':>8}{'stdR':>7}{'avgS':>7}{'stdS':>6} |"
f"{'UsedTime':>8} ########\n"
f"{self.agent_id:<3}{self.total_step:8.2e}{self.target_return:8.2f} |"
f"{r_avg:8.2f}{r_std:7.1f}{s_avg:7.0f}{s_std:6.0f} |"
f"{self.used_time:>8} ########")
print(f"{self.agent_id:<3}{self.total_step:8.2e}{self.r_max:8.2f} |"
f"{r_avg:8.2f}{r_std:7.1f}{s_avg:7.0f}{s_std:6.0f} |"
f"{r_exp:8.2f}{''.join(f'{n:7.2f}' for n in log_tuple)}")
self.draw_plot()
if hasattr(self.eval_env, 'curriculum_learning_for_evaluator'):
self.eval_env.curriculum_learning_for_evaluator(r_avg)
return if_reach_goal, if_save
@staticmethod
def get_r_avg_std_s_avg_std(rewards_steps_list):
rewards_steps_ary = np.array(rewards_steps_list, dtype=np.float32)
r_avg, s_avg = rewards_steps_ary.mean(axis=0) # average of episode return and episode step
r_std, s_std = rewards_steps_ary.std(axis=0) # standard dev. of episode return and episode step
return r_avg, r_std, s_avg, s_std
def save_or_load_recoder(self, if_save):
if if_save:
np.save(self.recorder_path, self.recorder)
elif os.path.exists(self.recorder_path):
recorder = np.load(self.recorder_path)
self.recorder = [tuple(i) for i in recorder] # convert numpy to list
self.total_step = self.recorder[-1][0]
def draw_plot(self):
if len(self.recorder) == 0:
print("| save_npy_draw_plot() WARNNING: len(self.recorder)==0")
return None
np.save(self.recorder_path, self.recorder)
'''draw plot and save as png'''
train_time = int(time.time() - self.start_time)
total_step = int(self.recorder[-1][0])
save_title = f"step_time_maxR_{int(total_step)}_{int(train_time)}_{self.r_max:.3f}"
save_learning_curve(self.recorder, self.cwd, save_title)
def get_episode_return_and_step(env, act) -> (float, int): # [ElegantRL.2021.10.13]
device_id = next(act.parameters()).get_device() # net.parameters() is a python generator.
device = torch.device('cpu' if device_id == -1 else f'cuda:{device_id}')
episode_step = 1
episode_return = 0.0 # sum of rewards in an episode
max_step = env.max_step
if_discrete = env.if_discrete
if if_discrete:
def get_action(_state):
_state = torch.as_tensor(_state, dtype=torch.float32, device=device)
_action = act(_state.unsqueeze(0))
_action = _action.argmax(dim=1)[0]
return _action.detach().cpu().numpy()
else:
def get_action(_state):
_state = torch.as_tensor(_state, dtype=torch.float32, device=device)
_action = act(_state.unsqueeze(0))[0]
return _action.detach().cpu().numpy()
state = env.reset()
for episode_step in range(max_step):
action = get_action(state)
state, reward, done, _ = env.step(action)
episode_return += reward
if done:
break
episode_return = getattr(env, 'episode_return', episode_return)
return episode_return, episode_step
def save_learning_curve(recorder=None, cwd='.', save_title='learning curve', fig_name='plot_learning_curve.jpg'):
if recorder is None:
recorder = np.load(f"{cwd}/recorder.npy")
recorder = np.array(recorder)
steps = recorder[:, 0] # x-axis is training steps
r_avg = recorder[:, 1]
r_std = recorder[:, 2]
r_exp = recorder[:, 3]
obj_c = recorder[:, 4]
obj_a = recorder[:, 5]
'''plot subplots'''
import matplotlib as mpl
mpl.use('Agg')
"""Generating matplotlib graphs without a running X server [duplicate]
write `mpl.use('Agg')` before `import matplotlib.pyplot as plt`
https://stackoverflow.com/a/4935945/9293137
"""
import matplotlib.pyplot as plt
fig, axs = plt.subplots(2)
'''axs[0]'''
ax00 = axs[0]
ax00.cla()
ax01 = axs[0].twinx()
color01 = 'darkcyan'
ax01.set_ylabel('Explore AvgReward', color=color01)
ax01.plot(steps, r_exp, color=color01, alpha=0.5, )
ax01.tick_params(axis='y', labelcolor=color01)
color0 = 'lightcoral'
ax00.set_ylabel('Episode Return')
ax00.plot(steps, r_avg, label='Episode Return', color=color0)
ax00.fill_between(steps, r_avg - r_std, r_avg + r_std, facecolor=color0, alpha=0.3)
ax00.grid()
'''axs[1]'''
ax10 = axs[1]
ax10.cla()
ax11 = axs[1].twinx()
color11 = 'darkcyan'
ax11.set_ylabel('objC', color=color11)
ax11.fill_between(steps, obj_c, facecolor=color11, alpha=0.2, )
ax11.tick_params(axis='y', labelcolor=color11)
color10 = 'royalblue'
ax10.set_xlabel('Total Steps')
ax10.set_ylabel('objA', color=color10)
ax10.plot(steps, obj_a, label='objA', color=color10)
ax10.tick_params(axis='y', labelcolor=color10)
for plot_i in range(6, recorder.shape[1]):
other = recorder[:, plot_i]
ax10.plot(steps, other, label=f'{plot_i}', color='grey', alpha=0.5)
ax10.legend()
ax10.grid()
'''plot save'''
plt.title(save_title, y=2.3)
plt.savefig(f"{cwd}/{fig_name}")
plt.close('all') # avoiding warning about too many open figures, rcParam `figure.max_open_warning`
# plt.show() # if use `mpl.use('Agg')` to draw figures without GUI, then plt can't plt.show()
================================================
FILE: elegantrl/net.py
================================================
import numpy as np
import torch
import torch.nn as nn
"""[ElegantRL.2021.09.01](https://github.com/AI4Finance-Foundation/ElegantRL)"""
'''Q Network'''
class QNet(nn.Module): # nn.Module is a standard PyTorch Network
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
self.net = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim))
def forward(self, state):
return self.net(state) # Q value
class QNetDuel(nn.Module): # Dueling DQN
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
self.net_state = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU())
self.net_adv = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, 1)) # advantage function value 1
self.net_val = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim)) # Q value
def forward(self, state):
t_tmp = self.net_state(state) # tensor of encoded state
q_adv = self.net_adv(t_tmp)
q_val = self.net_val(t_tmp)
return q_adv + q_val - q_val.mean(dim=1, keepdim=True) # dueling Q value
class QNetTwin(nn.Module): # Double DQN
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
self.net_state = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU())
self.net_q1 = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim)) # q1 value
self.net_q2 = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim)) # q2 value
def forward(self, state):
tmp = self.net_state(state)
return self.net_q1(tmp) # one Q value
def get_q1_q2(self, state):
tmp = self.net_state(state)
return self.net_q1(tmp), self.net_q2(tmp) # two Q values
class QNetTwinDuel(nn.Module): # D3QN: Dueling Double DQN
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
self.net_state = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU())
self.net_adv1 = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, 1)) # q1 value
self.net_adv2 = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, 1)) # q2 value
self.net_val1 = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim)) # advantage function value 1
self.net_val2 = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim)) # advantage function value 1
def forward(self, state):
t_tmp = self.net_state(state)
q_adv = self.net_adv1(t_tmp)
q_val = self.net_val1(t_tmp)
return q_adv + q_val - q_val.mean(dim=1, keepdim=True) # one dueling Q value
def get_q1_q2(self, state):
tmp = self.net_state(state)
adv1 = self.net_adv1(tmp)
val1 = self.net_val1(tmp)
q1 = adv1 + val1 - val1.mean(dim=1, keepdim=True)
adv2 = self.net_adv2(tmp)
val2 = self.net_val2(tmp)
q2 = adv2 + val2 - val2.mean(dim=1, keepdim=True)
return q1, q2 # two dueling Q values
'''Policy Network (Actor)'''
class Actor(nn.Module):
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
self.net = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim))
def forward(self, state):
return self.net(state).tanh() # action.tanh()
def get_action(self, state, action_std):
action = self.net(state).tanh()
noise = (torch.randn_like(action) * action_std).clamp(-0.5, 0.5)
return (action + noise).clamp(-1.0, 1.0)
class ActorSAC(nn.Module):
def __init__(self, mid_dim, state_dim, action_dim, if_use_dn=False):
super().__init__()
if if_use_dn:
nn_middle = DenseNet(mid_dim // 2)
inp_dim = nn_middle.inp_dim
out_dim = nn_middle.out_dim
else:
nn_middle = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU(), )
inp_dim = mid_dim
out_dim = mid_dim
self.net_state = nn.Sequential(nn.Linear(state_dim, inp_dim), nn.ReLU(),
nn_middle, )
self.net_a_avg = nn.Sequential(nn.Linear(out_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim)) # the average of action
self.net_a_std = nn.Sequential(nn.Linear(out_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim)) # the log_std of action
self.log_sqrt_2pi = np.log(np.sqrt(2 * np.pi))
def forward(self, state):
tmp = self.net_state(state)
return self.net_a_avg(tmp).tanh() # action
def get_action(self, state):
t_tmp = self.net_state(state)
a_avg = self.net_a_avg(t_tmp) # NOTICE! it is a_avg without .tanh()
a_std = self.net_a_std(t_tmp).clamp(-20, 2).exp()
return torch.normal(a_avg, a_std).tanh() # re-parameterize
def get_action_logprob(self, state):
t_tmp = self.net_state(state)
a_avg = self.net_a_avg(t_tmp) # NOTICE! it needs a_avg.tanh()
a_std_log = self.net_a_std(t_tmp).clamp(-20, 2)
a_std = a_std_log.exp()
"""add noise to action in stochastic policy"""
noise = torch.randn_like(a_avg, requires_grad=True)
a_tan = (a_avg + a_std * noise).tanh() # action.tanh()
# Can only use above code instead of below, because the tensor need gradients here.
# a_noise = torch.normal(a_avg, a_std, requires_grad=True)
'''compute log_prob according to mean and std of action (stochastic policy)'''
# self.sqrt_2pi_log = np.log(np.sqrt(2 * np.pi))
log_prob = a_std_log + self.log_sqrt_2pi + noise.pow(2).__mul__(0.5) # noise.pow(2) * 0.5
# same as below:
# from torch.distributions.normal import Normal
# log_prob_noise = Normal(a_avg, a_std).log_prob(a_noise)
# log_prob = log_prob_noise + (-a_noise_tanh.pow(2) + 1.000001).log()
# same as below:
# a_delta = (a_avg - a_noise).pow(2) /(2*a_std.pow(2))
# log_prob_noise = -a_delta - a_std.log() - np.log(np.sqrt(2 * np.pi))
# log_prob = log_prob_noise + (-a_noise_tanh.pow(2) + 1.000001).log()
log_prob = log_prob + (-a_tan.pow(2) + 1.000001).log() # fix log_prob using the derivative of action.tanh()
# same as below:
# epsilon = 1e-6
# log_prob = log_prob_noise - (1 - a_noise_tanh.pow(2) + epsilon).log()
return a_tan, log_prob.sum(1, keepdim=True)
# def log_abs_det_jacobian(self, x, y):
# # https://github.com/denisyarats/pytorch_sac/blob/81c5b536d3a1c5616b2531e446450df412a064fb/agent/actor.py#L37
# # ↑ MIT License, Thanks for https://www.zhihu.com/people/Z_WXCY 2ez4U
# # We use a formula that is more numerically stable, see details in the following link
# # https://pytorch.org/docs/stable/_modules/torch/distributions/transforms.html#TanhTransform
# # https://github.com/tensorflow/probability/commit/ef6bb176e0ebd1cf6e25c6b5cecdd2428c22963f
# return 2. * (math.log(2.) - x - F.softplus(-2. * x))
class ActorPPO(nn.Module):
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
if isinstance(state_dim, int):
nn_middle = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU(), )
else:
nn_middle = ConvNet(inp_dim=state_dim[2], out_dim=mid_dim, image_size=state_dim[0])
self.net = nn.Sequential(nn_middle,
nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim), )
layer_norm(self.net[-1], std=0.1) # output layer for action
# the logarithm (log) of standard deviation (std) of action, it is a trainable parameter
self.a_std_log = nn.Parameter(torch.zeros((1, action_dim)) - 0.5, requires_grad=True)
self.sqrt_2pi_log = np.log(np.sqrt(2 * np.pi))
def forward(self, state):
return self.net(state).tanh() # action.tanh()
def get_action(self, state):
a_avg = self.net(state)
a_std = self.a_std_log.exp()
noise = torch.randn_like(a_avg)
action = a_avg + noise * a_std
return action, noise
def get_logprob_entropy(self, state, action):
a_avg = self.net(state)
a_std = self.a_std_log.exp()
delta = ((a_avg - action) / a_std).pow(2) * 0.5
logprob = -(self.a_std_log + self.sqrt_2pi_log + delta).sum(1) # new_logprob
dist_entropy = (logprob.exp() * logprob).mean() # policy entropy
return logprob, dist_entropy
def get_old_logprob(self, _action, noise): # noise = action - a_noise
delta = noise.pow(2) * 0.5
return -(self.a_std_log + self.sqrt_2pi_log + delta).sum(1) # old_logprob
class ActorDiscretePPO(nn.Module):
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
if isinstance(state_dim, int):
nn_middle = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(), )
else:
nn_middle = ConvNet(inp_dim=state_dim[2], out_dim=mid_dim, image_size=state_dim[0])
self.net = nn.Sequential(nn_middle,
nn.Linear(mid_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim), )
layer_norm(self.net[-1], std=0.1) # output layer for action
self.action_dim = action_dim
self.soft_max = nn.Softmax(dim=-1)
self.Categorical = torch.distributions.Categorical
def forward(self, state):
return self.net(state) # action_prob without softmax
def get_action(self, state):
a_prob = self.soft_max(self.net(state))
# dist = Categorical(a_prob)
# a_int = dist.sample()
a_int = torch.multinomial(a_prob, num_samples=1, replacement=True)[:, 0]
# samples_2d = torch.multinomial(a_prob, num_samples=1, replacement=True)
# samples_2d.shape == (batch_size, num_samples)
return a_int, a_prob
def get_logprob_entropy(self, state, a_int):
a_prob = self.soft_max(self.net(state))
dist = self.Categorical(a_prob)
return dist.log_prob(a_int), dist.entropy().mean()
def get_old_logprob(self, a_int, a_prob):
dist = self.Categorical(a_prob)
return dist.log_prob(a_int)
class ActorBiConv(nn.Module):
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
self.net = nn.Sequential(
BiConvNet(mid_dim, state_dim, mid_dim * 4),
nn.Linear(mid_dim * 4, mid_dim * 1), nn.ReLU(),
DenseNet(mid_dim * 1), nn.ReLU(),
nn.Linear(mid_dim * 4, action_dim),
)
layer_norm(self.net[-1], std=0.1) # output layer for action
def forward(self, state):
action = self.net(state)
return action * torch.pow((action ** 2).sum(), -0.5) # action / sqrt(L2_norm(action))
def get_action(self, state, action_std):
action = self.net(state)
action = action + (torch.randn_like(action) * action_std)
return action * torch.pow((action ** 2).sum(), -0.5) # action / sqrt(L2_norm(action))
'''Value Network (Critic)'''
class Critic(nn.Module):
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
self.net = nn.Sequential(nn.Linear(state_dim + action_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, 1))
def forward(self, state, action):
return self.net(torch.cat((state, action), dim=1)) # Q value
class CriticTwin(nn.Module): # shared parameter
def __init__(self, mid_dim, state_dim, action_dim, if_use_dn=False):
super().__init__()
if if_use_dn:
nn_middle = DenseNet(mid_dim // 2)
inp_dim = nn_middle.inp_dim
out_dim = nn_middle.out_dim
else:
nn_middle = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU(), )
inp_dim = mid_dim
out_dim = mid_dim
self.net_sa = nn.Sequential(nn.Linear(state_dim + action_dim, inp_dim), nn.ReLU(),
nn_middle, ) # concat(state, action)
self.net_q1 = nn.Sequential(nn.Linear(out_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, 1)) # q1 value
self.net_q2 = nn.Sequential(nn.Linear(out_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, 1)) # q2 value
def forward(self, state, action):
tmp = self.net_sa(torch.cat((state, action), dim=1))
return self.net_q1(tmp) # one Q value
def get_q1_q2(self, state, action):
tmp = self.net_sa(torch.cat((state, action), dim=1))
return self.net_q1(tmp), self.net_q2(tmp) # two Q values
class CriticPPO(nn.Module):
def __init__(self, mid_dim, state_dim, _action_dim):
super().__init__()
if isinstance(state_dim, int):
nn_middle = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(), )
else:
nn_middle = ConvNet(inp_dim=state_dim[2], out_dim=mid_dim, image_size=state_dim[0])
self.net = nn.Sequential(nn_middle,
nn.Linear(mid_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, 1), )
layer_norm(self.net[-1], std=0.5) # output layer for advantage value
def forward(self, state):
return self.net(state) # advantage value
class CriticTwinPPO(nn.Module):
def __init__(self, mid_dim, state_dim, _action_dim):
super().__init__()
if isinstance(state_dim, int):
nn_middle = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU(), )
else:
nn_middle = ConvNet(inp_dim=state_dim[2], out_dim=mid_dim, image_size=state_dim[0])
self.net = nn.Sequential(nn_middle, )
self.net_v1 = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, 1)) # advantage value1
self.net_v2 = nn.Sequential(nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, 1)) # advantage value2
layer_norm(self.net[-1], std=0.5) # output layer for advantage value
def forward(self, state):
tmp = self.net(state)
return self.net_v1(tmp) # one advantage value
def get_q1_q2(self, state):
tmp = self.net(state)
return self.net_v1(tmp), self.net_v2(tmp) # two advantage values
class CriticBiConv(nn.Module):
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
i_c_dim, i_h_dim, i_w_dim = state_dim # inp_for_cnn.shape == (N, C, H, W)
assert action_dim == int(np.prod((i_c_dim, i_w_dim, i_h_dim)))
action_dim = (i_c_dim, i_w_dim, i_h_dim) # (2, bs_n, ur_n)
self.cnn_s = nn.Sequential(
BiConvNet(mid_dim, state_dim, mid_dim * 4),
nn.Linear(mid_dim * 4, mid_dim * 2), nn.ReLU(inplace=True),
nn.Linear(mid_dim * 2, mid_dim * 1),
)
self.cnn_a = nn.Sequential(
NnReshape(*action_dim),
BiConvNet(mid_dim, action_dim, mid_dim * 4),
nn.Linear(mid_dim * 4, mid_dim * 2), nn.ReLU(inplace=True),
nn.Linear(mid_dim * 2, mid_dim * 1),
)
self.out_net = nn.Sequential(
nn.Linear(mid_dim * 1, mid_dim * 1), nn.Hardswish(),
nn.Linear(mid_dim * 1, 1),
)
layer_norm(self.out_net[-1], std=0.1) # output layer for action
def forward(self, state, action):
xs = self.cnn_s(state)
xa = self.cnn_a(action)
return self.out_net(xs + xa) # Q value
'''Parameter sharing Network'''
class ShareDPG(nn.Module): # DPG means deterministic policy gradient
def __init__(self, state_dim, action_dim, mid_dim):
super().__init__()
nn_dense = DenseNet(mid_dim // 2)
inp_dim = nn_dense.inp_dim
out_dim = nn_dense.out_dim
self.enc_s = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, inp_dim))
self.enc_a = nn.Sequential(nn.Linear(action_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, inp_dim))
self.net = nn_dense
self.dec_a = nn.Sequential(nn.Linear(out_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim), nn.Tanh())
self.dec_q = nn.Sequential(nn.Linear(out_dim, mid_dim), nn.Hardswish(),
nn.utils.spectral_norm(nn.Linear(mid_dim, 1)))
@staticmethod
def add_noise(a, noise_std):
a_temp = torch.normal(a, noise_std)
mask = torch.lt(a_temp, -1) + torch.gt(a_temp, 1) # mask = (a_temp < -1.0) + (a_temp > 1.0)
mask = torch.tensor(mask, dtype=torch.float32).cuda()
noise_uniform = torch.rand_like(a)
a_noise = noise_uniform * mask + a_temp * (-mask + 1)
return a_noise
def forward(self, s, noise_std=0.0): # actor
s_ = self.enc_s(s)
a_ = self.net(s_)
a = self.dec_a(a_)
return a if noise_std == 0.0 else self.add_noise(a, noise_std)
def critic(self, s, a):
s_ = self.enc_s(s)
a_ = self.enc_a(a)
q_ = self.net(s_ + a_)
q = self.dec_q(q_)
return q
def next_q_action(self, s, s_next, noise_std):
s_ = self.enc_s(s)
a_ = self.net(s_)
a = self.dec_a(a_)
'''q_target (without noise)'''
a_ = self.enc_a(a)
s_next_ = self.enc_s(s_next)
q_target0_ = self.net(s_next_ + a_)
q_target0 = self.dec_q(q_target0_)
'''q_target (with noise)'''
a_noise = self.add_noise(a, noise_std)
a_noise_ = self.enc_a(a_noise)
q_target1_ = self.net(s_next_ + a_noise_)
q_target1 = self.dec_q(q_target1_)
q_target = (q_target0 + q_target1) * 0.5
return q_target, a
class ShareSPG(nn.Module): # SPG means stochastic policy gradient
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
self.log_sqrt_2pi_sum = np.log(np.sqrt(2 * np.pi)) * action_dim
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
nn_dense = DenseNet(mid_dim // 2)
inp_dim = nn_dense.inp_dim
out_dim = nn_dense.out_dim
self.enc_s = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, inp_dim), ) # state
self.enc_a = nn.Sequential(nn.Linear(action_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, inp_dim), ) # action without nn.Tanh()
self.net = nn_dense
self.dec_a = nn.Sequential(nn.Linear(out_dim, mid_dim // 2), nn.ReLU(),
nn.Linear(mid_dim // 2, action_dim), ) # action_mean
self.dec_d = nn.Sequential(nn.Linear(out_dim, mid_dim // 2), nn.ReLU(),
nn.Linear(mid_dim // 2, action_dim), ) # action_std_log (d means standard deviation)
self.dec_q1 = nn.Sequential(nn.Linear(out_dim, mid_dim // 2), nn.ReLU(),
nn.Linear(mid_dim // 2, 1), ) # q1 value
self.dec_q2 = nn.Sequential(nn.Linear(out_dim, mid_dim // 2), nn.ReLU(),
nn.Linear(mid_dim // 2, 1), ) # q2 value
self.log_alpha = nn.Parameter(torch.zeros((1, action_dim)) - np.log(action_dim), requires_grad=True)
layer_norm(self.dec_a[-1], std=0.5)
layer_norm(self.dec_d[-1], std=0.1)
layer_norm(self.dec_q1[-1], std=0.5)
layer_norm(self.dec_q2[-1], std=0.5)
def forward(self, s):
x = self.enc_s(s)
x = self.net(x)
a_avg = self.dec_a(x)
return a_avg.tanh()
def get_action(self, s):
s_ = self.enc_s(s)
a_ = self.net(s_)
a_avg = self.dec_a(a_) # NOTICE! it is a_avg without tensor.tanh()
a_std_log = self.dec_d(a_).clamp(-20, 2)
a_std = a_std_log.exp()
action = torch.normal(a_avg, a_std) # NOTICE! it is action without .tanh()
return action.tanh()
def get_action_logprob(self, state): # actor
s_ = self.enc_s(state)
a_ = self.net(s_)
"""add noise to action, stochastic policy"""
a_avg = self.dec_a(a_) # NOTICE! it is action without .tanh()
a_std_log = self.dec_d(a_).clamp(-20, 2)
a_std = a_std_log.exp()
noise = torch.randn_like(a_avg, requires_grad=True)
a_noise = a_avg + a_std * noise
a_noise_tanh = a_noise.tanh()
fix_term = (-a_noise_tanh.pow(2) + 1.00001).log()
logprob = (noise.pow(2) / 2 + a_std_log + fix_term).sum(1, keepdim=True) + self.log_sqrt_2pi_sum
return a_noise_tanh, logprob
def get_q_logprob(self, state):
s_ = self.enc_s(state)
a_ = self.net(s_)
"""add noise to action, stochastic policy"""
a_avg = self.dec_a(a_) # NOTICE! it is action without .tanh()
a_std_log = self.dec_d(a_).clamp(-20, 2)
a_std = a_std_log.exp()
noise = torch.randn_like(a_avg, requires_grad=True)
a_noise = a_avg + a_std * noise
a_noise_tanh = a_noise.tanh()
fix_term = (-a_noise_tanh.pow(2) + 1.00001).log()
logprob = (noise.pow(2) / 2 + a_std_log + fix_term).sum(1, keepdim=True) + self.log_sqrt_2pi_sum
'''get q'''
a_ = self.enc_a(a_noise_tanh)
q_ = self.net(s_ + a_)
q = torch.min(self.dec_q1(q_), self.dec_q2(q_))
return q, logprob
def get_q1_q2(self, s, a): # critic
s_ = self.enc_s(s)
a_ = self.enc_a(a)
q_ = self.net(s_ + a_)
q1 = self.dec_q1(q_)
q2 = self.dec_q2(q_)
return q1, q2
class SharePPO(nn.Module): # Pixel-level state version
def __init__(self, state_dim, action_dim, mid_dim):
super().__init__()
if isinstance(state_dim, int):
self.enc_s = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim)) # the only difference.
else:
self.enc_s = ConvNet(inp_dim=state_dim[2], out_dim=mid_dim, image_size=state_dim[0])
out_dim = mid_dim
self.dec_a = nn.Sequential(nn.Linear(out_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, action_dim))
self.a_std_log = nn.Parameter(torch.zeros(1, action_dim) - 0.5, requires_grad=True)
self.dec_q1 = nn.Sequential(nn.Linear(out_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, 1))
self.dec_q2 = nn.Sequential(nn.Linear(out_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, 1))
layer_norm(self.dec_a[-1], std=0.01)
layer_norm(self.dec_q1[-1], std=0.01)
layer_norm(self.dec_q2[-1], std=0.01)
self.sqrt_2pi_log = np.log(np.sqrt(2 * np.pi))
def forward(self, s):
s_ = self.enc_s(s)
a_avg = self.dec_a(s_)
return a_avg.tanh()
def get_action_noise(self, state):
s_ = self.enc_s(state)
a_avg = self.dec_a(s_)
a_std = self.a_std_log.exp()
# a_noise = torch.normal(a_avg, a_std) # same as below
noise = torch.randn_like(a_avg)
a_noise = a_avg + noise * a_std
return a_noise, noise
def get_q_logprob(self, state, noise):
s_ = self.enc_s(state)
q = torch.min(self.dec_q1(s_), self.dec_q2(s_))
logprob = -(noise.pow(2) / 2 + self.a_std_log + self.sqrt_2pi_log).sum(1)
return q, logprob
def get_q1_q2_logprob(self, state, action):
s_ = self.enc_s(state)
q1 = self.dec_q1(s_)
q2 = self.dec_q2(s_)
a_avg = self.dec_a(s_)
a_std = self.a_std_log.exp()
logprob = -(((a_avg - action) / a_std).pow(2) / 2 + self.a_std_log + self.sqrt_2pi_log).sum(1)
return q1, q2, logprob
class ShareBiConv(nn.Module):
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
i_c_dim, i_h_dim, i_w_dim = state_dim # inp_for_cnn.shape == (N, C, H, W)
assert action_dim == int(np.prod((i_c_dim, i_w_dim, i_h_dim)))
state_tuple = (i_c_dim, i_h_dim, i_w_dim)
self.enc_s = nn.Sequential(
# NnReshape(*state_tuple),
BiConvNet(mid_dim, state_tuple, mid_dim * 4),
)
action_tuple = (i_c_dim, i_w_dim, i_h_dim)
self.enc_a = nn.Sequential(
NnReshape(*action_tuple),
BiConvNet(mid_dim, action_tuple, mid_dim * 4),
)
self.mid_n = nn.Sequential(
nn.Linear(mid_dim * 4, mid_dim * 2), nn.ReLU(),
nn.Linear(mid_dim * 2, mid_dim * 1), nn.ReLU(),
DenseNet(mid_dim),
)
self.dec_a = nn.Sequential(
nn.Linear(mid_dim * 4, mid_dim * 2), nn.Hardswish(),
nn.Linear(mid_dim * 2, action_dim),
)
layer_norm(self.dec_a[-1], std=0.1) # output layer for action
self.dec_q = nn.Sequential(
nn.Linear(mid_dim * 4, mid_dim * 2), nn.Hardswish(),
nn.Linear(mid_dim * 2, 1),
)
layer_norm(self.dec_q[-1], std=0.1) # output layer for action
def forward(self, state): # actor
xs = self.enc_s(state)
xn = self.mid_n(xs)
action = self.dec_a(xn)
return action * torch.pow((action ** 2).sum(), -0.5) # action / sqrt(L2_norm(action))
def critic(self, state, action):
xs = self.enc_s(state)
xa = self.enc_a(action)
xn = self.mid_n(xs + xa)
return self.dec_q(xn) # Q value
def get_action(self, state, action_std): # actor, get noisy action
xs = self.enc_s(state)
xn = self.mid_n(xs)
action = self.dec_a(xn)
action = action + (torch.randn_like(action) * action_std)
return action * torch.pow((action ** 2).sum(), -0.5) # action / sqrt(L2_norm(action))
"""utils"""
class NnReshape(nn.Module):
def __init__(self, *args):
super().__init__()
self.args = args
def forward(self, x):
return x.view((x.size(0),) + self.args)
class DenseNet(nn.Module): # plan to hyper-param: layer_number
def __init__(self, lay_dim):
super().__init__()
self.dense1 = nn.Sequential(nn.Linear(lay_dim * 1, lay_dim * 1), nn.Hardswish())
self.dense2 = nn.Sequential(nn.Linear(lay_dim * 2, lay_dim * 2), nn.Hardswish())
self.inp_dim = lay_dim
self.out_dim = lay_dim * 4
def forward(self, x1): # x1.shape==(-1, lay_dim*1)
x2 = torch.cat((x1, self.dense1(x1)), dim=1)
x3 = torch.cat((x2, self.dense2(x2)), dim=1)
return x3 # x2.shape==(-1, lay_dim*4)
class ConcatNet(nn.Module): # concatenate
def __init__(self, lay_dim):
super().__init__()
self.dense1 = nn.Sequential(nn.Linear(lay_dim, lay_dim), nn.ReLU(),
nn.Linear(lay_dim, lay_dim), nn.Hardswish(), )
self.dense2 = nn.Sequential(nn.Linear(lay_dim, lay_dim), nn.ReLU(),
nn.Linear(lay_dim, lay_dim), nn.Hardswish(), )
self.dense3 = nn.Sequential(nn.Linear(lay_dim, lay_dim), nn.ReLU(),
nn.Linear(lay_dim, lay_dim), nn.Hardswish(), )
self.dense4 = nn.Sequential(nn.Linear(lay_dim, lay_dim), nn.ReLU(),
nn.Linear(lay_dim, lay_dim), nn.Hardswish(), )
self.inp_dim = lay_dim
self.out_dim = lay_dim * 4
def forward(self, x0):
x1 = self.dense1(x0)
x2 = self.dense2(x0)
x3 = self.dense3(x0)
x4 = self.dense4(x0)
return torch.cat((x1, x2, x3, x4), dim=1)
class ConvNet(nn.Module): # pixel-level state encoder
def __init__(self, inp_dim, out_dim, image_size=224):
super().__init__()
if image_size == 224:
self.net = nn.Sequential( # size==(batch_size, inp_dim, 224, 224)
nn.Conv2d(inp_dim, 32, (5, 5), stride=(2, 2), bias=False), nn.ReLU(inplace=True), # size=110
nn.Conv2d(32, 48, (3, 3), stride=(2, 2)), nn.ReLU(inplace=True), # size=54
nn.Conv2d(48, 64, (3, 3), stride=(2, 2)), nn.ReLU(inplace=True), # size=26
nn.Conv2d(64, 96, (3, 3), stride=(2, 2)), nn.ReLU(inplace=True), # size=12
nn.Conv2d(96, 128, (3, 3), stride=(2, 2)), nn.ReLU(inplace=True), # size=5
nn.Conv2d(128, 192, (5, 5), stride=(1, 1)), nn.ReLU(inplace=True), # size=1
NnReshape(-1), # size (batch_size, 1024, 1, 1) ==> (batch_size, 1024)
nn.Linear(192, out_dim), # size==(batch_size, out_dim)
)
elif image_size == 112:
self.net = nn.Sequential( # size==(batch_size, inp_dim, 112, 112)
nn.Conv2d(inp_dim, 32, (5, 5), stride=(2, 2), bias=False), nn.ReLU(inplace=True), # size=54
nn.Conv2d(32, 48, (3, 3), stride=(2, 2)), nn.ReLU(inplace=True), # size=26
nn.Conv2d(48, 64, (3, 3), stride=(2, 2)), nn.ReLU(inplace=True), # size=12
nn.Conv2d(64, 96, (3, 3), stride=(2, 2)), nn.ReLU(inplace=True), # size=5
nn.Conv2d(96, 128, (5, 5), stride=(1, 1)), nn.ReLU(inplace=True), # size=1
NnReshape(-1), # size (batch_size, 1024, 1, 1) ==> (batch_size, 1024)
nn.Linear(128, out_dim), # size==(batch_size, out_dim)
)
else:
assert image_size in {224, 112}
def forward(self, x):
# assert x.shape == (batch_size, inp_dim, image_size, image_size)
x = x.permute(0, 3, 1, 2)
x = x / 128.0 - 1.0
return self.net(x)
# @staticmethod
# def check():
# inp_dim = 3
# out_dim = 32
# batch_size = 2
# image_size = [224, 112][1]
# # from elegantrl.net import Conv2dNet
# net = Conv2dNet(inp_dim, out_dim, image_size)
#
# x = torch.ones((batch_size, image_size, image_size, inp_dim), dtype=torch.uint8) * 255
# print(x.shape)
# y = net(x)
# print(y.shape)
class BiConvNet(nn.Module):
def __init__(self, mid_dim, inp_dim, out_dim):
super().__init__()
i_c_dim, i_h_dim, i_w_dim = inp_dim # inp_for_cnn.shape == (N, C, H, W)
self.cnn_h = nn.Sequential(
nn.Conv2d(i_c_dim * 1, mid_dim * 2, (1, i_w_dim), bias=True), nn.LeakyReLU(inplace=True),
nn.Conv2d(mid_dim * 2, mid_dim * 1, (1, 1), bias=True), nn.ReLU(inplace=True),
NnReshape(-1), # shape=(-1, i_h_dim * mid_dim)
nn.Linear(i_h_dim * mid_dim, out_dim),
)
self.cnn_w = nn.Sequential(
nn.Conv2d(i_c_dim * 1, mid_dim * 2, (i_h_dim, 1), bias=True), nn.LeakyReLU(inplace=True),
nn.Conv2d(mid_dim * 2, mid_dim * 1, (1, 1), bias=True), nn.ReLU(inplace=True),
NnReshape(-1), # shape=(-1, i_w_dim * mid_dim)
nn.Linear(i_w_dim * mid_dim, out_dim),
)
def forward(self, state):
xh = self.cnn_h(state)
xw = self.cnn_w(state)
return xw + xh
def layer_norm(layer, std=1.0, bias_const=1e-6):
torch.nn.init.orthogonal_(layer.weight, std)
torch.nn.init.constant_(layer.bias, bias_const)
class ActorSimplify:
def __init__(self, gpu_id, actor_net):
self.device = torch.device(f"cuda:{gpu_id}" if (torch.cuda.is_available() and gpu_id >= 0) else 'cpu')
self.actor_net = actor_net.to(self.device)
def get_action(self, state: np.ndarray) -> np.ndarray:
states = torch.as_tensor(state[np.newaxis], dtype=torch.float32, device=self.device)
action = self.actor_net(states)[0]
return action.detach().cpu().numpy()
"""check"""
def check_actor_network():
batch_size = 3
mid_dim = 2 ** 8
state_dim = 24
action_dim = 4
if_check_actor_net = 0
if if_check_actor_net:
net = ActorPPO(mid_dim, state_dim, action_dim)
inp = torch.ones((batch_size, state_dim), dtype=torch.float32)
out = net(inp)
print(out.shape)
if_check_pixel_level = 1
if if_check_pixel_level:
img_channel = 6
img_size = 112 # {112, 224}
mid_dim = 2 ** 8
state_dim = (img_size, img_size, img_channel)
action_dim = 4
net = ActorPPO(mid_dim, state_dim, action_dim)
inp = torch.ones((batch_size, img_size, img_size, img_channel), dtype=torch.int8)
out = net(inp)
print(out.shape)
def check_network():
from envs.DownLink import DownLinkEnv
env = DownLinkEnv()
gpu_id = 1
mid_dim = 128
state_dim = env.state_dim
action_dim = env.action_dim
if_check_actor = 0
if if_check_actor:
net = ActorBiConv(mid_dim, state_dim, action_dim)
act = ActorSimplify(gpu_id, net)
state = env.reset()
action = env.get_action_mmse(state)
reward = env.get_reward(action)
print(f"| mmse : {reward:8.3f}")
action = env.get_action_mmse(state)
action = env.get_action_norm_power(action)
reward = env.get_reward(action)
print(f"| mmse (max Power): {reward:8.3f}")
action = np.ones(env.action_dim)
reward = env.get_reward(action)
print(f"| ones (max Power): {reward:8.3f}")
action = env.get_action_norm_power(action=None) # random.normal action
reward = env.get_reward(action)
print(f"| rand (max Power): {reward:8.3f}")
action = act.get_action(state)
action = env.get_action_norm_power(action)
reward = env.get_reward(action)
print(f"| net (max Power): {reward:8.3f}")
# state, reward, done, _ = env.step(action)
# print(reward)
if_check_critic = 1
if if_check_critic:
batch_size = 3
device = torch.device(f"cuda:{gpu_id}" if (torch.cuda.is_available() and gpu_id >= 0) else 'cpu')
critic_net = CriticBiConv(mid_dim, state_dim, action_dim).to(device)
ten_state = torch.randn(size=(batch_size, *state_dim), dtype=torch.float32, device=device)
ten_action = torch.randn(size=(batch_size, action_dim), dtype=torch.float32, device=device)
q_value = critic_net(ten_state, ten_action)
print(q_value)
================================================
FILE: elegantrl/replay.py
================================================
import os
import torch
import numpy as np
import numpy.random as rd
"""[ElegantRL.2021.09.01](https://github.com/AI4Finance-Foundation/ElegantRL)"""
class ReplayBuffer:
def __init__(self, max_len, state_dim, action_dim, if_use_per, gpu_id=0, state_type=torch.float32):
"""Experience Replay Buffer
save environment transition in a continuous RAM for high performance training
we save trajectory in order and save state and other (action, reward, mask, ...) separately.
`int max_len` the maximum capacity of ReplayBuffer. First In First Out
`int state_dim` the dimension of state
`int action_dim` the dimension of action (action_dim==1 for discrete action)
`bool if_on_policy` on-policy or off-policy
`bool if_gpu` create buffer space on CPU RAM or GPU
`bool if_per` Prioritized Experience Replay for sparse reward
"""
self.now_len = 0
self.next_idx = 0
self.if_full = False
self.max_len = max_len
self.data_type = torch.float32
self.action_dim = action_dim
self.device = torch.device(f"cuda:{gpu_id}" if (torch.cuda.is_available() and (gpu_id >= 0)) else "cpu")
self.per_tree = BinarySearchTree(max_len) if if_use_per else None
other_dim = 1 + 1 + self.action_dim
self.buf_other = torch.empty((max_len, other_dim), dtype=torch.float32, device=self.device)
buf_state_shape = (max_len, state_dim) if isinstance(state_dim, int) else (max_len, *state_dim)
self.buf_state = torch.empty(buf_state_shape, dtype=state_type, device=self.device)
def append_buffer(self, state, other): # CPU array to CPU array
self.buf_state[self.next_idx] = state
self.buf_other[self.next_idx] = other
if self.per_tree:
self.per_tree.update_id(self.next_idx)
self.next_idx += 1
if self.next_idx >= self.max_len:
self.if_full = True
self.next_idx = 0
def extend_buffer(self, state, other): # CPU array to CPU array
size = len(other)
next_idx = self.next_idx + size
if self.per_tree:
self.per_tree.update_ids(data_ids=np.arange(self.next_idx, next_idx) % self.max_len)
if next_idx > self.max_len:
self.buf_state[self.next_idx:self.max_len] = state[:self.max_len - self.next_idx]
self.buf_other[self.next_idx:self.max_len] = other[:self.max_len - self.next_idx]
self.if_full = True
next_idx = next_idx - self.max_len
self.buf_state[0:next_idx] = state[-next_idx:]
self.buf_other[0:next_idx] = other[-next_idx:]
else:
self.buf_state[self.next_idx:next_idx] = state
self.buf_other[self.next_idx:next_idx] = other
self.next_idx = next_idx
def sample_batch(self, batch_size) -> tuple:
"""randomly sample a batch of data for training
:int batch_size: the number of data in a batch for Stochastic Gradient Descent
:return torch.Tensor reward: reward.shape==(now_len, 1)
:return torch.Tensor mask: mask.shape ==(now_len, 1), mask = 0.0 if done else gamma
:return torch.Tensor action: action.shape==(now_len, action_dim)
:return torch.Tensor state: state.shape ==(now_len, state_dim)
:return torch.Tensor state: state.shape ==(now_len, state_dim), next state
"""
if self.per_tree:
beg = -self.max_len
end = (self.now_len - self.max_len) if (self.now_len < self.max_len) else None
indices, is_weights = self.per_tree.get_indices_is_weights(batch_size, beg, end)
r_m_a = self.buf_other[indices]
return (r_m_a[:, 0:1].type(torch.float32), # reward
r_m_a[:, 1:2].type(torch.float32), # mask
r_m_a[:, 2:].type(torch.float32), # action
self.buf_state[indices].type(torch.float32), # state
self.buf_state[indices + 1].type(torch.float32), # next state
torch.as_tensor(is_weights, dtype=torch.float32, device=self.device)) # important sampling weights
else:
indices = rd.randint(self.now_len - 1, size=batch_size)
r_m_a = self.buf_other[indices]
return (r_m_a[:, 0:1], # reward
r_m_a[:, 1:2], # mask
r_m_a[:, 2:], # action
self.buf_state[indices],
self.buf_state[indices + 1])
def sample_batch_one_step(self, batch_size) -> tuple:
if self.per_tree:
beg = -self.max_len
end = (self.now_len - self.max_len) if (self.now_len < self.max_len) else None
indices, is_weights = self.per_tree.get_indices_is_weights(batch_size, beg, end)
r_m_a = self.buf_other[indices]
return (r_m_a[:, 0:1].type(torch.float32), # reward
r_m_a[:, 2:].type(torch.float32), # action
self.buf_state[indices].type(torch.float32), # state
torch.as_tensor(is_weights, dtype=torch.float32, device=self.device)) # important sampling weights
else:
indices = rd.randint(self.now_len - 1, size=batch_size)
r_m_a = self.buf_other[indices]
return (r_m_a[:, 0:1], # reward
r_m_a[:, 2:], # action
self.buf_state[indices],)
def update_now_len(self):
"""update the a pointer `now_len`, which is the current data number of ReplayBuffer
"""
self.now_len = self.max_len if self.if_full else self.next_idx
def print_state_norm(self, neg_avg=None, div_std=None): # non-essential
"""print the state norm information: state_avg, state_std
We don't suggest to use running stat state.
We directly do normalization on state using the historical avg and std
eg. `state = (state + self.neg_state_avg) * self.div_state_std` in `PreprocessEnv.step_norm()`
neg_avg = -states.mean()
div_std = 1/(states.std()+1e-5) or 6/(states.max()-states.min())
:array neg_avg: neg_avg.shape=(state_dim)
:array div_std: div_std.shape=(state_dim)
"""
max_sample_size = 2 ** 14
'''check if pass'''
state_shape = self.buf_state.shape
if len(state_shape) > 2 or state_shape[1] > 64:
print(f"| print_state_norm(): state_dim: {state_shape} is too large to print its norm. ")
return None
'''sample state'''
indices = np.arange(self.now_len)
rd.shuffle(indices)
indices = indices[:max_sample_size] # len(indices) = min(self.now_len, max_sample_size)
batch_state = self.buf_state[indices]
'''compute state norm'''
if isinstance(batch_state, torch.Tensor):
batch_state = batch_state.cpu().data.numpy()
assert isinstance(batch_state, np.ndarray)
if batch_state.shape[1] > 64:
print(f"| _print_norm(): state_dim: {batch_state.shape[1]:.0f} is too large to print its norm. ")
return None
if np.isnan(batch_state).any(): # 2020-12-12
batch_state = np.nan_to_num(batch_state) # nan to 0
ary_avg = batch_state.mean(axis=0)
ary_std = batch_state.std(axis=0)
fix_std = ((np.max(batch_state, axis=0) - np.min(batch_state, axis=0)) / 6 + ary_std) / 2
if neg_avg is not None: # norm transfer
ary_avg = ary_avg - neg_avg / div_std
ary_std = fix_std / div_std
print(f"print_state_norm: state_avg, state_std (fixed)")
print(f"avg = np.{repr(ary_avg).replace('=float32', '=np.float32')}")
print(f"std = np.{repr(ary_std).replace('=float32', '=np.float32')}")
def td_error_update(self, td_error):
self.per_tree.td_error_update(td_error)
def save_or_load_history(self, cwd, if_save, buffer_id=0):
save_path = f"{cwd}/replay_{buffer_id}.npz"
if_load = None
if if_save:
self.update_now_len()
state_dim = self.buf_state.shape[1]
other_dim = self.buf_other.shape[1]
buf_state_data_type = np.float16 \
if self.buf_state.dtype in {np.float, np.float64, np.float32} \
else np.uint8
buf_state = np.empty((self.max_len, state_dim), dtype=buf_state_data_type)
buf_other = np.empty((self.max_len, other_dim), dtype=np.float16)
temp_len = self.max_len - self.now_len
buf_state[0:temp_len] = self.buf_state[self.now_len:self.max_len].detach().cpu().numpy()
buf_other[0:temp_len] = self.buf_other[self.now_len:self.max_len].detach().cpu().numpy()
buf_state[temp_len:] = self.buf_state[:self.now_len].detach().cpu().numpy()
buf_other[temp_len:] = self.buf_other[:self.now_len].detach().cpu().numpy()
np.savez_compressed(save_path, buf_state=buf_state, buf_other=buf_other)
print(f"| ReplayBuffer save in: {save_path}")
elif os.path.isfile(save_path):
buf_dict = np.load(save_path)
buf_state = buf_dict['buf_state']
buf_other = buf_dict['buf_other']
buf_state = torch.as_tensor(buf_state, dtype=torch.float32, device=self.device)
buf_other = torch.as_tensor(buf_other, dtype=torch.float32, device=self.device)
self.extend_buffer(buf_state, buf_other)
self.update_now_len()
print(f"| ReplayBuffer load: {save_path}")
if_load = True
else:
# print(f"| ReplayBuffer FileNotFound: {save_path}")
if_load = False
return if_load
class ReplayBufferMP:
def __init__(self, state_dim, action_dim, max_len, if_use_per, buffer_num, gpu_id):
"""Experience Replay Buffer for Multiple Processing
`int max_len` the max_len of ReplayBuffer, not the total len of ReplayBufferMP
`int worker_num` the rollout workers number
"""
self.now_len = 0
self.max_len = max_len
self.worker_num = buffer_num
buf_max_len = max_len // buffer_num
self.buffers = [ReplayBuffer(max_len=buf_max_len, state_dim=state_dim, action_dim=action_dim,
if_use_per=if_use_per, gpu_id=gpu_id)
for _ in range(buffer_num)]
def sample_batch(self, batch_size) -> list:
bs = batch_size // self.worker_num
list_items = [self.buffers[i].sample_batch(bs)
for i in range(self.worker_num)]
# list_items of reward, mask, action, state, next_state
# list_items of reward, mask, action, state, next_state, is_weights (PER)
list_items = list(map(list, zip(*list_items))) # 2D-list transpose
return [torch.cat(item, dim=0) for item in list_items]
def sample_batch_one_step(self, batch_size) -> list:
bs = batch_size // self.worker_num
list_items = [self.buffers[i].sample_batch_one_step(bs)
for i in range(self.worker_num)]
# list_items of reward, mask, action, state, next_state
# list_items of reward, mask, action, state, next_state, is_weights (PER)
list_items = list(map(list, zip(*list_items))) # 2D-list transpose
return [torch.cat(item, dim=0) for item in list_items]
def update_now_len(self):
self.now_len = 0
for buffer in self.buffers:
buffer.update_now_len()
self.now_len += buffer.now_len
def print_state_norm(self, neg_avg=None, div_std=None): # non-essential
# for buffer in self.l_buffer:
self.buffers[0].print_state_norm(neg_avg, div_std)
def td_error_update(self, td_error):
td_errors = td_error.view(self.worker_num, -1, 1)
for i in range(self.worker_num):
self.buffers[i].per_tree.td_error_update(td_errors[i])
def save_or_load_history(self, cwd, if_save):
for i in range(self.worker_num):
self.buffers[i].save_or_load_history(cwd, if_save, buffer_id=i)
class BinarySearchTree:
"""Binary Search Tree for PER
Contributor: Github GyChou, Github mississippiu
Reference: https://github.com/kaixindelele/DRLib/tree/main/algos/pytorch/td3_sp
Reference: https://github.com/jaromiru/AI-blog/blob/master/SumTree.py
"""
def __init__(self, memo_len):
self.memo_len = memo_len # replay buffer len
self.prob_ary = np.zeros((memo_len - 1) + memo_len) # parent_nodes_num + leaf_nodes_num
self.max_len = len(self.prob_ary)
self.now_len = self.memo_len - 1 # pointer
self.indices = None
self.depth = int(np.log2(self.max_len))
# PER. Prioritized Experience Replay. Section 4
# alpha, beta = 0.7, 0.5 for rank-based variant
# alpha, beta = 0.6, 0.4 for proportional variant
self.per_alpha = 0.6 # alpha = (Uniform:0, Greedy:1)
self.per_beta = 0.4 # beta = (PER:0, NotPER:1)
def update_id(self, data_id, prob=10): # 10 is max_prob
tree_id = data_id + self.memo_len - 1
if self.now_len == tree_id:
self.now_len += 1
delta = prob - self.prob_ary[tree_id]
self.prob_ary[tree_id] = prob
while tree_id != 0: # propagate the change through tree
tree_id = (tree_id - 1) // 2 # faster than the recursive loop
self.prob_ary[tree_id] += delta
def update_ids(self, data_ids, prob=10): # 10 is max_prob
ids = data_ids + self.memo_len - 1
self.now_len += (ids >= self.now_len).sum()
upper_step = self.depth - 1
self.prob_ary[ids] = prob # here, ids means the indices of given children (maybe the right ones or left ones)
p_ids = (ids - 1) // 2
while upper_step: # propagate the change through tree
ids = p_ids * 2 + 1 # in this while loop, ids means the indices of the left children
self.prob_ary[p_ids] = self.prob_ary[ids] + self.prob_ary[ids + 1]
p_ids = (p_ids - 1) // 2
upper_step -= 1
self.prob_ary[0] = self.prob_ary[1] + self.prob_ary[2]
# because we take depth-1 upper steps, ps_tree[0] need to be updated alone
def get_leaf_id(self, v):
"""Tree structure and array storage:
Tree index:
0 -> storing priority sum
| |
1 2
| | | |
3 4 5 6 -> storing priority for transitions
Array type for storing: [0, 1, 2, 3, 4, 5, 6]
"""
parent_idx = 0
while True:
l_idx = 2 * parent_idx + 1 # the leaf's left node
r_idx = l_idx + 1 # the leaf's right node
if l_idx >= (len(self.prob_ary)): # reach bottom, end search
leaf_idx = parent_idx
break
else: # downward search, always search for a higher priority node
if v <= self.prob_ary[l_idx]:
parent_idx = l_idx
else:
v -= self.prob_ary[l_idx]
parent_idx = r_idx
return min(leaf_idx, self.now_len - 2) # leaf_idx
def get_indices_is_weights(self, batch_size, beg, end):
self.per_beta = min(1., self.per_beta + 0.001)
# get random values for searching indices with proportional prioritization
values = (rd.rand(batch_size) + np.arange(batch_size)) * (self.prob_ary[0] / batch_size)
# get proportional prioritization
leaf_ids = np.array([self.get_leaf_id(v) for v in values])
self.indices = leaf_ids - (self.memo_len - 1)
prob_ary = self.prob_ary[leaf_ids] / self.prob_ary[beg:end].min()
is_weights = np.power(prob_ary, -self.per_beta) # important sampling weights
return self.indices, is_weights
def td_error_update(self, td_error): # td_error = (q-q).detach_().abs()
prob = td_error.squeeze().clamp(1e-6, 10).pow(self.per_alpha)
prob = prob.cpu().numpy()
self.update_ids(self.indices, prob)
================================================
FILE: elegantrl/run.py
================================================
import os
import sys
import time
import torch
import numpy as np
import numpy.random as rd
import multiprocessing as mp
from elegantrl.env import build_env, build_eval_env
from elegantrl.replay import ReplayBuffer, ReplayBufferMP
from elegantrl.evaluator import Evaluator
"""[ElegantRL.2021.11.08](https://github.com/AI4Finance-Foundation/ElegantRL)"""
class Arguments: # [ElegantRL.2021.10.21]
def __init__(self, env, agent):
self.env = env # the environment for training
self.env_num = getattr(env, 'env_num', 1) # env_num = 1. In vector env, env_num > 1.
self.max_step = getattr(env, 'max_step', None) # the max step of an episode
self.state_dim = getattr(env, 'state_dim', None) # vector dimension (feature number) of state
self.action_dim = getattr(env, 'action_dim', None) # vector dimension (feature number) of action
self.if_discrete = getattr(env, 'if_discrete', None) # discrete or continuous action space
self.target_return = getattr(env, 'target_return', None) # target average episode return
self.agent = agent # Deep Reinforcement Learning algorithm
self.if_off_policy = agent.if_off_policy # agent is on-policy or off-policy
if self.if_off_policy: # off-policy
self.net_dim = 2 ** 8 # the network width
self.max_memo = 2 ** 21 # capacity of replay buffer
self.batch_size = self.net_dim # num of transitions sampled from replay buffer.
self.target_step = 2 ** 10 # repeatedly update network to keep critic's loss small
self.repeat_times = 2 ** 0 # collect target_step, then update network
self.if_per_or_gae = False # use PER (Prioritized Experience Replay) for sparse reward
else: # on-policy
self.net_dim = 2 ** 9 # the network width
self.max_memo = 2 ** 12 # capacity of replay buffer
self.batch_size = self.net_dim * 2 # num of transitions sampled from replay buffer.
self.target_step = self.max_memo # repeatedly update network to keep critic's loss small
self.repeat_times = 2 ** 3 # collect target_step, then update network
self.if_per_or_gae = False # use PER: GAE (Generalized Advantage Estimation) for sparse reward
'''Arguments for training'''
self.gamma = 0.99 # discount factor of future rewards
self.reward_scale = 2 ** 0 # an approximate target reward usually be closed to 256
self.learning_rate = 2 ** -15 # 2 ** -14 ~= 3e-5
self.soft_update_tau = 2 ** -8 # 2 ** -8 ~= 5e-3
'''Arguments for device'''
self.worker_num = 2 # rollout workers number pre GPU (adjust it to get high GPU usage)
self.thread_num = 8 # cpu_num for evaluate model, torch.set_num_threads(self.num_threads)
self.random_seed = 0 # initialize random seed in self.init_before_training()
self.learner_gpus = (0,) # for example: os.environ['CUDA_VISIBLE_DEVICES'] = '0, 2,'
self.workers_gpus = self.learner_gpus # for GPU_VectorEnv (such as isaac gym)
'''Arguments for evaluate and save'''
self.cwd = None # the directory path to save the model
self.if_remove = True # remove the cwd folder? (True, False, None:ask me)
self.break_step = +np.inf # break training after 'total_step > break_step'
self.if_allow_break = True # allow break training when reach goal (early termination)
self.eval_env = None # the environment for evaluating. None means set automatically.
self.eval_gap = 2 ** 8 # evaluate the agent per eval_gap seconds
self.eval_times1 = 2 ** 2 # number of times that get episode return in first
self.eval_times2 = 2 ** 4 # number of times that get episode return in second
self.eval_gpu_id = None # -1 means use cpu, >=0 means use GPU, None means set as learner_gpus[0]
self.if_overwrite = True # Save policy networks with different episode return or overwrite
def init_before_training(self):
np.random.seed(self.random_seed)
torch.manual_seed(self.random_seed)
torch.set_num_threads(self.thread_num)
torch.set_default_dtype(torch.float32)
'''env'''
assert isinstance(self.env_num, int)
assert isinstance(self.max_step, int)
assert isinstance(self.state_dim, int) or isinstance(self.state_dim, tuple)
assert isinstance(self.action_dim, int) or isinstance(self.action_dim, tuple)
assert isinstance(self.if_discrete, bool)
assert isinstance(self.target_return, int) or isinstance(self.target_return, float)
'''agent'''
assert hasattr(self.agent, 'init')
assert hasattr(self.agent, 'update_net')
assert hasattr(self.agent, 'explore_env')
assert hasattr(self.agent, 'select_actions')
'''auto set'''
if self.cwd is None:
agent_name = self.agent.__class__.__name__
env_name = getattr(self.env, 'env_name', self.env)
self.cwd = f'./{agent_name}_{env_name}_{self.learner_gpus}'
if self.eval_gpu_id is None:
self.eval_gpu_id = self.learner_gpus[0]
'''remove history'''
if self.if_remove is None:
self.if_remove = bool(input(f"| PRESS 'y' to REMOVE: {self.cwd}? ") == 'y')
elif self.if_remove:
import shutil
shutil.rmtree(self.cwd, ignore_errors=True)
print(f"| Remove cwd: {self.cwd}")
else:
print(f"| Keep cwd: {self.cwd}")
os.makedirs(self.cwd, exist_ok=True)
if not self.if_overwrite:
os.makedirs('%s/best/'%self.cwd, exist_ok=True)
'''single processing training'''
def train_and_evaluate(args, learner_id=0):
args.init_before_training() # necessary!
'''init: Agent'''
agent = args.agent
agent.init(net_dim=args.net_dim, state_dim=args.state_dim, action_dim=args.action_dim,
gamma=args.gamma, reward_scale=args.reward_scale,
learning_rate=args.learning_rate, if_per_or_gae=args.if_per_or_gae,
env_num=args.env_num, gpu_id=args.learner_gpus[learner_id], )
agent.save_or_load_agent(args.cwd, if_save=False)
env = build_env(env=args.env, if_print=False,
env_num=args.env_num, device_id=args.eval_gpu_id, args=args, )
if env.env_num == 1:
agent.states = [env.reset(), ]
assert isinstance(agent.states[0], np.ndarray)
assert agent.states[0].shape in {(env.state_dim,), env.state_dim}
else:
agent.states = env.reset()
assert isinstance(agent.states, torch.Tensor)
assert agent.states.shape == (env.env_num, env.state_dim)
'''init Evaluator'''
eval_env = build_eval_env(args.eval_env, args.env, args.env_num, args.eval_gpu_id, args)
evaluator = Evaluator(cwd=args.cwd, agent_id=0,
eval_env=eval_env, eval_gap=args.eval_gap,
eval_times1=args.eval_times1, eval_times2=args.eval_times2,
target_return=args.target_return, if_overwrite=args.if_overwrite)
evaluator.save_or_load_recoder(if_save=False)
'''init ReplayBuffer'''
if args.if_off_policy:
buffer = ReplayBuffer(max_len=args.max_memo, state_dim=env.state_dim,
action_dim=1 if env.if_discrete else env.action_dim,
if_use_per=args.if_per_or_gae, gpu_id=args.learner_gpus[learner_id])
buffer.save_or_load_history(args.cwd, if_save=False)
def update_buffer(_traj_list):
ten_state, ten_other = _traj_list[0]
buffer.extend_buffer(ten_state, ten_other)
_steps, _r_exp = get_step_r_exp(ten_reward=ten_other[0]) # other = (reward, mask, action)
return _steps, _r_exp
else:
buffer = list()
def update_buffer(_traj_list):
(ten_state, ten_reward, ten_mask, ten_action, ten_noise) = _traj_list[0]
buffer[:] = (ten_state.squeeze(1),
ten_reward,
ten_mask,
ten_action.squeeze(1),
ten_noise.squeeze(1))
_step, _r_exp = get_step_r_exp(ten_reward=buffer[1])
return _step, _r_exp
"""start training"""
cwd = args.cwd
gamma = args.gamma
break_step = args.break_step
batch_size = args.batch_size
target_step = args.target_step
repeat_times = args.repeat_times
if_allow_break = args.if_allow_break
soft_update_tau = args.soft_update_tau
if_overwrite = args.if_overwrite
del args
'''init ReplayBuffer after training start'''
if agent.if_off_policy:
if_load = buffer.save_or_load_history(cwd, if_save=False)
if not if_load:
traj_list = agent.explore_env(env, target_step)
steps, r_exp = update_buffer(traj_list)
evaluator.total_step += steps
'''start training loop'''
if_train = True
while if_train:
with torch.no_grad():
traj_list = agent.explore_env(env, target_step)
steps, r_exp = update_buffer(traj_list)
logging_tuple = agent.update_net(buffer, batch_size, repeat_times, soft_update_tau)
with torch.no_grad():
temp = evaluator.evaluate_and_save(agent.act, steps, r_exp, logging_tuple)
if_reach_goal, if_save = temp
if if_save and not if_overwrite:
agent.save_or_load_agent('%s/best/'%(cwd), if_save=True)
buffer.save_or_load_history('%s/best/'%(cwd), if_save=True) if agent.if_off_policy else None
if_train = not ((if_allow_break and if_reach_goal)
or evaluator.total_step > break_step
or os.path.exists(f'{cwd}/stop'))
print(f'| UsedTime: {time.time() - evaluator.start_time:>7.0f} | SavedDir: {cwd}')
agent.save_or_load_agent(cwd, if_save=True)
buffer.save_or_load_history(cwd, if_save=True) if agent.if_off_policy else None
evaluator.save_or_load_recoder(if_save=True)
def get_step_r_exp(ten_reward):
return len(ten_reward), ten_reward.mean().item()
'''multiple processing training'''
def train_and_evaluate_mp(args, agent_id=0):
args.init_before_training() # necessary!
process = list()
mp.set_start_method(method='spawn', force=True) # force all the multiprocessing to 'spawn' methods
'''learner'''
learner_num = len(args.learner_gpus)
learner_pipe = PipeLearner(learner_num)
for learner_id in range(learner_num):
'''evaluator'''
if learner_id == learner_num - 1:
evaluator_pipe = PipeEvaluator()
process.append(mp.Process(target=evaluator_pipe.run, args=(args, agent_id)))
else:
evaluator_pipe = None
'''explorer'''
worker_pipe = PipeWorker(args.env_num, args.worker_num)
for worker_id in range(args.worker_num):
# if args.env_num == 1:
# env_pipe = None
# else:
# env_pipe = PipeVectorEnv(args)
# process.extend(env_pipe.process)
env_pipe = None
process.append(mp.Process(target=worker_pipe.run, args=(args, env_pipe, worker_id, learner_id)))
process.append(mp.Process(target=learner_pipe.run, args=(args, evaluator_pipe, worker_pipe, learner_id)))
[(p.start(), time.sleep(0.1)) for p in process]
process[-1].join()
process_safely_terminate(process)
class PipeWorker:
def __init__(self, env_num, worker_num):
self.env_num = env_num
self.worker_num = worker_num
self.pipes = [mp.Pipe() for _ in range(worker_num)]
self.pipe1s = [pipe[1] for pipe in self.pipes]
def explore0(self, agent):
act_dict = agent.act.state_dict()
for worker_id in range(self.worker_num):
self.pipe1s[worker_id].send(act_dict)
traj_lists = [pipe1.recv() for pipe1 in self.pipe1s]
return traj_lists
def explore(self, agent):
act_dict = agent.act.state_dict()
if sys.platform == 'win32': # Avoid CUDA runtime error (801)
# Python3.9< multiprocessing can't send torch.tensor_gpu in WinOS. So I send torch.tensor_cpu
for key, value in act_dict.items():
act_dict[key] = value.to(torch.device('cpu'))
for worker_id in range(self.worker_num):
self.pipe1s[worker_id].send(act_dict)
traj_lists = [pipe1.recv() for pipe1 in self.pipe1s]
return traj_lists
def run(self, args, _comm_env, worker_id, learner_id): # not elegant: comm_env
# print(f'| os.getpid()={os.getpid()} PipeExplore.run {learner_id}')
env = build_env(env=args.env, if_print=False,
env_num=args.env_num, device_id=args.workers_gpus[learner_id], args=args, )
'''init Agent'''
agent = args.agent
agent.init(net_dim=args.net_dim, state_dim=args.state_dim, action_dim=args.action_dim,
gamma=args.gamma, reward_scale=args.reward_scale,
learning_rate=args.learning_rate, if_per_or_gae=args.if_per_or_gae,
env_num=args.env_num, gpu_id=args.learner_gpus[learner_id], )
if args.env_num == 1:
agent.states = [env.reset(), ]
else:
agent.states = env.reset() # VecEnv
'''loop'''
target_step = args.target_step
del args
with torch.no_grad():
while True:
act_dict = self.pipes[worker_id][0].recv()
if sys.platform == 'win32': # todo: not elegant. YonV1943. Avoid CUDA runtime error (801)
# Python3.9< multiprocessing can't send torch.tensor_gpu in WinOS. So I send torch.tensor_cpu
for key, value in act_dict.items():
act_dict[key] = value.to(agent.device)
agent.act.load_state_dict(act_dict)
trajectory = agent.explore_env(env, target_step)
if sys.platform == 'win32': # todo: not elegant. YonV1943. Avoid CUDA runtime error (801)
# Python3.9< multiprocessing can't send torch.tensor_gpu in WinOS. So I send torch.tensor_cpu
trajectory = [[item.to(torch.device('cpu'))
for item in item_list]
for item_list in trajectory]
self.pipes[worker_id][0].send(trajectory)
class PipeLearner:
def __init__(self, learner_num):
self.learner_num = learner_num
self.round_num = int(np.log2(learner_num))
self.pipes = [mp.Pipe() for _ in range(learner_num)]
pipes = [mp.Pipe() for _ in range(learner_num)]
self.pipe0s = [pipe[0] for pipe in pipes]
self.pipe1s = [pipe[1] for pipe in pipes]
self.device_list = [torch.device(f'cuda:{i}') for i in range(learner_num)]
if learner_num == 1:
self.idx_l = None
elif learner_num == 2:
self.idx_l = [(1,), (0,), ]
elif learner_num == 4:
self.idx_l = [(1, 2), (0, 3),
(3, 0), (2, 1), ]
elif learner_num == 8:
self.idx_l = [(1, 2, 4), (0, 3, 5),
(3, 0, 6), (2, 1, 7),
(5, 6, 0), (4, 7, 1),
(7, 4, 2), (6, 5, 3), ]
else:
print(f"| LearnerPipe, ERROR: learner_num {learner_num} should in (1, 2, 4, 8)")
exit()
def comm_data(self, data, learner_id, round_id):
if round_id == -1:
learner_jd = self.idx_l[learner_id][round_id]
self.pipes[learner_jd][0].send(data)
return self.pipes[learner_id][1].recv()
else:
learner_jd = self.idx_l[learner_id][round_id]
self.pipe0s[learner_jd].send(data)
return self.pipe1s[learner_id].recv()
def comm_network_optim(self, agent, learner_id):
device = self.device_list[learner_id]
for round_id in range(self.round_num):
data = get_comm_data(agent)
data = self.comm_data(data, learner_id, round_id)
if data:
avg_update_net(agent.act, data[0], device)
avg_update_optim(agent.act_optim, data[1], device) if data[1] else None
avg_update_net(agent.cri, data[2], device) if data[2] else None
avg_update_optim(agent.cri_optim, data[3], device)
avg_update_net(agent.act_target, data[4], device) if agent.if_use_act_target else None
avg_update_net(agent.cri_target, data[5], device) if agent.if_use_cri_target else None
def run0(self, args, comm_eva, comm_exp, learner_id=0):
# print(f'| os.getpid()={os.getpid()} PipeLearn.run, {learner_id}')
pass
'''init Agent'''
agent = args.agent
agent.init(net_dim=args.net_dim, state_dim=args.state_dim, action_dim=args.action_dim,
gamma=args.gamma, reward_scale=args.reward_scale,
learning_rate=args.learning_rate, if_per_or_gae=args.if_per_or_gae,
env_num=args.env_num, gpu_id=args.learner_gpus[learner_id], )
agent.save_or_load_agent(args.cwd, if_save=False)
'''init ReplayBuffer'''
if agent.if_off_policy:
buffer_num = args.worker_num * args.env_num
if self.learner_num > 1:
buffer_num *= 2
buffer = ReplayBufferMP(max_len=args.max_memo, state_dim=args.state_dim,
action_dim=1 if args.if_discrete else args.action_dim,
if_use_per=args.if_per_or_gae,
buffer_num=buffer_num, gpu_id=args.learner_gpus[learner_id])
buffer.save_or_load_history(args.cwd, if_save=False)
def update_buffer(_traj_list):
step_sum = 0
r_exp_sum = 0
for buffer_i, (ten_state, ten_other) in enumerate(_traj_list):
buffer.buffers[buffer_i].extend_buffer(ten_state, ten_other)
step_r_exp = get_step_r_exp(ten_reward=ten_other[:, 0]) # other = (reward, mask, action)
step_sum += step_r_exp[0]
r_exp_sum += step_r_exp[1]
return step_sum, r_exp_sum / len(_traj_list)
else:
buffer = list()
def update_buffer(_traj_list):
_traj_list = list(map(list, zip(*_traj_list)))
_traj_list = [torch.cat(t, dim=0) for t in _traj_list]
(ten_state, ten_reward, ten_mask, ten_action, ten_noise) = _traj_list
buffer[:] = (ten_state.squeeze(1),
ten_reward,
ten_mask,
ten_action.squeeze(1),
ten_noise.squeeze(1))
_step, _r_exp = get_step_r_exp(ten_reward=buffer[1])
return _step, _r_exp
'''start training'''
cwd = args.cwd
batch_size = args.batch_size
repeat_times = args.repeat_times
soft_update_tau = args.soft_update_tau
del args
if_train = True
while if_train:
traj_lists = comm_exp.explore(agent)
if self.learner_num > 1:
data = self.comm_data(traj_lists, learner_id, round_id=-1)
traj_lists.extend(data)
traj_list = sum(traj_lists, list())
steps, r_exp = update_buffer(traj_list)
del traj_lists
logging_tuple = agent.update_net(buffer, batch_size, repeat_times, soft_update_tau)
if self.learner_num > 1:
self.comm_network_optim(agent, learner_id)
if comm_eva:
if_train, if_save = comm_eva.evaluate_and_save_mp(agent.act, steps, r_exp, logging_tuple)
agent.save_or_load_agent(cwd, if_save=True)
if agent.if_off_policy:
print(f"| LearnerPipe.run: ReplayBuffer saving in {cwd}")
buffer.save_or_load_history(cwd, if_save=True)
def run(self, args, comm_eva, comm_exp, learner_id=0):
# print(f'| os.getpid()={os.getpid()} PipeLearn.run, {learner_id}')
pass
'''init Agent'''
agent = args.agent
agent.init(net_dim=args.net_dim, state_dim=args.state_dim, action_dim=args.action_dim,
gamma=args.gamma, reward_scale=args.reward_scale,
learning_rate=args.learning_rate, if_per_or_gae=args.if_per_or_gae,
env_num=args.env_num, gpu_id=args.learner_gpus[learner_id], )
agent.save_or_load_agent(args.cwd, if_save=False)
'''init ReplayBuffer'''
if agent.if_off_policy:
buffer_num = args.worker_num * args.env_num
if self.learner_num > 1:
buffer_num *= 2
buffer = ReplayBufferMP(max_len=args.max_memo, state_dim=args.state_dim,
action_dim=1 if args.if_discrete else args.action_dim,
if_use_per=args.if_per_or_gae,
buffer_num=buffer_num, gpu_id=args.learner_gpus[learner_id])
buffer.save_or_load_history(args.cwd, if_save=False)
def update_buffer(_traj_list):
step_sum = 0
r_exp_sum = 0
for buffer_i, (ten_state, ten_other) in enumerate(_traj_list):
buffer.buffers[buffer_i].extend_buffer(ten_state, ten_other)
step_r_exp = get_step_r_exp(ten_reward=ten_other[:, 0]) # other = (reward, mask, action)
step_sum += step_r_exp[0]
r_exp_sum += step_r_exp[1]
return step_sum, r_exp_sum / len(_traj_list)
else:
buffer = list()
def update_buffer(_traj_list):
_traj_list = list(map(list, zip(*_traj_list)))
_traj_list = [torch.cat(t, dim=0) for t in _traj_list]
(ten_state, ten_reward, ten_mask, ten_action, ten_noise) = _traj_list
buffer[:] = (ten_state.squeeze(1),
ten_reward,
ten_mask,
ten_action.squeeze(1),
ten_noise.squeeze(1))
_step, _r_exp = get_step_r_exp(ten_reward=buffer[1])
return _step, _r_exp
'''start training'''
cwd = args.cwd
batch_size = args.batch_size
repeat_times = args.repeat_times
soft_update_tau = args.soft_update_tau
del args
if_train = True
while if_train:
traj_lists = comm_exp.explore(agent)
if self.learner_num > 1:
data = self.comm_data(traj_lists, learner_id, round_id=-1)
traj_lists.extend(data)
traj_list = sum(traj_lists, list())
if sys.platform == 'win32': # Avoid CUDA runtime error (801)
# Python3.9< multiprocessing can't send torch.tensor_gpu in WinOS. So I send torch.tensor_cpu
traj_list = [[item.to(torch.device('cpu'))
for item in item_list]
for item_list in traj_list]
steps, r_exp = update_buffer(traj_list)
del traj_lists
logging_tuple = agent.update_net(buffer, batch_size, repeat_times, soft_update_tau)
if self.learner_num > 1:
self.comm_network_optim(agent, learner_id)
if comm_eva:
if_train, if_save = comm_eva.evaluate_and_save_mp(agent.act, steps, r_exp, logging_tuple)
agent.save_or_load_agent(cwd, if_save=True)
if agent.if_off_policy:
print(f"| LearnerPipe.run: ReplayBuffer saving in {cwd}")
buffer.save_or_load_history(cwd, if_save=True)
class PipeEvaluator: # [ElegantRL.10.21]
def __init__(self):
super().__init__()
self.pipe0, self.pipe1 = mp.Pipe()
def evaluate_and_save_mp(self, agent_act, steps, r_exp, logging_tuple):
if self.pipe1.poll(): # if_evaluator_idle
if_train, if_save = self.pipe1.recv()
act_cpu_dict = {k: v.cpu() for k, v in agent_act.state_dict().items()}
else:
if_train, if_save = True, False
act_cpu_dict = None
self.pipe1.send((act_cpu_dict, steps, r_exp, logging_tuple))
return if_train, if_save
def run(self, args, _learner_id):
# print(f'| os.getpid()={os.getpid()} PipeEvaluate.run {agent_id}')
pass
'''init: Agent'''
agent = args.agent
agent.init(net_dim=args.net_dim, state_dim=args.state_dim, action_dim=args.action_dim,
gamma=args.gamma, reward_scale=args.reward_scale,
learning_rate=args.learning_rate, if_per_or_gae=args.if_per_or_gae,
env_num=args.env_num, gpu_id=args.eval_gpu_id, )
agent.save_or_load_agent(args.cwd, if_save=False)
act = agent.act
[setattr(param, 'requires_grad', False) for param in agent.act.parameters()]
del agent
'''init Evaluator'''
eval_env = build_eval_env(args.eval_env, args.env, args.env_num, args.eval_gpu_id, args)
evaluator = Evaluator(cwd=args.cwd, agent_id=0,
eval_env=eval_env, eval_gap=args.eval_gap,
eval_times1=args.eval_times1, eval_times2=args.eval_times2,
target_return=args.target_return, if_overwrite=args.if_overwrite)
evaluator.save_or_load_recoder(if_save=False)
'''loop'''
cwd = args.cwd
break_step = args.break_step
if_allow_break = args.if_allow_break
del args
if_save = False
if_train = True
if_reach_goal = False
with torch.no_grad():
while if_train:
act_dict, steps, r_exp, logging_tuple = self.pipe0.recv()
if act_dict:
act.load_state_dict(act_dict)
if_reach_goal, if_save = evaluator.evaluate_and_save(act, steps, r_exp, logging_tuple)
else:
evaluator.total_step += steps
if_train = not ((if_allow_break and if_reach_goal)
or evaluator.total_step > break_step
or os.path.exists(f'{cwd}/stop'))
self.pipe0.send((if_train, if_save))
print(f'| UsedTime: {time.time() - evaluator.start_time:>7.0f} | SavedDir: {cwd}')
evaluator.save_or_load_recoder(if_save=True)
# class PipeVectorEnv:
# def __init__(self, args):
# self.env_num = args.env_num
# self.pipes = [mp.Pipe() for _ in range(self.env_num)]
# self.pipe0s = [pipe[0] for pipe in self.pipes]
#
# env = build_env(args.eval_env)
# self.max_step = env.max_step
# self.env_name = env.env_name
# self.state_dim = env.state_dim
# self.action_dim = env.action_dim
# self.action_max = env.action_max
# self.if_discrete = env.if_discrete
# self.target_return = env.target_return
# del env
#
# self.process = list()
# for env_id in range(args.env_num):
# self.process.append(mp.Process(target=self.run, args=(args, env_id)))
# args.random_seed += 1 # set different for each env
# # [p.start() for p in self.process]
#
# def reset(self):
# vec_state = [pipe0.recv() for pipe0 in self.pipe0s]
# return vec_state
#
# def step(self, vec_action): # pipe0_step
# for i in range(self.env_num):
# self.pipe0s[i].send(vec_action[i])
# return [pipe0.recv() for pipe0 in self.pipe0s] # list of (state, reward, done)
#
# def run(self, args, env_id):
# np.random.seed(args.random_seed)
#
# env = build_env(args.eval_env, if_print=False)
# pipe1 = self.pipes[env_id][1]
# del args
#
# state = env.reset()
# pipe1.send(state)
#
# while True:
# action = pipe1.recv()
# state, reward, done, _ = env.step(action)
# pipe1.send((env.reset() if done else state, reward, done))
#
# # def check(self):
# # vec_state = self.reset()
# # ten_state = np.array(vec_state)
# # print(ten_state.shape)
# #
# # vec_action = np.array(((0.0, 1.0, 0.0),
# # (0.0, 0.5, 0.0),
# # (0.0, 0.1, 0.0),))[:self.env_num]
# # assert self.env_num <= 3
# #
# # trajectory_list = list()
# # for _ in range(8):
# # s_r_d_list = self.step(vec_action)
# # ten_state = np.array([s_r_d[0] for s_r_d in s_r_d_list])
# # print(ten_state.shape)
# # trajectory_list.append(s_r_d_list)
# #
# # trajectory_list = list(map(list, zip(*trajectory_list))) # 2D-list transpose
# # print('| shape of trajectory_list:', len(trajectory_list), len(trajectory_list[0]))
def get_comm_data(agent):
act = list(agent.act.parameters())
cri_optim = get_optim_parameters(agent.cri_optim)
if agent.cri is agent.act:
cri = None
act_optim = None
else:
cri = list(agent.cri.parameters())
act_optim = get_optim_parameters(agent.act_optim)
act_target = list(agent.act_target.parameters()) if agent.if_use_act_target else None
cri_target = list(agent.cri_target.parameters()) if agent.if_use_cri_target else None
return act, act_optim, cri, cri_optim, act_target, cri_target # data
"""Utils"""
def get_num_learner(visible_gpu):
assert isinstance(visible_gpu, str) # visible_gpu may in {'0', '1', '1,', '1,2', '1,2,'}
visible_gpu = eval(visible_gpu)
num_learner = 1 if isinstance(visible_gpu, int) else len(visible_gpu)
return num_learner
def process_safely_terminate(process):
for p in process:
try:
p.kill()
except OSError as e:
print(e)
pass
def get_optim_parameters(optim): # for avg_update_optim()
params_list = list()
for params_dict in optim.state_dict()['state'].values():
params_list.extend([t for t in params_dict.values() if isinstance(t, torch.Tensor)])
return params_list
def avg_update_optim(dst_optim, src_optim_param, device):
for dst, src in zip(get_optim_parameters(dst_optim), src_optim_param):
dst.data.copy_((dst.data + src.data.to(device)) * 0.5)
# dst.data.copy_(src.data * tau + dst.data * (1 - tau))
def avg_update_net(dst_net, src_net_param, device):
for dst, src in zip(dst_net.parameters(), src_net_param):
dst.data.copy_((dst.data + src.data.to(device)) * 0.5)
================================================
FILE: envs.py
================================================
from gym import Env
from gym.spaces import Box, Space
from features import Feature
from stoppers import Stopper
from assessments import Assessment
from wtpy.apps import WtBtAnalyst
from wtpy.WtBtEngine import WtBtEngine
from strategies import StateTransfer, EngineType
from multiprocessing import Pipe, Process
from os import getpid
# 一个进程只能有一个env
class WtEnv(Env):
TRAINER = 1
EVALUATOR = 2
DEBUGGER = 3
def __init__(self,
strategy: StateTransfer,
stopper: Stopper,
feature: Feature,
assessment: Assessment,
time_range: tuple,
slippage: int = 0,
id: int = getpid(),
mode=1,
):
self.reward_range
if mode == 3: # 调试模式
self._log_: str = './config/03research/log_debugger.json'
self._dump_: bool = True
self._mode_: str = 'WtDebugger'
elif mode == 2: # 评估模式
self._log_: str = './config/03research/log_evaluator.json'
self._dump_: bool = True
self._mode_: str = 'WtEvaluator'
else: # 训练模式
self._log_: str = './config/03research/log_trainer.json'
self._dump_: bool = False
self._mode_: str = 'WtTrainer'
self._id_: int = id
self._iter_: int = 0
self._run_: bool = False
self.__strategy__ = strategy
self._et_ = self.__strategy__.EngineType()
self.__stopper__: Stopper = stopper
self.__slippage__: int = slippage
self.__feature__: Feature = feature
self.observation_space: Box = Box(**self.__feature__.observation)
self.action_space: Space = self.__strategy__.Action(
len(self.__feature__.securities))
self._assessment_: Assessment = assessment
self.__time_range__ = time_range
def _debug_(self):
pass
def __step__(self):
finished = not self._cb_step_()
if self._assessment_.done or finished:
self._assessment_.finish()
self._debug_()
self.close()
# if self._dump_:
# self.analyst(self._iter_)
def close(self):
if self._run_ and hasattr(self, '_engine_'):
self._engine_.stop_backtest()
self._run_ = False
def reset(self):
self.close()
time_start, time_end = self.__time_range__[self._iter_%len(self.__time_range__)]
self._iter_ += 1
if not hasattr(self, '_engine_'):
# 创建一个运行环境
self._engine_: WtBtEngine = WtBtEngine(
eType=self._et_,
logCfg=self._log_,
)
if self._et_ == EngineType.ET_CTA:
self._engine_.init(
'./config/01commom/',
'./config/03research/cta.json')
self._cb_step_ = self._engine_.cta_step
elif self._et_ == EngineType.ET_HFT:
self._engine_.init(
'./config/01commom/',
'./config/03research/hft.json')
self._cb_step_ = self._engine_.hft_step
else:
raise AttributeError
self._engine_.configBacktest(time_start, time_end)
self._engine_.commitBTConfig()
else:
self._engine_.set_time_range(time_start, time_end)
# 重置奖励
self._assessment_.reset()
# 创建一个策略并加入运行环境
self._strategy_: StateTransfer = self.__strategy__(
name=self._name_(self._iter_),
feature=self.__feature__,
stopper=self.__stopper__,
assessment=self._assessment_,
)
# 设置策略的时候一定要安装钩子
if self._et_ == EngineType.ET_CTA:
self._engine_.set_cta_strategy(
self._strategy_, slippage=self.__slippage__, hook=True, persistData=self._dump_)
elif self._et_ == EngineType.ET_HFT:
self._engine_.set_hft_strategy(self._strategy_, hook=True)
else:
raise AttributeError
# 回测一定要异步运行
self._engine_.run_backtest(bAsync=True, bNeedDump=self._dump_)
self._run_ = True
self.__step__()
return self.__feature__.obs
def step(self, action):
assert hasattr(self, '_engine_')
self._strategy_.setAction(action)
self._cb_step_()
self.__step__()
return self.__feature__.obs, self._assessment_.reward, self._assessment_.done, {}
@property
def assets(self):
return self._assessment_.curr_assets
def analyst(self, iter: int):
name = self._name_(iter)
analyst = WtBtAnalyst()
folder = "./outputs_bt/%s/" % name
analyst.add_strategy(
name, folder=folder, init_capital=self._assessment_._init_assets_, rf=0.02, annual_trading_days=240)
try:
analyst.run_new('%s/PnLAnalyzing.xlsx' % folder)
except:
analyst.run('%s/PnLAnalyzing.xlsx' % folder)
def analysts(self):
for iter in range(1, self._iter_+1):
self.analysis(iter)
def _name_(self, iter):
time_start, time_end = self.__time_range__[(iter-1)%len(self.__time_range__)]
return '%s%s_%s_%s_%s-%s' % (self._mode_, self._id_, self.__strategy__.Name(), iter, str(time_start)[:8], str(time_end)[:8])
def __del__(self):
if hasattr(self, '_engine_'):
self._engine_.release_backtest()
def __sub_process_worker__(pipe: Pipe, _cmd_, _attr_, cli, kwargs):
env = cli(**kwargs)
while True:
cmd, kwargs = pipe.recv()
if cmd in _cmd_:
if cmd == 'stop':
pipe.send(True)
pipe.close()
break
call = getattr(env, cmd)
if kwargs:
# print(cmd, kwargs)
pipe.send(call(**kwargs))
else:
pipe.send(call())
elif cmd in _attr_:
pipe.send(getattr(env, cmd))
else:
pipe.send('unknow %s' % cmd)
class WtSubProcessEnv(Env):
_cmd_ = ('reset', 'step', 'close', 'stop')
_attr_ = ('reward_range', 'metadata',
'observation_space', 'action_space', 'assets')
def __init__(self, cli, **kwargs):
self._pipe_, pipe = Pipe()
self._process_ = Process(
target=__sub_process_worker__,
args=(pipe, self._cmd_, self._attr_, cli, kwargs),
daemon=True
)
self._process_.start()
def __do__(self, cmd, **kwargs):
self._pipe_.send((cmd, kwargs))
return self._pipe_.recv()
@property
def metadata(self):
return self.__do__('metadata')
@property
def reward_range(self):
return self.__do__('reward_range')
@property
def observation_space(self):
return self.__do__('observation_space')
@property
def action_space(self):
return self.__do__('action_space')
@property
def assets(self):
return self.__do__('assets')
def reset(self):
return self.__do__('reset')
def step(self, action):
# print(type(action))
return self.__do__('step', action=action)
def close(self):
return self.__do__('close')
def __del__(self):
self.__do__('stop')
self._process_.join()
self._process_.close()
================================================
FILE: envs_simple_cta.py
================================================
from features import Indicator
from assessments import SimpleAssessment
from stoppers import SimpleStopper
from strategies import SimpleCTA
from envs import WtEnv, WtSubProcessEnv
class SimpleCTAEnv(WtEnv):
def __init__(self,
time_range: tuple,
slippage: int = 0,
mode: int = 1
):
assets = 180000
# 角色:数据研究人员、强化学习研究人员、策略研究人员
# 原则:每个角色的分工模拟交易机构做隔离
# 特征工程组件, 滚动窗口=2,根据特征工程自动生成强化学习需要的observation
# 特征工程的因子生成绝大多数情况下(舆情因子、周期因子)不是由env负责的,所以尽量使用特征工程组件而不要在env中定义因子
# 特征工程的因子定义和生成,主要使用者是数据研究人员
# 特征工程的因子后处理,主要使用者是强化学习研究人员
feature: Indicator = Indicator(
code='DCE.c.HOT', period=Indicator.M5, roll=1, assets=assets) # 每一个特征工程必须指定一个主要标的
# 按需添加其他标的
feature.addSecurity(code='DCE.cs.HOT')
feature.addSecurity(code='DCE.m.HOT')
# feature.addSecurity(code='CZCE.RM.HOT')
# feature.addSecurity(code='CZCE.JR.HOT')
# feature.addSecurity(code='CZCE.TA.HOT')
# feature.addSecurity(code='DCE.jd.HOT')
# feature.addSecurity(code='SHFE.rb.HOT')
# feature.addSecurity(code='SHFE.hc.HOT')
# feature.addSecurity(code='SHFE.bu.HOT')
# feature.addSecurity(code='SHFE.fu.HOT')
# feature.addSecurity(code='SHFE.ni.HOT')
# 分别使用5分钟、15分钟、日线建立多周期因子
for period in (feature.M5, ): # feature.M5, feature.M10,
feature.price(period)
feature.volume(period)
# feature.roc(period)
feature.bollinger(period) # 标准差通道
# feature.sar(period)
# feature.trange(period) # 波动率
feature.macd(period) # 双均线强度
# feature.rsi(period)
# feature.dx(period)
# feature.obv(period)
feature.kdj(period)
# 除上述特征,特征工程组件会自动加上 "开仓的最大浮盈、开仓的最大亏损、开仓的浮动盈亏、当前持仓数"4列,如果没有持仓则全部为0
# 止盈止损组件,暂时是个摆设
# 止盈止损组件的主要使用者是策略研究人员
stopper: SimpleStopper = SimpleStopper()
# 评估组件
# 评估组件的主要使用者是强化学习研究人员定义reward
assessment: SimpleAssessment = SimpleAssessment(init_assets=assets)
super().__init__(
# 策略只做跟交易模式相关的操作(如趋势策略、日内回转、配对交易、统计套利),不参与特征生成和评估,主要使用者是策略研究人员
strategy=SimpleCTA,
stopper=stopper,
feature=feature, # 特征计算
assessment=assessment, # 评估计算
time_range=time_range,
slippage=slippage,
mode=mode, # 1训练模式,2评估模式,3debug模式
)
def _debug_(self):
print('%s: assets %s, reward %s, reward_sum %s' % (
self._name_(self._iter_), self._assessment_.curr_assets-self._assessment_.init_assets, self._assessment_.reward, sum(self._assessment_.rewards)))
class SimpleCTASubProcessEnv(WtSubProcessEnv):
def __init__(self,
time_range: tuple,
slippage: int = 0,
mode: int = 1):
super().__init__(
cli=SimpleCTAEnv,
time_range=time_range,
slippage=slippage,
mode=mode)
if __name__ == '__main__':
env: WtEnv = SimpleCTASubProcessEnv(time_start=201901011600,
time_end=202001011600, mode=2)
print(env.action_space.contains)
for i in range(1): # 模拟训练10次
obs = env.reset()
done = False
n = 0
while not done:
# box.contains of Box([-3. -3. -3. -3.], [3. 3. 3. 3.], (4,), float16)
action = env.action_space.sample() # 模拟智能体产生动作
obs, reward, done, info = env.step(action)
n += 1
print(
# 'action:', action,
# 'obs:', obs,
'reward:', reward,
# 'done:', done
)
# break
# break
print('第%s次训练完成,执行%s步, 市值%s。' % (i+1, n, env.assets))
env.close()
================================================
FILE: features.py
================================================
import numpy as np
import talib as ta
from reprocess import REPROCESS, ZFILTER, MAXMIN
from wtpy.StrategyDefs import CtaContext, HftContext
class Feature():
M1 = 'm1'
M3 = 'm3'
M5 = 'm5'
M10 = 'm10'
M15 = 'm15'
M30 = 'm30'
M60 = 'm60'
D1 = 'd1'
def __init__(self, code: str, period: str, roll: int, assets: float = 1000000) -> None:
self.__shape__: tuple = tuple()
self._roll_: int = int(roll)
self._assets_: float = float(assets)
self.__cb__: dict = {}
self.__obs__: dict = {}
self.__time__: int = 0
self.__securities__: list = []
self.addSecurity(code=code)
self.__main__: tuple = (code, period)
self.__subscribies__: dict = {}
self._subscribe_(period=period, count=1)
# self.__comminfo__: dict = {}
@property
def securities(self):
return self.__securities__
def addSecurity(self, code: str):
if self.__shape__ or code in self.__securities__:
return
self.__securities__.append(code)
def _subscribe_(self, period: str, count: int = 1):
self.__subscribies__[period] = max(
self.__subscribies__.get(period, 0),
count+self._roll_
)
def subscribe(self, context: CtaContext):
'''
根据特征需求订阅数据
'''
for code in self.__securities__:
# comminfo = context.stra_get_comminfo(code) # 品种信息数据
# self.__comminfo__[code] = (comminfo.pricetick, comminfo.volscale)
for period, count in self.__subscribies__.items():
context.stra_get_bars(
stdCode=code,
period=period,
count=count,
isMain=(code == self.__main__[0]
and period == self.__main__[1])
)
def _callback_(self, space: int, period: str, callback, reprocess: REPROCESS, **kwargs):
if self.__shape__ or space < 1:
return
if period not in self.__cb__:
self.__cb__[period] = {}
self.__cb__[period][callback.__name__] = (
space, callback, reprocess, kwargs)
def sigmoid(self, value, thresh=30):
return (1 / (1 + np.exp(-(value/thresh) * np.e)) - 0.5)*thresh
@property
def observation(self) -> dict:
'''
根据特征需求生成observation
'''
self.__shape__ = (
len(self.securities),
sum(c[0] for v in self.__cb__.values()
for c in v.values())*self._roll_+4
)
self.__flatten__ = (self.__shape__[0]*self.__shape__[1],)
return dict(low=-np.inf, high=np.inf, shape=self.__flatten__, dtype=np.float64)
def calculate(self, context: CtaContext):
self.__time__ = context.stra_get_date()*10000+context.stra_get_time()
if self.__time__ not in self.__obs__:
obs = np.full(shape=self.__shape__,
fill_value=np.nan, dtype=np.float64)
for i, code in enumerate(self.securities): # 处理每一个标的
n = 0
for period, v in self.__cb__.items(): # 处理每一个周期
for space, callback, p, args in v.values(): # 处理每一个特征
features = callback(
context=context, code=code, period=period, args=args) # 通过回调函数计算特征
if space == 1:
features = (features, )
for feature in features: # 处理每一个返回值
# print(p.calculate(feature))
# obs[i][n:n +self._roll_] = p.calculate(feature)[-self._roll_:]
obs[i][n:n +self._roll_] = p.calculate(feature)[-self._roll_:]
#np.clip(p.calculate(feature)[-self._roll_:], -1, 1)
n += self._roll_
# self.__obs__[self.__time__] = obs
# self.__obs__[self.__time__] = self.sigmoid(obs)
self.__obs__[self.__time__] = obs
# 开仓最大浮盈
self.__obs__[self.__time__][:, -4] = tuple(
context.stra_get_detail_profit(
# stdCode=code, usertag='', flag=1)/self.__comminfo__[code][1]/self.__comminfo__[code][0] for code in self.securities
stdCode=code, usertag='', flag=1)/self._assets_ for code in self.securities
)
# 开仓最大亏损
self.__obs__[self.__time__][:, -3] = tuple(
context.stra_get_detail_profit(
# stdCode=code, usertag='', flag=-1)/self.__comminfo__[code][1]/self.__comminfo__[code][0] for code in self.securities
stdCode=code, usertag='', flag=-1)/self._assets_ for code in self.securities
)
# 开仓浮动盈亏
self.__obs__[self.__time__][:, -2] = tuple(
context.stra_get_detail_profit(
# stdCode=code, usertag='', flag=0)/self.__comminfo__[code][1]/self.__comminfo__[code][0] for code in self.securities
stdCode=code, usertag='', flag=0)/self._assets_ for code in self.securities
)
# 持仓数
self.__obs__[self.__time__][:, -1] = tuple(
context.stra_get_position(stdCode=code) for code in self.securities)
# self.__obs__[self.__time__][:, -4:] = self.sigmoid(self.__obs__[self.__time__][:, -4:])
# np.clip(
# self.__obs__[self.__time__][:, -4:], -1, 1,
# out=self.__obs__[self.__time__][:, -4:])
@property
def obs(self):
# .astype(np.float64)
return self.__obs__.get(self.__time__).reshape(self.__flatten__)
def price(self, period: str, reprocess: REPROCESS = MAXMIN):
def price(context: CtaContext, code: str, period: str, args: dict):
return context.stra_get_bars(stdCode=code, period=period, count=self.__subscribies__[period]).closes
self._subscribe_(period=period, count=2+reprocess.n())
self._callback_(space=1, period=period,
callback=price, reprocess=reprocess)
def volume(self, period: str, reprocess: REPROCESS = MAXMIN):
def volume(context: CtaContext, code: str, period: str, args: dict):
return context.stra_get_bars(stdCode=code, period=period, count=self.__subscribies__[period]).volumes
self._subscribe_(period=period, count=2+reprocess.n())
self._callback_(space=1, period=period,
callback=volume, reprocess=reprocess)
class Indicator(Feature):
def roc(self, period: str, reprocess: REPROCESS = REPROCESS):
def roc(context: CtaContext, code: str, period: str, args: dict):
price = context.stra_get_bars(
stdCode=code, period=period, count=self.__subscribies__[period]).closes
price = np.log(price)
return (price[1:]/price[:-1]-1)
return np.diff(context.stra_get_bars(stdCode=code, period=period, count=self.__subscribies__[period]).closes)
self._subscribe_(period=period, count=2+reprocess.n())
self._callback_(space=1, period=period,
callback=roc, reprocess=reprocess)
def bollinger(self, period: str, timeperiod=5, nbdevup=2, nbdevdn=2, reprocess: REPROCESS = MAXMIN):
def bollinger(context: CtaContext, code: str, period: str, args: dict):
closes = context.stra_get_bars(
stdCode=code, period=period, count=self.__subscribies__[period]).closes
upperband, middleband, lowerband = ta.BBANDS(closes, **args)
return upperband, middleband, lowerband
return upperband/closes-1, middleband/closes-1, lowerband/closes-1
self._subscribe_(period=period, count=timeperiod+reprocess.n())
self._callback_(space=3, period=period, callback=bollinger, reprocess=reprocess,
timeperiod=timeperiod, nbdevup=nbdevup, nbdevdn=nbdevdn)
def sar(self, period: str, acceleration=0, maximum=0, reprocess: REPROCESS = MAXMIN):
def sar(context: CtaContext, code: str, period: str, args: dict):
bars = context.stra_get_bars(
stdCode=code, period=period, count=self.__subscribies__[period])
return ta.SAR(high=bars.highs, low=bars.lows, **args)
return ta.SAR(high=bars.highs, low=bars.lows, **args)/bars.closes-1
self._subscribe_(period=period, count=10+reprocess.n())
self._callback_(space=1, period=period, acceleration=acceleration, maximum=maximum,
callback=sar, reprocess=reprocess)
def trange(self, period: str, reprocess: REPROCESS = MAXMIN):
def trange(context: CtaContext, code: str, period: str, args: dict):
bars = context.stra_get_bars(
stdCode=code, period=period, count=self.__subscribies__[period])
return ta.TRANGE(high=bars.highs, low=bars.lows, close=bars.closes)
return ta.TRANGE(high=bars.highs, low=bars.lows, close=bars.closes)/bars.closes
self._subscribe_(period=period, count=2+reprocess.n())
self._callback_(space=1, period=period,
callback=trange, reprocess=reprocess)
def macd(self, period: str, fastperiod: int = 12, slowperiod: int = 26, signalperiod: int = 9, reprocess: REPROCESS = MAXMIN):
def macd(context: CtaContext, code: str, period: str, args: dict):
return ta.MACD(np.log(context.stra_get_bars(stdCode=code, period=period, count=self.__subscribies__[period]).closes), **args)
self._subscribe_(period=period, count=slowperiod +
signalperiod+reprocess.n())
self._callback_(space=3, period=period, callback=macd, reprocess=reprocess,
fastperiod=fastperiod, slowperiod=slowperiod, signalperiod=signalperiod)
def rsi(self, period: str, fastperiod: int = 6, midperiod: int = 12, slowperiod: int = 24, reprocess: REPROCESS = MAXMIN):
def rsi(context: CtaContext, code: str, period: str, args: dict):
bars = context.stra_get_bars(
stdCode=code, period=period, count=self.__subscribies__[period])
return ta.RSI(bars.closes, args['fastperiod']), ta.RSI(bars.closes, args['midperiod']), ta.RSI(bars.closes, args['slowperiod'])
return ta.RSI(bars.closes, args['fastperiod'])/100, ta.RSI(bars.closes, args['midperiod'])/100, ta.RSI(bars.closes, args['slowperiod'])/100
self._subscribe_(period=period, count=slowperiod + 1 + reprocess.n())
self._callback_(space=3, period=period, callback=rsi, reprocess=reprocess,
fastperiod=fastperiod, midperiod=midperiod, slowperiod=slowperiod)
def dx(self, period: str, timeperiod=14, reprocess: REPROCESS = MAXMIN):
def dx(context: CtaContext, code: str, period: str, args: dict):
bars = context.stra_get_bars(
stdCode=code, period=period, count=self.__subscribies__[period])
return ta.DX(high=bars.highs, low=bars.lows, close=bars.closes, **args)
return ta.DX(high=bars.highs, low=bars.lows, close=bars.closes, **args)/100
self._subscribe_(period=period, count=timeperiod+1+reprocess.n())
self._callback_(space=1, period=period, callback=dx, reprocess=reprocess,
timeperiod=timeperiod)
def obv(self, period: str, reprocess: REPROCESS = MAXMIN):
def obv(context: CtaContext, code: str, period: str, args: dict):
bars = context.stra_get_bars(
stdCode=code, period=period, count=self.__subscribies__[period])
return ta.OBV(bars.closes, bars.volumes, **args)
self._subscribe_(period=period, count=10+reprocess.n())
self._callback_(space=1, period=period,
callback=obv, reprocess=reprocess)
def kdj(self, period: str, fastk_period: int = 5, slowk_period: int = 3, reprocess: REPROCESS = MAXMIN):
def kdj(context: CtaContext, code: str, period: str, args: dict):
bars = context.stra_get_bars(
stdCode=code, period=period, count=self.__subscribies__[period])
k, d = ta.STOCH(high=bars.highs, low=bars.lows,
close=bars.closes, **args)
return k, d, (3*k-2*d)
return k/100, d/100, (3*k-2*d)/100
self._subscribe_(period=period, count=10 + 1 + reprocess.n())
self._callback_(space=3, period=period, callback=kdj, reprocess=reprocess,
fastk_period=fastk_period, slowk_period=slowk_period)
# def weights(self, period: str, timeperiod:int=1, index:str='000300', reprocess:REPROCESS =REPROCESS):
# def example(context: CtaContext, code: str, period: str, args: dict):
# # 标的代码 code
# # 标的周期 period
# # 自定义参数 args['index']
# # 日期int context.stra_get_date()
# # 时间int context.stra_get_time()
# return 查询代码(code, context.stra_get_date(), args['index'])
# self._subscribe_(period=period, count=1+reprocess.n()) # 在什么周期的event触发,需要几根bar
# self._callback_(
# space=1, #查询代码有几个值,自动生成obs的占位空间
# period=period,
# callback=example,
# reprocess=reprocess
# timeperiod=timeperiod,
# index=index)
================================================
FILE: reprocess.py
================================================
import numpy as np
import talib as ta
class REPROCESS():
@staticmethod
def n() -> int: # 定义至少需要多少条数据才能计算
return 0
@staticmethod
def calculate(data: np.ndarray) -> np.ndarray: # 计算方法
return data
class ZSCORE(REPROCESS):
@staticmethod
def n() -> int:
return 1200
@staticmethod
def calculate(data: np.ndarray) -> np.ndarray:
return (data-ta.MA(data, __class__.n()))/(ta.STDDEV(data, __class__.n())+1e-5)
class ZFILTER(REPROCESS):
'''
https://github.com/zhangchuheng123/Reinforcement-Implementation/blob/master/code/ppo.py
'''
@staticmethod
def n() -> int:
return 1200
@staticmethod
def calculate(data: np.ndarray) -> np.ndarray:
# 1e-8
return (data-ta.MA(data, __class__.n()))/(ta.STDDEV(data, __class__.n())+1e-5)
class MAXMIN(REPROCESS):
@staticmethod
def n() -> int:
return 1200
@staticmethod
def calculate(data: np.ndarray) -> np.ndarray:
data = data[-__class__.n():]
return (data-data.min())/(data.max()-data.min()+1e-5)*2-1
================================================
FILE: requirements/full_with_cuda.yaml
================================================
channels:
- pytorch
- conda-forge
- defaults
dependencies:
- python==3.8.12
- pip
# torch
- pytorch
- torchvision
- torchaudio
- cudatoolkit
# drl
- matplotlib
- gym
- stable-baselines3
- ray-rllib
- tensorboard
- gputil
- opencensus
- prometheus_client
- aiohttp-cors
- aioredis==1.3.1
- gpy
- scikit-learn
# wtpy
- pandas
- flask
- flask-compress
- xlsxwriter
- pyquery
- zstandard
#
- bottleneck
- numexpr
- tqdm
# vscode
- IPython
- ipywidgets
================================================
FILE: requirements/full_without_cuda.yaml
================================================
channels:
- pytorch
- conda-forge
- defaults
dependencies:
- python==3.8.12
- pip
# torch
- pytorch
- torchvision
- torchaudio
- cpuonly
# drl
- matplotlib
- gym
- stable-baselines3
- ray-rllib
- tensorboard
- gputil
- opencensus
- prometheus_client
- aiohttp-cors
- aioredis==1.3.1
- gpy
- scikit-learn
# wtpy
- pandas
- flask
- flask-compress
- xlsxwriter
- pyquery
- zstandard
#
- bottleneck
- numexpr
- tqdm
# vscode
- IPython
- ipywidgets
================================================
FILE: run_toy.py
================================================
from runner import entry, Runner
class SimpleRunner(Runner):
def test(self):
print('test1')
if __name__ == '__main__':
entry(obj=SimpleRunner())
================================================
FILE: runner.py
================================================
import click
class Runner():
def debug(self):
print('debug')
def train(self):
print('train')
def test(self):
print('test')
@click.group()
@click.pass_context
def entry(ctx, obj:Runner=Runner()):
assert isinstance(obj, Runner)
ctx.ensure_object(Runner)
@entry.command()
@click.pass_context
def debug(ctx):
ctx.obj.debug()
@entry.command()
@click.pass_context
def train(ctx):
ctx.obj.train()
@entry.command()
@click.pass_context
def test(ctx):
ctx.obj.test()
if __name__ == '__main__':
entry()
================================================
FILE: stoppers.py
================================================
class Stopper():
pass
class SimpleStopper(Stopper):
pass
================================================
FILE: strategies.py
================================================
from features import Feature
from stoppers import Stopper
from abc import abstractmethod
from gym.spaces import Space, Box, Discrete, MultiDiscrete
from assessments import Assessment
from wtpy.WtBtEngine import EngineType
from wtpy.StrategyDefs import BaseCtaStrategy, CtaContext, BaseHftStrategy, HftContext
from numpy import around, float32
class StateTransfer():
@staticmethod
@abstractmethod
def Name() -> str:
raise NotImplementedError
@staticmethod
@abstractmethod
def EngineType() -> int:
raise NotImplementedError
@staticmethod
@abstractmethod
def Action(size: int) -> dict:
raise NotImplementedError
@staticmethod
def setAction(self, action):
raise NotImplementedError
def __init__(self, feature: Feature, assessment: Assessment, stopper: Stopper):
self._feature_: Feature = feature
self._assessment_: Assessment = assessment
self._stopper_: Stopper = stopper
# print('StateTransfer')
class SimpleCTA(BaseCtaStrategy, StateTransfer):
@staticmethod
def Name() -> str:
return __class__.__name__
@staticmethod
def EngineType() -> int:
return EngineType.ET_CTA
@staticmethod
def Action(size: int) -> Space:
# return Discrete(10)
return Box(low=-1., high=1., shape=(size, ), dtype=float32)
# return MultiDiscrete([11]*size)
# return dict(low=-1., high=1., shape=(size, ), dtype=float32)
def setAction(self, action):
# print('setAction 1')
# action -= 5
# self._action_ = dict(zip(self._feature_.securities, [action-5]))
self._action_ = dict(zip(self._feature_.securities, around(action*3, 0)))
# print(self._action_)
# try:
# self._action_ = dict(zip(self._feature_.securities, around(action, 0)))
# print(self.name(), action, type(action))
# except:
# print(self.name(), action, type(action))
# print('setAction 2')
def __init__(self, name: str, feature: Feature, assessment: Assessment, stopper: Stopper):
super(BaseCtaStrategy, self).__init__(
feature=feature, assessment=assessment, stopper=stopper)
super().__init__(name)
self._action_: dict = {}
# print('TrainCTA')
def on_init(self, context: CtaContext):
# print('on_init 1')
self._feature_.subscribe(context)
# print('on_init 2')
def on_session_begin(self, context: CtaContext, curTDate: int):
# print('on_session_begin')
pass
def on_backtest_end(self, context: CtaContext):
# print('on_backtest_end')
pass
def on_calculate(self, context: CtaContext):
# print('on_calculate 1')
self._feature_.calculate(context=context)
self._assessment_.calculate(context=context)
# print('on_calculate 2')
def on_calculate_done(self, context: CtaContext):
# print('on_calculate_done 1')
for code in tuple(self._action_.keys()):
qty = self._action_.pop(code)
if qty != context.stra_get_position(stdCode=code):
context.stra_set_position(stdCode=code, qty=qty)
# print('stra_set_position %s'%code)
# print('on_calculate_done 2')
# class SimpleHFT(BaseHftStrategy, StateTransfer):
# @staticmethod
# def Name() -> str:
# return __class__.__name__
# @staticmethod
# def EngineType() -> int:
# return EngineType.ET_HFT
# def on_tick(self, context: HftContext, stdCode: str, newTick: dict):
# pass
================================================
FILE: wtpy/CodeHelper.py
================================================
import re
class CodeHelper:
@staticmethod
def isStdStkCode(stdCode:str) -> bool:
pattern = re.compile("^[A-Z]+.([A-Z]+.)?\\d{6}Q?$")
if re.match(pattern, stdCode) is not None:
return True
return False
@staticmethod
def stdCodeToStdCommID(stdCode:str) -> str:
if CodeHelper.isStdStkCode(stdCode):
return CodeHelper.stdStkCodeToStdCommID(stdCode)
else:
return CodeHelper.stdFutCodeToStdCommID(stdCode)
@staticmethod
def stdStkCodeToStdCommID(stdCode:str) -> str:
ay = stdCode.split(".")
return ay[0] + "." + "STK"
@staticmethod
def stdFutCodeToStdCommID(stdCode:str) -> str:
ay = stdCode.split(".")
return ay[0] + "." + ay[1]
================================================
FILE: wtpy/ContractMgr.py
================================================
import json
class ContractInfo:
def __init__(self):
self.exchg = '' #交易所
self.code = '' #合约代码
self.name = '' #合约名称
self.product = '' #品种代码
self.stdCode = '' #标准代码
class ContractMgr:
def __init__(self):
self.__contracts__ = dict()
def load(self, fname:str):
'''
从文件加载品种信息
'''
f = open(fname, 'r', encoding="gbk")
content = f.read()
f.close()
exchgMap = json.loads(content)
for exchg in exchgMap:
exchgObj = exchgMap[exchg]
for code in exchgObj:
cObj = exchgObj[code]
cInfo = ContractInfo()
cInfo.exchg = exchg
cInfo.code = code
cInfo.name = cObj["name"]
cInfo.product = cObj["product"]
#股票标准代码为SSE.000001,期货标准代码为SHFE.rb.2010
if cInfo.code[:len(cInfo.product)] == cInfo.product:
cInfo.stdCode = exchg + "." + cInfo.product + "." + cInfo.code[len(cInfo.product):]
else:
cInfo.stdCode = exchg + "." + cInfo.code
key = "%s.%s" % (exchg, code)
self.__contracts__[key] = cInfo
def getContractInfo(self, stdCode:str) -> ContractInfo:
if stdCode[-1] == 'Q':
stdCode = stdCode[:-1]
else:
items = stdCode.split(".")
if len(items) == 3:
stdCode = items[0] + "." + items[1] + items[2]
if stdCode not in self.__contracts__:
return None
return self.__contracts__[stdCode]
def getTotalCodes(self) -> list:
codes = list()
for code in self.__contracts__:
codes.append(self.__contracts__[code].stdCode)
return codes
================================================
FILE: wtpy/CtaContext.py
================================================
from pandas import DataFrame as df
import pandas as pd
import os
import json
from wtpy.wrapper import WtWrapper
from wtpy.WtDataDefs import WtKlineData, WtHftData
class CtaContext:
'''
Context是策略可以直接访问的唯一对象\n
策略所有的接口都通过Context对象调用\n
Context类包括以下几类接口:\n
1、时间接口(日期、时间等),接口格式如:stra_xxx\n
2、数据接口(K线、财务等),接口格式如:stra_xxx\n
3、下单接口(设置目标仓位、直接下单等),接口格式如:stra_xxx\n
'''
def __init__(self, id:int, stra, wrapper: WtWrapper, engine):
self.__stra_info__ = stra #策略对象,对象基类BaseStrategy.py
self.__wrapper__ = wrapper #底层接口转换器
self.__id__ = id #策略ID
self.__bar_cache__ = dict() #K线缓存
self.__tick_cache__ = dict() #tTick缓存,每次都重新去拉取,这个只做中转用,不在python里维护副本
self.__sname__ = stra.name()
self.__engine__ = engine #交易环境
self.__pos_cache__ = None
self.is_backtest = self.__engine__.is_backtest
@property
def id(self):
return self.__id__
def write_indicator(self, tag:str, time:int, data:dict):
'''
输出指标数据
@tag 指标标签
@time 输出时间
@data 输出的指标数据,dict类型,会转成json以后保存
'''
self.__engine__.write_indicator(self.__stra_info__.name(), tag, time, data)
def on_init(self):
'''
初始化,一般用于系统启动的时候
'''
self.__stra_info__.on_init(self)
def on_session_begin(self, curTDate:int):
'''
交易日开始事件\n
@curTDate 交易日,格式为20210220
'''
self.__stra_info__.on_session_begin(self, curTDate)
def on_session_end(self, curTDate:int):
'''
交易日结束事件\n
@curTDate 交易日,格式为20210220
'''
self.__stra_info__.on_session_end(self, curTDate)
def on_backtest_end(self):
'''
回测结束事件
'''
self.__stra_info__.on_backtest_end(self)
def on_getticks(self, stdCode:str, newTicks:list, isLast:bool):
key = stdCode
ticks = self.__tick_cache__[key]
for newTick in newTicks:
ticks.append_item(newTick)
def on_getpositions(self, stdCode:str, qty:float, isLast:bool):
if len(stdCode) == 0:
return
self.__pos_cache__[stdCode] = qty
def on_getbars(self, stdCode:str, period:str, newBars:list, isLast:bool):
key = "%s#%s" % (stdCode, period)
bars = self.__bar_cache__[key]
for newBar in newBars:
bars.append_bar(newBar)
def on_tick(self, stdCode:str, newTick):
self.__stra_info__.on_tick(self, stdCode, newTick)
def on_bar(self, stdCode:str, period:str, newBar:dict):
'''
K线闭合事件响应
@stdCode 品种代码
@period K线基础周期
@times 周期倍数
@newBar 最新K线
'''
key = "%s#%s" % (stdCode, period)
if key not in self.__bar_cache__:
return
try:
self.__bar_cache__[key].append_bar(newBar)
self.__bar_cache__[key].closed = True
self.__stra_info__.on_bar(self, stdCode, period, newBar)
except ValueError as ve:
print(ve)
else:
return
def on_calculate(self):
self.__stra_info__.on_calculate(self)
def on_calculate_done(self):
self.__stra_info__.on_calculate_done(self)
def stra_log_text(self, message:str):
'''
输出日志
@message 消息内容,最大242字符\n
'''
self.__wrapper__.cta_log_text(self.__id__, message[:242])
def stra_get_tdate(self):
'''
获取当前交易日\n
@return int,格式如20180513
'''
return self.__wrapper__.cta_get_tdate()
def stra_get_date(self):
'''
获取当前日期\n
@return int,格式如20180513
'''
return self.__wrapper__.cta_get_date()
def stra_get_position_avgpx(self, stdCode:str = "") -> float:
'''
获取当前持仓均价\n
@stdCode 合约代码
@return 持仓均价
'''
return self.__wrapper__.cta_get_position_avgpx(self.__id__, stdCode)
def stra_get_position_profit(self, stdCode:str = "") -> float:
'''
获取持仓浮动盈亏
@stdCode 合约代码,为None时读取全部品种的浮动盈亏
@return 浮动盈亏
'''
return self.__wrapper__.cta_get_position_profit(self.__id__, stdCode)
def stra_get_fund_data(self, flag:int = 0) -> float:
'''
获取资金数据\n
@flag 0-动态权益,1-总平仓盈亏,2-总浮动盈亏,3-总手续费\n
@return 资金数据
'''
return self.__wrapper__.cta_get_fund_data(self.__id__, flag)
def stra_get_time(self):
'''
获取当前时间,24小时制,精确到分\n
@return int,格式如1231
'''
return self.__wrapper__.cta_get_time()
def stra_get_price(self, stdCode:str):
'''
获取最新价格,一般在获取了K线以后再获取该价格
@return 最新价格
'''
return self.__wrapper__.cta_get_price(stdCode)
def stra_get_all_position(self):
'''
获取全部持仓
'''
self.__pos_cache__ = dict() #
self.__wrapper__.cta_get_all_position(self.__id__)
return self.__pos_cache__
def stra_get_bars(self, stdCode:str, period:str, count:int, isMain:bool = False) -> WtKlineData:
'''
获取历史K线
@stdCode 合约代码
@period K线周期,如m3/d7
@count 要拉取的K线条数
@isMain 是否是主K线
'''
key = "%s#%s" % (stdCode, period)
if key in self.__bar_cache__:
#这里做一个数据长度处理
return self.__bar_cache__[key]
self.__bar_cache__[key] = WtKlineData(size=count)
cnt = self.__wrapper__.cta_get_bars(self.__id__, stdCode, period, count, isMain)
if cnt == 0:
return None
df_bars = self.__bar_cache__[key]
df_bars.closed = False
return df_bars
def stra_get_ticks(self, stdCode:str, count:int) -> WtHftData:
'''
获取tick数据
@stdCode 合约代码
@count 要拉取的tick数量
'''
self.__tick_cache__[stdCode] = WtHftData(capacity=count)
cnt = self.__wrapper__.cta_get_ticks(self.__id__, stdCode, count)
if cnt == 0:
return None
df_ticks = self.__tick_cache__[stdCode]
return df_ticks
def stra_sub_ticks(self, stdCode:str):
'''
订阅实时行情\n
获取K线和tick数据的时候会自动订阅,这里只需要订阅额外要检测的品种即可\n
@stdCode 合约代码
'''
self.__wrapper__.cta_sub_ticks(self.__id__, stdCode)
def stra_get_position(self, stdCode:str = "", usertag:str = ""):
'''
读取当前仓位\n
@stdCode 合约/股票代码\n
@usertag 入场标记
@return 正为多仓,负为空仓
'''
return self.__wrapper__.cta_get_position(self.__id__, stdCode, usertag)
def stra_set_position(self, stdCode:str, qty:float, usertag:str = "", limitprice:float = 0.0, stopprice:float = 0.0):
'''
设置仓位\n
@stdCode 合约/股票代码\n
@qty 目标仓位,正为多仓,负为空仓\n
@return 设置结果TRUE/FALSE
'''
self.__wrapper__.cta_set_position(self.__id__, stdCode, qty, usertag, limitprice, stopprice)
def stra_enter_long(self, stdCode:str, qty:float, usertag:str = "", limitprice:float = 0.0, stopprice:float = 0.0):
'''
多仓进场,如果有空仓,则平空再开多\n
@stdCode 品种代码\n
@qty 数量\n
@limitprice 限价,默认为0\n
@stopprice 止价,默认为0
'''
self.__wrapper__.cta_enter_long(self.__id__, stdCode, qty, usertag, limitprice, stopprice)
def stra_exit_long(self, stdCode:str, qty:float, usertag:str = "", limitprice:float = 0.0, stopprice:float = 0.0):
'''
多仓出场,如果剩余多仓不够,则全部平掉即可\n
@stdCode 品种代码\n
@qty 数量\n
@limitprice 限价,默认为0\n
@stopprice 止价,默认为0
'''
self.__wrapper__.cta_exit_long(self.__id__, stdCode, qty, usertag, limitprice, stopprice)
def stra_enter_short(self, stdCode:str, qty:float, usertag:str = "", limitprice:float = 0.0, stopprice:float = 0.0):
'''
空仓进场,如果有多仓,则平多再开空\n
@stdCode 品种代码\n
@qty 数量\n
@limitprice 限价,默认为0\n
@stopprice 止价,默认为0
'''
self.__wrapper__.cta_enter_short(self.__id__, stdCode, qty, usertag, limitprice, stopprice)
def stra_exit_short(self, stdCode:str, qty:float, usertag:str = "", limitprice:float = 0.0, stopprice:float = 0.0):
'''
空仓出场,如果剩余空仓不够,则全部平掉即可\n
@stdCode 品种代码\n
@qty 数量\n
@limitprice 限价,默认为0\n
@stopprice 止价,默认为0
'''
self.__wrapper__.cta_exit_short(self.__id__, stdCode, qty, usertag, limitprice, stopprice)
def stra_get_last_entrytime(self, stdCode:str):
'''
获取当前持仓最后一次进场时间\n
@stdCode 品种代码\n
@return 返回最后一次开仓的时间,格式如201903121047
'''
return self.__wrapper__.cta_get_last_entertime(self.__id__, stdCode)
def stra_get_last_exittime(self, stdCode:str):
'''
获取当前持仓最后一次出场时间\n
@stdCode 品种代码\n
@return 返回最后一次开仓的时间,格式如201903121047
'''
return self.__wrapper__.cta_get_last_exittime(self.__id__, stdCode)
def stra_get_first_entrytime(self, stdCode:str):
'''
获取当前持仓第一次进场时间\n
@stdCode 品种代码\n
@return 返回最后一次开仓的时间,格式如201903121047
'''
return self.__wrapper__.cta_get_first_entertime(self.__id__, stdCode)
def user_save_data(self, key:str, val):
'''
保存用户数据
@key 数据id
@val 数据值,可以直接转换成str的数据均可
'''
self.__wrapper__.cta_save_user_data(self.__id__, key, str(val))
def user_load_data(self, key:str, defVal = None, vType = float):
'''
读取用户数据
@key 数据id
@defVal 默认数据,如果找不到则返回改数据,默认为None
@return 返回值,默认处理为float数据
'''
ret = self.__wrapper__.cta_load_user_data(self.__id__, key, "")
if ret == "":
return defVal
return vType(ret)
def stra_get_detail_profit(self, stdCode:str, usertag:str, flag:int = 0):
'''
获取指定标记的持仓的盈亏
@stdCode 合约代码\n
@usertag 进场标记\n
@flag 盈亏记号,0-浮动盈亏,1-最大浮盈,-1-最大亏损(负数)
@return 盈亏
'''
return self.__wrapper__.cta_get_detail_profit(self.__id__, stdCode, usertag, flag)
def stra_get_detail_cost(self, stdCode:str, usertag:str):
'''
获取指定标记的持仓的开仓价
@stdCode 合约代码\n
@usertag 进场标记\n
@return 开仓价
'''
return self.__wrapper__.cta_get_detail_cost(self.__id__, stdCode, usertag)
def stra_get_detail_entertime(self, stdCode:str, usertag:str):
'''
获取指定标记的持仓的进场时间\n
@stdCode 合约代码\n
@usertag 进场标记\n
@return 进场时间,格式如201907260932
'''
return self.__wrapper__.cta_get_detail_entertime(self.__id__, stdCode, usertag)
def stra_get_comminfo(self, stdCode:str):
'''
获取品种详情\n
@stdCode 合约代码如SHFE.ag.HOT,或者品种代码如SHFE.ag\n
@return 品种信息,结构请参考ProductMgr中的ProductInfo
'''
if self.__engine__ is None:
return None
return self.__engine__.getProductInfo(stdCode)
def stra_get_sessinfo(self, stdCode:str):
'''
获取交易时段详情\n
@stdCode 合约代码如SHFE.ag.HOT,或者品种代码如SHFE.ag\n
@return 品种信息,结构请参考SessionMgr中的SessionInfo
'''
if self.__engine__ is None:
return None
return self.__engine__.getSessionByCode(stdCode)
================================================
FILE: wtpy/ExtModuleDefs.py
================================================
class BaseExtParser:
'''
扩展行情接入模块基类
'''
def __init__(self, id:str):
'''
构造函数
@id 解析器ID
'''
self.__id__ = id
return
def id(self) -> str:
return self.__id__
def init(self, engine):
'''
初始化
'''
self.__engine__ = engine
return
def connect(self):
'''
开始连接
'''
return
def disconnect(self):
'''
断开连接
'''
return
def release(self):
'''
释放,一般是进程退出时调用
'''
return
def subscribe(self, fullCode:str):
'''
订阅实时行情\n
@fullCode 合约代码,格式如CFFEX.IF2106
'''
return
def unsubscribe(self, fullCode:str):
'''
退订实时行情\n
@fullCode 合约代码,格式如CFFEX.IF2106
'''
return
class BaseExtExecuter:
'''
扩展执行器基类
'''
def __init__(self, id:str, scale:float):
'''
构造函数\n
@id 执行器ID\n
@scale 数量放大倍数
'''
self.__id__ = id
self.__scale__ = scale
self.__targets__ = dict()
return
def id(self):
return self.__id__
def init(self):
return
def set_position(self, stdCode:str, targetPos:float):
'''
设置目标部位\n
@stdCode 合约代码,期货格式为CFFEX.IF.2106\n
@targetPos 目标仓位,浮点数
'''
# 确定原来的目标仓位
oldPos = 0
if stdCode in self.__targets__:
oldPos = self.__targets__[stdCode]
# 修改最新的目标仓位
self.__targets__[stdCode] = targetPos
return
================================================
FILE: wtpy/ExtToolDefs.py
================================================
import json
import time
from threading import Thread
def fileToJson(filename, encoding="utf-8"):
f = open(filename, 'r')
content = f.read()
f.close()
try:
return json.loads(content)
except:
return None
class BaseIndexWriter:
'''
基础指标输出工具
'''
def __init__(self):
return
def write_indicator(self, id:str, tag:str, time:int, data:dict):
'''
将指标数据出\n
@id 指标ID\n
@tag 数据标记\n
@time 指标时间\n
@data 数据对象,一个dict
'''
raise Exception("Basic writer cannot output index data to any media")
class BaseDataReporter:
'''
数据报告器
'''
TaskReportRTData = 1
TaskReportSettleData = 2
TaskReportInitData = 3
def __init__(self, id:str):
self.__inited__ = False
self.__id__ = id
return
def init(self):
self.__inited__ = True
self.__thrd_task__ = None
self.__tasks__ = list()
self.__stopped__ = False
#读取策略标记
filename = "./generated/marker.json"
obj = fileToJson(filename)
if obj is not None:
self.stra_names = obj["marks"]
def rpt_portfolio_rt_data_impl(self, rtData):
raise Exception("this method has not been implemented")
def rpt_strategy_rt_data_impl(self, rtData):
raise Exception("this method has not been implemented")
def rpt_init_data_impl(self, initData):
raise Exception("this method has not been implemented")
def __do_report_rt_data__(self):
print("settle data reporter triggered")
# 第一步,提交组合数据,读取portfolio
filename = "./generated/portfolio/datas.json"
objPort = fileToJson(filename)
objPort["pid"] = self.__id__
# 开始提交组合数据
self.rpt_portfolio_rt_data_impl(objPort)
# 第二步,提交策略数据
for sname in self.stra_names:
filename = "./generated/stradata/" + sname + ".json"
objStra = fileToJson(filename)
objStra["pid"] = self.__id__
objStra["sid"] = sname
self.rpt_strategy_rt_data_impl(objStra)
def __task_loop__(self):
while not self.__stopped__:
if len(self.__tasks__) == 0:
time.sleep(1)
continue
else:
taskid = self.__tasks__.pop(0)
if taskid == self.TaskReportRTData:
self.__do_report_rt_data__()
elif taskid == self.TaskReportSettleData:
self.__do_report_settle_data__()
elif taskid == self.TaskReportInitData:
self.__do_report_init_data__()
def __start__(self):
if self.__thrd_task__ is None:
self.__thrd_task__ = Thread(target=self.__task_loop__, name="reportthread")
# self.__thrd_task__.setDaemon(True)
self.__thrd_task__.start()
print("report thread started")
def __do_report_init_data__(self):
objInitData = dict()
objInitData["pid"] = self.__id__
objInitData["strategies"] = self.stra_names
self.rpt_init_data_impl(objInitData)
def __do_report_settle_data__(self):
print("settle data reporter triggered")
def report_rt_data(self):
print("rt data reporter triggered")
self.__tasks__.append(self.TaskReportRTData)
if self.__thrd_task__ is None:
self.__start__()
def report_settle_data(self):
self.__tasks__.append(self.TaskReportSettleData)
if self.__thrd_task__ is None:
self.__start__()
def report_init_data(self):
self.__tasks__.append(self.TaskReportInitData)
if self.__thrd_task__ is None:
self.__start__()
================================================
FILE: wtpy/HftContext.py
================================================
from pandas import DataFrame as df
import pandas as pd
import os
import json
from wtpy.wrapper import WtWrapper
from wtpy.WtDataDefs import WtKlineData, WtHftData
class HftContext:
'''
Context是策略可以直接访问的唯一对象\n
策略所有的接口都通过Context对象调用\n
Context类包括以下几类接口:\n
1、时间接口(日期、时间等),接口格式如:stra_xxx\n
2、数据接口(K线、财务等),接口格式如:stra_xxx\n
3、下单接口(设置目标仓位、直接下单等),接口格式如:stra_xxx\n
'''
def __init__(self, id:int, stra, wrapper: WtWrapper, engine):
self.__stra_info__ = stra #策略对象,对象基类BaseStrategy.py
self.__wrapper__ = wrapper #底层接口转换器
self.__id__ = id #策略ID
self.__bar_cache__ = dict() #K线缓存
self.__tick_cache__ = dict() #Tick缓存,每次都重新去拉取,这个只做中转用,不在python里维护副本
self.__ordque_cache__ = dict() #委托队列缓存,用法同__tick_cache__
self.__orddtl_cache__ = dict() #逐笔委托缓存,用法同__tick_cache__
self.__trans_cache__ = dict() #逐笔成交缓存,用法同__tick_cache__
self.__sname__ = stra.name()
self.__engine__ = engine #交易环境
self.is_backtest = self.__engine__.is_backtest
@property
def id(self):
return self.__id__
def on_init(self):
'''
初始化,一般用于系统启动的时候
'''
self.__stra_info__.on_init(self)
def on_session_begin(self, curTDate:int):
'''
交易日开始事件\n
@curTDate 交易日,格式为20210220
'''
self.__stra_info__.on_session_begin(self, curTDate)
def on_session_end(self, curTDate:int):
'''
交易日结束事件\n
@curTDate 交易日,格式为20210220
'''
self.__stra_info__.on_session_end(self, curTDate)
def on_backtest_end(self):
'''
回测结束事件
'''
self.__stra_info__.on_backtest_end(self)
def on_getticks(self, stdCode:str, newTicks:list, isLast:bool):
key = stdCode
ticks = self.__tick_cache__[key]
for newTick in newTicks:
ticks.append_item(newTick)
def on_getbars(self, stdCode:str, period:str, newBars:list, isLast:bool):
key = "%s#%s" % (stdCode, period)
bars = self.__bar_cache__[key]
for newBar in newBars:
bars.append_bar(newBar)
def on_tick(self, stdCode:str, newTick:dict):
self.__stra_info__.on_tick(self, stdCode, newTick)
def on_order_queue(self, stdCode:str, newOrdQue:dict):
self.__stra_info__.on_order_queue(self, stdCode, newOrdQue)
def on_get_order_queue(self, stdCode:str, newOdrQues:list, isLast:bool):
key = stdCode
items = self.__ordque_cache__[key]
for newItem in newOdrQues:
items.append_item(newItem)
def on_order_detail(self, stdCode:str, newOrdDtl:dict):
self.__stra_info__.on_order_detail(self, stdCode, newOrdDtl)
def on_get_order_detail(self, stdCode:str, newOrdDtls:list, isLast:bool):
key = stdCode
items = self.__orddtl_cache__[key]
for newItem in newOrdDtls:
items.append_item(newItem)
def on_transaction(self, stdCode:str, newTrans:dict):
self.__stra_info__.on_transaction(self, stdCode, newTrans)
def on_get_transaction(self, stdCode:str, newTranses:list, isLast:bool):
key = stdCode
items = self.__trans_cache__[key]
for newItem in newTranses:
items.append_item(newItem)
def on_channel_ready(self):
self.__stra_info__.on_channel_ready(self)
def on_channel_lost(self):
self.__stra_info__.on_channel_lost(self)
def on_entrust(self, localid:int, stdCode:str, bSucc:bool, msg:str, userTag:str):
self.__stra_info__.on_entrust(self, localid, stdCode, bSucc, msg, userTag)
def on_order(self, localid:int, stdCode:str, isBuy:bool, totalQty:float, leftQty:float, price:float, isCanceled:bool, userTag:str):
self.__stra_info__.on_order(self, localid, stdCode, isBuy, totalQty, leftQty, price, isCanceled, userTag)
def on_trade(self, localid:int, stdCode:str, isBuy:bool, qty:float, price:float, userTag:str):
self.__stra_info__.on_trade(self, localid, stdCode, isBuy, qty, price, userTag)
def on_bar(self, code:str, period:str, newBar:dict):
'''
K线闭合事件响应
@code 品种代码
@period K线基础周期
@times 周期倍数
@newBar 最新K线
'''
key = "%s#%s" % (code, period)
if key not in self.__bar_cache__:
return
try:
self.__bar_cache__[key].append_bar(newBar)
self.__bar_cache__[key].closed = True
self.__stra_info__.on_bar(self, code, period, newBar)
except ValueError as ve:
print(ve)
else:
return
def stra_log_text(self, message:str):
'''
输出日志
@message 消息内容\n
'''
self.__wrapper__.hft_log_text(self.__id__, message)
def stra_get_date(self):
'''
获取当前日期\n
@return int,格式如20180513
'''
return self.__wrapper__.hft_get_date()
def stra_get_time(self):
'''
获取当前时间,24小时制,精确到分\n
@return int,格式如1231
'''
return self.__wrapper__.hft_get_time()
def stra_get_secs(self):
'''
获取当前秒数,精确到毫秒\n
@return int,格式如1231
'''
return self.__wrapper__.hft_get_secs()
def stra_get_price(self, code):
'''
获取最新价格,一般在获取了K线以后再获取该价格
@return 最新价格
'''
return self.__wrapper__.hft_get_price(code)
def stra_get_bars(self, code:str, period:str, count:int) -> WtKlineData:
'''
获取历史K线
@code 合约代码
@period K线周期,如m3/d7
@count 要拉取的K线条数
@isMain 是否是主K线
'''
key = "%s#%s" % (code, period)
if key in self.__bar_cache__:
#这里做一个数据长度处理
return self.__bar_cache__[key]
self.__bar_cache__[key] = WtKlineData(size=count)
cnt = self.__wrapper__.hft_get_bars(self.__id__, code, period, count)
if cnt == 0:
return None
df_bars = self.__bar_cache__[key]
df_bars.closed = False
return df_bars
def stra_get_ticks(self, code:str, count:int) -> WtHftData:
'''
获取tick数据
@code 合约代码
@count 要拉取的tick数量
'''
self.__tick_cache__[code] = WtHftData(capacity=count)
cnt = self.__wrapper__.hft_get_ticks(self.__id__, code, count)
if cnt == 0:
return None
hftData = self.__tick_cache__[code]
return hftData
def stra_get_order_queue(self, code:str, count:int) -> WtHftData:
'''
获取委托队列数据
@code 合约代码
@count 要拉取的tick数量
'''
self.__ordque_cache__[code] = WtHftData(capacity=count)
cnt = self.__wrapper__.hft_get_ordque(self.__id__, code, count)
if cnt == 0:
return None
hftData = self.__ordque_cache__[code]
return hftData
def stra_get_order_detail(self, code:str, count:int) -> WtHftData:
'''
获取逐笔委托数据
@code 合约代码
@count 要拉取的tick数量
'''
self.__orddtl_cache__[code] = WtHftData(capacity=count)
cnt = self.__wrapper__.hft_get_orddtl(self.__id__, code, count)
if cnt == 0:
return None
hftData = self.__orddtl_cache__[code]
return hftData
def stra_get_transaction(self, code:str, count:int) -> WtHftData:
'''
获取逐笔成交数据
@code 合约代码
@count 要拉取的tick数量
'''
self.__trans_cache__[code] = WtHftData(capacity=count)
cnt = self.__wrapper__.hft_get_trans(self.__id__, code, count)
if cnt == 0:
return None
hftData = self.__trans_cache__[code]
return hftData
def stra_get_position(self, code:str = ""):
'''
读取当前仓位\n
@code 合约/股票代码\n
@return 正为多仓,负为空仓
'''
return self.__wrapper__.hft_get_position(self.__id__, code)
def stra_get_position_profit(self, code:str = ""):
'''
读取指定持仓的浮动盈亏\n
@code 合约/股票代码\n
@return 指定持仓的浮动盈亏
'''
return self.__wrapper__.hft_get_position_profit(self.__id__, code)
def stra_get_undone(self, stdCode:str):
return self.__wrapper__.hft_get_undone(self.__id__, stdCode)
def user_save_data(self, key:str, val):
'''
保存用户数据
@key 数据id
@val 数据值,可以直接转换成str的数据均可
'''
self.__wrapper__.hft_save_user_data(self.__id__, key, str(val))
def user_load_data(self, key:str, defVal = None, vType = float):
'''
读取用户数据
@key 数据id
@defVal 默认数据,如果找不到则返回改数据,默认为None
@return 返回值,默认处理为float数据
'''
ret = self.__wrapper__.hft_load_user_data(self.__id__, key, "")
if ret == "":
return defVal
return vType(ret)
def stra_get_comminfo(self, code:str):
'''
获取品种详情\n
@code 合约代码如SHFE.ag.HOT,或者品种代码如SHFE.ag\n
@return 品种信息,结构请参考ProductMgr中的ProductInfo
'''
if self.__engine__ is None:
return None
return self.__engine__.getProductInfo(code)
def stra_sub_ticks(self, stdCode:str):
'''
订阅实时行情数据\n
获取K线和tick数据的时候会自动订阅,这里只需要订阅额外要检测的品种即可\n
@code 品种代码
'''
self.__wrapper__.hft_sub_ticks(self.__id__, stdCode)
def stra_cancel(self, localid:int):
'''
撤销指定订单\n
@id 策略ID\n
@localid 下单时返回的本地订单号
'''
return self.__wrapper__.hft_cancel(self.__id__, localid)
def stra_cancel_all(self, stdCode:str, isBuy:bool):
'''
撤销指定品种的全部买入订单or卖出订单\n
@id 策略ID\n
@stdCode 品种代码\n
@isBuy 买入or卖出
'''
idstr = self.__wrapper__.hft_cancel_all(self.__id__, stdCode, isBuy)
if len(idstr) == 0:
return list()
ids = idstr.split(",")
localids = list()
for localid in ids:
localids.append(int(localid))
return localids
def stra_buy(self, stdCode:str, price:float, qty:float, userTag:str):
'''
买入指令\n
@id 策略ID\n
@stdCode 品种代码\n
@price 买入价格, 0为市价\n
@qty 买入数量
'''
idstr = self.__wrapper__.hft_buy(self.__id__, stdCode, price, qty, userTag)
if len(idstr) == 0:
return list()
ids = idstr.split(",")
localids = list()
for localid in ids:
localids.append(int(localid))
return localids
def stra_sell(self, stdCode:str, price:float, qty:float, userTag:str):
'''
卖出指令\n
@id 策略ID\n
@stdCode 品种代码\n
@price 卖出价格, 0为市价\n
@qty 卖出数量
'''
idstr = self.__wrapper__.hft_sell(self.__id__, stdCode, price, qty, userTag)
if len(idstr) == 0:
return list()
ids = idstr.split(",")
localids = list()
for localid in ids:
localids.append(int(localid))
return localids
================================================
FILE: wtpy/ProductMgr.py
================================================
import json
class ProductInfo:
'''
品种信息
'''
def __init__(self):
self.exchg = '' #交易所
self.product = '' #品种代码
self.name = '' #品种名称
self.session = '' #交易时段名
self.covermode = 0 #平仓模式
self.pricemode = 0 #价格模式
self.precision = 0 #精度
self.pricetick = 0 #价格变动单位
self.volscale = 1 #数量乘数
class ProductMgr:
'''
品种信息管理器
'''
def __init__(self):
self.__products__ = dict()
return
def load(self, fname:str):
'''
从文件加载品种信息
'''
f = open(fname, 'r', encoding="gbk")
content = f.read()
f.close()
exchgMap = json.loads(content)
for exchg in exchgMap:
exchgObj = exchgMap[exchg]
for pid in exchgObj:
pObj = exchgObj[pid]
pInfo = ProductInfo()
pInfo.exchg = exchg
pInfo.product = pid
pInfo.name = pObj["name"]
pInfo.session = pObj["session"]
pInfo.covermode = int(pObj["covermode"])
pInfo.pricemode = int(pObj["pricemode"])
pInfo.precision = int(pObj["precision"])
pInfo.volscale = int(pObj["volscale"])
pInfo.pricetick = float(pObj["pricetick"])
key = "%s.%s" % (exchg, pid)
self.__products__[key] = pInfo
def getProductInfo(self, pid:str) -> ProductInfo:
#pid形式可能为SHFE.ag.HOT,或者SHFE.ag.1912,或者SHFE.ag
items = pid.split(".")
key = items[0] + "." + items[1]
if key not in self.__products__:
return None
return self.__products__[key]
================================================
FILE: wtpy/SelContext.py
================================================
from pandas import DataFrame as df
import pandas as pd
import os
import json
from wtpy.wrapper import WtWrapper
from wtpy.WtDataDefs import WtKlineData, WtHftData
class SelContext:
'''
Context是策略可以直接访问的唯一对象\n
策略所有的接口都通过Context对象调用\n
Context类包括以下几类接口:\n
1、时间接口(日期、时间等),接口格式如:stra_xxx\n
2、数据接口(K线、财务等),接口格式如:stra_xxx\n
3、下单接口(设置目标仓位、直接下单等),接口格式如:stra_xxx\n
'''
def __init__(self, id:int, stra, wrapper: WtWrapper, engine):
self.__stra_info__ = stra #策略对象,对象基类BaseStrategy.py
self.__wrapper__ = wrapper #底层接口转换器
self.__id__ = id #策略ID
self.__bar_cache__ = dict() #K线缓存
self.__tick_cache__ = dict() #tTick缓存,每次都重新去拉取,这个只做中转用,不在python里维护副本
self.__sname__ = stra.name()
self.__engine__ = engine #交易环境
self.__pos_cache__ = None
self.is_backtest = self.__engine__.is_backtest
def write_indicator(self, tag, time, data):
'''
输出指标数据
@tag 指标标签
@time 输出时间
@data 输出的指标数据,dict类型,会转成json以后保存
'''
self.__engine__.write_indicator(self.__stra_info__.name(), tag, time, data)
def on_init(self):
'''
初始化,一般用于系统启动的时候
'''
self.__stra_info__.on_init(self)
def on_session_begin(self, curTDate:int):
'''
交易日开始事件\n
@curTDate 交易日,格式为20210220
'''
self.__stra_info__.on_session_begin(self, curTDate)
def on_session_end(self, curTDate:int):
'''
交易日结束事件\n
@curTDate 交易日,格式为20210220
'''
self.__stra_info__.on_session_end(self, curTDate)
def on_backtest_end(self):
'''
回测结束事件
'''
self.__stra_info__.on_backtest_end(self)
def on_getticks(self, stdCode:str, newTicks:list, isLast:bool):
key = stdCode
ticks = self.__tick_cache__[key]
for newTick in newTicks:
ticks.append_item(newTick)
def on_getpositions(self, stdCode:str, qty:float, isLast:bool):
if len(stdCode) == 0:
return
self.__pos_cache__[stdCode] = qty
def on_getbars(self, stdCode:str, period:str, newBars:list, isLast:bool):
key = "%s#%s" % (stdCode, period)
bars = self.__bar_cache__[key]
for newBar in newBars:
bars.append_bar(newBar)
def on_tick(self, stdCode:str, newTick):
self.__stra_info__.on_tick(self, stdCode, newTick)
def on_bar(self, stdCode:str, period:str, newBar:dict):
pass
def on_calculate(self):
self.__stra_info__.on_calculate(self)
def on_calculate_done(self):
self.__stra_info__.on_calculate_done(self)
def stra_log_text(self, message:str):
'''
输出日志
@message 消息内容\n
'''
self.__wrapper__.sel_log_text(self.__id__, message)
def stra_get_date(self):
'''
获取当前日期\n
@return int,格式如20180513
'''
return self.__wrapper__.sel_get_date()
def stra_get_time(self):
'''
获取当前时间,24小时制,精确到分\n
@return int,格式如1231
'''
return self.__wrapper__.sel_get_time()
def stra_get_price(self, stdCode):
'''
获取最新价格,一般在获取了K线以后再获取该价格
@return 最新价格
'''
return self.__wrapper__.sel_get_price(stdCode)
def stra_get_all_position(self):
'''
获取全部持仓
'''
self.__pos_cache__ = dict() #
self.__wrapper__.sel_get_all_position(self.__id__)
return self.__pos_cache__
def stra_get_bars(self, stdCode:str, period:str, count:int) -> WtKlineData:
'''
获取历史K线
@stdCode 合约代码
@period K线周期,如m3/d7
@count 要拉取的K线条数
'''
key = "%s#%s" % (stdCode, period)
if key in self.__bar_cache__:
#这里做一个数据长度处理
return self.__bar_cache__[key]
self.__bar_cache__[key] = WtKlineData(size=count)
cnt = self.__wrapper__.sel_get_bars(self.__id__, stdCode, period, count)
if cnt == 0:
return None
df_bars = self.__bar_cache__[key]
df_bars.closed = False
return df_bars
def stra_get_ticks(self, stdCode:str, count:int) -> WtHftData:
'''
获取tick数据
@stdCode 合约代码
@count 要拉取的tick数量
'''
self.__bar_cache__[stdCode] = WtHftData(capacity=count)
cnt = self.__wrapper__.cta_get_ticks(self.__id__, stdCode, count)
if cnt == 0:
return None
df_ticks = self.__tick_cache__[stdCode]
return df_ticks
def stra_sub_ticks(self, stdCode:str):
'''
订阅实时行情\n
@stdCode 合约代码
'''
self.__wrapper__.cta_sub_ticks(stdCode)
def stra_get_position(self, stdCode:str = "", usertag:str = "") -> float:
'''
读取当前仓位\n
@stdCode 合约/股票代码\n
@usertag 入场标记
@return 正为多仓,负为空仓
'''
return self.__wrapper__.sel_get_position(self.__id__, stdCode, usertag)
def stra_set_position(self, stdCode:str, qty:float, usertag:str = ""):
'''
设置仓位\n
@stdCode 合约/股票代码\n
@qty 目标仓位,正为多仓,负为空仓\n
@return 设置结果TRUE/FALSE
'''
self.__wrapper__.sel_set_position(self.__id__, stdCode, qty, usertag)
def user_save_data(self, key:str, val):
'''
保存用户数据
@key 数据id
@val 数据值,可以直接转换成str的数据均可
'''
self.__wrapper__.sel_save_user_data(self.__id__, key, str(val))
def user_load_data(self, key:str, defVal = None, vType = float):
'''
读取用户数据
@key 数据id
@defVal 默认数据,如果找不到则返回改数据,默认为None
@return 返回值,默认处理为float数据
'''
ret = self.__wrapper__.sel_load_user_data(self.__id__, key, "")
if ret == "":
return defVal
return vType(ret)
def stra_get_comminfo(self, stdCode:str):
'''
获取品种详情\n
@stdCode 合约代码如SHFE.ag.HOT,或者品种代码如SHFE.ag\n
@return 品种信息,结构请参考ProductMgr中的ProductInfo
'''
if self.__engine__ is None:
return None
return self.__engine__.getProductInfo(stdCode)
def stra_get_sessioninfo(self, stdCode:str):
'''
获取品种详情\n
@stdCode 合约代码如SHFE.ag.HOT,或者品种代码如SHFE.ag\n
@return 品种信息,结构请参考ProductMgr中的ProductInfo
'''
if self.__engine__ is None:
return None
return self.__engine__.getSessionByCode(stdCode)
def stra_get_contract(self, stdCode:str):
'''
获取品种详情\n
@stdCode 合约代码如SHFE.ag.HOT,或者品种代码如SHFE.ag\n
@return 品种信息,结构请参考ProductMgr中的ProductInfo
'''
if self.__engine__ is None:
return None
return self.__engine__.getContractInfo(stdCode)
def stra_get_all_codes(self):
if self.__engine__ is None:
return None
return self.__engine__.getAllCodes()
================================================
FILE: wtpy/SessionMgr.py
================================================
import math
import json
class SectionInfo:
def __init__(self):
self.stime = 0
self.etime = 0
class SessionInfo:
def __init__(self):
self.id = ""
self.name = ""
self.auction = SectionInfo()
self.sections = list()
self.offset = 0
self.totalMins = 0
def toString(self):
'''
将SessionInfo转换成json字符串
'''
obj = dict()
obj["name"] = self.name
obj["offset"] = self.offset
obj["auction"] = {
"from": self.originalTime(self.auction.stime),
"to": self.originalTime(self.auction.etime)
}
obj["sections"] = list()
for secInfo in self.sections:
obj["sections"].append({
"from": self.originalTime(secInfo.stime),
"to": self.originalTime(secInfo.etime)
})
return json.dumps(obj, ensure_ascii=True)
def offsetTime(self, rawTime:int):
curMinute = math.floor(rawTime/100)*60 + rawTime%100
curMinute += self.offset
if curMinute >= 1440:
curMinute -= 1440
elif curMinute < 0:
curMinute += 1440
return math.floor(curMinute/60)*100 + curMinute%60
def originalTime(self, offTime:int):
curMinute = math.floor(offTime/100)*60 + offTime%100
curMinute -= self.offset
if curMinute >= 1440:
curMinute -= 1440
elif curMinute < 0:
curMinute += 1440
return math.floor(curMinute/60)*100 + curMinute%60
def getOpenTime(self, bOffset:bool = False):
if len(self.sections) == 0:
return 0
opentm = self.sections[0].stime
if not bOffset:
return self.originalTime(opentm)
else:
return opentm
def getCloseTime(self, bOffset:bool = False):
if len(self.sections) == 0:
return 0
closetm = self.sections[-1].etime
if not bOffset:
return self.originalTime(closetm)
else:
return closetm
def getTradingMins(self):
if len(self.sections) == 0:
return 0
if self.totalMins == 0:
for sec in self.sections:
s = sec.stime
e = sec.etime
h = math.floor(e / 100) - math.floor(s / 100)
m = (e%100) - (s%100)
self.totalMins += (h*60 + m)
return self.totalMins
def getTradingSecs(self):
return self.getTradingMins()*60
def isLastOfSection(self, rawTime:int):
offTime = self.offsetTime(rawTime)
for sec in self.sections:
if sec.etime == offTime:
return True
return False
def isInTradingTime(self, rawTime:int, bStrict:bool = False):
mins = self.timeToMinutes(rawTime)
if mins == -1:
return False
if bStrict and self.isLastOfSection(rawTime):
return False
return True
def isFirstOfSection(self, rawTime:int):
offTime = self.offsetTime(rawTime)
for sec in self.sections:
if sec.stime == offTime:
return True
return False
def timeToMinutes(self, rawTime:int):
if len(self.sections) == 0:
return -1
offTime = self.offsetTime(rawTime)
bFound = False
offset = 0
for sec in self.sections:
if sec.stime <= offTime and offTime <= sec.etime:
hour = math.floor(offTime / 100) - math.floor(sec.stime / 100)
minute = offTime % 100 - sec.stime % 100
offset += hour*60 + minute
bFound = True
break
else:
hour = math.floor(sec.etime / 100) - math.floor(sec.stime / 100)
minute = sec.etime % 100 - sec.stime % 100
offset += hour*60 + minute
if not bFound:
return -1
return offset
def minutesToTime(self, minutes:int, bHeadFirst:bool = False):
if len(self.sections) == 0:
return -1
offset = minutes
for sec in self.sections:
startMin = math.floor(sec.stime / 100)*60 + sec.stime % 100
stopMin = math.floor(sec.etime / 100)*60 + sec.etime % 100
if not bHeadFirst:
if startMin + offset >= stopMin:
offset -= (stopMin - startMin)
if offset == 0:
return self.originalTime(math.floor(stopMin / 60) * 100 + stopMin % 60)
else:
desMin = startMin + offset
if desMin > 1440:
desMin -= 1440
return self.originalTime(math.floor(desMin / 60) * 100 + desMin % 60)
else:
if startMin + offset >= stopMin:
offset -= (stopMin - startMin)
else:
desMin = startMin + offset
if desMin > 1440:
desMin -= 1440
return self.originalTime(math.floor(desMin / 60) * 100 + desMin % 60)
return self.getCloseTime()
class SessionMgr:
def __init__(self):
self.__sessions__ = dict()
return
def load(self, fname:int):
f = open(fname, 'r', encoding="gbk")
content = f.read()
f.close()
sessions_dict = json.loads(content)
for sid in sessions_dict:
if sid in self.__sessions__:
continue
sObj = sessions_dict[sid]
sInfo = SessionInfo()
sInfo.id = sid
sInfo.offset = sObj["offset"]
sInfo.name = sObj["name"]
if "auction" in sObj:
sInfo.auction.stime = sInfo.offsetTime(sObj["auction"]["from"])
sInfo.auction.etime = sInfo.offsetTime(sObj["auction"]["to"])
for secObj in sObj["sections"]:
secInfo = SectionInfo()
secInfo.stime = sInfo.offsetTime(secObj["from"])
secInfo.etime = sInfo.offsetTime(secObj["to"])
sInfo.sections.append(secInfo)
self.__sessions__[sid] = sInfo
def getSession(self, sid:str) -> SessionInfo:
if sid not in self.__sessions__:
return None
return self.__sessions__[sid]
================================================
FILE: wtpy/StrategyDefs.py
================================================
from wtpy import CtaContext, SelContext, HftContext
class BaseCtaStrategy:
'''
CTA策略基础类,所有的策略都从该类派生
包含了策略的基本开发框架
'''
def __init__(self, name:str):
self.__name__ = name
def name(self) -> str:
return self.__name__
def on_init(self, context:CtaContext):
'''
策略初始化,启动的时候调用
用于加载自定义数据
@context 策略运行上下文
'''
return
def on_session_begin(self, context:CtaContext, curTDate:int):
'''
交易日开始事件
@curTDate 交易日,格式为20210220
'''
return
def on_session_end(self, context:CtaContext, curTDate:int):
'''
交易日结束事件
@curTDate 交易日,格式为20210220
'''
return
def on_calculate(self, context:CtaContext):
'''
K线闭合时调用,一般作为策略的核心计算模块
@context 策略运行上下文
'''
return
def on_calculate_done(self, context:CtaContext):
'''
K线闭合时调用,一般作为策略的核心计算模块
@context 策略运行上下文
'''
return
def on_tick(self, context:CtaContext, stdCode:str, newTick:dict):
'''
逐笔数据进来时调用
生产环境中,每笔行情进来就直接调用
回测环境中,是模拟的逐笔数据
@context 策略运行上下文
@stdCode 合约代码
@newTick 最新逐笔
'''
return
def on_bar(self, context:CtaContext, stdCode:str, period:str, newBar:dict):
'''
K线闭合时回调
@context 策略上下文
@stdCode 合约代码
@period K线周期
@newBar 最新闭合的K线
'''
return
def on_backtest_end(self, context:CtaContext):
'''
回测结束时回调,只在回测框架下会触发
@context 策略上下文
'''
return
class BaseHftStrategy:
'''
HFT策略基础类,所有的策略都从该类派生
包含了策略的基本开发框架
'''
def __init__(self, name:str):
self.__name__ = name
def name(self) -> str:
return self.__name__
def on_init(self, context:HftContext):
'''
策略初始化,启动的时候调用
用于加载自定义数据
@context 策略运行上下文
'''
return
def on_session_begin(self, context:HftContext, curTDate:int):
'''
交易日开始事件
@curTDate 交易日,格式为20210220
'''
return
def on_session_end(self, context:HftContext, curTDate:int):
'''
交易日结束事件
@curTDate 交易日,格式为20210220
'''
return
def on_backtest_end(self, context:CtaContext):
'''
回测结束时回调,只在回测框架下会触发
@context 策略上下文
'''
return
def on_tick(self, context:HftContext, stdCode:str, newTick:dict):
'''
Tick数据进来时调用
@context 策略运行上下文
@stdCode 合约代码
@newTick 最新Tick
'''
return
def on_order_detail(self, context:HftContext, stdCode:str, newOrdQue:dict):
'''
逐笔委托数据进来时调用
@context 策略运行上下文
@stdCode 合约代码
@newOrdQue 最新逐笔委托
'''
return
def on_order_queue(self, context:HftContext, stdCode:str, newOrdQue:dict):
'''
委托队列数据进来时调用
@context 策略运行上下文
@stdCode 合约代码
@newOrdQue 最新委托队列
'''
return
def on_transaction(self, context:HftContext, stdCode:str, newTrans:dict):
'''
逐笔成交数据进来时调用
@context 策略运行上下文
@stdCode 合约代码
@newTrans 最新逐笔成交
'''
return
def on_bar(self, context:HftContext, stdCode:str, period:str, newBar:dict):
'''
K线闭合时回调
@context 策略上下文
@stdCode 合约代码
@period K线周期
@newBar 最新闭合的K线
'''
return
def on_channel_ready(self, context:HftContext):
'''
交易通道就绪通知
@context 策略上下文
'''
return
def on_channel_lost(self, context:HftContext):
'''
交易通道丢失通知
@context 策略上下文
'''
return
def on_entrust(self, context:HftContext, localid:int, stdCode:str, bSucc:bool, msg:str, userTag:str):
'''
下单结果回报
@context 策略上下文
@localid 本地订单id
@stdCode 合约代码
@bSucc 下单结果
@mes 下单结果描述
'''
return
def on_order(self, context:HftContext, localid:int, stdCode:str, isBuy:bool, totalQty:float, leftQty:float, price:float, isCanceled:bool, userTag:str):
'''
订单回报
@context 策略上下文
@localid 本地订单id
@stdCode 合约代码
@isBuy 是否买入
@totalQty 下单数量
@leftQty 剩余数量
@price 下单价格
@isCanceled 是否已撤单
'''
return
def on_trade(self, context:HftContext, localid:int, stdCode:str, isBuy:bool, qty:float, price:float, userTag:str):
'''
成交回报
@context 策略上下文
@stdCode 合约代码
@isBuy 是否买入
@qty 成交数量
@price 成交价格
'''
return
class BaseSelStrategy:
'''
选股策略基础类,所有的多因子策略都从该类派生
包含了策略的基本开发框架
'''
def __init__(self, name:str):
self.__name__ = name
def name(self) -> str:
return self.__name__
def on_init(self, context:SelContext):
'''
策略初始化,启动的时候调用
用于加载自定义数据
@context 策略运行上下文
'''
return
def on_session_begin(self, context:SelContext, curTDate:int):
'''
交易日开始事件
@curTDate 交易日,格式为20210220
'''
return
def on_session_end(self, context:SelContext, curTDate:int):
'''
交易日结束事件
@curTDate 交易日,格式为20210220
'''
return
def on_calculate(self, context:SelContext):
'''
K线闭合时调用,一般作为策略的核心计算模块
@context 策略运行上下文
'''
return
def on_calculate_done(self, context:SelContext):
'''
K线闭合时调用,一般作为策略的核心计算模块
@context 策略运行上下文
'''
return
def on_backtest_end(self, context:CtaContext):
'''
回测结束时回调,只在回测框架下会触发
@context 策略上下文
'''
return
def on_tick(self, context:SelContext, stdCode:str, newTick:dict):
'''
逐笔数据进来时调用
生产环境中,每笔行情进来就直接调用
回测环境中,是模拟的逐笔数据
@context 策略运行上下文
@stdCode 合约代码
@newTick 最新逐笔
'''
return
def on_bar(self, context:SelContext, stdCode:str, period:str, newBar:dict):
'''
K线闭合时回调
@context 策略上下文
@stdCode 合约代码
@period K线周期
@newBar 最新闭合的K线
'''
return
================================================
FILE: wtpy/WtBtEngine.py
================================================
from wtpy.wrapper import WtBtWrapper
from wtpy.CtaContext import CtaContext
from wtpy.SelContext import SelContext
from wtpy.HftContext import HftContext
from wtpy.StrategyDefs import BaseCtaStrategy, BaseSelStrategy, BaseHftStrategy
from wtpy.ExtToolDefs import BaseIndexWriter
from wtpy.WtCoreDefs import EngineType
from wtpy.WtUtilDefs import singleton
from .ProductMgr import ProductMgr, ProductInfo
from .SessionMgr import SessionMgr, SessionInfo
from .ContractMgr import ContractMgr, ContractInfo
from .CodeHelper import CodeHelper
import os
import json
@singleton
class WtBtEngine:
def __init__(self, eType:EngineType = EngineType.ET_CTA, logCfg:str = "logcfgbt.json", isFile:bool = True, bDumpCfg:bool = False, outDir:str = "./outputs_bt"):
self.is_backtest = True
self.__wrapper__ = WtBtWrapper(self) #api接口转换器
self.__context__ = None #策略ctx映射表
self.__config__ = dict() #框架配置项
self.__cfg_commited__ = False #配置是否已提交
self.__idx_writer__ = None #指标输出模块
self.__dump_config__ = bDumpCfg #是否保存最终配置
if eType == eType.ET_CTA:
self.__wrapper__.initialize_cta(logCfg, isFile, outDir) #初始化CTA环境
elif eType == eType.ET_HFT:
self.__wrapper__.initialize_hft(logCfg, isFile, outDir) #初始化HFT环境
elif eType == eType.ET_SEL:
self.__wrapper__.initialize_sel(logCfg, isFile, outDir) #初始化SEL环境
def __check_config__(self):
'''
检查设置项\n
主要会补充一些默认设置项
'''
if "replayer" not in self.__config__:
self.__config__["replayer"] = dict()
self.__config__["replayer"]["basefiles"] = dict()
if "replayer" not in self.__config__:
self.__config__["replayer"] = dict()
self.__config__["replayer"]["mode"] = "csv"
self.__config__["replayer"]["path"] = "./storage/"
if "env" not in self.__config__:
self.__config__["env"] = dict()
self.__config__["env"]["mocker"] = "cta"
def set_writer(self, writer:BaseIndexWriter):
'''
设置指标输出模块
'''
self.__idx_writer__ = writer
def write_indicator(self, id, tag, time, data):
'''
写入指标数据
'''
if self.__idx_writer__ is not None:
self.__idx_writer__.write_indicator(id, tag, time, data)
def init(self, folder:str, cfgfile:str = "configbt.json", commfile:str="commodities.json", contractfile:str="contracts.json"):
'''
初始化\n
@folder 基础数据文件目录,\\结尾\n
@cfgfile 配置文件,json格式
'''
f = open(cfgfile, "r")
content =f.read()
self.__config__ = json.loads(content)
f.close()
self.__check_config__()
self.__config__["replayer"]["basefiles"]["commodity"] = folder + commfile
self.__config__["replayer"]["basefiles"]["contract"] = folder + contractfile
self.__config__["replayer"]["basefiles"]["holiday"] = folder + "holidays.json"
self.__config__["replayer"]["basefiles"]["session"] = folder + "sessions.json"
self.__config__["replayer"]["basefiles"]["hot"] = folder + "hots.json"
self.productMgr = ProductMgr()
self.productMgr.load(folder + commfile)
self.contractMgr = ContractMgr()
self.contractMgr.load(folder + contractfile)
self.sessionMgr = SessionMgr()
self.sessionMgr.load(folder + "sessions.json")
def configMocker(self, name:str):
'''
设置模拟器
'''
self.__config__["env"]["mocker"] = name
def configBacktest(self, stime:int, etime:int):
'''
配置回测设置项\n
@stime 开始时间\n
@etime 结束时间
'''
self.__config__["replayer"]["stime"] = int(stime)
self.__config__["replayer"]["etime"] = int(etime)
def configBTStorage(self, mode:str, path:str = None, dbcfg:dict = None):
'''
配置数据存储\n
@mode 存储模式,csv-表示从csv直接读取,一般回测使用,wtp-表示使用wt框架自带数据存储
'''
self.__config__["replayer"]["mode"] = mode
if path is not None:
self.__config__["replayer"]["path"] = path
if dbcfg is not None:
self.__config__["replayer"]["db"] = dbcfg
def setExternalCtaStrategy(self, id:str, module:str, typeName:str, params:dict):
'''
添加外部的CTA策略
'''
if "cta" not in self.__config__:
self.__config__["cta"] = dict()
self.__config__["cta"]["module"] = module
if "strategy" not in self.__config__["cta"]:
self.__config__["cta"]["strategy"] = dict()
self.__config__["cta"]["strategy"]["id"] = id
self.__config__["cta"]["strategy"]["name"] = typeName
self.__config__["cta"]["strategy"]["params"] = params
def setExternalHftStrategy(self, id:str, module:str, typeName:str, params:dict):
'''
添加外部的HFT策略
'''
if "hft" not in self.__config__:
self.__config__["hft"] = dict()
self.__config__["hft"]["module"] = module
if "strategy" not in self.__config__["hft"]:
self.__config__["hft"]["strategy"] = dict()
self.__config__["hft"]["strategy"]["id"] = id
self.__config__["hft"]["strategy"]["name"] = typeName
self.__config__["hft"]["strategy"]["params"] = params
def commitBTConfig(self):
'''
提交配置\n
只有第一次调用会生效,不可重复调用\n
如果执行run之前没有调用,run会自动调用该方法
'''
if self.__cfg_commited__:
return
cfgfile = json.dumps(self.__config__, indent=4, sort_keys=True)
self.__wrapper__.config_backtest(cfgfile, False)
self.__cfg_commited__ = True
if self.__dump_config__:
f = open("config_run.json", 'w')
f.write(cfgfile)
f.close()
def getSessionByCode(self, code:str) -> SessionInfo:
'''
通过合约代码获取交易时间模板\n
@code 合约代码,格式如SHFE.rb.HOT
'''
pid = CodeHelper.stdCodeToStdCommID(code)
pInfo = self.productMgr.getProductInfo(pid)
if pInfo is None:
return None
return self.sessionMgr.getSession(pInfo.session)
def getSessionByName(self, sname:str) -> SessionInfo:
'''
通过模板名获取交易时间模板\n
@sname 模板名
'''
return self.sessionMgr.getSession(sname)
def getProductInfo(self, code:str) -> ProductInfo:
'''
获取品种信息\n
@code 合约代码,格式如SHFE.rb.HOT
'''
return self.productMgr.getProductInfo(code)
def getContractInfo(self, code:str) -> ContractInfo:
'''
获取品种信息\n
@code 合约代码,格式如SHFE.rb.HOT
'''
return self.contractMgr.getContractInfo(code)
def getAllCodes(self) -> list:
'''
获取全部合约代码
'''
return self.contractMgr.getTotalCodes()
def set_time_range(self, beginTime:int, endTime:int):
'''
设置回测时间\r
@beginTime 开始时间,格式如yyyymmddHHMM
@endTime 结束时间,格式如yyyymmddHHMM
'''
self.__wrapper__.set_time_range(beginTime, endTime)
def set_cta_strategy(self, strategy:BaseCtaStrategy, slippage:int = 0, hook:bool = False, persistData:bool = True):
'''
添加策略\n
@strategy 策略对象
@slippage 滑点大小
@hook 是否安装钩子,主要用于单步控制重算
'''
ctxid = self.__wrapper__.init_cta_mocker(strategy.name(), slippage, hook, persistData)
self.__context__ = CtaContext(ctxid, strategy, self.__wrapper__, self)
def set_hft_strategy(self, strategy:BaseHftStrategy, hook:bool = False):
'''
添加策略\n
@strategy 策略对象
@hook 是否安装钩子,主要用于单步控制重算
'''
ctxid = self.__wrapper__.init_hft_mocker(strategy.name(), hook)
self.__context__ = HftContext(ctxid, strategy, self.__wrapper__, self)
def set_sel_strategy(self, strategy:BaseSelStrategy, date:int=0, time:int=0, period:str="d", trdtpl:str="CHINA", session:str="TRADING", slippage:int = 0):
'''
添加策略\n
@strategy 策略对象
'''
ctxid = self.__wrapper__.init_sel_mocker(strategy.name(), date, time, period, trdtpl, session, slippage)
self.__context__ = SelContext(ctxid, strategy, self.__wrapper__, self)
def get_context(self, id:int):
return self.__context__
def run_backtest(self, bAsync:bool = False, bNeedDump:bool = True):
'''
运行框架
@bAsync 是否异步运行,默认为false
'''
if not self.__cfg_commited__: #如果配置没有提交,则自动提交一下
self.commitBTConfig()
self.__wrapper__.run_backtest(bNeedDump = bNeedDump, bAsync = bAsync)
def cta_step(self, remark:str = "") -> bool:
'''
CTA策略单步执行
@remark 单步备注信息,没有实际作用,主要用于外部调用区分步骤
'''
return self.__wrapper__.cta_step(self.__context__.id)
def hft_step(self):
'''
HFT策略单步执行
'''
self.__wrapper__.hft_step(self.__context__.id)
def stop_backtest(self):
'''
手动停止回测
'''
self.__wrapper__.stop_backtest()
def release_backtest(self):
'''
释放框架
'''
self.__wrapper__.release_backtest()
def on_init(self):
return
def on_schedule(self, date:int, time:int, taskid:int = 0):
return
def on_session_begin(self, date:int):
return
def on_session_end(self, date:int):
return
def on_backtest_end(self):
if self.__context__ is None:
return
self.__context__.on_backtest_end()
def clear_cache(self):
'''
清除缓存的数据,即加已经加载到内存中的数据全部清除
'''
self.__wrapper__.clear_cache()
================================================
FILE: wtpy/WtCoreDefs.py
================================================
from ctypes import c_uint, c_void_p, CFUNCTYPE, POINTER, c_char_p, c_bool, c_ulong, c_double
from ctypes import Structure, c_char, c_int32, c_uint16, c_uint32, c_uint64, addressof, sizeof
from copy import copy
import numpy as np
import pandas as pd
from typing import Any
MAX_INSTRUMENT_LENGTH = c_char*32
MAX_EXCHANGE_LENGTH = c_char*10
PriceQueueType = c_double*10
VolumeQueueType = c_uint32*10
class WTSStruct(Structure):
@property
def fields(self) -> list:
return self._fields_
@property
def values(self) -> tuple:
return tuple(getattr(self, i[0]) for i in self._fields_)
@property
def to_dict(self) -> dict:
return {i[0]:getattr(self, i[0]) for i in self._fields_}
class WTSTickStruct(WTSStruct):
'''
C接口传递的tick数据结构
'''
_fields_ = [("exchg", MAX_EXCHANGE_LENGTH),
("code", MAX_INSTRUMENT_LENGTH),
("price", c_double),
("open", c_double),
("high", c_double),
("low", c_double),
("settle_price", c_double),
("upper_limit", c_double),
("lower_limit", c_double),
("total_volume", c_uint32),
("volume", c_uint32),
("total_turnover", c_double),
("turn_over", c_double),
("open_interest", c_uint32),
("diff_interest", c_int32),
("trading_date", c_uint32),
("action_date", c_uint32),
("action_time", c_uint32),
("pre_close", c_double),
("pre_settle", c_double),
("pre_interest", c_uint32),
("bid_prices", PriceQueueType),
("ask_prices", PriceQueueType),
("bid_qty", VolumeQueueType),
("ask_qty", VolumeQueueType)]
_pack_ = 1
@property
def fields(self) -> list:
fields = self._fields_.copy()
fields[0] = ('exchg', 'S10')
fields[1] = ('code', 'S10')
fields[-4] = ('bid_prices', 'O')
fields[-3] = ('ask_prices', 'O')
fields[-2] = ('bid_qty', 'O')
fields[-1] = ('ask_qty', 'O')
return fields
class WTSBarStruct(WTSStruct):
'''
C接口传递的bar数据结构
'''
_fields_ = [("date", c_uint32),
("time", c_uint32),
("open", c_double),
("high", c_double),
("low", c_double),
("close", c_double),
("settle", c_double),
("money", c_double),
("vol", c_uint32),
("hold", c_uint32),
("diff", c_int32)]
_pack_ = 1
class WTSTransStruct(WTSStruct):
'''
C接口传递的逐笔成交数据结构
'''
_fields_ = [("exchg", MAX_EXCHANGE_LENGTH),
("code", MAX_INSTRUMENT_LENGTH),
("trading_date", c_uint32),
("action_date", c_uint32),
("action_time", c_uint32),
("index", c_uint32),
("ttype", c_int32),
("side", c_int32),
("price", c_double),
("volume", c_uint32),
("askorder", c_int32),
("bidorder", c_int32)]
_pack_ = 1
class WTSOrdQueStruct(WTSStruct):
'''
C接口传递的委托队列数据结构
'''
_fields_ = [("exchg", MAX_EXCHANGE_LENGTH),
("code", MAX_INSTRUMENT_LENGTH),
("trading_date", c_uint32),
("action_date", c_uint32),
("action_time", c_uint32),
("side", c_int32),
("price", c_double),
("order_items", c_uint32),
("qsize", c_uint32),
("volumes", c_uint32*50)]
_pack_ = 1
class WTSOrdDtlStruct(WTSStruct):
'''
C接口传递的委托明细数据结构
'''
_fields_ = [("exchg", MAX_EXCHANGE_LENGTH),
("code", MAX_INSTRUMENT_LENGTH),
("trading_date", c_uint32),
("action_date", c_uint32),
("action_time", c_uint32),
("index", c_uint32),
("side", c_int32),
("price", c_double),
("volume", c_uint32),
("otype", c_int32)]
_pack_ = 1
class CacheList(list):
def to_record(self) -> np.recarray:
data = np.empty(len(self), dtype=self[0].fields)
for k, v in enumerate(self):
data[k] = v.values
return data.view(np.recarray)
def to_pandas(self) -> pd.DataFrame:
return pd.DataFrame(self.to_record())
class BarList(CacheList):
def on_read_bar(self, curBar:POINTER(WTSBarStruct), count:int, isLast:bool):
bsSize = sizeof(WTSBarStruct)
addr = addressof(curBar.contents)
for i in range(count):
thisBar = WTSBarStruct.from_address(addr)
self.append(copy(thisBar))
addr += bsSize
def on_data_count(self, count:int):
pass
class TickList(CacheList):
def on_read_tick(self, curTick:POINTER(WTSTickStruct), count:int, isLast:bool):
tsSize = sizeof(WTSTickStruct)
addr = addressof(curTick.contents)
for i in range(count):
thisTick = WTSTickStruct.from_address(addr)
self.append(copy(thisTick))
addr += tsSize
def on_data_count(self, count:int):
pass
# 回调函数定义
#策略初始化回调
CB_STRATEGY_INIT = CFUNCTYPE(c_void_p, c_ulong)
#策略tick数据推送回调
CB_STRATEGY_TICK = CFUNCTYPE(c_void_p, c_ulong, c_char_p, POINTER(WTSTickStruct))
#策略获取tick数据的单条tick同步回调
CB_STRATEGY_GET_TICK = CFUNCTYPE(c_void_p, c_ulong, c_char_p, POINTER(WTSTickStruct), c_uint32, c_bool)
#策略重算回调(CTA/SEL策略)
CB_STRATEGY_CALC = CFUNCTYPE(c_void_p, c_ulong, c_ulong, c_ulong)
#策略订阅的K线闭合事件回调
CB_STRATEGY_BAR = CFUNCTYPE(c_void_p, c_ulong, c_char_p, c_char_p, POINTER(WTSBarStruct))
#策略获取K线数据的单条K线同步回调
CB_STRATEGY_GET_BAR = CFUNCTYPE(c_void_p, c_ulong, c_char_p, c_char_p, POINTER(WTSBarStruct), c_uint32, c_bool)
#策略获取全部持仓的同步回调
CB_STRATEGY_GET_POSITION = CFUNCTYPE(c_void_p, c_ulong, c_char_p, c_double, c_bool)
#交易日开始结束事件回调
CB_SESSION_EVENT = CFUNCTYPE(c_void_p, c_ulong, c_ulong, c_bool)
#引擎事件回调(交易日开启结束等)
CB_ENGINE_EVENT = CFUNCTYPE(c_void_p, c_ulong, c_ulong, c_ulong)
#HFT策略交易通道事件回调
CB_HFTSTRA_CHNL_EVT = CFUNCTYPE(c_void_p, c_ulong, c_char_p, c_ulong)
#HFT策略订单推送回报
CB_HFTSTRA_ORD = CFUNCTYPE(c_void_p, c_ulong, c_ulong, c_char_p, c_bool, c_double, c_double, c_double, c_bool, c_char_p)
#HFT策略成交推送回报
CB_HFTSTRA_TRD = CFUNCTYPE(c_void_p, c_ulong, c_ulong, c_char_p, c_bool, c_double, c_double, c_char_p)
#HFT策略下单结果回报
CB_HFTSTRA_ENTRUST = CFUNCTYPE(c_void_p, c_ulong, c_ulong, c_char_p, c_bool, c_char_p, c_char_p)
#策略委托队列推送回调
CB_HFTSTRA_ORDQUE = CFUNCTYPE(c_void_p, c_ulong, c_char_p, POINTER(WTSOrdQueStruct))
#策略获取委托队列数据的单条数据同步回调
CB_HFTSTRA_GET_ORDQUE = CFUNCTYPE(c_void_p, c_ulong, c_char_p, POINTER(WTSOrdQueStruct), c_uint32, c_bool)
#策略委托明细推送回调
CB_HFTSTRA_ORDDTL = CFUNCTYPE(c_void_p, c_ulong, c_char_p, POINTER(WTSOrdDtlStruct))
#策略获取委托明细数据的单条数据同步回调
CB_HFTSTRA_GET_ORDDTL = CFUNCTYPE(c_void_p, c_ulong, c_char_p, POINTER(WTSOrdDtlStruct), c_uint32, c_bool)
#策略成交明细推送回调
CB_HFTSTRA_TRANS = CFUNCTYPE(c_void_p, c_ulong, c_char_p, POINTER(WTSTransStruct))
#策略获取成交明细数据的单条数据同步回调
CB_HFTSTRA_GET_TRANS = CFUNCTYPE(c_void_p, c_ulong, c_char_p, POINTER(WTSTransStruct), c_uint32, c_bool)
EVENT_ENGINE_INIT = 1 #框架初始化
EVENT_SESSION_BEGIN = 2 #交易日开始
EVENT_SESSION_END = 3 #交易日结束
EVENT_ENGINE_SCHDL = 4 #框架调度
EVENT_BACKTEST_END = 5 #回测结束
CHNL_EVENT_READY = 1000 #通道就绪事件
CHNL_EVENT_LOST = 1001 #通道断开事件
from enum import Enum
class EngineType(Enum):
'''
引擎类型
枚举变量
'''
ET_CTA = 999
ET_HFT = 1000
ET_SEL = 1001
'''
Parser外接实现
'''
EVENT_PARSER_INIT = 1; #Parser初始化
EVENT_PARSER_CONNECT = 2; #Parser连接
EVENT_PARSER_DISCONNECT = 3; #Parser断开连接
EVENT_PARSER_RELEASE = 4; #Parser释放
CB_PARSER_EVENT = CFUNCTYPE(c_void_p, c_ulong, c_char_p)
CB_PARSER_SUBCMD = CFUNCTYPE(c_void_p, c_char_p, c_char_p, c_bool)
'''
Executer外接实现
'''
CB_EXECUTER_INIT = CFUNCTYPE(c_void_p, c_char_p)
CB_EXECUTER_CMD = CFUNCTYPE(c_void_p, c_char_p, c_char_p, c_double)
================================================
FILE: wtpy/WtDataDefs.py
================================================
import numpy as np
from pandas import DataFrame
class WtKlineData:
def __init__(self, size:int, bAlloc:bool = True):
self.capacity:int = size
self.size:int = 0
if bAlloc:
self.bartimes = np.zeros(self.capacity, np.int64)
self.opens = np.zeros(self.capacity)
self.highs = np.zeros(self.capacity)
self.lows = np.zeros(self.capacity)
self.closes = np.zeros(self.capacity)
self.volumes = np.zeros(self.capacity)
else:
self.bartimes = None
self.opens = None
self.highs = None
self.lows = None
self.closes = None
self.volumes = None
def append_bar(self, newBar:dict):
pos = self.size
if pos == self.capacity:
self.bartimes[:-1] = self.bartimes[1:]
self.opens[:-1] = self.opens[1:]
self.highs[:-1] = self.highs[1:]
self.lows[:-1] = self.lows[1:]
self.closes[:-1] = self.closes[1:]
self.volumes[:-1] = self.volumes[1:]
pos = -1
else:
self.size += 1
self.bartimes[pos] = newBar["bartime"]
self.opens[pos] = newBar["open"]
self.highs[pos] = newBar["high"]
self.lows[pos] = newBar["low"]
self.closes[pos] = newBar["close"]
self.volumes[pos] = newBar["volume"]
def is_empty(self) -> bool:
return self.size==0
def clear(self):
self.size = 0
self.bartimes:np.ndarray = np.zeros(self.capacity, np.int64)
self.opens:np.ndarray = np.zeros(self.capacity)
self.highs:np.ndarray = np.zeros(self.capacity)
self.lows:np.ndarray = np.zeros(self.capacity)
self.closes:np.ndarray = np.zeros(self.capacity)
self.volumes:np.ndarray = np.zeros(self.capacity)
def get_bar(self, iLoc:int = -1) -> dict:
if self.is_empty():
return None
lastBar = dict()
lastBar["bartime"] = self.bartimes[iLoc]
lastBar["open"] = self.opens[iLoc]
lastBar["high"] = self.highs[iLoc]
lastBar["low"] = self.lows[iLoc]
lastBar["close"] = self.closes[iLoc]
lastBar["volume"] = self.volumes[iLoc]
return lastBar
def slice(self, iStart:int = 0, iEnd:int = -1, bCopy:bool = False):
if self.is_empty():
return None
bartimes = self.bartimes[iStart:iEnd]
cnt = len(bartimes)
ret = WtKlineData(cnt, False)
ret.size = cnt
if bCopy:
ret.bartimes = bartimes.copy()
ret.opens = self.opens[iStart:iEnd].copy()
ret.highs = self.highs[iStart:iEnd].copy()
ret.lows = self.lows[iStart:iEnd].copy()
ret.closes = self.closes[iStart:iEnd].copy()
ret.volumes = self.volumes[iStart:iEnd].copy()
else:
ret.bartimes = bartimes
ret.opens = self.opens[iStart:iEnd]
ret.highs = self.highs[iStart:iEnd]
ret.lows = self.lows[iStart:iEnd]
ret.closes = self.closes[iStart:iEnd]
ret.volumes = self.volumes[iStart:iEnd]
return ret
def to_df(self) -> DataFrame:
ret = DataFrame({
"bartime":self.bartimes,
"open":self.opens,
"high":self.highs,
"low":self.lows,
"close":self.closes,
"volume":self.volumes
})
ret.set_index(self.bartimes)
return ret
class WtHftData:
def __init__(self, capacity:int):
self.capacity:int = capacity
self.size:int = 0
self.items = [None]*capacity
def append_item(self, newItem:dict):
pos = self.size
if pos == self.capacity:
self.items[:-1] = self.items[1:]
pos = -1
else:
self.size += 1
self.items[pos] = newItem
def is_empty(self) -> bool:
return self.size==0
def clear(self):
self.size = 0
self.items = []*self.capacity
def get_item(self, iLoc:int=-1) -> dict:
if self.is_empty():
return None
return self.items[iLoc]
def to_df(self) -> DataFrame:
ret = DataFrame(self.items)
return ret
================================================
FILE: wtpy/WtDtEngine.py
================================================
from wtpy.wrapper import WtDtWrapper
from wtpy.ExtModuleDefs import BaseExtParser
from wtpy.WtUtilDefs import singleton
@singleton
class WtDtEngine:
def __init__(self):
self.__wrapper__ = WtDtWrapper() #api接口转换器
self.__ext_parsers__ = dict() #外接的行情接入模块
def initialize(self, cfgfile:str = "dtcfg.json", logprofile:str = "logcfgdt.json"):
'''
数据引擎初始化\n
@cfgfile 配置文件\n
@logprofile 日志模块配置文件
'''
self.__wrapper__.initialize(cfgfile, logprofile)
def run(self):
'''
运行数据引擎
'''
self.__wrapper__.run_datakit()
def add_exetended_parser(self, parser:BaseExtParser):
'''
添加扩展parser
'''
id = parser.id()
if id not in self.__ext_parsers__:
self.__ext_parsers__[id] = parser
if not self.__wrapper__.create_extended_parser(id):
self.__ext_parsers__.pop(id)
def get_extended_parser(self, id:str)->BaseExtParser:
'''
根据id获取扩展parser
'''
if id not in self.__ext_parsers__:
return None
return self.__ext_parsers__[id]
def push_quote_from_extended_parser(self, id:str, newTick, bNeedSlice:bool):
'''
向底层推送tick数据
'''
self.__wrapper__.push_quote_from_exetended_parser(id, newTick, bNeedSlice)
================================================
FILE: wtpy/WtDtServo.py
================================================
from wtpy.WtUtilDefs import singleton
from wtpy.wrapper import WtDtServoApi
from wtpy.WtCoreDefs import BarList, TickList, WTSBarStruct, WTSTickStruct
from flask import Flask, session, redirect, request, make_response
from flask_compress import Compress
import urllib.request
import io
import gzip
import json
def pack_rsp(obj):
rsp = make_response(json.dumps(obj))
rsp.headers["content-type"]= "text/json;charset=utf-8"
return rsp
def parse_data():
try:
data = request.get_data()
json_data = json.loads(data.decode("utf-8"))
return True,json_data
except:
return False, {
"result": -998,
"message": "请求数据解析失败"
}
def get_param(json_data, key:str, type=str, defVal = ""):
if key not in json_data:
return defVal
else:
return type(json_data[key])
def httpPost(url, datas:dict, encoding='utf-8') -> dict:
headers = {
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
'Accept-encoding': 'gzip'
}
data = json.dumps(datas).encode("utf-8")
request = urllib.request.Request(url, data, headers)
if True:
f = urllib.request.urlopen(request)
ec = f.headers.get('Content-Encoding')
if ec == 'gzip':
cd = f.read()
cs = io.BytesIO(cd)
f = gzip.GzipFile(fileobj=cs)
ret = json.loads(f.read().decode(encoding))
f.close()
return ret
else:
return None
@singleton
class WtDtServo:
# 构造函数,传入动态库名
def __init__(self):
self.__config__ = None
self.__cfg_commited__ = False
self.local_api = None
self.server_inst = None
self.remote_api = None
def __check_config__(self):
'''
检查设置项\n
主要会补充一些默认设置项
'''
if self.local_api is None:
self.local_api = WtDtServoApi()
if self.__config__ is None:
self.__config__ = dict()
if "basefiles" not in self.__config__:
self.__config__["basefiles"] = dict()
if "data" not in self.__config__:
self.__config__["data"] = {
"store":{
"path":"./storage/"
}
}
def setRemoteUrl(self, url:str="http://127.0.0.1:8081"):
if self.__config__ is not None:
raise Exception('WtDtServo is already in local mode')
return
self.remote_api = WtDtRemoteServo(url)
def setBasefiles(self, commfile:str="./common/commodities.json", contractfile:str="./common/contracts.json",
holidayfile:str="./common/holidays.json", sessionfile:str="./common/sessions.json", hotfile:str="./common/hots.json"):
'''
C接口初始化
'''
self.__check_config__()
self.__config__["basefiles"]["commodity"] = commfile
self.__config__["basefiles"]["contract"] = contractfile
self.__config__["basefiles"]["holiday"] = holidayfile
self.__config__["basefiles"]["session"] = sessionfile
self.__config__["basefiles"]["hot"] = hotfile
def setStorage(self, path:str = "./storage/"):
self.__config__["data"]["store"]["path"] = path
def commitConfig(self):
if self.remote_api is not None:
raise Exception('WtDtServo is already in remote mode')
return
if self.__cfg_commited__:
return
cfgfile = json.dumps(self.__config__, indent=4, sort_keys=True)
try:
self.local_api.initialize(cfgfile, False)
self.__cfg_commited__ = True
except OSError as oe:
print(oe)
def __server_impl__(self, port:int, host:str):
self.server_inst.run(port = port, host = host)
def runServer(self, port:int = 8081, host="0.0.0.0", bSync:bool = True):
if self.remote_api is not None:
raise Exception('WtDtServo is already in remote mode')
return
app = Flask(__name__)
app.secret_key = "!@#$%^&*()"
Compress(app)
self.server_inst = app
@app.route("/getbars", methods=["POST"])
def on_get_bars():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
stdCode = get_param(json_data, "code")
period = get_param(json_data, "period")
fromTime = get_param(json_data, "stime", int, None)
dataCount = get_param(json_data, "count", int, None)
endTime = get_param(json_data, "etime", int)
if (fromTime is None and dataCount is None) or (fromTime is not None and dataCount is not None):
ret = {
"result":-1,
"message":"Only one of stime and count must be valid at the same time"
}
else:
bars = self.local_api.get_bars(stdCode=stdCode, period=period, fromTime=fromTime, dataCount=dataCount, endTime=endTime)
if bars is None:
ret = {
"result":-2,
"message":"Data not found"
}
else:
bar_list = [curBar.to_dict for curBar in bars]
ret = {
"result":0,
"message":"Ok",
"bars": bar_list
}
return pack_rsp(ret)
@app.route("/getticks", methods=["POST"])
def on_get_ticks():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
stdCode = get_param(json_data, "code")
fromTime = get_param(json_data, "stime", int, None)
dataCount = get_param(json_data, "count", int, None)
endTime = get_param(json_data, "etime", int)
if (fromTime is None and dataCount is None) or (fromTime is not None and dataCount is not None):
ret = {
"result":-1,
"message":"Only one of stime and count must be valid at the same time"
}
else:
ticks = self.local_api.get_ticks(stdCode=stdCode, fromTime=fromTime, dataCount=dataCount, endTime=endTime)
if ticks is None:
ret = {
"result":-2,
"message":"Data not found"
}
else:
tick_list = list()
for curTick in ticks:
curTick = curTick.to_dict
curTick["exchg"] = curTick["exchg"].decode()
curTick["code"] = curTick["code"].decode()
# TODO bid/ask还有问题,先剔除
curTick.pop("bid_prices")
curTick.pop("ask_prices")
curTick.pop("bid_qty")
curTick.pop("ask_qty")
tick_list.append(curTick)
ret = {
"result":0,
"message":"Ok",
"ticks": tick_list
}
return pack_rsp(ret)
self.commitConfig()
if bSync:
self.__server_impl__(port, host)
else:
import threading
self.worker = threading.Thread(target=self.__server_impl__, args=(port,host,))
self.worker.setDaemon(True)
self.worker.start()
def get_bars(self, stdCode:str, period:str, fromTime:int = None, dataCount:int = None, endTime:int = 0) -> BarList:
'''
获取K线数据\n
@stdCode 标准合约代码\n
@period 基础K线周期,m1/m5/d\n
@fromTime 开始时间,日线数据格式yyyymmdd,分钟线数据为格式为yyyymmddHHMM\n
@endTime 结束时间,日线数据格式yyyymmdd,分钟线数据为格式为yyyymmddHHMM,为0则读取到最后一条
'''
if self.remote_api is not None:
return self.remote_api.get_bars(stdCode=stdCode, period=period, fromTime=fromTime, dataCount=dataCount, endTime=endTime)
self.commitConfig()
if (fromTime is None and dataCount is None) or (fromTime is not None and dataCount is not None):
raise Exception('Only one of fromTime and dataCount must be valid at the same time')
return self.local_api.get_bars(stdCode=stdCode, period=period, fromTime=fromTime, dataCount=dataCount, endTime=endTime)
def get_ticks(self, stdCode:str, fromTime:int = None, dataCount:int = None, endTime:int = 0) -> TickList:
'''
获取tick数据\n
@stdCode 标准合约代码\n
@fromTime 开始时间,格式为yyyymmddHHMM\n
@endTime 结束时间,格式为yyyymmddHHMM,为0则读取到最后一条
'''
if self.remote_api is not None:
return self.remote_api.get_ticks(stdCode=stdCode, fromTime=fromTime, dataCount=dataCount, endTime=endTime)
self.commitConfig()
if (fromTime is None and dataCount is None) or (fromTime is not None and dataCount is not None):
raise Exception('Only one of fromTime and dataCount must be valid at the same time')
return self.local_api.get_ticks(stdCode=stdCode, fromTime=fromTime, dataCount=dataCount, endTime=endTime)
class WtDtRemoteServo:
def __init__(self, url:str="http://127.0.0.1:8081"):
self.remote_url = url
def get_bars(self, stdCode:str, period:str, fromTime:int = None, dataCount:int = None, endTime:int = 0) -> BarList:
'''
获取K线数据\n
@stdCode 标准合约代码\n
@period 基础K线周期,m1/m5/d\n
@fromTime 开始时间,日线数据格式yyyymmdd,分钟线数据为格式为yyyymmddHHMM\n
@endTime 结束时间,日线数据格式yyyymmdd,分钟线数据为格式为yyyymmddHHMM,为0则读取到最后一条
'''
if (fromTime is None and dataCount is None) or (fromTime is not None and dataCount is not None):
raise Exception('Only one of fromTime and dataCount must be valid at the same time')
url = self.remote_url + "/getbars"
data = {
"code":stdCode,
"period":period,
"etime":endTime
}
if fromTime is not None:
data["stime"] = fromTime
elif dataCount is not None:
data["count"] = dataCount
resObj = httpPost(url, data)
if resObj["result"] < 0:
print(resObj["message"])
return None
barCache = BarList()
for curBar in resObj["bars"]:
bs = WTSBarStruct()
bs.date = curBar["date"]
bs.time = curBar["time"]
bs.open = curBar["open"]
bs.high = curBar["high"]
bs.low = curBar["low"]
bs.close = curBar["close"]
bs.settle = curBar["settle"]
bs.money = curBar["money"]
bs.vol = curBar["vol"]
bs.hold = curBar["hold"]
bs.diff = curBar["diff"]
barCache.append(bs)
return barCache
def get_ticks(self, stdCode:str, fromTime:int = None, dataCount:int = None, endTime:int = 0) -> TickList:
'''
获取tick数据\n
@stdCode 标准合约代码\n
@fromTime 开始时间,格式为yyyymmddHHMM\n
@endTime 结束时间,格式为yyyymmddHHMM,为0则读取到最后一条
'''
if (fromTime is None and dataCount is None) or (fromTime is not None and dataCount is not None):
raise Exception('Only one of fromTime and dataCount must be valid at the same time')
url = self.remote_url + "/getticks"
data = {
"code":stdCode,
"etime":endTime
}
if fromTime is not None:
data["stime"] = fromTime
elif dataCount is not None:
data["count"] = dataCount
resObj = httpPost(url, data)
if resObj["result"] < 0:
print(resObj["message"])
return None
tickCache = TickList()
for curTick in resObj["ticks"]:
ts = WTSTickStruct()
ts.exchg = curTick["exchg"].encode('utf-8')
ts.code = stdCode.encode('utf-8')
ts.open = curTick["open"]
ts.high = curTick["high"]
ts.low = curTick["low"]
ts.price = curTick["price"]
ts.settle_price = curTick["settle_price"]
ts.upper_limit = curTick["upper_limit"]
ts.lower_limit = curTick["lower_limit"]
ts.total_volume = curTick["total_volume"]
ts.volume = curTick["volume"]
ts.total_turnover = curTick["total_turnover"]
ts.turn_over = curTick["turn_over"]
ts.open_interest = curTick["open_interest"]
ts.diff_interest = curTick["diff_interest"]
ts.trading_date = curTick["trading_date"]
ts.action_date = curTick["action_date"]
ts.action_time = curTick["action_time"]
ts.pre_close = curTick["pre_close"]
ts.pre_settle = curTick["pre_settle"]
ts.pre_interest = curTick["pre_interest"]
# TODO 还有bid和ask档位没处理
tickCache.append(ts)
return tickCache
================================================
FILE: wtpy/WtEngine.py
================================================
from wtpy.wrapper import WtWrapper
from wtpy.CtaContext import CtaContext
from wtpy.SelContext import SelContext
from wtpy.HftContext import HftContext
from wtpy.StrategyDefs import BaseCtaStrategy, BaseSelStrategy, BaseHftStrategy
from wtpy.ExtToolDefs import BaseIndexWriter, BaseDataReporter
from wtpy.WtCoreDefs import EngineType
from wtpy.ExtModuleDefs import BaseExtParser, BaseExtExecuter
from wtpy.WtUtilDefs import singleton
from .ProductMgr import ProductMgr, ProductInfo
from .SessionMgr import SessionMgr, SessionInfo
from .ContractMgr import ContractMgr, ContractInfo
from .CodeHelper import CodeHelper
import json
@singleton
class WtEngine:
'''
实盘交易引擎
'''
def __init__(self, eType:EngineType, logCfg:str = "logcfg.json", genDir:str = "generated", bDumpCfg:bool = False):
'''
WtEngine构造函数\n
@eType 引擎类型:EngineType.ET_CTA、EngineType.ET_HFT、EngineType.ET_SEL\n
@logCfg 日志配置文件\n
@genDir 数据输出目录\n
@bDumpCfg 是否保存最终配置文件
'''
self.is_backtest = False
self.__wrapper__ = WtWrapper(self) #api接口转换器
self.__cta_ctxs__ = dict() #CTA策略ctx映射表
self.__sel_ctxs__ = dict() #SEL策略ctx映射表
self.__hft_ctxs__ = dict() #HFT策略ctx映射表
self.__config__ = dict() #框架配置项
self.__cfg_commited__ = False #配置是否已提交
self.__writer__ = None #指标输出模块
self.__reporter__ = None #数据提交模块
self.__ext_parsers__ = dict() #外接的行情接入模块
self.__ext_executers__ = dict() #外接的执行器
self.__dump_config__ = bDumpCfg #是否保存最终配置
self.__engine_type = eType
if eType == EngineType.ET_CTA:
self.__wrapper__.initialize_cta(logCfg=logCfg, isFile=True, genDir=genDir)
elif eType == EngineType.ET_HFT:
self.__wrapper__.initialize_hft(logCfg=logCfg, isFile=True, genDir=genDir)
elif eType == EngineType.ET_SEL:
self.__wrapper__.initialize_sel(logCfg=logCfg, isFile=True, genDir=genDir)
def __check_config__(self):
'''
检查设置项\n
主要会补充一些默认设置项
'''
if "basefiles" not in self.__config__:
self.__config__["basefiles"] = dict()
if "env" not in self.__config__:
self.__config__["env"] = dict()
self.__config__["env"]["name"] = "cta"
self.__config__["env"]["mode"] = "product"
self.__config__["env"]["product"] = {
"session":"TRADING"
}
def getEngineType(self):
return self.__engine_type
def add_exetended_parser(self, parser:BaseExtParser):
id = parser.id()
if id not in self.__ext_parsers__:
self.__ext_parsers__[id] = parser
if not self.__wrapper__.create_extended_parser(id):
self.__ext_parsers__.pop(id)
def add_exetended_executer(self, executer:BaseExtExecuter):
id = executer.id()
if id not in self.__ext_executers__:
self.__ext_executers__[id] = executer
if not self.__wrapper__.create_extended_executer(id):
self.__ext_executers__.pop(id)
def get_extended_parser(self, id:str)->BaseExtParser:
if id not in self.__ext_parsers__:
return None
return self.__ext_parsers__[id]
def get_extended_executer(self, id:str)->BaseExtExecuter:
if id not in self.__ext_executers__:
return None
return self.__ext_executers__[id]
def push_quote_from_extended_parser(self, id:str, newTick, bNeedSlice:bool):
self.__wrapper__.push_quote_from_exetended_parser(id, newTick, bNeedSlice)
def set_writer(self, writer:BaseIndexWriter):
'''
设置指标输出模块
'''
self.__writer__ = writer
def write_indicator(self, id:str, tag:str, time:int, data:dict):
'''
写入指标数据
'''
if self.__writer__ is not None:
self.__writer__.write_indicator(id, tag, time, data)
def set_data_reporter(self, reporter:BaseDataReporter):
'''
设置数据报告器
'''
self.__reporter__ = reporter
def init(self, folder:str, cfgfile:str = "config.json", commfile:str="commodities.json", contractfile:str="contracts.json"):
'''
初始化\n
@folder 基础数据文件目录,\\结尾\n
@cfgfile 配置文件,json格式
'''
f = open(cfgfile, "r")
content =f.read()
self.__config__ = json.loads(content)
f.close()
self.__check_config__()
self.__config__["basefiles"]["commodity"] = folder + commfile
self.__config__["basefiles"]["contract"] = folder + contractfile
self.__config__["basefiles"]["holiday"] = folder + "holidays.json"
self.__config__["basefiles"]["session"] = folder + "sessions.json"
self.__config__["basefiles"]["hot"] = folder + "hots.json"
self.productMgr = ProductMgr()
self.productMgr.load(folder + commfile)
self.contractMgr = ContractMgr()
self.contractMgr.load(folder + contractfile)
self.sessionMgr = SessionMgr()
self.sessionMgr.load(folder + "sessions.json")
def configEngine(self, name:str, mode:str = "product"):
'''
设置引擎和运行模式
'''
self.__config__["env"]["name"] = name
self.__config__["env"]["mode"] = mode
def addExternalCtaStrategy(self, id:str, params:dict):
'''
添加外部的CTA策略
'''
if "strategies" not in self.__config__:
self.__config__["strategies"] = dict()
if "cta" not in self.__config__["strategies"]:
self.__config__["strategies"]["cta"] = list()
params["id"] = id
self.__config__["strategies"]["cta"].append(params)
def addExternalHftStrategy(self, id:str, params:dict):
'''
添加外部的HFT策略
'''
if "strategies" not in self.__config__:
self.__config__["strategies"] = dict()
if "hft" not in self.__config__["strategies"]:
self.__config__["strategies"]["hft"] = list()
params["id"] = id
self.__config__["strategies"]["hft"].append(params)
def configStorage(self, path:str, module:str=""):
'''
配置数据存储\n
@mode 存储模式,csv-表示从csv直接读取,一般回测使用,wtp-表示使用wt框架自带数据存储
'''
self.__config__["data"]["store"]["module"] = module
self.__config__["data"]["store"]["path"] = path
def commitConfig(self):
'''
提交配置\n
只有第一次调用会生效,不可重复调用\n
如果执行run之前没有调用,run会自动调用该方法
'''
if self.__cfg_commited__:
return
cfgfile = json.dumps(self.__config__, indent=4, sort_keys=True)
self.__wrapper__.config(cfgfile, False)
self.__cfg_commited__ = True
if self.__dump_config__:
f = open("config_run.json", 'w')
f.write(cfgfile)
f.close()
def regCtaStraFactories(self, factFolder:str):
'''
向底层模块注册CTA工厂模块目录\n
!!!CTA策略只会被CTA引擎加载!!!\n
@factFolder 工厂模块所在的目录
'''
return self.__wrapper__.reg_cta_factories(factFolder)
def regHftStraFactories(self, factFolder:str):
'''
向底层模块注册HFT工厂模块目录\n
!!!HFT策略只会被HFT引擎加载!!!\n
@factFolder 工厂模块所在的目录
'''
return self.__wrapper__.reg_hft_factories(factFolder)
def regExecuterFactories(self, factFolder:str):
'''
向底层模块注册执行器模块目录\n
!!!执行器只在CTA引擎有效!!!\n
@factFolder 工厂模块所在的目录
'''
return self.__wrapper__.reg_exe_factories(factFolder)
def addExecuter(self, id:str, trader:str, policies:dict, scale:int = 1):
if "executers" not in self.__config__:
self.__config__["executers"] = list()
exeItem = {
"active":True,
"id": id,
"scale": scale,
"policy": policies,
"trader":trader
}
self.__config__["executers"].append(exeItem)
def addTrader(self, id:str, params:dict):
if "traders" not in self.__config__:
self.__config__["traders"] = list()
tItem = params
tItem["active"] = True
tItem["id"] = id
self.__config__["traders"].append(tItem)
def getSessionByCode(self, stdCode:str) -> SessionInfo:
'''
通过合约代码获取交易时间模板\n
@stdCode 合约代码,格式如SHFE.rb.HOT
'''
pid = CodeHelper.stdCodeToStdCommID(stdCode)
pInfo = self.productMgr.getProductInfo(pid)
if pInfo is None:
return None
return self.sessionMgr.getSession(pInfo.session)
def getSessionByName(self, sname:str) -> SessionInfo:
'''
通过模板名获取交易时间模板\n
@sname 模板名
'''
return self.sessionMgr.getSession(sname)
def getProductInfo(self, stdCode:str) -> ProductInfo:
'''
获取品种信息\n
@stdCode 合约代码,格式如SHFE.rb.HOT
'''
return self.productMgr.getProductInfo(stdCode)
def getContractInfo(self, stdCode:str) -> ContractInfo:
'''
获取品种信息\n
@stdCode 合约代码,格式如SHFE.rb.HOT
'''
return self.contractMgr.getContractInfo(stdCode)
def getAllCodes(self) -> list:
'''
获取全部合约代码
'''
return self.contractMgr.getTotalCodes()
def add_cta_strategy(self, strategy:BaseCtaStrategy):
'''
添加CTA策略\n
@strategy 策略对象
'''
id = self.__wrapper__.create_cta_context(strategy.name())
self.__cta_ctxs__[id] = CtaContext(id, strategy, self.__wrapper__, self)
def add_hft_strategy(self, strategy:BaseHftStrategy, trader:str, agent:bool = True):
'''
添加HFT策略\n
@strategy 策略对象
'''
id = self.__wrapper__.create_hft_context(strategy.name(), trader, agent)
self.__hft_ctxs__[id] = HftContext(id, strategy, self.__wrapper__, self)
def add_sel_strategy(self, strategy:BaseSelStrategy, date:int, time:int, period:str):
id = self.__wrapper__.create_sel_context(strategy.name(), date, time, period)
self.__sel_ctxs__[id] = SelContext(id, strategy, self.__wrapper__, self)
def get_context(self, id:int):
'''
根据ID获取策略上下文\n
@id 上下文id,一般添加策略的时候会自动生成一个唯一的上下文id
'''
if self.__engine_type == EngineType.ET_CTA:
if id not in self.__cta_ctxs__:
return None
return self.__cta_ctxs__[id]
elif self.__engine_type == EngineType.ET_HFT:
if id not in self.__hft_ctxs__:
return None
return self.__hft_ctxs__[id]
elif self.__engine_type == EngineType.ET_SEL:
if id not in self.__sel_ctxs__:
return None
return self.__sel_ctxs__[id]
def run(self):
'''
运行框架
'''
if not self.__cfg_commited__: #如果配置没有提交,则自动提交一下
self.commitConfig()
self.__wrapper__.run()
def release(self):
'''
释放框架
'''
self.__wrapper__.release()
def on_init(self):
if self.__reporter__ is not None:
self.__reporter__.report_init_data()
return
def on_schedule(self, date:int, time:int, taskid:int = 0):
# print("engine scheduled")
if self.__reporter__ is not None:
self.__reporter__.report_rt_data()
def on_session_begin(self, date:int):
# print("session begin")
return
def on_session_end(self, date:int):
if self.__reporter__ is not None:
self.__reporter__.report_settle_data()
return
================================================
FILE: wtpy/WtMsgQue.py
================================================
from wtpy.wrapper.WtMQWrapper import WtMQWrapper, CB_ON_MSG
from wtpy.WtUtilDefs import singleton
class WtMQServer:
def __init__(self):
self.id = None
def init(self, wrapper:WtMQWrapper, id:int):
self.id = id
self.wrapper = wrapper
def publish_message(self, topic:str, message:str):
if self.id is None:
raise Exception("MQServer not initialzied")
self.wrapper.publish_message(self.id, topic, message)
class WtMQClient:
def __init__(self):
return
def init(self, wrapper:WtMQWrapper, id:int):
self.id = id
self.wrapper = wrapper
def start(self):
if self.id is None:
raise Exception("MQClient not initialzied")
self.wrapper.start_client(self.id)
def subscribe(self, topic:str):
if self.id is None:
raise Exception("MQClient not initialzied")
self.wrapper.subcribe_topic(self.id, topic)
def on_mq_message(self, topic:str, message:str, dataLen:int):
pass
@singleton
class WtMsgQue:
def __init__(self) -> None:
self._servers = dict()
self._clients = dict()
self._wrapper = WtMQWrapper(self)
self._cb_msg = CB_ON_MSG(self.on_mq_message)
def get_client(self, client_id:int) -> WtMQClient:
if client_id not in self._clients:
return None
return self._clients[client_id]
def on_mq_message(self, client_id:int, topic:str, message:str, dataLen:int):
client = self.get_client(client_id)
if client is None:
return
client.on_mq_message(topic, message, dataLen)
def add_mq_server(self, url:str, server:WtMQServer = None) -> WtMQServer:
id = self._wrapper.create_server(url)
if server is None:
server = WtMQServer()
server.init(self._wrapper, id)
self._servers[id] = server
return server
def destroy_mq_server(self, server:WtMQServer):
id = server.id
if id not in self._servers:
return
self._wrapper.destroy_server(id)
self._servers.pop(id)
def add_mq_client(self, url:str, client:WtMQClient = None) -> WtMQClient:
id = self._wrapper.create_client(url, self._cb_msg)
if client is None:
client = WtMQClient()
client.init(self._wrapper, id)
self._clients[id] = client
return client
def destroy_mq_client(self, client:WtMQClient):
id = client.id
if id not in self._clients:
return
self._wrapper.destroy_client(id)
self._clients.pop(id)
================================================
FILE: wtpy/WtUtilDefs.py
================================================
def singleton(cls):
instances = {}
def getinstance(*args,**kwargs):
if cls not in instances:
instances[cls] = cls(*args,**kwargs)
return instances[cls]
return getinstance
================================================
FILE: wtpy/__init__.py
================================================
from .StrategyDefs import BaseCtaStrategy, BaseSelStrategy, BaseHftStrategy
from .CtaContext import CtaContext
from .SelContext import SelContext
from .HftContext import HftContext
from .WtEngine import WtEngine
from .WtBtEngine import WtBtEngine
from .WtDtEngine import WtDtEngine
from .WtCoreDefs import WTSTickStruct,WTSBarStruct,EngineType
from .WtDataDefs import WtKlineData,WtHftData
from .ExtToolDefs import BaseDataReporter, BaseIndexWriter
from .ExtModuleDefs import BaseExtExecuter, BaseExtParser
from .WtMsgQue import WtMsgQue, WtMQClient, WtMQServer
from .WtDtServo import WtDtServo
from wtpy.wrapper.WtExecApi import WtExecApi
from wtpy.wrapper.ContractLoader import ContractLoader,LoaderType
__all__ = ["BaseCtaStrategy", "BaseSelStrategy", "BaseHftStrategy", "WtEngine", "CtaContext", "SelContext", "HftContext",
"WtBtEngine", "WtDtEngine", "WtExecApi","WTSTickStruct","WTSBarStruct","BaseIndexWriter","BaseIndexWriter",
"EngineType", "WtKlineData", "WtHftData","ContractLoader", "BaseDataReporter", "BaseExtParser", "BaseExtExecuter",
"LoaderType", "WtDtServo", "WtMsgQue", "WtMQClient", "WtMQServer"]
================================================
FILE: wtpy/apps/WtBtAnalyst.py
================================================
from pandas import DataFrame as df
import pandas as pd
import numpy as np
from dateutil.parser import parse
from collections import Counter
from datetime import datetime
import math
import json
from xlsxwriter import Workbook
class Calculate():
'''
绩效比率计算
'''
def __init__(self, ret, mar, rf, period, trade):
"""
:param ret: 收益率序列
:param mar: 最低可接受回报
:param rf: 无风险利率
:param period: 年交易日(用来年化)240
"""
self.ret = ret
self.mar = mar
self.rf = rf
self.trade = math.ceil(trade)
self.period = period
self.daily_rf = (self.rf + 1) ** (1 / self.period) - 1
# 潜在上行比率
def calculate_upside_ratio(self):
acess_return = np.sum(self.ret - self.mar)
downside_std = self.ret[self.ret.apply(lambda x: x < 0)].std()
upside_ratio = acess_return / downside_std
return upside_ratio
# 夏普率
def sharp_ratio(self):
expect_return = self.ret.mean()
std = self.ret.std()
sharp_ratio = (expect_return - self.daily_rf) / std
return sharp_ratio
# 索提诺比率
def sortion_ratio(self):
expect_return = self.ret.mean()
downside_std = self.ret[self.ret.apply(lambda x: x < 0)].std()
sortion_ratio = (expect_return - self.daily_rf) / downside_std
return sortion_ratio
# 最大回撤
def maxDrawdown(self):
ret = (self.ret + 1).cumprod()
i = np.argmax((np.maximum.accumulate(ret) - ret) / np.maximum.accumulate(ret))
if i == 0:
return 0
j = np.argmax(ret[:i])
return (ret[j] - ret[i]) / ret[j]
def maxDrawdown_time(self):
ret = (self.ret + 1).cumprod()
i = np.argmax((np.maximum.accumulate(ret) - ret) / np.maximum.accumulate(ret))
if i == 0:
return 0
j = np.argmax(ret[:i])
return i, j
# 卡尔马比率
def calmar_ratio(self):
maxdrawdown = Calculate.maxDrawdown(self)
annual_return = Calculate.get_annual_return(self)
calmar_ratio = annual_return / maxdrawdown
return calmar_ratio
# 斯特林比率
def sterling_a_ratio(self):
average_drawdown = abs(self.ret.where(self.ret < 0, 0).mean())
annual_return = Calculate.get_annual_return(self)
sterling_a_ratio = (annual_return - self.rf) / average_drawdown
return sterling_a_ratio
# 单笔最大回撤
def single_largest_maxdrawdown(self):
single_largest_mdd = self.ret[self.ret.apply(lambda x: x < 0)]
if len(single_largest_mdd) == 0:
single_largest_mxd = 0
return single_largest_mxd
single_largest_mxd = abs(single_largest_mdd.min())
return single_largest_mxd
# 单笔最大回撤索引
def single_maxdrawdown_time(self):
i = np.argmin(self.ret)
return i
# 年化收益率
def get_annual_return(self):
annual_return = 0 if self.trade==0 else ((1 + self.ret.sum()) ** (self.period / self.trade) - 1)
return annual_return
# 月化收益率
def monthly_return(self):
ann = Calculate.get_annual_return(self)
monthly_return = (ann + 1) ** (1/12) - 1
return monthly_return
# 月平均收益
def monthly_average_return(self):
monthly = self.ret.mean() * (self.period / 12)
return monthly
# 衰落时间
def decay_time(self):
netvalue = (self.ret+1).cumprod()
ser = []
temp = netvalue[0]
ss = 0
for x in netvalue:
if x >= temp:
ss = 0
ser.append(ss)
temp = x
else:
ss = ss + 1
ser.append(ss)
ss = max(pd.Series(ser))
return ss
def fmtNAN(val, defVal = 0):
if math.isnan(val):
return defVal
return val
def continue_trading_analysis(data, x_value) -> dict:
'''
连续交易分析
'''
mean = data['profit'].mean()
std = data['profit'].std()
z_score = (x_value - mean) / std
times = 0
win_time = 0
ltimes = 0
loss_time = 0
for i in range(len(data)-1):
sss = data['profit'][i]
if sss > 0:
times += 1
ltimes = 0
rem = i
if times > win_time:
win_time = times
con_win_p_end = rem
else:
times = 0
ltimes += 1
rem = i
if ltimes > loss_time:
loss_time = ltimes
con_loss_p_end = rem
capital = 500000
con_win_profit = (data['profit'].loc[con_win_p_end-win_time + 1:con_win_p_end]).sum()
con_lose_loss = (data['profit'].loc[con_loss_p_end-loss_time + 1:con_loss_p_end]).sum()
# con_win_p_end, win_time 连续盈利结束位置,连续盈利最大次数
# con_loss_p_end,loss_time 连续亏损结束位置,连续亏损最大次数
cot_profit_ratio = con_win_profit / capital
loss_profit_ratio = con_lose_loss / capital
result = {'z值': z_score,
'最大连续盈利交易次数': win_time,
'最大连续亏损交易次数': loss_time,
'最大连续盈利额': con_win_profit,
'最大连续亏损额': con_lose_loss,
'最大连续盈利(%)': cot_profit_ratio,
'最大连续亏损(%)': loss_profit_ratio}
return result
def nomalize_val(val):
if math.isnan(val):
return 0
else:
return val
def extreme_trading(data, time_of_std=1):
'''
极端交易分析
'''
std = data['profit'].std()
df_wins = data[data["profit"] > 0]
df_loses = data[data["profit"] <= 0]
winamout = df_wins["profit"].sum() # 毛盈利
loseamount = df_loses["profit"].sum() # 毛亏损
trdnetprofit = winamout + loseamount # 交易净盈亏
totaltimes = len(data) # 总交易次数
avgprof = trdnetprofit / totaltimes if totaltimes > 0 else 0 # 单次平均盈亏
# 单笔盈利 + 标准差
sin_profit_plstd = avgprof + (std * time_of_std)
# 单笔盈利 - 标准差
sin_profit_mistd = avgprof - (std * time_of_std)
# 极端交易数量
extreme_result = data[data['profit'].apply(lambda x: x > sin_profit_plstd or x < sin_profit_mistd)]
extreme_num = len(extreme_result)
# 极端交易盈亏 1 Std. Deviation of Avg. Trade
extreme_profit = 0 if extreme_num==0 else extreme_result['profit'].sum()
result = {'1 Std. Deviation of Avg. Trade': nomalize_val(std),
'单笔净利 +1倍标准差': nomalize_val(sin_profit_plstd),
'单笔盈利 - 标准差': nomalize_val(sin_profit_mistd),
'极端交易数量': extreme_num,
'极端交易盈亏': extreme_profit
}
return result
def average_profit(data):
'''
连续交易分析之平均收益
'''
data = data['profit']
win = 0
li = []
lose = 0
li_2 = []
dic= []
dicc = []
for i in range(1, len(data) - 1):
if (data[i] > 0) == (data[i - 1] > 0):
if data[i] > 0:
c = 1
win = win + c
else:
c = 1
lose = lose + c
if (data[i] > 0) == ((data[i + 1]) > 0):
pass
else:
if data[i] > 0:
dis = {str(win): data[i-win:i+1].sum()}
dic.append(dis)
li.append(win)
win = 0
else:
dis = {str(lose): data[i-lose:i+1].sum()}
dicc.append(dis)
li_2.append(lose)
lose = 0
else:
win = 0
lose = 0
number_win = Counter(li)
number_lose = Counter(li_2)
ss = pd.DataFrame()
for x in dic:
df = pd.DataFrame([x])
ss = pd.concat([ss, df.T])
win_ss = ss.reset_index()
win_ss = win_ss.groupby('index').mean()
ss2 = pd.DataFrame()
for y in dicc:
df = pd.DataFrame([y])
ss2 = pd.concat([ss2, df.T])
lose_ss = ss2.reset_index()
lose_ss = lose_ss.groupby('index').mean()
result = {'连续盈利次数': number_win,
'连续亏损次数': number_lose,
'每个序列平均收益': win_ss,
'每个序列平均亏损': lose_ss}
return result
def stat_closes_by_day(df_closes:df, capital) -> df:
'''
按天统计平仓数据
'''
df_closes['day'] = df_closes['opentime']
df_closes['win'] = df_closes['profit'].apply(lambda x: 1 if x > 0 else 0)
df_closes['times'] = 1
df_closes['gross_profit'] = df_closes['profit'].apply(lambda x: x if x > 0 else 0)
df_closes['gross_loss'] = df_closes['profit'].apply(lambda x: x if x < 0 else 0)
profit = df_closes.groupby(df_closes['day']).sum()
profit['win_rate'] = profit['win'] / profit['times']
profit['profit_ratio'] = profit['profit']*100.0/capital
res = profit[['profit', 'gross_profit', 'gross_loss', 'times', 'win_rate','profit_ratio']]
return res.iloc[::-1]
def stat_closes_by_month(df_closes:df, capital) -> df:
'''
按月统计平仓数据
'''
df_closes['month'] = df_closes['opentime'].apply(lambda x: x.strftime("%Y/%m"))
df_closes['win'] = df_closes['profit'].apply(lambda x: 1 if x > 0 else 0)
df_closes['times'] = 1
df_closes['gross_profit'] = df_closes['profit'].apply(lambda x: x if x > 0 else 0)
df_closes['gross_loss'] = df_closes['profit'].apply(lambda x: x if x < 0 else 0)
profit = df_closes.groupby(df_closes['month']).sum()
profit['win_rate'] = profit['win'] / profit['times']
profit['profit_ratio'] = profit['profit']*100.0/capital
res = profit[['profit', 'gross_profit', 'gross_loss', 'times', 'win_rate','profit_ratio']]
return res.iloc[::-1]
def stat_closes_by_year(df_closes:df, capital) -> df:
'''
按年统计平仓数据
'''
df_closes['year'] = df_closes['opentime'].apply(lambda x: x.strftime("%Y"))
df_closes['win'] = df_closes['profit'].apply(lambda x: 1 if x > 0 else 0)
df_closes['times'] = 1
df_closes['gross_profit'] = df_closes['profit'].apply(lambda x: x if x > 0 else 0)
df_closes['gross_loss'] = df_closes['profit'].apply(lambda x: x if x < 0 else 0)
profit = df_closes.groupby(df_closes['year']).sum()
profit['win_rate'] = profit['win'] / profit['times']
profit['profit_ratio'] = profit['profit']*100.0/capital
res = profit[['profit', 'gross_profit', 'gross_loss', 'times', 'win_rate','profit_ratio']]
return res.iloc[::-1]
def time_analysis(df_closes:df) -> dict:
'''
时间分析
'''
trading_time = df_closes['closebarno'][len(df_closes) - 1] * 5
def time_trans(x):
d = x // (24 * 60)
h = x % (24 * 60) // 60
m = x % (24 * 60) % 60
return str(d), str(h), str(m)
# 转化为日,时,分
s_trading_time = time_trans(trading_time)
# 策略运行时间
str_time = (df_closes['closebarno'] - df_closes['openbarno']).sum() * 5
s_str_time = time_trans(str_time)
# 比率
porition = str_time / trading_time * 100
# 最大空仓时间
empty_time = (df_closes['openbarno'].shift(-1) - df_closes['closebarno']).max() * 5
s_empty_time = time_trans(empty_time)
capital = 500000
df_closes['principal'] = df_closes['totalprofit'] + capital
df_closes['principal'] = df_closes['principal'].shift(1)
input_data = df_closes.fillna(value=500000)
ret = input_data['profit'] / input_data['principal']
mar = 0
rf = 0.02
period = 240
trade = input_data['closebarno'][len(input_data) - 1] / 47
factors = Calculate(ret, mar, rf, period, trade)
single_drawdown_date = factors.single_maxdrawdown_time()
signe_drawdown_date = parse(str(input_data['opentime'][single_drawdown_date]))
# 最大回撤区间
maxdrawdown_time = factors.maxDrawdown_time()
# 回撤开始日期
start_time = maxdrawdown_time[1]
start_time = parse(str(input_data['opentime'][start_time]))
# 回撤结束日期
end_time = maxdrawdown_time[0]
end_time = parse(str(input_data['opentime'][end_time]))
# 最大损失日期
loss_time_index = np.argmin(input_data['profit'])
loss_time = parse(str(input_data['opentime'][loss_time_index]))
result = {'交易周期': '(%s)' % ','.join(s_trading_time),
'策略运行时间': '(%s)' % ','.join(s_str_time),
'策略运行时间%': porition,
'最长空仓时间': '(%s)' % ','.join(s_empty_time),
'策略最大回撤开始时间': start_time.strftime("%Y/%m/%d %H:%M"),
'策略最大回撤结束时间': end_time.strftime("%Y/%m/%d %H:%M"),
'单笔最大回撤时间': signe_drawdown_date.strftime("%Y/%m/%d %H:%M"),
'平仓交易最大损失日期': loss_time.strftime("%Y/%m/%d %H:%M")}
return result
def ratio_calculate(data, after_merge, capital = 500000, rf = 0, period = 240) -> dict:
data['principal'] = data['totalprofit'] + capital
data['principal'] = data['principal'].shift(1)
input_data = data.fillna(value=capital)
ret = input_data['profit'] / input_data['principal']
mar = 0
trade = input_data['closebarno'][len(input_data) - 1] / 47
factors = Calculate(ret, mar, rf, period, trade)
# 潜在上涨比率
potential_upside_ratio = factors.calculate_upside_ratio()
# 夏普比率
sharpe_ratio = factors.sharp_ratio()
# 索提诺比率
sortino_ratio = factors.sortion_ratio()
# 卡尔马比率(Calmar Ratio)
calmar_ratio = factors.calmar_ratio()
# 斯特林比率
sterling_ratio = factors.sterling_a_ratio()
result1 = performance_summary(data, after_merge)
# 净利/单笔最大亏损
net_s_loss = result1.get('净利') / result1.get('单笔最大亏损')
# 净利/单笔最大回撤
net_s_drawdown = result1.get('净利') / factors.single_largest_maxdrawdown()
# 净利/ 策略最大回撤
net_strategy_drawdown = result1.get('净利') / factors.maxDrawdown()
# 调整净利/单笔最大亏损
adjust_s_loss = result1.get('调整净利') / result1.get('单笔最大亏损')
# 调整净利/单笔最大回撤
adjust_s_drawdown = result1.get('调整净利') / factors.single_largest_maxdrawdown()
# 调整净利/ 策略最大回撤
adjust_strategy_drawdown = result1.get('调整净利') / factors.maxDrawdown()
result = {'潜在上涨比率':potential_upside_ratio,
'夏普比率':sharpe_ratio,
'索提诺比率':sortino_ratio,
'卡尔马比率':calmar_ratio,
'斯特林比率':sterling_ratio,
'净利/单笔最大亏损':net_s_loss,
'净利/单笔最大回撤':net_s_drawdown,
'净利/ 策略最大回撤':net_strategy_drawdown,
'调整净利/单笔最大亏损':adjust_s_loss,
'调整净利/单笔最大回撤':adjust_s_drawdown,
'调整净利/ 策略最大回撤':adjust_strategy_drawdown}
return result
def performance_summary(input_data, input_data1, capital = 500000, rf = 0.00, period = 240):
'''
绩效统计
'''
# 指标计算准备
input_data['principal'] = input_data['totalprofit'] + capital
input_data['principal'] = input_data['principal'].shift(1)
input_data = input_data.fillna(value=capital)
ret = input_data['profit'] / input_data['principal']
mar = 0
trade = len(input_data)
#trade = input_data['closebarno'][len(input_data)-1] / 47
# 指标class
factors = Calculate(ret, mar, rf, period, trade)
# 毛利
profit = input_data[input_data['profit'].apply(lambda x: x >= 0)]
total_profit = 0 if len(profit)==0 else profit['profit'].sum()
# 毛损
loss = input_data[input_data['profit'].apply(lambda x: x < 0)]
total_loss = 0 if len(loss)==0 else loss['profit'].sum()
# 净利
net_profit = total_profit + total_loss
input_data1['adjust_profit'] = (input_data1['profit'] - input_data1['transaction_fee']) if len(input_data1)>0 else 0
# 调整毛利
adjust_profit = input_data1[input_data1['adjust_profit'].apply(lambda x: x >= 0)]
total_adjust_profit = 0 if len(adjust_profit)==0 else adjust_profit['adjust_profit'].sum()
# 调整毛损
adjust_loss = input_data1[input_data1['adjust_profit'].apply((lambda x: x < 0))]
total_adjust_loss = 0 if len(adjust_loss)==0 else adjust_loss['adjust_profit'].sum()
# 调整净利
adjust_net_profit = total_adjust_profit + total_adjust_loss
# 盈利因子
profit_factor = 0 if total_loss==0 else np.abs(total_profit / total_loss)
# 调整盈利因子
adjust_profit_factor = 0 if total_adjust_loss == 0 else np.abs(total_adjust_profit / total_adjust_loss)
# 最大持有合约数量
max_holding_number = 1
# 已付手续费
paid_trading_fee = input_data1['transaction_fee'].sum() if len(input_data1)>0 else 0
# 单笔最大亏损
single_loss = input_data[input_data['profit'].apply(lambda x: x < 0)]
single_largest_loss = 0 if len(single_loss)==0 else abs(single_loss['profit'].min())
# 平仓交易最大亏损
trading_loss = single_largest_loss
# 平仓交易最大亏损比
trading_loss_rate = trading_loss / capital
# 年化收益率
annual_ret = factors.get_annual_return()
# 月化收益率
monthly_return = factors.monthly_return()
# 月平均收益
monthly_average_return = factors.monthly_average_return()
# 结果封装字典
result = {'毛利': total_profit,
'毛损': total_loss,
'净利': net_profit,
'调整毛利': adjust_net_profit,
'调整毛损': total_adjust_loss,
'调整净利': adjust_net_profit,
'盈利因子': profit_factor,
'调整盈利因子': adjust_profit_factor,
'最大持有合约数量': max_holding_number,
'已付手续费': paid_trading_fee,
'单笔最大亏损': single_largest_loss,
'平仓交易最大亏损': trading_loss,
'平仓交易最大亏损比': trading_loss_rate,
'年化收益率': annual_ret,
'月化收益率': monthly_return,
'月平均收益': monthly_average_return}
return result
def do_trading_analyze(df_closes, df_funds):
df_wins = df_closes[df_closes["profit"] > 0]
df_loses = df_closes[df_closes["profit"] <= 0]
ay_WinnerBarCnts = df_wins["closebarno"] - df_wins["openbarno"]
ay_LoserBarCnts = df_loses["closebarno"] - df_loses["openbarno"]
total_winbarcnts = ay_WinnerBarCnts.sum()
total_losebarcnts = ay_LoserBarCnts.sum()
total_fee = df_funds.iloc[-1]["fee"]
totaltimes = len(df_closes) # 总交易次数
wintimes = len(df_wins) # 盈利次数
losetimes = len(df_loses) # 亏损次数
winamout = df_wins["profit"].sum() # 毛盈利
loseamount = df_loses["profit"].sum() # 毛亏损
trdnetprofit = winamout + loseamount # 交易净盈亏
accnetprofit = trdnetprofit - total_fee # 账户净盈亏
winrate = (wintimes / totaltimes) if totaltimes > 0 else 0 # 胜率
avgprof = (trdnetprofit / totaltimes) if totaltimes > 0 else 0 # 单次平均盈亏
avgprof_win = (winamout / wintimes) if wintimes > 0 else 0 # 单次盈利均值
avgprof_lose = (loseamount / losetimes) if losetimes > 0 else 0 # 单次亏损均值
winloseratio = abs(avgprof_win / avgprof_lose) if avgprof_lose != 0 else "N/A" # 单次盈亏均值比
# 单笔最大盈利交易
largest_profit = df_wins['profit'].max()
# 单笔最大亏损交易
largest_loss = df_loses['profit'].min()
# 交易的平均持仓K线根数
avgtrd_hold_bar = 0 if totaltimes==0 else ((df_closes['closebarno'] - df_closes['openbarno']).sum()) / totaltimes
# 平均空仓K线根数
avb = (df_closes['openbarno'].shift(-1) - df_closes['closebarno']).dropna()
avgemphold_bar = 0 if len(df_closes)==0 else avb.sum() / len(df_closes)
# 两笔盈利交易之间的平均空仓K线根数
win_holdbar_situ = (df_wins['openbarno'].shift(-1) - df_wins['closebarno']).dropna()
winempty_avgholdbar = 0 if len(df_wins)==0 else win_holdbar_situ.sum() / len(df_wins)
# 两笔亏损交易之间的偶平均空仓K线根数
loss_holdbar_situ = (df_loses['openbarno'].shift(-1) - df_loses['closebarno']).dropna()
lossempty_avgholdbar = 0 if len(df_loses)==0 else loss_holdbar_situ.sum() / len(df_loses)
max_consecutive_wins = 0 # 最大连续盈利次数
max_consecutive_loses = 0 # 最大连续亏损次数
avg_bars_in_winner = total_winbarcnts / wintimes if wintimes > 0 else "N/A"
avg_bars_in_loser = total_losebarcnts / losetimes if losetimes > 0 else "N/A"
consecutive_wins = 0
consecutive_loses = 0
for idx, row in df_closes.iterrows():
profit = row["profit"]
if profit > 0:
consecutive_wins += 1
consecutive_loses = 0
else:
consecutive_wins = 0
consecutive_loses += 1
max_consecutive_wins = max(max_consecutive_wins, consecutive_wins)
max_consecutive_loses = max(max_consecutive_loses, consecutive_loses)
summary = dict()
summary["交易总数量"] = totaltimes
summary["盈利交易次数"] = wintimes
summary["亏损交易次数"] = losetimes
summary["毛盈利"] = float(winamout)
summary["毛亏损"] = float(loseamount)
summary["交易净盈亏"] = float(trdnetprofit)
summary["% 胜率"] = winrate * 100
summary["单次平均盈亏"] = avgprof
summary["单次盈利均值"] = avgprof_win
summary["单次亏损均值"] = avgprof_lose
summary["% 单次盈亏均值比"] = winloseratio
summary["最大连续盈利次数"] = max_consecutive_wins
summary["最大连续亏损次数"] = max_consecutive_loses
summary["盈利交易的平均持仓K线根数"] = avg_bars_in_winner
summary["亏损交易的平均持仓K线根数"] = avg_bars_in_loser
summary["账户净盈亏"] = 0 if totaltimes==0 else accnetprofit / totaltimes
summary['单笔最大盈利交易'] = largest_profit
summary['单笔最大亏损交易'] = largest_loss
summary['交易的平均持仓K线根数'] = avgtrd_hold_bar
summary['平均空仓K线根数'] = avgemphold_bar
summary['两笔盈利交易之间的平均空仓K线根数'] = winempty_avgholdbar
summary['两笔亏损交易之间的平均空仓K线根数'] = lossempty_avgholdbar
summary = pd.DataFrame([summary]).T
summary = summary.reset_index()
return summary
def trading_analyze(workbook:Workbook, df_closes, df_funds, capital = 500000):
'''
交易分析
'''
res = average_profit(df_closes)
rr = res.get('连续盈利次数')
df = pd.DataFrame([rr]).T
df.columns = ['连续次数']
df = df.reset_index()
every_series_profit = res.get('每个序列平均收益')
every_series_profit = every_series_profit.reset_index()
df['index'] = df['index'].apply(lambda x: int(x))
every_series_profit['index'] = every_series_profit['index'].apply(lambda x: int(x))
f_result = df.merge(every_series_profit)
f_result = f_result.sort_values('index')
f_result.columns = ['连续次数', '出现次数', '每个序列平均收益']
rr_2 = res.get('连续亏损次数')
df_2 = pd.DataFrame([rr_2]).T
df_2.columns = ['连续次数']
df_2 = df_2.reset_index()
every_series_loss = res.get('每个序列平均亏损')
every_series_loss = every_series_loss.reset_index()
df_2['index'] = df_2['index'].apply(lambda x: int(x))
every_series_loss['index'] = every_series_loss['index'].apply(lambda x: int(x))
f_2_result = df_2.merge(every_series_loss)
f_2_result = f_2_result.sort_values('index')
f_2_result.columns = ['连续次数', '出现次数', '每个序列平均亏损']
ssaa = 1000
# 连续交易系列分析
s = continue_trading_analysis(df_closes, ssaa)
# 极端交易
ss = extreme_trading(df_closes)
extre_pro = ss.get('单笔净利 +1倍标准差')
extre_los = ss.get('单笔盈利 - 标准差')
data1 = df_closes[df_closes['profit'].apply(lambda x: x > extre_pro)]
data2 = df_closes[df_closes['profit'].apply(lambda x: x < extre_los)]
ss_1 = extreme_trading(data1)
ss_2 = extreme_trading(data2)
ss_1 = pd.DataFrame([ss_1]).T
ss_2 = pd.DataFrame([ss_2]).T
sss = pd.concat([ss_1, ss_2], axis=1)
ss = pd.DataFrame([ss]).T
sss = pd.concat([ss, sss], axis=1)
sss.columns = ['总计', '极端盈利', '极端亏损']
title_format = workbook.add_format({
'font_size': 16,
'bold': True,
'align': 'left', # 水平居中
'valign': 'vcenter' # 垂直居中
})
index_format = workbook.add_format({
'font_size': 12,
'bold': True,
'align': 'left', # 水平居中
'valign': 'vcenter' # 垂直居中
})
value_format = workbook.add_format({
'align': 'right', # 水平居中
'valign': 'vcenter' # 垂直居中
})
date_format = workbook.add_format({
'num_format': 'yyyy/mm/dd',
'bold': True,
'align': 'left', # 水平居中
'valign': 'vcenter' # 垂直居中
})
worksheet = workbook.add_worksheet('交易分析')
trade_s = do_trading_analyze(df_closes, df_funds)
data_1 = df_closes[df_closes['direct'].apply(lambda x: 'LONG' in x)]
trade_s_long = do_trading_analyze(data_1, df_funds)
data_2 = df_closes[df_closes['direct'].apply(lambda x: 'SHORT' in x)]
trade_s_short = do_trading_analyze(data_2, df_funds)
trade_s = trade_s.merge(trade_s_long, how='inner', on='index')
trade_s = trade_s.merge(trade_s_short,how='inner', on='index')
trade_s.columns =['类别', '所有交易', '多头', '空头']
trade_s.fillna(value=0, inplace=True)
worksheet.write_row('A1', ['总体交易分析'], title_format)
worksheet.write_row('B3', ['所有交易','多头交易','空头交易'], index_format)
worksheet.write_column('A4', trade_s['类别'], index_format)
worksheet.write_column('B4', trade_s['所有交易'], value_format)
worksheet.write_column('C4', trade_s['多头'], value_format)
worksheet.write_column('D4', trade_s['空头'], value_format)
worksheet.write_row('A28', ['极端交易'], title_format)
worksheet.write_row('B30', ['总计','极端盈利','极端亏损'], index_format)
worksheet.write_column('A31', sss.index, index_format)
worksheet.write_column('B31', sss['总计'], value_format)
worksheet.write_column('C31', sss['极端盈利'], value_format)
worksheet.write_column('D31', sss['极端亏损'], value_format)
worksheet.write_row('A38', ['连续交易系列分析'], title_format)
worksheet.write_column('A40', s.keys(), index_format)
worksheet.write_column('B40', s.values(), value_format)
worksheet.write_row('A49', ['连续交易系列统计'], title_format)
worksheet.write_row('A51', ['连续盈利次数','出现次数','每个序列的平均收益'], index_format)
worksheet.write_column('A52', f_result['连续次数'], value_format)
worksheet.write_column('B52', f_result['出现次数'], value_format)
worksheet.write_column('C52', f_result['每个序列平均收益'], value_format)
win_cnt = len(f_result)
next_row = win_cnt+52
worksheet.write_row('A%d'%next_row, ['连续亏损次数','出现次数','每个序列的平均亏损'], index_format)
worksheet.write_column('A%d'%(next_row+1), f_2_result['连续次数'], value_format)
worksheet.write_column('B%d'%(next_row+1), f_2_result['出现次数'], value_format)
worksheet.write_column('C%d'%(next_row+1), f_2_result['每个序列平均亏损'], value_format)
# 这里开始画图
next_row += len(f_2_result) + 3
worksheet.write_row('A%d'%next_row, ['全部交易'], title_format)
chart_col = workbook.add_chart({'type': 'scatter'})
length = len(df_closes)
sheetName = '交易列表'
chart_col.add_series(
{
'name': '收益分布',
'categories': '=%s!$A$4:$A$%s' % (sheetName, length+3),
'values': '=%s!$J$4:$J$%s' % (sheetName, length+3),
'marker': {
'type':"circle",
'size':3
}
}
)
chart_col.set_title({'name': '收益分布'})
worksheet.insert_chart('A%d' % (next_row+2), chart_col)
next_row += 30
worksheet.write_row('A%d'%next_row, ['潜在盈利'], title_format)
chart_col = workbook.add_chart({'type': 'scatter'})
length = len(df_closes)
sheetName = '交易列表'
chart_col.add_series(
{
'name': '潜在盈利',
'categories': '=%s!$A$4:$A$%s' % (sheetName, length+3),
'values': '=%s!$N$4:$N$%s' % (sheetName, length+3),
'marker': {
'type':"diamond",
'size':3,
'border': {'color': 'red'},
'fill': {'color': 'red'}
}
}
)
chart_col.set_title({'name': '潜在盈利'})
worksheet.insert_chart('A%d' % (next_row+2), chart_col)
next_row += 30
worksheet.write_row('A%d'%next_row, ['潜在亏损'], title_format)
chart_col = workbook.add_chart({'type': 'scatter'})
length = len(df_closes)
sheetName = '交易列表'
chart_col.add_series(
{
'name': '潜在亏损',
'categories': '=%s!$A$4:$A$%s' % (sheetName, length+3),
'values': '=%s!$P$4:$P$%s' % (sheetName, length+3),
'marker': {
'type':"triangle",
'size':3,
'border': {'color': 'green'},
'fill': {'color': 'green'}
}
}
)
chart_col.set_title({'name': '潜在亏损'})
worksheet.insert_chart('A%d' % (next_row+2), chart_col)
# 周期分析
worksheet = workbook.add_worksheet('周期分析')
df_closes['opentime'] = df_closes['opentime'].apply(lambda x: parse(str(int(x / 10000))))
res = stat_closes_by_day(df_closes.copy(), capital)
worksheet.write_row('A1', ['日度绩效分析'], title_format)
worksheet.write_row('A3', ['期间','盈利(¤)','盈利(%)','毛利','毛损','交易次数','胜率(%)'], index_format)
worksheet.write_column('A4', res.index, date_format)
worksheet.write_column('B4', res["profit"], value_format)
worksheet.write_column('C4', res["profit_ratio"], value_format)
worksheet.write_column('D4', res["gross_profit"], value_format)
worksheet.write_column('E4', res["gross_loss"], value_format)
worksheet.write_column('F4', res["times"], value_format)
worksheet.write_column('G4', res["win_rate"]*100, value_format)
next_row = 5 + len(res)
res = stat_closes_by_month(df_closes.copy(), capital)
worksheet.write_row('A%d'%(next_row+1), ['月度绩效分析'], title_format)
worksheet.write_row('A%d'%(next_row+3), ['期间','盈利(¤)','盈利(%)','毛利','毛损','交易次数','胜率(%)'], index_format)
worksheet.write_column('A%d'%(next_row+4), res.index, index_format)
worksheet.write_column('B%d'%(next_row+4), res["profit"], value_format)
worksheet.write_column('C%d'%(next_row+4), res["profit_ratio"], value_format)
worksheet.write_column('D%d'%(next_row+4), res["gross_profit"], value_format)
worksheet.write_column('E%d'%(next_row+4), res["gross_loss"], value_format)
worksheet.write_column('F%d'%(next_row+4), res["times"], value_format)
worksheet.write_column('G%d'%(next_row+4), res["win_rate"]*100, value_format)
next_row = next_row + 4 + len(res)
res = stat_closes_by_year(df_closes.copy(), capital)
worksheet.write_row('A%d'%(next_row+1), ['年度绩效分析'], title_format)
worksheet.write_row('A%d'%(next_row+3), ['期间','盈利(¤)','盈利(%)','毛利','毛损','交易次数','胜率(%)'], index_format)
worksheet.write_column('A%d'%(next_row+4), res.index, index_format)
worksheet.write_column('B%d'%(next_row+4), res["profit"], value_format)
worksheet.write_column('C%d'%(next_row+4), res["profit_ratio"], value_format)
worksheet.write_column('D%d'%(next_row+4), res["gross_profit"], value_format)
worksheet.write_column('E%d'%(next_row+4), res["gross_loss"], value_format)
worksheet.write_column('F%d'%(next_row+4), res["times"], value_format)
worksheet.write_column('G%d'%(next_row+4), res["win_rate"]*100, value_format)
def strategy_analyze(workbook:Workbook, df_closes, df_trades, capital, rf = 0.0, period = 240):
'''
策略分析
'''
# 截取开仓明细
data1_open = df_trades[df_trades['action'].apply(lambda x: 'OPEN' in x)].reset_index()
data1_open = data1_open.drop(columns=['index'])
# 截取平仓明细
data1_close = df_trades[df_trades['action'].apply(lambda x: 'CLOSE' in x)].reset_index()
data1_close = data1_close.drop(columns=['index'])
# 将平仓明细字段重命名,并跟开仓明细合并成一个大表
data1_close = data1_close.rename(columns={'code': 'code_1', 'time': 'time_1', 'direct': 'direct_1',
'action': 'action_1', 'price': 'price_1', 'qty': 'qty_1', 'tag': 'tag_1',
'fee': 'fee_1'})
new_data = pd.concat([data1_open, data1_close], axis=1)
new_data = new_data.dropna()
new_data = new_data.drop(columns=['code_1', 'qty_1'])
# 计算开仓平仓手续费
new_data['transaction_fee'] = new_data['fee'] + new_data['fee_1']
clean_data = new_data[['time', 'transaction_fee']]
clean_data = clean_data.rename(columns={'time': 'opentime'})
# 合并数据
after_merge = pd.merge(df_closes, clean_data, how='inner', on='opentime')
data_long = df_closes[df_closes['direct'].apply(lambda x:'LONG' in x )].reset_index()
after_merge_long = after_merge[after_merge['direct'].apply(lambda x: 'LONG' in x)].reset_index()
data_short = df_closes[df_closes['direct'].apply(lambda x: 'SHORT' in x)].reset_index()
after_merge_short = after_merge[after_merge['direct'].apply(lambda x: 'SHORT' in x)].reset_index()
# 全部平仓明细进行绩效分析
result1 = performance_summary(df_closes, after_merge, capital=capital, rf=rf, period=period)
# 做多平仓明细进行绩效分析
result1_2 = performance_summary(data_long, after_merge_long, capital=capital, rf=rf, period=period)
# 做空平仓明细进行绩效分析
result1_3 = performance_summary(data_short,after_merge_short, capital=capital, rf=rf, period=period)
# 绩效比率计算
result2 = ratio_calculate(df_closes, after_merge, capital=capital, rf=rf, period=period)
# 时间分析
result3 = time_analysis(df_closes)
result1 = pd.DataFrame(pd.Series(result1), columns=['所有交易'])
result1 = result1.reset_index().rename(columns={'index': '策略绩效概要'})
result1_2 = pd.DataFrame(pd.Series(result1_2), columns=['多头交易'])
result1_2 = result1_2.reset_index().rename(columns={'index': '策略绩效概要'})
result1_3 = pd.DataFrame(pd.Series(result1_3), columns=['空头交易'])
result1_3 = result1_3.reset_index().rename(columns={'index': '策略绩效概要'})
result1 = result1.merge(result1_2,how='inner',on='策略绩效概要')
result1 = result1.merge(result1_3,how='inner',on='策略绩效概要')
sheetName = '策略分析'
worksheet = workbook.add_worksheet(sheetName)
title_format = workbook.add_format({
'font_size': 16,
'bold': True,
'align': 'left', # 水平居中
'valign': 'vcenter' # 垂直居中
})
index_format = workbook.add_format({
'font_size': 12,
'bold': True,
'align': 'left', # 水平居中
'valign': 'vcenter' # 垂直居中
})
value_format = workbook.add_format({
'align': 'right', # 水平居中
'valign': 'vcenter' # 垂直居中
})
result1.fillna(value=0, inplace=True)
worksheet.write_row('A1', ['策略绩效概要'], title_format)
worksheet.write_row('B3', ['所有交易','多头交易','空头交易'], index_format)
worksheet.write_column('A4', result1['策略绩效概要'], index_format)
worksheet.write_column('B4', result1['所有交易'], value_format)
worksheet.write_column('C4', result1['多头交易'], value_format)
worksheet.write_column('D4', result1['空头交易'], value_format)
worksheet.write_row('A22', ['绩效比率'], title_format)
worksheet.write_column('A24', result2.keys(), index_format)
worksheet.write_column('B24', result2.values(), value_format)
worksheet.write_row('A37', ['时间分析'], title_format)
worksheet.write_column('A39', result3.keys(), index_format)
worksheet.write_column('B39', result3.values(), value_format)
# 这里开始画图
worksheet.write_row('A49', ['详细权益曲线'], title_format)
chart_col = workbook.add_chart({'type': 'line'})
length = len(df_closes)
sheetName = '交易列表'
chart_col.add_series(
{
'name': '详细权益曲线',
'categories': '=%s!$G$4:$G$%s' % (sheetName, length+3),
'values': '=%s!$R$4:$R$%s' % (sheetName, length+3),
'line': {'color': 'red','width': 1}
}
)
chart_col.set_title({'name': '详细权益曲线'})
worksheet.insert_chart('A51', chart_col)
worksheet.write_row('A79', ['详细权益曲线及潜在亏损'], title_format)
chart_col = workbook.add_chart({'type': 'line'})
chart_col.add_series(
{
'name': '权益曲线',
'categories': '=%s!$G$4:$G$%s' % (sheetName, length+3),
'values': '=%s!$R$4:$R$%s' % (sheetName, length+3),
'line': {'color': 'red','width': 1}
}
)
chart_col.add_series(
{
'name': '潜在亏损',
'categories': '=%s!$G$4:$G$%s' % (sheetName, length+3),
'values': '=%s!$P$4:$P$%s' % (sheetName, length+3),
'line': {'color': 'green','width': 1}
}
)
chart_col.set_title({'name': '详细权益曲线及潜在亏损'})
worksheet.insert_chart('A81', chart_col)
worksheet.write_row('A109', ['潜在盈利与亏损'], title_format)
chart_col = workbook.add_chart({'type': 'line'})
chart_col.add_series(
{
'name': '潜在盈利',
'categories': '=%s!$G$4:$G$%s' % (sheetName, length+3),
'values': '=%s!$N$4:$N$%s' % (sheetName, length+3),
'line': {'color': 'red','width': 1}
}
)
chart_col.add_series(
{
'name': '潜在亏损',
'categories': '=%s!$G$4:$G$%s' % (sheetName, length+3),
'values': '=%s!$P$4:$P$%s' % (sheetName, length+3),
'line': {'color': 'green','width': 1}
}
)
chart_col.set_title({'name': '潜在盈利与亏损'})
worksheet.insert_chart('A111', chart_col)
def output_closes(workbook:Workbook, df_closes:df, capital = 500000):
worksheet = workbook.add_worksheet('交易列表')
title_format = workbook.add_format({
'font_size': 16,
'bold': True,
'align': 'left', # 水平居中
'valign': 'vcenter' # 垂直居中
})
index_format = workbook.add_format({
'font_size': 12,
'bold': True,
'align': 'left', # 水平居中
'valign': 'vcenter' # 垂直居中
})
value_format = workbook.add_format({
'align': 'right', # 水平居中
'valign': 'vcenter' # 垂直居中
})
time_format = workbook.add_format({
'num_format': 'yyyy/mm/dd HH:MM',
'align': 'right', # 水平居中
'valign': 'vcenter' # 垂直居中
})
df_closes['entrytime'] = df_closes['opentime'].apply(lambda x: datetime.strptime(str(x), '%Y%m%d%H%M'))
df_closes['exittime'] = df_closes['closetime'].apply(lambda x: datetime.strptime(str(x), '%Y%m%d%H%M'))
worksheet.write_row('A1', ['交易列表'], title_format)
worksheet.write_row('A3', ['编号', '代码','方向','进场时间','进场价格','进场标记','出场时间','出场价格','出场标记',
'盈利¤','盈利%','累计盈利¤','累计盈利%','潜在盈利¤','潜在盈利%','潜在亏损¤','潜在亏损%','累计权益'], index_format)
df_closes["profit_ratio"] = df_closes["profit"]*100/capital
df_closes["total_profit_ratio"] = df_closes["totalprofit"]*100/capital
df_closes["max_profit_ratio"] = df_closes["maxprofit"]*100/capital
df_closes["max_loss_ratio"] = df_closes["maxloss"]*100/capital
worksheet.write_column('A4', df_closes.index+1, value_format)
worksheet.write_column('B4', df_closes['code'], value_format)
worksheet.write_column('C4', df_closes['direct'], value_format)
worksheet.write_column('D4', df_closes['entrytime'], time_format)
worksheet.write_column('E4', df_closes['openprice'], value_format)
ay = df_closes['entertag'].apply(lambda x: x if type(x)==str else '' if math.isnan(x) else x)
worksheet.write_column('F4', ay, value_format)
worksheet.write_column('G4', df_closes['exittime'], time_format)
worksheet.write_column('H4', df_closes['closeprice'], value_format)
ay = df_closes['exittag'].apply(lambda x: x if type(x)==str else '' if math.isnan(x) else x)
worksheet.write_column('I4', ay, value_format)
worksheet.write_column('J4', df_closes['profit'], value_format)
worksheet.write_column('K4', df_closes['profit_ratio'], value_format)
worksheet.write_column('L4', df_closes['totalprofit'], value_format)
worksheet.write_column('M4', df_closes['total_profit_ratio'], value_format)
worksheet.write_column('N4', df_closes['maxprofit'], value_format)
worksheet.write_column('O4', df_closes['max_profit_ratio'], value_format)
worksheet.write_column('P4', df_closes['maxloss'], value_format)
worksheet.write_column('Q4', df_closes['max_loss_ratio'], value_format)
worksheet.write_column('R4', df_closes['totalprofit']+capital, value_format)
def summary_analyze(df_funds:df, capital = 5000000, rf = 0, period = 240) -> dict:
'''
概要分析
'''
init_capital = capital
annual_days = period
days = len(df_funds)
#先做资金统计吧
print("anayzing fund data……")
df_funds["dynbalance"] += init_capital
ayBal = df_funds["dynbalance"] # 每日期末动态权益
#生成每日期初动态权益
ayPreBal = np.array(ayBal.tolist()[:-1])
ayPreBal = np.insert(ayPreBal, 0, init_capital) #每日期初权益
df_funds["prebalance"] = ayPreBal
#统计期末权益大于期初权益的天数,即盈利天数
windays = len(df_funds[df_funds["dynbalance"]>df_funds["prebalance"]])
#每日净值
ayNetVals = (ayBal/init_capital)
ar = math.pow(ayNetVals.iloc[-1], annual_days/days) - 1 #年化收益率=总收益率^(年交易日天数/统计天数)
ayDailyReturn = ayBal/ayPreBal-1 #每日收益率
delta = fmtNAN(ayDailyReturn.std(axis=0)*math.pow(annual_days,0.5),0) #年化标准差=每日收益率标准差*根号下(年交易日天数)
down_delta = fmtNAN(ayDailyReturn[ayDailyReturn<0].std(axis=0)*math.pow(annual_days,0.5), 0) #下行标准差=每日亏损收益率标准差*根号下(年交易日天数)
#sharpe率
if delta != 0.0:
sr = (ar-rf)/delta
else:
sr = 9999.0
#计算最大回撤和最大上涨
maxub = ayNetVals[0]
minub = maxub
mdd = 0.0
midd = 0.0
mup = 0.0
for idx in range(1,len(ayNetVals)):
maxub = max(maxub, ayNetVals[idx])
minub = min(minub, ayNetVals[idx])
profit = (ayNetVals[idx] - ayNetVals[idx-1])/ayNetVals[idx-1]
falldown = (ayNetVals[idx] - maxub)/maxub
riseup = (ayNetVals[idx] - minub)/minub
if profit <= 0:
midd = max(midd, abs(profit))
mdd = max(mdd, abs(falldown))
else:
mup = max(mup, abs(riseup))
#索提诺比率
if down_delta != 0.0:
sortino = (ar-rf)/down_delta
else:
sortino = 0.0
if mdd != 0.0:
calmar = ar/mdd
else:
calmar = 999999.0
# key_indicator = ['交易天数', '累积收益(%)', '年化收益率(%)', '胜率(%)', '最大回撤(%)', '最大上涨(%)', '标准差(%)',
# '下行波动率(%)', 'Sharpe比率', 'Sortino比率', 'Calmar比率']
return {
"days": days,
"total_return":(ayNetVals.iloc[-1]-1)*100,
"annual_return":ar*100,
"win_rate":(windays/days)*100,
"max_falldown":mdd*100,
"max_profratio":mup*100,
"std":delta*100,
"down_std":down_delta*100,
"sharpe_ratio":sr,
"sortino_ratio":sortino,
"calmar_ratio":calmar
}
def funds_analyze(workbook:Workbook, df_funds:df, capital = 5000000, rf = 0, period = 240):
'''
逐日资金分析
'''
init_capital = capital
annual_days = period
days = len(df_funds)
#先做资金统计吧
print("anayzing fund data……")
df_funds["dynbalance"] += init_capital
ayBal = df_funds["dynbalance"] # 每日期末动态权益
#生成每日期初动态权益
ayPreBal = np.array(ayBal.tolist()[:-1])
ayPreBal = np.insert(ayPreBal, 0, init_capital) #每日期初权益
df_funds["prebalance"] = ayPreBal
#统计期末权益大于期初权益的天数,即盈利天数
windays = len(df_funds[df_funds["dynbalance"]>df_funds["prebalance"]])
#每日净值
ayNetVals = (ayBal/init_capital)
ar = math.pow(ayNetVals.iloc[-1], annual_days/days) - 1 #年化收益率=总收益率^(年交易日天数/统计天数)
ayDailyReturn = ayBal/ayPreBal-1 #每日收益率
delta = fmtNAN(ayDailyReturn.std(axis=0)*math.pow(annual_days,0.5),0) #年化标准差=每日收益率标准差*根号下(年交易日天数)
down_delta = fmtNAN(ayDailyReturn[ayDailyReturn<0].std(axis=0)*math.pow(annual_days,0.5), 0) #下行标准差=每日亏损收益率标准差*根号下(年交易日天数)
#sharpe率
if delta != 0.0:
sr = (ar-rf)/delta
else:
sr = 9999.0
#计算最大回撤和最大上涨
maxub = ayNetVals[0]
minub = maxub
mdd = 0.0
midd = 0.0
mup = 0.0
for idx in range(1,len(ayNetVals)):
maxub = max(maxub, ayNetVals[idx])
minub = min(minub, ayNetVals[idx])
profit = (ayNetVals[idx] - ayNetVals[idx-1])/ayNetVals[idx-1]
falldown = (ayNetVals[idx] - maxub)/maxub
riseup = (ayNetVals[idx] - minub)/minub
if profit <= 0:
midd = max(midd, abs(profit))
mdd = max(mdd, abs(falldown))
else:
mup = max(mup, abs(riseup))
#索提诺比率
if down_delta != 0.0:
sortino = (ar-rf)/down_delta
else:
sortino = 0.0
if mdd != 0.0:
calmar = ar/mdd
else:
calmar = 999999.0
#输出到excel
sheetName = '逐日绩效概览'
worksheet = workbook.add_worksheet(sheetName)
# 设置合并单元格及格式 #
# ~~~~~~ 写入数据 ~~~~~~ #
title_format = workbook.add_format({
'bold': True,
'border': 1,
'align': 'center', # 水平居中
'valign': 'vcenter', # 垂直居中
'fg_color': '#bcbcbc'
})
fund_data_format = workbook.add_format({
'border': 1,
'align': 'right', # 右对齐
'valign': 'vcenter', # 垂直居中
})
fund_data_format_2 = workbook.add_format({
'border': 1,
'align': 'right', # 右对齐
'valign': 'vcenter', # 垂直居中
'num_format': '0.00'
})
fund_data_format_3 = workbook.add_format({
'border': 1,
'align': 'right', # 右对齐
'valign': 'vcenter', # 垂直居中
'num_format': '0.000'
})
fund_data_format_4 = workbook.add_format({
'border': 1,
'align': 'right', # 右对齐
'valign': 'vcenter', # 垂直居中
'num_format': '0.0000'
})
merge_format = workbook.add_format({
'font_size': 16,
'bold': True,
'align': 'center', # 水平居中
'valign': 'vcenter', # 垂直居中
})
indicator_format = workbook.add_format({
'font_size': 12,
'bold': True,
'align': 'center', # 水平居中
'valign': 'vcenter', # 垂直居中
})
worksheet.merge_range('A1:D1', '收益率统计指标', merge_format)
worksheet.merge_range('E1:H1', '风险统计指标', merge_format)
worksheet.merge_range('I1:K1', '综合指标', merge_format)
key_indicator = ['交易天数', '累积收益(%)', '年化收益率(%)', '胜率(%)', '最大回撤(%)', '最大上涨(%)', '标准差(%)',
'下行波动率(%)', 'Sharpe比率', 'Sortino比率', 'Calmar比率']
key_data = [(ayNetVals.iloc[-1]-1)*100, ar*100, (windays/days)*100, mdd*100, mup*100, delta*100, down_delta*100, sr, sortino, calmar]
worksheet.write_row('A2', key_indicator, indicator_format)
worksheet.write_column('A3', [days], fund_data_format)
worksheet.write_row('B3', key_data, fund_data_format_3)
# 画图 #
chart_col = workbook.add_chart({'type': 'line'})
length = days
chart_col.add_series( # 给图表设置格式,填充内容
{
'name': '=逐日绩效分析!$B$1',
'categories': '=逐日绩效分析!$A$3:$A$%d' % (length+2),
'values': '=逐日绩效分析!$G$3:$G$%d' % (length+2),
'line': {'color': 'blue', 'width':1},
}
)
chart_col.set_title({'name': '累计净值'})
worksheet.insert_chart('A8', chart_col)
# 准备第二张表格 #
sheetName = '逐日绩效分析'
worksheet = workbook.add_worksheet(sheetName)
title_format2 = workbook.add_format({
'border': 1,
'align': 'center', # 水平居中
'valign': 'vcenter', # 垂直居中
'fg_color': '#D3D3D3',
'text_wrap': 1
})
worksheet.merge_range('A1:A2', '日期', title_format2)
worksheet.merge_range('B1:B2', '统计时间', title_format2)
worksheet.merge_range('C1:C2', '初始资金', title_format2)
worksheet.merge_range('D1:D2', '出入金', title_format2)
worksheet.merge_range('E1:E2', '当前权益', title_format2)
worksheet.merge_range('F1:F2', '累计盈亏', title_format2)
worksheet.merge_range('G1:G2', '累计净值', title_format2)
worksheet.merge_range('H1:I1', '当日盈亏', title_format2)
indicator = ['数值', '比例']
worksheet.write_row('H2', indicator, title_format2)
worksheet.merge_range('J1:J2', '峰值', title_format2)
worksheet.merge_range('K1:K2', '当日累计回撤', title_format2)
worksheet.merge_range('L1:L2', '历史最大累计回撤', title_format2)
worksheet.merge_range('M1:M2', '最大单日回撤', title_format2)
worksheet.merge_range('N1:N2', '衰落时间', title_format2)
# worksheet.merge_range('O1:O2', 'IF指数', title_format2)
# worksheet.merge_range('P1:P2', 'IF净值', title_format2)
# 写入内容 #
profit_format = workbook.add_format({
'border': 1,
'align': 'right', # 靠右
'valign': 'vcenter', # 垂直居中
'fg_color': '#FAFAD2',
'num_format': '0.00'
})
percent_format = workbook.add_format({
'border': 1,
'align': 'right', # 右对齐
'valign': 'vcenter', # 垂直居中
'num_format': '0.00%'
})
date_format = workbook.add_format({
'num_format': 'yyyy/mm/dd',
'border': 1,
'align': 'right', # 右对齐
'valign': 'vcenter', # 垂直居中
})
ayDates = df_funds['date'].apply(lambda x: str(x)[:4]+'/'+str(x)[4:6]+'/'+str(x)[6:8])
worksheet.write_column('A3', ayDates, date_format)
worksheet.write_column('B3', range(len(df_funds)), fund_data_format)
initial = [init_capital]*len(df_funds)
worksheet.write_column('C3', initial, fund_data_format)
worksheet.write_column('D3', '/', fund_data_format)
worksheet.write_column('E3', ayBal, fund_data_format)
worksheet.write_column('F3', ayBal-init_capital, fund_data_format_2)
worksheet.write_column('G3', ayNetVals, fund_data_format_4)
worksheet.write_column('H3', ayBal-ayPreBal, profit_format)
worksheet.write_column('I3', ayDailyReturn, percent_format)
# 计算峰值
upper = np.maximum.accumulate(ayNetVals)
worksheet.write_column('J3', upper, fund_data_format_4)
# 回撤指标
temp = 1-(ayNetVals)/(np.maximum.accumulate(ayNetVals))
worksheet.write_column('K3', temp, percent_format)
worksheet.write_column('L3', np.maximum.accumulate(temp), percent_format)
worksheet.write_column('M3', np.minimum.accumulate(ayDailyReturn), percent_format)
# 计算衰落时间
down_time = [0]
for i in range(1, len(upper)):
if upper[i] > upper[i-1]:
down_time.append(0)
else:
l = down_time[i-1]
down_time.append(l+1)
worksheet.write_column('N3', down_time, fund_data_format)
class WtBtAnalyst:
def __init__(self):
self.__strategies__ = dict()
return
def add_strategy(self, sname:str, folder:str, init_capital:float, rf:float=0.02, annual_trading_days:int = 240):
self.__strategies__[sname] = {
"folder": folder,
"cap":init_capital,
"rf":rf,
"atd":annual_trading_days
}
def run_new(self, outFileName:str = ''):
if len(self.__strategies__.keys()) == 0:
raise Exception("strategies is empty")
for sname in self.__strategies__:
sInfo = self.__strategies__[sname]
folder = sInfo["folder"]
print("start PnL analyzing for strategy %s……" % (sname))
df_funds = pd.read_csv(folder + "funds.csv")
df_closes = pd.read_csv(folder + "closes.csv")
df_trades = pd.read_csv(folder + "trades.csv")
if len(outFileName) == 0:
outFileName = 'Strategy[%s]_PnLAnalyzing_%s_%s.xlsx' % (sname, df_funds['date'][0], df_funds['date'].iloc[-1])
workbook = Workbook(outFileName)
init_capital = sInfo["cap"]
annual_days = sInfo["atd"]
rf = sInfo["rf"]
strategy_analyze(workbook, df_closes.copy(), df_trades.copy(), capital=init_capital, rf=rf, period=annual_days)
output_closes(workbook, df_closes.copy(), capital=init_capital)
trading_analyze(workbook, df_closes.copy(), df_funds.copy(), capital=init_capital)
funds_analyze(workbook, df_funds.copy(), capital=init_capital, rf=rf, period=annual_days)
workbook.close()
print("PnL analyzing of strategy %s done" % (sname))
def run(self, outFileName:str = ''):
if len(self.__strategies__.keys()) == 0:
raise Exception("strategies is empty")
for sname in self.__strategies__:
sInfo = self.__strategies__[sname]
folder = sInfo["folder"]
print("start PnL analyzing for strategy %s……" % (sname))
df_funds = pd.read_csv(folder + "funds.csv")
print("fund logs loaded……")
init_capital = sInfo["cap"]
annual_days = sInfo["atd"]
rf = sInfo["rf"]
if len(outFileName) == 0:
outFileName = 'Strategy[%s]_PnLAnalyzing_%s_%s.xlsx' % (sname, df_funds['date'][0], df_funds['date'].iloc[-1])
workbook = Workbook(outFileName)
funds_analyze(workbook, df_funds, capital=init_capital, rf=rf, period=annual_days)
workbook.close()
print("PnL analyzing of strategy %s done" % (sname))
def run_simple(self):
if len(self.__strategies__.keys()) == 0:
raise Exception("strategies is empty")
for sname in self.__strategies__:
sInfo = self.__strategies__[sname]
folder = sInfo["folder"]
df_funds = pd.read_csv(folder + "funds.csv")
init_capital = sInfo["cap"]
annual_days = sInfo["atd"]
rf = sInfo["rf"]
filename = folder + 'summary.json'
sumObj = summary_analyze(df_funds, capital=init_capital, rf=rf, period=annual_days)
sumObj["name"] = sname
f = open(filename,"w")
f.write(json.dumps(sumObj, indent=4, ensure_ascii=True))
f.close()
================================================
FILE: wtpy/apps/WtCtaOptimizer.py
================================================
from json import encoder
import multiprocessing
import time
import threading
import json
import os
import math
import numpy as np
import pandas as pd
from pandas import DataFrame as df
from wtpy import WtBtEngine,EngineType
from wtpy.apps import WtBtAnalyst
def fmtNAN(val, defVal = 0):
if math.isnan(val):
return defVal
return val
class ParamInfo:
'''
参数信息类
'''
def __init__(self, name:str, start_val = None, end_val = None, step_val = None, ndigits = 1, val_list:list = None):
self.name = name #参数名
self.start_val = start_val #起始值
self.end_val = end_val #结束值
self.step_val = step_val #变化步长
self.ndigits = ndigits #小数位
self.val_list = val_list #指定参数
def gen_array(self):
if self.val_list is not None:
return self.val_list
values = list()
curVal = round(self.start_val, self.ndigits)
while curVal < self.end_val:
values.append(curVal)
curVal += self.step_val
curVal = round(curVal, self.ndigits)
if curVal >= self.end_val:
curVal = self.end_val
break
values.append(round(curVal, self.ndigits))
return values
class WtCtaOptimizer:
'''
参数优化器\n
主要用于做策略参数优化的
'''
def __init__(self, worker_num:int = 8):
'''
构造函数\n
@worker_num 工作进程个数,默认为8,可以根据CPU核心数设置
'''
self.worker_num = worker_num
self.running_worker = 0
self.mutable_params = dict()
self.fixed_params = dict()
self.env_params = dict()
self.cpp_stra_module = None
return
def add_mutable_param(self, name:str, start_val, end_val, step_val, ndigits = 1):
'''
添加可变参数\n
@name 参数名\n
@start_val 起始值\n
@end_val 结束值\n
@step_val 步长\n
@ndigits 小数位
'''
self.mutable_params[name] = ParamInfo(name=name, start_val=start_val, end_val=end_val, step_val=step_val, ndigits=ndigits)
def add_listed_param(self, name:str, val_list:list):
'''
添加限定范围的可变参数\n
@name 参数名\n
@val_list 参数值列表
'''
self.mutable_params[name] = ParamInfo(name=name, val_list=val_list)
def add_fixed_param(self, name:str, val):
'''
添加固定参数\n
@name 参数名\n
@val 值\n
'''
self.fixed_params[name] = val
return
def set_strategy(self, typeName:type, name_prefix:str):
'''
设置策略\n
@typeName 策略类名\n
@name_prefix 命名前缀,用于自动命名用,一般为格式为"前缀_参数1名_参数1值_参数2名_参数2值"
'''
self.strategy_type = typeName
self.name_prefix = name_prefix
return
def set_cpp_strategy(self, module:str, type_name:type, name_prefix:str):
'''
设置CPP策略\n
@module 模块文件\n
@typeName 策略类名\n
@name_prefix 命名前缀,用于自动命名用,一般为格式为"前缀_参数1名_参数1值_参数2名_参数2值"
'''
self.cpp_stra_module = module
self.cpp_stra_type = type_name
self.name_prefix = name_prefix
return
def config_backtest_env(self, deps_dir:str, cfgfile:str="configbt.json", storage_type:str="csv", storage_path:str = None, db_config:dict = None):
'''
配置回测环境\n
@deps_dir 依赖文件目录\n
@cfgfile 配置文件名\n
@storage_type 存储类型,csv/bin等\n
@storage_path 存储路径
'''
self.env_params["deps_dir"] = deps_dir
self.env_params["cfgfile"] = cfgfile
self.env_params["storage_type"] = storage_type
if storage_path is None and db_config is None:
raise Exception("storage_path and db_config cannot be both None!")
if storage_type == 'db' and db_config is None:
raise Exception("db_config cannot be None while storage_type is db!")
self.env_params["storage_path"] = storage_path
self.env_params["db_config"] = db_config
def config_backtest_time(self, start_time:int, end_time:int):
'''
配置回测时间,可多次调用配置多个回测时间区间\n
@start_time 开始时间,精确到分钟,格式如201909100930\n
@end_time 结束时间,精确到分钟,格式如201909100930
'''
if "time_ranges" not in self.env_params:
self.env_params["time_ranges"] = []
self.env_params["time_ranges"].append([start_time,end_time])
def __gen_tasks__(self, markerfile:str = "strategies.json"):
'''
生成回测任务
'''
param_names = self.mutable_params.keys()
param_values = dict()
# 先生成各个参数的变量数组
# 并计算总的参数有多少组
total_groups = 1
for name in param_names:
paramInfo = self.mutable_params[name]
values = paramInfo.gen_array()
param_values[name] = values
total_groups *= len(values)
#再生成最终每一组的参数dict
param_groups = list()
stra_names = dict()
time_ranges = self.env_params["time_ranges"]
for time_range in time_ranges:
start_time = time_range[0]
end_time = time_range[1]
for i in range(total_groups):
k = i
thisGrp = self.fixed_params.copy() #复制固定参数
endix = ''
for name in param_names:
cnt = len(param_values[name])
curVal = param_values[name][k%cnt]
tname = type(curVal)
if tname.__name__ == "list":
val_str = ''
for item in curVal:
val_str += str(item)
val_str += "_"
val_str = val_str[:-1]
thisGrp[name] = curVal
endix += name
endix += "_"
endix += val_str
endix += "_"
else:
thisGrp[name] = curVal
endix += name
endix += "_"
endix += str(curVal)
endix += "_"
k = math.floor(k / cnt)
endix = endix[:-1]
straName = self.name_prefix + endix
straName += "_%d_%d" % (start_time, end_time)
thisGrp["name"] = straName
thisGrp["start_time"] = start_time
thisGrp["end_time"] = end_time
stra_names[straName] = thisGrp
param_groups.append(thisGrp)
# 将每一组参数和对应的策略ID落地到文件中,方便后续的分析
f = open(markerfile, "w")
f.write(json.dumps(obj=stra_names, sort_keys=True, indent=4))
f.close()
return param_groups
def __ayalyze_result__(self, strName:str, time_range:tuple, params:dict):
folder = "./outputs_bt/%s/" % (strName)
df_closes = pd.read_csv(folder + "closes.csv")
df_funds = pd.read_csv(folder + "funds.csv")
df_wins = df_closes[df_closes["profit"]>0]
df_loses = df_closes[df_closes["profit"]<=0]
ay_WinnerBarCnts = df_wins["closebarno"]-df_wins["openbarno"]
ay_LoserBarCnts = df_loses["closebarno"]-df_loses["openbarno"]
total_winbarcnts = ay_WinnerBarCnts.sum()
total_losebarcnts = ay_LoserBarCnts.sum()
total_fee = df_funds.iloc[-1]["fee"]
totaltimes = len(df_closes) # 总交易次数
wintimes = len(df_wins) # 盈利次数
losetimes = len(df_loses) # 亏损次数
winamout = df_wins["profit"].sum() #毛盈利
loseamount = df_loses["profit"].sum() #毛亏损
trdnetprofit = winamout + loseamount #交易净盈亏
accnetprofit = trdnetprofit - total_fee #账户净盈亏
winrate = wintimes / totaltimes if totaltimes>0 else 0 # 胜率
avgprof = trdnetprofit/totaltimes if totaltimes>0 else 0 # 单次平均盈亏
avgprof_win = winamout/wintimes if wintimes>0 else 0 # 单次盈利均值
avgprof_lose = loseamount/losetimes if losetimes>0 else 0 # 单次亏损均值
winloseratio = abs(avgprof_win/avgprof_lose) if avgprof_lose!=0 else "N/A" # 单次盈亏均值比
max_consecutive_wins = 0 # 最大连续盈利次数
max_consecutive_loses = 0 # 最大连续亏损次数
avg_bars_in_winner = total_winbarcnts/wintimes if wintimes>0 else "N/A"
avg_bars_in_loser = total_losebarcnts/losetimes if losetimes>0 else "N/A"
consecutive_wins = 0
consecutive_loses = 0
for idx, row in df_closes.iterrows():
profit = row["profit"]
if profit > 0:
consecutive_wins += 1
consecutive_loses = 0
else:
consecutive_wins = 0
consecutive_loses += 1
max_consecutive_wins = max(max_consecutive_wins, consecutive_wins)
max_consecutive_loses = max(max_consecutive_loses, consecutive_loses)
summary = params.copy()
summary["开始时间"] = time_range[0]
summary["结束时间"] = time_range[1]
summary["总交易次数"] = totaltimes
summary["盈利次数"] = wintimes
summary["亏损次数"] = losetimes
summary["毛盈利"] = float(winamout)
summary["毛亏损"] = float(loseamount)
summary["交易净盈亏"] = float(trdnetprofit)
summary["胜率"] = winrate*100
summary["单次平均盈亏"] = avgprof
summary["单次盈利均值"] = avgprof_win
summary["单次亏损均值"] = avgprof_lose
summary["单次盈亏均值比"] = winloseratio
summary["最大连续盈利次数"] = max_consecutive_wins
summary["最大连续亏损次数"] = max_consecutive_loses
summary["平均盈利周期"] = avg_bars_in_winner
summary["平均亏损周期"] = avg_bars_in_loser
summary["平均账户收益率"] = accnetprofit/totaltimes
f = open(folder+"summary.json", mode="w")
f.write(json.dumps(obj=summary, indent=4))
f.close()
return
def __execute_task__(self, params:dict):
'''
执行单个回测任务\n
@params kv形式的参数
'''
name = params["name"]
f = open("logcfg_tpl.json", "r")
content =f.read()
f.close()
content = content.replace("$NAME$", name)
engine = WtBtEngine(eType=EngineType.ET_CTA, logCfg=content, isFile=False)
engine.init(self.env_params["deps_dir"], self.env_params["cfgfile"])
engine.configBacktest(params["start_time"], params["end_time"])
engine.configBTStorage(mode=self.env_params["storage_type"], path=self.env_params["storage_path"], dbcfg=self.env_params["db_config"])
time_range = (params["start_time"], params["end_time"])
# 去掉多余的参数
params.pop("start_time")
params.pop("end_time")
if self.cpp_stra_module is not None:
params.pop("name")
engine.setExternalCtaStrategy(name, self.cpp_stra_module, self.cpp_stra_type, params)
else:
straInfo = self.strategy_type(**params)
engine.set_cta_strategy(straInfo)
engine.commitBTConfig()
engine.run_backtest()
engine.release_backtest()
self.__ayalyze_result__(name, time_range, params)
def __start_task__(self, params:dict):
'''
启动单个回测任务\n
这里用线程启动子进程的目的是为了可以控制总的工作进程个数\n
可以在线程中join等待子进程结束,再更新running_worker变量\n
如果在__execute_task__中修改running_worker,因为在不同进程中,数据并不同步\n
@params kv形式的参数
'''
p = multiprocessing.Process(target=self.__execute_task__, args=(params,))
p.start()
p.join()
self.running_worker -= 1
print("工作进程%d个" % (self.running_worker))
def go(self, interval:float = 0.2, out_marker_file:str = "strategies.json", out_summary_file:str = "total_summary.csv"):
'''
启动优化器\n
@interval 时间间隔,单位秒
@markerfile 标记文件名,回测完成以后分析会用到
'''
self.tasks = self.__gen_tasks__(out_marker_file)
self.running_worker = 0
total_task = len(self.tasks)
left_task = total_task
while True:
if left_task == 0:
break
if self.running_worker < self.worker_num:
params = self.tasks[total_task-left_task]
left_task -= 1
print("剩余任务%d个" % (left_task))
p = threading.Thread(target=self.__start_task__, args=(params,))
p.start()
self.running_worker += 1
print("工作进程%d个" % (self.running_worker))
else:
time.sleep(interval)
#最后,全部任务都已经启动完了,再等待所有工作进程结束
while True:
if self.running_worker == 0:
break
else:
time.sleep(interval)
#开始汇总回测结果
f = open(out_marker_file, "r")
content = f.read()
f.close()
obj_stras = json.loads(content)
total_summary = list()
for straName in obj_stras:
filename = "./outputs_bt/%s/summary.json" % (straName)
if not os.path.exists(filename):
print("%s不存在,请检查数据" % (filename))
continue
f = open(filename, "r")
content = f.read()
f.close()
obj_summary = json.loads(content)
total_summary.append(obj_summary)
df_summary = df(total_summary)
# df_summary = df_summary.drop(labels=["name"], axis='columns')
df_summary.to_csv(out_summary_file, encoding='utf-8-sig')
def analyze(self, out_marker_file:str = "strategies.json", out_summary_file:str = "total_summary.csv"):
#开始汇总回测结果
f = open(out_marker_file, "r")
content = f.read()
f.close()
total_summary = list()
obj_stras = json.loads(content)
for straName in obj_stras:
params = obj_stras[straName]
filename = "./outputs_bt/%s/summary.json" % (straName)
if not os.path.exists(filename):
print("%s不存在,请检查数据" % (filename))
continue
time_range = (params["start_time"],params["end_time"])
self.__ayalyze_result__(straName, time_range, params)
f = open(filename, "r")
content = f.read()
f.close()
obj_summary = json.loads(content)
total_summary.append(obj_summary)
df_summary = df(total_summary)
df_summary = df_summary.drop(labels=["name"], axis='columns')
df_summary.to_csv(out_summary_file)
def analyzer(self, out_marker_file:str = "strategies.json", init_capital=500000, rf=0.02, annual_trading_days=240):
for straname in json.load(open(out_marker_file, mode='r')).keys():
try:
analyst = WtBtAnalyst()
analyst.add_strategy(straname, folder="./outputs_bt/%s/"%straname, init_capital=init_capital, rf=rf, annual_trading_days=annual_trading_days)
analyst.run()
except:
pass
================================================
FILE: wtpy/apps/WtHotPicker.py
================================================
import datetime
import time
import json
import os
import logging
import functools
import urllib.request
import io
import gzip
import xml.dom.minidom
from pyquery import PyQuery as pq
import re
class DayData:
'''
每日行情数据
'''
def __init__(self):
self.pid = ''
self.month = 0
self.code = '' # 代码
self.close = 0 # 今收盘(收盘价)
self.volume = 0 # 成交量(手)
self.hold = 0 # 空盘量(总持?持仓量)
def extractPID(code):
for idx in range(0, len(code)):
c = code[idx]
if '0' <= c and c <= '9':
break
return code[:idx]
def readFileContent(filename):
if not os.path.exists(filename):
return ""
f = open(filename, 'r')
content = f.read()
f.close()
return content
def cmp_alg_01(left:DayData, right:DayData):
if left.month > right.month:
if left.hold > right.hold and left.volume > right.volume/3:
return 1
else:
return -1
else:
if left.hold <= right.hold or left.volume <= right.volume/3:
return -1
else:
return 1
def countFridays(curDate:datetime.datetime):
'''
计算截止到当周的周五的天数
'''
wd = curDate.weekday()
checkDate = datetime.datetime(year=curDate.year, month=curDate.month, day=1)
count = 0
while checkDate < curDate:
if checkDate.weekday() == 4:
count += 1
checkDate += datetime.timedelta(days=1)
if wd < 4:
count += 1
return count
def httpGet(url, encoding='utf-8'):
request = urllib.request.Request(url)
request.add_header('Accept-encoding', 'gzip')
request.add_header(
'User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')
try:
f = urllib.request.urlopen(request)
ec = f.headers.get('Content-Encoding')
if ec == 'gzip':
cd = f.read()
cs = io.BytesIO(cd)
f = gzip.GzipFile(fileobj=cs)
return f.read().decode(encoding)
except:
return ""
def httpPost(url, datas, encoding='utf-8'):
headers = {
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
'Accept-encoding': 'gzip'
}
data = urllib.parse.urlencode(datas).encode('utf-8')
request = urllib.request.Request(url, data, headers)
try:
f = urllib.request.urlopen(request)
ec = f.headers.get('Content-Encoding')
if ec == 'gzip':
cd = f.read()
cs = io.BytesIO(cd)
f = gzip.GzipFile(fileobj=cs)
return f.read().decode(encoding)
except:
return ""
class WtCacheMon:
'''
缓存管理器基类
'''
def __init__(self):
self.day_cache = dict()
def get_cache(self, exchg, curDT:datetime.datetime):
pass
class WtCacheMonExchg(WtCacheMon):
'''
交易所行情缓存器
通过到交易所官网上拉取当日的行情快照,缓存当日行情数据
'''
@staticmethod
def getCffexData(curDT:datetime.datetime) -> dict:
'''
读取CFFEX指定日期的行情快照
@curDT 指定的日期
'''
dtStr = curDT.strftime('%Y%m%d')
dtNum = int(dtStr)
path = "http://www.cffex.com.cn/fzjy/mrhq/%d/%02d/index.xml" % (dtNum/100, dtNum % 100)
content = httpGet(path)
if len(content) == 0:
return None
try:
dom = xml.dom.minidom.parseString(content)
except:
logging.info("[CFFEX]%s无数据,跳过" % (dtStr))
return None
root = dom.documentElement
items = {}
days = root.getElementsByTagName("dailydata")
for day in days:
pid = day.getElementsByTagName(
"productid")[0].firstChild.data.strip()
if pid not in ["IF","IH","IC","T",'TF']:
continue
item = DayData()
item.code = day.getElementsByTagName("instrumentid")[
0].firstChild.data.strip()
item.pid = pid
item.hold = float(day.getElementsByTagName(
"openinterest")[0].firstChild.data)
item.close = float(day.getElementsByTagName(
"closeprice")[0].firstChild.data)
item.volume = int(day.getElementsByTagName(
"volume")[0].firstChild.data)
item.month = item.code[len(item.pid):]
items[item.code] = item
return items
@staticmethod
def getShfeData(curDT:datetime.datetime) -> dict:
'''
读取SHFE指定日期的行情快照
@curDT 指定的日期
'''
dtStr = curDT.strftime('%Y%m%d')
content = httpGet("http://www.shfe.com.cn/data/dailydata/kx/kx%s.dat" % (dtStr))
if len(content) == 0:
return None
items = {}
root = json.loads(content)
for day in root['o_curinstrument']:
pid = day['PRODUCTID'].strip().rstrip('_f')
dm = day['DELIVERYMONTH']
if len(str(day['CLOSEPRICE']).strip()) == 0:
continue
code = pid + dm
item = DayData()
item.pid = pid
item.code = code
item.hold = int(day['OPENINTEREST'])
if day['VOLUME'] != '':
item.volume = int(day['VOLUME'])
item.close = float(day["CLOSEPRICE"])
item.month = item.code[len(item.pid):]
items[code] = item
return items
@staticmethod
def getCzceData(curDT:datetime.datetime) -> dict:
'''
读取CZCE指定日期的行情快照
@curDT 指定的日期
'''
dtStr = curDT.strftime('%Y%m%d')
url = 'http://www.czce.com.cn/cn/DFSStaticFiles/Future/%s/%s/FutureDataDaily.htm' % (dtStr[0:4], dtStr)
try:
html = httpGet(url).strip()
except urllib.error.HTTPError as httperror:
print(httperror)
return None
if len(html) == 0:
return None
dataitems = {}
doc = pq(html)
# print(doc(#senfe .table table))
items = doc('#senfe')
# 去掉第一行标题
items.remove('tr.tr0')
# 获取tr items.find('tr')
lis = items('tr')
# print(lis)
# tr行数
trcount = len(lis)
# 遍历行
for tr in range(0, trcount-1):
item = DayData()
tdlis = doc(lis[tr])('td')
item.code = doc(tdlis[0]).text()
ay = re.compile('[A-Za-z]+').findall(item.code)
if len(ay) == 0:
continue
item.pid = ay[0]
close = doc(tdlis[5]).text()
if close != '':
item.close = float(close.replace(",",""))
volume = doc(tdlis[9]).text()
if volume != '':
item.volume = int(volume.replace(",",""))
hold = doc(tdlis[10]).text()
if hold != '':
item.hold = int(hold.replace(",",""))
item.month = item.code[len(item.pid):]
if item.month[0] == '0':
item.month = "2" + item.month
else:
item.month = "1" + item.month
dataitems[item.code] = item
# print(dataitems)
return dataitems
@staticmethod
def getDceData(curDT:datetime.datetime) -> dict:
'''
读取DCE指定日期的行情快照
@curDT 指定的日期
'''
pname_map = {
"聚乙烯": "l",
"鸡蛋": "jd",
"焦煤": "jm",
"豆二": "b",
"胶合板": "bb",
"玉米": "c",
"豆粕": "m",
"棕榈油": "p",
"玉米淀粉": "cs",
"纤维板": "fb",
"铁矿石": "i",
"焦炭": "j",
"豆一": "a",
"聚丙烯": "pp",
"聚氯乙烯": "v",
"豆油": "y",
"乙二醇":"eg",
"粳米":"rr",
"苯乙烯":"eb",
"液化石油气":"pg",
"生猪":"lh"
}
url = 'http://www.dce.com.cn/publicweb/quotesdata/dayQuotesCh.html'
try:
data = {}
data['dayQuotes.variety'] = 'all'
data['dayQuotes.trade_type'] = 0
data['year'] = curDT.year
data['month'] = curDT.month - 1
data['day'] = curDT.day
html = httpPost(url, data)
except urllib.error.HTTPError as httperror:
print(httperror)
return None
dataitems = {}
doc = pq(html)
items = doc('.dataArea') # doc('#printData')
# # 获取tr items.find('tr')
lis = items('tr')
trcount = len(lis)
# 遍历行
for tr in range(1, trcount):
tdlis = doc(lis[tr])('td')
# 商品名称
pzname = doc(tdlis[0]).text()
if pzname not in pname_map:
if "小计" not in pzname and "总计" not in pzname:
logging.error("未知品种:" + pzname)
continue
# 交割月份
item = DayData()
item.pid = pname_map[pzname]
item.code = item.pid + doc(tdlis[1]).text()
# 收盘价
spj = doc(tdlis[5]).text()
item.close = float(spj if spj != '' else 0)
# 成交量
item.volume = int(doc(tdlis[10]).text())
# 持仓量
item.hold = int(doc(tdlis[11]).text())
item.month = item.code[len(item.pid):]
dataitems[item.code] = item
return dataitems
@staticmethod
def getIneData(curDT:datetime.datetime) -> dict:
'''
读取INE指定日期的行情快照
@curDT 指定的日期
'''
dtStr = curDT.strftime('%Y%m%d')
content = httpGet("http://www.ine.cn/data/dailydata/kx/kx%s.dat" % (dtStr))
if len(content) == 0:
return None
items = {}
root = json.loads(content)
for day in root['o_curinstrument']:
pid = day['PRODUCTID'].strip().rstrip('_f')
dm = day['DELIVERYMONTH']
if pid != 'sc' or dm == '' or dm == '小计':
continue
item = DayData()
item.pid = pid
item.code = pid + dm
item.hold = int(day['OPENINTEREST'])
item.close = float(day['CLOSEPRICE'])
item.volume = int(day['VOLUME']) if day['VOLUME']!='' else 0
item.month = item.code[len(item.pid):]
items[item.code] = item
return items
def cache_by_date(self, exchg:str, curDT:datetime.datetime):
'''
缓存指定日期指定交易所的行数据
@exchg 交易所代码
@curDT 指定日期
'''
dtStr = curDT.strftime('%Y%m%d')
if dtStr not in self.day_cache:
self.day_cache[dtStr] = dict()
cacheItem = self.day_cache[dtStr]
if exchg == 'CFFEX':
cacheItem[exchg] = WtCacheMonExchg.getCffexData(curDT)
elif exchg == 'SHFE':
cacheItem[exchg] = WtCacheMonExchg.getShfeData(curDT)
elif exchg == 'DCE':
cacheItem[exchg] = WtCacheMonExchg.getDceData(curDT)
elif exchg == 'CZCE':
cacheItem[exchg] = WtCacheMonExchg.getCzceData(curDT)
elif exchg == 'INE':
cacheItem[exchg] = WtCacheMonExchg.getIneData(curDT)
else:
raise Exception("未知交易所代码" + exchg)
def get_cache(self, exchg:str, curDT:datetime.datetime):
'''
获取指定日期的某个交易所合约的快照数据
@exchg 交易所代码
@curDT 指定日期
'''
dtStr = curDT.strftime('%Y%m%d')
if dtStr not in self.day_cache or exchg not in self.day_cache[dtStr]:
self.cache_by_date(exchg, curDT)
if dtStr not in self.day_cache:
return None
if exchg not in self.day_cache[dtStr]:
return None
return self.day_cache[dtStr][exchg]
class WtCacheMonSS(WtCacheMon):
'''
快照缓存管理器
通过读取wtpy的datakit当日生成的快照文件,缓存当日行情数据
一般目录为"数据存储目录/his/snapshots/xxxxxxx.csv"
'''
def __init__(self, snapshot_path:str):
WtCacheMon.__init__(self)
self.snapshot_path = snapshot_path
def cache_snapshot(self, curDT:datetime):
'''
缓存指定日期的快照数据
@curDT 指定的日期
'''
dtStr = curDT.strftime('%Y%m%d')
filename = "%s%s.csv" % (self.snapshot_path, dtStr)
content = readFileContent(filename)
lines = content.split("\n")
if dtStr not in self.day_cache:
self.day_cache[dtStr] = dict()
cacheItem = self.day_cache[dtStr]
for idx in range(1, len(lines)):
line = lines[idx]
if len(line) == 0:
break
items = line.split(",")
exchg = items[1]
if exchg not in cacheItem:
cacheItem[exchg] = dict()
day = DayData()
day.pid = extractPID(items[2])
day.code = items[2]
# 收盘价
day.close = float(items[6])
# 成交量
day.volume = int(items[8])
# 持仓量
day.hold = int(items[10])
day.month = day.code[len(day.pid):]
if len(day.month) == 3:
if day.month[0] >= '0' and day.month[0] <= '5':
day.month = "2" + day.month
else:
day.month = "1" + day.month
cacheItem[exchg][day.code] = day
def get_cache(self, exchg, curDT:datetime):
'''
获取指定日期的某个交易所合约的快照数据
@exchg 交易所代码
@curDT 指定日期
'''
dtStr = curDT.strftime('%Y%m%d')
if dtStr not in self.day_cache:
self.cache_snapshot(curDT)
if dtStr not in self.day_cache:
return None
if exchg not in self.day_cache[dtStr]:
return None
return self.day_cache[dtStr][exchg]
class WtMailNotifier:
'''
邮件通知器
'''
def __init__(self, user:str, pwd:str, sender:str=None, host:str="smtp.exmail.qq.com", port=465, isSSL:bool = True):
self.user = user
self.pwd = pwd
self.sender = sender if sender is not None else "WtHotNotifier<%s>" % (user)
self.receivers = list()
self.mail_host = host
self.mail_port = port
self.mail_ssl = isSSL
def add_receiver(self, name:str, addr:str):
'''
添加收件人
@name 收件人姓名
@addr 收件人邮箱地址
'''
self.receivers.append({
"name":name,
"addr":addr
})
def notify(self, hot_changes:dict, sec_changes:dict, nextDT:datetime.datetime, hotFile:str, hotMap:str, secFile:str, secMap:str):
'''
通知主力切换事件
@hot_changes 当日主力切换的规则列表
@sec_changes 当日次主力切换的规则列表
@nextDT 生效日期
@hotFile 主力规则文件
@hotMap 主力映射文件
'''
dtStr = nextDT.strftime('%Y.%m.%d')
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.header import Header
sender = self.sender
receivers = self.receivers
content = ''
for exchg in hot_changes:
for pid in hot_changes[exchg]:
item = hot_changes[exchg][pid][-1]
content += '品种%s.%s的主力合约已切换,下个交易日(%s)生效, %s -> %s\n' % (exchg, pid, dtStr, item["from"], item["to"])
content += '\n'
for exchg in sec_changes:
for pid in sec_changes[exchg]:
item = sec_changes[exchg][pid][-1]
content += '品种%s.%s的次主力合约已切换,下个交易日(%s)生效, %s -> %s\n' % (exchg, pid, dtStr, item["from"], item["to"])
msg_mp = MIMEMultipart()
msg_mp['From'] = sender # 发送者
subject = '主力合约换月邮件<%s>' % (dtStr)
msg_mp['Subject'] = Header(subject, 'utf-8')
content = MIMEText(content, 'plain', 'utf-8')
msg_mp.attach(content)
xlspart = MIMEApplication(open(hotFile,'rb').read())
xlspart["Content-Type"] = 'application/octet-stream'
xlspart.add_header('Content-Disposition','attachment', filename=os.path.basename(hotFile))
msg_mp.attach(xlspart)
xlspart = MIMEApplication(open(hotMap,'rb').read())
xlspart["Content-Type"] = 'application/octet-stream'
xlspart.add_header('Content-Disposition','attachment', filename=os.path.basename(hotMap))
msg_mp.attach(xlspart)
xlspart = MIMEApplication(open(secFile,'rb').read())
xlspart["Content-Type"] = 'application/octet-stream'
xlspart.add_header('Content-Disposition','attachment', filename=os.path.basename(secFile))
msg_mp.attach(xlspart)
xlspart = MIMEApplication(open(secMap,'rb').read())
xlspart["Content-Type"] = 'application/octet-stream'
xlspart.add_header('Content-Disposition','attachment', filename=os.path.basename(secMap))
msg_mp.attach(xlspart)
if self.mail_ssl:
smtpObj = smtplib.SMTP_SSL(self.mail_host, self.mail_port)
else:
smtpObj = smtplib.SMTP(self.mail_host, self.mail_port)
try:
smtpObj.ehlo()
smtpObj.login(self.user, self.pwd)
logging.info("%s 登录成功 %s:%d", self.user, self.mail_host, self.mail_port)
except smtplib.SMTPException as ex:
logging.error("邮箱初始化失败:{}".format(ex))
for item in receivers:
to = "%s<%s>" % (item["name"], item["addr"])
msg_mp['To'] = Header(to, 'utf-8') # 接收者
try:
smtpObj.sendmail(sender, item["addr"], msg_mp.as_string())
logging.info("邮件发送失败,收件人: %s", to)
except smtplib.SMTPException as ex:
logging.error("邮件发送失败,收件人:{}, {}".format(to, ex))
class WtHotPicker:
'''
主力选择器
'''
def __init__(self, markerFile:str = "./marker.json", hotFile:str = "../Common/hots.json", secFile:str = None):
self.marker_file = markerFile
self.hot_file = hotFile
self.sec_file = secFile
self.mail_notifier:WtMailNotifier = None
self.cache_monitor:WtCacheMon = None
self.current_hots = None
self.current_secs = None
def set_cacher(self, cacher:WtCacheMon):
'''
设置日行情缓存器
'''
self.cache_monitor = cacher
def set_mail_notifier(self, notifier:WtMailNotifier):
'''
设置邮件通知器
'''
self.mail_notifier = notifier
def pick_exchg_hots(self, exchg:str, beginDT:datetime.datetime, endDT:datetime.datetime, alg:int = 0):
'''
确定指定市场的主力合约
@exchg 交易所代码
@beginDT 开始日期
@endDT 截止日期
@alg 切换规则算法,0-除中金所外,按成交量确定,1-中金所,按照成交量和总持共同确定
'''
cacheMon = self.cache_monitor
current_hots = self.current_hots
current_secs = self.current_secs
if exchg not in current_hots:
current_hots[exchg] = dict()
if exchg not in current_secs:
current_secs[exchg] = dict()
lastHots = current_hots[exchg]
lastSecs = current_secs[exchg]
hot_switches = {}
sec_switches = {}
curDT = beginDT
while curDT <= endDT:
hots = {}
seconds = {}
logging.info("[%s]开始拉取%s数据" % (exchg, curDT.strftime('%Y%m%d')))
items = cacheMon.get_cache(exchg, curDT)
if items is not None:
wd = curDT.weekday()
fri_cnt = countFridays(curDT)
cur_month = curDT.strftime('%Y%m')[2:]
next_month = int(cur_month)+1
if next_month % 100 == 13:
next_month = str(int(cur_month[:2])+1)+"01"
else:
next_month = str(next_month)
items_by_pid = dict()
for code in items:
item = items[code]
pid = item.pid
if pid not in items_by_pid:
items_by_pid[pid] = list()
items_by_pid[pid].append(item)
for pid in items_by_pid:
ay = items_by_pid[pid]
if alg == 1:
#ay.sort(key=functools.cmp_to_key(cmp_alg_01)) #按总持排序
ay.sort(key=lambda x : x.volume) #按成交量
elif alg == 0:
ay.sort(key=lambda x : x.hold) #按总持
hot = ay[-1]
if len(ay) > 1:
sec = ay[-2]
#中金所算法,如果是当月第三个周三,并且主力合约月份小于次主力合约月份,
#说明没有根据数据自动换月,强制进行换月
if alg == 1 and wd == 2 and fri_cnt == 3 and hot.month==cur_month:
for item in ay:
if item.month == next_month:
hot = item
break
#如果主力合约月份大于等于次主力合约,则次主力递延一位
if hot.month >= sec.month:
sec = ay[-3]
seconds[pid] = sec.code
hots[pid] = hot.code
for key in hots.keys():
nextDT = curDT + datetime.timedelta(days=1)
if key not in lastHots:
item = {}
item["date"] = int(curDT.strftime('%Y%m%d'))
item["from"] = ""
item["to"] = hots[key]
item["oldclose"] = 0.0
item["newclose"] = items[hots[key]].close
hot_switches[key] = [item]
lastHots[key] = hots[key]
logging.info("[%s]品种%s主力确认, 确认日期: %s, %s", exchg,key, nextDT.strftime('%Y%m%d'), hots[key])
else:
oldcode = lastHots[key]
newcode = hots[key]
oldItem = None
if oldcode in items:
oldItem = items[oldcode]
newItem = items[newcode]
if oldItem is None or newItem.month > oldItem.month:
item = {}
item["date"] = int(nextDT.strftime('%Y%m%d'))
item["from"] = oldcode
item["to"] = newcode
if oldcode in items:
item["oldclose"] = items[oldcode].close
else:
item["oldclose"] = 0.0
item["date"] = int(curDT.strftime('%Y%m%d'))
item["newclose"] = items[newcode].close
if key not in hot_switches:
hot_switches[key] = list()
hot_switches[key].append(item)
logging.info("[%s]品种%s主力切换 切换日期: %s,%s -> %s", exchg, key, nextDT.strftime('%Y%m%d'), lastHots[key], hots[key])
lastHots[key] = hots[key]
for key in seconds.keys():
nextDT = curDT + datetime.timedelta(days=1)
if key not in lastSecs:
item = {}
item["date"] = int(curDT.strftime('%Y%m%d'))
item["from"] = ""
item["to"] = seconds[key]
item["oldclose"] = 0.0
item["newclose"] = items[seconds[key]].close
sec_switches[key] = [item]
lastSecs[key] = seconds[key]
logging.info("[%s]品种%s次主力确认, 确认日期: %s, %s", exchg,key, nextDT.strftime('%Y%m%d'), seconds[key])
else:
oldcode = lastSecs[key]
newcode = seconds[key]
oldItem = None
if oldcode in items:
oldItem = items[oldcode]
newItem = items[newcode]
if oldItem is None or newItem.month > oldItem.month:
item = {}
item["date"] = int(nextDT.strftime('%Y%m%d'))
item["from"] = oldcode
item["to"] = newcode
if oldcode in items:
item["oldclose"] = items[oldcode].close
else:
item["oldclose"] = 0.0
item["date"] = int(curDT.strftime('%Y%m%d'))
item["newclose"] = items[newcode].close
if key not in sec_switches:
sec_switches[key] = list()
sec_switches[key].append(item)
logging.info("[%s]品种%s次主力切换 切换日期: %s,%s -> %s", exchg, key, nextDT.strftime('%Y%m%d'), lastSecs[key], seconds[key])
lastSecs[key] = seconds[key]
# 日期递增
curDT = curDT + datetime.timedelta(days=1)
return hot_switches,sec_switches
def merge_switch_list(self, total, exchg, switch_list):
'''
合并主力切换规则
@total 已有的全部切换规则
@exchg 交易所代码
@switcg_list 新的切换规则
'''
if exchg not in total:
total[exchg] = switch_list
logging.info("[%s]全市场主力切换规则重构" % (exchg))
return True, total
bChanged = False
for pid in switch_list:
if pid not in total[exchg]:
total[exchg][pid] = switch_list[pid]
logging.info("[%s]品种%s主力切换规则重构" % (exchg, pid))
bChanged = True
else:
total[exchg][pid].extend(switch_list[pid])
logging.info("[%s]品种%s主力切换规则追加%d条" % (exchg, pid, len(switch_list[pid])))
bChanged = True
return bChanged, total
def execute_rebuild(self, beginDate:datetime.datetime = None, endDate:datetime.datetime = None, exchanges = ["CFFEX", "SHFE", "CZCE", "DCE", "INE"], wait=False):
'''
重构全部的主力切换规则
不依赖现有数据,全部重新确定主力合约的切换规则
@beginDate 开始日期
@endDate 截止日期
@exchanges 要重构的交易所列表
@wait 每个日期切换是否等待,等待时间1s,主要针对从交易所官网拉取,防止被拉黑名单
'''
if endDate is None:
endDate = datetime.datetime.now()
if beginDate is None:
beginDate = datetime.datetime.strptime("2016-01-01", '%Y-%m-%d')
total_hots = dict()
total_secs = dict()
self.current_hots = dict()
self.current_secs = dict()
for exchg in exchanges:
self.current_hots[exchg] = dict()
self.current_secs[exchg] = dict()
hot_changes = dict()
sec_changes = dict()
curDate = beginDate
while curDate <= endDate:
if wait:
time.sleep(1)
for exchg in exchanges:
alg = 1 if exchg=='CFFEX' else 0 # 中金所的换月算法和其他交易所不同
hotRules,secRules = self.pick_exchg_hots(exchg, curDate, curDate, alg=alg)
if len(hotRules.keys()) > 0:
hasChange,total_hots = self.merge_switch_list(total_hots, exchg, hotRules)
if exchg not in hot_changes:
hot_changes[exchg] = dict()
hot_changes[exchg].update(hotRules)
if len(secRules.keys()) > 0:
hasChange,total_secs = self.merge_switch_list(total_secs, exchg, secRules)
if exchg not in sec_changes:
sec_changes[exchg] = dict()
sec_changes[exchg].update(secRules)
curDate = curDate + datetime.timedelta(days=1)
#日期标记要保存
marker = dict()
marker["date"] = int(endDate.strftime('%Y%m%d'))
output = open(self.marker_file, 'w')
output.write(json.dumps(marker, sort_keys=True, indent = 4))
output.close()
logging.info("主力切换规则已更新")
output = open(self.hot_file, 'w')
output.write(json.dumps(total_hots, sort_keys=True, indent = 4))
output.close()
if self.sec_file is not None:
output = open(self.sec_file, 'w')
output.write(json.dumps(total_secs, sort_keys=True, indent = 4))
output.close()
output = open("hotmap.json", 'w')
output.write(json.dumps(self.current_hots, sort_keys=True, indent = 4))
output.close()
output = open("secmap.json", 'w')
output.write(json.dumps(self.current_secs, sort_keys=True, indent = 4))
output.close()
if self.mail_notifier is not None:
self.mail_notifier.notify(hot_changes, sec_changes, endDate, hotFile, "hotmap.json", secFile, "secmap.json")
return total_hots,total_secs
def execute_increment(self, endDate:datetime.datetime = None, exchanges = ["CFFEX", "SHFE", "CZCE", "DCE", "INE"]):
'''
增量更新主力切换规则
会自动加载marker.json取得上次更新的日期,并读取hots.json确定当前的映射规则
@endDate 截止日期
@exchanges 要重构的交易所列表
'''
if endDate is None:
endDate = datetime.datetime.now()
markerFile = self.marker_file
hotFile = self.hot_file
secFile = self.sec_file
marker = {"date":"0"}
c = readFileContent(markerFile)
if len(c) > 0:
marker = json.loads(c)
c = readFileContent(hotFile)
total_hots = dict()
if len(c) > 0:
total_hots = json.loads(c)
else:
marker["date"] = "0"
c = readFileContent(secFile)
total_secs = dict()
if len(c) > 0:
total_secs = json.loads(c)
else:
marker["date"] = "0"
lastDate = str(marker["date"])
if lastDate >= endDate.strftime('%Y%m%d'):
logging.info("上次更新日期%s大于结束日期%s,退出更新" % (lastDate, endDate.strftime('%Y%m%d')))
exit()
elif lastDate != "0":
beginDT = datetime.datetime.strptime(lastDate, "%Y%m%d") + datetime.timedelta(days=1)
else:
beginDT = datetime.datetime.strptime("2016-01-01", '%Y-%m-%d')
self.current_hots = dict()
self.current_secs = dict()
for exchg in total_hots:
if exchg not in self.current_hots:
self.current_hots[exchg] = dict()
for pid in total_hots[exchg]:
ay = total_hots[exchg][pid]
self.current_hots[exchg][pid] = ay[-1]["to"]
for exchg in total_secs:
if exchg not in self.current_secs:
self.current_secs[exchg] = dict()
for pid in total_secs[exchg]:
ay = total_secs[exchg][pid]
self.current_secs[exchg][pid] = ay[-1]["to"]
bChanged = False
hot_changes = dict()
sec_changes = dict()
for exchg in exchanges:
logging.info("[%s]开始分析主力换月数据" % exchg)
alg = 1 if exchg=='CFFEX' else 0 # 中金所的换月算法和其他交易所不同
hotRules,secRules = self.pick_exchg_hots(exchg, beginDT, endDate, alg=alg)
if len(hotRules.keys()) > 0:
hasChange,total_hots = self.merge_switch_list(total_hots, exchg, hotRules)
bChanged = bChanged or hasChange
hot_changes[exchg] = hotRules
if len(secRules.keys()) > 0:
hasChange,total_secs = self.merge_switch_list(total_secs, exchg, secRules)
bChanged = bChanged or hasChange
sec_changes[exchg] = secRules
#日期标记要保存
marker = dict()
marker["date"] = int(endDate.strftime('%Y%m%d'))
output = open(markerFile, 'w')
output.write(json.dumps(marker, sort_keys=True, indent = 4))
output.close()
if bChanged:
logging.info("主力切换规则已更新")
output = open(hotFile, 'w')
output.write(json.dumps(total_hots, sort_keys=True, indent = 4))
output.close()
output = open(secFile, 'w')
output.write(json.dumps(total_secs, sort_keys=True, indent = 4))
output.close()
output = open("hotmap.json", 'w')
output.write(json.dumps(self.current_hots, sort_keys=True, indent = 4))
output.close()
output = open("secmap.json", 'w')
output.write(json.dumps(self.current_secs, sort_keys=True, indent = 4))
output.close()
if self.mail_notifier is not None:
self.mail_notifier.notify(hot_changes, sec_changes, endDate, hotFile, "hotmap.json", secFile, "secmap.json")
else:
logging.info("主力切换规则未更新,不保存数据")
================================================
FILE: wtpy/apps/__init__.py
================================================
from .WtBtAnalyst import WtBtAnalyst
from .WtCtaOptimizer import WtCtaOptimizer
from .WtHotPicker import WtHotPicker, WtCacheMonExchg, WtCacheMonSS, WtMailNotifier, WtCacheMon
__all__ = ["WtBtAnalyst","WtCtaOptimizer", "WtHotPicker", "WtCacheMonExchg", "WtCacheMonSS", "WtMailNotifier", "WtCacheMon"]
================================================
FILE: wtpy/apps/datahelper/DHBaostock.py
================================================
from wtpy.apps.datahelper.DHDefs import BaseDataHelper, DBHelper
import baostock as bs
from datetime import datetime
import json
import os
def transCodes(codes:list) -> list:
ret = list()
for code in codes:
items = code.split(".")
exchg = items[0]
if exchg == "SSE":
ret.append("sh."+items[1])
else:
ret.append("sz."+items[1])
return ret
class DHBaostock(BaseDataHelper):
def __init__(self):
BaseDataHelper.__init__(self)
print("Baostock helper has been created.")
return
def auth(self, **kwargs):
if self.isAuthed:
return
bs.login()
self.isAuthed = True
print("Baostock has been authorized.")
def dmpCodeListToFile(self, filename:str, hasIndex:bool=True, hasStock:bool=True):
raise Exception("Baostock has not code list api")
def dmpAdjFactorsToFile(self, codes:list, filename:str):
codes = transCodes(codes)
stocks = {
"SSE":{},
"SZSE":{}
}
count = 0
length = len(codes)
for code in codes:
exchg = code[:2]
if exchg == 'sh':
exchg = 'SSE'
else:
exchg = 'SZSE'
count += 1
stocks[exchg][code[3:]] = list()
print("Fetching adjust factors of %s(%d/%s)..." % (code, count, length))
rs = bs.query_adjust_factor(code=code, start_date="1990-01-01")
if rs.error_code != '0':
print("Error occured: %s" % (rs.error_msg))
continue
while rs.next():
items = rs.get_row_data()
date = int(items[1].replace("-",""))
factor = float(items[4])
stocks[exchg][code[3:]].append({
"date": date,
"factor": factor
})
print("Writing adjust factors into file %s..." % (filename))
f = open(filename, 'w+')
f.write(json.dumps(stocks, sort_keys=True, indent=4, ensure_ascii=False))
f.close()
def dmpBarsToFile(self, folder:str, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
codes = transCodes(codes)
if start_date is None:
start_date = datetime(year=1990, month=1, day=1)
if end_date is None:
end_date = datetime.now()
start_date = start_date.strftime("%Y-%m-%d")
end_date = end_date.strftime("%Y-%m-%d")
freq = ''
isDay = False
filetag = ''
fields = ""
if period == 'day':
freq = 'd'
isDay = True
filetag = 'd'
fields = "date,open,high,low,close,volume,amount"
elif period == "min5":
freq = '5'
filetag = 'm5'
fields = "date,time,open,high,low,close,volume,amount"
else:
raise Exception("Baostock has only bars of frequency day and min5")
count = 0
length = len(codes)
for code in codes:
exchg = code[:2]
if exchg == 'sh':
exchg = 'SSE'
else:
exchg = 'SZSE'
count += 1
print("Fetching %s bars of %s(%d/%s)..." % (period, code, count, length))
rs = bs.query_history_k_data_plus(code=code, fields=fields, start_date=start_date, end_date=end_date, frequency=freq)
content = "date,time,open,high,low,close,volume,turnover\n"
if rs.error_code != '0':
print("Error occured: %s" % (rs.error_msg))
continue
while rs.next():
items = rs.get_row_data().copy()
if isDay:
items.insert(1, "0")
else:
time = items[1][-9:-3]
items[1] = time[:2]+":"+time[2:4]+":"+time[4:]
content += ",".join(items) + "\n"
filename = "%s.%s_%s.csv" % (exchg, code[3:], filetag)
filepath = os.path.join(folder, filename)
print("Writing bars into file %s..." % (filepath))
f = open(filepath, "w", encoding="utf-8")
f.write(content)
f.close()
def dmpAdjFactorsToDB(self, dbHelper:DBHelper, codes:list):
codes = transCodes(codes)
stocks = {
"SSE":{},
"SZSE":{}
}
count = 0
length = len(codes)
for code in codes:
exchg = code[:2]
if exchg == 'sh':
exchg = 'SSE'
else:
exchg = 'SZSE'
count += 1
print("Fetching adjust factors of %s(%d/%s)..." % (code, count, length))
stocks[exchg][code[3:]] = list()
rs = bs.query_adjust_factor(code=code, start_date="1990-01-01")
if rs.error_code == '0':
print("Error occured: %s" % (rs.error_msg))
continue
while rs.next():
items = rs.get_row_data()
date = int(items[1].replace("-",""))
factor = float(items[4])
stocks[exchg][code[3:]].append({
"date": date,
"factor": factor
})
print("Writing adjust factors into database...")
dbHelper.writeFactors(stocks)
def dmpBarsToDB(self, dbHelper:DBHelper, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
codes = transCodes(codes)
if start_date is None:
start_date = datetime(year=1990, month=1, day=1)
if end_date is None:
end_date = datetime.now()
start_date = start_date.strftime("%Y-%m-%d")
end_date = end_date.strftime("%Y-%m-%d")
freq = ''
isDay = False
fields = ""
if period == 'day':
freq = 'd'
isDay = True
fields = "date,open,high,low,close,volume,amount"
elif period == "min5":
freq = '5'
fields = "date,time,open,high,low,close,volume,amount"
else:
raise Exception("Baostock has only bars of frequency day and min5")
count = 0
length = len(codes)
for code in codes:
exchg = code[:2]
if exchg == 'sh':
exchg = 'SSE'
else:
exchg = 'SZSE'
count += 1
print("Fetching %s bars of %s(%d/%s)..." % (period, code, count, length))
rs = bs.query_history_k_data_plus(code=code, fields=fields, start_date=start_date, end_date=end_date, frequency=freq)
bars = []
while (rs.error_code == '0') & rs.next():
items = rs.get_row_data()
if isDay:
bars.append({
"exchange":exchg,
"code":code[3:],
"date": int(items[0].replace("-","")),
"time": 0,
"open": float(items[1]),
"high": float(items[2]),
"low": float(items[3]),
"close": float(items[4]),
"volume": float(items[5]),
"turnover": float(items[6])
})
else:
time = int(items[1][-9:-5])
bars.append({
"exchange":exchg,
"code":code[3:],
"date": int(items[0].replace("-","")),
"time": time,
"open": float(items[2]),
"high": float(items[3]),
"low": float(items[4]),
"close": float(items[5]),
"volume": float(items[6]),
"turnover": float(items[7])
})
print("Writing bars into database...")
dbHelper.writeBars(bars, period)
================================================
FILE: wtpy/apps/datahelper/DHDefs.py
================================================
from datetime import datetime
class DBHelper:
def __init__(self):
pass
def initDB(self):
'''
初始化数据库,主要是建表等工作
'''
pass
def writeBars(self, bars:list, period="day"):
'''
将K线存储到数据库中\n
@bars K线序列\n
@period K线周期
'''
pass
def writeFactors(self, factors:dict):
'''
将复权因子存储到数据库中\n
@factors 复权因子
'''
pass
class BaseDataHelper:
def __init__(self):
self.isAuthed = False
pass
def __check__(self):
if not self.isAuthed:
raise Exception("This module has not authorized yet!")
def auth(self, **kwargs):
'''
模块认证
'''
pass
def dmpCodeListToFile(self, filename:str, hasIndex:bool=True, hasStock:bool=True):
'''
将代码列表导出到文件\n
@filename 要输出的文件名,json格式\n
@hasIndex 是否包含指数\n
@hasStock 是否包含股票\n
'''
pass
def dmpAdjFactorsToFile(self, codes:list, filename:str):
'''
将除权因子导出到文件\n
@codes 股票列表,格式如["SSE.600000","SZSE.000001"]\n
@filename 要输出的文件名,json格式
'''
pass
def dmpBarsToFile(self, folder:str, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
'''
将K线导出到指定的目录下的csv文件,文件名格式如SSE.600000_d.csv\n
@folder 要输出的文件夹\n
@codes 股票列表,格式如["SSE.600000","SZSE.000001"]\n
@start_date 开始日期,datetime类型,传None则自动设置为1990-01-01\n
@end_date 结束日期,datetime类型,传None则自动设置为当前日期\n
@period K线周期,支持day、min1、min5\n
'''
pass
def dmpAdjFactorsToDB(self, dbHelper:DBHelper, codes:list):
'''
将除权因子导出到数据库\n
@codes 股票列表,格式如["SSE.600000","SZSE.000001"]\n
@dbHelper 数据库辅助模块
'''
pass
def dmpBarsToDB(self, dbHelper:DBHelper, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
'''
将K线导出到数据库\n
@dbHelper 数据库辅助模块\n
@codes 股票列表,格式如["SSE.600000","SZSE.000001"]\n
@start_date 开始日期,datetime类型,传None则自动设置为1990-01-01\n
@end_date 结束日期,datetime类型,传None则自动设置为当前日期\n
@period K线周期,支持day、min1、min5\n
'''
pass
================================================
FILE: wtpy/apps/datahelper/DHFactory.py
================================================
from wtpy.apps.datahelper.DHDefs import BaseDataHelper
from wtpy.apps.datahelper.DHBaostock import DHBaostock
from wtpy.apps.datahelper.DHTushare import DHTushare
from wtpy.apps.datahelper.DHRqData import DHRqData
class DHFactory:
@staticmethod
def createHelper(name:str) -> BaseDataHelper:
'''
创建数据辅助模块\n
@name 模块名称,目前支持的有tushare、baostock、rqdata
'''
name = name.lower()
if name == "baostock":
return DHBaostock()
elif name == "tushare":
return DHTushare()
elif name == "rqdata":
return DHRqData()
else:
raise Exception("Cannot recognize helper with name %s" % (name))
================================================
FILE: wtpy/apps/datahelper/DHRqData.py
================================================
from wtpy.apps.datahelper.DHDefs import BaseDataHelper, DBHelper
import rqdatac as rq
from datetime import datetime, timedelta
import json
import os
def exchgStdToRQ(exchg:str) -> str:
if exchg == 'SSE':
return "XSHG"
elif exchg == 'SZSE':
return "XSHE"
else:
return exchg
def exchgRQToStd(exchg:str) -> str:
if exchg == 'XSHG':
return "SSE"
elif exchg == 'XSHE':
return "SZSE"
else:
return exchg
def stdCodeToRQ(stdCode:str):
stdCode = stdCode.upper()
items = stdCode.split(".")
exchg = exchgStdToRQ(items[0])
if len(items) == 2:
# 简单股票代码,格式如SSE.600000
return items[1] + "." + exchg
elif items[1] in ["IDX","ETF","STK","OPT"]:
# 标准股票代码,格式如SSE.IDX.000001
return items[2] + "." + exchg
elif len(items) == 3:
# 标准期货代码,格式如CFFEX.IF.2103
if items[2] != 'HOT':
return ''.join(items[1:])
else:
return items[1] + "88"
class DHRqData(BaseDataHelper):
def __init__(self):
BaseDataHelper.__init__(self)
print("Rqdata helper has been created.")
return
def auth(self, **kwargs):
if self.isAuthed:
return
rq.init(**kwargs)
self.isAuthed = True
print("Rqdata has been authorized.")
def dmpCodeListToFile(self, filename:str, hasIndex:bool=True, hasStock:bool=True):
stocks = {
"SSE":{},
"SZSE":{}
}
#个股列表
if hasStock:
print("Fetching stock list...")
df_stocks = rq.all_instruments(type='CS', market="cn")
for idx, row in df_stocks.iterrows():
rawcode = row["order_book_id"][:6]
exchg = row["exchange"]
if exchg == 'XSHG':
exchg = "SSE"
else:
exchg = "SZSE"
sInfo = dict()
sInfo["exchg"] = exchg
sInfo["code"] = rawcode
sInfo["name"] = row["symbol"]
sInfo["product"] = "STK"
stocks[sInfo["exchg"]][rawcode] = sInfo
if hasIndex:
#上证指数列表
print("Fetching index list...")
df_stocks = rq.all_instruments(type='INDX', market="cn")
for idx, row in df_stocks.iterrows():
rawcode = row["order_book_id"][:6]
exchg = row["exchange"]
if exchg == 'XSHG':
exchg = "SSE"
else:
exchg = "SZSE"
sInfo = dict()
sInfo["exchg"] = exchg
sInfo["code"] = rawcode
sInfo["name"] = row["symbol"]
sInfo["product"] = "IDX"
stocks[sInfo["exchg"]][rawcode] = sInfo
print("Writing code list into file %s..." % (filename))
f = open(filename, 'w')
f.write(json.dumps(stocks, sort_keys=True, indent=4, ensure_ascii=False))
f.close()
def dmpAdjFactorsToFile(self, codes:list, filename:str):
stocks = {
"SSE":{},
"SZSE":{}
}
count = 0
length = len(codes)
for stdCode in codes:
exchg = stdCode.split(".")[0]
code = stdCode[-6:]
count += 1
rq_code = code + "." + exchgStdToRQ(exchg)
stocks[exchg][code] = list()
print("Fetching adjust factors of %s(%d/%s)..." % (code, count, length))
df_factors = rq.get_ex_factor(order_book_ids=rq_code, start_date="1990-01-01")
for idx, row in df_factors.iterrows():
date = row['announcement_date'].to_pydatetime()
date = date + timedelta(days=1)
factor = float(row['ex_cum_factor'])
stocks[exchg][code].append({
"date": int(date.strftime("%Y%m%d")),
"factor": factor
})
print("Writing adjust factors into file %s..." % (filename))
f = open(filename, 'w+')
f.write(json.dumps(stocks, sort_keys=True, indent=4, ensure_ascii=False))
f.close()
def dmpBarsToFile(self, folder:str, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
if start_date is None:
start_date = datetime(year=1990, month=1, day=1)
if end_date is None:
end_date = datetime.now()
freq = ''
isDay = False
filetag = ''
if period == 'day':
freq = '1d'
isDay = True
filetag = 'd'
elif period == "min5":
freq = '5m'
filetag = 'm5'
elif period == "min1":
freq = '1m'
filetag = 'm1'
else:
raise Exception("Unrecognized period")
count = 0
length = len(codes)
for stdCode in codes:
count += 1
rq_code = stdCodeToRQ(stdCode)
print("Fetching %s bars of %s(%d/%s)..." % (period, stdCode, count, length))
df_bars = rq.get_price(order_book_ids = rq_code,start_date=start_date, end_date=end_date,frequency=freq,adjust_type='none',expect_df=True)
content = "date,time,open,high,low,close,volume,turnover,hold\n"
total_nums = len(df_bars)
cur_num = 0
for idx, row in df_bars.iterrows():
trade_date = row.name[1].to_pydatetime()
date = trade_date.strftime("%Y-%m-%d")
if isDay:
time = '0'
else:
time = trade_date.strftime("%H:%M:%S")
o = str(row["open"])
h = str(row["high"])
l = str(row["low"])
c = str(row["close"])
v = str(row["volume"])
t = str(row["total_turnover"])
items = [date, time, o, h, l, c, v, t]
if "open_interest" in row:
items.append(str(row["open_interest"]))
content += ",".join(items) + "\n"
cur_num += 1
if cur_num % 500 == 0:
print("Processing bars %d/%d..." % (cur_num, total_nums))
filename = "%s_%s.csv" % (stdCode, filetag)
filepath = os.path.join(folder, filename)
print("Writing bars into file %s..." % (filepath))
f = open(filepath, "w", encoding="utf-8")
f.write(content)
f.close()
def dmpAdjFactorsToDB(self, dbHelper:DBHelper, codes:list):
stocks = {
"SSE":{},
"SZSE":{}
}
count = 0
length = len(codes)
for stdCode in codes:
exchg = stdCode.split(".")[0]
code = stdCode[-6:]
count += 1
rq_code = code + "." + exchgStdToRQ(exchg)
stocks[exchg][code] = list()
print("Fetching adjust factors of %s(%d/%s)..." % (code, count, length))
df_factors = rq.get_ex_factor(order_book_ids=rq_code, start_date="1990-01-01")
for idx, row in df_factors.iterrows():
date = row['announcement_date'].to_pydatetime()
date = date + timedelta(days=1)
factor = float(row['ex_cum_factor'])
stocks[exchg][code].append({
"date": int(date.strftime("%Y%m%d")),
"factor": factor
})
print("Writing adjust factors into database...")
dbHelper.writeFactors(stocks)
def dmpBarsToDB(self, dbHelper:DBHelper, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
if start_date is None:
start_date = datetime(year=1990, month=1, day=1)
if end_date is None:
end_date = datetime.now()
freq = ''
isDay = False
if period == 'day':
freq = '1d'
isDay = True
elif period == "min5":
freq = '5m'
elif period == "min1":
freq = '1m'
else:
raise Exception("Unrecognized period")
count = 0
length = len(codes)
for stdCode in codes:
items = stdCode.split(".")
exchg = items[0]
code = stdCode[(len(exchg)+1):]
rq_code = stdCodeToRQ(stdCode)
count += 1
print("Fetching %s bars of %s(%d/%s)..." % (period, stdCode, count, length))
df_bars = rq.get_price(order_book_ids = rq_code,start_date=start_date, end_date=end_date,frequency=freq,adjust_type='none',expect_df=True)
bars = list()
total_nums = len(df_bars)
cur_num = 0
for idx, row in df_bars.iterrows():
trade_date = row.name[1].to_pydatetime()
date = int(trade_date.strftime("%Y%m%d"))
if isDay:
time = 0
else:
time = int(trade_date.strftime("%H%M"))
curBar = {
"exchange":exchg,
"code": code,
"date": date,
"time": time,
"open": row["open"],
"high": row["open"],
"low": row["open"],
"close": row["open"],
"volume": row["volume"],
"turnover": row["total_turnover"]
}
if "settlement" in row:
curBar["settle"] = row["settlement"]
if "open_interest" in row:
curBar["interest"] = row["open_interest"]
bars.append(curBar)
cur_num += 1
if cur_num % 500 == 0:
print("Processing bars %d/%d..." % (cur_num, total_nums))
print("Writing bars into database...")
dbHelper.writeBars(bars, period)
================================================
FILE: wtpy/apps/datahelper/DHTushare.py
================================================
from wtpy.apps.datahelper.DHDefs import BaseDataHelper, DBHelper
import tushare as ts
from datetime import datetime
import json
import os
def transCode(stdCode:str) -> str:
items = stdCode.split(".")
exchg = items[0]
if exchg == "SSE":
exchg = "SH"
elif exchg == "SZSE":
exchg = "SZ"
if exchg in ['SH','SZ']:
rawCode = ''
if len(items) > 2:
rawCode = items[2]
else:
rawCode = items[1]
else:
# 期货合约代码,格式为DCE.a.2018
rawCode = ''
if exchg == "CZCE":
rawCode = items[1] + items[2][1:]
else:
rawCode = ''.join(items[1:])
return rawCode.upper() + "." + exchg
class DHTushare(BaseDataHelper):
def __init__(self):
BaseDataHelper.__init__(self)
self.api = None
self.use_pro = True
print("Tushare helper has been created.")
return
def auth(self, **kwargs):
if self.isAuthed:
return
if "use_pro" in kwargs:
self.use_pro = kwargs["use_pro"]
kwargs.pop("use_pro")
self.api = ts.pro_api(**kwargs)
self.isAuthed = True
print("Tushare has been authorized, use_pro is %s." % ("enabled" if self.use_pro else "disabled"))
def dmpCodeListToFile(self, filename:str, hasIndex:bool=True, hasStock:bool=True):
stocks = {
"SSE":{},
"SZSE":{}
}
#个股列表
if hasStock:
print("Fetching stock list...")
df_stocks = self.api.stock_basic(exchange='', list_status='L', fields='ts_code,symbol,name,area,industry,list_date')
for idx, row in df_stocks.iterrows():
code = row["ts_code"]
rawcode = row["symbol"]
sInfo = dict()
pid = "STK"
if code[-2:] == "SH":
sInfo["exchg"] = "SSE"
else:
sInfo["exchg"] = "SZSE"
code = rawcode #code[-2:] + rawcode
sInfo["code"] = code
sInfo["name"] = row["name"]
sInfo["product"] = pid
stocks[sInfo["exchg"]][code] = sInfo
if hasIndex:
#上证指数列表
print("Fetching index list of SSE...")
df_stocks = self.api.index_basic(market='SSE')
for idx, row in df_stocks.iterrows():
code = row["ts_code"]
rawcode = code[:6]
if rawcode[0] != '0':
continue
sInfo = dict()
sInfo["exchg"] = "SSE"
code = rawcode #"SH" + rawcode
sInfo["code"] = code
sInfo["name"] = row["name"]
sInfo["product"] = "IDX"
stocks[sInfo["exchg"]][code] = sInfo
#深证指数列表
print("Fetching index list of SZSE...")
df_stocks = self.api.index_basic(market='SZSE')
for idx, row in df_stocks.iterrows():
code = row["ts_code"]
rawcode = code[:6]
if rawcode[:3] != '399':
continue
sInfo = dict()
sInfo["exchg"] = "SZSE"
code = rawcode #"SZ" + rawcode
sInfo["code"] = code
sInfo["name"] = row["name"]
sInfo["product"] = "IDX"
stocks[sInfo["exchg"]][code] = sInfo
print("Writing code list into file %s..." % (filename))
f = open(filename, 'w')
f.write(json.dumps(stocks, sort_keys=True, indent=4, ensure_ascii=False))
f.close()
def dmpAdjFactorsToFile(self, codes:list, filename:str):
stocks = {
"SSE":{},
"SZSE":{}
}
count = 0
length = len(codes)
for stdCode in codes:
ts_code = transCode(stdCode)
exchg = stdCode.split(".")[0]
code = stdCode[-6:]
count += 1
print("Fetching adjust factors of %s(%d/%s)..." % (stdCode, count, length))
stocks[exchg][code] = list()
df_factors = self.api.adj_factor(ts_code=ts_code)
items = list()
for idx, row in df_factors.iterrows():
date = row["trade_date"]
factor = row["adj_factor"]
items.append({
"date": int(date),
"factor": float(factor)
})
items.reverse()
pre_factor = 0
for item in items:
if item["factor"] != pre_factor:
stocks[exchg][code].append(item)
pre_factor = item["factor"]
print("Writing adjust factors into file %s..." % (filename))
f = open(filename, 'w+')
f.write(json.dumps(stocks, sort_keys=True, indent=4, ensure_ascii=False))
f.close()
def __dmp_bars_to_file_from_pro__(self, folder:str, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
if start_date is None:
start_date = datetime(year=1990, month=1, day=1)
if end_date is None:
end_date = datetime.now()
freq = ''
isDay = False
filetag = ''
if period == 'day':
freq = 'D'
isDay = True
filetag = 'd'
elif period == "min5":
freq = '5min'
filetag = 'm5'
elif period == "min1":
freq = '1min'
filetag = 'm1'
else:
raise Exception("Unrecognized period")
if isDay:
start_date = start_date.strftime("%Y%m%d")
end_date = end_date.strftime("%Y%m%d")
else:
start_date = start_date.strftime("%Y-%m-%d") + " 09:00:00"
end_date = end_date.strftime("%Y-%m-%d") + " 15:15:00"
count = 0
length = len(codes)
for stdCode in codes:
ts_code = transCode(stdCode)
exchg = stdCode.split(".")[0]
code = stdCode[-6:]
asset_type = "E"
if (exchg == 'SSE' and code[0] == '0') | (exchg == 'SZSE' and code[:3] == '399'):
asset_type = "I"
elif exchg not in ['SSE','SZSE']:
asset_type = "FT"
count += 1
print("Fetching %s bars of %s(%d/%s)..." % (period, code, count, length))
df_bars = ts.pro_bar(api=self.api, ts_code=ts_code, start_date=start_date, end_date=end_date, freq=freq, asset=asset_type)
df_bars = df_bars.iloc[::-1]
content = "date,time,open,high,low,close,volume,turnover\n"
for idx, row in df_bars.iterrows():
if isDay:
trade_date = row["trade_date"]
date = trade_date + ''
time = '0'
else:
trade_time = row["trade_time"]
date = trade_time.split(' ')[0]
time = trade_time.split(' ')[1]
o = str(row["open"])
h = str(row["high"])
l = str(row["low"])
c = str(row["close"])
v = str(row["vol"]*100)
t = str(row["amount"]*100)
items = [date, time, o, h, l, c, v, t]
content += ",".join(items) + "\n"
filename = "%s.%s_%s.csv" % (exchg, code, filetag)
filepath = os.path.join(folder, filename)
print("Writing bars into file %s..." % (filepath))
f = open(filepath, "w", encoding="utf-8")
f.write(content)
f.close()
def __dmp_bars_to_file_from_old__(self, folder:str, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
if start_date is None:
start_date = datetime(year=1990, month=1, day=1)
if end_date is None:
end_date = datetime.now()
freq = ''
isDay = False
filetag = ''
if period == 'day':
freq = 'D'
isDay = True
filetag = 'd'
elif period == "min5":
freq = '5'
filetag = 'm5'
else:
raise Exception("Unrecognized period")
start_date = start_date.strftime("%Y-%m-%d")
end_date = end_date.strftime("%Y-%m-%d")
count = 0
length = len(codes)
for stdCode in codes:
exchg = stdCode.split(".")[0]
code = stdCode[-6:]
count += 1
if (exchg == 'SSE' and code[0] == '0') | (exchg == 'SZSE' and code[:3] == '399'):
raise Exception("Old api only supports stocks")
print("Fetching %s bars of %s(%d/%s)..." % (period, code, count, length))
df_bars = ts.get_k_data(code, start=start_date, end=end_date, ktype=freq)
content = "date,time,open,high,low,close,volume\n"
for idx, row in df_bars.iterrows():
if isDay:
date = row["date"]
time = '0'
else:
trade_time = row["date"]
date = trade_time.split(' ')[0]
time = trade_time.split(' ')[1] + ":00"
o = str(row["open"])
h = str(row["high"])
l = str(row["low"])
c = str(row["close"])
v = str(row["volume"])
items = [date, time, o, h, l, c, v]
content += ",".join(items) + "\n"
filename = "%s.%s_%s.csv" % (exchg, code, filetag)
filepath = os.path.join(folder, filename)
print("Writing bars into file %s..." % (filepath))
f = open(filepath, "w", encoding="utf-8")
f.write(content)
f.close()
def dmpBarsToFile(self, folder:str, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
if self.use_pro:
self.__dmp_bars_to_file_from_pro__(folder=folder, codes=codes, start_date=start_date, end_date=end_date, period=period)
else:
self.__dmp_bars_to_file_from_old__(folder=folder, codes=codes, start_date=start_date, end_date=end_date, period=period)
def dmpAdjFactorsToDB(self, dbHelper:DBHelper, codes:list):
stocks = {
"SSE":{},
"SZSE":{}
}
count = 0
length = len(codes)
for stdCode in codes:
ts_code = transCode(stdCode)
exchg = stdCode.split(".")[0]
code = stdCode[-6:]
count += 1
print("Fetching adjust factors of %s(%d/%s)..." % (stdCode, count, length))
stocks[exchg][code] = list()
df_factors = self.api.adj_factor(ts_code=ts_code)
items = list()
for idx, row in df_factors.iterrows():
date = row["trade_date"]
factor = row["adj_factor"]
items.append({
"date": int(date),
"factor": factor
})
items.reverse()
pre_factor = 0
for item in items:
if item["factor"] != pre_factor:
stocks[exchg][code].append(item)
pre_factor = item["factor"]
print("Writing adjust factors into database...")
dbHelper.writeFactors(stocks)
def __dmp_bars_to_db_from_pro__(self, dbHelper:DBHelper, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
if start_date is None:
start_date = datetime(year=1990, month=1, day=1)
if end_date is None:
end_date = datetime.now()
freq = ''
isDay = False
filetag = ''
if period == 'day':
freq = 'D'
isDay = True
filetag = 'd'
elif period == "min5":
freq = '5min'
filetag = 'm5'
elif period == "min1":
freq = '1min'
filetag = 'm1'
else:
raise Exception("Unrecognized period")
if isDay:
start_date = start_date.strftime("%Y%m%d")
end_date = end_date.strftime("%Y%m%d")
else:
start_date = start_date.strftime("%Y-%m-%d") + " 09:00:00"
end_date = end_date.strftime("%Y-%m-%d") + " 15:15:00"
count = 0
length = len(codes)
for stdCode in codes:
ts_code = transCode(stdCode)
exchg = stdCode.split(".")[0]
code = stdCode[-6:]
asset_type = "E"
if (exchg == 'SSE' and code[0] == '0') | (exchg == 'SZSE' and code[:3] == '399'):
asset_type = "I"
elif exchg not in ['SSE','SZSE']:
asset_type = "FT"
count += 1
print("Fetching %s bars of %s(%d/%s)..." % (period, code, count, length))
df_bars = ts.pro_bar(api=self.api, ts_code=ts_code, start_date=start_date, end_date=end_date, freq=freq, asset=asset_type)
bars = []
for idx, row in df_bars.iterrows():
if isDay:
trade_date = row["trade_date"]
bars.append({
"exchange":exchg,
"code": code,
"date": int(trade_date),
"time": 0,
"open": row["open"],
"high": row["high"],
"low": row["low"],
"close": row["close"],
"volume": row["vol"]*100,
"turnover": row["amount"]*100
})
else:
trade_time = row["trade_time"]
date = int(trade_time.split(' ')[0].replace("-",""))
time = int(trade_time.split(' ')[1].replace(":","")[:4])
bars.append({
"exchange":exchg,
"code":code,
"date": date,
"time": time,
"open": row["open"],
"high": row["high"],
"low": row["low"],
"close": row["close"],
"volume": row["vol"]*100,
"turnover": row["amount"]*100
})
print("Writing bars into database...")
dbHelper.writeBars(bars, period)
def __dmp_bars_to_db_from_old__(self, dbHelper:DBHelper, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
if start_date is None:
start_date = datetime(year=1990, month=1, day=1)
if end_date is None:
end_date = datetime.now()
freq = ''
isDay = False
if period == 'day':
freq = 'D'
isDay = True
elif period == "min5":
freq = '5'
else:
raise Exception("Unrecognized period")
start_date = start_date.strftime("%Y-%m-%d")
end_date = end_date.strftime("%Y-%m-%d")
count = 0
length = len(codes)
for stdCode in codes:
exchg = stdCode.split(".")[0]
code = stdCode[-6:]
if (exchg == 'SSE' and code[0] == '0') | (exchg == 'SZSE' and code[:3] == '399'):
raise Exception("Old api only supports stocks")
count += 1
print("Fetching %s bars of %s(%d/%s)..." % (period, code, count, length))
df_bars = ts.get_k_data(code, start=start_date, end=end_date, ktype=freq)
bars = []
for idx, row in df_bars.iterrows():
if isDay:
trade_date = row["date"]
bars.append({
"exchange":exchg,
"code": code,
"date": int(trade_date.replace('-','')),
"time": 0,
"open": row["open"],
"high": row["high"],
"low": row["low"],
"close": row["close"],
"volume": row["volume"]
})
else:
trade_time = row["date"]
date = int(trade_time.split(' ')[0].replace("-",""))
time = int(trade_time.split(' ')[1].replace(":",""))
bars.append({
"exchange":exchg,
"code":code,
"date": date,
"time": time,
"open": row["open"],
"high": row["high"],
"low": row["low"],
"close": row["close"],
"volume": row["volume"]
})
print("Writing bars into database...")
dbHelper.writeBars(bars, period)
def dmpBarsToDB(self, dbHelper:DBHelper, codes:list, start_date:datetime=None, end_date:datetime=None, period:str="day"):
if self.use_pro:
self.__dmp_bars_to_db_from_pro__(dbHelper=dbHelper, codes=codes, start_date=start_date, end_date=end_date, period=period)
else:
self.__dmp_bars_to_db_from_old__(dbHelper=dbHelper, codes=codes, start_date=start_date, end_date=end_date, period=period)
================================================
FILE: wtpy/apps/datahelper/__init__.py
================================================
from .DHFactory import DHFactory
from .db.MysqlHelper import MysqlHelper
__all__ = ["DHFactory","MysqlHelper"]
================================================
FILE: wtpy/apps/datahelper/db/MysqlHelper.py
================================================
from wtpy.apps.datahelper.DHDefs import DBHelper
import pymysql
import math
import os
class MysqlHelper(DBHelper):
def __init__(self, host:str, user:str, pwd:str, dbname:str, port:int=3306):
self.params = {
"host":host,
'user':user,
'password':pwd,
'database':dbname,
'port':port
}
self.conn:pymysql.Connection = None
def __get_conn__(self):
if self.conn is None:
self.conn = pymysql.connect(**self.params)
try:
self.conn.ping()
except:
self.conn = pymysql.connect(**self.params)
return self.conn
def initDB(self):
paths = os.path.split(__file__)
a = (paths[:-1] + ('initdb_mysql.sql',))
_path = os.path.join(*a)
f = open(_path, "r", encoding="UTF-8")
content = f.read()
f.close()
conn = self.__get_conn__()
cursor = conn.cursor()
items = content.split(";")
for item in items:
item = item.strip()
if len(item) == 0:
continue
cursor.execute(item+";")
conn.commit()
cursor.close()
def writeBars(self, bars:list, period="day"):
count = 0
sql = ""
isDay = (period=='day')
tbname = "tb_kline_%s" % (period)
for curBar in bars:
if count == 0:
if isDay:
sql = "REPLACE INTO %s(exchange,`code`,`date`,open,high,low,close,settle,volume,turnover,interest,diff_interest) VALUES" % (tbname)
else:
sql = "REPLACE INTO %s(exchange,`code`,`date`,`time`,open,high,low,close,volume,turnover,interest,diff_interest) VALUES" % (tbname)
if isDay:
subsql = "('%s','%s',%d,%f,%f,%f,%f," % (curBar["exchange"], curBar["code"], curBar["date"], curBar["open"], curBar["high"], curBar["low"], curBar["close"])
if "settle" in curBar:
subsql += str(curBar["settle"]) + ","
else:
subsql += "0,"
if "volume" in curBar:
subsql += str(curBar["volume"]) + ","
else:
subsql += "0,"
if "turnover" in curBar:
subsql += str(curBar["turnover"]) + ","
else:
subsql += "0,"
if "interest" in curBar:
subsql += str(curBar["interest"]) + ","
else:
subsql += "0,"
if "diff_interest" in curBar:
subsql += str(curBar["diff_interest"]) + ","
else:
subsql += "0,"
subsql = subsql[:-1] + "),"
sql += subsql
else:
barTime = (curBar["date"] - 19900000)*10000 + curBar["time"]
subsql = "('%s','%s',%d,%d,%f,%f,%f,%f," % (curBar["exchange"], curBar["code"], curBar["date"], barTime, curBar["open"], curBar["high"], curBar["low"], curBar["close"])
if "volume" in curBar:
subsql += str(curBar["volume"]) + ","
else:
subsql += "0,"
if "turnover" in curBar:
subsql += str(curBar["turnover"]) + ","
else:
subsql += "0,"
if "interest" in curBar:
subsql += str(curBar["interest"]) + ","
else:
subsql += "0,"
if "diff_interest" in curBar:
subsql += str(curBar["diff_interest"]) + ","
else:
subsql += "0,"
subsql = subsql[:-1] + "),"
sql += subsql
count += 1
if count == 500:
count = 0
sql = sql[:-1] + ";"
conn = self.__get_conn__()
cursor = conn.cursor()
cursor.execute(sql)
conn.commit()
cursor.close()
# 循环完了,再做一次提交
if count > 0:
sql = sql[:-1] + ";"
conn = self.__get_conn__()
cursor = conn.cursor()
cursor.execute(sql)
conn.commit()
cursor.close()
def writeFactors(self, factors:dict):
for exchg in factors:
codelist = factors[exchg]
for code in codelist:
items = codelist[code]
sql = 'REPLACE INTO tb_adj_factors(exchange,`code`,`date`,factor) VALUES'
for item in items:
sql += "('%s','%s',%d,%f)," % (exchg, code, item["date"], item["factor"])
sql = sql[:-1] + ";"
conn = self.__get_conn__()
cursor = conn.cursor()
cursor.execute(sql)
conn.commit()
cursor.close()
================================================
FILE: wtpy/apps/datahelper/db/__init__.py
================================================
================================================
FILE: wtpy/apps/datahelper/db/initdb_mysql.sql
================================================
CREATE TABLE IF NOT EXISTS `tb_adj_factors` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`exchange` varchar(20) NOT NULL DEFAULT '',
`code` varchar(32) NOT NULL DEFAULT '',
`date` int(10) unsigned NOT NULL DEFAULT 0,
`factor` double(22,12) NOT NULL DEFAULT 0.000000000000,
`createtime` datetime NOT NULL DEFAULT current_timestamp(),
`updatetime` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `exchange_code_date` (`exchange`,`code`,`date`),
KEY `exchange_code` (`exchange`,`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='除权因子表';
CREATE TABLE IF NOT EXISTS `tb_kline_day` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`exchange` varchar(20) NOT NULL DEFAULT '',
`code` varchar(30) NOT NULL DEFAULT '',
`date` int(10) unsigned NOT NULL DEFAULT 0,
`open` double(22,4) NOT NULL DEFAULT 0.0000,
`high` double(22,4) NOT NULL DEFAULT 0.0000,
`low` double(22,4) NOT NULL DEFAULT 0.0000,
`close` double(22,4) NOT NULL DEFAULT 0.0000,
`settle` double(22,4) NOT NULL DEFAULT 0.0000,
`volume` double(22,6) unsigned NOT NULL DEFAULT 0.000000,
`turnover` double(22,4) NOT NULL DEFAULT 0.0000,
`interest` bigint(20) unsigned NOT NULL DEFAULT 0,
`diff_interest` bigint(20) NOT NULL DEFAULT 0,
`createtime` datetime NOT NULL DEFAULT current_timestamp(),
`updatetime` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `exchange_code_date` (`exchange`,`code`,`date`),
KEY `exchange_code` (`exchange`,`code`),
KEY `date` (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='日线表';
CREATE TABLE IF NOT EXISTS `tb_kline_min1` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`exchange` varchar(20) NOT NULL DEFAULT '',
`code` varchar(30) NOT NULL DEFAULT '',
`date` int(10) unsigned NOT NULL DEFAULT 0,
`time` int(10) unsigned NOT NULL DEFAULT 0,
`open` double(22,4) NOT NULL DEFAULT 0.0000,
`high` double(22,4) NOT NULL DEFAULT 0.0000,
`low` double(22,4) NOT NULL DEFAULT 0.0000,
`close` double(22,4) NOT NULL DEFAULT 0.0000,
`volume` double(22,6) unsigned NOT NULL DEFAULT 0.000000,
`turnover` double(22,4) NOT NULL DEFAULT 0.0000,
`interest` bigint(20) unsigned NOT NULL DEFAULT 0,
`diff_interest` bigint(20) NOT NULL DEFAULT 0,
`createtime` datetime NOT NULL DEFAULT current_timestamp(),
`updatetime` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `exchange_code_date_time` (`exchange`,`code`,`date`,`time`),
KEY `exchange_code` (`exchange`,`code`),
KEY `time` (`time`),
KEY `date` (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='1分钟线表';
CREATE TABLE IF NOT EXISTS `tb_kline_min5` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`exchange` varchar(20) NOT NULL DEFAULT '',
`code` varchar(30) NOT NULL DEFAULT '',
`date` int(10) unsigned NOT NULL DEFAULT 0,
`time` int(10) unsigned NOT NULL DEFAULT 0,
`open` double(22,4) NOT NULL DEFAULT 0.0000,
`high` double(22,4) NOT NULL DEFAULT 0.0000,
`low` double(22,4) NOT NULL DEFAULT 0.0000,
`close` double(22,4) NOT NULL DEFAULT 0.0000,
`volume` double(22,6) unsigned NOT NULL DEFAULT 0.000000,
`turnover` double(22,4) NOT NULL DEFAULT 0.0000,
`interest` bigint(20) unsigned NOT NULL DEFAULT 0,
`diff_interest` bigint(20) NOT NULL DEFAULT 0,
`createtime` datetime NOT NULL DEFAULT current_timestamp(),
`updatetime` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `exchange_code_date_time` (`exchange`,`code`,`date`,`time`) USING BTREE,
KEY `exchange_code` (`exchange`,`code`),
KEY `date` (`date`),
KEY `time` (`time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='5分钟线表';
================================================
FILE: wtpy/monitor/DataMgr.py
================================================
import json
import os
import sqlite3
import hashlib
import datetime
from .WtLogger import WtLogger
def backup_file(filename):
if not os.path.exists(filename):
return
items = filename.split(".")
ext = items[-1]
prefix = ".".join(items[:-1])
now = datetime.datetime.now()
timetag = now.strftime("%Y%m%d_%H%M%S")
target = prefix + "_" + timetag + "." + ext
import shutil
shutil.copy(filename, target)
class DataMgr:
def __init__(self, datafile:str="mondata.db", logger:WtLogger=None):
self.__grp_cache__ = dict()
self.__logger__ = logger
self.__db_conn__ = sqlite3.connect(datafile, check_same_thread=False)
self.__check_db__()
#加载组合列表
cur = self.__db_conn__.cursor()
self.__config__ = {
"groups":{},
"users":{}
}
for row in cur.execute("SELECT * FROM groups;"):
grpInfo = dict()
grpInfo["id"] = row[1]
grpInfo["name"] = row[2]
grpInfo["path"] = row[3]
grpInfo["info"] = row[4]
grpInfo["gtype"] = row[5]
grpInfo["datmod"] = row[6]
grpInfo["env"] = row[7]
grpInfo["mqurl"] = row[8]
self.__config__["groups"][grpInfo["id"]] = grpInfo
for row in cur.execute("SELECT * FROM users;"):
usrInfo = dict()
usrInfo["loginid"] = row[1]
usrInfo["name"] = row[2]
usrInfo["role"] = row[3]
usrInfo["passwd"] = row[4]
usrInfo["iplist"] = row[5]
usrInfo["remark"] = row[6]
usrInfo["createby"] = row[7]
usrInfo["createtime"] = row[8]
usrInfo["modifyby"] = row[9]
usrInfo["modifytime"] = row[10]
self.__config__["users"][usrInfo["loginid"]] = usrInfo
def get_db(self):
return self.__db_conn__
def __check_db__(self):
if self.__db_conn__ is None:
return
cur = self.__db_conn__.cursor()
tables = []
for row in cur.execute("select name from sqlite_master where type='table' order by name"):
tables.append(row[0])
if "actions" not in tables:
sql = "CREATE TABLE [actions] (\n"
sql += "[id] INTEGER PRIMARY KEY autoincrement, \n"
sql += "[loginid] VARCHAR(20) NOT NULL DEFAULT '', \n"
sql += "[actiontime] DATETIME default (datetime('now', 'localtime')), \n"
sql += "[actionip] VARCHAR(30) NOT NULL DEFAULT '', \n"
sql += "[actiontype] VARCHAR(20) NOT NULL DEFAULT '',\n"
sql += "[remark] TEXT default '');"
cur.execute(sql)
cur.execute("CREATE INDEX [idx_actions_loginid] ON [actions] ([loginid]);")
cur.execute("CREATE INDEX [idx_actions_actiontime] ON [actions] ([actiontime]);")
self.__db_conn__.commit()
if "groups" not in tables:
sql = "CREATE TABLE [groups] (\n"
sql += "[id] INTEGER PRIMARY KEY autoincrement,\n"
sql += "[groupid] VARCHAR(20) NOT NULL DEFAULT '',\n"
sql += "[name] VARCHAR(30) NOT NULL DEFAULT '',\n"
sql += "[path] VARCHAR(256) NOT NULL DEFAULT '',\n"
sql += "[info] TEXT DEFAULT '',\n"
sql += "[gtype] VARCHAR(10) NOT NULL DEFAULT 'cta',\n"
sql += "[datmod] VARCHAR(10) NOT NULL DEFAULT 'mannual',\n"
sql += "[env] VARCHAR(20) NOT NULL DEFAULT 'product',\n"
sql += "[mqurl] VARCHAR(255) NOT NULL DEFAULT '',\n"
sql += "[createtime] DATETIME default (datetime('now', 'localtime')),\n"
sql += "[modifytime] DATETIME default (datetime('now', 'localtime')));"
cur.execute(sql)
cur.execute("CREATE UNIQUE INDEX [idx_groupid] ON [groups] ([groupid]);")
self.__db_conn__.commit()
if "schedules" not in tables:
sql = "CREATE TABLE [schedules] (\n"
sql += "[id] INTEGER PRIMARY KEY autoincrement,\n"
sql += "[appid] VARCHAR(20) NOT NULL DEFAULT '',\n"
sql += "[path] VARCHAR(256) NOT NULL DEFAULT '',\n"
sql += "[folder] VARCHAR(256) NOT NULL DEFAULT '',\n"
sql += "[param] VARCHAR(50) NOT NULL DEFAULT '',\n"
sql += "[type] INTEGER DEFAULT 0,\n"
sql += "[span] INTEGER DEFAULT 3,\n"
sql += "[guard] VARCHAR(20) DEFAULT 'false',\n"
sql += "[redirect] VARCHAR(20) DEFAULT 'false',\n"
sql += "[schedule] VARCHAR(20) DEFAULT 'false',\n"
sql += "[weekflag] VARCHAR(20) DEFAULT '000000',\n"
sql += "[mqurl] VARCHAR(255) NOT NULL DEFAULT '',\n"
sql += "[task1] VARCHAR(100) NOT NULL DEFAULT '{\"active\": true,\"time\": 0,\"action\": 0}',\n"
sql += "[task2] VARCHAR(100) NOT NULL DEFAULT '{\"active\": true,\"time\": 0,\"action\": 0}',\n"
sql += "[task3] VARCHAR(100) NOT NULL DEFAULT '{\"active\": true,\"time\": 0,\"action\": 0}',\n"
sql += "[task4] VARCHAR(100) NOT NULL DEFAULT '{\"active\": true,\"time\": 0,\"action\": 0}',\n"
sql += "[task5] VARCHAR(100) NOT NULL DEFAULT '{\"active\": true,\"time\": 0,\"action\": 0}',\n"
sql += "[task6] VARCHAR(100) NOT NULL DEFAULT '{\"active\": true,\"time\": 0,\"action\": 0}',\n"
sql += "[createtime] DATETIME default (datetime('now', 'localtime')),\n"
sql += "[modifytime] DATETIME default (datetime('now', 'localtime')));"
cur.execute(sql)
cur.execute("CREATE UNIQUE INDEX [idx_appid] ON [schedules] ([appid]);")
self.__db_conn__.commit()
if "users" not in tables:
sql = "CREATE TABLE [users] (\n"
sql += "[id] INTEGER PRIMARY KEY autoincrement,\n"
sql += "[loginid] VARCHAR(20) NOT NULL DEFAULT '',\n"
sql += "[name] VARCHAR(30) NOT NULL DEFAULT '',\n"
sql += "[role] VARCHAR(10) NOT NULL DEFAULT '',\n"
sql += "[passwd] VARCHAR(30) NOT NULL DEFAULT 'cta',\n"
sql += "[iplist] VARCHAR(100) NOT NULL DEFAULT 'mannual',\n"
sql += "[remark] VARCHAR(256) NOT NULL DEFAULT '',\n"
sql += "[createby] VARCHAR(20) NOT NULL DEFAULT '',\n"
sql += "[createtime] DATETIME default (datetime('now', 'localtime')),\n"
sql += "[modifyby] VARCHAR(20) NOT NULL DEFAULT '',\n"
sql += "[modifytime] DATETIME default (datetime('now', 'localtime')));"
cur.execute(sql)
cur.execute("CREATE UNIQUE INDEX [idx_loginid] ON [users] ([loginid]);")
self.__db_conn__.commit()
def __check_cache__(self, grpid, grpInfo):
now = datetime.datetime.now()
if grpid not in self.__grp_cache__:
self.__grp_cache__[grpid] = dict()
self.__grp_cache__[grpid]["cachetime"] = None
else:
cache_time = self.__grp_cache__[grpid]["cachetime"]
bNeedReset = False
if cache_time is None:
bNeedReset = True
else:
td = now - cache_time
if td.total_seconds() >= 60:# 上次缓存时间超过60s,则重新读取
bNeedReset = True
if bNeedReset:
self.__grp_cache__[grpid] = dict()
self.__grp_cache__[grpid]["cachetime"] = None
if "strategies" not in self.__grp_cache__[grpid]:
filepath = "./generated/marker.json"
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
else:
try:
f = open(filepath, "r")
content = f.read()
marker = json.loads(content)
f.close()
self.__grp_cache__[grpid] = {
"strategies":marker["marks"],
"channels":marker["channels"]
}
if "executers" in marker:
self.__grp_cache__[grpid]["executers"] = marker["executers"]
else:
self.__grp_cache__[grpid]["executers"] = []
except:
self.__grp_cache__[grpid] = {
"strategies":[],
"channels":[],
"executers":[]
}
self.__grp_cache__[grpid]["strategies"].sort()
self.__grp_cache__[grpid]["channels"].sort()
self.__grp_cache__[grpid]["executers"].sort()
self.__grp_cache__[grpid]["cachetime"] = now
def get_groups(self, tpfilter:str=''):
ret = []
for grpid in self.__config__["groups"]:
grpinfo = self.__config__["groups"][grpid]
if tpfilter == '':
ret.append(grpinfo)
elif grpinfo["gtype"] == tpfilter:
ret.append(grpinfo)
return ret
def has_group(self, grpid:str):
return (grpid in self.__config__["groups"])
def get_group(self, grpid:str):
if grpid in self.__config__["groups"]:
return self.__config__["groups"][grpid]
else:
return None
def get_group_cfg(self, grpid:str):
if grpid not in self.__config__["groups"]:
return "{}"
else:
grpInfo = self.__config__["groups"][grpid]
filepath = "./config.json"
filepath = os.path.join(grpInfo["path"], filepath)
f = open(filepath, "r")
content = f.read()
f.close()
return json.loads(content)
def set_group_cfg(self, grpid:str, config:dict):
if grpid not in self.__config__["groups"]:
return False
else:
grpInfo = self.__config__["groups"][grpid]
filepath = "./config.json"
filepath = os.path.join(grpInfo["path"], filepath)
backup_file(filepath)
f = open(filepath, "w")
f.write(json.dumps(config, indent=4))
f.close()
return True
def get_group_entry(self, grpid:str):
if grpid not in self.__config__["groups"]:
return "{}"
else:
grpInfo = self.__config__["groups"][grpid]
filepath = "./run.py"
filepath = os.path.join(grpInfo["path"], filepath)
f = open(filepath, "r", encoding="utf-8")
content = f.read()
f.close()
return content
def set_group_entry(self, grpid:str, content:str):
if grpid not in self.__config__["groups"]:
return False
else:
grpInfo = self.__config__["groups"][grpid]
filepath = "./run.py"
filepath = os.path.join(grpInfo["path"], filepath)
backup_file(filepath)
f = open(filepath, "w", encoding="utf-8")
f.write(content)
f.close()
return True
def add_group(self, grpInfo:dict):
grpid = grpInfo["id"]
isNewGrp = not (grpid in self.__config__["groups"])
bSucc = False
try:
cur = self.__db_conn__.cursor()
sql = ''
if isNewGrp:
sql = "INSERT INTO groups(groupid,name,path,info,gtype,datmod,env,mqurl) VALUES('%s','%s','%s','%s','%s','%s','%s','%s');" \
% (grpid, grpInfo["name"], grpInfo["path"], grpInfo["info"], grpInfo["gtype"], grpInfo["datmod"], grpInfo["env"], grpInfo["mqurl"])
else:
sql = "UPDATE groups SET name='%s',path='%s',info='%s',gtype='%s',datmod='%s',env='%s',mqurl='%s',modifytime=datetime('now','localtime') WHERE groupid='%s';" \
% (grpInfo["name"], grpInfo["path"], grpInfo["info"], grpInfo["gtype"], grpInfo["datmod"], grpInfo["env"], grpInfo["mqurl"], grpid)
cur.execute(sql)
self.__db_conn__.commit()
bSucc = True
except sqlite3.Error as e:
print(e)
if bSucc:
self.__config__["groups"][grpid] = grpInfo
return bSucc
def del_group(self, grpid:str):
if grpid in self.__config__["groups"]:
self.__config__["groups"].pop(grpid)
cur = self.__db_conn__.cursor()
cur.execute("DELETE FROM groups WHERE groupid='%s';" % (grpid))
self.__db_conn__.commit()
def get_users(self):
ret = []
for loginid in self.__config__["users"]:
usrInfo = self.__config__["users"][loginid]
ret.append(usrInfo.copy())
return ret
def add_user(self, usrInfo, admin):
loginid = usrInfo["loginid"]
isNewUser = not (loginid in self.__config__["users"])
cur = self.__db_conn__.cursor()
now = datetime.datetime.now()
if isNewUser:
encpwd = hashlib.md5((loginid+usrInfo["passwd"]).encode("utf-8")).hexdigest()
usrInfo["passwd"] = encpwd
usrInfo["createby"] = admin
usrInfo["modifyby"] = admin
usrInfo["createtime"] = now.strftime("%Y-%m-%d %H:%M:%S")
usrInfo["modifytime"] = now.strftime("%Y-%m-%d %H:%M:%S")
cur.execute("INSERT INTO users(loginid,name,role,passwd,iplist,remark,createby,modifyby) VALUES(?,?,?,?,?,?,?,?);",
(loginid, usrInfo["name"], usrInfo["role"], encpwd, usrInfo["iplist"], usrInfo["remark"], admin, admin))
else:
usrInfo["modifyby"] = admin
usrInfo["modifytime"] = now.strftime("%Y-%m-%d %H:%M:%S")
cur.execute("UPDATE users SET name=?,role=?,iplist=?,remark=?,modifyby=?,modifytime=datetime('now','localtime') WHERE loginid=?;",
(usrInfo["name"], usrInfo["role"], usrInfo["iplist"], usrInfo["remark"], admin, loginid))
self.__db_conn__.commit()
self.__config__["users"][loginid] = usrInfo
def mod_user_pwd(self, loginid:str, newpwd:str, admin:str):
cur = self.__db_conn__.cursor()
cur.execute("UPDATE users SET passwd=?,modifyby=?,modifytime=datetime('now','localtime') WHERE loginid=?;",
(newpwd,admin,loginid))
self.__db_conn__.commit()
self.__config__["users"][loginid]["passwd"]=newpwd
def del_user(self, loginid, admin):
if loginid in self.__config__["users"]:
self.__config__["users"].pop(loginid)
cur = self.__db_conn__.cursor()
cur.execute("DELETE FROM users WHERE loginid='%s';" % (loginid))
self.__db_conn__.commit()
return True
else:
return False
def log_action(self, adminInfo, atype, remark):
cur = self.__db_conn__.cursor()
sql = "INSERT INTO actions(loginid,actiontime,actionip,actiontype,remark) VALUES('%s',datetime('now','localtime'),'%s','%s','%s');" % (
adminInfo["loginid"], adminInfo["loginip"], atype, remark)
cur.execute(sql)
self.__db_conn__.commit()
def get_user(self, loginid:str):
if loginid in self.__config__["users"]:
return self.__config__["users"][loginid].copy()
elif loginid == 'superman':
return {
"loginid":loginid,
"name":"超管",
"role":"superman",
"passwd":"25ed305a56504e95fd1ca9900a1da174",
"iplist":"",
"remark":"内置超管账号",
'builtin':True
}
else:
return None
def get_strategies(self, grpid:str):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
return self.__grp_cache__[grpid]["strategies"]
def get_channels(self, grpid:str):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
return self.__grp_cache__[grpid]["channels"]
def get_trades(self, grpid:str, straid:str, limit:int = 200):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
if straid not in self.__grp_cache__[grpid]["strategies"]:
return []
if "trades" not in self.__grp_cache__[grpid]:
self.__grp_cache__[grpid]["trades"] = dict()
if straid not in self.__grp_cache__[grpid]["trades"]:
filepath = "./generated/outputs/%s/trades.csv" % (straid)
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
else:
trdCache = dict()
trdCache["file"] = filepath
trdCache["lastrow"] = 0
trdCache["trades"] = list()
self.__grp_cache__[grpid]["trades"][straid] = trdCache
trdCache = self.__grp_cache__[grpid]["trades"][straid]
f = open(trdCache["file"], "r")
last_row = trdCache["lastrow"]
lines = f.readlines()
f.close()
lines = lines[1+last_row:]
for line in lines:
cells = line.split(",")
if len(cells) > 10:
continue
tItem = {
"strategy":straid,
"code": cells[0],
"time": int(cells[1]),
"direction": cells[2],
"offset": cells[3],
"price": float(cells[4]),
"volume": float(cells[5]),
"tag": cells[6],
"fee": 0
}
if len(cells) > 7:
tItem["fee"] = float(cells[7])
trdCache["trades"].append(tItem)
trdCache["lastrow"] += 1
return trdCache["trades"][-limit:]
def get_funds(self, grpid:str, straid:str):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
if straid not in self.__grp_cache__[grpid]["strategies"]:
return []
if "funds" not in self.__grp_cache__[grpid]:
self.__grp_cache__[grpid]["funds"] = dict()
if straid not in self.__grp_cache__[grpid]["funds"]:
filepath = "./generated/outputs/%s/funds.csv" % (straid)
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
else:
trdCache = dict()
trdCache["file"] = filepath
trdCache["lastrow"] = 0
trdCache["funds"] = list()
self.__grp_cache__[grpid]["funds"][straid] = trdCache
trdCache = self.__grp_cache__[grpid]["funds"][straid]
f = open(trdCache["file"], "r")
last_row = trdCache["lastrow"]
lines = f.readlines()
f.close()
lines = lines[1+last_row:]
for line in lines:
cells = line.split(",")
if len(cells) > 10:
continue
tItem = {
"strategy":straid,
"date": int(cells[0]),
"closeprofit": float(cells[1]),
"dynprofit": float(cells[2]),
"dynbalance": float(cells[3]),
"fee": 0
}
if len(cells) > 4:
tItem["fee"] = float(cells[4])
trdCache["funds"].append(tItem)
trdCache["lastrow"] += 1
ret = trdCache["funds"].copy()
if len(ret) > 0:
last_date = ret[-1]["date"]
else:
last_date = 0
# 这里再更新一条实时数据
filepath = "./generated/stradata/%s.json" % (straid)
filepath = os.path.join(grpInfo["path"], filepath)
f = open(filepath, "r")
try:
content = f.read()
json_data = json.loads(content)
fund = json_data["fund"]
if fund["tdate"] > last_date:
ret.append({
"strategy":straid,
"date": fund["tdate"],
"closeprofit": fund["total_profit"],
"dynprofit": fund["total_dynprofit"],
"dynbalance": fund["total_profit"] + fund["total_dynprofit"] - fund["total_fees"],
"fee": fund["total_fees"]
})
except:
pass
f.close()
return ret
def get_signals(self, grpid:str, straid:str, limit:int = 200):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
if straid not in self.__grp_cache__[grpid]["strategies"]:
return []
if "signals" not in self.__grp_cache__[grpid]:
self.__grp_cache__[grpid]["signals"] = dict()
if straid not in self.__grp_cache__[grpid]["signals"]:
filepath = "./generated/outputs/%s/signals.csv" % (straid)
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
else:
trdCache = dict()
trdCache["file"] = filepath
trdCache["lastrow"] = 0
trdCache["signals"] = list()
self.__grp_cache__[grpid]["signals"][straid] = trdCache
trdCache = self.__grp_cache__[grpid]["signals"][straid]
f = open(trdCache["file"], "r")
last_row = trdCache["lastrow"]
lines = f.readlines()
f.close()
lines = lines[1+last_row:]
for line in lines:
cells = line.split(",")
tItem = {
"strategy":straid,
"code": cells[0],
"target": float(cells[1]),
"sigprice": float(cells[2]),
"gentime": cells[3],
"tag": cells[4]
}
trdCache["signals"].append(tItem)
trdCache["lastrow"] += len(lines)
return trdCache["signals"][-limit:]
def get_rounds(self, grpid:str, straid:str, limit:int = 200):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
if straid not in self.__grp_cache__[grpid]["strategies"]:
return []
if "rounds" not in self.__grp_cache__[grpid]:
self.__grp_cache__[grpid]["rounds"] = dict()
if straid not in self.__grp_cache__[grpid]["rounds"]:
filepath = "./generated/outputs/%s/closes.csv" % (straid)
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
else:
trdCache = dict()
trdCache["file"] = filepath
trdCache["lastrow"] = 0
trdCache["rounds"] = list()
self.__grp_cache__[grpid]["rounds"][straid] = trdCache
trdCache = self.__grp_cache__[grpid]["rounds"][straid]
f = open(trdCache["file"], "r")
last_row = trdCache["lastrow"]
lines = f.readlines()
f.close()
lines = lines[1+last_row:]
for line in lines:
cells = line.split(",")
tItem = {
"strategy":straid,
"code": cells[0],
"direct": cells[1],
"opentime": int(cells[2]),
"openprice": float(cells[3]),
"closetime": int(cells[4]),
"closeprice": float(cells[5]),
"qty": float(cells[6]),
"profit": float(cells[7]),
"entertag": cells[9],
"exittag": cells[10]
}
trdCache["rounds"].append(tItem)
trdCache["lastrow"] += len(lines)
return trdCache["rounds"][-limit:]
def get_positions(self, grpid:str, straid:str):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
ret = list()
if straid != "all":
if straid not in self.__grp_cache__[grpid]["strategies"]:
return []
filepath = "./generated/stradata/%s.json" % (straid)
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
f = open(filepath, "r")
try:
content = f.read()
json_data = json.loads(content)
positions = json_data["positions"]
for pItem in positions:
tag = "volumn" if "volume" not in pItem else "volume"
if pItem[tag] == 0.0:
continue
for dItem in pItem["details"]:
dItem["code"] = pItem["code"]
dItem["strategy"] = straid
if "volumn" in dItem:
dItem["volume"] = dItem["volumn"]
dItem.pop("volumn")
ret.append(dItem)
except:
pass
f.close()
else:
for straid in self.__grp_cache__[grpid]["strategies"]:
filepath = "./generated/stradata/%s.json" % (straid)
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
f = open(filepath, "r")
try:
content = f.read()
json_data = json.loads(content)
positions = json_data["positions"]
for pItem in positions:
tag = "volumn" if "volume" not in pItem else "volume"
if pItem[tag] == 0.0:
continue
for dItem in pItem["details"]:
dItem["code"] = pItem["code"]
dItem["strategy"] = straid
if "volumn" in dItem:
dItem["volume"] = dItem["volumn"]
dItem.pop("volumn")
ret.append(dItem)
except:
pass
f.close()
return ret
def get_channel_orders(self, grpid:str, chnlid:str, limit:int = 200):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
if chnlid not in self.__grp_cache__[grpid]["channels"]:
return []
if "corders" not in self.__grp_cache__[grpid]:
self.__grp_cache__[grpid]["corders"] = dict()
if chnlid not in self.__grp_cache__[grpid]["corders"]:
filepath = "./generated/traders/%s/orders.csv" % (chnlid)
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
else:
trdCache = dict()
trdCache["file"] = filepath
trdCache["lastrow"] = 0
trdCache["corders"] = list()
self.__grp_cache__[grpid]["corders"][chnlid] = trdCache
trdCache = self.__grp_cache__[grpid]["corders"][chnlid]
f = open(trdCache["file"], "r",encoding="gb2312",errors="ignore")
last_row = trdCache["lastrow"]
lines = f.readlines()
f.close()
lines = lines[1+last_row:]
for line in lines:
cells = line.split(",")
tItem = {
"channel":chnlid,
"localid":int(cells[0]),
"time":int(cells[2]),
"code": cells[3],
"action": cells[4],
"total": float(cells[5]),
"traded": float(cells[6]),
"price": float(cells[7]),
"orderid": cells[8],
"canceled": cells[9],
"remark": cells[10]
}
trdCache["corders"].append(tItem)
return trdCache["corders"][-limit:]
def get_channel_trades(self, grpid:str, chnlid:str, limit:int = 200):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
if chnlid not in self.__grp_cache__[grpid]["channels"]:
return []
if "ctrades" not in self.__grp_cache__[grpid]:
self.__grp_cache__[grpid]["ctrades"] = dict()
if chnlid not in self.__grp_cache__[grpid]["ctrades"]:
filepath = "./generated/traders/%s/trades.csv" % (chnlid)
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
else:
trdCache = dict()
trdCache["file"] = filepath
trdCache["lastrow"] = 0
trdCache["ctrades"] = list()
self.__grp_cache__[grpid]["ctrades"][chnlid] = trdCache
trdCache = self.__grp_cache__[grpid]["ctrades"][chnlid]
f = open(trdCache["file"], "r",encoding="gb2312")
last_row = trdCache["lastrow"]
lines = f.readlines()
f.close()
lines = lines[1+last_row:]
for line in lines:
cells = line.split(",")
tItem = {
"channel":chnlid,
"localid":int(cells[0]),
"time":int(cells[2]),
"code": cells[3],
"action": cells[4],
"volume": float(cells[5]),
"price": float(cells[6]),
"tradeid": cells[7],
"orderid": cells[8]
}
trdCache["ctrades"].append(tItem)
return trdCache["ctrades"][-limit:]
def get_channel_positions(self, grpid:str, chnlid:str):
if self.__config__ is None:
return []
if "groups" not in self.__config__:
return []
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
ret = list()
channels = list()
if chnlid != 'all':
channels.append(chnlid)
else:
channels = self.__grp_cache__[grpid]["channels"]
for cid in channels:
if cid not in self.__grp_cache__[grpid]["channels"]:
continue
filepath = "./generated/traders/%s/rtdata.json" % (cid)
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
f = open(filepath, "r")
try:
content = f.read()
json_data = json.loads(content)
positions = json_data["positions"]
for pItem in positions:
pItem["channel"] = cid
ret.append(pItem)
except:
pass
f.close()
return ret
def get_channel_funds(self, grpid:str, chnlid:str):
if self.__config__ is None:
return []
if "groups" not in self.__config__:
return []
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
ret = dict()
channels = list()
if chnlid != 'all':
channels.append(chnlid)
else:
channels = self.__grp_cache__[grpid]["channels"]
print(channels)
for cid in channels:
if cid not in self.__grp_cache__[grpid]["channels"]:
continue
filepath = "./generated/traders/%s/rtdata.json" % (cid)
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
continue
f = open(filepath, "r")
try:
content = f.read()
json_data = json.loads(content)
funds = json_data["funds"]
ret[cid] = funds
except:
pass
f.close()
print(ret)
return ret
def get_actions(self, sdate, edate):
ret = list()
cur = self.__db_conn__.cursor()
for row in cur.execute("SELECT id,loginid,actiontime,actionip,actiontype,remark FROM actions WHERE actiontime>=? and actiontime<=?;", (sdate, edate)):
aInfo = dict()
aInfo["id"] = row[0]
aInfo["loginid"] = row[1]
aInfo["actiontime"] = row[2]
aInfo["actionip"] = row[3]
aInfo["action"] = row[4]
aInfo["remark"] = row[5]
ret.append(aInfo)
return ret
def get_group_trades(self, grpid:str):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
if "grptrades" not in self.__grp_cache__[grpid]:
self.__grp_cache__[grpid]["grptrades"] = dict()
filepath = "./generated/portfolio/trades.csv"
filepath = os.path.join(grpInfo["path"], filepath)
print(filepath)
if not os.path.exists(filepath):
return []
else:
trdCache = dict()
trdCache["file"] = filepath
trdCache["lastrow"] = 0
trdCache["trades"] = list()
self.__grp_cache__[grpid]["grptrades"]["cache"] = trdCache
trdCache = self.__grp_cache__[grpid]["grptrades"]['cache']
f = open(trdCache["file"], "r")
last_row = trdCache["lastrow"]
lines = f.readlines()
f.close()
lines = lines[1+last_row:]
print(lines)
for line in lines:
cells = line.split(",")
tItem = {
"code": cells[0],
"time": int(cells[1]),
"direction": cells[2],
"offset": cells[3],
"price": float(cells[4]),
"volume": float(cells[5]),
"fee": float(cells[6])
}
trdCache["trades"].append(tItem)
trdCache["lastrow"] += 1
return trdCache["trades"]
def get_group_rounds(self, grpid:str):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
if "grprounds" not in self.__grp_cache__[grpid]:
self.__grp_cache__[grpid]["grprounds"] = dict()
filepath = "./generated/portfolio/closes.csv"
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
else:
trdCache = dict()
trdCache["file"] = filepath
trdCache["lastrow"] = 0
trdCache["rounds"] = list()
self.__grp_cache__[grpid]["grprounds"]["cache"] = trdCache
trdCache = self.__grp_cache__[grpid]["grprounds"]['cache']
f = open(trdCache["file"], "r")
last_row = trdCache["lastrow"]
lines = f.readlines()
f.close()
lines = lines[1+last_row:]
print(lines)
for line in lines:
cells = line.split(",")
tItem = {
"code": cells[0],
"direct": cells[1],
"opentime": int(cells[2]),
"openprice": float(cells[3]),
"closetime": int(cells[4]),
"closeprice": float(cells[5]),
"qty": float(cells[6]),
"profit": float(cells[7])
}
trdCache["rounds"].append(tItem)
trdCache["lastrow"] += 1
return trdCache["rounds"]
def get_group_funds(self, grpid:str):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
if "grpfunds" not in self.__grp_cache__[grpid]:
self.__grp_cache__[grpid]["grpfunds"] = dict()
filepath = "./generated/portfolio/funds.csv"
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
else:
trdCache = dict()
trdCache["file"] = filepath
trdCache["lastrow"] = 0
trdCache["funds"] = list()
self.__grp_cache__[grpid]["grpfunds"]["cache"] = trdCache
trdCache = self.__grp_cache__[grpid]["grpfunds"]['cache']
f = open(trdCache["file"], "r")
last_row = trdCache["lastrow"]
lines = f.readlines()
f.close()
lines = lines[1+last_row:]
for line in lines:
cells = line.split(",")
tItem = {
"date": int(cells[0]),
"predynbalance": float(cells[1]),
"prebalance": float(cells[2]),
"balance": float(cells[3]),
"closeprofit": float(cells[4]),
"dynprofit": float(cells[5]),
"fee": float(cells[6]),
"maxdynbalance": float(cells[7]),
"maxtime": float(cells[8]),
"mindynbalance": float(cells[9]),
"mintime": float(cells[10]),
"mdmaxbalance": float(cells[11]),
"mdmaxdate": float(cells[12]),
"mdminbalance": float(cells[13]),
"mdmindate": float(cells[14])
}
trdCache["funds"].append(tItem)
trdCache["lastrow"] += 1
ret = trdCache["funds"].copy()
if len(ret) > 0:
last_date = ret[-1]["date"]
else:
last_date = 0
# 这里再更新一条实时数据
filepath = "./generated/portfolio/datas.json"
filepath = os.path.join(grpInfo["path"], filepath)
f = open(filepath, "r")
try:
content = f.read()
json_data = json.loads(content)
fund = json_data["fund"]
if ["date"] > last_date:
ret.append({
"date": fund["date"],
"predynbalance": fund["predynbal"],
"prebalance": fund["prebalance"],
"balance": fund["balance"],
"closeprofit": fund["profit"],
"dynprofit": fund["dynprofit"],
"fee": fund["fees"],
"maxdynbalance": fund["max_dyn_bal"],
"maxtime": fund["max_time"],
"mindynbalance": fund["min_dyn_bal"],
"mintime": fund["min_time"],
"mdmaxbalance": fund["maxmd"]["dyn_balance"],
"mdmaxdate": fund["maxmd"]["date"],
"mdminbalance": fund["minmd"]["dyn_balance"],
"mdmindate": fund["minmd"]["date"]
})
except:
pass
f.close()
return ret
def get_group_positions(self, grpid:str):
if grpid not in self.__config__["groups"]:
return []
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
filepath = "./generated/portfolio/datas.json"
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return []
else:
ret = list()
f = open(filepath, "r")
try:
content = f.read()
json_data = json.loads(content)
positions = json_data["positions"]
for pItem in positions:
if pItem["volume"] == 0:
continue
for dItem in pItem["details"]:
dItem["code"] = pItem["code"]
ret.append(dItem)
except:
pass
f.close()
return ret
def get_group_performances(self, grpid:str):
if grpid not in self.__config__["groups"]:
return {}
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
filepath = "./generated/portfolio/datas.json"
filepath = os.path.join(grpInfo["path"], filepath)
if not os.path.exists(filepath):
return {}
else:
perf = dict()
f = open(filepath, "r")
try:
content = f.read()
json_data = json.loads(content)
positions = json_data["positions"]
for pItem in positions:
code = pItem['code']
ay = code.split(".")
pid = code
if len(ay) > 2:
if ay[1] not in ['IDX','STK','ETF']:
pid = ay[0] + "." + ay[1]
else:
pid = ay[0] + "." + ay[2]
if pid not in perf:
perf[pid] = {
'closeprofit':0,
'dynprofit':0
}
perf[pid]['closeprofit'] += pItem['closeprofit']
perf[pid]['dynprofit'] += pItem['dynprofit']
except:
pass
f.close()
return perf
def get_group_filters(self, grpid:str):
if grpid not in self.__config__["groups"]:
return {}
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
filepath = os.path.join(grpInfo["path"], 'filters.json')
if not os.path.exists(filepath):
filters = {}
else:
filters = {}
f = open(filepath, "r")
try:
content = f.read()
filters = json.loads(content)
except:
pass
f.close()
gpCache = self.__grp_cache__[grpid]
if "executer_filters" not in filters:
filters["executer_filters"] = dict()
if "strategy_filters" not in filters:
filters["strategy_filters"] = dict()
if "code_filters" not in filters:
filters["code_filters"] = dict()
for sid in gpCache["strategies"]:
if sid not in filters['strategy_filters']:
filters['strategy_filters'][sid] = False
for eid in gpCache["executers"]:
if eid not in filters['executer_filters']:
filters['executer_filters'][eid] = False
for id in filters['strategy_filters'].keys():
if type(filters['strategy_filters'][id]) != bool:
filters['strategy_filters'][id] = True
for id in filters['code_filters'].keys():
if type(filters['code_filters'][id]) != bool:
filters['code_filters'][id] = True
return filters
def set_group_filters(self, grpid:str, filters:dict):
if grpid not in self.__config__["groups"]:
return False
grpInfo = self.__config__["groups"][grpid]
self.__check_cache__(grpid, grpInfo)
realfilters = {
"strategy_filters":{},
"code_filters":{},
"executer_filters":{}
}
if "strategy_filters" in filters:
for sid in filters["strategy_filters"]:
if filters["strategy_filters"][sid]:
realfilters["strategy_filters"][sid] = {
"action":"redirect",
"target":0
}
if "code_filters" in filters:
for sid in filters["code_filters"]:
if filters["code_filters"][sid]:
realfilters["code_filters"][sid] = {
"action":"redirect",
"target":0
}
if "executer_filters" in filters:
realfilters["executer_filters"] = filters["executer_filters"]
filepath = os.path.join(grpInfo["path"], 'filters.json')
backup_file(filepath)
f = open(filepath, "w")
f.write(json.dumps(realfilters, indent=4))
f.close()
return True
================================================
FILE: wtpy/monitor/EventReceiver.py
================================================
import threading
import struct
import json
import chardet
from wtpy import WtMsgQue, WtMQClient
mq = WtMsgQue()
TOPIC_RT_TRADE = "TRD_TRADE" # 生产环境下的成交通知
TOPIC_RT_ORDER = "TRD_ORDER" # 生产环境下的订单通知
TOPIC_RT_NOTIFY = "TRD_NOTIFY" # 生产环境下的普通通知
TOPIC_RT_LOG = "LOG" # 生产环境下的日志通知
class EventSink:
def __init__(self):
pass
def on_order(self, chnl:str, ordInfo:dict):
pass
def on_trade(self, chnl:str, trdInfo:dict):
pass
def on_notify(self, chnl:str, message:str):
pass
def on_log(self, tag:str, time:int, message:str):
pass
def decode_bytes(data:bytes):
ret = chardet.detect(data)
if ret is not None:
encoding = ret["encoding"]
if encoding is not None:
return data.decode(encoding)
else:
return data.decode()
else:
return data.decode()
class EventReceiver(WtMQClient):
def __init__(self, url:str, topics:list = [], sink:EventSink = None, logger = None):
self.url = url
self.logger = logger
mq.add_mq_client(url, self)
for topic in topics:
self.subscribe(topic)
self._stopped = False
self._worker = None
self._sink = sink
def on_mq_message(self, topic:str, message:str, dataLen:int):
topic = decode_bytes(topic)
message = decode_bytes(message[:dataLen])
if self._sink is not None:
if topic == TOPIC_RT_TRADE:
msgObj = json.loads(message)
trader = msgObj["trader"]
msgObj.pop("trader")
self._sink.on_trade(trader, msgObj)
elif topic == TOPIC_RT_ORDER:
msgObj = json.loads(message)
trader = msgObj["trader"]
msgObj.pop("trader")
self._sink.on_order(trader, msgObj)
elif topic == TOPIC_RT_NOTIFY:
trader = msgObj["trader"]
self._sink.on_notify(trader, msgObj["message"])
elif topic == TOPIC_RT_LOG:
msgObj = json.loads(message)
self._sink.on_log(msgObj["tag"], msgObj["time"], msgObj["message"])
def run(self):
self.start()
def release(self):
mq.destroy_mq_client(self)
TOPIC_BT_EVENT = "BT_EVENT" # 回测环境下的事件,主要通知回测的启动和结束
TOPIC_BT_STATE = "BT_STATE" # 回测的状态
TOPIC_BT_FUND = "BT_FUND" # 每日资金变化
class BtEventSink:
def __init__(self):
pass
def on_begin(self):
pass
def on_finish(self):
pass
def on_fund(self, fundInfo:dict):
pass
def on_state(self, statInfo:float):
pass
class BtEventReceiver(WtMQClient):
def __init__(self, url:str, topics:list = [], sink:BtEventSink = None, logger = None):
self.url = url
self.logger = logger
mq.add_mq_client(url, self)
for topic in topics:
self.subscribe(topic)
self._stopped = False
self._worker = None
self._sink = sink
def on_mq_message(self, topic:str, message:str, dataLen:int):
topic = decode_bytes(topic)
message = decode_bytes(message[:dataLen])
if self._sink is not None:
if topic == TOPIC_BT_EVENT:
if message == 'BT_START':
self._sink.on_begin()
else:
self._sink.on_finish()
elif topic == TOPIC_BT_STATE:
msgObj = json.loads(message)
self._sink.on_state(msgObj)
elif topic == TOPIC_BT_FUND:
msgObj = json.loads(message)
self._sink.on_fund(msgObj)
def run(self):
self.start()
def release(self):
mq.destroy_mq_client(self)
================================================
FILE: wtpy/monitor/PushSvr.py
================================================
from flask_socketio import SocketIO, emit
from flask import session, sessions
from .WtLogger import WtLogger
def get_param(json_data, key:str, type=str, defVal = ""):
if key not in json_data:
return defVal
else:
return type(json_data[key])
class PushServer:
def __init__(self, app, dataMgr, logger:WtLogger = None):
sockio:SocketIO = SocketIO(app)
self.sockio = sockio
self.app = app
self.dataMgr = dataMgr
self.logger = logger
@sockio.on('connect', namespace='/')
def on_connect():
usrInfo = session.get("userinfo")
if usrInfo is not None:
self.logger.info("%s connected" % usrInfo["loginid"])
@sockio.on('disconnect', namespace='/')
def on_disconnect():
usrInfo = session.get("userinfo")
if usrInfo is not None:
self.logger.info("%s disconnected" % usrInfo["loginid"])
@sockio.on('setgroup', namespace='/')
def set_group(data):
groupid = get_param(data, "groupid")
if len(groupid) == 0:
emit('setgroup', {"result":-2, "message":"组合ID不能为空"})
else:
session["groupid"] = groupid
def run(self, port:int, host:str):
self.sockio.run(self.app, host, port)
def notifyGrpLog(self, groupid, tag:str, time:int, message):
self.sockio.emit("notify", {"type":"gplog", "groupid":groupid, "tag":tag, "time":time, "message":message}, broadcast=True)
def notifyGrpEvt(self, groupid, evttype):
self.sockio.emit("notify", {"type":"gpevt", "groupid":groupid, "evttype":evttype}, broadcast=True)
def notifyGrpChnlEvt(self, groupid, chnlid, evttype, data):
self.sockio.emit("notify", {"type":"chnlevt", "groupid":groupid, "channel":chnlid, "data":data, "evttype":evttype}, broadcast=True)
================================================
FILE: wtpy/monitor/WatchDog.py
================================================
import threading
import time
import subprocess
import os
import datetime
import json
import copy
import platform
import psutil
from .EventReceiver import EventReceiver, EventSink
from .WtLogger import WtLogger
from enum import Enum
def isWindows():
if "windows" in platform.system().lower():
return True
return False
class WatcherSink:
def __init__(self):
pass
def on_start(self, appid:str):
pass
def on_stop(self, appid:str):
pass
def on_output(self, appid:str, tag:str, time:int, message:str):
pass
def on_order(self, appid:str, chnl:str, ordInfo:dict):
pass
def on_trade(self, appid:str, chnl:str, trdInfo:dict):
pass
def on_notify(self, appid:str, chnl:str, message:str):
pass
class ActionType(Enum):
'''
操作类型
枚举变量
'''
AT_START = 0
AT_STOP = 1
AT_RESTART = 2
class AppState(Enum):
'''
app状态
枚举变量
'''
AS_NotExist = 901
AS_NotRunning = 902
AS_Running = 903
AS_Closed = 904
class AppInfo(EventSink):
def __init__(self, appConf:dict, sink:WatcherSink = None, logger:WtLogger=None):
self.__info__ = appConf
self._cmd_line = None
self.__logger__ = logger
self._lock = threading.Lock()
self._id = appConf["id"]
self._check_span = appConf["span"]
self._guard = appConf["guard"]
self._redirect = appConf["redirect"]
self._mq_url = appConf["mqurl"].strip()
self._schedule = appConf["schedule"]["active"]
self._weekflag = appConf["schedule"]["weekflag"]
self._ticks = 0
self._state = AppState.AS_NotRunning
self._procid = None
self._sink = sink
self._evt_receiver = None
if not os.path.exists(appConf["folder"]) or not os.path.exists(appConf["path"]):
self._state == AppState.AS_NotExist
def applyConf(self, appConf:dict):
self._lock.acquire()
self.__info__ = appConf
self._check_span = appConf["span"]
self._guard = appConf["guard"]
old_mqurl = self._mq_url
self._mq_url = appConf["mqurl"]
self._redirect = appConf["redirect"]
self._schedule = appConf["schedule"]["active"]
self._weekflag = appConf["schedule"]["weekflag"]
self._ticks = 0
self._lock.release()
self.__logger__.info("应用%s的调度设置已更新" % (self._id))
if self._mq_url != old_mqurl:
if self._evt_receiver is not None:
self._evt_receiver.release()
if self._mq_url != '':
self._evt_receiver = EventReceiver(url=self._mq_url, logger=self.__logger__)
self._evt_receiver.run()
self.__logger__.info("应用%s开始接收%s的通知信息" % (self._id, self._mq_url))
def getConf(self):
self._lock.acquire()
ret = copy.copy(self.__info__)
self._lock.release()
return ret
@property
def cmd_line(self) -> str:
fullPath = os.path.join(self.__info__["folder"], self.__info__["param"])
if self._cmd_line is None:
self._cmd_line = self.__info__["path"] + " " + fullPath
return self._cmd_line
def is_running(self, pids) -> bool:
bNeedCheck = (self._procid is None) or (not psutil.pid_exists(self._procid))
if bNeedCheck:
for pid in pids:
try:
pInfo = psutil.Process(pid)
cmdLine = pInfo.cmdline()
if len(cmdLine) == 0:
continue
# print(cmdLine)
cmdLine = ' '.join(cmdLine)
if self.cmd_line.upper() == cmdLine.upper():
self._procid = pid
self.__logger__.info("应用%s挂载成功,进程ID: %d" % (self._id, self._procid))
if self._mq_url != '':
# 如果事件接收器为空或者url发生了改变,则需要重新创建
bNeedCreate = self._evt_receiver is None or self._evt_receiver.url != self._mq_url
if bNeedCreate:
if self._evt_receiver is not None:
self._evt_receiver.release()
self._evt_receiver = EventReceiver(url=self._mq_url, logger=self.__logger__, sink=self)
self._evt_receiver.run()
self.__logger__.info("应用%s开始接收%s的通知信息" % (self._id, self._mq_url))
except:
pass
return False
return True
def run(self):
if self._state == AppState.AS_Running:
return
if self._mq_url != '':
# 每次启动都重新创建接收器
if self._evt_receiver is not None:
self._evt_receiver.release()
self._evt_receiver = EventReceiver(url=self._mq_url, logger=self.__logger__, sink=self)
self._evt_receiver.run()
self.__logger__.info("应用%s开始接收%s的通知信息" % (self._id, self._mq_url))
try:
fullPath = os.path.join(self.__info__["folder"], self.__info__["param"])
if isWindows():
self._procid = subprocess.Popen([self.__info__["path"], fullPath], # 需要执行的文件路径
cwd=self.__info__["folder"], creationflags=subprocess.CREATE_NEW_CONSOLE).pid
else:
self._procid = subprocess.Popen([self.__info__["path"], fullPath], # 需要执行的文件路径
cwd=self.__info__["folder"]).pid
self._cmd_line = self.__info__["path"] + " " + fullPath
except:
self.__logger__.info("应用%s启动异常" % (self._id))
self._state = AppState.AS_Running
self.__logger__.info("应用%s的已启动,进程ID: %d" % (self._id, self._procid))
if self._sink is not None:
self._sink.on_start(self._id)
def stop(self):
if self._state != AppState.AS_Running:
return
try:
if isWindows():
os.system("taskkill /pid " + str(self._procid))
else:
os.system("kill -9 " + str(self._procid))
except e as SystemError:
self.__logger__.error("关闭异常: {}" % (e))
pass
self.__logger__.info("应用%s的已停止,进程ID: %d" % (self._id, self._procid))
if self._sink is not None:
self._sink.on_stop(self._id)
self._procid = None
self._state = AppState.AS_Closed
def restart(self):
if self._procid is not None:
self.stop()
self.run()
def update_state(self, pids):
if self.is_running(pids):
self._state = AppState.AS_Running
elif self._state == AppState.AS_Running:
self._state = AppState.AS_NotRunning
self.__logger__.info("应用%s的已停止" % (self._id))
self._procid = None
if self._sink is not None:
self._sink.on_stop(self._id)
def tick(self, pids):
self._ticks += 1
if self._ticks == self._check_span:
self.update_state(pids)
if self._state == AppState.AS_NotRunning and self._guard:
self.__logger__.info("应用%s未启动,正在自动重启" % (self._id))
self.run()
elif self._schedule:
self.__schedule__()
self._ticks = 0
def __schedule__(self):
weekflag = self._weekflag
now = datetime.datetime.now()
# python中周一是0,周天是6
# 但是web端沿用了C++里的规则,周日是0,周六是6,所以做一个变换
wd = now.weekday() + 1
if wd == 7:
wd = 0
if weekflag[wd] != "1":
return
appid = self.__info__["id"]
curMin = int(now.strftime("%H%M"))
curDt = int(now.strftime("%y%m%d"))
self._lock.acquire()
for tInfo in self.__info__["schedule"]["tasks"]:
if not tInfo["active"]:
continue
if "lastDate" in tInfo:
lastDate = tInfo["lastDate"]
else:
lastDate = 0
if "lastTime" in tInfo:
lastTime = tInfo["lastTime"]
else:
lastTime = 0
targetTm = tInfo["time"]
action = tInfo["action"]
if curMin == targetTm and (curMin != lastTime or curDt != lastDate):
if action == ActionType.AT_START.value:
if self._state not in [AppState.AS_NotExist, AppState.AS_Running]:
self.__logger__.info("自动启动应用%s" % (appid))
self.run()
elif action == ActionType.AT_STOP.value:
if self._state == AppState.AS_Running:
self.__logger__.info("自动停止应用%s" % (appid))
self.stop()
elif action == ActionType.AT_RESTART.value:
self.__logger__.info("自动重启应用%s" % (appid))
self.restart()
tInfo["lastDate"] = curDt
tInfo["lastTime"] = curMin
self._lock.release()
def isRunning(self):
return self._state == AppState.AS_Running
# EventSink.on_order
def on_order(self, chnl:str, ordInfo:dict):
if self._sink is not None:
self._sink.on_order(self._id, chnl, ordInfo)
# EventSink.on_trade
def on_trade(self, chnl:str, trdInfo:dict):
if self._sink is not None:
self._sink.on_trade(self._id, chnl, trdInfo)
# EventSink.on_notify
def on_notify(self, chnl:str, message:str):
if self._sink is not None:
self._sink.on_notify(self._id, chnl, message)
# EventSink.on_log
def on_log(self, tag:str, time:int, message:str):
if self._sink is not None:
self._sink.on_output(self._id, tag, time, message)
pass
class WatchDog:
def __init__(self, db, sink:WatcherSink = None, logger:WtLogger=None):
self.__db_conn__ = db
self.__apps__ = dict()
self.__app_conf__ = dict()
self.__stopped__ = False
self.__worker__ = None
self.__sink__ = sink
self.__logger__ = logger
#加载调度列表
cur = self.__db_conn__.cursor()
for row in cur.execute("SELECT * FROM schedules;"):
appConf = dict()
appConf["id"] = row[1]
appConf["path"] = row[2]
appConf["folder"] = row[3]
appConf["param"] = row[4]
appConf["type"] = row[5]
appConf["span"] = row[6]
appConf["guard"] = row[7]=='true'
appConf["redirect"] = row[8]=='true'
appConf["mqurl"] = row[11]
appConf["schedule"] = dict()
appConf["schedule"]["active"] = row[9]=='true'
appConf["schedule"]["weekflag"] = row[10]
appConf["schedule"]["tasks"] = list()
appConf["schedule"]["tasks"].append(json.loads(row[12]))
appConf["schedule"]["tasks"].append(json.loads(row[13]))
appConf["schedule"]["tasks"].append(json.loads(row[14]))
appConf["schedule"]["tasks"].append(json.loads(row[15]))
appConf["schedule"]["tasks"].append(json.loads(row[16]))
appConf["schedule"]["tasks"].append(json.loads(row[17]))
self.__app_conf__[appConf["id"]] = appConf
self.__apps__[appConf["id"]] = AppInfo(appConf, sink, self.__logger__)
def __watch_impl__(self):
while not self.__stopped__:
time.sleep(1)
pids = psutil.pids()
for appid in self.__apps__:
appInfo = self.__apps__[appid]
appInfo.tick(pids)
def get_apps(self):
ret = {}
for appid in self.__app_conf__:
bRunning = self.__apps__[appid].isRunning()
conf = copy.copy(self.__app_conf__[appid])
conf["running"] = bRunning
ret[appid] = conf
return ret
def run(self):
if self.__worker__ is None:
self.__worker__ = threading.Thread(target=self.__watch_impl__, name="WatchDog", daemon=True)
self.__worker__.start()
self.__logger__.info("自动调度服务已启动")
def start(self, appid:str):
if appid not in self.__apps__:
return
self.__logger__.info("手动启动%s" % (appid))
appInfo = self.__apps__[appid]
appInfo.run()
def stop(self, appid:str):
if appid not in self.__apps__:
return
self.__logger__.info("手动停止%s" % (appid))
appInfo = self.__apps__[appid]
appInfo.stop()
def has_app(self, appid:str):
return appid in self.__apps__
def restart(self, appid:str):
if appid not in self.__apps__:
return
appInfo = self.__apps__[appid]
appInfo.restart()
def isRunning(self, appid:str):
if appid not in self.__apps__:
return False
appInfo = self.__apps__[appid]
return appInfo.isRunning()
def getAppConf(self, appid:str):
if appid not in self.__apps__:
return None
appInfo = self.__apps__[appid]
return appInfo.getConf()
def delApp(self, appid:str):
if appid not in self.__apps__:
return
self.__apps__.pop(appid)
cur = self.__db_conn__.cursor()
cur.execute("DELETE FROM schedules WHERE appid='%s';" % (appid))
self.__db_conn__.commit()
self.__logger__.info("应用%s自动调度已删除" % (appid))
def updateMQURL(self, appid:str, mqurl:str):
if appid not in self.__apps__:
return
self.__app_conf__[appid]["mqurl"] = mqurl
appConf = self.__app_conf__[appid]
appInst = self.__apps__[appid]
appInst.applyConf(appConf)
cur = self.__db_conn__.cursor()
sql = "UPDATE schedules SET mqurl='%s',modifytime=datetime('now','localtime') WHERE appid='%s';" % (mqurl, appid)
print(sql)
cur.execute(sql)
self.__db_conn__.commit()
def applyAppConf(self, appConf:dict, isGroup:bool = False):
appid = appConf["id"]
self.__app_conf__[appid] = appConf
isNewApp = False
if appid not in self.__apps__:
isNewApp = True
self.__apps__[appid] = AppInfo(appConf, self.__sink__, self.__logger__)
else:
appInst = self.__apps__[appid]
appInst.applyConf(appConf)
guard = 'true' if appConf["guard"] else 'false'
redirect = 'true' if appConf["redirect"] else 'false'
schedule = 'true' if appConf["schedule"] else 'false'
stype = 1 if isGroup else 0
cur = self.__db_conn__.cursor()
sql = ''
if isNewApp:
sql = "INSERT INTO schedules(appid,path,folder,param,type,span,guard,redirect,schedule,weekflag,task1,task2,task3,task4,task5,task6,mqurl) \
VALUES('%s','%s','%s','%s',%d, %d,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');" % (
appid, appConf["path"], appConf["folder"], appConf["param"], stype, appConf["span"], guard, redirect, schedule, appConf["schedule"]["weekflag"],
json.dumps(appConf["schedule"]["tasks"][0]),json.dumps(appConf["schedule"]["tasks"][1]),json.dumps(appConf["schedule"]["tasks"][2]),
json.dumps(appConf["schedule"]["tasks"][3]),json.dumps(appConf["schedule"]["tasks"][4]),json.dumps(appConf["schedule"]["tasks"][5]),
appConf["mqurl"])
else:
sql = "UPDATE schedules SET path='%s',folder='%s',param='%s',type=%d,span='%s',guard='%s',redirect='%s',schedule='%s',weekflag='%s',task1='%s',task2='%s',\
task3='%s',task4='%s',task5='%s',task6='%s',mqurl='%s',modifytime=datetime('now','localtime') WHERE appid='%s';" % (
appConf["path"], appConf["folder"], appConf["param"], stype, appConf["span"], guard, redirect, schedule, appConf["schedule"]["weekflag"],
json.dumps(appConf["schedule"]["tasks"][0]),json.dumps(appConf["schedule"]["tasks"][1]),json.dumps(appConf["schedule"]["tasks"][2]),
json.dumps(appConf["schedule"]["tasks"][3]),json.dumps(appConf["schedule"]["tasks"][4]),json.dumps(appConf["schedule"]["tasks"][5]),
appConf["mqurl"], appid)
cur.execute(sql)
self.__db_conn__.commit()
================================================
FILE: wtpy/monitor/WtBtMon.py
================================================
'''
Descripttion: 回测管理模块
version:
Author: Wesley
Date: 2021-08-11 14:03:33
LastEditors: Wesley
LastEditTime: 2021-09-02 14:18:50
'''
import os
import json
import subprocess
import platform
import sys
import psutil
import hashlib
import datetime
import shutil
import json
import threading
import time
from wtpy import WtDtServo
from .WtLogger import WtLogger
from .EventReceiver import BtEventReceiver, BtEventSink
def isWindows():
if "windows" in platform.system().lower():
return True
return False
def md5_str(v:str) -> str:
return hashlib.md5(v.encode()).hexdigest()
def gen_btid(user:str, straid:str) -> str:
now = datetime.datetime.now()
s = user + "_" + straid + "_" + str(now.timestamp())
return md5_str(s)
def gen_straid(user:str) -> str:
now = datetime.datetime.now()
s = user + "_" + str(now.timestamp())
return md5_str(s)
class BtTaskSink:
def __init__(self):
pass
def on_start(self, user:str, straid:str, btid:str):
pass
def on_stop(self, user:str, straid:str, btid:str):
pass
def on_state(self, user:str, straid:str, btid:str, statInfo:dict):
pass
def on_fund(self, user:str, straid:str, btid:str, fundInfo:dict):
pass
class WtBtTask(BtEventSink):
'''
回测任务类
'''
def __init__(self, user:str, straid:str, btid:str, folder:str, logger:WtLogger = None, sink:BtTaskSink = None):
self.user = user
self.straid = straid
self.btid = btid
self.logger = logger
self.folder = folder
self.sink = sink
self._cmd_line = None
self._mq_url = "ipc:///wtpy/bt_%s.ipc" % (btid)
self._ticks = 0
self._state = 0
self._procid = None
self._evt_receiver = None
def __check__(self):
while True:
time.sleep(1)
pids = psutil.pids()
if psutil.pid_exists(self._procid):
continue
else:
print("%s process %d finished" % (self.btid, self._procid))
if self.sink is not None:
self.sink.on_stop(self.user, self.straid, self.btid)
break
def run(self):
if self._state != 0:
return
self._evt_receiver = BtEventReceiver(url=self._mq_url, logger=self.logger, sink=self)
self._evt_receiver.run()
self.logger.info("回测%s开始接收%s的通知信息" % (self.btid, self._mq_url))
try:
fullPath = os.path.join(self.folder, "runBT.py")
if isWindows():
self._procid = subprocess.Popen([sys.executable, fullPath], # 需要执行的文件路径
cwd=self.folder, creationflags=subprocess.CREATE_NEW_CONSOLE).pid
else:
self._procid = subprocess.Popen([sys.executable, fullPath], # 需要执行的文件路径
cwd=self.folder).pid
self._cmd_line = sys.executable + " " + fullPath
except:
self.logger.info("回测%s启动异常" % (self.btid))
self._state = 1
self.logger.info("回测%s的已启动,进程ID: %d" % (self.btid, self._procid))
self.watcher = threading.Thread(target=self.__check__, name=self.btid, daemon=True)
self.watcher.start()
@property
def cmd_line(self) -> str:
fullPath = os.path.join(self.folder, "runBT.py")
if self._cmd_line is None:
self._cmd_line = sys.executable + " " + fullPath
return self._cmd_line
def is_running(self, pids) -> bool:
bNeedCheck = (self._procid is None) or (not psutil.pid_exists(self._procid))
if bNeedCheck:
for pid in pids:
try:
pInfo = psutil.Process(pid)
cmdLine = pInfo.cmdline()
if len(cmdLine) == 0:
continue
# print(cmdLine)
cmdLine = ' '.join(cmdLine)
if self.cmd_line.upper() == cmdLine.upper():
self._procid = pid
self.logger.info("回测%s挂载成功,进程ID: %d" % (self.btid, self._procid))
if self._mq_url != '':
self._evt_receiver = BtEventReceiver(url=self._mq_url, logger=self.logger, sink=self)
self._evt_receiver.run()
self.logger.info("回测%s开始接收%s的通知信息" % (self.btid, self._mq_url))
self.watcher = threading.Thread(target=self.__check__, name=self.btid, daemon=True)
self.watcher.run()
except:
pass
return False
return True
def on_begin(self):
if self.sink is not None:
self.sink.on_start(self.user, self.straid, self.btid)
def on_finish(self):
pass
def on_state(self, statInfo:dict):
if self.sink is not None:
self.sink.on_state(self.user, self.straid, self.btid, statInfo)
print(statInfo)
def on_fund(self, fundInfo:dict):
if self.sink is not None:
self.sink.on_fund(self.user, self.straid, self.btid, fundInfo)
print(fundInfo)
class WtBtMon(BtTaskSink):
'''
回测管理器
'''
def __init__(self, deploy_folder:str, dtServo:WtDtServo = None, logger:WtLogger = None):
self.path = deploy_folder
self.user_stras = dict()
self.user_bts = dict()
self.logger = logger
self.dt_servo = dtServo
self.task_infos = dict()
self.task_map = dict()
self.__load_tasks__()
def __load_user_data__(self, user:str):
folder = os.path.join(self.path, user)
if not os.path.exists(folder):
os.mkdir(folder)
filepath = os.path.join(folder, "marker.json")
if not os.path.exists(filepath):
return False
f = open(filepath, "r")
content = f.read()
f.close()
obj = json.loads(content)
self.user_stras[user] = obj["strategies"]
self.user_bts[user] = obj["backtests"]
return True
def __save_user_data__(self, user):
folder = os.path.join(self.path, user)
if not os.path.exists(folder):
os.mkdir(folder)
obj = {
"strategies":{},
"backtests":{}
}
if user in self.user_stras:
obj["strategies"] = self.user_stras[user]
if user in self.user_bts:
obj["backtests"] = self.user_bts[user]
filepath = os.path.join(folder, "marker.json")
f = open(filepath, "w")
f.write(json.dumps(obj, indent=4, ensure_ascii=False))
f.close()
return True
def get_strategies(self, user:str) -> list:
if user not in self.user_stras:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
ay = list()
for straid in self.user_stras[user]:
ay.append(self.user_stras[user][straid])
return ay
def add_strategy(self, user:str, name:str) -> dict:
if user not in self.user_stras:
self.__load_user_data__(user)
if user not in self.user_stras:
self.user_stras[user] = dict()
straid = gen_straid(user)
self.user_stras[user][straid] = {
"id":straid,
"name":name,
"perform":{
"days": 0,
"total_return": 0,
"annual_return": 0,
"win_rate": 0,
"max_falldown": 0,
"max_profratio": 0,
"std": 0,
"down_std": 0,
"sharpe_ratio": 0,
"sortino_ratio": 0,
"calmar_ratio": 0
}
}
folder = os.path.join(self.path, user, straid)
if not os.path.exists(folder):
os.mkdir(folder)
fname = os.path.join(folder, "MyStrategy.py")
srcfname = os.path.join(self.path, "template/MyStrategy.py")
shutil.copyfile(srcfname, fname)
self.__save_user_data__(user)
return self.user_stras[user][straid]
def del_strategy(self, user:str, straid:str):
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return False
if straid not in self.user_stras[user]:
return True
folder = os.path.join(self.path, user, straid)
if not os.path.exists(folder):
return True
delFolder = os.path.join(self.path, user, ".del")
if not os.path.exists(delFolder):
os.mkdir(delFolder)
shutil.move(folder, delFolder)
self.user_stras[user].pop(straid)
self.__save_user_data__(user)
return True
def has_strategy(self, user:str, straid:str, btid:str = None) -> bool:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return False
if btid is None:
return straid in self.user_stras[user]
else:
return btid in self.user_bts[user]
def get_strategy_code(self, user:str, straid:str, btid:str = None) -> str:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
if btid is None:
path = os.path.join(self.path, user, straid, "MyStrategy.py")
if not os.path.exists(path):
return None
f = open(path, "r", encoding="UTF-8")
content = f.read()
f.close()
return content
else:
thisBts = self.user_bts[user]
if btid not in thisBts:
return None
bt_path = os.path.join(self.path, "%s/%s/backtests/%s/runBT.py" % (user, straid, btid))
f = open(bt_path, "r")
content = f.read()
f.close()
return content
def set_strategy_code(self, user:str, straid:str, content:str) -> bool:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return False
path = os.path.join(self.path, user, straid, "MyStrategy.py")
if not os.path.exists(path):
return None
f = open(path, "w", encoding="UTF-8")
f.write(content)
f.close()
return True
def get_backtests(self, user:str, straid:str) -> list:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
if user not in self.user_bts:
return None
ay = list()
for btid in self.user_bts[user]:
ay.append(self.user_bts[user][btid])
return ay
def del_backtest(self, user:str, btid:str):
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return
if user not in self.user_bts:
return
if btid in self.user_bts[user]:
self.user_bts[user].pop(btid)
self.logger.info("Backtest %s of %s deleted" % (btid, user))
self.__save_user_data__(user)
def get_bt_funds(self, user:str, straid:str, btid:str) -> list:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
thisBts = self.user_bts[user]
if btid not in thisBts:
return None
filename = "%s/%s/backtests/%s/outputs_bt/%s/funds.csv" % (user, straid, btid, btid)
filename = os.path.join(self.path, filename)
if not os.path.exists(filename):
return None
f = open(filename, "r")
lines = f.readlines()
f.close()
lines = lines[1:]
funds = list()
for line in lines:
cells = line.split(",")
if len(cells) > 10:
continue
tItem = {
"date": int(cells[0]),
"closeprofit": float(cells[1]),
"dynprofit": float(cells[2]),
"dynbalance": float(cells[3]),
"fee": 0
}
if len(cells) > 4:
tItem["fee"] = float(cells[4])
funds.append(tItem)
return funds
def get_bt_trades(self, user:str, straid:str, btid:str) -> list:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
thisBts = self.user_bts[user]
if btid not in thisBts:
return None
filename = "%s/%s/backtests/%s/outputs_bt/%s/trades.csv" % (user, straid, btid, btid)
filename = os.path.join(self.path, filename)
if not os.path.exists(filename):
return None
f = open(filename, "r")
lines = f.readlines()
f.close()
lines = lines[1:]
items = list()
for line in lines:
cells = line.split(",")
if len(cells) > 10:
continue
item = {
"code": cells[0],
"time": int(cells[1]),
"direction": cells[2],
"offset": cells[3],
"price": float(cells[4]),
"volume": float(cells[5]),
"tag": cells[6],
"fee": 0
}
if len(cells) > 7:
item["fee"] = float(cells[7])
if len(cells) > 4:
item["fee"] = float(cells[4])
items.append(item)
return items
def get_bt_rounds(self, user:str, straid:str, btid:str) -> list:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
thisBts = self.user_bts[user]
if btid not in thisBts:
return None
filename = "%s/%s/backtests/%s/outputs_bt/%s/closes.csv" % (user, straid, btid, btid)
filename = os.path.join(self.path, filename)
if not os.path.exists(filename):
return None
f = open(filename, "r")
lines = f.readlines()
f.close()
lines = lines[1:]
items = list()
for line in lines:
cells = line.split(",")
item = {
"code": cells[0],
"direct": cells[1],
"opentime": int(cells[2]),
"openprice": float(cells[3]),
"closetime": int(cells[4]),
"closeprice": float(cells[5]),
"qty": float(cells[6]),
"profit": float(cells[7]),
"maxprofit": float(cells[8]),
"maxloss": float(cells[9]),
"entertag": cells[11],
"exittag": cells[12]
}
items.append(item)
return items
def get_bt_signals(self, user:str, straid:str, btid:str) -> list:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
thisBts = self.user_bts[user]
if btid not in thisBts:
return None
filename = "%s/%s/backtests/%s/outputs_bt/%s/signals.csv" % (user, straid, btid, btid)
filename = os.path.join(self.path, filename)
if not os.path.exists(filename):
return None
f = open(filename, "r")
lines = f.readlines()
f.close()
lines = lines[1:]
items = list()
for line in lines:
cells = line.split(",")
if len(cells) > 10:
continue
item = {
"code": cells[0],
"target": float(cells[1]),
"sigprice": float(cells[2]),
"gentime": cells[3],
"tag": cells[4]
}
items.append(item)
return items
def get_bt_summary(self, user:str, straid:str, btid:str) -> list:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
thisBts = self.user_bts[user]
if btid not in thisBts:
return None
filename = "%s/%s/backtests/%s/outputs_bt/%s/summary.json" % (user, straid, btid, btid)
filename = os.path.join(self.path, filename)
if not os.path.exists(filename):
return None
f = open(filename, "r")
content = f.read()
f.close()
obj = json.loads(content)
return obj
def get_bt_state(self, user:str, straid:str, btid:str) -> list:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
thisBts = self.user_bts[user]
if btid not in thisBts:
return None
filename = "%s/%s/backtests/%s/outputs_bt/%s/btenv.json" % (user, straid, btid, btid)
filename = os.path.join(self.path, filename)
if not os.path.exists(filename):
return None
f = open(filename, "r")
content = f.read()
f.close()
obj = json.loads(content)
return obj
def get_bt_state(self, user:str, straid:str, btid:str) -> dict:
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
thisBts = self.user_bts[user]
if btid not in thisBts:
return None
filename = "%s/%s/backtests/%s/outputs_bt/%s/btenv.json" % (user, straid, btid, btid)
filename = os.path.join(self.path, filename)
if not os.path.exists(filename):
return None
f = open(filename, "r")
content = f.read()
f.close()
thisBts[btid]["state"] = json.loads(content)
return thisBts[btid]["state"]
def update_bt_state(self, user:str, straid:str, btid:str, stateObj:dict):
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
thisBts = self.user_bts[user]
if btid not in thisBts:
return None
thisBts[btid]["state"] = stateObj
def get_bt_kline(self, user:str, straid:str, btid:str) -> list:
if self.dt_servo is None:
return None
if user not in self.user_bts:
bSucc = self.__load_user_data__(user)
if not bSucc:
return None
btState = self.get_bt_state(user, straid, btid)
if btState is None:
return None
thisBts = self.user_bts[user]
if "kline" not in thisBts[btid]:
code = btState["code"]
period = btState["period"]
stime = btState["stime"]
etime = btState["etime"]
barList = self.dt_servo.get_bars(stdCode=code, period=period, fromTime=stime, endTime=etime)
if barList is None:
return None
bars = list()
for realBar in barList:
bar = dict()
if period[0] == 'd':
bar["time"] = realBar.date
else:
bar["time"] = 1990*100000000 + realBar.time
bar["bartime"] = bar["time"]
bar["open"] = realBar.open
bar["high"] = realBar.high
bar["low"] = realBar.low
bar["close"] = realBar.close
bar["volume"] = realBar.vol
bars.append(bar)
thisBts[btid]["kline"] = bars
return thisBts[btid]["kline"]
def run_backtest(self, user:str, straid:str, fromTime:int, endTime:int, capital:float, slippage:int=0) -> dict:
if user not in self.user_bts:
self.__load_user_data__(user)
if user not in self.user_bts:
self.user_bts[user] = dict()
btid = gen_btid(user, straid)
# 生成回测目录
folder = os.path.join(self.path, user, straid, "backtests")
if not os.path.exists(folder):
os.mkdir(folder)
folder = os.path.join(folder, btid)
os.mkdir(folder)
# 将策略文件复制到该目录下
old_path = os.path.join(self.path, user, straid, "MyStrategy.py")
new_path = os.path.join(folder, "MyStrategy.py")
shutil.copyfile(old_path, new_path)
# 初始化目录下的配置文件
old_path = os.path.join(self.path, "template/configbt.json")
new_path = os.path.join(folder, "configbt.json")
f = open(old_path, "r", encoding="UTF-8")
content = f.read()
f.close()
content = content.replace("$BTID$", btid)
f = open(new_path, "w", encoding="UTF-8")
f.write(content)
f.close()
old_path = os.path.join(self.path, "template/logcfgbt.json")
new_path = os.path.join(folder, "logcfgbt.json")
shutil.copyfile(old_path, new_path)
old_path = os.path.join(self.path, "template/fees.json")
new_path = os.path.join(folder, "fees.json")
shutil.copyfile(old_path, new_path)
old_path = os.path.join(self.path, "template/runBT.py")
new_path = os.path.join(folder, "runBT.py")
f = open(old_path, "r", encoding="UTF-8")
content = f.read()
f.close()
content = content.replace("$FROMTIME$", str(fromTime))
content = content.replace("$ENDTIME$", str(endTime))
content = content.replace("$STRAID$", btid)
content = content.replace("$CAPITAL$", str(capital))
content = content.replace("$SLIPPAGE$", str(slippage))
f = open(new_path, "w", encoding="UTF-8")
f.write(content)
f.close()
btInfo = {
"id":btid,
"capital":capital,
"runtime":datetime.datetime.now().strftime("%Y.%m.%d %H:%M:%S"),
"state":{
"code": "",
"period": "",
"stime": fromTime,
"etime": endTime,
"progress": 0,
"elapse": 0
},
"perform":{
"days": 0,
"total_return": 0,
"annual_return": 0,
"win_rate": 0,
"max_falldown": 0,
"max_profratio": 0,
"std": 0,
"down_std": 0,
"sharpe_ratio": 0,
"sortino_ratio": 0,
"calmar_ratio": 0
}
}
self.user_bts[user][btid] = btInfo
self.__save_user_data__(user)
# 添加
btTask = WtBtTask(user, straid, btid, folder, self.logger, sink=self)
btTask.run()
self.task_map[btid] = btTask
# 这里还需要记录一下回测的任务,不然如果重启就恢复不了了
taskInfo = {
"user":user,
"straid":straid,
"btid":btid,
"folder":folder
}
self.task_infos[btid]= taskInfo
self.__save_tasks__()
return btInfo
def __update_bt_result__(self, user:str, straid:str, btid:str):
if user not in self.user_bts:
self.__load_user_data__(user)
if user not in self.user_bts:
self.user_bts[user] = dict()
# 更新回测状态
stateObj = self.get_bt_state(user, straid, btid)
self.user_bts[user][btid]["state"] = stateObj
# 更新回测结果摘要
summaryObj = self.get_bt_summary(user, straid, btid)
self.user_bts[user][btid]["perform"] = summaryObj
self.user_stras[user][straid]["perform"] = summaryObj
self.__save_user_data__(user)
def __save_tasks__(self):
obj = self.task_infos
filename = os.path.join(self.path, "tasks.json")
f = open(filename, "w")
f.write(json.dumps(obj, indent=4))
f.close()
def __load_tasks__(self):
filename = os.path.join(self.path, "tasks.json")
if not os.path.exists(filename):
return
f = open(filename, "r")
content = f.read()
f.close()
task_infos = json.loads(content)
pids = psutil.pids()
for btid in task_infos:
tInfo = task_infos[btid].copy()
tInfo["logger"] = self.logger
btTask = WtBtTask(**tInfo)
if btTask.is_running(pids):
self.task_map[btid] = btTask
self.task_infos[btid] = task_infos[btid]
self.logger.info("回测任务%s已恢复")
else:
# 之前记录过测回测任务,执行完成了,要更新回测数据
self.__update_bt_result__(tInfo["user"], tInfo["straid"], btid)
self.__save_tasks__()
def on_start(self, user:str, straid:str, btid:str):
pass
def on_stop(self, user:str, straid:str, btid:str):
self.__update_bt_result__(user, straid, btid)
def on_state(self, user:str, straid:str, btid:str, statInfo:dict):
self.user_bts[user][btid]["state"] = statInfo
def on_fund(self, user:str, straid:str, btid:str, fundInfo:dict):
pass
================================================
FILE: wtpy/monitor/WtLogger.py
================================================
import logging
import os
class WtLogger:
def __init__(self, catName:str='', filename:str="out.log"):
self.logger = logging.getLogger(catName)
self.logger.setLevel(logging.DEBUG)
#创建一个handler,用于写入日志文件
log_path = os.getcwd()+"/logs/" # 指定文件输出路径,注意logs是个文件夹,一定要加上/,不然会导致输出路径错误,把logs变成文件名的一部分了
if not os.path.exists(log_path):
os.mkdir(log_path)
logname = log_path + filename #指定输出的日志文件名
fh = logging.FileHandler(logname,encoding = 'utf-8',mode='a') # 指定utf-8格式编码,避免输出的日志文本乱码
fh.setLevel(logging.INFO)
#创建一个handler,用于将日志输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# 定义handler的输出格式
formatter = logging.Formatter('[%(asctime)s - %(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 给logger添加handler
self.logger.addHandler(fh)
self.logger.addHandler(ch)
def info(self, message:str):
self.logger.info(message)
def warn(self, message:str):
self.logger.warn(message)
def error(self, message:str):
self.logger.error(message)
def fatal(self, message:str):
self.logger.fatal(message)
================================================
FILE: wtpy/monitor/WtMonSvr.py
================================================
from flask import Flask, session, redirect, request, make_response
from flask_compress import Compress
import json
import datetime
import os
import hashlib
import sys
import base64
import chardet
import pytz
from .WtLogger import WtLogger
from .DataMgr import DataMgr, backup_file
from .PushSvr import PushServer
from .WatchDog import WatchDog, WatcherSink
from .EventReceiver import EventReceiver, EventSink
from .WtBtMon import WtBtMon
from wtpy import WtDtServo
def pack_rsp(obj):
rsp = make_response(json.dumps(obj))
rsp.headers["content-type"]= "text/json;charset=utf-8"
return rsp
def parse_data():
try:
data = request.get_data()
json_data = json.loads(data.decode("utf-8"))
return True,json_data
except:
return False, {
"result": -998,
"message": "请求数据解析失败"
}
def get_param(json_data, key:str, type=str, defVal = ""):
if key not in json_data:
return defVal
else:
return type(json_data[key])
#获取文件最后N行的函数
def get_tail(filename, N:int = 100, encoding="GBK") :
filesize = os.path.getsize(filename)
blocksize = 10240
dat_file = open(filename, 'r', encoding=encoding)
last_line = ""
if filesize > blocksize :
maxseekpoint = (filesize // blocksize)
dat_file.seek((maxseekpoint-1)*blocksize)
elif filesize :
dat_file.seek(0, 0)
lines = dat_file.readlines()
if lines :
last_line = lines[-N:]
dat_file.close()
return ''.join(last_line), len(last_line)
def check_auth():
usrInfo = session.get("userinfo")
# session里没有用户信息
if usrInfo is None:
return False, {
"result":-999,
"message":"请先登录"
}
# session里有用户信息,则要读取
exptime = session.get("expiretime")
now = datetime.datetime.now().replace(tzinfo=pytz.timezone('UTC')).strftime("%Y.%m.%d %H:%M:%S")
if now > exptime:
return False, {
"result":-999,
"message":"登录已超时,请重新登录"
}
return True, usrInfo
def get_cfg_tree(root:str, name:str):
if not os.path.exists(root):
return {
"label":name,
"path":root,
"exist":False,
"isfile":False,
"children":[]
}
if os.path.isfile(root):
return {
"label":name,
"path":root,
"exist":False,
"isfile":True
}
ret = {
"label":name,
"path":root,
"exist":True,
"isfile":False,
"children":[]
}
filepath = os.path.join(root, "run.py")
ret['children'].append({
"label":"run.py",
"path":filepath,
"exist":True,
"isfile":True,
"children":[]
})
filepath = os.path.join(root, "config.json")
ret['children'].append({
"label":"config.json",
"path":filepath,
"exist":True,
"isfile":True,
"children":[]
})
f = open(filepath, "r")
content = f.read()
f.close()
cfgObj = json.loads(content)
if "executers" in cfgObj:
filename = cfgObj["executers"]
if type(filename) == str:
filepath = os.path.join(root, filename)
ret['children'].append({
"label":filename,
"path":filepath,
"exist":True,
"isfile":True,
"children":[]
})
if "parsers" in cfgObj:
filename = cfgObj["parsers"]
if type(filename) == str:
filepath = os.path.join(root, filename)
ret['children'].append({
"label":filename,
"path":filepath,
"exist":True,
"isfile":True,
"children":[]
})
if "traders" in cfgObj:
filename = cfgObj["traders"]
if type(filename) == str:
filepath = os.path.join(root, filename)
ret['children'].append({
"label":filename,
"path":filepath,
"exist":True,
"isfile":True,
"children":[]
})
filepath = os.path.join(root, 'generated')
ret["children"].append(get_path_tree(filepath, 'generated', True))
return ret
def get_path_tree(root:str, name:str, hasFile:bool = True):
if not os.path.exists(root):
return {
"label":name,
"path":root,
"exist":False,
"isfile":False,
"children":[]
}
if os.path.isfile(root):
return {
"label":name,
"path":root,
"exist":False,
"isfile":True
}
ret = {
"label":name,
"path":root,
"exist":True,
"isfile":False,
"children":[]
}
files = os.listdir(root, )
for filename in files:
if filename in ['__pycache__', '.vscode', 'wtpy', '__init__.py']:
continue
if filename[-3:] == 'pyc':
continue
filepath = os.path.join(root, filename)
if os.path.isfile(filepath):
if not hasFile:
continue
else:
ret["children"].append({
"label":filename,
"path":filepath,
"exist":True,
"isfile":True})
else:
ret["children"].append(get_path_tree(filepath, filename, hasFile))
ay1 = list()
ay2 = list()
for item in ret["children"]:
if item["isfile"]:
ay2.append(item)
else:
ay1.append(item)
ay = ay1 + ay2
ret["children"] = ay
return ret
class WtMonSvr(WatcherSink):
def __init__(self, static_folder:str="", static_url_path="/", deploy_dir="C:/"):
if len(static_folder) == 0:
static_folder = 'static'
self.logger = WtLogger(__name__, "WtMonSvr.log")
# 数据管理器,主要用于缓存各组合的数据
self.__data_mgr__ = DataMgr('data.db', logger=self.logger)
self.__bt_mon__:WtBtMon = None
self.__dt_servo__:WtDtServo = None
# 看门狗模块,主要用于调度各个组合启动关闭
self._dog = WatchDog(sink=self, db=self.__data_mgr__.get_db(), logger=self.logger)
app = Flask(__name__, instance_relative_config=True, static_folder=static_folder, static_url_path=static_url_path)
app.secret_key = "!@#$%^&*()"
Compress(app)
# app.debug = True
self.app = app
self.worker = None
self.deploy_dir = deploy_dir
self.deploy_tree = None
self.push_svr = PushServer(app, self.__data_mgr__, self.logger)
self.init_mgr_apis(app)
def set_bt_mon(self, btMon:WtBtMon):
self.__bt_mon__ = btMon
self.init_bt_apis(self.app)
def set_dt_servo(self, dtServo:WtDtServo):
self.__dt_servo__ = dtServo
def init_bt_apis(self, app:Flask):
# 拉取K线数据
@app.route("/bt/qrybars", methods=["POST"])
def qry_bt_bars():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
if self.__dt_servo__ is None:
ret = {
"result":-2,
"message":"没有配置数据伺服"
}
return pack_rsp(ret)
stdCode = get_param(json_data, "code")
period = get_param(json_data, "period")
fromTime = get_param(json_data, "stime", int, None)
dataCount = get_param(json_data, "count", int, None)
endTime = get_param(json_data, "etime", int)
bars = self.__dt_servo__.get_bars(stdCode=stdCode, period=period, fromTime=fromTime, dataCount=dataCount, endTime=endTime)
if bars is None:
ret = {
"result":-2,
"message":"Data not found"
}
else:
bar_list = [curBar.to_dict for curBar in bars]
ret = {
"result":0,
"message":"Ok",
"bars": bar_list
}
return pack_rsp(ret)
# 拉取用户策略列表
@app.route("/bt/qrystras", methods=["POST"])
def qry_my_stras():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
ret = {
"result":0,
"message":"OK",
"strategies": self.__bt_mon__.get_strategies(user)
}
return pack_rsp(ret)
# 拉取策略代码
@app.route("/bt/qrycode", methods=["POST"])
def qry_stra_code():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
straid = get_param(json_data, "straid")
if self.__bt_mon__ is None:
ret = {
"result":-1,
"message":"回测管理器未配置"
}
else:
if not self.__bt_mon__.has_strategy(user, straid):
ret = {
"result":-2,
"message":"策略代码不存在"
}
else:
content = self.__bt_mon__.get_strategy_code(user, straid)
ret = {
"result":0,
"message":"OK",
"content":content
}
return pack_rsp(ret)
# 提交策略代码
@app.route("/bt/setcode", methods=["POST"])
def set_stra_code():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
straid = get_param(json_data, "straid")
content = get_param(json_data, "content")
if len(content) == 0 or len(straid) == 0:
ret = {
"result":-2,
"message":"策略ID和代码不能为空"
}
return pack_rsp(ret)
if self.__bt_mon__ is None:
ret = {
"result":-1,
"message":"回测管理器未配置"
}
else:
if not self.__bt_mon__.has_strategy(user, straid):
ret = {
"result":-2,
"message":"策略不存在"
}
else:
ret = self.__bt_mon__.set_strategy_code(user, straid, content)
if ret:
ret = {
"result":0,
"message":"OK"
}
else:
ret = {
"result":-3,
"message":"保存策略代码失败"
}
return pack_rsp(ret)
# 添加用户策略
@app.route("/bt/addstra", methods=["POST"])
def cmd_add_stra():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
name = get_param(json_data, "name")
if len(name) == 0:
ret = {
"result":-2,
"message":"策略名称不能为空"
}
return pack_rsp(ret)
if self.__bt_mon__ is None:
ret = {
"result":-3,
"message":"回测管理器未配置"
}
return pack_rsp(ret)
straInfo = self.__bt_mon__.add_strategy(user, name)
if straInfo is None:
ret = {
"result":-4,
"message":"策略添加失败"
}
else:
ret = {
"result":0,
"message":"OK",
"strategy": straInfo
}
return pack_rsp(ret)
# 删除用户策略
@app.route("/bt/delstra", methods=["POST"])
def cmd_del_stra():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
straid = get_param(json_data, "straid")
if len(straid) == 0:
ret = {
"result":-2,
"message":"策略ID不能为空"
}
return pack_rsp(ret)
if self.__bt_mon__ is None:
ret = {
"result":-1,
"message":"回测管理器未配置"
}
else:
if not self.__bt_mon__.has_strategy(user, straid):
ret = {
"result":-2,
"message":"策略不存在"
}
else:
ret = self.__bt_mon__.del_strategy(user, straid)
if ret:
ret = {
"result":0,
"message":"OK"
}
else:
ret = {
"result":-3,
"message":"保存策略代码失败"
}
return pack_rsp(ret)
# 获取策略回测列表
@app.route("/bt/qrystrabts", methods=["POST"])
def qry_stra_bts():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
straid = get_param(json_data, "straid")
if len(straid) == 0:
ret = {
"result":-2,
"message":"策略ID不能为空"
}
return pack_rsp(ret)
if self.__bt_mon__ is None:
ret = {
"result":-1,
"message":"回测管理器未配置"
}
else:
if not self.__bt_mon__.has_strategy(user, straid):
ret = {
"result":-2,
"message":"策略不存在"
}
else:
ret = {
"result":0,
"message":"OK",
"backtests":self.__bt_mon__.get_backtests(user, straid)
}
return pack_rsp(ret)
# 获取策略回测信号
@app.route("/bt/qrybtsigs", methods=["POST"])
def qry_stra_bt_signals():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
straid = get_param(json_data, "straid")
btid = get_param(json_data, "btid")
if len(straid) == 0 or len(btid) == 0:
ret = {
"result":-2,
"message":"策略ID和回测ID不能为空"
}
return pack_rsp(ret)
if self.__bt_mon__ is None:
ret = {
"result":-1,
"message":"回测管理器未配置"
}
else:
if not self.__bt_mon__.has_strategy(user, straid):
ret = {
"result":-2,
"message":"策略不存在"
}
else:
ret = {
"result":0,
"message":"OK",
"signals":self.__bt_mon__.get_bt_signals(user, straid, btid)
}
return pack_rsp(ret)
# 删除策略回测列表
@app.route("/bt/delstrabt", methods=["POST"])
def cmd_del_stra_bt():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
btid = get_param(json_data, "btid")
if len(btid) == 0:
ret = {
"result":-2,
"message":"回测ID不能为空"
}
return pack_rsp(ret)
if self.__bt_mon__ is None:
ret = {
"result":-1,
"message":"回测管理器未配置"
}
else:
self.__bt_mon__.del_backtest(user, btid)
ret = {
"result":0,
"message":"OK"
}
return pack_rsp(ret)
# 获取策略回测成交
@app.route("/bt/qrybttrds", methods=["POST"])
def qry_stra_bt_trades():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
straid = get_param(json_data, "straid")
btid = get_param(json_data, "btid")
if len(straid) == 0 or len(btid) == 0:
ret = {
"result":-2,
"message":"策略ID和回测ID不能为空"
}
return pack_rsp(ret)
if self.__bt_mon__ is None:
ret = {
"result":-1,
"message":"回测管理器未配置"
}
else:
if not self.__bt_mon__.has_strategy(user, straid):
ret = {
"result":-2,
"message":"策略不存在"
}
else:
ret = {
"result":0,
"message":"OK",
"trades":self.__bt_mon__.get_bt_trades(user, straid, btid)
}
return pack_rsp(ret)
# 获取策略回测资金
@app.route("/bt/qrybtfunds", methods=["POST"])
def qry_stra_bt_funds():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
straid = get_param(json_data, "straid")
btid = get_param(json_data, "btid")
if len(straid) == 0 or len(btid) == 0:
ret = {
"result":-2,
"message":"策略ID和回测ID不能为空"
}
return pack_rsp(ret)
if self.__bt_mon__ is None:
ret = {
"result":-1,
"message":"回测管理器未配置"
}
else:
if not self.__bt_mon__.has_strategy(user, straid):
ret = {
"result":-2,
"message":"策略不存在"
}
else:
ret = {
"result":0,
"message":"OK",
"funds":self.__bt_mon__.get_bt_funds(user, straid, btid)
}
return pack_rsp(ret)
# 获取策略回测回合
@app.route("/bt/qrybtrnds", methods=["POST"])
def qry_stra_bt_rounds():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
straid = get_param(json_data, "straid")
btid = get_param(json_data, "btid")
if len(straid) == 0 or len(btid) == 0:
ret = {
"result":-2,
"message":"策略ID和回测ID不能为空"
}
return pack_rsp(ret)
if self.__bt_mon__ is None:
ret = {
"result":-1,
"message":"回测管理器未配置"
}
else:
if not self.__bt_mon__.has_strategy(user, straid):
ret = {
"result":-2,
"message":"策略不存在"
}
else:
ret = {
"result":0,
"message":"OK",
"rounds":self.__bt_mon__.get_bt_rounds(user, straid, btid)
}
return pack_rsp(ret)
# 启动策略回测
@app.route("/bt/runstrabt", methods=["POST"])
def cmd_run_stra_bt():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, userInfo = check_auth()
if not bSucc:
return pack_rsp(userInfo)
user = userInfo["loginid"]
role = userInfo["role"]
if role not in ['researcher','superman']:
ret = {
"result":-1,
"message":"没有权限"
}
return pack_rsp(ret)
curDt = int(datetime.datetime.now().strftime("%Y%m%d"))
straid = get_param(json_data, "straid")
fromtime = get_param(json_data, "stime", int, defVal=curDt)
endtime = get_param(json_data, "etime", int, defVal=curDt)
capital = get_param(json_data, "capital", float, defVal=500000)
slippage = get_param(json_data, "slippage", int, defVal=0)
if len(straid) == 0:
ret = {
"result":-2,
"message":"策略ID不能为空"
}
return pack_rsp(ret)
if fromtime > endtime:
fromtime,endtime = endtime,fromtime
fromtime = fromtime*10000 + 900
endtime = endtime*10000 + 1515
if self.__bt_mon__ is None:
ret = {
"result":-1,
"message":"回测管理器未配置"
}
else:
if not self.__bt_mon__.has_strategy(user, straid):
ret = {
"result":-2,
"message":"策略不存在"
}
else:
btInfo = self.__bt_mon__.run_backtest(user,straid,fromtime,endtime,capital,slippage)
ret = {
"result":0,
"message":"OK",
"backtest": btInfo
}
return pack_rsp(ret)
def init_mgr_apis(self, app:Flask):
@app.route("/console", methods=["GET"])
def stc_console_index():
return redirect("./console/index.html")
@app.route("/mobile", methods=["GET"])
def stc_mobile_index():
return redirect("./mobile/index.html")
'''下面是API接口的编写'''
@app.route("/mgr/login", methods=["POST"])
def cmd_login():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
if True:
user = get_param(json_data, "loginid")
pwd = get_param(json_data, "passwd")
if len(user) == 0 or len(pwd) == 0:
ret = {
"result":-1,
"message":"用户名和密码不能为空"
}
else:
encpwd = hashlib.md5((user+pwd).encode("utf-8")).hexdigest()
now = datetime.datetime.now()
usrInf = self.__data_mgr__.get_user(user)
if usrInf is None:
ret = {
"result":-1,
"message":"用户不存在"
}
elif encpwd != usrInf["passwd"]:
ret = {
"result":-1,
"message":"登录密码错误"
}
else:
usrInf.pop("passwd")
usrInf["loginip"]=request.remote_addr
usrInf["logintime"]=now.strftime("%Y/%m/%d %H:%M:%S")
exptime = now + datetime.timedelta(minutes=360) #360分钟令牌超时
session["userinfo"] = usrInf
session["expiretime"] = exptime.replace(tzinfo=pytz.timezone('UTC')).strftime("%Y.%m.%d %H:%M:%S")
ret = {
"result":0,
"message":"Ok",
"userinfo":usrInf
}
self.__data_mgr__.log_action(usrInf, "login", json.dumps(request.headers.get('User-Agent')))
else:
ret = {
"result":-1,
"message":"请求处理出现异常",
}
if session.get("userinfo") is not None:
session.pop("userinfo")
return pack_rsp(ret)
# 修改密码
@app.route("/mgr/modpwd", methods=["POST"])
def mod_pwd():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
oldpwd = get_param(json_data, "oldpwd")
newpwd = get_param(json_data, "newpwd")
if len(oldpwd) == 0 or len(newpwd) == 0:
ret = {
"result":-1,
"message":"新旧密码都不能为空"
}
else:
user = adminInfo["loginid"]
oldencpwd = hashlib.md5((user+oldpwd).encode("utf-8")).hexdigest()
usrInf = self.__data_mgr__.get_user(user)
if usrInf is None:
ret = {
"result":-1,
"message":"用户不存在"
}
else:
if oldencpwd != usrInf["passwd"]:
ret = {
"result":-1,
"message":"旧密码错误"
}
else:
if 'builtin' in usrInf and usrInf["builtin"]:
#如果是内建账号要改密码,则先添加用户
usrInf["passwd"] = oldpwd
self.__data_mgr__.add_user(usrInf, user)
print("%s是内建账户,自动添加到数据库中" % user)
newencpwd = hashlib.md5((user+newpwd).encode("utf-8")).hexdigest()
self.__data_mgr__.mod_user_pwd(user, newencpwd, user)
ret = {
"result":0,
"message":"Ok"
}
return pack_rsp(ret)
# 添加组合
@app.route("/mgr/addgrp", methods=["POST"])
def cmd_add_group():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
id = get_param(json_data, "groupid")
name = get_param(json_data, "name")
path = get_param(json_data, "path")
info = get_param(json_data, "info")
gtype = get_param(json_data, "gtype")
env = get_param(json_data, "env")
datmod = get_param(json_data, "datmod")
mqurl = get_param(json_data, "mqurl")
action = get_param(json_data, "action")
if action == "":
action = "add"
if len(id) == 0 or len(name) == 0 or len(gtype) == 0:
ret = {
"result":-1,
"message":"组合ID、名称、类型都不能为空"
}
elif not os.path.exists(path) or not os.path.isdir(path):
ret = {
"result":-2,
"message":"组合运行目录不正确"
}
elif action == "add" and self.__data_mgr__.has_group(id):
ret = {
"result":-3,
"message":"组合ID不能重复"
}
else:
try:
grpInfo = {
"id":id,
"name":name,
"path":path,
"info":info,
"gtype":gtype,
"datmod":datmod,
"env":env,
"mqurl":mqurl
}
if self.__data_mgr__.add_group(grpInfo):
ret = {
"result":0,
"message":"Ok"
}
if action == "add":
self.__data_mgr__.log_action(adminInfo, "addgrp", json.dumps(grpInfo))
else:
self.__data_mgr__.log_action(adminInfo, "modgrp", json.dumps(grpInfo))
self._dog.updateMQURL(id, mqurl)
else:
ret = {
"result":-2,
"message":"添加用户失败"
}
except:
ret = {
"result":-1,
"message":"请求解析失败"
}
return pack_rsp(ret)
# 删除组合
@app.route("/mgr/delgrp", methods=["POST"])
def cmd_del_group():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
id = get_param(json_data, "groupid")
if len(id) == 0:
ret = {
"result":-1,
"message":"组合ID不能为空"
}
elif not self.__data_mgr__.has_group(id):
ret = {
"result":-3,
"message":"该组合不存在"
}
elif self._dog.isRunning(id):
ret = {
"result":-3,
"message":"请先停止该组合"
}
else:
if True:
self._dog.delApp(id)
self.__data_mgr__.del_group(id)
ret = {
"result":0,
"message":"Ok"
}
self.__data_mgr__.log_action(adminInfo, "delgrp", id)
else:
ret = {
"result":-1,
"message":"请求解析失败"
}
return pack_rsp(ret)
# 组合停止
@app.route("/mgr/stopgrp", methods=["POST"])
def cmd_stop_group():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
grpid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(grpid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
if self._dog.isRunning(grpid):
self._dog.stop(grpid)
ret = {
"result":0,
"message":"Ok"
}
self.__data_mgr__.log_action(adminInfo, "stopgrp", grpid)
return pack_rsp(ret)
# 组合启动
@app.route("/mgr/startgrp", methods=["POST"])
def cmd_start_group():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
grpid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(grpid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
if not self._dog.isRunning(grpid):
self._dog.start(grpid)
ret = {
"result":0,
"message":"Ok"
}
self.__data_mgr__.log_action(adminInfo, "startgrp", grpid)
return pack_rsp(ret)
# 获取执行的python进程的路径
@app.route("/mgr/qryexec", methods=["POST"])
def qry_exec_path():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
ret = {
"result":0,
"message":"Ok",
"path": sys.executable
}
return pack_rsp(ret)
# 配置监控
@app.route("/mgr/qrymon", methods=["POST"])
def qry_mon_cfg():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
grpid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(grpid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
monCfg = self._dog.getAppConf(grpid)
if monCfg is None:
ret = {
"result":0,
"message":"ok"
}
else:
ret = {
"result":0,
"message":"ok",
"config":monCfg
}
return pack_rsp(ret)
# 配置监控
@app.route("/mgr/cfgmon", methods=["POST"])
def cmd_config_monitor():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
#这里本来是要做检查的,算了,先省事吧
isGrp = get_param(json_data, "group", bool, False)
self._dog.applyAppConf(json_data, isGrp)
ret = {
"result":0,
"message":"ok"
}
self.__data_mgr__.log_action(adminInfo, "cfgmon", json.dumps(json_data))
return pack_rsp(ret)
# 查询目录结构
@app.route("/mgr/qrydir", methods=["POST"])
def qry_directories():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
if True:
if self.deploy_tree is None:
self.deploy_tree = get_path_tree(self.deploy_dir, "root")
ret = {
"result":0,
"message":"Ok",
"tree":self.deploy_tree
}
else:
ret = {
"result":-1,
"message":"请求解析失败"
}
return pack_rsp(ret)
# 查询目录结构
@app.route("/mgr/qrygrpdir", methods=["POST"])
def qry_grp_directories():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
grpid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(grpid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
monCfg = self.__data_mgr__.get_group(grpid)
ret = {
"result":0,
"message":"Ok",
"tree": get_cfg_tree(monCfg["path"], "root")
}
return pack_rsp(ret)
# 查询组合列表
@app.route("/mgr/qrygrp", methods=["POST"])
def qry_groups():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
try:
groups = self.__data_mgr__.get_groups()
for grpInfo in groups:
grpInfo["running"] = self._dog.isRunning(grpInfo["id"])
ret = {
"result":0,
"message":"Ok",
"groups":groups
}
except:
ret = {
"result":-1,
"message":"请求解析失败"
}
return pack_rsp(ret)
# 查询文件信息
@app.route("/mgr/qrygrpfile", methods=["POST"])
def qry_group_file():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
grpid = get_param(json_data, "groupid")
path = get_param(json_data, "path")
if not self.__data_mgr__.has_group(grpid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
monCfg = self.__data_mgr__.get_group(grpid)
root = monCfg["path"]
if path[:len(root)] != root:
ret = {
"result":-1,
"message":"目标文件不在当前组合下"
}
else:
f = open(path,'rb')
content = f.read()
f.close()
encoding = chardet.detect(content)["encoding"]
content = content.decode(encoding)
ret = {
"result":0,
"message":"Ok",
"content": content
}
return pack_rsp(ret)
# 提交组合文件
@app.route("/mgr/cmtgrpfile", methods=["POST"])
def cmd_commit_group_file():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
grpid = get_param(json_data, "groupid")
content = get_param(json_data, "content")
path = get_param(json_data, "path")
if not self.__data_mgr__.has_group(grpid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
monCfg = self.__data_mgr__.get_group(grpid)
root = monCfg["path"]
if path[:len(root)] != root:
ret = {
"result":-1,
"message":"目标文件不在当前组合下"
}
else:
try:
f = open(path,'rb')
old_content = f.read()
f.close()
encoding = chardet.detect(old_content)["encoding"]
backup_file(path)
f = open(path,'wb')
f.write(content.encode(encoding))
f.close()
ret = {
"result":0,
"message":"Ok"
}
except:
ret = {
"result":-1,
"message":"文件保存失败"
}
return pack_rsp(ret)
# 查询策略列表
@app.route("/mgr/qrystras", methods=["POST"])
def qry_strategys():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
grpid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(grpid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"Ok",
"strategies":self.__data_mgr__.get_strategies(grpid)
}
return pack_rsp(ret)
# 查询通道列表
@app.route("/mgr/qrychnls", methods=["POST"])
def qry_channels():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
grpid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(grpid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"Ok",
"channels":self.__data_mgr__.get_channels(grpid)
}
return pack_rsp(ret)
# 查询组合日志
@app.route("/mgr/qrylogs", methods=["POST"])
def qry_logs():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
grpid = get_param(json_data, "id")
logtype = get_param(json_data, "type")
if not self.__data_mgr__.has_group(grpid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
grpInfo = self.__data_mgr__.get_group(grpid)
try:
logfolder = os.path.join(grpInfo["path"], "./Logs/")
file_list = os.listdir(logfolder)
targets = list()
for fname in file_list:
if fname[:6] == "Runner":
targets.append(fname)
targets.sort()
filename = os.path.join(logfolder, targets[-1])
content,lines = get_tail(filename, 100)
ret = {
"result":0,
"message":"Ok",
"content":content,
"lines":lines
}
except:
ret = {
"result":-1,
"message":"请求解析失败"
}
return pack_rsp(ret)
# 查询策略成交
@app.route("/mgr/qrytrds", methods=["POST"])
def qry_trades():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
sid = get_param(json_data, "strategyid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"trades": self.__data_mgr__.get_trades(gid, sid)
}
return pack_rsp(ret)
# 查询策略信号
@app.route("/mgr/qrysigs", methods=["POST"])
def qry_signals():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
sid = get_param(json_data, "strategyid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"signals": self.__data_mgr__.get_signals(gid, sid)
}
return pack_rsp(ret)
# 查询策略回合
@app.route("/mgr/qryrnds", methods=["POST"])
def qry_rounds():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
sid = get_param(json_data, "strategyid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"rounds": self.__data_mgr__.get_rounds(gid, sid)
}
return pack_rsp(ret)
# 查询策略持仓
@app.route("/mgr/qrypos", methods=["POST"])
def qry_positions():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
sid = get_param(json_data, "strategyid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"positions": self.__data_mgr__.get_positions(gid, sid)
}
return pack_rsp(ret)
# 查询策略持仓
@app.route("/mgr/qryfunds", methods=["POST"])
def qry_funds():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
sid = get_param(json_data, "strategyid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"funds": self.__data_mgr__.get_funds(gid, sid)
}
return pack_rsp(ret)
# 查询通道订单
@app.route("/mgr/qrychnlords", methods=["POST"])
def qry_channel_orders():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
cid = get_param(json_data, "channelid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"orders": self.__data_mgr__.get_channel_orders(gid, cid)
}
return pack_rsp(ret)
# 查询通道成交
@app.route("/mgr/qrychnltrds", methods=["POST"])
def qry_channel_trades():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
cid = get_param(json_data, "channelid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"trades": self.__data_mgr__.get_channel_trades(gid, cid)
}
return pack_rsp(ret)
# 查询通道持仓
@app.route("/mgr/qrychnlpos", methods=["POST"])
def qry_channel_position():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
cid = get_param(json_data, "channelid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"positions": self.__data_mgr__.get_channel_positions(gid, cid)
}
return pack_rsp(ret)
# 查询通道资金
@app.route("/mgr/qrychnlfund", methods=["POST"])
def qry_channel_funds():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
cid = get_param(json_data, "channelid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"funds": self.__data_mgr__.get_channel_funds(gid, cid)
}
return pack_rsp(ret)
# 查询用户列表
@app.route("/mgr/qryusers", methods=["POST"])
def qry_users():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
users = self.__data_mgr__.get_users()
for usrInfo in users:
usrInfo.pop("passwd")
ret = {
"result":0,
"message":"",
"users": users
}
return pack_rsp(ret)
# 提交用户信息
@app.route("/mgr/cmtuser", methods=["POST"])
def cmd_commit_user():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
self.__data_mgr__.add_user(json_data, adminInfo["loginid"])
ret = {
"result":0,
"message":"Ok"
}
self.__data_mgr__.log_action(adminInfo, "cmtuser", json.dumps(json_data))
return pack_rsp(ret)
# 删除用户
@app.route("/mgr/deluser", methods=["POST"])
def cmd_delete_user():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
loginid = get_param(json_data, "loginid")
if self.__data_mgr__.del_user(loginid, adminInfo["loginid"]):
self.__data_mgr__.log_action(adminInfo, "delusr", loginid)
ret = {
"result":0,
"message":"Ok"
}
return pack_rsp(ret)
# 修改密码
@app.route("/mgr/resetpwd", methods=["POST"])
def reset_pwd():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
user = get_param(json_data, "loginid")
pwd = get_param(json_data, "passwd")
if len(pwd) == 0 or len(user) == 0:
ret = {
"result":-1,
"message":"密码都不能为空"
}
else:
encpwd = hashlib.md5((user+pwd).encode("utf-8")).hexdigest()
usrInf = self.__data_mgr__.get_user(user)
if usrInf is None:
ret = {
"result":-1,
"message":"用户不存在"
}
else:
self.__data_mgr__.mod_user_pwd(user, encpwd, adminInfo["loginid"])
self.__data_mgr__.log_action(adminInfo, "resetpwd", loginid)
ret = {
"result":0,
"message":"Ok"
}
return pack_rsp(ret)
# 查询操作记录
@app.route("/mgr/qryacts", methods=["POST"])
def qry_actions():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
sdate = get_param(json_data, "sdate")
edate = get_param(json_data, "edate")
ret = {
"result":0,
"message":"",
"actions": self.__data_mgr__.get_actions(sdate, edate)
}
return pack_rsp(ret)
# 查询全部调度
@app.route("/mgr/qrymons", methods=["POST"])
def qry_mon_apps():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
schedules = self._dog.get_apps()
for appid in schedules:
schedules[appid]["group"] = self.__data_mgr__.has_group(appid)
ret = {
"result":0,
"message":"",
"schedules": schedules
}
return pack_rsp(ret)
@app.route("/mgr/startapp", methods=["POST"])
def cmd_start_app():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
appid = get_param(json_data, "appid")
if not self._dog.has_app(appid):
ret = {
"result":-1,
"message":"App不存在"
}
else:
if not self._dog.isRunning(appid):
self._dog.start(appid)
ret = {
"result":0,
"message":"Ok"
}
self.__data_mgr__.log_action(adminInfo, "startapp", appid)
return pack_rsp(ret)
# 组合停止
@app.route("/mgr/stopapp", methods=["POST"])
def cmd_stop_app():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
appid = get_param(json_data, "appid")
if not self._dog.has_app(appid):
ret = {
"result":-1,
"message":"App不存在"
}
else:
if self._dog.isRunning(appid):
self._dog.stop(appid)
ret = {
"result":0,
"message":"Ok"
}
self.__data_mgr__.log_action(adminInfo, "stopapp", appid)
return pack_rsp(ret)
# 查询调度日志
@app.route("/mgr/qrymonlog", methods=["POST"])
def qry_mon_logs():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
filename = os.getcwd() + "/logs/WtMonSvr.log"
content,lines = get_tail(filename, 100, "UTF-8")
ret = {
"result":0,
"message":"Ok",
"content":content,
"lines":lines
}
return pack_rsp(ret)
# 删除调度任务
@app.route("/mgr/delapp", methods=["POST"])
def cmd_del_app():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, adminInfo = check_auth()
if not bSucc:
return pack_rsp(adminInfo)
id = get_param(json_data, "appid")
if len(id) == 0:
ret = {
"result":-1,
"message":"组合ID不能为空"
}
elif self.__data_mgr__.has_group(id):
ret = {
"result":-2,
"message":"该调度任务是策略组合,请从组合管理删除"
}
elif not self._dog.has_app(id):
ret = {
"result":-3,
"message":"该调度任务不存在"
}
elif self._dog.isRunning(id):
ret = {
"result":-4,
"message":"请先停止该任务"
}
else:
if True:
self._dog.delApp(id)
ret = {
"result":0,
"message":"Ok"
}
self.__data_mgr__.log_action(adminInfo, "delapp", id)
else:
ret = {
"result":-1,
"message":"请求解析失败"
}
return pack_rsp(ret)
# 查询组合持仓
@app.route("/mgr/qryportpos", methods=["POST"])
def qry_group_positions():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"positions": self.__data_mgr__.get_group_positions(gid)
}
return pack_rsp(ret)
# 查询组合成交
@app.route("/mgr/qryporttrd", methods=["POST"])
def qry_group_trades():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"trades": self.__data_mgr__.get_group_trades(gid)
}
return pack_rsp(ret)
# 查询组合回合
@app.route("/mgr/qryportrnd", methods=["POST"])
def qry_group_rounds():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"rounds": self.__data_mgr__.get_group_rounds(gid)
}
return pack_rsp(ret)
# 查询组合资金
@app.route("/mgr/qryportfunds", methods=["POST"])
def qry_group_funds():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"funds": self.__data_mgr__.get_group_funds(gid)
}
return pack_rsp(ret)
# 查询组合绩效分析
@app.route("/mgr/qryportperfs", methods=["POST"])
def qry_group_perfs():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"performance": self.__data_mgr__.get_group_performances(gid)
}
return pack_rsp(ret)
# 查询组合过滤器
@app.route("/mgr/qryportfilters", methods=["POST"])
def qry_group_filters():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
gid = get_param(json_data, "groupid")
if not self.__data_mgr__.has_group(gid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
ret = {
"result":0,
"message":"",
"filters": self.__data_mgr__.get_group_filters(gid)
}
return pack_rsp(ret)
# 提交组合过滤器
@app.route("/mgr/cmtgrpfilters", methods=["POST"])
def cmd_commit_group_filters():
bSucc, json_data = parse_data()
if not bSucc:
return pack_rsp(json_data)
bSucc, usrInfo = check_auth()
if not bSucc:
return pack_rsp(usrInfo)
grpid = get_param(json_data, "groupid")
filters = get_param(json_data, "filters", type=dict)
if not self.__data_mgr__.has_group(grpid):
ret = {
"result":-1,
"message":"组合不存在"
}
else:
try:
self.__data_mgr__.set_group_filters(grpid, filters)
ret = {
"result":0,
"message":"Ok"
}
except:
ret = {
"result":-1,
"message":"过滤器保存失败"
}
return pack_rsp(ret)
def __run_impl__(self, port:int, host:str):
self._dog.run()
self.push_svr.run(port = port, host = host)
def run(self, port:int = 8080, host="0.0.0.0", bSync:bool = True):
if bSync:
self.__run_impl__(port, host)
else:
import threading
self.worker = threading.Thread(target=self.__run_impl__, args=(port,host,))
self.worker.setDaemon(True)
self.worker.start()
def init_logging(self):
pass
def on_start(self, grpid:str):
if self.__data_mgr__.has_group(grpid):
self.push_svr.notifyGrpEvt(grpid, 'start')
def on_stop(self, grpid:str):
if self.__data_mgr__.has_group(grpid):
self.push_svr.notifyGrpEvt(grpid, 'stop')
def on_output(self, grpid:str, tag:str, time:int, message:str):
if self.__data_mgr__.has_group(grpid):
self.push_svr.notifyGrpLog(grpid, tag, time, message)
def on_order(self, grpid:str, chnl:str, ordInfo:dict):
self.push_svr.notifyGrpChnlEvt(grpid, chnl, 'order', ordInfo)
def on_trade(self, grpid:str, chnl:str, trdInfo:dict):
self.push_svr.notifyGrpChnlEvt(grpid, chnl, 'trade', trdInfo)
def on_notify(self, grpid:str, chnl:str, message:str):
self.push_svr.notifyGrpChnlEvt(grpid, chnl, 'notify', message)
================================================
FILE: wtpy/monitor/__init__.py
================================================
'''
Descripttion: Automatically generated file comment
version:
Author: Wesley
Date: 2020-08-25 15:38:28
LastEditors: Wesley
LastEditTime: 2021-08-16 17:29:42
'''
from .WtMonSvr import WtMonSvr
from .WtBtMon import WtBtMon
from .WtLogger import WtLogger
__all__ = ["WtMonSvr","WtBtMon","WtLogger"]
================================================
FILE: wtpy/monitor/static/__init__.py
================================================
================================================
FILE: wtpy/monitor/static/console/__init__.py
================================================
================================================
FILE: wtpy/monitor/static/console/index.html
================================================
策略控制台
================================================
FILE: wtpy/monitor/static/console/static/__init__.py
================================================
================================================
FILE: wtpy/monitor/static/console/static/css/__init__.py
================================================
================================================
FILE: wtpy/monitor/static/console/static/css/app.7b6729291f05cec91b99cfd44c17df6b.css
================================================
#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;height:100vh;width:100%}body{margin:0}.el-main{padding:4px!important}.box{border:1px solid #e4e7ed;margin:4px;border-radius:4px}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track-piece{background-color:#f1f1f1;border-radius:2px}::-webkit-scrollbar-thumb:vertical{height:6px;background-color:#ccc;border-radius:2px}::-webkit-scrollbar-thumb:horizontal{width:6px;background-color:#ccc;border-radius:2px}.el-table td,.el-table th{padding:4px 0}.text-danger{color:#f56c6c;font-weight:700}.text-warning{color:#e6a23c;font-weight:700}.text-success{color:#67c23a;font-weight:700}.text-info{color:#909399;font-weight:700}[data-v-02eab36d]{margin:0}.el-input__inner[data-v-02eab36d]{border-radius:10px!important;height:35px!important;line-height:35px!important;font-size:12px}.el-button[data-v-02eab36d]{width:100%;border-radius:10px;height:35px!important;line-height:35px!important;padding:0}.el-checkbox__label[data-v-02eab36d]{font-size:12px;color:#a7aab2;padding-left:5px}.loginBox[data-v-02eab36d]{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:100vh;width:100%}.login[data-v-02eab36d]{width:240px;height:400px;text-align:center}.logo[data-v-02eab36d]{width:100px;margin:0 auto}.log img[data-v-02eab36d]{width:100%}.loginForm[data-v-02eab36d]{margin-top:30px}.loginForm div[data-v-02eab36d]{width:100%;margin:5px auto}#index[data-v-ce9460cc]{height:100%;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.statusbar[data-v-ce9460cc]{height:36px!important}.row[data-v-ce9460cc],.statusbar[data-v-ce9460cc]{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.el-menu[data-v-ce9460cc]{border-right:0 solid transparent!important}.user[data-v-ce9460cc]{font-size:16px;display:block;margin:3px 0}.button[data-v-ce9460cc]{font-size:18px;padding:4px;font-weight:700;color:#909399}.button[data-v-ce9460cc]:hover{cursor:pointer}.userhead[data-v-ce9460cc]{font-size:44px;color:#909399}.userhead[data-v-ce9460cc]:hover{cursor:pointer;color:#f56c6c}.group[data-v-ce9460cc]{background-color:#a3b8e3;padding:2px 4px;color:#2a3a57;border-radius:2px}.channel[data-v-ce9460cc]{background-color:#c2dbff;padding:2px 4px;color:#2a3a57;border-radius:2px}.title-success[data-v-ce9460cc]{background-color:#9ee379;padding:2px 4px;color:#2a3a57;border-radius:2px}.title-warning[data-v-ce9460cc]{background-color:#ffd16e;padding:2px 4px;color:#2a3a57;border-radius:2px}.time[data-v-ce9460cc]{color:#5e93fc;padding:2px 4px}.message[data-v-ce9460cc]{padding:2px 4px;color:#707070}.scroller[data-v-ce9460cc]{padding-top:5px;padding-left:5px}.scroller[data-v-ce9460cc]:hover{cursor:pointer;border:1px solid #f56c6c;padding-top:4px;padding-left:4px}.el-menu--horizontal>.el-menu-item[data-v-6717c4fd]{float:left;height:36px;line-height:36px;margin:0;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-menu-item.is-active[data-v-6717c4fd]{border-bottom:2px solid #000;color:#303133}.el-menu.el-menu--horizontal[data-v-6717c4fd]{border-bottom:0 solid #e6e6e6}.el-header[data-v-6717c4fd]{border-bottom:1px solid #e6e6e6}.dialog-group .el-row[data-v-6717c4fd]{margin:4px 8px;padding:4px 0;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;vertical-align:middle;height:36px}.simtab[data-v-7f597c06]{padding:0 20px;height:100%;line-height:39px;border:1px solid #e4e7ed;border-bottom:none;border-top-left-radius:4px;border-top-right-radius:4px;min-width:56px;font-size:14px}.el-textarea__inner[data-v-7f597c06]{height:100%!important}.el-select[data-v-7f597c06]{width:120px}.el-header[data-v-d91c86fe]{padding:0 4px}.table[data-v-d91c86fe]{width:100%;height:100%}.el-select[data-v-d91c86fe]{width:120px}.el-header[data-v-b10ba514]{padding:0 4px}.table[data-v-b10ba514]{width:100%;height:100%}.el-select[data-v-b10ba514]{width:120px}.el-header[data-v-4147835e]{padding:0 4px}.table[data-v-4147835e]{width:100%;height:100%}.filter[data-v-4147835e]{font-size:24px;font-weight:700}.filter-row[data-v-4147835e]{padding-top:4px;padding-bottom:4px}.filter-channel[data-v-4147835e],.filter-code[data-v-4147835e],.filter-strategy[data-v-4147835e]{padding-left:4px}.filter-pane[data-v-4147835e]{-webkit-box-flex:1;-ms-flex:1;flex:1;margin:2px}.delete-btn[data-v-4147835e]{float:right;color:#ff4949}.delete-btn i[data-v-4147835e]{color:#ff4949}.CodeMirror{height:100%!important;font-family:monospace;height:300px;color:#000;direction:ltr}.CodeMirror-lines{padding:4px 0}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:transparent}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor-mark{background-color:rgba(20,255,20,.5)}.cm-animate-fat-cursor,.cm-fat-cursor-mark{-webkit-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite}.cm-animate-fat-cursor{width:auto;border:0;background-color:#7e7}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-type,.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-invalidchar,.cm-s-default .cm-error{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:none;position:relative}.CodeMirror-sizer{position:relative;border-right:50px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none;outline:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:none}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-webkit-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:""}span.CodeMirror-selectedtext{background:none}.CodeMirror-foldmarker{color:#00f;text-shadow:#b9f 1px 1px 2px,#b9f -1px -1px 2px,#b9f 1px -1px 2px,#b9f -1px 1px 2px;font-family:arial;line-height:.3;cursor:pointer}.CodeMirror-foldgutter{width:.7em}.CodeMirror-foldgutter-folded,.CodeMirror-foldgutter-open{cursor:pointer}.CodeMirror-foldgutter-open:after{content:"\25BE"}.CodeMirror-foldgutter-folded:after{content:"\25B8"}.CodeMirror-lint-markers{width:16px}.CodeMirror-lint-tooltip{background-color:#ffd;border:1px solid #000;border-radius:4px 4px 4px 4px;color:#000;font-family:monospace;font-size:10pt;overflow:hidden;padding:2px 5px;position:fixed;white-space:pre;white-space:pre-wrap;z-index:100;max-width:600px;opacity:0;transition:opacity .4s;-moz-transition:opacity .4s;-webkit-transition:opacity .4s;-o-transition:opacity .4s;-ms-transition:opacity .4s}.CodeMirror-lint-mark{background-position:0 100%;background-repeat:repeat-x}.CodeMirror-lint-mark-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=")}.CodeMirror-lint-mark-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")}.CodeMirror-lint-marker{background-position:50%;background-repeat:no-repeat;cursor:pointer;display:inline-block;height:16px;width:16px;vertical-align:middle;position:relative}.CodeMirror-lint-message{padding-left:18px;background-position:0 0;background-repeat:no-repeat}.CodeMirror-lint-marker-warning,.CodeMirror-lint-message-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=")}.CodeMirror-lint-marker-error,.CodeMirror-lint-message-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=")}.CodeMirror-lint-marker-multiple{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");background-repeat:no-repeat;background-position:100% 100%;width:100%;height:100%}.empty-box[data-v-53f6add5]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:100vh;width:100%}.content[data-v-53f6add5]{width:240px;height:400px;text-align:center}.week-marker[data-v-4b8c701c]{-webkit-box-flex:0;-ms-flex:0;flex:0;padding-right:12px}.config-row[data-v-4b8c701c]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;padding:8px 4px}.el-row[data-v-4b8c701c]{padding:8px 4px}.task-item[data-v-4b8c701c]{width:100%;-webkit-box-flex:1;-ms-flex:1;flex:1;display:inline}.config-label[data-v-4b8c701c]{color:#606266;font-weight:500;font-size:14px}[data-v-0cbbd64e]{margin:0}.loginBox[data-v-0cbbd64e]{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:100vh;width:100%}.login[data-v-0cbbd64e]{width:240px;height:400px;text-align:center}.simtab[data-v-081057d1]{padding:0 20px;height:100%;line-height:39px;border:1px solid #e4e7ed;border-bottom:none;border-top-left-radius:4px;border-top-right-radius:4px;min-width:56px;font-size:14px}.CodeMirror[data-v-081057d1]{height:100%!important}.btopt-btn[data-v-081057d1]:hover{cursor:pointer;color:#f56c6c}.stra-item[data-v-081057d1]{padding:8px 16px;border-bottom:1px solid #e4e7ed}.stra-item[data-v-081057d1]:hover{background-color:#f5f7fa;cursor:pointer}.stra-list[data-v-081057d1]:nth-child(odd){background-color:#fafafa}.stra-list[data-v-081057d1]{padding:4px 0}.stra-title[data-v-081057d1]{font-size:16px}.stra-label[data-v-081057d1],.stra-mdd[data-v-081057d1],.stra-return[data-v-081057d1]{color:#707070;font-size:14px}.button[data-v-081057d1]{font-weight:700}.button[data-v-081057d1],.toolbar[data-v-081057d1]{padding:4px;color:#909399}.button[data-v-081057d1]:hover,.toolbar[data-v-081057d1]:hover{cursor:pointer;color:#f56c6c}.divider[data-v-081057d1]{display:block;height:1px;width:100%;margin:4px 0;background-color:#dcdfe6}.table[data-v-081057d1]{width:100%;height:100%}.el-tabs__header{margin:0}.CodeMirror-dialog{position:absolute;left:0;right:0;background:inherit;z-index:15;padding:.1em .8em;overflow:hidden;color:inherit}.CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{border:none;outline:none;background:transparent;width:20em;color:inherit;font-family:monospace}.CodeMirror-dialog button{font-size:70%}.panel[data-v-64b19f62]{-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:100%;border-right:1px solid #dcdfe6}.panel-tag[data-v-64b19f62]{text-align:center;font-weight:700}.panel-val[data-v-64b19f62]{text-align:center}.divider[data-v-64b19f62]{display:block;height:1px;width:100%;margin:4px 0;background-color:#dcdfe6}.table[data-v-42cf5d6d],.table[data-v-64b19f62]{width:100%;height:100%}.simtab[data-v-42cf5d6d]{padding:0 20px;height:100%;line-height:39px;border:1px solid #e4e7ed;border-bottom:none;border-top-left-radius:4px;border-top-right-radius:4px;min-width:56px;font-size:14px}.el-textarea__inner[data-v-42cf5d6d]{height:100%!important}.simtab[data-v-c381bfac]{padding:0 20px;height:100%;line-height:39px;border:1px solid #e4e7ed;border-bottom:none;border-top-left-radius:4px;border-top-right-radius:4px;min-width:56px;font-size:14px}.dialog-user .el-row[data-v-c381bfac]{margin:4px 8px;padding:4px 0;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;vertical-align:middle;height:36px}.el-pagination--small .arrow.disabled,.el-table--hidden,.el-table .hidden-columns,.el-table td.is-hidden>*,.el-table th.is-hidden>*{visibility:hidden}.el-input__suffix,.el-tree.is-dragging .el-tree-node__content *{pointer-events:none}.el-dropdown .el-dropdown-selfdefine:focus:active,.el-dropdown .el-dropdown-selfdefine:focus:not(.focusing),.el-message__closeBtn:focus,.el-message__content:focus,.el-popover:focus,.el-popover:focus:active,.el-popover__reference:focus:hover,.el-popover__reference:focus:not(.focusing),.el-rate:active,.el-rate:focus,.el-tooltip:focus:hover,.el-tooltip:focus:not(.focusing),.el-upload-list__item.is-success:active,.el-upload-list__item.is-success:not(.focusing):focus{outline-width:0}@font-face{font-family:element-icons;src:url(static/fonts/element-icons.535877f.woff) format("woff"),url(static/fonts/element-icons.732389d.ttf) format("truetype");font-weight:400;font-display:"auto";font-style:normal}[class*=" el-icon-"],[class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-icon-ice-cream-round:before{content:"\E6A0"}.el-icon-ice-cream-square:before{content:"\E6A3"}.el-icon-lollipop:before{content:"\E6A4"}.el-icon-potato-strips:before{content:"\E6A5"}.el-icon-milk-tea:before{content:"\E6A6"}.el-icon-ice-drink:before{content:"\E6A7"}.el-icon-ice-tea:before{content:"\E6A9"}.el-icon-coffee:before{content:"\E6AA"}.el-icon-orange:before{content:"\E6AB"}.el-icon-pear:before{content:"\E6AC"}.el-icon-apple:before{content:"\E6AD"}.el-icon-cherry:before{content:"\E6AE"}.el-icon-watermelon:before{content:"\E6AF"}.el-icon-grape:before{content:"\E6B0"}.el-icon-refrigerator:before{content:"\E6B1"}.el-icon-goblet-square-full:before{content:"\E6B2"}.el-icon-goblet-square:before{content:"\E6B3"}.el-icon-goblet-full:before{content:"\E6B4"}.el-icon-goblet:before{content:"\E6B5"}.el-icon-cold-drink:before{content:"\E6B6"}.el-icon-coffee-cup:before{content:"\E6B8"}.el-icon-water-cup:before{content:"\E6B9"}.el-icon-hot-water:before{content:"\E6BA"}.el-icon-ice-cream:before{content:"\E6BB"}.el-icon-dessert:before{content:"\E6BC"}.el-icon-sugar:before{content:"\E6BD"}.el-icon-tableware:before{content:"\E6BE"}.el-icon-burger:before{content:"\E6BF"}.el-icon-knife-fork:before{content:"\E6C1"}.el-icon-fork-spoon:before{content:"\E6C2"}.el-icon-chicken:before{content:"\E6C3"}.el-icon-food:before{content:"\E6C4"}.el-icon-dish-1:before{content:"\E6C5"}.el-icon-dish:before{content:"\E6C6"}.el-icon-moon-night:before{content:"\E6EE"}.el-icon-moon:before{content:"\E6F0"}.el-icon-cloudy-and-sunny:before{content:"\E6F1"}.el-icon-partly-cloudy:before{content:"\E6F2"}.el-icon-cloudy:before{content:"\E6F3"}.el-icon-sunny:before{content:"\E6F6"}.el-icon-sunset:before{content:"\E6F7"}.el-icon-sunrise-1:before{content:"\E6F8"}.el-icon-sunrise:before{content:"\E6F9"}.el-icon-heavy-rain:before{content:"\E6FA"}.el-icon-lightning:before{content:"\E6FB"}.el-icon-light-rain:before{content:"\E6FC"}.el-icon-wind-power:before{content:"\E6FD"}.el-icon-baseball:before{content:"\E712"}.el-icon-soccer:before{content:"\E713"}.el-icon-football:before{content:"\E715"}.el-icon-basketball:before{content:"\E716"}.el-icon-ship:before{content:"\E73F"}.el-icon-truck:before{content:"\E740"}.el-icon-bicycle:before{content:"\E741"}.el-icon-mobile-phone:before{content:"\E6D3"}.el-icon-service:before{content:"\E6D4"}.el-icon-key:before{content:"\E6E2"}.el-icon-unlock:before{content:"\E6E4"}.el-icon-lock:before{content:"\E6E5"}.el-icon-watch:before{content:"\E6FE"}.el-icon-watch-1:before{content:"\E6FF"}.el-icon-timer:before{content:"\E702"}.el-icon-alarm-clock:before{content:"\E703"}.el-icon-map-location:before{content:"\E704"}.el-icon-delete-location:before{content:"\E705"}.el-icon-add-location:before{content:"\E706"}.el-icon-location-information:before{content:"\E707"}.el-icon-location-outline:before{content:"\E708"}.el-icon-location:before{content:"\E79E"}.el-icon-place:before{content:"\E709"}.el-icon-discover:before{content:"\E70A"}.el-icon-first-aid-kit:before{content:"\E70B"}.el-icon-trophy-1:before{content:"\E70C"}.el-icon-trophy:before{content:"\E70D"}.el-icon-medal:before{content:"\E70E"}.el-icon-medal-1:before{content:"\E70F"}.el-icon-stopwatch:before{content:"\E710"}.el-icon-mic:before{content:"\E711"}.el-icon-copy-document:before{content:"\E718"}.el-icon-full-screen:before{content:"\E719"}.el-icon-switch-button:before{content:"\E71B"}.el-icon-aim:before{content:"\E71C"}.el-icon-crop:before{content:"\E71D"}.el-icon-odometer:before{content:"\E71E"}.el-icon-time:before{content:"\E71F"}.el-icon-bangzhu:before{content:"\E724"}.el-icon-close-notification:before{content:"\E726"}.el-icon-microphone:before{content:"\E727"}.el-icon-turn-off-microphone:before{content:"\E728"}.el-icon-position:before{content:"\E729"}.el-icon-postcard:before{content:"\E72A"}.el-icon-message:before{content:"\E72B"}.el-icon-chat-line-square:before{content:"\E72D"}.el-icon-chat-dot-square:before{content:"\E72E"}.el-icon-chat-dot-round:before{content:"\E72F"}.el-icon-chat-square:before{content:"\E730"}.el-icon-chat-line-round:before{content:"\E731"}.el-icon-chat-round:before{content:"\E732"}.el-icon-set-up:before{content:"\E733"}.el-icon-turn-off:before{content:"\E734"}.el-icon-open:before{content:"\E735"}.el-icon-connection:before{content:"\E736"}.el-icon-link:before{content:"\E737"}.el-icon-cpu:before{content:"\E738"}.el-icon-thumb:before{content:"\E739"}.el-icon-female:before{content:"\E73A"}.el-icon-male:before{content:"\E73B"}.el-icon-guide:before{content:"\E73C"}.el-icon-news:before{content:"\E73E"}.el-icon-price-tag:before{content:"\E744"}.el-icon-discount:before{content:"\E745"}.el-icon-wallet:before{content:"\E747"}.el-icon-coin:before{content:"\E748"}.el-icon-money:before{content:"\E749"}.el-icon-bank-card:before{content:"\E74A"}.el-icon-box:before{content:"\E74B"}.el-icon-present:before{content:"\E74C"}.el-icon-sell:before{content:"\E6D5"}.el-icon-sold-out:before{content:"\E6D6"}.el-icon-shopping-bag-2:before{content:"\E74D"}.el-icon-shopping-bag-1:before{content:"\E74E"}.el-icon-shopping-cart-2:before{content:"\E74F"}.el-icon-shopping-cart-1:before{content:"\E750"}.el-icon-shopping-cart-full:before{content:"\E751"}.el-icon-smoking:before{content:"\E752"}.el-icon-no-smoking:before{content:"\E753"}.el-icon-house:before{content:"\E754"}.el-icon-table-lamp:before{content:"\E755"}.el-icon-school:before{content:"\E756"}.el-icon-office-building:before{content:"\E757"}.el-icon-toilet-paper:before{content:"\E758"}.el-icon-notebook-2:before{content:"\E759"}.el-icon-notebook-1:before{content:"\E75A"}.el-icon-files:before{content:"\E75B"}.el-icon-collection:before{content:"\E75C"}.el-icon-receiving:before{content:"\E75D"}.el-icon-suitcase-1:before{content:"\E760"}.el-icon-suitcase:before{content:"\E761"}.el-icon-film:before{content:"\E763"}.el-icon-collection-tag:before{content:"\E765"}.el-icon-data-analysis:before{content:"\E766"}.el-icon-pie-chart:before{content:"\E767"}.el-icon-data-board:before{content:"\E768"}.el-icon-data-line:before{content:"\E76D"}.el-icon-reading:before{content:"\E769"}.el-icon-magic-stick:before{content:"\E76A"}.el-icon-coordinate:before{content:"\E76B"}.el-icon-mouse:before{content:"\E76C"}.el-icon-brush:before{content:"\E76E"}.el-icon-headset:before{content:"\E76F"}.el-icon-umbrella:before{content:"\E770"}.el-icon-scissors:before{content:"\E771"}.el-icon-mobile:before{content:"\E773"}.el-icon-attract:before{content:"\E774"}.el-icon-monitor:before{content:"\E775"}.el-icon-search:before{content:"\E778"}.el-icon-takeaway-box:before{content:"\E77A"}.el-icon-paperclip:before{content:"\E77D"}.el-icon-printer:before{content:"\E77E"}.el-icon-document-add:before{content:"\E782"}.el-icon-document:before{content:"\E785"}.el-icon-document-checked:before{content:"\E786"}.el-icon-document-copy:before{content:"\E787"}.el-icon-document-delete:before{content:"\E788"}.el-icon-document-remove:before{content:"\E789"}.el-icon-tickets:before{content:"\E78B"}.el-icon-folder-checked:before{content:"\E77F"}.el-icon-folder-delete:before{content:"\E780"}.el-icon-folder-remove:before{content:"\E781"}.el-icon-folder-add:before{content:"\E783"}.el-icon-folder-opened:before{content:"\E784"}.el-icon-folder:before{content:"\E78A"}.el-icon-edit-outline:before{content:"\E764"}.el-icon-edit:before{content:"\E78C"}.el-icon-date:before{content:"\E78E"}.el-icon-c-scale-to-original:before{content:"\E7C6"}.el-icon-view:before{content:"\E6CE"}.el-icon-loading:before{content:"\E6CF"}.el-icon-rank:before{content:"\E6D1"}.el-icon-sort-down:before{content:"\E7C4"}.el-icon-sort-up:before{content:"\E7C5"}.el-icon-sort:before{content:"\E6D2"}.el-icon-finished:before{content:"\E6CD"}.el-icon-refresh-left:before{content:"\E6C7"}.el-icon-refresh-right:before{content:"\E6C8"}.el-icon-refresh:before{content:"\E6D0"}.el-icon-video-play:before{content:"\E7C0"}.el-icon-video-pause:before{content:"\E7C1"}.el-icon-d-arrow-right:before{content:"\E6DC"}.el-icon-d-arrow-left:before{content:"\E6DD"}.el-icon-arrow-up:before{content:"\E6E1"}.el-icon-arrow-down:before{content:"\E6DF"}.el-icon-arrow-right:before{content:"\E6E0"}.el-icon-arrow-left:before{content:"\E6DE"}.el-icon-top-right:before{content:"\E6E7"}.el-icon-top-left:before{content:"\E6E8"}.el-icon-top:before{content:"\E6E6"}.el-icon-bottom:before{content:"\E6EB"}.el-icon-right:before{content:"\E6E9"}.el-icon-back:before{content:"\E6EA"}.el-icon-bottom-right:before{content:"\E6EC"}.el-icon-bottom-left:before{content:"\E6ED"}.el-icon-caret-top:before{content:"\E78F"}.el-icon-caret-bottom:before{content:"\E790"}.el-icon-caret-right:before{content:"\E791"}.el-icon-caret-left:before{content:"\E792"}.el-icon-d-caret:before{content:"\E79A"}.el-icon-share:before{content:"\E793"}.el-icon-menu:before{content:"\E798"}.el-icon-s-grid:before{content:"\E7A6"}.el-icon-s-check:before{content:"\E7A7"}.el-icon-s-data:before{content:"\E7A8"}.el-icon-s-opportunity:before{content:"\E7AA"}.el-icon-s-custom:before{content:"\E7AB"}.el-icon-s-claim:before{content:"\E7AD"}.el-icon-s-finance:before{content:"\E7AE"}.el-icon-s-comment:before{content:"\E7AF"}.el-icon-s-flag:before{content:"\E7B0"}.el-icon-s-marketing:before{content:"\E7B1"}.el-icon-s-shop:before{content:"\E7B4"}.el-icon-s-open:before{content:"\E7B5"}.el-icon-s-management:before{content:"\E7B6"}.el-icon-s-ticket:before{content:"\E7B7"}.el-icon-s-release:before{content:"\E7B8"}.el-icon-s-home:before{content:"\E7B9"}.el-icon-s-promotion:before{content:"\E7BA"}.el-icon-s-operation:before{content:"\E7BB"}.el-icon-s-unfold:before{content:"\E7BC"}.el-icon-s-fold:before{content:"\E7A9"}.el-icon-s-platform:before{content:"\E7BD"}.el-icon-s-order:before{content:"\E7BE"}.el-icon-s-cooperation:before{content:"\E7BF"}.el-icon-bell:before{content:"\E725"}.el-icon-message-solid:before{content:"\E799"}.el-icon-video-camera:before{content:"\E772"}.el-icon-video-camera-solid:before{content:"\E796"}.el-icon-camera:before{content:"\E779"}.el-icon-camera-solid:before{content:"\E79B"}.el-icon-download:before{content:"\E77C"}.el-icon-upload2:before{content:"\E77B"}.el-icon-upload:before{content:"\E7C3"}.el-icon-picture-outline-round:before{content:"\E75F"}.el-icon-picture-outline:before{content:"\E75E"}.el-icon-picture:before{content:"\E79F"}.el-icon-close:before{content:"\E6DB"}.el-icon-check:before{content:"\E6DA"}.el-icon-plus:before{content:"\E6D9"}.el-icon-minus:before{content:"\E6D8"}.el-icon-help:before{content:"\E73D"}.el-icon-s-help:before{content:"\E7B3"}.el-icon-circle-close:before{content:"\E78D"}.el-icon-circle-check:before{content:"\E720"}.el-icon-circle-plus-outline:before{content:"\E723"}.el-icon-remove-outline:before{content:"\E722"}.el-icon-zoom-out:before{content:"\E776"}.el-icon-zoom-in:before{content:"\E777"}.el-icon-error:before{content:"\E79D"}.el-icon-success:before{content:"\E79C"}.el-icon-circle-plus:before{content:"\E7A0"}.el-icon-remove:before{content:"\E7A2"}.el-icon-info:before{content:"\E7A1"}.el-icon-question:before{content:"\E7A4"}.el-icon-warning-outline:before{content:"\E6C9"}.el-icon-warning:before{content:"\E7A3"}.el-icon-goods:before{content:"\E7C2"}.el-icon-s-goods:before{content:"\E7B2"}.el-icon-star-off:before{content:"\E717"}.el-icon-star-on:before{content:"\E797"}.el-icon-more-outline:before{content:"\E6CC"}.el-icon-more:before{content:"\E794"}.el-icon-phone-outline:before{content:"\E6CB"}.el-icon-phone:before{content:"\E795"}.el-icon-user:before{content:"\E6E3"}.el-icon-user-solid:before{content:"\E7A5"}.el-icon-setting:before{content:"\E6CA"}.el-icon-s-tools:before{content:"\E7AC"}.el-icon-delete:before{content:"\E6D7"}.el-icon-delete-solid:before{content:"\E7C9"}.el-icon-eleme:before{content:"\E7C7"}.el-icon-platform-eleme:before{content:"\E7CA"}.el-icon-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@-webkit-keyframes rotating{0%{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes rotating{0%{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.el-pagination{white-space:nowrap;padding:2px 5px;color:#303133;font-weight:700}.el-pagination:after,.el-pagination:before{display:table;content:""}.el-pagination:after{clear:both}.el-pagination button,.el-pagination span:not([class*=suffix]){display:inline-block;font-size:13px;min-width:35.5px;height:28px;line-height:28px;vertical-align:top;-webkit-box-sizing:border-box;box-sizing:border-box}.el-pagination .el-input__inner{text-align:center;-moz-appearance:textfield;line-height:normal}.el-pagination .el-input__suffix{right:0;-webkit-transform:scale(.8);transform:scale(.8)}.el-pagination .el-select .el-input{width:100px;margin:0 5px}.el-pagination .el-select .el-input .el-input__inner{padding-right:25px;border-radius:3px}.el-pagination button{border:none;padding:0 6px;background:0 0}.el-pagination button:focus{outline:0}.el-pagination button:hover{color:#000}.el-pagination button:disabled{color:#c0c4cc;background-color:#fff;cursor:not-allowed}.el-pagination .btn-next,.el-pagination .btn-prev{background:50% no-repeat #fff;background-size:16px;cursor:pointer;margin:0;color:#303133}.el-pagination .btn-next .el-icon,.el-pagination .btn-prev .el-icon{display:block;font-size:12px;font-weight:700}.el-pagination .btn-prev{padding-right:12px}.el-pagination .btn-next{padding-left:12px}.el-pagination .el-pager li.disabled{color:#c0c4cc;cursor:not-allowed}.el-pager li,.el-pager li.btn-quicknext:hover,.el-pager li.btn-quickprev:hover{cursor:pointer}.el-pagination--small .btn-next,.el-pagination--small .btn-prev,.el-pagination--small .el-pager li,.el-pagination--small .el-pager li.btn-quicknext,.el-pagination--small .el-pager li.btn-quickprev,.el-pagination--small .el-pager li:last-child{border-color:transparent;font-size:12px;line-height:22px;height:22px;min-width:22px}.el-pagination--small .more:before,.el-pagination--small li.more:before{line-height:24px}.el-pagination--small button,.el-pagination--small span:not([class*=suffix]){height:22px;line-height:22px}.el-pagination--small .el-pagination__editor,.el-pagination--small .el-pagination__editor.el-input .el-input__inner{height:22px}.el-pagination__sizes{margin:0 10px 0 0;font-weight:400;color:#606266}.el-pagination__sizes .el-input .el-input__inner{font-size:13px;padding-left:8px}.el-pagination__sizes .el-input .el-input__inner:hover{border-color:#000}.el-pagination__total{margin-right:10px;font-weight:400;color:#606266}.el-pagination__jump{margin-left:24px;font-weight:400;color:#606266}.el-pagination__jump .el-input__inner{padding:0 3px}.el-pagination__rightwrapper{float:right}.el-pagination__editor{line-height:18px;padding:0 2px;height:28px;text-align:center;margin:0 2px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:3px}.el-pager,.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev{padding:0}.el-pagination__editor.el-input{width:50px}.el-pagination__editor.el-input .el-input__inner{height:28px}.el-pagination__editor .el-input__inner::-webkit-inner-spin-button,.el-pagination__editor .el-input__inner::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev,.el-pagination.is-background .el-pager li{margin:0 5px;background-color:#f4f4f5;color:#606266;min-width:30px;border-radius:2px}.el-pagination.is-background .btn-next.disabled,.el-pagination.is-background .btn-next:disabled,.el-pagination.is-background .btn-prev.disabled,.el-pagination.is-background .btn-prev:disabled,.el-pagination.is-background .el-pager li.disabled{color:#c0c4cc}.el-pagination.is-background .el-pager li:not(.disabled):hover{color:#000}.el-pagination.is-background .el-pager li:not(.disabled).active{background-color:#000;color:#fff}.el-dialog,.el-pager li{background:#fff;-webkit-box-sizing:border-box}.el-pagination.is-background.el-pagination--small .btn-next,.el-pagination.is-background.el-pagination--small .btn-prev,.el-pagination.is-background.el-pagination--small .el-pager li{margin:0 3px;min-width:22px}.el-pager,.el-pager li{vertical-align:top;margin:0;display:inline-block}.el-pager{-ms-user-select:none;user-select:none;list-style:none;font-size:0}.el-date-table,.el-pager,.el-table th{-webkit-user-select:none;-moz-user-select:none}.el-pager .more:before{line-height:30px}.el-pager li{padding:0 4px;font-size:13px;min-width:35.5px;height:28px;line-height:28px;-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center}.el-menu--collapse .el-menu .el-submenu,.el-menu--popup{min-width:200px}.el-pager li.btn-quicknext,.el-pager li.btn-quickprev{line-height:28px;color:#303133}.el-pager li.btn-quicknext.disabled,.el-pager li.btn-quickprev.disabled{color:#c0c4cc}.el-pager li.active+li{border-left:0}.el-pager li:hover{color:#000}.el-pager li.active{color:#000;cursor:default}@-webkit-keyframes v-modal-in{0%{opacity:0}}@-webkit-keyframes v-modal-out{to{opacity:0}}.el-dialog{position:relative;margin:0 auto 50px;border-radius:2px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.3);box-shadow:0 1px 3px rgba(0,0,0,.3);-webkit-box-sizing:border-box;box-sizing:border-box;width:50%}.el-dialog.is-fullscreen{width:100%;margin-top:0;margin-bottom:0;height:100%;overflow:auto}.el-dialog__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;margin:0}.el-dialog__header{padding:20px 20px 10px}.el-dialog__headerbtn{position:absolute;top:20px;right:20px;padding:0;background:0 0;border:none;outline:0;cursor:pointer;font-size:16px}.el-dialog__headerbtn .el-dialog__close{color:#909399}.el-dialog__headerbtn:focus .el-dialog__close,.el-dialog__headerbtn:hover .el-dialog__close{color:#000}.el-dialog__title{line-height:24px;font-size:18px;color:#303133}.el-dialog__body{padding:30px 20px;color:#606266;font-size:14px;word-break:break-all}.el-dialog__footer{padding:10px 20px 20px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.el-dialog--center{text-align:center}.el-dialog--center .el-dialog__body{text-align:initial;padding:25px 25px 30px}.el-dialog--center .el-dialog__footer{text-align:inherit}.dialog-fade-enter-active{-webkit-animation:dialog-fade-in .3s;animation:dialog-fade-in .3s}.dialog-fade-leave-active{-webkit-animation:dialog-fade-out .3s;animation:dialog-fade-out .3s}@-webkit-keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@-webkit-keyframes dialog-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes dialog-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-autocomplete{position:relative;display:inline-block}.el-autocomplete-suggestion{margin:5px 0;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:4px;border:1px solid #e4e7ed;-webkit-box-sizing:border-box;box-sizing:border-box;background-color:#fff}.el-dropdown-menu,.el-menu--collapse .el-submenu .el-menu{z-index:10;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-autocomplete-suggestion__wrap{max-height:280px;padding:10px 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-autocomplete-suggestion__list{margin:0;padding:0}.el-autocomplete-suggestion li{padding:0 20px;margin:0;line-height:34px;cursor:pointer;color:#606266;font-size:14px;list-style:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.el-autocomplete-suggestion li.highlighted,.el-autocomplete-suggestion li:hover{background-color:#f5f7fa}.el-autocomplete-suggestion li.divider{margin-top:6px;border-top:1px solid #000}.el-autocomplete-suggestion li.divider:last-child{margin-bottom:-6px}.el-autocomplete-suggestion.is-loading li{text-align:center;height:100px;line-height:100px;font-size:20px;color:#999}.el-autocomplete-suggestion.is-loading li:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-autocomplete-suggestion.is-loading li:hover{background-color:#fff}.el-autocomplete-suggestion.is-loading .el-icon-loading{vertical-align:middle}.el-dropdown{display:inline-block;position:relative;color:#606266;font-size:14px}.el-dropdown .el-button-group{display:block}.el-dropdown .el-button-group .el-button{float:none}.el-dropdown .el-dropdown__caret-button{padding-left:5px;padding-right:5px;position:relative;border-left:none}.el-dropdown .el-dropdown__caret-button:before{content:"";position:absolute;display:block;width:1px;top:5px;bottom:5px;left:0;background:hsla(0,0%,100%,.5)}.el-dropdown .el-dropdown__caret-button.el-button--default:before{background:rgba(220,223,230,.5)}.el-dropdown .el-dropdown__caret-button:hover:before{top:0;bottom:0}.el-dropdown .el-dropdown__caret-button .el-dropdown__icon{padding-left:0}.el-dropdown__icon{font-size:12px;margin:0 3px}.el-dropdown-menu{position:absolute;top:0;left:0;padding:10px 0;margin:5px 0;background-color:#fff;border:1px solid #ebeef5;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-dropdown-menu__item{list-style:none;line-height:36px;padding:0 20px;margin:0;font-size:14px;color:#606266;cursor:pointer;outline:0}.el-dropdown-menu__item:focus,.el-dropdown-menu__item:not(.is-disabled):hover{background-color:#e6e6e6;color:#333}.el-dropdown-menu__item i{margin-right:5px}.el-dropdown-menu__item--divided{position:relative;margin-top:6px;border-top:1px solid #ebeef5}.el-dropdown-menu__item--divided:before{content:"";height:6px;display:block;margin:0 -20px;background-color:#fff}.el-dropdown-menu__item.is-disabled{cursor:default;color:#bbb;pointer-events:none}.el-dropdown-menu--medium{padding:6px 0}.el-dropdown-menu--medium .el-dropdown-menu__item{line-height:30px;padding:0 17px;font-size:14px}.el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:6px}.el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:6px;margin:0 -17px}.el-dropdown-menu--small{padding:6px 0}.el-dropdown-menu--small .el-dropdown-menu__item{line-height:27px;padding:0 15px;font-size:13px}.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:4px}.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:4px;margin:0 -15px}.el-dropdown-menu--mini{padding:3px 0}.el-dropdown-menu--mini .el-dropdown-menu__item{line-height:24px;padding:0 10px;font-size:12px}.el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:3px}.el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:3px;margin:0 -10px}.el-menu{border-right:1px solid #e6e6e6;list-style:none;position:relative;margin:0;padding-left:0}.el-menu,.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover,.el-menu--horizontal>.el-submenu .el-submenu__title:hover{background-color:#fff}.el-menu:after,.el-menu:before{display:table;content:""}.el-menu:after{clear:both}.el-menu.el-menu--horizontal{border-bottom:1px solid #e6e6e6}.el-menu--horizontal{border-right:none}.el-menu--horizontal>.el-menu-item{float:left;height:60px;line-height:60px;margin:0;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-menu-item a,.el-menu--horizontal>.el-menu-item a:hover{color:inherit}.el-menu--horizontal>.el-submenu{float:left}.el-menu--horizontal>.el-submenu:focus,.el-menu--horizontal>.el-submenu:hover{outline:0}.el-menu--horizontal>.el-submenu:focus .el-submenu__title,.el-menu--horizontal>.el-submenu:hover .el-submenu__title{color:#303133}.el-menu--horizontal>.el-submenu.is-active .el-submenu__title{border-bottom:2px solid #000;color:#303133}.el-menu--horizontal>.el-submenu .el-submenu__title{height:60px;line-height:60px;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-submenu .el-submenu__icon-arrow{position:static;vertical-align:middle;margin-left:8px;margin-top:-3px}.el-menu--horizontal .el-menu .el-menu-item,.el-menu--horizontal .el-menu .el-submenu__title{background-color:#fff;float:none;height:36px;line-height:36px;padding:0 10px;color:#909399}.el-menu--horizontal .el-menu .el-menu-item.is-active,.el-menu--horizontal .el-menu .el-submenu.is-active>.el-submenu__title{color:#303133}.el-menu--horizontal .el-menu-item:not(.is-disabled):focus,.el-menu--horizontal .el-menu-item:not(.is-disabled):hover{outline:0;color:#303133}.el-menu--horizontal>.el-menu-item.is-active{border-bottom:2px solid #000;color:#303133}.el-menu--collapse{width:64px}.el-menu--collapse>.el-menu-item [class^=el-icon-],.el-menu--collapse>.el-submenu>.el-submenu__title [class^=el-icon-]{margin:0;vertical-align:middle;width:24px;text-align:center}.el-menu--collapse>.el-menu-item .el-submenu__icon-arrow,.el-menu--collapse>.el-submenu>.el-submenu__title .el-submenu__icon-arrow{display:none}.el-menu--collapse>.el-menu-item span,.el-menu--collapse>.el-submenu>.el-submenu__title span{height:0;width:0;overflow:hidden;visibility:hidden;display:inline-block}.el-menu--collapse>.el-menu-item.is-active i{color:inherit}.el-menu--collapse .el-submenu{position:relative}.el-menu--collapse .el-submenu .el-menu{position:absolute;margin-left:5px;top:0;left:100%;border:1px solid #e4e7ed;border-radius:2px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu-item,.el-submenu__title{height:56px;line-height:56px;position:relative;-webkit-box-sizing:border-box;white-space:nowrap;list-style:none}.el-menu--collapse .el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:none;transform:none}.el-menu--popup{z-index:100;border:none;padding:5px 0;border-radius:2px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu--popup-bottom-start{margin-top:5px}.el-menu--popup-right-start{margin-left:5px;margin-right:5px}.el-menu-item{font-size:14px;color:#303133;padding:0 20px;cursor:pointer;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;-webkit-box-sizing:border-box;box-sizing:border-box}.el-menu-item *{vertical-align:middle}.el-menu-item i{color:#909399}.el-menu-item:focus,.el-menu-item:hover{outline:0;background-color:#e6e6e6}.el-menu-item.is-disabled{opacity:.25;cursor:not-allowed;background:0 0!important}.el-menu-item [class^=el-icon-]{margin-right:5px;width:24px;text-align:center;font-size:18px;vertical-align:middle}.el-menu-item.is-active{color:#000}.el-menu-item.is-active i{color:inherit}.el-submenu{list-style:none;margin:0;padding-left:0}.el-submenu__title{font-size:14px;color:#303133;padding:0 20px;cursor:pointer;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;-webkit-box-sizing:border-box;box-sizing:border-box}.el-submenu__title *{vertical-align:middle}.el-submenu__title i{color:#909399}.el-submenu__title:focus,.el-submenu__title:hover{outline:0;background-color:#e6e6e6}.el-submenu__title.is-disabled{opacity:.25;cursor:not-allowed;background:0 0!important}.el-submenu__title:hover{background-color:#e6e6e6}.el-submenu .el-menu{border:none}.el-submenu .el-menu-item{height:50px;line-height:50px;padding:0 45px;min-width:200px}.el-submenu__icon-arrow{position:absolute;top:50%;right:20px;margin-top:-7px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:12px}.el-submenu.is-active .el-submenu__title{border-bottom-color:#000}.el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.el-submenu.is-disabled .el-menu-item,.el-submenu.is-disabled .el-submenu__title{opacity:.25;cursor:not-allowed;background:0 0!important}.el-submenu [class^=el-icon-]{vertical-align:middle;margin-right:5px;width:24px;text-align:center;font-size:18px}.el-menu-item-group>ul{padding:0}.el-menu-item-group__title{padding:7px 0 7px 20px;line-height:normal;font-size:12px;color:#909399}.el-radio-button__inner,.el-radio-group{display:inline-block;line-height:1;vertical-align:middle}.horizontal-collapse-transition .el-submenu__title .el-submenu__icon-arrow{-webkit-transition:.2s;transition:.2s;opacity:0}.el-radio-group{font-size:0}.el-radio-button{position:relative;display:inline-block;outline:0}.el-radio-button__inner{white-space:nowrap;background:#fff;border:1px solid #dcdfe6;font-weight:500;border-left:0;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;position:relative;cursor:pointer;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.el-radio-button__inner.is-round{padding:12px 20px}.el-radio-button__inner:hover{color:#000}.el-radio-button__inner [class*=el-icon-]{line-height:.9}.el-radio-button__inner [class*=el-icon-]+span{margin-left:5px}.el-radio-button:first-child .el-radio-button__inner{border-left:1px solid #dcdfe6;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.el-radio-button__orig-radio{opacity:0;outline:0;position:absolute;z-index:-1}.el-radio-button__orig-radio:checked+.el-radio-button__inner{color:#fff;background-color:#000;border-color:#000;-webkit-box-shadow:-1px 0 0 0 #000;box-shadow:-1px 0 0 0 #000}.el-radio-button__orig-radio:disabled+.el-radio-button__inner{color:#c0c4cc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#ebeef5;-webkit-box-shadow:none;box-shadow:none}.el-radio-button__orig-radio:disabled:checked+.el-radio-button__inner{background-color:#f2f6fc}.el-radio-button:last-child .el-radio-button__inner{border-radius:0 4px 4px 0}.el-popover,.el-radio-button:first-child:last-child .el-radio-button__inner{border-radius:4px}.el-radio-button--medium .el-radio-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-radio-button--medium .el-radio-button__inner.is-round{padding:10px 20px}.el-radio-button--small .el-radio-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-radio-button--small .el-radio-button__inner.is-round{padding:9px 15px}.el-radio-button--mini .el-radio-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-radio-button--mini .el-radio-button__inner.is-round{padding:7px 15px}.el-radio-button:focus:not(.is-focus):not(:active):not(.is-disabled){-webkit-box-shadow:0 0 2px 2px #000;box-shadow:0 0 2px 2px #000}.el-switch{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative;font-size:14px;line-height:20px;height:20px;vertical-align:middle}.el-switch__core,.el-switch__label{display:inline-block;cursor:pointer}.el-switch.is-disabled .el-switch__core,.el-switch.is-disabled .el-switch__label{cursor:not-allowed}.el-switch__label{-webkit-transition:.2s;transition:.2s;height:20px;font-size:14px;font-weight:500;vertical-align:middle;color:#303133}.el-switch__label.is-active{color:#000}.el-switch__label--left{margin-right:10px}.el-switch__label--right{margin-left:10px}.el-switch__label *{line-height:1;font-size:14px;display:inline-block}.el-switch__input{position:absolute;width:0;height:0;opacity:0;margin:0}.el-switch__core{margin:0;position:relative;width:40px;height:20px;border:1px solid #dcdfe6;outline:0;border-radius:10px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#dcdfe6;-webkit-transition:border-color .3s,background-color .3s;transition:border-color .3s,background-color .3s;vertical-align:middle}.el-switch__core:after{content:"";position:absolute;top:1px;left:1px;border-radius:100%;-webkit-transition:all .3s;transition:all .3s;width:16px;height:16px;background-color:#fff}.el-switch.is-checked .el-switch__core{border-color:#000;background-color:#000}.el-switch.is-checked .el-switch__core:after{left:100%;margin-left:-17px}.el-switch.is-disabled{opacity:.6}.el-switch--wide .el-switch__label.el-switch__label--left span{left:10px}.el-switch--wide .el-switch__label.el-switch__label--right span{right:10px}.el-switch .label-fade-enter,.el-switch .label-fade-leave-active{opacity:0}.el-select-dropdown{position:absolute;z-index:1001;border:1px solid #e4e7ed;border-radius:4px;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:5px 0}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected{color:#000;background-color:#fff}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover{background-color:#f5f7fa}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected:after{position:absolute;right:20px;font-family:element-icons;content:"\E6DA";font-size:12px;font-weight:700;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list{padding:0}.el-select-dropdown__empty{padding:10px 0;margin:0;text-align:center;color:#999;font-size:14px}.el-select-dropdown__wrap{max-height:274px}.el-select-dropdown__list{list-style:none;padding:6px 0;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-select-dropdown__item{font-size:14px;padding:0 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#606266;height:34px;line-height:34px;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer}.el-select-dropdown__item.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-select-dropdown__item.is-disabled:hover{background-color:#fff}.el-select-dropdown__item.hover,.el-select-dropdown__item:hover{background-color:#f5f7fa}.el-select-dropdown__item.selected{color:#000;font-weight:700}.el-select-group{margin:0;padding:0}.el-select-group__wrap{position:relative;list-style:none;margin:0;padding:0}.el-select-group__wrap:not(:last-of-type){padding-bottom:24px}.el-select-group__wrap:not(:last-of-type):after{content:"";position:absolute;display:block;left:20px;right:20px;bottom:12px;height:1px;background:#e4e7ed}.el-select-group__title{padding-left:20px;font-size:12px;color:#909399;line-height:30px}.el-select-group .el-select-dropdown__item{padding-left:20px}.el-select{display:inline-block;position:relative}.el-select .el-select__tags>span{display:contents}.el-select:hover .el-input__inner{border-color:#c0c4cc}.el-select .el-input__inner{cursor:pointer;padding-right:35px}.el-select .el-input__inner:focus{border-color:#000}.el-select .el-input .el-select__caret{color:#c0c4cc;font-size:14px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;-webkit-transform:rotate(180deg);transform:rotate(180deg);cursor:pointer}.el-select .el-input .el-select__caret.is-reverse{-webkit-transform:rotate(0);transform:rotate(0)}.el-select .el-input .el-select__caret.is-show-close{font-size:14px;text-align:center;-webkit-transform:rotate(180deg);transform:rotate(180deg);border-radius:100%;color:#c0c4cc;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-select .el-input .el-select__caret.is-show-close:hover{color:#909399}.el-select .el-input.is-disabled .el-input__inner{cursor:not-allowed}.el-select .el-input.is-disabled .el-input__inner:hover{border-color:#e4e7ed}.el-select .el-input.is-focus .el-input__inner{border-color:#000}.el-select>.el-input{display:block}.el-select__input{border:none;outline:0;padding:0;margin-left:15px;color:#666;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:28px;background-color:transparent}.el-select__input.is-mini{height:14px}.el-select__close{cursor:pointer;position:absolute;top:8px;z-index:1000;right:25px;color:#c0c4cc;line-height:18px;font-size:14px}.el-select__close:hover{color:#909399}.el-select__tags{position:absolute;line-height:normal;white-space:normal;z-index:1;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-select .el-tag__close{margin-top:-2px}.el-select .el-tag{-webkit-box-sizing:border-box;box-sizing:border-box;border-color:transparent;margin:2px 0 2px 6px;background-color:#f0f2f5}.el-select .el-tag__close.el-icon-close{background-color:#c0c4cc;right:-7px;top:0;color:#fff}.el-select .el-tag__close.el-icon-close:hover{background-color:#909399}.el-table,.el-table__expanded-cell{background-color:#fff}.el-select .el-tag__close.el-icon-close:before{display:block;-webkit-transform:translateY(.5px);transform:translateY(.5px)}.el-table{position:relative;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-flex:1;-ms-flex:1;flex:1;width:100%;max-width:100%;font-size:14px;color:#606266}.el-table--mini,.el-table--small,.el-table__expand-icon{font-size:12px}.el-table__empty-block{min-height:60px;text-align:center;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-table__empty-text{line-height:60px;width:50%;color:#909399}.el-table__expand-column .cell{padding:0;text-align:center}.el-table__expand-icon{position:relative;cursor:pointer;color:#666;-webkit-transition:-webkit-transform .2s ease-in-out;transition:-webkit-transform .2s ease-in-out;transition:transform .2s ease-in-out;transition:transform .2s ease-in-out,-webkit-transform .2s ease-in-out;height:20px}.el-table__expand-icon--expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-table__expand-icon>.el-icon{position:absolute;left:50%;top:50%;margin-left:-5px;margin-top:-5px}.el-table__expanded-cell[class*=cell]{padding:20px 50px}.el-table__expanded-cell:hover{background-color:transparent!important}.el-table__placeholder{display:inline-block;width:20px}.el-table__append-wrapper{overflow:hidden}.el-table--fit{border-right:0;border-bottom:0}.el-table--fit td.gutter,.el-table--fit th.gutter{border-right-width:1px}.el-table--scrollable-x .el-table__body-wrapper{overflow-x:auto}.el-table--scrollable-y .el-table__body-wrapper{overflow-y:auto}.el-table thead{color:#909399;font-weight:500}.el-table thead.is-group th{background:#f5f7fa}.el-table th,.el-table tr{background-color:#fff}.el-table td,.el-table th{padding:12px 0;min-width:0;-webkit-box-sizing:border-box;box-sizing:border-box;text-overflow:ellipsis;vertical-align:middle;position:relative;text-align:left}.el-table td.is-center,.el-table th.is-center{text-align:center}.el-table td.is-right,.el-table th.is-right{text-align:right}.el-table td.gutter,.el-table th.gutter{width:15px;border-right-width:0;border-bottom-width:0;padding:0}.el-table--medium td,.el-table--medium th{padding:10px 0}.el-table--small td,.el-table--small th{padding:8px 0}.el-table--mini td,.el-table--mini th{padding:6px 0}.el-table--border td:first-child .cell,.el-table--border th:first-child .cell,.el-table .cell{padding-left:10px}.el-table tr input[type=checkbox]{margin:0}.el-table td,.el-table th.is-leaf{border-bottom:1px solid #ebeef5}.el-table th.is-sortable{cursor:pointer}.el-table th{overflow:hidden;-ms-user-select:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.el-table th>.cell{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;vertical-align:middle;padding-left:10px;padding-right:10px;width:100%}.el-table th>.cell.highlight{color:#000}.el-table th.required>div:before{display:inline-block;content:"";width:8px;height:8px;border-radius:50%;background:#ff4d51;margin-right:5px;vertical-align:middle}.el-table td div{-webkit-box-sizing:border-box;box-sizing:border-box}.el-table td.gutter{width:0}.el-table .cell{-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;white-space:normal;word-break:break-all;line-height:23px;padding-right:10px}.el-table .cell.el-tooltip{white-space:nowrap;min-width:50px}.el-table--border,.el-table--group{border:1px solid #ebeef5}.el-table--border:after,.el-table--group:after,.el-table:before{content:"";position:absolute;background-color:#ebeef5;z-index:1}.el-table--border:after,.el-table--group:after{top:0;right:0;width:1px;height:100%}.el-table:before{left:0;bottom:0;width:100%;height:1px}.el-table--border{border-right:none;border-bottom:none}.el-table--border.el-loading-parent--relative{border-color:transparent}.el-table--border td,.el-table--border th,.el-table__body-wrapper .el-table--border.is-scrolling-left~.el-table__fixed{border-right:1px solid #ebeef5}.el-table--border th,.el-table--border th.gutter:last-of-type,.el-table__fixed-right-patch{border-bottom:1px solid #ebeef5}.el-table__fixed,.el-table__fixed-right{position:absolute;top:0;left:0;overflow-x:hidden;overflow-y:hidden;-webkit-box-shadow:0 0 10px rgba(0,0,0,.12);box-shadow:0 0 10px rgba(0,0,0,.12)}.el-table__fixed-right:before,.el-table__fixed:before{content:"";position:absolute;left:0;bottom:0;width:100%;height:1px;background-color:#ebeef5;z-index:4}.el-table__fixed-right-patch{position:absolute;top:-1px;right:0;background-color:#fff}.el-table__fixed-right{top:0;left:auto;right:0}.el-table__fixed-right .el-table__fixed-body-wrapper,.el-table__fixed-right .el-table__fixed-footer-wrapper,.el-table__fixed-right .el-table__fixed-header-wrapper{left:auto;right:0}.el-table__fixed-header-wrapper{position:absolute;left:0;top:0;z-index:3}.el-table__fixed-footer-wrapper{position:absolute;left:0;bottom:0;z-index:3}.el-table__fixed-footer-wrapper tbody td{border-top:1px solid #ebeef5;background-color:#f5f7fa;color:#606266}.el-table__fixed-body-wrapper{position:absolute;left:0;top:37px;overflow:hidden;z-index:3}.el-table__body-wrapper,.el-table__footer-wrapper,.el-table__header-wrapper{width:100%}.el-table__footer-wrapper{margin-top:-1px}.el-table__footer-wrapper td{border-top:1px solid #ebeef5}.el-table__body,.el-table__footer,.el-table__header{table-layout:fixed;border-collapse:separate}.el-table__footer-wrapper,.el-table__header-wrapper{overflow:hidden}.el-table__footer-wrapper tbody td,.el-table__header-wrapper tbody td{background-color:#f5f7fa;color:#606266}.el-table__body-wrapper{overflow:hidden;position:relative}.el-table__body-wrapper.is-scrolling-left~.el-table__fixed,.el-table__body-wrapper.is-scrolling-none~.el-table__fixed,.el-table__body-wrapper.is-scrolling-none~.el-table__fixed-right,.el-table__body-wrapper.is-scrolling-right~.el-table__fixed-right{-webkit-box-shadow:none;box-shadow:none}.el-picker-panel,.el-table-filter{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-table__body-wrapper .el-table--border.is-scrolling-right~.el-table__fixed-right{border-left:1px solid #ebeef5}.el-table .caret-wrapper{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:34px;width:24px;vertical-align:middle;cursor:pointer;overflow:initial;position:relative}.el-table .sort-caret{width:0;height:0;border:5px solid transparent;position:absolute;left:7px}.el-table .sort-caret.ascending{border-bottom-color:#c0c4cc;top:5px}.el-table .sort-caret.descending{border-top-color:#c0c4cc;bottom:7px}.el-table .ascending .sort-caret.ascending{border-bottom-color:#000}.el-table .descending .sort-caret.descending{border-top-color:#000}.el-table .hidden-columns{position:absolute;z-index:-1}.el-table--striped .el-table__body tr.el-table__row--striped td{background:#fafafa}.el-table--striped .el-table__body tr.el-table__row--striped.current-row td{background-color:#e6e6e6}.el-table__body tr.hover-row.current-row>td,.el-table__body tr.hover-row.el-table__row--striped.current-row>td,.el-table__body tr.hover-row.el-table__row--striped>td,.el-table__body tr.hover-row>td{background-color:#f5f7fa}.el-table__body tr.current-row>td{background-color:#e6e6e6}.el-table__column-resize-proxy{position:absolute;left:200px;top:0;bottom:0;width:0;border-left:1px solid #ebeef5;z-index:10}.el-table__column-filter-trigger{display:inline-block;line-height:34px;cursor:pointer}.el-table__column-filter-trigger i{color:#909399;font-size:12px;-webkit-transform:scale(.75);transform:scale(.75)}.el-table--enable-row-transition .el-table__body td{-webkit-transition:background-color .25s ease;transition:background-color .25s ease}.el-table--enable-row-hover .el-table__body tr:hover>td{background-color:#f5f7fa}.el-table--fluid-height .el-table__fixed,.el-table--fluid-height .el-table__fixed-right{bottom:0;overflow:hidden}.el-table [class*=el-table__row--level] .el-table__expand-icon{display:inline-block;width:20px;line-height:20px;height:20px;text-align:center;margin-right:3px}.el-table-column--selection .cell{padding-left:14px;padding-right:14px}.el-table-filter{border:1px solid #ebeef5;border-radius:2px;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:2px 0}.el-date-table td,.el-date-table td div{height:30px;-webkit-box-sizing:border-box}.el-table-filter__list{padding:5px 0;margin:0;list-style:none;min-width:100px}.el-table-filter__list-item{line-height:36px;padding:0 10px;cursor:pointer;font-size:14px}.el-table-filter__list-item:hover{background-color:#e6e6e6;color:#333}.el-table-filter__list-item.is-active{background-color:#000;color:#fff}.el-table-filter__content{min-width:100px}.el-table-filter__bottom{border-top:1px solid #ebeef5;padding:8px}.el-table-filter__bottom button{background:0 0;border:none;color:#606266;cursor:pointer;font-size:13px;padding:0 3px}.el-date-table.is-week-mode .el-date-table__row.current div,.el-date-table.is-week-mode .el-date-table__row:hover div,.el-date-table td.in-range div,.el-date-table td.in-range div:hover{background-color:#f2f6fc}.el-table-filter__bottom button:hover{color:#000}.el-table-filter__bottom button:focus{outline:0}.el-table-filter__bottom button.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-table-filter__wrap{max-height:280px}.el-table-filter__checkbox-group{padding:10px}.el-table-filter__checkbox-group label.el-checkbox{display:block;margin-right:5px;margin-bottom:8px;margin-left:5px}.el-table-filter__checkbox-group .el-checkbox:last-child{margin-bottom:0}.el-date-table{font-size:12px;-ms-user-select:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.el-date-table.is-week-mode .el-date-table__row:hover td.available:hover{color:#606266}.el-date-table.is-week-mode .el-date-table__row:hover td:first-child div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.el-date-table.is-week-mode .el-date-table__row:hover td:last-child div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.el-date-table td{width:32px;padding:4px 0;text-align:center;cursor:pointer;position:relative}.el-date-table td,.el-date-table td div{-webkit-box-sizing:border-box;box-sizing:border-box}.el-date-table td div{padding:3px 0}.el-date-table td span{width:24px;height:24px;display:block;margin:0 auto;line-height:24px;position:absolute;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);border-radius:50%}.el-date-table td.next-month,.el-date-table td.prev-month{color:#c0c4cc}.el-date-table td.today{position:relative}.el-date-table td.today span{color:#000;font-weight:700}.el-date-table td.today.end-date span,.el-date-table td.today.start-date span{color:#fff}.el-date-table td.available:hover{color:#000}.el-date-table td.current:not(.disabled) span{color:#fff;background-color:#000}.el-date-table td.end-date div,.el-date-table td.start-date div{color:#fff}.el-date-table td.end-date span,.el-date-table td.start-date span{background-color:#000}.el-date-table td.start-date div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.el-date-table td.end-date div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.el-date-table td.disabled div{background-color:#f5f7fa;opacity:1;cursor:not-allowed;color:#c0c4cc}.el-date-table td.selected div{margin-left:5px;margin-right:5px;background-color:#f2f6fc;border-radius:15px}.el-date-table td.selected div:hover{background-color:#f2f6fc}.el-date-table td.selected span{background-color:#000;color:#fff;border-radius:15px}.el-date-table td.week{font-size:80%;color:#606266}.el-month-table,.el-year-table{font-size:12px;border-collapse:collapse}.el-date-table th{padding:5px;color:#606266;font-weight:400;border-bottom:1px solid #ebeef5}.el-month-table{margin:-1px}.el-month-table td{text-align:center;padding:8px 0;cursor:pointer}.el-month-table td div{height:48px;padding:6px 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-month-table td.today .cell{color:#000;font-weight:700}.el-month-table td.today.end-date .cell,.el-month-table td.today.start-date .cell{color:#fff}.el-month-table td.disabled .cell{background-color:#f5f7fa;cursor:not-allowed;color:#c0c4cc}.el-month-table td.disabled .cell:hover{color:#c0c4cc}.el-month-table td .cell{width:60px;height:36px;display:block;line-height:36px;color:#606266;margin:0 auto;border-radius:18px}.el-month-table td .cell:hover{color:#000}.el-month-table td.in-range div,.el-month-table td.in-range div:hover{background-color:#f2f6fc}.el-month-table td.end-date div,.el-month-table td.start-date div{color:#fff}.el-month-table td.end-date .cell,.el-month-table td.start-date .cell{color:#fff;background-color:#000}.el-month-table td.start-date div{border-top-left-radius:24px;border-bottom-left-radius:24px}.el-month-table td.end-date div{border-top-right-radius:24px;border-bottom-right-radius:24px}.el-month-table td.current:not(.disabled) .cell{color:#000}.el-year-table{margin:-1px}.el-year-table .el-icon{color:#303133}.el-year-table td{text-align:center;padding:20px 3px;cursor:pointer}.el-year-table td.today .cell{color:#000;font-weight:700}.el-year-table td.disabled .cell{background-color:#f5f7fa;cursor:not-allowed;color:#c0c4cc}.el-year-table td.disabled .cell:hover{color:#c0c4cc}.el-year-table td .cell{width:48px;height:32px;display:block;line-height:32px;color:#606266;margin:0 auto}.el-year-table td .cell:hover,.el-year-table td.current:not(.disabled) .cell{color:#000}.el-date-range-picker{width:646px}.el-date-range-picker.has-sidebar{width:756px}.el-date-range-picker table{table-layout:fixed;width:100%}.el-date-range-picker .el-picker-panel__body{min-width:513px}.el-date-range-picker .el-picker-panel__content{margin:0}.el-date-range-picker__header{position:relative;text-align:center;height:28px}.el-date-range-picker__header [class*=arrow-left]{float:left}.el-date-range-picker__header [class*=arrow-right]{float:right}.el-date-range-picker__header div{font-size:16px;font-weight:500;margin-right:50px}.el-date-range-picker__content{float:left;width:50%;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:16px}.el-date-range-picker__content.is-left{border-right:1px solid #e4e4e4}.el-date-range-picker__content .el-date-range-picker__header div{margin-left:50px;margin-right:50px}.el-date-range-picker__editors-wrap{-webkit-box-sizing:border-box;box-sizing:border-box;display:table-cell}.el-date-range-picker__editors-wrap.is-right{text-align:right}.el-date-range-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.el-date-range-picker__time-header>.el-icon-arrow-right{font-size:20px;vertical-align:middle;display:table-cell;color:#303133}.el-date-range-picker__time-picker-wrap{position:relative;display:table-cell;padding:0 5px}.el-date-range-picker__time-picker-wrap .el-picker-panel{position:absolute;top:13px;right:0;z-index:1;background:#fff}.el-date-picker{width:322px}.el-date-picker.has-sidebar.has-time{width:434px}.el-date-picker.has-sidebar{width:438px}.el-date-picker.has-time .el-picker-panel__body-wrapper{position:relative}.el-date-picker .el-picker-panel__content{width:292px}.el-date-picker table{table-layout:fixed;width:100%}.el-date-picker__editor-wrap{position:relative;display:table-cell;padding:0 5px}.el-date-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.el-date-picker__header{margin:12px;text-align:center}.el-date-picker__header--bordered{margin-bottom:0;padding-bottom:12px;border-bottom:1px solid #ebeef5}.el-date-picker__header--bordered+.el-picker-panel__content{margin-top:0}.el-date-picker__header-label{font-size:16px;font-weight:500;padding:0 5px;line-height:22px;text-align:center;cursor:pointer;color:#606266}.el-date-picker__header-label.active,.el-date-picker__header-label:hover{color:#000}.el-date-picker__prev-btn{float:left}.el-date-picker__next-btn{float:right}.el-date-picker__time-wrap{padding:10px;text-align:center}.el-date-picker__time-label{float:left;cursor:pointer;line-height:30px;margin-left:10px}.time-select{margin:5px 0;min-width:0}.time-select .el-picker-panel__content{max-height:200px;margin:0}.time-select-item{padding:8px 10px;font-size:14px;line-height:20px}.time-select-item.selected:not(.disabled){color:#000;font-weight:700}.time-select-item.disabled{color:#e4e7ed;cursor:not-allowed}.time-select-item:hover{background-color:#f5f7fa;font-weight:700;cursor:pointer}.el-date-editor{position:relative;display:inline-block;text-align:left}.el-date-editor.el-input,.el-date-editor.el-input__inner{width:220px}.el-date-editor--monthrange.el-input,.el-date-editor--monthrange.el-input__inner{width:300px}.el-date-editor--daterange.el-input,.el-date-editor--daterange.el-input__inner,.el-date-editor--timerange.el-input,.el-date-editor--timerange.el-input__inner{width:350px}.el-date-editor--datetimerange.el-input,.el-date-editor--datetimerange.el-input__inner{width:400px}.el-date-editor--dates .el-input__inner{text-overflow:ellipsis;white-space:nowrap}.el-date-editor .el-icon-circle-close{cursor:pointer}.el-date-editor .el-range__icon{font-size:14px;margin-left:-5px;color:#c0c4cc;float:left;line-height:32px}.el-date-editor .el-range-input,.el-date-editor .el-range-separator{height:100%;margin:0;text-align:center;display:inline-block;font-size:14px}.el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;outline:0;padding:0;width:39%;color:#606266}.el-date-editor .el-range-input::-webkit-input-placeholder{color:#c0c4cc}.el-date-editor .el-range-input:-ms-input-placeholder{color:#c0c4cc}.el-date-editor .el-range-input::-ms-input-placeholder{color:#c0c4cc}.el-date-editor .el-range-input::-moz-placeholder{color:#c0c4cc}.el-date-editor .el-range-input::placeholder{color:#c0c4cc}.el-date-editor .el-range-separator{padding:0 5px;line-height:32px;width:5%;color:#303133}.el-date-editor .el-range__close-icon{font-size:14px;color:#c0c4cc;width:25px;display:inline-block;float:right;line-height:32px}.el-range-editor.el-input__inner{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:3px 10px}.el-range-editor .el-range-input{line-height:1}.el-range-editor.is-active,.el-range-editor.is-active:hover{border-color:#000}.el-range-editor--medium.el-input__inner{height:36px}.el-range-editor--medium .el-range-separator{line-height:28px;font-size:14px}.el-range-editor--medium .el-range-input{font-size:14px}.el-range-editor--medium .el-range__close-icon,.el-range-editor--medium .el-range__icon{line-height:28px}.el-range-editor--small.el-input__inner{height:32px}.el-range-editor--small .el-range-separator{line-height:24px;font-size:13px}.el-range-editor--small .el-range-input{font-size:13px}.el-range-editor--small .el-range__close-icon,.el-range-editor--small .el-range__icon{line-height:24px}.el-range-editor--mini.el-input__inner{height:28px}.el-range-editor--mini .el-range-separator{line-height:20px;font-size:12px}.el-range-editor--mini .el-range-input{font-size:12px}.el-range-editor--mini .el-range__close-icon,.el-range-editor--mini .el-range__icon{line-height:20px}.el-range-editor.is-disabled{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-range-editor.is-disabled:focus,.el-range-editor.is-disabled:hover{border-color:#e4e7ed}.el-range-editor.is-disabled input{background-color:#f5f7fa;color:#c0c4cc;cursor:not-allowed}.el-range-editor.is-disabled input::-webkit-input-placeholder{color:#c0c4cc}.el-range-editor.is-disabled input:-ms-input-placeholder{color:#c0c4cc}.el-range-editor.is-disabled input::-ms-input-placeholder{color:#c0c4cc}.el-range-editor.is-disabled input::-moz-placeholder{color:#c0c4cc}.el-range-editor.is-disabled input::placeholder{color:#c0c4cc}.el-range-editor.is-disabled .el-range-separator{color:#c0c4cc}.el-picker-panel{color:#606266;border:1px solid #e4e7ed;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);background:#fff;border-radius:4px;line-height:30px;margin:5px 0}.el-picker-panel,.el-popover,.el-time-panel{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-picker-panel__body-wrapper:after,.el-picker-panel__body:after{content:"";display:table;clear:both}.el-picker-panel__content{position:relative;margin:15px}.el-picker-panel__footer{border-top:1px solid #e4e4e4;padding:4px;text-align:right;background-color:#fff;position:relative;font-size:0}.el-picker-panel__shortcut{display:block;width:100%;border:0;background-color:transparent;line-height:28px;font-size:14px;color:#606266;padding-left:12px;text-align:left;outline:0;cursor:pointer}.el-picker-panel__shortcut:hover{color:#000}.el-picker-panel__shortcut.active{background-color:#e6f1fe;color:#000}.el-picker-panel__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.el-picker-panel__btn[disabled]{color:#ccc;cursor:not-allowed}.el-picker-panel__icon-btn{font-size:12px;color:#303133;border:0;background:0 0;cursor:pointer;outline:0;margin-top:8px}.el-picker-panel__icon-btn:hover{color:#000}.el-picker-panel__icon-btn.is-disabled{color:#bbb}.el-picker-panel__icon-btn.is-disabled:hover{cursor:not-allowed}.el-picker-panel__link-btn{vertical-align:middle}.el-picker-panel [slot=sidebar],.el-picker-panel__sidebar{position:absolute;top:0;bottom:0;width:110px;border-right:1px solid #e4e4e4;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;background-color:#fff;overflow:auto}.el-picker-panel [slot=sidebar]+.el-picker-panel__body,.el-picker-panel__sidebar+.el-picker-panel__body{margin-left:110px}.el-time-spinner.has-seconds .el-time-spinner__wrapper{width:33.3%}.el-time-spinner__wrapper{max-height:190px;overflow:auto;display:inline-block;width:50%;vertical-align:top;position:relative}.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default){padding-bottom:15px}.el-time-spinner__input.el-input .el-input__inner,.el-time-spinner__list{padding:0;text-align:center}.el-time-spinner__wrapper.is-arrow{-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;overflow:hidden}.el-time-spinner__wrapper.is-arrow .el-time-spinner__list{-webkit-transform:translateY(-32px);transform:translateY(-32px)}.el-time-spinner__wrapper.is-arrow .el-time-spinner__item:hover:not(.disabled):not(.active){background:#fff;cursor:default}.el-time-spinner__arrow{font-size:12px;color:#909399;position:absolute;left:0;width:100%;z-index:1;text-align:center;height:30px;line-height:30px;cursor:pointer}.el-time-spinner__arrow:hover{color:#000}.el-time-spinner__arrow.el-icon-arrow-up{top:10px}.el-time-spinner__arrow.el-icon-arrow-down{bottom:10px}.el-time-spinner__input.el-input{width:70%}.el-time-spinner__list{margin:0;list-style:none}.el-time-spinner__list:after,.el-time-spinner__list:before{content:"";display:block;width:100%;height:80px}.el-time-spinner__item{height:32px;line-height:32px;font-size:12px;color:#606266}.el-time-spinner__item:hover:not(.disabled):not(.active){background:#f5f7fa;cursor:pointer}.el-time-spinner__item.active:not(.disabled){color:#303133;font-weight:700}.el-time-spinner__item.disabled{color:#c0c4cc;cursor:not-allowed}.el-time-panel{margin:5px 0;border:1px solid #e4e7ed;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:2px;position:absolute;width:180px;left:0;z-index:1000;user-select:none;-webkit-box-sizing:content-box;box-sizing:content-box}.el-slider__button,.el-slider__button-wrapper,.el-time-panel{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-time-panel__content{font-size:0;position:relative;overflow:hidden}.el-time-panel__content:after,.el-time-panel__content:before{content:"";top:50%;position:absolute;margin-top:-15px;height:32px;z-index:-1;left:0;right:0;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;text-align:left;border-top:1px solid #e4e7ed;border-bottom:1px solid #e4e7ed}.el-time-panel__content:after{left:50%;margin-left:12%;margin-right:12%}.el-time-panel__content:before{padding-left:50%;margin-right:12%;margin-left:12%}.el-time-panel__content.has-seconds:after{left:66.66667%}.el-time-panel__content.has-seconds:before{padding-left:33.33333%}.el-time-panel__footer{border-top:1px solid #e4e4e4;padding:4px;height:36px;line-height:25px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.el-time-panel__btn{border:none;line-height:28px;padding:0 5px;margin:0 5px;cursor:pointer;background-color:transparent;outline:0;font-size:12px;color:#303133}.el-time-panel__btn.confirm{font-weight:800;color:#000}.el-time-range-picker{width:354px;overflow:visible}.el-time-range-picker__content{position:relative;text-align:center;padding:10px}.el-time-range-picker__cell{-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:4px 7px 7px;width:50%;display:inline-block}.el-time-range-picker__header{margin-bottom:5px;text-align:center;font-size:14px}.el-time-range-picker__body{border-radius:2px;border:1px solid #e4e7ed}.el-popover{position:absolute;background:#fff;min-width:150px;border:1px solid #ebeef5;padding:12px;z-index:2000;color:#606266;line-height:1.4;text-align:justify;font-size:14px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);word-break:break-all}.el-popover--plain{padding:18px 20px}.el-popover__title{color:#303133;font-size:16px;line-height:1;margin-bottom:12px}.v-modal-enter{-webkit-animation:v-modal-in .2s ease;animation:v-modal-in .2s ease}.v-modal-leave{-webkit-animation:v-modal-out .2s ease forwards;animation:v-modal-out .2s ease forwards}@keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-out{to{opacity:0}}.v-modal{position:fixed;left:0;top:0;width:100%;height:100%;opacity:.5;background:#000}.el-popup-parent--hidden{overflow:hidden}.el-message-box{display:inline-block;width:420px;padding-bottom:10px;vertical-align:middle;background-color:#fff;border-radius:4px;border:1px solid #ebeef5;font-size:18px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);text-align:left;overflow:hidden;-webkit-backface-visibility:hidden;backface-visibility:hidden}.el-message-box__wrapper{position:fixed;top:0;bottom:0;left:0;right:0;text-align:center}.el-message-box__wrapper:after{content:"";display:inline-block;height:100%;width:0;vertical-align:middle}.el-message-box__header{position:relative;padding:15px 15px 10px}.el-message-box__title{padding-left:0;margin-bottom:0;font-size:18px;line-height:1;color:#303133}.el-message-box__headerbtn{position:absolute;top:15px;right:15px;padding:0;border:none;outline:0;background:0 0;font-size:16px;cursor:pointer}.el-form-item.is-error .el-input__inner,.el-form-item.is-error .el-input__inner:focus,.el-form-item.is-error .el-textarea__inner,.el-form-item.is-error .el-textarea__inner:focus,.el-message-box__input input.invalid,.el-message-box__input input.invalid:focus{border-color:#f56c6c}.el-message-box__headerbtn .el-message-box__close{color:#909399}.el-message-box__headerbtn:focus .el-message-box__close,.el-message-box__headerbtn:hover .el-message-box__close{color:#000}.el-message-box__content{padding:10px 15px;color:#606266;font-size:14px}.el-message-box__container{position:relative}.el-message-box__input{padding-top:15px}.el-message-box__status{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);font-size:24px!important}.el-message-box__status:before{padding-left:1px}.el-message-box__status+.el-message-box__message{padding-left:36px;padding-right:12px}.el-message-box__status.el-icon-success{color:#67c23a}.el-message-box__status.el-icon-info{color:#909399}.el-message-box__status.el-icon-warning{color:#e6a23c}.el-message-box__status.el-icon-error{color:#f56c6c}.el-message-box__message{margin:0}.el-message-box__message p{margin:0;line-height:24px}.el-message-box__errormsg{color:#f56c6c;font-size:12px;min-height:18px;margin-top:2px}.el-message-box__btns{padding:5px 15px 0;text-align:right}.el-message-box__btns button:nth-child(2){margin-left:10px}.el-message-box__btns-reverse{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.el-message-box--center{padding-bottom:30px}.el-message-box--center .el-message-box__header{padding-top:30px}.el-message-box--center .el-message-box__title{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-message-box--center .el-message-box__status{position:relative;top:auto;padding-right:5px;text-align:center;-webkit-transform:translateY(-1px);transform:translateY(-1px)}.el-message-box--center .el-message-box__message{margin-left:0}.el-message-box--center .el-message-box__btns,.el-message-box--center .el-message-box__content{text-align:center}.el-message-box--center .el-message-box__content{padding-left:27px;padding-right:27px}.msgbox-fade-enter-active{-webkit-animation:msgbox-fade-in .3s;animation:msgbox-fade-in .3s}.msgbox-fade-leave-active{-webkit-animation:msgbox-fade-out .3s;animation:msgbox-fade-out .3s}@-webkit-keyframes msgbox-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes msgbox-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@-webkit-keyframes msgbox-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes msgbox-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-breadcrumb{font-size:14px;line-height:1}.el-breadcrumb:after,.el-breadcrumb:before{display:table;content:""}.el-breadcrumb:after{clear:both}.el-breadcrumb__separator{margin:0 9px;font-weight:700;color:#c0c4cc}.el-breadcrumb__separator[class*=icon]{margin:0 6px;font-weight:400}.el-breadcrumb__item{float:left}.el-breadcrumb__inner{color:#606266}.el-breadcrumb__inner.is-link,.el-breadcrumb__inner a{font-weight:700;text-decoration:none;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1);color:#303133}.el-breadcrumb__inner.is-link:hover,.el-breadcrumb__inner a:hover{color:#000;cursor:pointer}.el-breadcrumb__item:last-child .el-breadcrumb__inner,.el-breadcrumb__item:last-child .el-breadcrumb__inner:hover,.el-breadcrumb__item:last-child .el-breadcrumb__inner a,.el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover{font-weight:400;color:#606266;cursor:text}.el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none}.el-form--label-left .el-form-item__label{text-align:left}.el-form--label-top .el-form-item__label{float:none;display:inline-block;text-align:left;padding:0 0 10px}.el-form--inline .el-form-item{display:inline-block;margin-right:10px;vertical-align:top}.el-form--inline .el-form-item__label{float:none;display:inline-block}.el-form--inline .el-form-item__content{display:inline-block;vertical-align:top}.el-form--inline.el-form--label-top .el-form-item__content{display:block}.el-form-item{margin-bottom:22px}.el-form-item:after,.el-form-item:before{display:table;content:""}.el-form-item:after{clear:both}.el-form-item .el-form-item{margin-bottom:0}.el-form-item--mini.el-form-item,.el-form-item--small.el-form-item{margin-bottom:18px}.el-form-item .el-input__validateIcon{display:none}.el-form-item--medium .el-form-item__content,.el-form-item--medium .el-form-item__label{line-height:36px}.el-form-item--small .el-form-item__content,.el-form-item--small .el-form-item__label{line-height:32px}.el-form-item--small .el-form-item__error{padding-top:2px}.el-form-item--mini .el-form-item__content,.el-form-item--mini .el-form-item__label{line-height:28px}.el-form-item--mini .el-form-item__error{padding-top:1px}.el-form-item__label-wrap{float:left}.el-form-item__label-wrap .el-form-item__label{display:inline-block;float:none}.el-form-item__label{text-align:right;vertical-align:middle;float:left;font-size:14px;color:#606266;line-height:40px;padding:0 12px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-form-item__content{line-height:40px;position:relative;font-size:14px}.el-form-item__content:after,.el-form-item__content:before{display:table;content:""}.el-form-item__content:after{clear:both}.el-form-item__content .el-input-group{vertical-align:top}.el-form-item__error{color:#f56c6c;font-size:12px;line-height:1;padding-top:4px;position:absolute;top:100%;left:0}.el-form-item__error--inline{position:relative;top:auto;left:auto;display:inline-block;margin-left:10px}.el-form-item.is-required:not(.is-no-asterisk) .el-form-item__label-wrap>.el-form-item__label:before,.el-form-item.is-required:not(.is-no-asterisk)>.el-form-item__label:before{content:"*";color:#f56c6c;margin-right:4px}.el-form-item.is-error .el-input-group__append .el-input__inner,.el-form-item.is-error .el-input-group__prepend .el-input__inner{border-color:transparent}.el-form-item.is-error .el-input__validateIcon{color:#f56c6c}.el-form-item--feedback .el-input__validateIcon{display:inline-block}.el-tabs__header{padding:0;position:relative;margin:0 0 15px}.el-tabs__active-bar{position:absolute;bottom:0;left:0;height:2px;background-color:#000;z-index:1;-webkit-transition:-webkit-transform .3s cubic-bezier(.645,.045,.355,1);transition:-webkit-transform .3s cubic-bezier(.645,.045,.355,1);transition:transform .3s cubic-bezier(.645,.045,.355,1);transition:transform .3s cubic-bezier(.645,.045,.355,1),-webkit-transform .3s cubic-bezier(.645,.045,.355,1);list-style:none}.el-tabs__new-tab{float:right;border:1px solid #d3dce6;height:18px;width:18px;line-height:18px;margin:12px 0 9px 10px;border-radius:3px;text-align:center;font-size:12px;color:#d3dce6;cursor:pointer;-webkit-transition:all .15s;transition:all .15s}.el-collapse-item__arrow,.el-tabs__nav{-webkit-transition:-webkit-transform .3s}.el-tabs__new-tab .el-icon-plus{-webkit-transform:scale(.8);transform:scale(.8)}.el-tabs__new-tab:hover{color:#000}.el-tabs__nav-wrap{overflow:hidden;margin-bottom:-1px;position:relative}.el-tabs__nav-wrap:after{content:"";position:absolute;left:0;bottom:0;width:100%;height:2px;background-color:#e4e7ed;z-index:1}.el-tabs--border-card>.el-tabs__header .el-tabs__nav-wrap:after,.el-tabs--card>.el-tabs__header .el-tabs__nav-wrap:after{content:none}.el-tabs__nav-wrap.is-scrollable{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box}.el-tabs__nav-scroll{overflow:hidden}.el-tabs__nav-next,.el-tabs__nav-prev{position:absolute;cursor:pointer;line-height:44px;font-size:12px;color:#909399}.el-tabs__nav-next{right:0}.el-tabs__nav-prev{left:0}.el-tabs__nav{white-space:nowrap;position:relative;transition:-webkit-transform .3s;-webkit-transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;float:left;z-index:2}.el-tabs__nav.is-stretch{min-width:100%;display:-webkit-box;display:-ms-flexbox;display:flex}.el-tabs__nav.is-stretch>*{-webkit-box-flex:1;-ms-flex:1;flex:1;text-align:center}.el-tabs__item{padding:0 20px;height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;line-height:40px;display:inline-block;list-style:none;font-size:14px;font-weight:500;color:#303133;position:relative}.el-tabs__item:focus,.el-tabs__item:focus:active{outline:0}.el-tabs__item:focus.is-active.is-focus:not(:active){-webkit-box-shadow:0 0 2px 2px #000 inset;box-shadow:inset 0 0 2px 2px #000;border-radius:3px}.el-tabs__item .el-icon-close{border-radius:50%;text-align:center;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);margin-left:5px}.el-tabs__item .el-icon-close:before{-webkit-transform:scale(.9);transform:scale(.9);display:inline-block}.el-tabs__item .el-icon-close:hover{background-color:#c0c4cc;color:#fff}.el-tabs__item.is-active{color:#000}.el-tabs__item:hover{color:#000;cursor:pointer}.el-tabs__item.is-disabled{color:#c0c4cc;cursor:default}.el-tabs__content{overflow:hidden;position:relative}.el-tabs--card>.el-tabs__header{border-bottom:1px solid #e4e7ed}.el-tabs--card>.el-tabs__header .el-tabs__nav{border:1px solid #e4e7ed;border-bottom:none;border-radius:4px 4px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-tabs--card>.el-tabs__header .el-tabs__active-bar{display:none}.el-tabs--card>.el-tabs__header .el-tabs__item .el-icon-close{position:relative;font-size:12px;width:0;height:14px;vertical-align:middle;line-height:15px;overflow:hidden;top:-1px;right:-2px;-webkit-transform-origin:100% 50%;transform-origin:100% 50%}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable .el-icon-close,.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover .el-icon-close{width:14px}.el-tabs--card>.el-tabs__header .el-tabs__item{border-bottom:1px solid transparent;border-left:1px solid #e4e7ed;-webkit-transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1);transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1)}.el-tabs--card>.el-tabs__header .el-tabs__item:first-child{border-left:none}.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover{padding-left:13px;padding-right:13px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active{border-bottom-color:#fff}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable{padding-left:20px;padding-right:20px}.el-tabs--border-card{background:#fff;border:1px solid #dcdfe6;-webkit-box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04);box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04)}.el-tabs--border-card>.el-tabs__content{padding:15px}.el-tabs--border-card>.el-tabs__header{background-color:#f5f7fa;border-bottom:1px solid #e4e7ed;margin:0}.el-tabs--border-card>.el-tabs__header .el-tabs__item{-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);border:1px solid transparent;margin-top:-1px;color:#909399}.el-tabs--border-card>.el-tabs__header .el-tabs__item+.el-tabs__item,.el-tabs--border-card>.el-tabs__header .el-tabs__item:first-child{margin-left:-1px}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active{color:#000;background-color:#fff;border-right-color:#dcdfe6;border-left-color:#dcdfe6}.el-tabs--border-card>.el-tabs__header .el-tabs__item:not(.is-disabled):hover{color:#000}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-disabled{color:#c0c4cc}.el-tabs--border-card>.el-tabs__header .is-scrollable .el-tabs__item:first-child{margin-left:0}.el-tabs--bottom .el-tabs__item.is-bottom:nth-child(2),.el-tabs--bottom .el-tabs__item.is-top:nth-child(2),.el-tabs--top .el-tabs__item.is-bottom:nth-child(2),.el-tabs--top .el-tabs__item.is-top:nth-child(2){padding-left:0}.el-tabs--bottom .el-tabs__item.is-bottom:last-child,.el-tabs--bottom .el-tabs__item.is-top:last-child,.el-tabs--top .el-tabs__item.is-bottom:last-child,.el-tabs--top .el-tabs__item.is-top:last-child{padding-right:0}.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:nth-child(2){padding-left:20px}.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:last-child{padding-right:20px}.el-tabs--bottom .el-tabs__header.is-bottom{margin-bottom:0;margin-top:10px}.el-tabs--bottom.el-tabs--border-card .el-tabs__header.is-bottom{border-bottom:0;border-top:1px solid #dcdfe6}.el-tabs--bottom.el-tabs--border-card .el-tabs__nav-wrap.is-bottom{margin-top:-1px;margin-bottom:0}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom:not(.is-active){border:1px solid transparent}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom{margin:0 -1px -1px}.el-tabs--left,.el-tabs--right{overflow:hidden}.el-tabs--left .el-tabs__header.is-left,.el-tabs--left .el-tabs__header.is-right,.el-tabs--left .el-tabs__nav-scroll,.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__header.is-left,.el-tabs--right .el-tabs__header.is-right,.el-tabs--right .el-tabs__nav-scroll,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{height:100%}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__active-bar.is-right,.el-tabs--right .el-tabs__active-bar.is-left,.el-tabs--right .el-tabs__active-bar.is-right{top:0;bottom:auto;width:2px;height:auto}.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{margin-bottom:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{height:30px;line-height:30px;width:100%;text-align:center;cursor:pointer}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{left:auto;top:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next{right:auto;bottom:0}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__nav-wrap.is-left:after{right:0;left:auto}.el-tabs--left .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--left .el-tabs__nav-wrap.is-right.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-right.is-scrollable{padding:30px 0}.el-tabs--left .el-tabs__nav-wrap.is-left:after,.el-tabs--left .el-tabs__nav-wrap.is-right:after,.el-tabs--right .el-tabs__nav-wrap.is-left:after,.el-tabs--right .el-tabs__nav-wrap.is-right:after{height:100%;width:2px;bottom:auto;top:0}.el-tabs--left .el-tabs__nav.is-left,.el-tabs--left .el-tabs__nav.is-right,.el-tabs--right .el-tabs__nav.is-left,.el-tabs--right .el-tabs__nav.is-right{float:none}.el-tabs--left .el-tabs__item.is-left,.el-tabs--left .el-tabs__item.is-right,.el-tabs--right .el-tabs__item.is-left,.el-tabs--right .el-tabs__item.is-right{display:block}.el-tabs--left.el-tabs--card .el-tabs__active-bar.is-left,.el-tabs--right.el-tabs--card .el-tabs__active-bar.is-right{display:none}.el-tabs--left .el-tabs__header.is-left{float:left;margin-bottom:0;margin-right:10px}.el-tabs--left .el-tabs__nav-wrap.is-left{margin-right:-1px}.el-tabs--left .el-tabs__item.is-left{text-align:right}.el-tabs--left.el-tabs--card .el-tabs__item.is-left{border:1px solid #e4e7ed;border-bottom:none;border-left:none;text-align:left}.el-tabs--left.el-tabs--card .el-tabs__item.is-left:first-child{border-right:1px solid #e4e7ed;border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active{border:none;border-top:1px solid #e4e7ed;border-right:1px solid #fff}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:first-child{border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:last-child{border-bottom:none}.el-tabs--left.el-tabs--card .el-tabs__nav{border-radius:4px 0 0 4px;border-bottom:1px solid #e4e7ed;border-right:none}.el-tabs--left.el-tabs--card .el-tabs__new-tab{float:none}.el-tabs--left.el-tabs--border-card .el-tabs__header.is-left{border-right:1px solid #dfe4ed}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left{border:1px solid transparent;margin:-1px 0 -1px -1px}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left.is-active{border-color:#d1dbe5 transparent}.el-tabs--right .el-tabs__header.is-right{float:right;margin-bottom:0;margin-left:10px}.el-tabs--right .el-tabs__nav-wrap.is-right{margin-left:-1px}.el-tabs--right .el-tabs__nav-wrap.is-right:after{left:0;right:auto}.el-tabs--right .el-tabs__active-bar.is-right{left:0}.el-tabs--right.el-tabs--card .el-tabs__item.is-right{border-bottom:none;border-top:1px solid #e4e7ed}.el-tabs--right.el-tabs--card .el-tabs__item.is-right:first-child{border-left:1px solid #e4e7ed;border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active{border:none;border-top:1px solid #e4e7ed;border-left:1px solid #fff}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:first-child{border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:last-child{border-bottom:none}.el-tabs--right.el-tabs--card .el-tabs__nav{border-radius:0 4px 4px 0;border-bottom:1px solid #e4e7ed;border-left:none}.el-tabs--right.el-tabs--border-card .el-tabs__header.is-right{border-left:1px solid #dfe4ed}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right{border:1px solid transparent;margin:-1px -1px -1px 0}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right.is-active{border-color:#d1dbe5 transparent}.slideInLeft-transition,.slideInRight-transition{display:inline-block}.slideInRight-enter{-webkit-animation:slideInRight-enter .3s;animation:slideInRight-enter .3s}.slideInRight-leave{position:absolute;left:0;right:0;-webkit-animation:slideInRight-leave .3s;animation:slideInRight-leave .3s}.slideInLeft-enter{-webkit-animation:slideInLeft-enter .3s;animation:slideInLeft-enter .3s}.slideInLeft-leave{position:absolute;left:0;right:0;-webkit-animation:slideInLeft-leave .3s;animation:slideInLeft-leave .3s}@-webkit-keyframes slideInRight-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInRight-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes slideInRight-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}to{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%);opacity:0}}@keyframes slideInRight-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}to{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%);opacity:0}}@-webkit-keyframes slideInLeft-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInLeft-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes slideInLeft-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}to{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%);opacity:0}}@keyframes slideInLeft-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}to{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%);opacity:0}}.el-tree{position:relative;cursor:default;background:#fff;color:#606266}.el-tree__empty-block{position:relative;min-height:60px;text-align:center;width:100%;height:100%}.el-tree__empty-text{position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#909399;font-size:14px}.el-tree__drop-indicator{position:absolute;left:0;right:0;height:1px;background-color:#000}.el-tree-node{white-space:nowrap;outline:0}.el-tree-node:focus>.el-tree-node__content{background-color:#f5f7fa}.el-tree-node.is-drop-inner>.el-tree-node__content .el-tree-node__label{background-color:#000;color:#fff}.el-tree-node__content{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:26px;cursor:pointer}.el-tree-node__content>.el-tree-node__expand-icon{padding:6px}.el-tree-node__content>label.el-checkbox{margin-right:8px}.el-tree-node__content:hover{background-color:#f5f7fa}.el-tree.is-dragging .el-tree-node__content{cursor:move}.el-tree.is-dragging.is-drop-not-allow .el-tree-node__content{cursor:not-allowed}.el-tree-node__expand-icon{cursor:pointer;color:#c0c4cc;font-size:12px;-webkit-transform:rotate(0);transform:rotate(0);-webkit-transition:-webkit-transform .3s ease-in-out;transition:-webkit-transform .3s ease-in-out;transition:transform .3s ease-in-out;transition:transform .3s ease-in-out,-webkit-transform .3s ease-in-out}.el-tree-node__expand-icon.expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-tree-node__expand-icon.is-leaf{color:transparent;cursor:default}.el-tree-node__label{font-size:14px}.el-tree-node__loading-icon{margin-right:8px;font-size:14px;color:#c0c4cc}.el-tree-node>.el-tree-node__children{overflow:hidden;background-color:transparent}.el-tree-node.is-expanded>.el-tree-node__children{display:block}.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content{background-color:#f0f7ff}.el-alert{width:100%;padding:8px 16px;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;position:relative;background-color:#fff;overflow:hidden;opacity:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transition:opacity .2s;transition:opacity .2s}.el-alert.is-light .el-alert__closebtn{color:#c0c4cc}.el-alert.is-dark .el-alert__closebtn,.el-alert.is-dark .el-alert__description{color:#fff}.el-alert.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-alert--success.is-light{background-color:#f0f9eb;color:#67c23a}.el-alert--success.is-light .el-alert__description{color:#67c23a}.el-alert--success.is-dark{background-color:#67c23a;color:#fff}.el-alert--info.is-light{background-color:#f4f4f5;color:#909399}.el-alert--info.is-dark{background-color:#909399;color:#fff}.el-alert--info .el-alert__description{color:#909399}.el-alert--warning.is-light{background-color:#fdf6ec;color:#e6a23c}.el-alert--warning.is-light .el-alert__description{color:#e6a23c}.el-alert--warning.is-dark{background-color:#e6a23c;color:#fff}.el-alert--error.is-light{background-color:#fef0f0;color:#f56c6c}.el-alert--error.is-light .el-alert__description{color:#f56c6c}.el-alert--error.is-dark{background-color:#f56c6c;color:#fff}.el-alert__content{display:table-cell;padding:0 8px}.el-alert__icon{font-size:16px;width:16px}.el-alert__icon.is-big{font-size:28px;width:28px}.el-alert__title{font-size:13px;line-height:18px}.el-alert__title.is-bold{font-weight:700}.el-alert .el-alert__description{font-size:12px;margin:5px 0 0}.el-alert__closebtn{font-size:12px;opacity:1;position:absolute;top:12px;right:15px;cursor:pointer}.el-alert-fade-enter,.el-alert-fade-leave-active,.el-loading-fade-enter,.el-loading-fade-leave-active,.el-notification-fade-leave-active{opacity:0}.el-alert__closebtn.is-customed{font-style:normal;font-size:13px;top:9px}.el-notification{display:-webkit-box;display:-ms-flexbox;display:flex;width:330px;padding:14px 26px 14px 13px;border-radius:8px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #ebeef5;position:fixed;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;overflow:hidden}.el-notification.right{right:16px}.el-notification.left{left:16px}.el-notification__group{margin-left:13px;margin-right:8px}.el-notification__title{font-weight:700;font-size:16px;color:#303133;margin:0}.el-notification__content{font-size:14px;line-height:21px;margin:6px 0 0;color:#606266;text-align:justify}.el-notification__content p{margin:0}.el-notification__icon{height:24px;width:24px;font-size:24px}.el-notification__closeBtn{position:absolute;top:18px;right:15px;cursor:pointer;color:#909399;font-size:16px}.el-notification__closeBtn:hover{color:#606266}.el-notification .el-icon-success{color:#67c23a}.el-notification .el-icon-error{color:#f56c6c}.el-notification .el-icon-info{color:#909399}.el-notification .el-icon-warning{color:#e6a23c}.el-notification-fade-enter.right{right:0;-webkit-transform:translateX(100%);transform:translateX(100%)}.el-notification-fade-enter.left{left:0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}.el-input-number{position:relative;display:inline-block;width:180px;line-height:38px}.el-input-number .el-input{display:block}.el-input-number .el-input__inner{-webkit-appearance:none;padding-left:50px;padding-right:50px;text-align:center}.el-input-number__decrease,.el-input-number__increase{position:absolute;z-index:1;top:1px;width:40px;height:auto;text-align:center;background:#f5f7fa;color:#606266;cursor:pointer;font-size:13px}.el-input-number__decrease:hover,.el-input-number__increase:hover{color:#000}.el-input-number__decrease:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled),.el-input-number__increase:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled){border-color:#000}.el-input-number__decrease.is-disabled,.el-input-number__increase.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-input-number__increase{right:1px;border-radius:0 4px 4px 0;border-left:1px solid #dcdfe6}.el-input-number__decrease{left:1px;border-radius:4px 0 0 4px;border-right:1px solid #dcdfe6}.el-input-number.is-disabled .el-input-number__decrease,.el-input-number.is-disabled .el-input-number__increase{border-color:#e4e7ed;color:#e4e7ed}.el-input-number.is-disabled .el-input-number__decrease:hover,.el-input-number.is-disabled .el-input-number__increase:hover{color:#e4e7ed;cursor:not-allowed}.el-input-number--medium{width:200px;line-height:34px}.el-input-number--medium .el-input-number__decrease,.el-input-number--medium .el-input-number__increase{width:36px;font-size:14px}.el-input-number--medium .el-input__inner{padding-left:43px;padding-right:43px}.el-input-number--small{width:130px;line-height:30px}.el-input-number--small .el-input-number__decrease,.el-input-number--small .el-input-number__increase{width:32px;font-size:13px}.el-input-number--small .el-input-number__decrease [class*=el-icon],.el-input-number--small .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.9);transform:scale(.9)}.el-input-number--small .el-input__inner{padding-left:39px;padding-right:39px}.el-input-number--mini{width:130px;line-height:26px}.el-input-number--mini .el-input-number__decrease,.el-input-number--mini .el-input-number__increase{width:28px;font-size:12px}.el-input-number--mini .el-input-number__decrease [class*=el-icon],.el-input-number--mini .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.el-input-number--mini .el-input__inner{padding-left:35px;padding-right:35px}.el-input-number.is-without-controls .el-input__inner{padding-left:15px;padding-right:15px}.el-input-number.is-controls-right .el-input__inner{padding-left:15px;padding-right:50px}.el-input-number.is-controls-right .el-input-number__decrease,.el-input-number.is-controls-right .el-input-number__increase{height:auto;line-height:19px}.el-input-number.is-controls-right .el-input-number__decrease [class*=el-icon],.el-input-number.is-controls-right .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.el-input-number.is-controls-right .el-input-number__increase{border-radius:0 4px 0 0;border-bottom:1px solid #dcdfe6}.el-input-number.is-controls-right .el-input-number__decrease{right:1px;bottom:1px;top:auto;left:auto;border-right:none;border-left:1px solid #dcdfe6;border-radius:0 0 4px}.el-input-number.is-controls-right[class*=medium] [class*=decrease],.el-input-number.is-controls-right[class*=medium] [class*=increase]{line-height:17px}.el-input-number.is-controls-right[class*=small] [class*=decrease],.el-input-number.is-controls-right[class*=small] [class*=increase]{line-height:15px}.el-input-number.is-controls-right[class*=mini] [class*=decrease],.el-input-number.is-controls-right[class*=mini] [class*=increase]{line-height:13px}.el-tooltip__popper{position:absolute;border-radius:4px;padding:10px;z-index:2000;font-size:12px;line-height:1.2;min-width:10px;word-wrap:break-word}.el-tooltip__popper .popper__arrow,.el-tooltip__popper .popper__arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-tooltip__popper .popper__arrow{border-width:6px}.el-tooltip__popper .popper__arrow:after{content:" ";border-width:5px}.el-progress-bar__inner:after,.el-row:after,.el-row:before,.el-slider:after,.el-slider:before,.el-slider__button-wrapper:after,.el-upload-cover:after{content:""}.el-tooltip__popper[x-placement^=top]{margin-bottom:12px}.el-tooltip__popper[x-placement^=top] .popper__arrow{bottom:-6px;border-top-color:#303133;border-bottom-width:0}.el-tooltip__popper[x-placement^=top] .popper__arrow:after{bottom:1px;margin-left:-5px;border-top-color:#303133;border-bottom-width:0}.el-tooltip__popper[x-placement^=bottom]{margin-top:12px}.el-tooltip__popper[x-placement^=bottom] .popper__arrow{top:-6px;border-top-width:0;border-bottom-color:#303133}.el-tooltip__popper[x-placement^=bottom] .popper__arrow:after{top:1px;margin-left:-5px;border-top-width:0;border-bottom-color:#303133}.el-tooltip__popper[x-placement^=right]{margin-left:12px}.el-tooltip__popper[x-placement^=right] .popper__arrow{left:-6px;border-right-color:#303133;border-left-width:0}.el-tooltip__popper[x-placement^=right] .popper__arrow:after{bottom:-5px;left:1px;border-right-color:#303133;border-left-width:0}.el-tooltip__popper[x-placement^=left]{margin-right:12px}.el-tooltip__popper[x-placement^=left] .popper__arrow{right:-6px;border-right-width:0;border-left-color:#303133}.el-tooltip__popper[x-placement^=left] .popper__arrow:after{right:1px;bottom:-5px;margin-left:-5px;border-right-width:0;border-left-color:#303133}.el-tooltip__popper.is-dark{background:#303133;color:#fff}.el-tooltip__popper.is-light{background:#fff;border:1px solid #303133}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow{border-top-color:#303133}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow:after{border-top-color:#fff}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow{border-bottom-color:#303133}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow:after{border-bottom-color:#fff}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow{border-left-color:#303133}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow:after{border-left-color:#fff}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow{border-right-color:#303133}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow:after{border-right-color:#fff}.el-slider:after,.el-slider:before{display:table}.el-slider__button-wrapper .el-tooltip,.el-slider__button-wrapper:after{vertical-align:middle;display:inline-block}.el-slider:after{clear:both}.el-slider__runway{width:100%;height:6px;margin:16px 0;background-color:#e4e7ed;border-radius:3px;position:relative;cursor:pointer;vertical-align:middle}.el-slider__runway.show-input{margin-right:160px;width:auto}.el-slider__runway.disabled{cursor:default}.el-slider__runway.disabled .el-slider__bar{background-color:#c0c4cc}.el-slider__runway.disabled .el-slider__button{border-color:#c0c4cc}.el-slider__runway.disabled .el-slider__button-wrapper.dragging,.el-slider__runway.disabled .el-slider__button-wrapper.hover,.el-slider__runway.disabled .el-slider__button-wrapper:hover{cursor:not-allowed}.el-slider__runway.disabled .el-slider__button.dragging,.el-slider__runway.disabled .el-slider__button.hover,.el-slider__runway.disabled .el-slider__button:hover{-webkit-transform:scale(1);transform:scale(1);cursor:not-allowed}.el-slider__button-wrapper,.el-slider__stop{-webkit-transform:translateX(-50%);position:absolute}.el-slider__input{float:right;margin-top:3px;width:130px}.el-slider__input.el-input-number--mini{margin-top:5px}.el-slider__input.el-input-number--medium{margin-top:0}.el-slider__input.el-input-number--large{margin-top:-2px}.el-slider__bar{height:6px;background-color:#000;border-top-left-radius:3px;border-bottom-left-radius:3px;position:absolute}.el-slider__button-wrapper{height:36px;width:36px;z-index:1001;top:-15px;-webkit-transform:translateX(-50%);transform:translateX(-50%);background-color:transparent;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;line-height:normal}.el-slider__button-wrapper:after{height:100%}.el-slider__button-wrapper.hover,.el-slider__button-wrapper:hover{cursor:-webkit-grab;cursor:grab}.el-slider__button-wrapper.dragging{cursor:-webkit-grabbing;cursor:grabbing}.el-slider__button{width:16px;height:16px;border:2px solid #000;background-color:#fff;border-radius:50%;-webkit-transition:.2s;transition:.2s;user-select:none}.el-image-viewer__btn,.el-slider__button,.el-step__icon-inner{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-slider__button.dragging,.el-slider__button.hover,.el-slider__button:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.el-slider__button.hover,.el-slider__button:hover{cursor:-webkit-grab;cursor:grab}.el-slider__button.dragging{cursor:-webkit-grabbing;cursor:grabbing}.el-slider__stop{height:6px;width:6px;border-radius:100%;background-color:#fff;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.el-slider__marks{top:0;left:12px;width:18px;height:100%}.el-slider__marks-text{position:absolute;-webkit-transform:translateX(-50%);transform:translateX(-50%);font-size:14px;color:#909399;margin-top:15px}.el-slider.is-vertical{position:relative}.el-slider.is-vertical .el-slider__runway{width:6px;height:100%;margin:0 16px}.el-slider.is-vertical .el-slider__bar{width:6px;height:auto;border-radius:0 0 3px 3px}.el-slider.is-vertical .el-slider__button-wrapper{top:auto;left:-15px}.el-slider.is-vertical .el-slider__button-wrapper,.el-slider.is-vertical .el-slider__stop{-webkit-transform:translateY(50%);transform:translateY(50%)}.el-slider.is-vertical.el-slider--with-input{padding-bottom:58px}.el-slider.is-vertical.el-slider--with-input .el-slider__input{overflow:visible;float:none;position:absolute;bottom:22px;width:36px;margin-top:15px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input__inner{text-align:center;padding-left:5px;padding-right:5px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{top:32px;margin-top:-1px;border:1px solid #dcdfe6;line-height:20px;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease{width:18px;right:18px;border-bottom-left-radius:4px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{width:19px;border-bottom-right-radius:4px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase~.el-input .el-input__inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__increase{border-color:#c0c4cc}.el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__increase{border-color:#000}.el-slider.is-vertical .el-slider__marks-text{margin-top:0;left:15px;-webkit-transform:translateY(50%);transform:translateY(50%)}.el-loading-parent--relative{position:relative!important}.el-loading-parent--hidden{overflow:hidden!important}.el-loading-mask{position:absolute;z-index:2000;background-color:hsla(0,0%,100%,.9);margin:0;top:0;right:0;bottom:0;left:0;-webkit-transition:opacity .3s;transition:opacity .3s}.el-loading-mask.is-fullscreen{position:fixed}.el-loading-mask.is-fullscreen .el-loading-spinner{margin-top:-25px}.el-loading-mask.is-fullscreen .el-loading-spinner .circular{height:50px;width:50px}.el-loading-spinner{top:50%;margin-top:-21px;width:100%;text-align:center;position:absolute}.el-col-pull-0,.el-col-pull-1,.el-col-pull-2,.el-col-pull-3,.el-col-pull-4,.el-col-pull-5,.el-col-pull-6,.el-col-pull-7,.el-col-pull-8,.el-col-pull-9,.el-col-pull-10,.el-col-pull-11,.el-col-pull-13,.el-col-pull-14,.el-col-pull-15,.el-col-pull-16,.el-col-pull-17,.el-col-pull-18,.el-col-pull-19,.el-col-pull-20,.el-col-pull-21,.el-col-pull-22,.el-col-pull-23,.el-col-pull-24,.el-col-push-0,.el-col-push-1,.el-col-push-2,.el-col-push-3,.el-col-push-4,.el-col-push-5,.el-col-push-6,.el-col-push-7,.el-col-push-8,.el-col-push-9,.el-col-push-10,.el-col-push-11,.el-col-push-12,.el-col-push-13,.el-col-push-14,.el-col-push-15,.el-col-push-16,.el-col-push-17,.el-col-push-18,.el-col-push-19,.el-col-push-20,.el-col-push-21,.el-col-push-22,.el-col-push-23,.el-col-push-24,.el-row{position:relative}.el-loading-spinner .el-loading-text{color:#000;margin:3px 0;font-size:14px}.el-loading-spinner .circular{height:42px;width:42px;-webkit-animation:loading-rotate 2s linear infinite;animation:loading-rotate 2s linear infinite}.el-loading-spinner .path{-webkit-animation:loading-dash 1.5s ease-in-out infinite;animation:loading-dash 1.5s ease-in-out infinite;stroke-dasharray:90,150;stroke-dashoffset:0;stroke-width:2;stroke:#000;stroke-linecap:round}.el-loading-spinner i{color:#000}@-webkit-keyframes loading-rotate{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes loading-rotate{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}to{stroke-dasharray:90,150;stroke-dashoffset:-120px}}@keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}to{stroke-dasharray:90,150;stroke-dashoffset:-120px}}.el-row{-webkit-box-sizing:border-box;box-sizing:border-box}.el-row:after,.el-row:before{display:table}.el-row:after{clear:both}.el-row--flex{display:-webkit-box;display:-ms-flexbox;display:flex}.el-col-0,.el-row--flex:after,.el-row--flex:before{display:none}.el-row--flex.is-justify-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-row--flex.is-justify-end{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.el-row--flex.is-justify-space-between{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-row--flex.is-justify-space-around{-ms-flex-pack:distribute;justify-content:space-around}.el-row--flex.is-align-middle{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-row--flex.is-align-bottom{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}[class*=el-col-]{float:left;-webkit-box-sizing:border-box;box-sizing:border-box}.el-upload--picture-card,.el-upload-dragger{-webkit-box-sizing:border-box;cursor:pointer}.el-col-0{width:0}.el-col-offset-0{margin-left:0}.el-col-pull-0{right:0}.el-col-push-0{left:0}.el-col-1{width:4.16667%}.el-col-offset-1{margin-left:4.16667%}.el-col-pull-1{right:4.16667%}.el-col-push-1{left:4.16667%}.el-col-2{width:8.33333%}.el-col-offset-2{margin-left:8.33333%}.el-col-pull-2{right:8.33333%}.el-col-push-2{left:8.33333%}.el-col-3{width:12.5%}.el-col-offset-3{margin-left:12.5%}.el-col-pull-3{right:12.5%}.el-col-push-3{left:12.5%}.el-col-4{width:16.66667%}.el-col-offset-4{margin-left:16.66667%}.el-col-pull-4{right:16.66667%}.el-col-push-4{left:16.66667%}.el-col-5{width:20.83333%}.el-col-offset-5{margin-left:20.83333%}.el-col-pull-5{right:20.83333%}.el-col-push-5{left:20.83333%}.el-col-6{width:25%}.el-col-offset-6{margin-left:25%}.el-col-pull-6{right:25%}.el-col-push-6{left:25%}.el-col-7{width:29.16667%}.el-col-offset-7{margin-left:29.16667%}.el-col-pull-7{right:29.16667%}.el-col-push-7{left:29.16667%}.el-col-8{width:33.33333%}.el-col-offset-8{margin-left:33.33333%}.el-col-pull-8{right:33.33333%}.el-col-push-8{left:33.33333%}.el-col-9{width:37.5%}.el-col-offset-9{margin-left:37.5%}.el-col-pull-9{right:37.5%}.el-col-push-9{left:37.5%}.el-col-10{width:41.66667%}.el-col-offset-10{margin-left:41.66667%}.el-col-pull-10{right:41.66667%}.el-col-push-10{left:41.66667%}.el-col-11{width:45.83333%}.el-col-offset-11{margin-left:45.83333%}.el-col-pull-11{right:45.83333%}.el-col-push-11{left:45.83333%}.el-col-12{width:50%}.el-col-offset-12{margin-left:50%}.el-col-pull-12{position:relative;right:50%}.el-col-push-12{left:50%}.el-col-13{width:54.16667%}.el-col-offset-13{margin-left:54.16667%}.el-col-pull-13{right:54.16667%}.el-col-push-13{left:54.16667%}.el-col-14{width:58.33333%}.el-col-offset-14{margin-left:58.33333%}.el-col-pull-14{right:58.33333%}.el-col-push-14{left:58.33333%}.el-col-15{width:62.5%}.el-col-offset-15{margin-left:62.5%}.el-col-pull-15{right:62.5%}.el-col-push-15{left:62.5%}.el-col-16{width:66.66667%}.el-col-offset-16{margin-left:66.66667%}.el-col-pull-16{right:66.66667%}.el-col-push-16{left:66.66667%}.el-col-17{width:70.83333%}.el-col-offset-17{margin-left:70.83333%}.el-col-pull-17{right:70.83333%}.el-col-push-17{left:70.83333%}.el-col-18{width:75%}.el-col-offset-18{margin-left:75%}.el-col-pull-18{right:75%}.el-col-push-18{left:75%}.el-col-19{width:79.16667%}.el-col-offset-19{margin-left:79.16667%}.el-col-pull-19{right:79.16667%}.el-col-push-19{left:79.16667%}.el-col-20{width:83.33333%}.el-col-offset-20{margin-left:83.33333%}.el-col-pull-20{right:83.33333%}.el-col-push-20{left:83.33333%}.el-col-21{width:87.5%}.el-col-offset-21{margin-left:87.5%}.el-col-pull-21{right:87.5%}.el-col-push-21{left:87.5%}.el-col-22{width:91.66667%}.el-col-offset-22{margin-left:91.66667%}.el-col-pull-22{right:91.66667%}.el-col-push-22{left:91.66667%}.el-col-23{width:95.83333%}.el-col-offset-23{margin-left:95.83333%}.el-col-pull-23{right:95.83333%}.el-col-push-23{left:95.83333%}.el-col-24{width:100%}.el-col-offset-24{margin-left:100%}.el-col-pull-24{right:100%}.el-col-push-24{left:100%}@media only screen and (max-width:767px){.el-col-xs-0{display:none;width:0}.el-col-xs-offset-0{margin-left:0}.el-col-xs-pull-0{position:relative;right:0}.el-col-xs-push-0{position:relative;left:0}.el-col-xs-1{width:4.16667%}.el-col-xs-offset-1{margin-left:4.16667%}.el-col-xs-pull-1{position:relative;right:4.16667%}.el-col-xs-push-1{position:relative;left:4.16667%}.el-col-xs-2{width:8.33333%}.el-col-xs-offset-2{margin-left:8.33333%}.el-col-xs-pull-2{position:relative;right:8.33333%}.el-col-xs-push-2{position:relative;left:8.33333%}.el-col-xs-3{width:12.5%}.el-col-xs-offset-3{margin-left:12.5%}.el-col-xs-pull-3{position:relative;right:12.5%}.el-col-xs-push-3{position:relative;left:12.5%}.el-col-xs-4{width:16.66667%}.el-col-xs-offset-4{margin-left:16.66667%}.el-col-xs-pull-4{position:relative;right:16.66667%}.el-col-xs-push-4{position:relative;left:16.66667%}.el-col-xs-5{width:20.83333%}.el-col-xs-offset-5{margin-left:20.83333%}.el-col-xs-pull-5{position:relative;right:20.83333%}.el-col-xs-push-5{position:relative;left:20.83333%}.el-col-xs-6{width:25%}.el-col-xs-offset-6{margin-left:25%}.el-col-xs-pull-6{position:relative;right:25%}.el-col-xs-push-6{position:relative;left:25%}.el-col-xs-7{width:29.16667%}.el-col-xs-offset-7{margin-left:29.16667%}.el-col-xs-pull-7{position:relative;right:29.16667%}.el-col-xs-push-7{position:relative;left:29.16667%}.el-col-xs-8{width:33.33333%}.el-col-xs-offset-8{margin-left:33.33333%}.el-col-xs-pull-8{position:relative;right:33.33333%}.el-col-xs-push-8{position:relative;left:33.33333%}.el-col-xs-9{width:37.5%}.el-col-xs-offset-9{margin-left:37.5%}.el-col-xs-pull-9{position:relative;right:37.5%}.el-col-xs-push-9{position:relative;left:37.5%}.el-col-xs-10{width:41.66667%}.el-col-xs-offset-10{margin-left:41.66667%}.el-col-xs-pull-10{position:relative;right:41.66667%}.el-col-xs-push-10{position:relative;left:41.66667%}.el-col-xs-11{width:45.83333%}.el-col-xs-offset-11{margin-left:45.83333%}.el-col-xs-pull-11{position:relative;right:45.83333%}.el-col-xs-push-11{position:relative;left:45.83333%}.el-col-xs-12{width:50%}.el-col-xs-offset-12{margin-left:50%}.el-col-xs-pull-12{position:relative;right:50%}.el-col-xs-push-12{position:relative;left:50%}.el-col-xs-13{width:54.16667%}.el-col-xs-offset-13{margin-left:54.16667%}.el-col-xs-pull-13{position:relative;right:54.16667%}.el-col-xs-push-13{position:relative;left:54.16667%}.el-col-xs-14{width:58.33333%}.el-col-xs-offset-14{margin-left:58.33333%}.el-col-xs-pull-14{position:relative;right:58.33333%}.el-col-xs-push-14{position:relative;left:58.33333%}.el-col-xs-15{width:62.5%}.el-col-xs-offset-15{margin-left:62.5%}.el-col-xs-pull-15{position:relative;right:62.5%}.el-col-xs-push-15{position:relative;left:62.5%}.el-col-xs-16{width:66.66667%}.el-col-xs-offset-16{margin-left:66.66667%}.el-col-xs-pull-16{position:relative;right:66.66667%}.el-col-xs-push-16{position:relative;left:66.66667%}.el-col-xs-17{width:70.83333%}.el-col-xs-offset-17{margin-left:70.83333%}.el-col-xs-pull-17{position:relative;right:70.83333%}.el-col-xs-push-17{position:relative;left:70.83333%}.el-col-xs-18{width:75%}.el-col-xs-offset-18{margin-left:75%}.el-col-xs-pull-18{position:relative;right:75%}.el-col-xs-push-18{position:relative;left:75%}.el-col-xs-19{width:79.16667%}.el-col-xs-offset-19{margin-left:79.16667%}.el-col-xs-pull-19{position:relative;right:79.16667%}.el-col-xs-push-19{position:relative;left:79.16667%}.el-col-xs-20{width:83.33333%}.el-col-xs-offset-20{margin-left:83.33333%}.el-col-xs-pull-20{position:relative;right:83.33333%}.el-col-xs-push-20{position:relative;left:83.33333%}.el-col-xs-21{width:87.5%}.el-col-xs-offset-21{margin-left:87.5%}.el-col-xs-pull-21{position:relative;right:87.5%}.el-col-xs-push-21{position:relative;left:87.5%}.el-col-xs-22{width:91.66667%}.el-col-xs-offset-22{margin-left:91.66667%}.el-col-xs-pull-22{position:relative;right:91.66667%}.el-col-xs-push-22{position:relative;left:91.66667%}.el-col-xs-23{width:95.83333%}.el-col-xs-offset-23{margin-left:95.83333%}.el-col-xs-pull-23{position:relative;right:95.83333%}.el-col-xs-push-23{position:relative;left:95.83333%}.el-col-xs-24{width:100%}.el-col-xs-offset-24{margin-left:100%}.el-col-xs-pull-24{position:relative;right:100%}.el-col-xs-push-24{position:relative;left:100%}}@media only screen and (min-width:768px){.el-col-sm-0{display:none;width:0}.el-col-sm-offset-0{margin-left:0}.el-col-sm-pull-0{position:relative;right:0}.el-col-sm-push-0{position:relative;left:0}.el-col-sm-1{width:4.16667%}.el-col-sm-offset-1{margin-left:4.16667%}.el-col-sm-pull-1{position:relative;right:4.16667%}.el-col-sm-push-1{position:relative;left:4.16667%}.el-col-sm-2{width:8.33333%}.el-col-sm-offset-2{margin-left:8.33333%}.el-col-sm-pull-2{position:relative;right:8.33333%}.el-col-sm-push-2{position:relative;left:8.33333%}.el-col-sm-3{width:12.5%}.el-col-sm-offset-3{margin-left:12.5%}.el-col-sm-pull-3{position:relative;right:12.5%}.el-col-sm-push-3{position:relative;left:12.5%}.el-col-sm-4{width:16.66667%}.el-col-sm-offset-4{margin-left:16.66667%}.el-col-sm-pull-4{position:relative;right:16.66667%}.el-col-sm-push-4{position:relative;left:16.66667%}.el-col-sm-5{width:20.83333%}.el-col-sm-offset-5{margin-left:20.83333%}.el-col-sm-pull-5{position:relative;right:20.83333%}.el-col-sm-push-5{position:relative;left:20.83333%}.el-col-sm-6{width:25%}.el-col-sm-offset-6{margin-left:25%}.el-col-sm-pull-6{position:relative;right:25%}.el-col-sm-push-6{position:relative;left:25%}.el-col-sm-7{width:29.16667%}.el-col-sm-offset-7{margin-left:29.16667%}.el-col-sm-pull-7{position:relative;right:29.16667%}.el-col-sm-push-7{position:relative;left:29.16667%}.el-col-sm-8{width:33.33333%}.el-col-sm-offset-8{margin-left:33.33333%}.el-col-sm-pull-8{position:relative;right:33.33333%}.el-col-sm-push-8{position:relative;left:33.33333%}.el-col-sm-9{width:37.5%}.el-col-sm-offset-9{margin-left:37.5%}.el-col-sm-pull-9{position:relative;right:37.5%}.el-col-sm-push-9{position:relative;left:37.5%}.el-col-sm-10{width:41.66667%}.el-col-sm-offset-10{margin-left:41.66667%}.el-col-sm-pull-10{position:relative;right:41.66667%}.el-col-sm-push-10{position:relative;left:41.66667%}.el-col-sm-11{width:45.83333%}.el-col-sm-offset-11{margin-left:45.83333%}.el-col-sm-pull-11{position:relative;right:45.83333%}.el-col-sm-push-11{position:relative;left:45.83333%}.el-col-sm-12{width:50%}.el-col-sm-offset-12{margin-left:50%}.el-col-sm-pull-12{position:relative;right:50%}.el-col-sm-push-12{position:relative;left:50%}.el-col-sm-13{width:54.16667%}.el-col-sm-offset-13{margin-left:54.16667%}.el-col-sm-pull-13{position:relative;right:54.16667%}.el-col-sm-push-13{position:relative;left:54.16667%}.el-col-sm-14{width:58.33333%}.el-col-sm-offset-14{margin-left:58.33333%}.el-col-sm-pull-14{position:relative;right:58.33333%}.el-col-sm-push-14{position:relative;left:58.33333%}.el-col-sm-15{width:62.5%}.el-col-sm-offset-15{margin-left:62.5%}.el-col-sm-pull-15{position:relative;right:62.5%}.el-col-sm-push-15{position:relative;left:62.5%}.el-col-sm-16{width:66.66667%}.el-col-sm-offset-16{margin-left:66.66667%}.el-col-sm-pull-16{position:relative;right:66.66667%}.el-col-sm-push-16{position:relative;left:66.66667%}.el-col-sm-17{width:70.83333%}.el-col-sm-offset-17{margin-left:70.83333%}.el-col-sm-pull-17{position:relative;right:70.83333%}.el-col-sm-push-17{position:relative;left:70.83333%}.el-col-sm-18{width:75%}.el-col-sm-offset-18{margin-left:75%}.el-col-sm-pull-18{position:relative;right:75%}.el-col-sm-push-18{position:relative;left:75%}.el-col-sm-19{width:79.16667%}.el-col-sm-offset-19{margin-left:79.16667%}.el-col-sm-pull-19{position:relative;right:79.16667%}.el-col-sm-push-19{position:relative;left:79.16667%}.el-col-sm-20{width:83.33333%}.el-col-sm-offset-20{margin-left:83.33333%}.el-col-sm-pull-20{position:relative;right:83.33333%}.el-col-sm-push-20{position:relative;left:83.33333%}.el-col-sm-21{width:87.5%}.el-col-sm-offset-21{margin-left:87.5%}.el-col-sm-pull-21{position:relative;right:87.5%}.el-col-sm-push-21{position:relative;left:87.5%}.el-col-sm-22{width:91.66667%}.el-col-sm-offset-22{margin-left:91.66667%}.el-col-sm-pull-22{position:relative;right:91.66667%}.el-col-sm-push-22{position:relative;left:91.66667%}.el-col-sm-23{width:95.83333%}.el-col-sm-offset-23{margin-left:95.83333%}.el-col-sm-pull-23{position:relative;right:95.83333%}.el-col-sm-push-23{position:relative;left:95.83333%}.el-col-sm-24{width:100%}.el-col-sm-offset-24{margin-left:100%}.el-col-sm-pull-24{position:relative;right:100%}.el-col-sm-push-24{position:relative;left:100%}}@media only screen and (min-width:992px){.el-col-md-0{display:none;width:0}.el-col-md-offset-0{margin-left:0}.el-col-md-pull-0{position:relative;right:0}.el-col-md-push-0{position:relative;left:0}.el-col-md-1{width:4.16667%}.el-col-md-offset-1{margin-left:4.16667%}.el-col-md-pull-1{position:relative;right:4.16667%}.el-col-md-push-1{position:relative;left:4.16667%}.el-col-md-2{width:8.33333%}.el-col-md-offset-2{margin-left:8.33333%}.el-col-md-pull-2{position:relative;right:8.33333%}.el-col-md-push-2{position:relative;left:8.33333%}.el-col-md-3{width:12.5%}.el-col-md-offset-3{margin-left:12.5%}.el-col-md-pull-3{position:relative;right:12.5%}.el-col-md-push-3{position:relative;left:12.5%}.el-col-md-4{width:16.66667%}.el-col-md-offset-4{margin-left:16.66667%}.el-col-md-pull-4{position:relative;right:16.66667%}.el-col-md-push-4{position:relative;left:16.66667%}.el-col-md-5{width:20.83333%}.el-col-md-offset-5{margin-left:20.83333%}.el-col-md-pull-5{position:relative;right:20.83333%}.el-col-md-push-5{position:relative;left:20.83333%}.el-col-md-6{width:25%}.el-col-md-offset-6{margin-left:25%}.el-col-md-pull-6{position:relative;right:25%}.el-col-md-push-6{position:relative;left:25%}.el-col-md-7{width:29.16667%}.el-col-md-offset-7{margin-left:29.16667%}.el-col-md-pull-7{position:relative;right:29.16667%}.el-col-md-push-7{position:relative;left:29.16667%}.el-col-md-8{width:33.33333%}.el-col-md-offset-8{margin-left:33.33333%}.el-col-md-pull-8{position:relative;right:33.33333%}.el-col-md-push-8{position:relative;left:33.33333%}.el-col-md-9{width:37.5%}.el-col-md-offset-9{margin-left:37.5%}.el-col-md-pull-9{position:relative;right:37.5%}.el-col-md-push-9{position:relative;left:37.5%}.el-col-md-10{width:41.66667%}.el-col-md-offset-10{margin-left:41.66667%}.el-col-md-pull-10{position:relative;right:41.66667%}.el-col-md-push-10{position:relative;left:41.66667%}.el-col-md-11{width:45.83333%}.el-col-md-offset-11{margin-left:45.83333%}.el-col-md-pull-11{position:relative;right:45.83333%}.el-col-md-push-11{position:relative;left:45.83333%}.el-col-md-12{width:50%}.el-col-md-offset-12{margin-left:50%}.el-col-md-pull-12{position:relative;right:50%}.el-col-md-push-12{position:relative;left:50%}.el-col-md-13{width:54.16667%}.el-col-md-offset-13{margin-left:54.16667%}.el-col-md-pull-13{position:relative;right:54.16667%}.el-col-md-push-13{position:relative;left:54.16667%}.el-col-md-14{width:58.33333%}.el-col-md-offset-14{margin-left:58.33333%}.el-col-md-pull-14{position:relative;right:58.33333%}.el-col-md-push-14{position:relative;left:58.33333%}.el-col-md-15{width:62.5%}.el-col-md-offset-15{margin-left:62.5%}.el-col-md-pull-15{position:relative;right:62.5%}.el-col-md-push-15{position:relative;left:62.5%}.el-col-md-16{width:66.66667%}.el-col-md-offset-16{margin-left:66.66667%}.el-col-md-pull-16{position:relative;right:66.66667%}.el-col-md-push-16{position:relative;left:66.66667%}.el-col-md-17{width:70.83333%}.el-col-md-offset-17{margin-left:70.83333%}.el-col-md-pull-17{position:relative;right:70.83333%}.el-col-md-push-17{position:relative;left:70.83333%}.el-col-md-18{width:75%}.el-col-md-offset-18{margin-left:75%}.el-col-md-pull-18{position:relative;right:75%}.el-col-md-push-18{position:relative;left:75%}.el-col-md-19{width:79.16667%}.el-col-md-offset-19{margin-left:79.16667%}.el-col-md-pull-19{position:relative;right:79.16667%}.el-col-md-push-19{position:relative;left:79.16667%}.el-col-md-20{width:83.33333%}.el-col-md-offset-20{margin-left:83.33333%}.el-col-md-pull-20{position:relative;right:83.33333%}.el-col-md-push-20{position:relative;left:83.33333%}.el-col-md-21{width:87.5%}.el-col-md-offset-21{margin-left:87.5%}.el-col-md-pull-21{position:relative;right:87.5%}.el-col-md-push-21{position:relative;left:87.5%}.el-col-md-22{width:91.66667%}.el-col-md-offset-22{margin-left:91.66667%}.el-col-md-pull-22{position:relative;right:91.66667%}.el-col-md-push-22{position:relative;left:91.66667%}.el-col-md-23{width:95.83333%}.el-col-md-offset-23{margin-left:95.83333%}.el-col-md-pull-23{position:relative;right:95.83333%}.el-col-md-push-23{position:relative;left:95.83333%}.el-col-md-24{width:100%}.el-col-md-offset-24{margin-left:100%}.el-col-md-pull-24{position:relative;right:100%}.el-col-md-push-24{position:relative;left:100%}}@media only screen and (min-width:1200px){.el-col-lg-0{display:none;width:0}.el-col-lg-offset-0{margin-left:0}.el-col-lg-pull-0{position:relative;right:0}.el-col-lg-push-0{position:relative;left:0}.el-col-lg-1{width:4.16667%}.el-col-lg-offset-1{margin-left:4.16667%}.el-col-lg-pull-1{position:relative;right:4.16667%}.el-col-lg-push-1{position:relative;left:4.16667%}.el-col-lg-2{width:8.33333%}.el-col-lg-offset-2{margin-left:8.33333%}.el-col-lg-pull-2{position:relative;right:8.33333%}.el-col-lg-push-2{position:relative;left:8.33333%}.el-col-lg-3{width:12.5%}.el-col-lg-offset-3{margin-left:12.5%}.el-col-lg-pull-3{position:relative;right:12.5%}.el-col-lg-push-3{position:relative;left:12.5%}.el-col-lg-4{width:16.66667%}.el-col-lg-offset-4{margin-left:16.66667%}.el-col-lg-pull-4{position:relative;right:16.66667%}.el-col-lg-push-4{position:relative;left:16.66667%}.el-col-lg-5{width:20.83333%}.el-col-lg-offset-5{margin-left:20.83333%}.el-col-lg-pull-5{position:relative;right:20.83333%}.el-col-lg-push-5{position:relative;left:20.83333%}.el-col-lg-6{width:25%}.el-col-lg-offset-6{margin-left:25%}.el-col-lg-pull-6{position:relative;right:25%}.el-col-lg-push-6{position:relative;left:25%}.el-col-lg-7{width:29.16667%}.el-col-lg-offset-7{margin-left:29.16667%}.el-col-lg-pull-7{position:relative;right:29.16667%}.el-col-lg-push-7{position:relative;left:29.16667%}.el-col-lg-8{width:33.33333%}.el-col-lg-offset-8{margin-left:33.33333%}.el-col-lg-pull-8{position:relative;right:33.33333%}.el-col-lg-push-8{position:relative;left:33.33333%}.el-col-lg-9{width:37.5%}.el-col-lg-offset-9{margin-left:37.5%}.el-col-lg-pull-9{position:relative;right:37.5%}.el-col-lg-push-9{position:relative;left:37.5%}.el-col-lg-10{width:41.66667%}.el-col-lg-offset-10{margin-left:41.66667%}.el-col-lg-pull-10{position:relative;right:41.66667%}.el-col-lg-push-10{position:relative;left:41.66667%}.el-col-lg-11{width:45.83333%}.el-col-lg-offset-11{margin-left:45.83333%}.el-col-lg-pull-11{position:relative;right:45.83333%}.el-col-lg-push-11{position:relative;left:45.83333%}.el-col-lg-12{width:50%}.el-col-lg-offset-12{margin-left:50%}.el-col-lg-pull-12{position:relative;right:50%}.el-col-lg-push-12{position:relative;left:50%}.el-col-lg-13{width:54.16667%}.el-col-lg-offset-13{margin-left:54.16667%}.el-col-lg-pull-13{position:relative;right:54.16667%}.el-col-lg-push-13{position:relative;left:54.16667%}.el-col-lg-14{width:58.33333%}.el-col-lg-offset-14{margin-left:58.33333%}.el-col-lg-pull-14{position:relative;right:58.33333%}.el-col-lg-push-14{position:relative;left:58.33333%}.el-col-lg-15{width:62.5%}.el-col-lg-offset-15{margin-left:62.5%}.el-col-lg-pull-15{position:relative;right:62.5%}.el-col-lg-push-15{position:relative;left:62.5%}.el-col-lg-16{width:66.66667%}.el-col-lg-offset-16{margin-left:66.66667%}.el-col-lg-pull-16{position:relative;right:66.66667%}.el-col-lg-push-16{position:relative;left:66.66667%}.el-col-lg-17{width:70.83333%}.el-col-lg-offset-17{margin-left:70.83333%}.el-col-lg-pull-17{position:relative;right:70.83333%}.el-col-lg-push-17{position:relative;left:70.83333%}.el-col-lg-18{width:75%}.el-col-lg-offset-18{margin-left:75%}.el-col-lg-pull-18{position:relative;right:75%}.el-col-lg-push-18{position:relative;left:75%}.el-col-lg-19{width:79.16667%}.el-col-lg-offset-19{margin-left:79.16667%}.el-col-lg-pull-19{position:relative;right:79.16667%}.el-col-lg-push-19{position:relative;left:79.16667%}.el-col-lg-20{width:83.33333%}.el-col-lg-offset-20{margin-left:83.33333%}.el-col-lg-pull-20{position:relative;right:83.33333%}.el-col-lg-push-20{position:relative;left:83.33333%}.el-col-lg-21{width:87.5%}.el-col-lg-offset-21{margin-left:87.5%}.el-col-lg-pull-21{position:relative;right:87.5%}.el-col-lg-push-21{position:relative;left:87.5%}.el-col-lg-22{width:91.66667%}.el-col-lg-offset-22{margin-left:91.66667%}.el-col-lg-pull-22{position:relative;right:91.66667%}.el-col-lg-push-22{position:relative;left:91.66667%}.el-col-lg-23{width:95.83333%}.el-col-lg-offset-23{margin-left:95.83333%}.el-col-lg-pull-23{position:relative;right:95.83333%}.el-col-lg-push-23{position:relative;left:95.83333%}.el-col-lg-24{width:100%}.el-col-lg-offset-24{margin-left:100%}.el-col-lg-pull-24{position:relative;right:100%}.el-col-lg-push-24{position:relative;left:100%}}@media only screen and (min-width:1920px){.el-col-xl-0{display:none;width:0}.el-col-xl-offset-0{margin-left:0}.el-col-xl-pull-0{position:relative;right:0}.el-col-xl-push-0{position:relative;left:0}.el-col-xl-1{width:4.16667%}.el-col-xl-offset-1{margin-left:4.16667%}.el-col-xl-pull-1{position:relative;right:4.16667%}.el-col-xl-push-1{position:relative;left:4.16667%}.el-col-xl-2{width:8.33333%}.el-col-xl-offset-2{margin-left:8.33333%}.el-col-xl-pull-2{position:relative;right:8.33333%}.el-col-xl-push-2{position:relative;left:8.33333%}.el-col-xl-3{width:12.5%}.el-col-xl-offset-3{margin-left:12.5%}.el-col-xl-pull-3{position:relative;right:12.5%}.el-col-xl-push-3{position:relative;left:12.5%}.el-col-xl-4{width:16.66667%}.el-col-xl-offset-4{margin-left:16.66667%}.el-col-xl-pull-4{position:relative;right:16.66667%}.el-col-xl-push-4{position:relative;left:16.66667%}.el-col-xl-5{width:20.83333%}.el-col-xl-offset-5{margin-left:20.83333%}.el-col-xl-pull-5{position:relative;right:20.83333%}.el-col-xl-push-5{position:relative;left:20.83333%}.el-col-xl-6{width:25%}.el-col-xl-offset-6{margin-left:25%}.el-col-xl-pull-6{position:relative;right:25%}.el-col-xl-push-6{position:relative;left:25%}.el-col-xl-7{width:29.16667%}.el-col-xl-offset-7{margin-left:29.16667%}.el-col-xl-pull-7{position:relative;right:29.16667%}.el-col-xl-push-7{position:relative;left:29.16667%}.el-col-xl-8{width:33.33333%}.el-col-xl-offset-8{margin-left:33.33333%}.el-col-xl-pull-8{position:relative;right:33.33333%}.el-col-xl-push-8{position:relative;left:33.33333%}.el-col-xl-9{width:37.5%}.el-col-xl-offset-9{margin-left:37.5%}.el-col-xl-pull-9{position:relative;right:37.5%}.el-col-xl-push-9{position:relative;left:37.5%}.el-col-xl-10{width:41.66667%}.el-col-xl-offset-10{margin-left:41.66667%}.el-col-xl-pull-10{position:relative;right:41.66667%}.el-col-xl-push-10{position:relative;left:41.66667%}.el-col-xl-11{width:45.83333%}.el-col-xl-offset-11{margin-left:45.83333%}.el-col-xl-pull-11{position:relative;right:45.83333%}.el-col-xl-push-11{position:relative;left:45.83333%}.el-col-xl-12{width:50%}.el-col-xl-offset-12{margin-left:50%}.el-col-xl-pull-12{position:relative;right:50%}.el-col-xl-push-12{position:relative;left:50%}.el-col-xl-13{width:54.16667%}.el-col-xl-offset-13{margin-left:54.16667%}.el-col-xl-pull-13{position:relative;right:54.16667%}.el-col-xl-push-13{position:relative;left:54.16667%}.el-col-xl-14{width:58.33333%}.el-col-xl-offset-14{margin-left:58.33333%}.el-col-xl-pull-14{position:relative;right:58.33333%}.el-col-xl-push-14{position:relative;left:58.33333%}.el-col-xl-15{width:62.5%}.el-col-xl-offset-15{margin-left:62.5%}.el-col-xl-pull-15{position:relative;right:62.5%}.el-col-xl-push-15{position:relative;left:62.5%}.el-col-xl-16{width:66.66667%}.el-col-xl-offset-16{margin-left:66.66667%}.el-col-xl-pull-16{position:relative;right:66.66667%}.el-col-xl-push-16{position:relative;left:66.66667%}.el-col-xl-17{width:70.83333%}.el-col-xl-offset-17{margin-left:70.83333%}.el-col-xl-pull-17{position:relative;right:70.83333%}.el-col-xl-push-17{position:relative;left:70.83333%}.el-col-xl-18{width:75%}.el-col-xl-offset-18{margin-left:75%}.el-col-xl-pull-18{position:relative;right:75%}.el-col-xl-push-18{position:relative;left:75%}.el-col-xl-19{width:79.16667%}.el-col-xl-offset-19{margin-left:79.16667%}.el-col-xl-pull-19{position:relative;right:79.16667%}.el-col-xl-push-19{position:relative;left:79.16667%}.el-col-xl-20{width:83.33333%}.el-col-xl-offset-20{margin-left:83.33333%}.el-col-xl-pull-20{position:relative;right:83.33333%}.el-col-xl-push-20{position:relative;left:83.33333%}.el-col-xl-21{width:87.5%}.el-col-xl-offset-21{margin-left:87.5%}.el-col-xl-pull-21{position:relative;right:87.5%}.el-col-xl-push-21{position:relative;left:87.5%}.el-col-xl-22{width:91.66667%}.el-col-xl-offset-22{margin-left:91.66667%}.el-col-xl-pull-22{position:relative;right:91.66667%}.el-col-xl-push-22{position:relative;left:91.66667%}.el-col-xl-23{width:95.83333%}.el-col-xl-offset-23{margin-left:95.83333%}.el-col-xl-pull-23{position:relative;right:95.83333%}.el-col-xl-push-23{position:relative;left:95.83333%}.el-col-xl-24{width:100%}.el-col-xl-offset-24{margin-left:100%}.el-col-xl-pull-24{position:relative;right:100%}.el-col-xl-push-24{position:relative;left:100%}}@-webkit-keyframes progress{0%{background-position:0 0}to{background-position:32px 0}}.el-upload{display:inline-block;text-align:center;cursor:pointer;outline:0}.el-upload__input{display:none}.el-upload__tip{font-size:12px;color:#606266;margin-top:7px}.el-upload iframe{position:absolute;z-index:-1;top:0;left:0;opacity:0;filter:alpha(opacity=0)}.el-upload--picture-card{background-color:#fbfdff;border:1px dashed #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:148px;height:148px;line-height:146px;vertical-align:top}.el-upload--picture-card i{font-size:28px;color:#8c939d}.el-upload--picture-card:hover,.el-upload:focus{border-color:#000;color:#000}.el-upload:focus .el-upload-dragger{border-color:#000}.el-upload-dragger{background-color:#fff;border:1px dashed #d9d9d9;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:360px;height:180px;text-align:center;position:relative;overflow:hidden}.el-upload-dragger .el-icon-upload{font-size:67px;color:#c0c4cc;margin:40px 0 16px;line-height:50px}.el-upload-dragger+.el-upload__tip{text-align:center}.el-upload-dragger~.el-upload__files{border-top:1px solid #dcdfe6;margin-top:7px;padding-top:5px}.el-upload-dragger .el-upload__text{color:#606266;font-size:14px;text-align:center}.el-upload-dragger .el-upload__text em{color:#000;font-style:normal}.el-upload-dragger:hover{border-color:#000}.el-upload-dragger.is-dragover{background-color:rgba(32,159,255,.06);border:2px dashed #000}.el-upload-list{margin:0;padding:0;list-style:none}.el-upload-list__item{-webkit-transition:all .5s cubic-bezier(.55,0,.1,1);transition:all .5s cubic-bezier(.55,0,.1,1);font-size:14px;color:#606266;line-height:1.8;margin-top:5px;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;width:100%}.el-upload-list__item .el-progress{position:absolute;top:20px;width:100%}.el-upload-list__item .el-progress__text{position:absolute;right:0;top:-13px}.el-upload-list__item .el-progress-bar{margin-right:0;padding-right:0}.el-upload-list__item:first-child{margin-top:10px}.el-upload-list__item .el-icon-upload-success{color:#67c23a}.el-upload-list__item .el-icon-close{display:none;position:absolute;top:5px;right:5px;cursor:pointer;opacity:.75;color:#606266}.el-upload-list__item .el-icon-close:hover{opacity:1}.el-upload-list__item .el-icon-close-tip{display:none;position:absolute;top:5px;right:5px;font-size:12px;cursor:pointer;opacity:1;color:#000}.el-upload-list__item:hover{background-color:#f5f7fa}.el-upload-list__item:hover .el-icon-close{display:inline-block}.el-upload-list__item:hover .el-progress__text{display:none}.el-upload-list__item.is-success .el-upload-list__item-status-label{display:block}.el-upload-list__item.is-success .el-upload-list__item-name:focus,.el-upload-list__item.is-success .el-upload-list__item-name:hover{color:#000;cursor:pointer}.el-upload-list__item.is-success:focus:not(:hover) .el-icon-close-tip{display:inline-block}.el-upload-list__item.is-success:active .el-icon-close-tip,.el-upload-list__item.is-success:focus .el-upload-list__item-status-label,.el-upload-list__item.is-success:hover .el-upload-list__item-status-label,.el-upload-list__item.is-success:not(.focusing):focus .el-icon-close-tip{display:none}.el-upload-list.is-disabled .el-upload-list__item:hover .el-upload-list__item-status-label{display:block}.el-upload-list__item-name{color:#606266;display:block;margin-right:40px;overflow:hidden;padding-left:4px;text-overflow:ellipsis;-webkit-transition:color .3s;transition:color .3s;white-space:nowrap}.el-upload-list__item-name [class^=el-icon]{height:100%;margin-right:7px;color:#909399;line-height:inherit}.el-upload-list__item-status-label{position:absolute;right:5px;top:0;line-height:inherit;display:none}.el-upload-list__item-delete{position:absolute;right:10px;top:0;font-size:12px;color:#606266;display:none}.el-upload-list__item-delete:hover{color:#000}.el-upload-list--picture-card{margin:0;display:inline;vertical-align:top}.el-upload-list--picture-card .el-upload-list__item{overflow:hidden;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:148px;height:148px;margin:0 8px 8px 0;display:inline-block}.el-upload-list--picture-card .el-upload-list__item .el-icon-check,.el-upload-list--picture-card .el-upload-list__item .el-icon-circle-check{color:#fff}.el-upload-list--picture-card .el-upload-list__item .el-icon-close,.el-upload-list--picture-card .el-upload-list__item:hover .el-upload-list__item-status-label{display:none}.el-upload-list--picture-card .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture-card .el-upload-list__item-name{display:none}.el-upload-list--picture-card .el-upload-list__item-thumbnail{width:100%;height:100%}.el-upload-list--picture-card .el-upload-list__item-status-label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 0 1pc 1px rgba(0,0,0,.2);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.el-upload-list--picture-card .el-upload-list__item-status-label i{font-size:12px;margin-top:11px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.el-upload-list--picture-card .el-upload-list__item-actions{position:absolute;width:100%;height:100%;left:0;top:0;cursor:default;text-align:center;color:#fff;opacity:0;font-size:20px;background-color:rgba(0,0,0,.5);-webkit-transition:opacity .3s;transition:opacity .3s}.el-upload-list--picture-card .el-upload-list__item-actions:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-upload-list--picture-card .el-upload-list__item-actions span{display:none;cursor:pointer}.el-upload-list--picture-card .el-upload-list__item-actions span+span{margin-left:15px}.el-upload-list--picture-card .el-upload-list__item-actions .el-upload-list__item-delete{position:static;font-size:inherit;color:inherit}.el-upload-list--picture-card .el-upload-list__item-actions:hover{opacity:1}.el-upload-list--picture-card .el-upload-list__item-actions:hover span{display:inline-block}.el-upload-list--picture-card .el-progress{top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);bottom:auto;width:126px}.el-upload-list--picture-card .el-progress .el-progress__text{top:50%}.el-upload-list--picture .el-upload-list__item{overflow:hidden;z-index:0;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;margin-top:10px;padding:10px 10px 10px 90px;height:92px}.el-upload-list--picture .el-upload-list__item .el-icon-check,.el-upload-list--picture .el-upload-list__item .el-icon-circle-check{color:#fff}.el-upload-list--picture .el-upload-list__item:hover .el-upload-list__item-status-label{background:0 0;-webkit-box-shadow:none;box-shadow:none;top:-2px;right:-12px}.el-upload-list--picture .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name{line-height:70px;margin-top:0}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name i{display:none}.el-upload-list--picture .el-upload-list__item-thumbnail{vertical-align:middle;display:inline-block;width:70px;height:70px;float:left;position:relative;z-index:1;margin-left:-80px;background-color:#fff}.el-upload-list--picture .el-upload-list__item-name{display:block;margin-top:20px}.el-upload-list--picture .el-upload-list__item-name i{font-size:70px;line-height:1;position:absolute;left:9px;top:10px}.el-upload-list--picture .el-upload-list__item-status-label{position:absolute;right:-17px;top:-7px;width:46px;height:26px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 1px 1px #ccc;box-shadow:0 1px 1px #ccc}.el-upload-list--picture .el-upload-list__item-status-label i{font-size:12px;margin-top:12px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.el-upload-list--picture .el-progress{position:relative;top:-7px}.el-upload-cover{position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden;z-index:10;cursor:default}.el-upload-cover:after{display:inline-block;height:100%;vertical-align:middle}.el-upload-cover img{display:block;width:100%;height:100%}.el-upload-cover__label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 0 1pc 1px rgba(0,0,0,.2);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.el-upload-cover__label i{font-size:12px;margin-top:11px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);color:#fff}.el-upload-cover__progress{display:inline-block;vertical-align:middle;position:static;width:243px}.el-upload-cover__progress+.el-upload__inner{opacity:0}.el-upload-cover__content{position:absolute;top:0;left:0;width:100%;height:100%}.el-upload-cover__interact{position:absolute;bottom:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,.72);text-align:center}.el-upload-cover__interact .btn{display:inline-block;color:#fff;font-size:14px;cursor:pointer;vertical-align:middle;-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);margin-top:60px}.el-upload-cover__interact .btn span{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.el-upload-cover__interact .btn:not(:first-child){margin-left:35px}.el-upload-cover__interact .btn:hover{-webkit-transform:translateY(-13px);transform:translateY(-13px)}.el-upload-cover__interact .btn:hover span{opacity:1}.el-upload-cover__interact .btn i{color:#fff;display:block;font-size:24px;line-height:inherit;margin:0 auto 5px}.el-upload-cover__title{position:absolute;bottom:0;left:0;background-color:#fff;height:36px;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400;text-align:left;padding:0 10px;margin:0;line-height:36px;font-size:14px;color:#303133}.el-upload-cover+.el-upload__inner{opacity:0;position:relative;z-index:1}.el-progress{position:relative;line-height:1}.el-progress__text{font-size:14px;color:#606266;display:inline-block;vertical-align:middle;margin-left:10px;line-height:1}.el-progress__text i{vertical-align:middle;display:block}.el-progress--circle,.el-progress--dashboard{display:inline-block}.el-progress--circle .el-progress__text,.el-progress--dashboard .el-progress__text{position:absolute;top:50%;left:0;width:100%;text-align:center;margin:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-progress--circle .el-progress__text i,.el-progress--dashboard .el-progress__text i{vertical-align:middle;display:inline-block}.el-progress--without-text .el-progress__text{display:none}.el-progress--without-text .el-progress-bar{padding-right:0;margin-right:0;display:block}.el-progress-bar,.el-progress-bar__inner:after,.el-progress-bar__innerText,.el-spinner{display:inline-block;vertical-align:middle}.el-progress--text-inside .el-progress-bar{padding-right:0;margin-right:0}.el-progress.is-success .el-progress-bar__inner{background-color:#67c23a}.el-progress.is-success .el-progress__text{color:#67c23a}.el-progress.is-warning .el-progress-bar__inner{background-color:#e6a23c}.el-progress.is-warning .el-progress__text{color:#e6a23c}.el-progress.is-exception .el-progress-bar__inner{background-color:#f56c6c}.el-progress.is-exception .el-progress__text{color:#f56c6c}.el-progress-bar{padding-right:50px;width:100%;margin-right:-55px;-webkit-box-sizing:border-box;box-sizing:border-box}.el-progress-bar__outer{height:6px;border-radius:100px;background-color:#ebeef5;overflow:hidden;position:relative;vertical-align:middle}.el-progress-bar__inner{position:absolute;left:0;top:0;height:100%;background-color:#000;text-align:right;border-radius:100px;line-height:1;white-space:nowrap;-webkit-transition:width .6s ease;transition:width .6s ease}.el-card,.el-message{border-radius:4px;overflow:hidden}.el-progress-bar__inner:after{height:100%}.el-progress-bar__innerText{color:#fff;font-size:12px;margin:0 5px}@keyframes progress{0%{background-position:0 0}to{background-position:32px 0}}.el-time-spinner{width:100%;white-space:nowrap}.el-spinner-inner{-webkit-animation:rotate 2s linear infinite;animation:rotate 2s linear infinite;width:50px;height:50px}.el-spinner-inner .path{stroke:#ececec;stroke-linecap:round;-webkit-animation:dash 1.5s ease-in-out infinite;animation:dash 1.5s ease-in-out infinite}@-webkit-keyframes rotate{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes rotate{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}to{stroke-dasharray:90,150;stroke-dashoffset:-124}}@keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}to{stroke-dasharray:90,150;stroke-dashoffset:-124}}.el-message{min-width:380px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #ebeef5;position:fixed;left:50%;top:20px;-webkit-transform:translateX(-50%);transform:translateX(-50%);background-color:#edf2fc;-webkit-transition:opacity .3s,top .4s,-webkit-transform .4s;transition:opacity .3s,top .4s,-webkit-transform .4s;transition:opacity .3s,transform .4s,top .4s;transition:opacity .3s,transform .4s,top .4s,-webkit-transform .4s;padding:15px 15px 15px 20px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-message.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-message.is-closable .el-message__content{padding-right:16px}.el-message p{margin:0}.el-message--info .el-message__content{color:#909399}.el-message--success{background-color:#f0f9eb;border-color:#e1f3d8}.el-message--success .el-message__content{color:#67c23a}.el-message--warning{background-color:#fdf6ec;border-color:#faecd8}.el-message--warning .el-message__content{color:#e6a23c}.el-message--error{background-color:#fef0f0;border-color:#fde2e2}.el-message--error .el-message__content{color:#f56c6c}.el-message__icon{margin-right:10px}.el-message__content{padding:0;font-size:14px;line-height:1}.el-message__closeBtn{position:absolute;top:50%;right:15px;-webkit-transform:translateY(-50%);transform:translateY(-50%);cursor:pointer;color:#c0c4cc;font-size:16px}.el-message__closeBtn:hover{color:#909399}.el-message .el-icon-success{color:#67c23a}.el-message .el-icon-error{color:#f56c6c}.el-message .el-icon-info{color:#909399}.el-message .el-icon-warning{color:#e6a23c}.el-message-fade-enter,.el-message-fade-leave-active{opacity:0;-webkit-transform:translate(-50%,-100%);transform:translate(-50%,-100%)}.el-badge{position:relative;vertical-align:middle;display:inline-block}.el-badge__content{background-color:#f56c6c;border-radius:10px;color:#fff;display:inline-block;font-size:12px;height:18px;line-height:18px;padding:0 6px;text-align:center;white-space:nowrap;border:1px solid #fff}.el-badge__content.is-fixed{position:absolute;top:0;right:10px;-webkit-transform:translateY(-50%) translateX(100%);transform:translateY(-50%) translateX(100%)}.el-rate__icon,.el-rate__item{position:relative;display:inline-block}.el-badge__content.is-fixed.is-dot{right:5px}.el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%}.el-badge__content--primary{background-color:#000}.el-badge__content--success{background-color:#67c23a}.el-badge__content--warning{background-color:#e6a23c}.el-badge__content--info{background-color:#909399}.el-badge__content--danger{background-color:#f56c6c}.el-card{border:1px solid #ebeef5;background-color:#fff;color:#303133;-webkit-transition:.3s;transition:.3s}.el-card.is-always-shadow,.el-card.is-hover-shadow:focus,.el-card.is-hover-shadow:hover{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-card__header{padding:18px 20px;border-bottom:1px solid #ebeef5;-webkit-box-sizing:border-box;box-sizing:border-box}.el-card__body{padding:20px}.el-rate{height:20px;line-height:1}.el-rate__item{font-size:0;vertical-align:middle}.el-rate__icon{font-size:18px;margin-right:6px;color:#c0c4cc;-webkit-transition:.3s;transition:.3s}.el-rate__decimal,.el-rate__icon .path2{position:absolute;top:0;left:0}.el-rate__icon.hover{-webkit-transform:scale(1.15);transform:scale(1.15)}.el-rate__decimal{display:inline-block;overflow:hidden}.el-step.is-vertical,.el-steps{display:-webkit-box;display:-ms-flexbox}.el-rate__text{font-size:14px;vertical-align:middle}.el-steps{display:-webkit-box;display:-ms-flexbox;display:flex}.el-steps--simple{padding:13px 8%;border-radius:4px;background:#f5f7fa}.el-steps--horizontal{white-space:nowrap}.el-steps--vertical{height:100%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-flow:column;flex-flow:column}.el-step{position:relative;-ms-flex-negative:1;flex-shrink:1}.el-step:last-of-type .el-step__line{display:none}.el-step:last-of-type.is-flex{-ms-flex-preferred-size:auto!important;flex-basis:auto!important;-ms-flex-negative:0;flex-shrink:0;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0}.el-step:last-of-type .el-step__description,.el-step:last-of-type .el-step__main{padding-right:0}.el-step__head{position:relative;width:100%}.el-step__head.is-process{color:#303133;border-color:#303133}.el-step__head.is-wait{color:#c0c4cc;border-color:#c0c4cc}.el-step__head.is-success{color:#67c23a;border-color:#67c23a}.el-step__head.is-error{color:#f56c6c;border-color:#f56c6c}.el-step__head.is-finish{color:#000;border-color:#000}.el-step__icon{position:relative;z-index:1;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:24px;height:24px;font-size:14px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#fff;-webkit-transition:.15s ease-out;transition:.15s ease-out}.el-step__icon.is-text{border-radius:50%;border:2px solid;border-color:inherit}.el-step__icon.is-icon{width:40px}.el-step__icon-inner{display:inline-block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-align:center;font-weight:700;line-height:1;color:inherit}.el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:25px;font-weight:400}.el-step__icon-inner.is-status{-webkit-transform:translateY(1px);transform:translateY(1px)}.el-step__line{position:absolute;border-color:inherit;background-color:#c0c4cc}.el-step__line-inner{display:block;border:1px solid;border-color:inherit;-webkit-transition:.15s ease-out;transition:.15s ease-out;-webkit-box-sizing:border-box;box-sizing:border-box;width:0;height:0}.el-step__main{white-space:normal;text-align:left}.el-step__title{font-size:16px;line-height:38px}.el-step__title.is-process{font-weight:700;color:#303133}.el-step__title.is-wait{color:#c0c4cc}.el-step__title.is-success{color:#67c23a}.el-step__title.is-error{color:#f56c6c}.el-step__title.is-finish{color:#000}.el-step__description{padding-right:10%;margin-top:-5px;font-size:12px;line-height:20px;font-weight:400}.el-step__description.is-process{color:#303133}.el-step__description.is-wait{color:#c0c4cc}.el-step__description.is-success{color:#67c23a}.el-step__description.is-error{color:#f56c6c}.el-step__description.is-finish{color:#000}.el-step.is-horizontal{display:inline-block}.el-step.is-horizontal .el-step__line{height:2px;top:11px;left:0;right:0}.el-step.is-vertical{display:-webkit-box;display:-ms-flexbox;display:flex}.el-step.is-vertical .el-step__head{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;width:24px}.el-step.is-vertical .el-step__main{padding-left:10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.el-step.is-vertical .el-step__title{line-height:24px;padding-bottom:8px}.el-step.is-vertical .el-step__line{width:2px;top:0;bottom:0;left:11px}.el-step.is-vertical .el-step__icon.is-icon{width:24px}.el-step.is-center .el-step__head,.el-step.is-center .el-step__main{text-align:center}.el-step.is-center .el-step__description{padding-left:20%;padding-right:20%}.el-step.is-center .el-step__line{left:50%;right:-50%}.el-step.is-simple{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-step.is-simple .el-step__head{width:auto;font-size:0;padding-right:10px}.el-step.is-simple .el-step__icon{background:0 0;width:16px;height:16px;font-size:12px}.el-step.is-simple .el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:18px}.el-step.is-simple .el-step__icon-inner.is-status{-webkit-transform:scale(.8) translateY(1px);transform:scale(.8) translateY(1px)}.el-step.is-simple .el-step__main{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.el-step.is-simple .el-step__title{font-size:16px;line-height:20px}.el-step.is-simple:not(:last-of-type) .el-step__title{max-width:50%;word-break:break-all}.el-step.is-simple .el-step__arrow{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-step.is-simple .el-step__arrow:after,.el-step.is-simple .el-step__arrow:before{content:"";display:inline-block;position:absolute;height:15px;width:1px;background:#c0c4cc}.el-step.is-simple .el-step__arrow:before{-webkit-transform:rotate(-45deg) translateY(-4px);transform:rotate(-45deg) translateY(-4px);-webkit-transform-origin:0 0;transform-origin:0 0}.el-step.is-simple .el-step__arrow:after{-webkit-transform:rotate(45deg) translateY(4px);transform:rotate(45deg) translateY(4px);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}.el-step.is-simple:last-of-type .el-step__arrow{display:none}.el-carousel{position:relative}.el-carousel--horizontal{overflow-x:hidden}.el-carousel--vertical{overflow-y:hidden}.el-carousel__container{position:relative;height:300px}.el-carousel__arrow{border:none;outline:0;padding:0;margin:0;height:36px;width:36px;cursor:pointer;-webkit-transition:.3s;transition:.3s;border-radius:50%;background-color:rgba(31,45,61,.11);color:#fff;position:absolute;top:50%;z-index:10;-webkit-transform:translateY(-50%);transform:translateY(-50%);text-align:center;font-size:12px}.el-carousel__arrow--left{left:16px}.el-carousel__arrow--right{right:16px}.el-carousel__arrow:hover{background-color:rgba(31,45,61,.23)}.el-carousel__arrow i{cursor:pointer}.el-carousel__indicators{position:absolute;list-style:none;margin:0;padding:0;z-index:2}.el-carousel__indicators--horizontal{bottom:0;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.el-carousel__indicators--vertical{right:0;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-carousel__indicators--outside{bottom:26px;text-align:center;position:static;-webkit-transform:none;transform:none}.el-carousel__indicators--outside .el-carousel__indicator:hover button{opacity:.64}.el-carousel__indicators--outside button{background-color:#c0c4cc;opacity:.24}.el-carousel__indicators--labels{left:0;right:0;-webkit-transform:none;transform:none;text-align:center}.el-carousel__indicators--labels .el-carousel__button{height:auto;width:auto;padding:2px 18px;font-size:12px}.el-carousel__indicators--labels .el-carousel__indicator{padding:6px 4px}.el-carousel__indicator{background-color:transparent;cursor:pointer}.el-carousel__indicator:hover button{opacity:.72}.el-carousel__indicator--horizontal{display:inline-block;padding:12px 4px}.el-carousel__indicator--vertical{padding:4px 12px}.el-carousel__indicator--vertical .el-carousel__button{width:2px;height:15px}.el-carousel__indicator.is-active button{opacity:1}.el-carousel__button{display:block;opacity:.48;width:30px;height:2px;background-color:#fff;border:none;outline:0;padding:0;margin:0;cursor:pointer;-webkit-transition:.3s;transition:.3s}.el-carousel__item,.el-carousel__mask{height:100%;top:0;left:0;position:absolute}.carousel-arrow-left-enter,.carousel-arrow-left-leave-active{-webkit-transform:translateY(-50%) translateX(-10px);transform:translateY(-50%) translateX(-10px);opacity:0}.carousel-arrow-right-enter,.carousel-arrow-right-leave-active{-webkit-transform:translateY(-50%) translateX(10px);transform:translateY(-50%) translateX(10px);opacity:0}.el-carousel__item{width:100%;display:inline-block;overflow:hidden;z-index:0}.el-carousel__item.is-active{z-index:2}.el-carousel__item--card,.el-carousel__item.is-animating{-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.el-carousel__item--card{width:50%}.el-carousel__item--card.is-in-stage{cursor:pointer;z-index:1}.el-carousel__item--card.is-in-stage.is-hover .el-carousel__mask,.el-carousel__item--card.is-in-stage:hover .el-carousel__mask{opacity:.12}.el-carousel__item--card.is-active{z-index:2}.el-carousel__mask{width:100%;background-color:#fff;opacity:.24;-webkit-transition:.2s;transition:.2s}.el-fade-in-enter,.el-fade-in-leave-active,.el-fade-in-linear-enter,.el-fade-in-linear-leave,.el-fade-in-linear-leave-active,.fade-in-linear-enter,.fade-in-linear-leave,.fade-in-linear-leave-active{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active,.fade-in-linear-enter-active,.fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.el-fade-in-enter-active,.el-fade-in-leave-active,.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter,.el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center top;transform-origin:center top}.el-zoom-in-top-enter,.el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}.el-zoom-in-bottom-enter,.el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1);transform:scale(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:top left;transform-origin:top left}.el-zoom-in-left-enter,.el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45);transform:scale(.45)}.collapse-transition{-webkit-transition:height .3s ease-in-out,padding-top .3s ease-in-out,padding-bottom .3s ease-in-out;transition:height .3s ease-in-out,padding-top .3s ease-in-out,padding-bottom .3s ease-in-out}.horizontal-collapse-transition{-webkit-transition:width .3s ease-in-out,padding-left .3s ease-in-out,padding-right .3s ease-in-out;transition:width .3s ease-in-out,padding-left .3s ease-in-out,padding-right .3s ease-in-out}.el-list-enter-active,.el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.el-list-enter,.el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.el-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}.el-collapse{border-top:1px solid #ebeef5;border-bottom:1px solid #ebeef5}.el-collapse-item.is-disabled .el-collapse-item__header{color:#bbb;cursor:not-allowed}.el-collapse-item__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:48px;line-height:48px;background-color:#fff;color:#303133;cursor:pointer;border-bottom:1px solid #ebeef5;font-size:13px;font-weight:500;-webkit-transition:border-bottom-color .3s;transition:border-bottom-color .3s;outline:0}.el-collapse-item__arrow{margin:0 8px 0 auto;transition:-webkit-transform .3s;-webkit-transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-weight:300}.el-collapse-item__arrow.is-active{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-collapse-item__header.focusing:focus:not(:hover){color:#000}.el-collapse-item__header.is-active{border-bottom-color:transparent}.el-collapse-item__wrap{will-change:height;background-color:#fff;overflow:hidden;box-sizing:border-box;border-bottom:1px solid #ebeef5}.el-cascader__tags,.el-collapse-item__wrap,.el-tag{-webkit-box-sizing:border-box}.el-collapse-item__content{padding-bottom:25px;font-size:13px;color:#303133;line-height:1.769230769230769}.el-collapse-item:last-child{margin-bottom:-1px}.el-popper .popper__arrow,.el-popper .popper__arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0,0,0,.03));filter:drop-shadow(0 2px 12px rgba(0,0,0,.03))}.el-popper .popper__arrow:after{content:" ";border-width:6px}.el-popper[x-placement^=top]{margin-bottom:12px}.el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#ebeef5;border-bottom-width:0}.el-popper[x-placement^=top] .popper__arrow:after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.el-popper[x-placement^=bottom]{margin-top:12px}.el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#ebeef5}.el-popper[x-placement^=bottom] .popper__arrow:after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.el-popper[x-placement^=right]{margin-left:12px}.el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#ebeef5;border-left-width:0}.el-popper[x-placement^=right] .popper__arrow:after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.el-popper[x-placement^=left]{margin-right:12px}.el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#ebeef5}.el-popper[x-placement^=left] .popper__arrow:after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.el-tag{background-color:#e6e6e6;display:inline-block;height:32px;padding:0 10px;line-height:30px;font-size:12px;color:#000;border:1px solid #ccc;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box;white-space:nowrap}.el-tag.is-hit{border-color:#000}.el-tag .el-tag__close{color:#000}.el-tag .el-tag__close:hover{color:#fff;background-color:#000}.el-tag.el-tag--info{background-color:#f4f4f5;border-color:#e9e9eb;color:#909399}.el-tag.el-tag--info.is-hit{border-color:#909399}.el-tag.el-tag--info .el-tag__close{color:#909399}.el-tag.el-tag--info .el-tag__close:hover{color:#fff;background-color:#909399}.el-tag.el-tag--success{background-color:#f0f9eb;border-color:#e1f3d8;color:#67c23a}.el-tag.el-tag--success.is-hit{border-color:#67c23a}.el-tag.el-tag--success .el-tag__close{color:#67c23a}.el-tag.el-tag--success .el-tag__close:hover{color:#fff;background-color:#67c23a}.el-tag.el-tag--warning{background-color:#fdf6ec;border-color:#faecd8;color:#e6a23c}.el-tag.el-tag--warning.is-hit{border-color:#e6a23c}.el-tag.el-tag--warning .el-tag__close{color:#e6a23c}.el-tag.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#e6a23c}.el-tag.el-tag--danger{background-color:#fef0f0;border-color:#fde2e2;color:#f56c6c}.el-tag.el-tag--danger.is-hit{border-color:#f56c6c}.el-tag.el-tag--danger .el-tag__close{color:#f56c6c}.el-tag.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#f56c6c}.el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:16px;width:16px;line-height:16px;vertical-align:middle;top:-1px;right:-5px}.el-tag .el-icon-close:before{display:block}.el-tag--dark{background-color:#000;color:#fff}.el-tag--dark,.el-tag--dark.is-hit{border-color:#000}.el-tag--dark .el-tag__close{color:#fff}.el-tag--dark .el-tag__close:hover{color:#fff;background-color:#333}.el-tag--dark.el-tag--info{background-color:#909399;border-color:#909399;color:#fff}.el-tag--dark.el-tag--info.is-hit{border-color:#909399}.el-tag--dark.el-tag--info .el-tag__close{color:#fff}.el-tag--dark.el-tag--info .el-tag__close:hover{color:#fff;background-color:#a6a9ad}.el-tag--dark.el-tag--success{background-color:#67c23a;border-color:#67c23a;color:#fff}.el-tag--dark.el-tag--success.is-hit{border-color:#67c23a}.el-tag--dark.el-tag--success .el-tag__close{color:#fff}.el-tag--dark.el-tag--success .el-tag__close:hover{color:#fff;background-color:#85ce61}.el-tag--dark.el-tag--warning{background-color:#e6a23c;border-color:#e6a23c;color:#fff}.el-tag--dark.el-tag--warning.is-hit{border-color:#e6a23c}.el-tag--dark.el-tag--warning .el-tag__close{color:#fff}.el-tag--dark.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#ebb563}.el-tag--dark.el-tag--danger{background-color:#f56c6c;border-color:#f56c6c;color:#fff}.el-tag--dark.el-tag--danger.is-hit{border-color:#f56c6c}.el-tag--dark.el-tag--danger .el-tag__close{color:#fff}.el-tag--dark.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#f78989}.el-tag--plain{background-color:#fff;border-color:#999;color:#000}.el-tag--plain.is-hit{border-color:#000}.el-tag--plain .el-tag__close{color:#000}.el-tag--plain .el-tag__close:hover{color:#fff;background-color:#000}.el-tag--plain.el-tag--info{background-color:#fff;border-color:#d3d4d6;color:#909399}.el-tag--plain.el-tag--info.is-hit{border-color:#909399}.el-tag--plain.el-tag--info .el-tag__close{color:#909399}.el-tag--plain.el-tag--info .el-tag__close:hover{color:#fff;background-color:#909399}.el-tag--plain.el-tag--success{background-color:#fff;border-color:#c2e7b0;color:#67c23a}.el-tag--plain.el-tag--success.is-hit{border-color:#67c23a}.el-tag--plain.el-tag--success .el-tag__close{color:#67c23a}.el-tag--plain.el-tag--success .el-tag__close:hover{color:#fff;background-color:#67c23a}.el-tag--plain.el-tag--warning{background-color:#fff;border-color:#f5dab1;color:#e6a23c}.el-tag--plain.el-tag--warning.is-hit{border-color:#e6a23c}.el-tag--plain.el-tag--warning .el-tag__close{color:#e6a23c}.el-tag--plain.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#e6a23c}.el-tag--plain.el-tag--danger{background-color:#fff;border-color:#fbc4c4;color:#f56c6c}.el-tag--plain.el-tag--danger.is-hit{border-color:#f56c6c}.el-tag--plain.el-tag--danger .el-tag__close{color:#f56c6c}.el-tag--plain.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#f56c6c}.el-tag--medium{height:28px;line-height:26px}.el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--small{height:24px;padding:0 8px;line-height:22px}.el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--mini{height:20px;padding:0 5px;line-height:19px}.el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)}.el-cascader{display:inline-block;position:relative;font-size:14px;line-height:40px}.el-cascader:not(.is-disabled):hover .el-input__inner{cursor:pointer;border-color:#c0c4cc}.el-cascader .el-input .el-input__inner:focus,.el-cascader .el-input.is-focus .el-input__inner{border-color:#000}.el-cascader .el-input{cursor:pointer}.el-cascader .el-input .el-input__inner{text-overflow:ellipsis}.el-cascader .el-input .el-icon-arrow-down{-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:14px}.el-cascader .el-input .el-icon-arrow-down.is-reverse{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.el-cascader .el-input .el-icon-circle-close:hover{color:#909399}.el-cascader--medium{font-size:14px;line-height:36px}.el-cascader--small{font-size:13px;line-height:32px}.el-cascader--mini{font-size:12px;line-height:28px}.el-cascader.is-disabled .el-cascader__label{z-index:2;color:#c0c4cc}.el-cascader__dropdown{margin:5px 0;font-size:14px;background:#fff;border:1px solid #e4e7ed;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-cascader__tags{position:absolute;left:0;right:30px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;line-height:normal;text-align:left;-webkit-box-sizing:border-box;box-sizing:border-box}.el-cascader__tags .el-tag{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;max-width:100%;margin:2px 0 2px 6px;text-overflow:ellipsis;background:#f0f2f5}.el-cascader__tags .el-tag:not(.is-hit){border-color:transparent}.el-cascader__tags .el-tag>span{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow:hidden;text-overflow:ellipsis}.el-cascader__tags .el-tag .el-icon-close{-webkit-box-flex:0;-ms-flex:none;flex:none;background-color:#c0c4cc;color:#fff}.el-cascader__tags .el-tag .el-icon-close:hover{background-color:#909399}.el-cascader__suggestion-panel{border-radius:4px}.el-cascader__suggestion-list{max-height:204px;margin:0;padding:6px 0;font-size:14px;color:#606266;text-align:center}.el-cascader__suggestion-item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:34px;padding:0 15px;text-align:left;outline:0;cursor:pointer}.el-cascader__suggestion-item:focus,.el-cascader__suggestion-item:hover{background:#f5f7fa}.el-cascader__suggestion-item.is-checked{color:#000;font-weight:700}.el-cascader__suggestion-item>span{margin-right:10px}.el-cascader__empty-text{margin:10px 0;color:#c0c4cc}.el-cascader__search-input{-webkit-box-flex:1;-ms-flex:1;flex:1;height:24px;min-width:60px;margin:2px 0 2px 15px;padding:0;color:#606266;border:none;outline:0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-cascader__search-input::-webkit-input-placeholder{color:#c0c4cc}.el-cascader__search-input:-ms-input-placeholder{color:#c0c4cc}.el-cascader__search-input::-ms-input-placeholder{color:#c0c4cc}.el-cascader__search-input::-moz-placeholder{color:#c0c4cc}.el-cascader__search-input::placeholder{color:#c0c4cc}.el-color-predefine{font-size:12px;margin-top:8px;width:280px}.el-color-predefine,.el-color-predefine__colors{display:-webkit-box;display:-ms-flexbox;display:flex}.el-color-predefine__colors{-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-color-predefine__color-selector{margin:0 0 8px 8px;width:20px;height:20px;border-radius:4px;cursor:pointer}.el-color-predefine__color-selector:nth-child(10n+1){margin-left:0}.el-color-predefine__color-selector.selected{-webkit-box-shadow:0 0 3px 2px #000;box-shadow:0 0 3px 2px #000}.el-color-predefine__color-selector>div{display:-webkit-box;display:-ms-flexbox;display:flex;height:100%;border-radius:3px}.el-color-predefine__color-selector.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-hue-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background-color:red;padding:0 2px}.el-color-hue-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,color-stop(0,red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(90deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red);height:100%}.el-color-hue-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-hue-slider.is-vertical{width:12px;height:180px;padding:2px 0}.el-color-hue-slider.is-vertical .el-color-hue-slider__bar{background:-webkit-gradient(linear,left top,left bottom,color-stop(0,red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(180deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red)}.el-color-hue-slider.is-vertical .el-color-hue-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-svpanel{position:relative;width:280px;height:180px}.el-color-svpanel__black,.el-color-svpanel__white{position:absolute;top:0;left:0;right:0;bottom:0}.el-color-svpanel__white{background:-webkit-gradient(linear,left top,right top,from(#fff),to(hsla(0,0%,100%,0)));background:linear-gradient(90deg,#fff,hsla(0,0%,100%,0))}.el-color-svpanel__black{background:-webkit-gradient(linear,left bottom,left top,from(#000),to(transparent));background:linear-gradient(0deg,#000,transparent)}.el-color-svpanel__cursor{position:absolute}.el-color-svpanel__cursor>div{cursor:head;width:4px;height:4px;-webkit-box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);border-radius:50%;-webkit-transform:translate(-2px,-2px);transform:translate(-2px,-2px)}.el-color-alpha-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-alpha-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,color-stop(0,hsla(0,0%,100%,0)),to(#fff));background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,#fff);height:100%}.el-color-alpha-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-alpha-slider.is-vertical{width:20px;height:180px}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__bar{background:-webkit-gradient(linear,left top,left bottom,color-stop(0,hsla(0,0%,100%,0)),to(#fff));background:linear-gradient(180deg,hsla(0,0%,100%,0) 0,#fff)}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-dropdown{width:300px}.el-color-dropdown__main-wrapper{margin-bottom:6px}.el-color-dropdown__main-wrapper:after{content:"";display:table;clear:both}.el-color-dropdown__btns{margin-top:6px;text-align:right}.el-color-dropdown__value{float:left;line-height:26px;font-size:12px;color:#000;width:160px}.el-color-dropdown__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.el-color-dropdown__btn[disabled]{color:#ccc;cursor:not-allowed}.el-color-dropdown__btn:hover{color:#000;border-color:#000}.el-color-dropdown__link-btn{cursor:pointer;color:#000;text-decoration:none;padding:15px;font-size:12px}.el-color-dropdown__link-btn:hover{color:tint(primary,20%)}.el-color-picker{display:inline-block;position:relative;line-height:normal;height:40px}.el-color-picker.is-disabled .el-color-picker__trigger{cursor:not-allowed}.el-color-picker--medium{height:36px}.el-color-picker--medium .el-color-picker__trigger{height:36px;width:36px}.el-color-picker--medium .el-color-picker__mask{height:34px;width:34px}.el-color-picker--small{height:32px}.el-color-picker--small .el-color-picker__trigger{height:32px;width:32px}.el-color-picker--small .el-color-picker__mask{height:30px;width:30px}.el-color-picker--small .el-color-picker__empty,.el-color-picker--small .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker--mini{height:28px}.el-color-picker--mini .el-color-picker__trigger{height:28px;width:28px}.el-color-picker--mini .el-color-picker__mask{height:26px;width:26px}.el-color-picker--mini .el-color-picker__empty,.el-color-picker--mini .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker__mask{height:38px;width:38px;border-radius:4px;position:absolute;top:1px;left:1px;z-index:1;cursor:not-allowed;background-color:hsla(0,0%,100%,.7)}.el-color-picker__trigger{display:inline-block;height:40px;width:40px;padding:4px;border:1px solid #e6e6e6;border-radius:4px;font-size:0;cursor:pointer}.el-color-picker__color,.el-color-picker__trigger{-webkit-box-sizing:border-box;box-sizing:border-box;position:relative}.el-color-picker__color{display:block;border:1px solid #999;border-radius:2px;width:100%;height:100%;text-align:center}.el-color-picker__color.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-picker__color-inner{position:absolute;left:0;top:0;right:0;bottom:0}.el-color-picker__empty,.el-color-picker__icon{top:50%;left:50%;font-size:12px;position:absolute}.el-color-picker__empty{color:#999}.el-color-picker__empty,.el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.el-color-picker__icon{display:inline-block;width:100%;color:#fff;text-align:center}.el-color-picker__panel{position:absolute;z-index:10;padding:6px;-webkit-box-sizing:content-box;box-sizing:content-box;background-color:#fff;border:1px solid #ebeef5;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-textarea{position:relative;display:inline-block;width:100%;vertical-align:bottom;font-size:14px}.el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:inherit;color:#606266;background-color:#fff;background-image:none;border:1px solid #dcdfe6;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-textarea__inner::-webkit-input-placeholder{color:#c0c4cc}.el-textarea__inner:-ms-input-placeholder{color:#c0c4cc}.el-textarea__inner::-ms-input-placeholder{color:#c0c4cc}.el-textarea__inner::-moz-placeholder{color:#c0c4cc}.el-textarea__inner::placeholder{color:#c0c4cc}.el-textarea__inner:hover{border-color:#c0c4cc}.el-textarea__inner:focus{outline:0;border-color:#000}.el-textarea .el-input__count{color:#909399;background:#fff;position:absolute;font-size:12px;bottom:5px;right:10px}.el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::-moz-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::placeholder{color:#c0c4cc}.el-textarea.is-exceed .el-textarea__inner{border-color:#f56c6c}.el-textarea.is-exceed .el-input__count{color:#f56c6c}.el-input{position:relative;font-size:14px;display:inline-block;width:100%}.el-input::-webkit-scrollbar{z-index:11;width:6px}.el-input::-webkit-scrollbar:horizontal{height:6px}.el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.el-input::-webkit-scrollbar-corner,.el-input::-webkit-scrollbar-track{background:#fff}.el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.el-input .el-input__clear{color:#c0c4cc;font-size:14px;cursor:pointer;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-input .el-input__clear:hover{color:#909399}.el-input .el-input__count{height:100%;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#909399;font-size:12px}.el-input .el-input__count .el-input__count-inner{background:#fff;line-height:normal;display:inline-block;padding:0 5px}.el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #dcdfe6;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;display:inline-block;font-size:inherit;height:40px;line-height:40px;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.el-input__prefix,.el-input__suffix{position:absolute;top:0;-webkit-transition:all .3s;height:100%;color:#c0c4cc;text-align:center}.el-input__inner::-webkit-input-placeholder{color:#c0c4cc}.el-input__inner:-ms-input-placeholder{color:#c0c4cc}.el-input__inner::-ms-input-placeholder{color:#c0c4cc}.el-input__inner::-moz-placeholder{color:#c0c4cc}.el-input__inner::placeholder{color:#c0c4cc}.el-input__inner:hover{border-color:#c0c4cc}.el-input.is-active .el-input__inner,.el-input__inner:focus{border-color:#000;outline:0}.el-input__suffix{right:5px;-webkit-transition:all .3s;transition:all .3s}.el-input__suffix-inner{pointer-events:all}.el-input__prefix{left:5px}.el-input__icon,.el-input__prefix{-webkit-transition:all .3s;transition:all .3s}.el-input__icon{height:100%;width:25px;text-align:center;line-height:40px}.el-input__icon:after{content:"";height:100%;width:0;display:inline-block;vertical-align:middle}.el-input__validateIcon{pointer-events:none}.el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::-moz-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__icon{cursor:not-allowed}.el-link,.el-transfer-panel__filter .el-icon-circle-close{cursor:pointer}.el-input.is-exceed .el-input__inner{border-color:#f56c6c}.el-input.is-exceed .el-input__suffix .el-input__count{color:#f56c6c}.el-input--suffix .el-input__inner{padding-right:30px}.el-input--prefix .el-input__inner{padding-left:30px}.el-input--medium{font-size:14px}.el-input--medium .el-input__inner{height:36px;line-height:36px}.el-input--medium .el-input__icon{line-height:36px}.el-input--small{font-size:13px}.el-input--small .el-input__inner{height:32px;line-height:32px}.el-input--small .el-input__icon{line-height:32px}.el-input--mini{font-size:12px}.el-input--mini .el-input__inner{height:28px;line-height:28px}.el-input--mini .el-input__icon{line-height:28px}.el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate;border-spacing:0}.el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.el-input-group__append,.el-input-group__prepend{background-color:#f5f7fa;color:#909399;vertical-align:middle;display:table-cell;position:relative;border:1px solid #dcdfe6;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.el-input-group--prepend .el-input__inner,.el-input-group__append{border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--append .el-input__inner,.el-input-group__prepend{border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group__append:focus,.el-input-group__prepend:focus{outline:0}.el-input-group__append .el-button,.el-input-group__append .el-select,.el-input-group__prepend .el-button,.el-input-group__prepend .el-select{display:inline-block;margin:-10px -20px}.el-input-group__append button.el-button,.el-input-group__append div.el-select .el-input__inner,.el-input-group__append div.el-select:hover .el-input__inner,.el-input-group__prepend button.el-button,.el-input-group__prepend div.el-select .el-input__inner,.el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input{font-size:inherit}.el-input-group__prepend{border-right:0}.el-input-group__append{border-left:0}.el-input-group--append .el-select .el-input.is-focus .el-input__inner,.el-input-group--prepend .el-select .el-input.is-focus .el-input__inner{border-color:transparent}.el-input__inner::-ms-clear{display:none;width:0;height:0}.el-transfer{font-size:14px}.el-transfer__buttons{display:inline-block;vertical-align:middle;padding:0 30px}.el-transfer__button{display:block;margin:0 auto;padding:10px;border-radius:50%;color:#fff;background-color:#000;font-size:0}.el-transfer-panel__item+.el-transfer-panel__item,.el-transfer__button [class*=el-icon-]+span{margin-left:0}.el-transfer__button.is-with-texts{border-radius:4px}.el-transfer__button.is-disabled,.el-transfer__button.is-disabled:hover{border:1px solid #dcdfe6;background-color:#f5f7fa;color:#c0c4cc}.el-transfer__button:first-child{margin-bottom:10px}.el-transfer__button:nth-child(2){margin:0}.el-transfer__button i,.el-transfer__button span{font-size:14px}.el-transfer-panel{border:1px solid #ebeef5;border-radius:4px;overflow:hidden;background:#fff;display:inline-block;vertical-align:middle;width:200px;max-height:100%;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative}.el-transfer-panel__body{height:246px}.el-transfer-panel__body.is-with-footer{padding-bottom:40px}.el-transfer-panel__list{margin:0;padding:6px 0;list-style:none;height:246px;overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box}.el-transfer-panel__list.is-filterable{height:194px;padding-top:0}.el-transfer-panel__item{height:30px;line-height:30px;padding-left:15px;display:block!important}.el-transfer-panel__item.el-checkbox{color:#606266}.el-transfer-panel__item:hover{color:#000}.el-transfer-panel__item.el-checkbox .el-checkbox__label{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;-webkit-box-sizing:border-box;box-sizing:border-box;padding-left:24px;line-height:30px}.el-transfer-panel__item .el-checkbox__input{position:absolute;top:8px}.el-transfer-panel__filter{text-align:center;margin:15px;-webkit-box-sizing:border-box;box-sizing:border-box;display:block;width:auto}.el-transfer-panel__filter .el-input__inner{height:32px;width:100%;font-size:12px;display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:16px;padding-right:10px;padding-left:30px}.el-transfer-panel__filter .el-input__icon{margin-left:5px}.el-transfer-panel .el-transfer-panel__header{height:40px;line-height:40px;background:#f5f7fa;margin:0;padding-left:15px;border-bottom:1px solid #ebeef5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#000}.el-transfer-panel .el-transfer-panel__header .el-checkbox{display:block;line-height:40px}.el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label{font-size:16px;color:#303133;font-weight:400}.el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label span{position:absolute;right:15px;color:#909399;font-size:12px;font-weight:400}.el-divider__text,.el-link{font-weight:500;font-size:14px}.el-transfer-panel .el-transfer-panel__footer{height:40px;background:#fff;margin:0;padding:0;border-top:1px solid #ebeef5;position:absolute;bottom:0;left:0;width:100%;z-index:1}.el-transfer-panel .el-transfer-panel__footer:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-container,.el-timeline-item__node{display:-webkit-box;display:-ms-flexbox}.el-transfer-panel .el-transfer-panel__footer .el-checkbox{padding-left:20px;color:#606266}.el-transfer-panel .el-transfer-panel__empty{margin:0;height:30px;line-height:30px;padding:6px 15px 0;color:#909399;text-align:center}.el-transfer-panel .el-checkbox__label{padding-left:8px}.el-transfer-panel .el-checkbox__inner{height:14px;width:14px;border-radius:3px}.el-transfer-panel .el-checkbox__inner:after{height:6px;width:3px;left:4px}.el-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-box-sizing:border-box;box-sizing:border-box;min-width:0}.el-container.is-vertical,.el-drawer{-webkit-box-orient:vertical;-webkit-box-direction:normal}.el-aside,.el-header{-webkit-box-sizing:border-box}.el-container.is-vertical{-ms-flex-direction:column;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column}.el-header{padding:0 20px}.el-aside,.el-header{-webkit-box-sizing:border-box;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}.el-aside{overflow:auto}.el-footer,.el-main{-webkit-box-sizing:border-box}.el-main{display:block;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;overflow:auto;padding:20px}.el-footer,.el-main{-webkit-box-sizing:border-box;box-sizing:border-box}.el-footer{padding:0 20px;-ms-flex-negative:0;flex-shrink:0}.el-timeline{margin:0;font-size:14px;list-style:none}.el-timeline .el-timeline-item:last-child .el-timeline-item__tail{display:none}.el-timeline-item{position:relative;padding-bottom:20px}.el-timeline-item__wrapper{position:relative;padding-left:28px;top:-3px}.el-timeline-item__tail{position:absolute;left:4px;height:100%;border-left:2px solid #e4e7ed}.el-timeline-item__icon{color:#fff;font-size:13px}.el-timeline-item__node{position:absolute;background-color:#e4e7ed;border-radius:50%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-image__error,.el-timeline-item__dot{display:-webkit-box;display:-ms-flexbox}.el-timeline-item__node--normal{left:-1px;width:12px;height:12px}.el-timeline-item__node--large{left:-2px;width:14px;height:14px}.el-timeline-item__node--primary{background-color:#000}.el-timeline-item__node--success{background-color:#67c23a}.el-timeline-item__node--warning{background-color:#e6a23c}.el-timeline-item__node--danger{background-color:#f56c6c}.el-timeline-item__node--info{background-color:#909399}.el-timeline-item__dot{position:absolute;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-timeline-item__content{color:#303133}.el-timeline-item__timestamp{color:#909399;line-height:1;font-size:13px}.el-timeline-item__timestamp.is-top{margin-bottom:8px;padding-top:4px}.el-timeline-item__timestamp.is-bottom{margin-top:8px}.el-link{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;vertical-align:middle;position:relative;text-decoration:none;outline:0;padding:0}.el-link.is-underline:hover:after{content:"";position:absolute;left:0;right:0;height:0;bottom:0;border-bottom:1px solid #000}.el-link.el-link--default:after,.el-link.el-link--primary.is-underline:hover:after,.el-link.el-link--primary:after{border-color:#000}.el-link.is-disabled{cursor:not-allowed}.el-link [class*=el-icon-]+span{margin-left:5px}.el-link.el-link--default{color:#606266}.el-link.el-link--default:hover{color:#000}.el-link.el-link--default.is-disabled{color:#c0c4cc}.el-link.el-link--primary{color:#000}.el-link.el-link--primary:hover{color:#333}.el-link.el-link--primary.is-disabled{color:grey}.el-link.el-link--danger.is-underline:hover:after,.el-link.el-link--danger:after{border-color:#f56c6c}.el-link.el-link--danger{color:#f56c6c}.el-link.el-link--danger:hover{color:#f78989}.el-link.el-link--danger.is-disabled{color:#fab6b6}.el-link.el-link--success.is-underline:hover:after,.el-link.el-link--success:after{border-color:#67c23a}.el-link.el-link--success{color:#67c23a}.el-link.el-link--success:hover{color:#85ce61}.el-link.el-link--success.is-disabled{color:#b3e19d}.el-link.el-link--warning.is-underline:hover:after,.el-link.el-link--warning:after{border-color:#e6a23c}.el-link.el-link--warning{color:#e6a23c}.el-link.el-link--warning:hover{color:#ebb563}.el-link.el-link--warning.is-disabled{color:#f3d19e}.el-link.el-link--info.is-underline:hover:after,.el-link.el-link--info:after{border-color:#909399}.el-link.el-link--info{color:#909399}.el-link.el-link--info:hover{color:#a6a9ad}.el-link.el-link--info.is-disabled{color:#c8c9cc}.el-divider{background-color:#dcdfe6;position:relative}.el-divider--horizontal{display:block;height:1px;width:100%;margin:24px 0}.el-divider--vertical{display:inline-block;width:1px;height:1em;margin:0 8px;vertical-align:middle;position:relative}.el-divider__text{position:absolute;background-color:#fff;padding:0 20px;color:#303133}.el-image__error,.el-image__placeholder{background:#f5f7fa}.el-divider__text.is-left{left:20px;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-divider__text.is-center{left:50%;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.el-divider__text.is-right{right:20px;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-image__error,.el-image__inner,.el-image__placeholder{width:100%;height:100%}.el-image{position:relative;display:inline-block;overflow:hidden}.el-image__inner{vertical-align:top}.el-image__inner--center{position:relative;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);display:block}.el-image__error{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:14px;color:#c0c4cc;vertical-align:middle}.el-image__preview{cursor:pointer}.el-image-viewer__wrapper{position:fixed;top:0;right:0;bottom:0;left:0}.el-image-viewer__btn{position:absolute;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;opacity:.8;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;user-select:none}.el-button,.el-checkbox,.el-image-viewer__btn{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-image-viewer__close{top:40px;right:40px;width:40px;height:40px;font-size:40px}.el-image-viewer__canvas{width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-image-viewer__actions{left:50%;bottom:30px;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:282px;height:44px;padding:0 23px;background-color:#606266;border-color:#fff;border-radius:22px}.el-image-viewer__actions__inner{width:100%;height:100%;text-align:justify;cursor:default;font-size:23px;color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-pack:distribute;justify-content:space-around}.el-image-viewer__next,.el-image-viewer__prev{top:50%;width:44px;height:44px;font-size:24px;color:#fff;background-color:#606266;border-color:#fff}.el-image-viewer__prev{left:40px}.el-image-viewer__next,.el-image-viewer__prev{-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-image-viewer__next{right:40px;text-indent:2px}.el-image-viewer__mask{position:absolute;width:100%;height:100%;top:0;left:0;opacity:.5;background:#000}.viewer-fade-enter-active{-webkit-animation:viewer-fade-in .3s;animation:viewer-fade-in .3s}.viewer-fade-leave-active{-webkit-animation:viewer-fade-out .3s;animation:viewer-fade-out .3s}@-webkit-keyframes viewer-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes viewer-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@-webkit-keyframes viewer-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes viewer-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#fff;border:1px solid #dcdfe6;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:500;padding:12px 20px;font-size:14px;border-radius:4px}.el-button+.el-button{margin-left:10px}.el-button:focus,.el-button:hover{color:#000;border-color:#b3b3b3;background-color:#e6e6e6}.el-button:active{color:#000;border-color:#000;outline:0}.el-button::-moz-focus-inner{border:0}.el-button [class*=el-icon-]+span{margin-left:5px}.el-button.is-plain:focus,.el-button.is-plain:hover{background:#fff;border-color:#000;color:#000}.el-button.is-active,.el-button.is-plain:active{color:#000;border-color:#000}.el-button.is-plain:active{background:#fff;outline:0}.el-button.is-disabled,.el-button.is-disabled:focus,.el-button.is-disabled:hover{color:#c0c4cc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#ebeef5}.el-button.is-disabled.el-button--text{background-color:transparent}.el-button.is-disabled.is-plain,.el-button.is-disabled.is-plain:focus,.el-button.is-disabled.is-plain:hover{background-color:#fff;border-color:#ebeef5;color:#c0c4cc}.el-button.is-loading{position:relative;pointer-events:none}.el-button.is-loading:before{pointer-events:none;content:"";position:absolute;left:-1px;top:-1px;right:-1px;bottom:-1px;border-radius:inherit;background-color:hsla(0,0%,100%,.35)}.el-button.is-round{border-radius:20px;padding:12px 23px}.el-button.is-circle{border-radius:50%;padding:12px}.el-button--primary{color:#fff;background-color:#000;border-color:#000}.el-button--primary:focus,.el-button--primary:hover{background:#333;border-color:#333;color:#fff}.el-button--primary.is-active,.el-button--primary:active{background:#000;border-color:#000;color:#fff}.el-button--primary:active{outline:0}.el-button--primary.is-disabled,.el-button--primary.is-disabled:active,.el-button--primary.is-disabled:focus,.el-button--primary.is-disabled:hover{color:#fff;background-color:grey;border-color:grey}.el-button--primary.is-plain{color:#000;background:#e6e6e6;border-color:#999}.el-button--primary.is-plain:active,.el-button--primary.is-plain:focus,.el-button--primary.is-plain:hover{background:#000;border-color:#000;color:#fff}.el-button--primary.is-plain:active{outline:0}.el-button--primary.is-plain.is-disabled,.el-button--primary.is-plain.is-disabled:active,.el-button--primary.is-plain.is-disabled:focus,.el-button--primary.is-plain.is-disabled:hover{color:#666;background-color:#e6e6e6;border-color:#ccc}.el-button--success{color:#fff;background-color:#67c23a;border-color:#67c23a}.el-button--success:focus,.el-button--success:hover{background:#85ce61;border-color:#85ce61;color:#fff}.el-button--success.is-active,.el-button--success:active{background:#5daf34;border-color:#5daf34;color:#fff}.el-button--success:active{outline:0}.el-button--success.is-disabled,.el-button--success.is-disabled:active,.el-button--success.is-disabled:focus,.el-button--success.is-disabled:hover{color:#fff;background-color:#b3e19d;border-color:#b3e19d}.el-button--success.is-plain{color:#67c23a;background:#f0f9eb;border-color:#c2e7b0}.el-button--success.is-plain:focus,.el-button--success.is-plain:hover{background:#67c23a;border-color:#67c23a;color:#fff}.el-button--success.is-plain:active{background:#5daf34;border-color:#5daf34;color:#fff;outline:0}.el-button--success.is-plain.is-disabled,.el-button--success.is-plain.is-disabled:active,.el-button--success.is-plain.is-disabled:focus,.el-button--success.is-plain.is-disabled:hover{color:#a4da89;background-color:#f0f9eb;border-color:#e1f3d8}.el-button--warning{color:#fff;background-color:#e6a23c;border-color:#e6a23c}.el-button--warning:focus,.el-button--warning:hover{background:#ebb563;border-color:#ebb563;color:#fff}.el-button--warning.is-active,.el-button--warning:active{background:#cf9236;border-color:#cf9236;color:#fff}.el-button--warning:active{outline:0}.el-button--warning.is-disabled,.el-button--warning.is-disabled:active,.el-button--warning.is-disabled:focus,.el-button--warning.is-disabled:hover{color:#fff;background-color:#f3d19e;border-color:#f3d19e}.el-button--warning.is-plain{color:#e6a23c;background:#fdf6ec;border-color:#f5dab1}.el-button--warning.is-plain:focus,.el-button--warning.is-plain:hover{background:#e6a23c;border-color:#e6a23c;color:#fff}.el-button--warning.is-plain:active{background:#cf9236;border-color:#cf9236;color:#fff;outline:0}.el-button--warning.is-plain.is-disabled,.el-button--warning.is-plain.is-disabled:active,.el-button--warning.is-plain.is-disabled:focus,.el-button--warning.is-plain.is-disabled:hover{color:#f0c78a;background-color:#fdf6ec;border-color:#faecd8}.el-button--danger{color:#fff;background-color:#f56c6c;border-color:#f56c6c}.el-button--danger:focus,.el-button--danger:hover{background:#f78989;border-color:#f78989;color:#fff}.el-button--danger.is-active,.el-button--danger:active{background:#dd6161;border-color:#dd6161;color:#fff}.el-button--danger:active{outline:0}.el-button--danger.is-disabled,.el-button--danger.is-disabled:active,.el-button--danger.is-disabled:focus,.el-button--danger.is-disabled:hover{color:#fff;background-color:#fab6b6;border-color:#fab6b6}.el-button--danger.is-plain{color:#f56c6c;background:#fef0f0;border-color:#fbc4c4}.el-button--danger.is-plain:focus,.el-button--danger.is-plain:hover{background:#f56c6c;border-color:#f56c6c;color:#fff}.el-button--danger.is-plain:active{background:#dd6161;border-color:#dd6161;color:#fff;outline:0}.el-button--danger.is-plain.is-disabled,.el-button--danger.is-plain.is-disabled:active,.el-button--danger.is-plain.is-disabled:focus,.el-button--danger.is-plain.is-disabled:hover{color:#f9a7a7;background-color:#fef0f0;border-color:#fde2e2}.el-button--info{color:#fff;background-color:#909399;border-color:#909399}.el-button--info:focus,.el-button--info:hover{background:#a6a9ad;border-color:#a6a9ad;color:#fff}.el-button--info.is-active,.el-button--info:active{background:#82848a;border-color:#82848a;color:#fff}.el-button--info:active{outline:0}.el-button--info.is-disabled,.el-button--info.is-disabled:active,.el-button--info.is-disabled:focus,.el-button--info.is-disabled:hover{color:#fff;background-color:#c8c9cc;border-color:#c8c9cc}.el-button--info.is-plain{color:#909399;background:#f4f4f5;border-color:#d3d4d6}.el-button--info.is-plain:focus,.el-button--info.is-plain:hover{background:#909399;border-color:#909399;color:#fff}.el-button--info.is-plain:active{background:#82848a;border-color:#82848a;color:#fff;outline:0}.el-button--info.is-plain.is-disabled,.el-button--info.is-plain.is-disabled:active,.el-button--info.is-plain.is-disabled:focus,.el-button--info.is-plain.is-disabled:hover{color:#bcbec2;background-color:#f4f4f5;border-color:#e9e9eb}.el-button--text,.el-button--text.is-disabled,.el-button--text.is-disabled:focus,.el-button--text.is-disabled:hover,.el-button--text:active{border-color:transparent}.el-button--medium{padding:10px 20px;font-size:14px;border-radius:4px}.el-button--mini,.el-button--small{font-size:12px;border-radius:3px}.el-button--medium.is-round{padding:10px 20px}.el-button--medium.is-circle{padding:10px}.el-button--small,.el-button--small.is-round{padding:9px 15px}.el-button--small.is-circle{padding:9px}.el-button--mini,.el-button--mini.is-round{padding:7px 15px}.el-button--mini.is-circle{padding:7px}.el-button--text{color:#000;background:0 0;padding-left:0;padding-right:0}.el-button--text:focus,.el-button--text:hover{color:#333;border-color:transparent;background-color:transparent}.el-button--text:active{color:#000;background-color:transparent}.el-button-group{display:inline-block;vertical-align:middle}.el-button-group:after,.el-button-group:before{display:table;content:""}.el-button-group:after{clear:both}.el-button-group>.el-button{float:left;position:relative}.el-button-group>.el-button+.el-button{margin-left:0}.el-button-group>.el-button.is-disabled{z-index:1}.el-button-group>.el-button:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.el-button-group>.el-button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.el-button-group>.el-button:first-child:last-child{border-radius:4px}.el-button-group>.el-button:first-child:last-child.is-round{border-radius:20px}.el-button-group>.el-button:first-child:last-child.is-circle{border-radius:50%}.el-button-group>.el-button:not(:first-child):not(:last-child){border-radius:0}.el-button-group>.el-button:not(:last-child){margin-right:-1px}.el-button-group>.el-button.is-active,.el-button-group>.el-button:active,.el-button-group>.el-button:focus,.el-button-group>.el-button:hover{z-index:1}.el-button-group>.el-dropdown>.el-button{border-top-left-radius:0;border-bottom-left-radius:0;border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--primary:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--primary:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--primary:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--success:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--success:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--success:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--warning:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--warning:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--warning:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--danger:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--danger:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--danger:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--info:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--info:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--info:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-calendar{background-color:#fff}.el-calendar__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:12px 20px;border-bottom:1px solid #ebeef5}.el-backtop,.el-page-header{display:-webkit-box;display:-ms-flexbox}.el-calendar__title{color:#000;-ms-flex-item-align:center;align-self:center}.el-calendar__body{padding:12px 20px 35px}.el-calendar-table{table-layout:fixed;width:100%}.el-calendar-table thead th{padding:12px 0;color:#606266;font-weight:400}.el-calendar-table:not(.is-range) td.next,.el-calendar-table:not(.is-range) td.prev{color:#c0c4cc}.el-backtop,.el-calendar-table td.is-today{color:#000}.el-calendar-table td{border-bottom:1px solid #ebeef5;border-right:1px solid #ebeef5;vertical-align:top;-webkit-transition:background-color .2s ease;transition:background-color .2s ease}.el-calendar-table td.is-selected{background-color:#f2f8fe}.el-calendar-table tr:first-child td{border-top:1px solid #ebeef5}.el-calendar-table tr td:first-child{border-left:1px solid #ebeef5}.el-calendar-table tr.el-calendar-table__row--hide-border td{border-top:none}.el-calendar-table .el-calendar-day{-webkit-box-sizing:border-box;box-sizing:border-box;padding:8px;height:85px}.el-calendar-table .el-calendar-day:hover{cursor:pointer;background-color:#f2f8fe}.el-backtop{position:fixed;background-color:#fff;width:40px;height:40px;border-radius:50%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;font-size:20px;-webkit-box-shadow:0 0 6px rgba(0,0,0,.12);box-shadow:0 0 6px rgba(0,0,0,.12);cursor:pointer;z-index:5}.el-backtop:hover{background-color:#f2f6fc}.el-page-header{line-height:24px}.el-page-header,.el-page-header__left{display:-webkit-box;display:-ms-flexbox;display:flex}.el-page-header__left{cursor:pointer;margin-right:40px;position:relative}.el-page-header__left:after{content:"";position:absolute;width:1px;height:16px;right:-20px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);background-color:#dcdfe6}.el-checkbox,.el-checkbox__input{display:inline-block;position:relative;white-space:nowrap}.el-page-header__left .el-icon-back{font-size:18px;margin-right:6px;-ms-flex-item-align:center;align-self:center}.el-page-header__title{font-size:14px;font-weight:500}.el-page-header__content{font-size:18px;color:#303133}.el-checkbox{color:#606266;font-size:14px;cursor:pointer;user-select:none;margin-right:30px}.el-checkbox,.el-checkbox-button__inner,.el-radio{font-weight:500;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-checkbox.is-bordered{padding:9px 20px 9px 10px;border-radius:4px;border:1px solid #dcdfe6;-webkit-box-sizing:border-box;box-sizing:border-box;line-height:normal;height:40px}.el-checkbox.is-bordered.is-checked{border-color:#000}.el-checkbox.is-bordered.is-disabled{border-color:#ebeef5;cursor:not-allowed}.el-checkbox.is-bordered+.el-checkbox.is-bordered{margin-left:10px}.el-checkbox.is-bordered.el-checkbox--medium{padding:7px 20px 7px 10px;border-radius:4px;height:36px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label{line-height:17px;font-size:14px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner{height:14px;width:14px}.el-checkbox.is-bordered.el-checkbox--small{padding:5px 15px 5px 10px;border-radius:3px;height:32px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:15px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner:after{height:6px;width:2px}.el-checkbox.is-bordered.el-checkbox--mini{padding:3px 15px 3px 10px;border-radius:3px;height:28px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label{line-height:12px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner:after{height:6px;width:2px}.el-checkbox__input{cursor:pointer;outline:0;line-height:1;vertical-align:middle}.el-checkbox__input.is-disabled .el-checkbox__inner{background-color:#edf2fc;border-color:#dcdfe6;cursor:not-allowed}.el-checkbox__input.is-disabled .el-checkbox__inner:after{cursor:not-allowed;border-color:#c0c4cc}.el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label{cursor:not-allowed}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:#f2f6fc;border-color:#dcdfe6}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner:after{border-color:#c0c4cc}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:#f2f6fc;border-color:#dcdfe6}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner:before{background-color:#c0c4cc;border-color:#c0c4cc}.el-checkbox__input.is-checked .el-checkbox__inner,.el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#000;border-color:#000}.el-checkbox__input.is-disabled+span.el-checkbox__label{color:#c0c4cc;cursor:not-allowed}.el-checkbox__input.is-checked .el-checkbox__inner:after{-webkit-transform:rotate(45deg) scaleY(1);transform:rotate(45deg) scaleY(1)}.el-checkbox__input.is-checked+.el-checkbox__label{color:#000}.el-checkbox__input.is-focus .el-checkbox__inner{border-color:#000}.el-checkbox__input.is-indeterminate .el-checkbox__inner:before{content:"";position:absolute;display:block;background-color:#fff;height:2px;-webkit-transform:scale(.5);transform:scale(.5);left:0;right:0;top:5px}.el-checkbox__input.is-indeterminate .el-checkbox__inner:after{display:none}.el-checkbox__inner{display:inline-block;position:relative;border:1px solid #dcdfe6;border-radius:2px;-webkit-box-sizing:border-box;box-sizing:border-box;width:14px;height:14px;background-color:#fff;z-index:1;-webkit-transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46);transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46)}.el-checkbox__inner:hover{border-color:#000}.el-checkbox__inner:after{-webkit-box-sizing:content-box;box-sizing:content-box;content:"";border:1px solid #fff;border-left:0;border-top:0;height:7px;left:4px;position:absolute;top:1px;-webkit-transform:rotate(45deg) scaleY(0);transform:rotate(45deg) scaleY(0);width:3px;-webkit-transition:-webkit-transform .15s ease-in .05s;transition:-webkit-transform .15s ease-in .05s;transition:transform .15s ease-in .05s;transition:transform .15s ease-in .05s,-webkit-transform .15s ease-in .05s;-webkit-transform-origin:center;transform-origin:center}.el-checkbox__original{opacity:0;outline:0;position:absolute;margin:0;width:0;height:0;z-index:-1}.el-checkbox-button,.el-checkbox-button__inner{display:inline-block;position:relative}.el-checkbox__label{display:inline-block;padding-left:10px;line-height:19px;font-size:14px}.el-checkbox:last-of-type{margin-right:0}.el-checkbox-button__inner{line-height:1;white-space:nowrap;vertical-align:middle;cursor:pointer;background:#fff;border:1px solid #dcdfe6;border-left:0;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.el-checkbox-button__inner.is-round{padding:12px 20px}.el-checkbox-button__inner:hover{color:#000}.el-checkbox-button__inner [class*=el-icon-]{line-height:.9}.el-radio,.el-radio__input{line-height:1;outline:0;white-space:nowrap}.el-checkbox-button__inner [class*=el-icon-]+span{margin-left:5px}.el-checkbox-button__original{opacity:0;outline:0;position:absolute;margin:0;z-index:-1}.el-radio,.el-radio__inner,.el-radio__input{position:relative;display:inline-block}.el-checkbox-button.is-checked .el-checkbox-button__inner{color:#fff;background-color:#000;border-color:#000;-webkit-box-shadow:-1px 0 0 0 #666;box-shadow:-1px 0 0 0 #666}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner{border-left-color:#000}.el-checkbox-button.is-disabled .el-checkbox-button__inner{color:#c0c4cc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#ebeef5;-webkit-box-shadow:none;box-shadow:none}.el-checkbox-button.is-disabled:first-child .el-checkbox-button__inner{border-left-color:#ebeef5}.el-checkbox-button:first-child .el-checkbox-button__inner{border-left:1px solid #dcdfe6;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.el-checkbox-button.is-focus .el-checkbox-button__inner{border-color:#000}.el-checkbox-button:last-child .el-checkbox-button__inner{border-radius:0 4px 4px 0}.el-checkbox-button--medium .el-checkbox-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-checkbox-button--medium .el-checkbox-button__inner.is-round{padding:10px 20px}.el-checkbox-button--small .el-checkbox-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-checkbox-button--small .el-checkbox-button__inner.is-round{padding:9px 15px}.el-checkbox-button--mini .el-checkbox-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-checkbox-button--mini .el-checkbox-button__inner.is-round{padding:7px 15px}.el-checkbox-group{font-size:0}.el-radio,.el-radio--medium.is-bordered .el-radio__label{font-size:14px}.el-radio{color:#606266;cursor:pointer;margin-right:30px}.el-cascader-node>.el-radio,.el-radio:last-child{margin-right:0}.el-radio.is-bordered{padding:12px 20px 0 10px;border-radius:4px;border:1px solid #dcdfe6;-webkit-box-sizing:border-box;box-sizing:border-box;height:40px}.el-radio.is-bordered.is-checked{border-color:#000}.el-radio.is-bordered.is-disabled{cursor:not-allowed;border-color:#ebeef5}.el-radio__input.is-disabled .el-radio__inner,.el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:#f5f7fa;border-color:#e4e7ed}.el-radio.is-bordered+.el-radio.is-bordered{margin-left:10px}.el-radio--medium.is-bordered{padding:10px 20px 0 10px;border-radius:4px;height:36px}.el-radio--mini.is-bordered .el-radio__label,.el-radio--small.is-bordered .el-radio__label{font-size:12px}.el-radio--medium.is-bordered .el-radio__inner{height:14px;width:14px}.el-radio--small.is-bordered{padding:8px 15px 0 10px;border-radius:3px;height:32px}.el-radio--small.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio--mini.is-bordered{padding:6px 15px 0 10px;border-radius:3px;height:28px}.el-radio--mini.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio__input{cursor:pointer;vertical-align:middle}.el-radio__input.is-disabled .el-radio__inner{cursor:not-allowed}.el-radio__input.is-disabled .el-radio__inner:after{cursor:not-allowed;background-color:#f5f7fa}.el-radio__input.is-disabled .el-radio__inner+.el-radio__label{cursor:not-allowed}.el-radio__input.is-disabled.is-checked .el-radio__inner:after{background-color:#c0c4cc}.el-radio__input.is-disabled+span.el-radio__label{color:#c0c4cc;cursor:not-allowed}.el-radio__input.is-checked .el-radio__inner{border-color:#000;background:#000}.el-radio__input.is-checked .el-radio__inner:after{-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}.el-radio__input.is-checked+.el-radio__label{color:#000}.el-radio__input.is-focus .el-radio__inner{border-color:#000}.el-radio__inner{border:1px solid #dcdfe6;border-radius:100%;width:14px;height:14px;background-color:#fff;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box}.el-radio__inner:hover{border-color:#000}.el-radio__inner:after{width:4px;height:4px;border-radius:100%;background-color:#fff;content:"";position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%) scale(0);transform:translate(-50%,-50%) scale(0);-webkit-transition:-webkit-transform .15s ease-in;transition:-webkit-transform .15s ease-in;transition:transform .15s ease-in;transition:transform .15s ease-in,-webkit-transform .15s ease-in}.el-radio__original{opacity:0;outline:0;position:absolute;z-index:-1;top:0;left:0;right:0;bottom:0;margin:0}.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner{-webkit-box-shadow:0 0 2px 2px #000;box-shadow:0 0 2px 2px #000}.el-radio__label{font-size:14px;padding-left:10px}.el-scrollbar{overflow:hidden;position:relative}.el-scrollbar:active>.el-scrollbar__bar,.el-scrollbar:focus>.el-scrollbar__bar,.el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity .34s ease-out;transition:opacity .34s ease-out}.el-scrollbar__wrap{overflow:scroll;height:100%}.el-scrollbar__wrap--hidden-default{scrollbar-width:none}.el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(144,147,153,.3);-webkit-transition:background-color .3s;transition:background-color .3s}.el-scrollbar__thumb:hover{background-color:rgba(144,147,153,.5)}.el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity .12s ease-out;transition:opacity .12s ease-out}.el-scrollbar__bar.is-vertical{width:6px;top:2px}.el-scrollbar__bar.is-vertical>div{width:100%}.el-scrollbar__bar.is-horizontal{height:6px;left:2px}.el-scrollbar__bar.is-horizontal>div{height:100%}.el-cascader-panel{display:-webkit-box;display:-ms-flexbox;display:flex;border-radius:4px;font-size:14px}.el-cascader-panel.is-bordered{border:1px solid #e4e7ed;border-radius:4px}.el-cascader-menu{min-width:180px;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;border-right:1px solid #e4e7ed}.el-cascader-menu:last-child{border-right:none}.el-cascader-menu:last-child .el-cascader-node{padding-right:20px}.el-cascader-menu__wrap{height:204px}.el-cascader-menu__list{position:relative;min-height:100%;margin:0;padding:6px 0;list-style:none;-webkit-box-sizing:border-box;box-sizing:border-box}.el-avatar,.el-drawer{-webkit-box-sizing:border-box;overflow:hidden}.el-cascader-menu__hover-zone{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.el-cascader-menu__empty-text{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);text-align:center;color:#c0c4cc}.el-cascader-node{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0 30px 0 20px;height:34px;line-height:34px;outline:0}.el-cascader-node.is-selectable.in-active-path{color:#606266}.el-cascader-node.in-active-path,.el-cascader-node.is-active,.el-cascader-node.is-selectable.in-checked-path{color:#000;font-weight:700}.el-cascader-node:not(.is-disabled){cursor:pointer}.el-cascader-node:not(.is-disabled):focus,.el-cascader-node:not(.is-disabled):hover{background:#f5f7fa}.el-cascader-node.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-cascader-node__prefix{position:absolute;left:10px}.el-cascader-node__postfix{position:absolute;right:10px}.el-cascader-node__label{-webkit-box-flex:1;-ms-flex:1;flex:1;padding:0 10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.el-cascader-node>.el-radio .el-radio__label{padding-left:0}.el-avatar{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;color:#fff;background:#c0c4cc;width:40px;height:40px;line-height:40px;font-size:14px}.el-avatar>img{display:block;height:100%;vertical-align:middle}.el-drawer,.el-drawer__header{display:-webkit-box;display:-ms-flexbox}.el-avatar--circle{border-radius:50%}.el-avatar--square{border-radius:4px}.el-avatar--icon{font-size:18px}.el-avatar--large{width:40px;height:40px;line-height:40px}.el-avatar--medium{width:36px;height:36px;line-height:36px}.el-avatar--small{width:28px;height:28px;line-height:28px}.el-drawer.btt,.el-drawer.ttb,.el-drawer__container{left:0;right:0;width:100%}.el-drawer.ltr,.el-drawer.rtl,.el-drawer__container{top:0;bottom:0;height:100%}@-webkit-keyframes el-drawer-fade-in{0%{opacity:0}to{opacity:1}}@keyframes el-drawer-fade-in{0%{opacity:0}to{opacity:1}}@-webkit-keyframes rtl-drawer-in{0%{-webkit-transform:translate(100%);transform:translate(100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@keyframes rtl-drawer-in{0%{-webkit-transform:translate(100%);transform:translate(100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@-webkit-keyframes rtl-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translate(100%);transform:translate(100%)}}@keyframes rtl-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translate(100%);transform:translate(100%)}}@-webkit-keyframes ltr-drawer-in{0%{-webkit-transform:translate(-100%);transform:translate(-100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@keyframes ltr-drawer-in{0%{-webkit-transform:translate(-100%);transform:translate(-100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@-webkit-keyframes ltr-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translate(-100%);transform:translate(-100%)}}@keyframes ltr-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translate(-100%);transform:translate(-100%)}}@-webkit-keyframes ttb-drawer-in{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@keyframes ttb-drawer-in{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@-webkit-keyframes ttb-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translateY(-100%);transform:translateY(-100%)}}@keyframes ttb-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translateY(-100%);transform:translateY(-100%)}}@-webkit-keyframes btt-drawer-in{0%{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@keyframes btt-drawer-in{0%{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@-webkit-keyframes btt-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translateY(100%);transform:translateY(100%)}}@keyframes btt-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translateY(100%);transform:translateY(100%)}}.el-drawer{position:absolute;-webkit-box-sizing:border-box;box-sizing:border-box;background-color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;-webkit-box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12);box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12)}.el-drawer.rtl{-webkit-animation:rtl-drawer-out .3s;animation:rtl-drawer-out .3s;right:0}.el-drawer__open .el-drawer.rtl{-webkit-animation:rtl-drawer-in .3s 1ms;animation:rtl-drawer-in .3s 1ms}.el-drawer.ltr{-webkit-animation:ltr-drawer-out .3s;animation:ltr-drawer-out .3s;left:0}.el-drawer__open .el-drawer.ltr{-webkit-animation:ltr-drawer-in .3s 1ms;animation:ltr-drawer-in .3s 1ms}.el-drawer.ttb{-webkit-animation:ttb-drawer-out .3s;animation:ttb-drawer-out .3s;top:0}.el-drawer__open .el-drawer.ttb{-webkit-animation:ttb-drawer-in .3s 1ms;animation:ttb-drawer-in .3s 1ms}.el-drawer.btt{-webkit-animation:btt-drawer-out .3s;animation:btt-drawer-out .3s;bottom:0}.el-drawer__open .el-drawer.btt{-webkit-animation:btt-drawer-in .3s 1ms;animation:btt-drawer-in .3s 1ms}.el-drawer__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:hidden;margin:0}.el-drawer__header{-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#72767b;display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:32px;padding:20px 20px 0}.el-drawer__header>:first-child,.el-drawer__title{-webkit-box-flex:1;-ms-flex:1;flex:1}.el-drawer__title{margin:0;line-height:inherit;font-size:1rem}.el-drawer__close-btn{border:none;cursor:pointer;font-size:20px;color:inherit;background-color:transparent}.el-drawer__body{-webkit-box-flex:1;-ms-flex:1;flex:1}.el-drawer__body>*{-webkit-box-sizing:border-box;box-sizing:border-box}.el-drawer__container{position:relative}.el-drawer-fade-enter-active{-webkit-animation:el-drawer-fade-in .3s;animation:el-drawer-fade-in .3s}.el-drawer-fade-leave-active{animation:el-drawer-fade-in .3s reverse}.el-popconfirm__main{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-popconfirm__icon{margin-right:5px}.el-popconfirm__action{text-align:right;margin:0}
/*# sourceMappingURL=app.7b6729291f05cec91b99cfd44c17df6b.css.map */
================================================
FILE: wtpy/monitor/static/console/static/css/static/__init__.py
================================================
================================================
FILE: wtpy/monitor/static/console/static/css/static/fonts/__init__.py
================================================
================================================
FILE: wtpy/monitor/static/console/static/js/__init__.py
================================================
================================================
FILE: wtpy/monitor/static/console/static/js/app.d3652914f874ea570f72.js
================================================
webpackJsonp([1],{"+B9X":function(t,e){},"4/hK":function(t,e){},"7Otq":function(t,e){t.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAABaNSURBVHja7J15eFTlvcc/Z5slM5nJvkEIWwICgqAoKFUEC7jTul5rq3axdn16e61t773VWuu1avVavHax9kr1WqtAXaqoFAsFccEFkC0QCCEkIWSZbLPPnHPuH+9JGmIimclMQMn3eeYZNM+Z95z3e36/97e9v1eqmLeMExAKoFrfBcBEYJL1nQ/kAHlAFuACNMAEdCAAtAOtvT77gEqgGjgMxK2PfqI9uHoC3YsG2IBi4FxgLnA6UGH9TbY+AJL1+TiYvb4N66MDVcAW4C3gDaAGiAKxEULE+HZgAnAJcBEwqx8CkoHU67v370wHTgWutwjaBrwCvAzsAiLHkxzpOKksh6V6LgWuBs5JAQFDQbcEvQ+sAFYCTUDo005IBjAa+C5wDZA7CNUz3DCBTuAvwK+s9ScwXIPLwygR5cCDwAfAt6xF+UQjo1vFeYGbgHeBR4GpgPPTQIgClAI/sR7u65ZV9EmBBlxnqbJfAGOt/5e+Ccsdc2G6fttrrQ+PA5dZUvJJhQqcZT1PyDKfg58UCZGAycATwHJgPJ8elAAPA88AMywNcEIT4gSWAn+zpOLTigXAeuBLgPtEJaQQuN2yTkbz6UcW8L/A/dY6eUIR0q2ifsTJh1ss32XGiULITOApYBEnL84CViHCPceVkLOB56xwx8mOCYjwy6LjRcgC6wbKRrjoQbY1J1cONyFnW4t31ggH/foszyYrKckQMgt42nL8RjCwL7YCETRNKyGTgceAMSNzfkx4LGNnZroIyQMeSnSAkxxliOBkaaoJkYDvA4tH5jhhnAH8J4OM5Q2WkEuBH4/MbdK4GZGhTAkhp1jrxgiGht9Y0jIkQlzAPYh06wiGbg4vQ1TMJE3IUuDykblMGeYCNyZLSAnwy5E5TDl+jgizJEzILUDRyPylHE7gPwaa+4EImWCZaiNID25C1IZJgyFEAm7lxKwI+TThbvopVJQHkI5bRuYr7bgYUUUpfRwhCvC9Ad11SUJR5KM+knRiCZIs97lHeXjur7+5kY89N7f35aCvyBT0Z5bJEsiKTCAYpaMzgq4b4mJNxuN24HZp6HET3TSPm55TVRnDMGnvCBMMRjFME0mScDo0srOcqIpELG6kdEzTBEWRkGWJYDBGZyBCPCbGUBSJTJcdl8sGpolumP39xGWIQO2BHonoVZelIOqOruj7xpkSHDnip6Mrgl1T8GTacTpUojGdtvYQ4Ugcd4YNVZXRdZPhFBpJktA0hS5/hIbDnUTjJh63DY/bjqoq+AMxfO0hFFnG6bT1TGQqyLDbFULhuJgbfwRFkclwatg0lbhu0OmP0umPYFNlHA4Vo39S2oBNiNrioyREBb7WlwzDMDh4qAOPx85XrzidBfPGUlzoAeBQfQfr3zrAM8/vYG+Nj/Kx2WiaTCxuDIukSBJomkJTi5+mxk7mzC7jmqXTmDmtGE+mnUAwwo7KZp55fgfr36gmL99FYUEmYAw0OYOG3a7Q3hGirqGL0mIPSy88hblnjKaowI2ETIsvwOZt9by8Zi+79zVTmO8mNzuDeFynz8hfBB4A/HB0sfUkxKaWnocFiYO1bYwtzeKBO5dw0YLyfm/updf3cutPX2PPvhamnlJILGYAZlrJME1wOBSajvg50uTnli+fxV23LSAv+6MluP5glDsfWM+Dv9lEVpaTkmIP0ag+pHHb2kLUHWzj6itm8N2vzuGc2f1H2Cv3t3DfI5t4/OkPKCzMJDfLia4bfWdnHvAmYHarLMWSjgXdq74kQVNrAK/LzrOPXct5cwZOnVeMz+Xs2aW8vGYPTY1+8vJcPetM+tYMia5gjIbqFm7+8ln89t5LyXD2X3Zr0xQWnTeBUFRn7dq9aHYVl8uWlJSoikQoqnOwsolbbpnL8oc+x5hRAydP83IyuHzxZGKGyatr9+LM0NBsSu/31UTsSXkNMLoJsSEKvkZ1kxGJ6rT4gtx7+yIu+2zFMW+0uDCTokIPT6/YRma2M+3WjU1TqNrbzMwzSnnsgctxu2zHDiSdUcr72w+zdUcjOTkZyakqTaVy+2GWXDKFPz1y1aDXy4XnjGP3vhbe3VKPx2NH/uf8SJYx9SgQlXvFrXoygYZhEgzGOG1qMV++ZvAJwiXnlzN77lgOHWpHltNXWC/LEu2dYVRF4qZrZ1GUP7hqTpdT48brZuF02WhpDSZ8j7Is0dgawO2xc/cPFyZsvNx/+yKK89wEgtG+hkUZIs0hde9amt/bQdF1k2A4ypkzR6GpcgJvrczs6cVEOyNptbQUWaalLUROfibTJhUkdO3kCXkU5rno7Er8HhVFpulQOwsXTmJ8WU7C9z2mxMviC8rp6IwQP1ql64gqFVW2rKtzeq8dBhCPm2RmaAnesCT0uJRe01eSIB7T0VQZmy2xAnSnU8NhU5MyOuJxA0yT2dOLcbuS2yYyZ9YoTN3o6x7IiNC81E3Imb0lRJaEo9XeFUlwoVUozHeDqqLHzbQSYugmLqeK121L6NpAIEooEk8qwqDrJjhUcrKdqEpyKrkoPxPNptLHnpAQO44zZMQ+v/LeZp0qCwfnva31RKLxQQ+mqTJnnjYap9dBZyBKutZ13TDRYzqTJuQzbkx2QtfuqGyisSWAJ9OekIMo+DNBFmGRZBGJxjEMo78gYiFQJltkKEcvXpDh0Ni6s5Hlz25LaMBxY7yMHeWlpdWPrKR+YZdlCX8wCrLEmbNG4coYvIQEgjGeWLmNcDBGXk7ipnk4GsdpVyktSb5GcF9NG/G40d/LGgemycCUvhFHE7DbVDyZTn724Ho+2NE46AG9Hgezp5cQ9YVQ1NQToigKvtYgmS47Z0wfldC1dzywjtfXVVFSnImsJPoiyLS2BSnMczE+QansjTUb9uFw2vqbG7mbkPK+EUfTFBQVFbhobvVz9dee4dX1+4QOPQY8bjtLFpSDKhEMxVK+uMsy+DvDzJhSyOzTSgZ1zcH6dm698zUeePgNsvPd5OU4rWhCYo5oyBdi8oRccrzJbch97pXdbHi7hqxMG4oi9VWZMjBaRVQk9hMiMDFNmDg2l737W7jq5hVct3Qai+ZPZEyJl/LxOWR5+q/9mnv6aMZNKqDhgI+KqUUEg6khRlUl2jpCEDe54PxycrMHnpiqGh+1dR1U7mvhqVXbeGtDNTmjvYwqSi5souuAbnDe3LFkZSW+f3XvgVZuveNVbJqK3dZvAw0JyFMRJfT96hbDMJFlickVBdTWdfDoHzfzxKptFOS4mDGlkEXzJzDvrDJOm3J06r2kKJOvXD2T//z+84QqClAUacjBPDDRbDbqa5soKPFwzWVTPxo27Qzz9vt1rPnHfjZs2E9NYye+9jDoBmMq8snKchIOxxM2eVVVoqGpC3uum/PPGZ+QbwbwwY7DfOO2F6mpa6diYj6m9cL3g+wBJaQ3KYahUzbaS7Qok1ZfgIbGTmprfPx17V4mjs5i0cJyrrl8GueeVdYT1rjhX2bx5Mqt7HmvlunzxhH2R4cQSDRxZGi0HOnC9AX59m0LmDwht+fvR1qDrHp5Jy++tIs33j9EoD2MpEg43HZKR3nxehwYhkEoCRVqmiY2m432A618/rrTmVoxuBK1QCBK9aE21r9Zw0OPvUP1/hbKy/NAAnPgl9MjVcxbVoVoezQo00+WZVRFwjChrT3EkcZO9HCMgiIP1145g+/dfDbjRgsr5JW1e/n8FY+jehyMm1JEPBIjHk/caXQ6Nbq6wlS/XcOF18zi/x69mhxLXT63Zg8PP7yRdW8cAEzcuRkUF3lxODUM3UDXkw+1d+c8mlqCNNe2sfovN7Hk/I9OVZMvyK49TVRV+6g60Io/EKW5NcCuvc3s3teCLMPEcbkosiScy49Z7qSKecvqrVhWEhaPhKophIIxDtW2EfGFOGtuGfffczGfmS12LDyyfDPf/sZKbG47ZZMLyHBqhMMxDINjEqNpCpIq01TfTtOuRuYunsKTv7+aCaVZROIGd937d37x0D/QQ1GKxueRW+BGMk1iMX3ISSjTFM6xpMnsWlvJVV+fxxO/+hwO+z/1/+ZtDaxZV8XfN1azo6qFto6QyBiaJhgmsqZQVOAmN9tJLK5jHNuOOCxVzFvWxBBLRWVZQrOpdHWGqdnRQMnYPJ78/dUsOHssAL978j3uvmcth2rbySxwUzTai9Ouocd14rp5lD6VZQlVlUGS8LX4OVzbjhmLc+UVp3H/f13M2BIPwajOrT9ZzW/uex13aQ5lFfnIpvATUmHUGSbYNQVUiZ1v1jBhbC4vPn8TU8rFNFVWt/K7J9/nuee3c7C+HQxwZNrwZjlxO20osoh7yYospHTwEtqcEkK6nRfNpqAbJpVb6iguzeLVVTcx3Qr+vf1hA7/7wzs8++J2gh0RVLtKZo6TXG8Gdod46wzdoMMfxdcaINwVBlnijClFfPGGM/nmDWf0hCvuvH8dP73tefImFzOqLIdIMIpuDD1+Zln7OJ0awWCcqi2HKMxz8dRT17NwnmhIsWL1bu762Rq2727E5rZTVCLWKNM0hXq0Xq4kJbR5SCqrXzHXZOJxg6oPG1j02Um88KfrrWCewOp1Vbz4SiX/2FjNgSNdREIx0M2eHnGSqpDtsTNrSiEXLTmFyxdPZnxp1j+vX7+Pq65YjpxhY+K0IoL+SCreJRRJwmZTiOkGjXXttB1oY9rMEn754FIWf0aQsezxzfzbj14iHo4xflIBLq+TeExPZTLusFQxb9k+PqbWNJmnU20Kra1BjtR3svy3V3DDlR/dU7+9sonKqmbe3VbPjspmYnGdwgI3c2aO5pSKfGZMLSQv6+gkkq8jzNIbn2Lj6kpOnT9RkDmExUKShHpUFJlgMEpDQyfBtiCyU+X6z03ntu/PZ+pEYYS+sGYPS697Ak1VmDStGFMXa1WKiwcOShXzlr1PiveZS5KYp+radmZPK+b1VTfgdAwcrm7rDBOP6Xg9DmzawDGNP67Yxo23rCC/xENhQSaRBNeMbpWkqQpIEAxGafYFCbSFMHUDb76bxfMncOVl07jq4ik91x2oa2fhZY9xqL6D8mlFEDeJG2kpeapUgZZU/6ppgqbJ5GTa2bqzgdc3HeCShQOngbM9x/Z8O7rCrHxhO0TjFBVlEgknRoZhmthUBVVVqK3voL2lCxQFV6adWTOKWTh/Iovml3PunLKPOH73/noTB2raGF+Rh2RAzEhb/VmnCvgQOamURgJ13cTpstHWFWb12qqPJWRwoQcfmz9sQPU6Es4tmSY4bCpxw2TXrkZMVeLSi6Zw7pwypk4uoHx8LhMHyADuO+jjxRd34M7JwJFhIx5La4lTh4roa5tyGIaJw6HicGhs3lJPW0eYbG/yPcx2722hpS1EQYFb6O4EQx+6CZUfNlBals0v7ljMReeXkzWI+3nmhZ0945rpLaQxgRYZ0eQxLUPJkoSmStQc9PHB9oakfyceN3jrvVqMUIxsrzMxz9sEm11h/+4jTJyYx58fu5brlp46KDICwSivrd+H4tRwZTrSXdpkAHUyoldt2qCpMuGozt7q5AWx2RdkV1WziA4kWCmiqjJ1h7swJbjjxxdw9umDb+W1ZWcjuyuP4FTlpFO2CRKyU0Z0ek5LAtw0TDRNxZSg4UhX0r/T4gvQ1BJAtqsDRUk/Jrwj03qog3PPm8jSxZMSunbnnmaC4TiapmCmWV8hsrY7ZUTD4H3pUoqK5T6LsHdyiMV0otE4UoJJekmSCIaiYJNZeM5Y3BmJFUTsr2kFWcLu1AYThxoqfEC1jGir/W56pMREViSR5QskH343DJOYbiaco5ckkUe3uWyMK0s87XqwrgNZApuqkO5aZUQrWr+MKNJ6Kx0jmlZ8yzSFZx5JssC57nAXTU1d5FiFygmtYZqCHjPoSjDEYhgmvvYwJlJ/6dZ0rB/vAEY3IetI055CVZax21S27WrkbxuqE74+Eo2z8qWdRNpC5OVmJGRhGYaJ22VD74qwZVvDoGoCulHf2EVdfTuY5pDKfgZrkAJrAL17pFpgRzpG0g2T3OwMouEYv/yfjYQiia0lL62tYuUzW8kvzUJK4i1VFIkMr4OVq3cnZOmtenkX1bVtCddvJYkGYDu9tiOAKNT6TDokRdNkbJrChzsO0+aPcvHC8kFd9/YHdXztO6voDOmMn5BLOKInHGI3DMjKyaB2TxN1rQEuX3LKMXPiH2xv5Fv//hJx3aAo351IPiNZ22cVondlz3YEw/LYv5mWEU0Th8OGpMqs37CfQ0e6OP3UEjyZ9gGv+dNz2/nGD/5K7aF2Jk0rwtBNjCRzHpom43Db2byphoNHuph/zjicjv6PTtn4Ti1f+d5z1NS1M7Y0G0lK+/ohAT8A9sPReww7EJsQC9NDCmS6bMiqwhubDrDujRr8IXFuSjRmEAhGqW3oZOPbB7nv15u4d9kGOvxRJlYUIEsiF51sAkrXTVwuO5pDY9NbNby5pR5ME1WRMQ1o7wyzq6qZ5c9u5cd3/42aQ+2Uj8tFUeV0RXV7oxZxWEEYjt7SpgDfAf47ba+CJKEqMv5AlKYWP0gSuV4H3iwRdu/0R/H5gvhDUbwuO3l5bhRZIhrTU1LXZbMpdHZFaGoOoGkyBblucrKdxHWdlpYATb4QDpsoGFdVue+WgXThPkSrjXhfQkC0CK8m3UcyKDIgtjD7A1HEcwt1ZNMUsr1OnE6NeNxIWk0NpKxtmkI8ZtDaHiQUjvWoI0WSyMy0k+V1YBpmuteN3pgM7O12O/oq0sOIjqNfSucddPsSXo+DLK/TmhSzZ6OpYZg9Ed1UlqJKltcPkJ+b0aNKu6XXNM1jlemkGusQe9TN3vZvXwflnuG6G8OwCgMMw/q3+G9zGOxMMZZpFQKaQ6rfGgLuoM8BZHI/Ul1lSckI0ouN/YWs5AHc+LtG5ivt+FG3ZXUsQkxEA4FfjcxZ2rDSkg4GQ0g3KQ8ijo8bQerxEwY4vFI+hsMy0qs39fgZvVqYJEIIiN7l60bmMGXYhejfS7KEdCBajIdG5jIl+DrQOBRCALYy0vIvFfgB4nRqhkoIwJ+BR0bmNGk8g2guQ6oIiVoe/Dsjc5sw9llWVWcqCQGot3Rg9cgcDxrtiI5xVYO9INFk8TZrgI6RuR4UrgXeTuSCZLL3bwJXMQx1MZ9wXIfoEke6CQFx1u01iIqVEXwU37QMIYaLEBCnkF08or6Ogg58wbKozOEmBEskL6RXI+CTGK2Io6GeHormSEUF2FvA5xGlkCcr9lra4pWhrq2pKsnbiuiI/YeTkIwVwCWp8tFSWSN5EPhXRJil6yQgIg780HreqlT9aKqLVrssKTmPQcRtPsHYhujk+hBiGwEnKiHdb84WxInJPwSaP0VEdCEOYrkE0UA/muoB0lnWfcR6g84GlpOmfYzDiJWI7q0/B+rSNUi66+yjiODat4E5wAufQCLWAucjzo2qpJ/ChE8SId0IIJL611sSs9J6sBM1/KIDq4ELLJN+PdZxEumGOswP6rf8lhsQZatfsMzlSYja4uN1QI9pqdT9lhQ/ZUl2YLhvpG9t73DDBmQgujpfBiwBxlmSK6eRoG4CDGs9WAs8j4jMBhDHRxwXHG9Ceu7DIscJTEVsHJqDOMw3rw9BUgJEmb0+3afMtFlRhc2I6sGtFgnRE0GFqpwY6D7UJGKZk+9Y95ZhScwkRMPnUkSztWwg0/q7o5e60621KWipRx8ixlSHqPjY00sV6QxQG3U88f8DAOnpVoI55ywSAAAAAElFTkSuQmCC"},"7tVw":function(t,e){},C17c:function(t,e){},Cb2R:function(t,e){},DoUX:function(t,e){},GUiZ:function(t,e){},JgyK:function(t,e){},LxZp:function(t,e){},NHnr:function(t,e,s){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a=s("7+uW"),i={render:function(){var t=this.$createElement,e=this._self._c||t;return e("div",{attrs:{id:"app"}},[e("router-view")],1)},staticRenderFns:[]};var n=s("VU/8")({name:"App"},i,!1,function(t){s("wx9h")},null,null).exports,o=s("/ocq"),r={name:"Login",data:function(){return{loginid:"",passwd:"",saveuser:!0,loading:!1}},methods:{doLogin:function(){var t=this,e=this,s=e.loginid,a=e.passwd;e.loading=!0,this.$api.login(s,a,function(a){if(a.result<0)e.$alert("用户登录出错:"+a.message,"登录失败");else{var i=a.userinfo;localStorage.setItem("last_user",s),localStorage.setItem("save_user",e.saveuser?"true":"false"),t.$store.commit("loginok",{isLogined:!0,loginid:s,userinfo:i}),t.$socket.emit("connect",1),e.$router.push("/index")}e.loading=!1})}},mounted:function(){this.saveuser="true"==localStorage.getItem("save_user"),this.saveuser&&(this.loginid=localStorage.getItem("last_user"))}},l={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{directives:[{name:"loading",rawName:"v-loading",value:t.loading,expression:"loading"}],staticClass:"loginBox"},[s("div",{staticClass:"login"},[s("div",{staticClass:"loginForm"},[s("el-row",[s("h2",[t._v("交易控制中心")])]),t._v(" "),s("el-row",[s("el-input",{attrs:{placeholder:"用户名"},model:{value:t.loginid,callback:function(e){t.loginid=e},expression:"loginid"}})],1),t._v(" "),s("el-row",[s("el-input",{attrs:{placeholder:"密码",type:"password"},model:{value:t.passwd,callback:function(e){t.passwd=e},expression:"passwd"}})],1),t._v(" "),s("el-row",[s("el-checkbox",{staticStyle:{float:"right"},model:{value:t.saveuser,callback:function(e){t.saveuser=e},expression:"saveuser"}},[t._v("保存用户名")])],1),t._v(" "),s("el-row",{staticStyle:{"margin-top":"30px"}},[s("el-button",{attrs:{type:"primary",plain:""},on:{click:function(e){return t.doLogin()}}},[t._v("登 录")])],1)],1)])])},staticRenderFns:[]};var c=s("VU/8")(r,l,!1,function(t){s("kk+i")},"data-v-02eab36d",null).exports,u=s("Dd8w"),d=s.n(u),p=s("NYxO"),f={name:"index",computed:d()({},Object(p.b)(["cache"]),{isAdmin:function(){var t=this.cache.userinfo;return!!t&&("admin"==t.role||"superman"==t.role)}}),components:{},data:function(){return{showDlgModPwd:!1,oldpwd:"",newpwd:"",confirmpwd:"",loading:!1,notifies:[],lastNotify:null,showNotifies:!1}},methods:{handleItemSel:function(t,e,s,a){},onLogout:function(t){var e=this;this.$confirm("确定要注销登录吗?","注销登录",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){e.$store.commit("logoutok"),e.$router.push("/login")}).catch(function(){})},onModPwd:function(t){this.showDlgModPwd=!0},doModPwd:function(){var t=this,e=this;""!=this.newpwd&&""!=this.oldpwd&&""!=this.confirmpwd?this.newpwd==this.confirmpwd?(this.loading=!0,this.$api.modpwd(this.oldpwd,this.newpwd,function(s){s.result<0?e.$notify.error("密码修改失败:"+s.message,"修改密码"):(e.$notify.success("密码修改成功","修改密码"),t.showDlgModPwd=!1),setTimeout(function(){t.loading=!1},150)})):this.$alert("两次输入的新密码不一致"):this.$alert("密码不能为空")},handleNotify:function(t){this.notifies.push(t),this.notifies.length>100&&(this.notifies=this.notifies.slice(this.notifies.length-100)),this.lastNotify=t},onClickScroller:function(t){this.showNotifies=!this.showNotifies},getTitleStyle:function(t){return"成交回报"==t?"title-success":"title-warning"},getIconStyle:function(t){return"成交回报"==t?"el-icon-news":"el-icon-warning-outline"}},mounted:function(){this.cache.isLogined||this.$router.push("/login")}},m={render:function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{directives:[{name:"loading",rawName:"v-loading",value:t.loading,expression:"loading"}],staticStyle:{height:"100%"},attrs:{id:"index"}},[a("el-container",{staticStyle:{height:"100%"}},[a("el-aside",{staticStyle:{"border-right":"2px solid #E4E7ED",height:"100%"},attrs:{width:"230px"}},[a("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[a("div",{staticStyle:{flex:"0",height:"44px !important","border-bottom":"1px solid #E4E7ED"}},[a("div",{staticStyle:{display:"flex","flex-direction":"row","margin-left":"12px"}},[a("div",{staticStyle:{flex:"0","margin-top":"6px","padding-right":"12px"}},[a("img",{attrs:{src:s("7Otq"),height:"28px",width:"28px"}})]),t._v(" "),a("div",{staticStyle:{flex:"1","margin-top":"4px",overflow:"hidden"}},[a("span",{staticStyle:{"font-size":"24px"}},[t._v("WT控制台")])])])]),t._v(" "),a("div",{staticStyle:{flex:"1"}},[a("div",{staticStyle:{display:"flex","flex-direction":"column",height:"100%"}},[a("div",{staticStyle:{flex:"1",display:"block","border-bottom":"1px solid #E4E7ED"}},[a("el-menu",{staticClass:"el-menu-vertical-demo",staticStyle:{height:"100%"},attrs:{"default-active":"1-1",router:""},on:{select:t.handleItemSel}},[a("el-submenu",{attrs:{index:"1"}},[a("template",{slot:"title"},[a("i",{staticClass:"el-icon-set-up"}),t._v(" "),a("span",[t._v("控制台")])]),t._v(" "),a("el-menu-item-group",[a("el-menu-item",{attrs:{index:"1-1",route:"/monitor"}},[a("i",{staticClass:"el-icon-view"}),t._v(" "),a("span",[t._v("监控中心")])]),t._v(" "),t.isAdmin?a("el-menu-item",{attrs:{index:"1-2",route:"/schedule"}},[a("i",{staticClass:"el-icon-time"}),t._v(" "),a("span",[t._v("调度中心")])]):t._e()],1)],2),t._v(" "),t.isAdmin?a("el-submenu",{attrs:{index:"2"}},[a("template",{slot:"title"},[a("i",{staticClass:"el-icon-box"}),t._v(" "),a("span",[t._v("自动实施")])]),t._v(" "),a("el-menu-item-group",[a("el-menu-item",{attrs:{index:"2-1",route:"/deploy"}},[a("i",{staticClass:"el-icon-thumb"}),t._v(" "),a("span",[t._v("策略部署")])]),t._v(" "),a("el-menu-item",{attrs:{index:"2-2",route:"/backtest"}},[a("i",{staticClass:"el-icon-data-line"}),t._v(" "),a("span",[t._v("在线回测")])])],1)],2):t._e(),t._v(" "),t.isAdmin?a("el-submenu",{attrs:{index:"3"}},[a("template",{slot:"title"},[a("i",{staticClass:"el-icon-setting"}),t._v(" "),a("span",[t._v("系统设置")])]),t._v(" "),a("el-menu-item-group",[a("el-menu-item",{attrs:{index:"3-1",route:"/admins"}},[a("i",{staticClass:"el-icon-user"}),t._v(" "),a("span",[t._v("用户管理")])])],1)],2):t._e()],1)],1),t._v(" "),a("div",{staticStyle:{flex:"0",margin:"8px","font-size":"14px",display:"flex","flex-direction":"row"}},[a("div",{staticStyle:{flex:"0 0","margin-right":"8px"}},[a("el-popover",{attrs:{placement:"top-start",width:"240",trigger:"hover"}},[a("div",[a("el-row",{staticStyle:{"font-size":"20px","padding-bottom":"4px"}},[a("i",{staticClass:"el-icon-user",staticStyle:{"padding-right":"4px"}}),a("strong",[t._v("用户信息")])]),t._v(" "),a("el-row",[a("el-col",{attrs:{span:8}},[t._v("账户:")]),t._v(" "),a("el-col",{attrs:{span:14}},[t._v(t._s(t.cache.loginid))])],1),t._v(" "),a("el-row",[a("el-col",{attrs:{span:8}},[t._v("姓名:")]),t._v(" "),a("el-col",{attrs:{span:14}},[t._v(t._s(t.cache.userinfo.name))])],1),t._v(" "),a("el-row",[a("el-col",{attrs:{span:8}},[t._v("用户类型:")]),t._v(" "),a("el-col",{attrs:{span:14}},[t._v(t._s(t.isAdmin?"管理员":"风控员"))])],1),t._v(" "),a("el-row",[a("el-col",{attrs:{span:8}},[t._v("登录时间:")]),t._v(" "),a("el-col",{attrs:{span:14}},[t._v(t._s(t.cache.userinfo.logintime))])],1),t._v(" "),a("el-row",[a("el-col",{attrs:{span:8}},[t._v("登录地址:")]),t._v(" "),a("el-col",{attrs:{span:14}},[t._v(t._s(t.cache.userinfo.loginip))])],1)],1),t._v(" "),a("span",{attrs:{slot:"reference"},slot:"reference"},[a("i",{staticClass:"el-icon-user userhead"})])])],1),t._v(" "),a("div",{staticStyle:{flex:"1 0"}},[a("el-row",[a("span",{staticClass:"user"},[t._v(t._s(t.cache.userinfo.name))])]),t._v(" "),a("el-row",[a("span",{staticClass:"user"},[t._v(t._s(t.cache.loginid))])])],1),t._v(" "),a("div",{staticStyle:{flex:"0 0"}},[a("div",[a("el-tooltip",{attrs:{placement:"top"}},[a("div",{attrs:{slot:"content"},slot:"content"},[t._v("修改密码")]),t._v(" "),a("i",{staticClass:"el-icon-setting button",on:{click:t.onModPwd}})])],1),t._v(" "),a("div",[a("el-tooltip",{attrs:{placement:"top"}},[a("div",{attrs:{slot:"content"},slot:"content"},[t._v("注销登录")]),t._v(" "),a("i",{staticClass:"el-icon-switch-button button",staticStyle:{color:"#F56C6C"},on:{click:t.onLogout}})])],1)])])])])])]),t._v(" "),a("el-container",[a("el-main",{staticStyle:{"border-bottom":"2px solid #E4E7ED"}},[a("keep-alive",[a("router-view",{ref:"main",on:{notify:t.handleNotify}})],1)],1),t._v(" "),a("el-footer",{staticClass:"statusbar"},[a("div",{staticStyle:{flex:"1",margin:"4px"}},[a("div",{directives:[{name:"show",rawName:"v-show",value:t.lastNotify,expression:"lastNotify"}],staticClass:"scroller",on:{click:t.onClickScroller}},[a("marquee",[a("i",{class:t.getIconStyle(t.lastNotify?t.lastNotify.title:""),staticStyle:{"padding-right":"4px"}}),t._v(" "),a("span",{staticClass:"time"},[t._v(t._s(t.lastNotify?t.lastNotify.time.format("hh:mm:ss"):""))]),t._v(" "),a("span",{staticClass:"group"},[t._v(t._s(t.lastNotify?t.lastNotify.group:""))]),t._v(" "),a("span",{staticClass:"channel"},[t._v(t._s(t.lastNotify?t.lastNotify.channel:""))]),t._v(" "),a("span",{class:t.getTitleStyle(t.lastNotify?t.lastNotify.title:"")},[t._v(t._s(t.lastNotify?t.lastNotify.title:""))]),t._v(" "),a("span",{staticClass:"message"},[t._v(t._s(t.lastNotify?t.lastNotify.message:""))])])],1)]),t._v(" "),a("div",{staticStyle:{flex:"0","margin-top":"4px","min-width":"150px"}},[a("i",{staticClass:"el-icon-connection",staticStyle:{color:"green","padding-right":"8px"}}),a("a",[t._v("推送通道已连接")])])])],1)],1),t._v(" "),a("el-dialog",{attrs:{title:"修改密码",visible:t.showDlgModPwd,width:"360px"},on:{"update:visible":function(e){t.showDlgModPwd=e}}},[a("el-row",{staticStyle:{margin:"8px 0"}},[a("el-input",{attrs:{placeholder:"请输入旧密码","show-password":""},model:{value:t.oldpwd,callback:function(e){t.oldpwd=e},expression:"oldpwd"}})],1),t._v(" "),a("el-row",{staticStyle:{margin:"8px 0"}},[a("el-input",{attrs:{placeholder:"请输入新密码","show-password":""},model:{value:t.newpwd,callback:function(e){t.newpwd=e},expression:"newpwd"}})],1),t._v(" "),a("el-row",{staticStyle:{margin:"8px 0"}},[a("el-input",{attrs:{placeholder:"请确认新密码","show-password":""},model:{value:t.confirmpwd,callback:function(e){t.confirmpwd=e},expression:"confirmpwd"}})],1),t._v(" "),a("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{attrs:{type:"primary",plain:""},on:{click:function(e){return t.doModPwd()}}},[t._v("确 定")])],1)],1),t._v(" "),a("el-dialog",{attrs:{title:"通知列表",visible:t.showNotifies,direction:"btt",width:"800px"},on:{"update:visible":function(e){t.showNotifies=e}}},[a("div",{staticStyle:{height:"400px","overflow-y":"scroll",border:"solid 1px #E4E7ED",padding:"4px"}},[a("el-table",{attrs:{data:t.notifies,stripe:"","show-header":!1}},[a("el-table-column",{scopedSlots:t._u([{key:"default",fn:function(e){return[a("i",{class:t.getIconStyle(e.row.title),staticStyle:{"padding-right":"4px"}}),t._v(" "),a("span",{staticClass:"time"},[t._v(t._s(e.row.time.format("hh:mm:ss")))]),t._v(" "),a("span",{staticClass:"group"},[t._v(t._s(e.row.group))]),t._v(" "),a("span",{staticClass:"channel"},[t._v(t._s(e.row.channel))]),t._v(" "),a("span",{class:t.getTitleStyle(e.row.title)},[t._v(t._s(e.row.title))]),t._v(" "),a("span",{staticClass:"message"},[t._v(t._s(e.row.message))])]}}])})],1)],1)])],1)},staticRenderFns:[]};var h=s("VU/8")(f,m,!1,function(t){s("pKwi")},"data-v-ce9460cc",null).exports,v=s("mvHQ"),g=s.n(v),b=s("bOdI"),y=s.n(b),w={name:"stradata",props:{groupid:{type:String,default:function(){return""}}},computed:d()({},Object(p.b)(["cache"]),{isAdmin:function(){var t=this.cache.userinfo;return!!t&&("admin"==t.role||"superman"==t.role)}}),components:{},watch:{groupid:function(t,e){var s=this;t=t||"",e=e||"",0!=t.length&&t!=e&&setTimeout(function(){s.$api.getStrategies(t,function(t){t.result<0?s.$notify.error("获取策略列表出错:"+t.message):(s.strategies=t.strategies,"pos"!=s.selCat&&"all"==s.strafilter?s.strafilter=s.strategies[0]:"pos"==s.selCat&&(s.strafilter="all"),s.queryData())})},300)}},data:function(){var t;return t={selCat:"pos",strafilter:"all",strategies:[],loading:{trade:!1,signal:!1,round:!1,position:!1,fund:!1},capital:5e6,trades:[],positions:[],signals:[],rounds:[]},y()(t,"strategies",[]),y()(t,"funds",[]),y()(t,"nvChart",null),y()(t,"autoData",!1),y()(t,"dataInterval",0),y()(t,"refreshTime",(new Date).format("yyyy.MM.dd hh:mm:ss")),t},methods:{handleCheckAutoData:function(t){this.resetDataInterval()},resetDataInterval:function(){var t=this;this.autoData?(0!=this.dataInterval&&clearInterval(this.dataInterval),this.dataInterval=setInterval(function(){t.queryData()},3e4)):0!=this.dataInterval&&clearInterval(this.dataInterval)},getTrdSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){if(e<3||e>5)a[e]="";else if(3!=e){if(4==e)a[e]=s.length+"笔";else if(5==e){var i=s.map(function(t){return Number(t.volume)});i.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=i.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手"}}else a[e]="总计"}),a},getSigSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){e>1?a[e]="":0!=e?1==e&&(a[e]=s.length+"笔"):a[e]="总计"}),a},getPosSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){if(0==e||1==e||2==e||3==e)if(0!=e){if(1==e)a[e]=s.length+"笔";else if(2==e){var i=s.map(function(t){return Number(t.qty)});i.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=i.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+Math.abs(e)},0)+"手"}else if(3==e){var n=s.map(function(t){return Number(t.profit)});n.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=n.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(1)}}else a[e]="总计";else a[e]=""}),a},getRndSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){if(5==e||6==e||7==e||8==e)if(5!=e)if(6!=e){if(7==e){var i=s.map(function(t){return Number(t.qty)});i.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=i.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手"}else if(8==e){var n=s.map(function(t){return Number(t.profit)});n.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=n.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(1)}}else a[e]=s.length+"笔";else a[e]="总计";else a[e]=""}),a},handleCatChange:function(t,e){this.selCat!=t.name&&(this.selCat=t.name,"pos"!=this.selCat&&"all"==this.strafilter?this.strafilter=this.strategies[0]:"pos"==this.selCat&&(this.strafilter="all"),this.queryData())},onStraSwitch:function(){this.queryData()},formatAmount:function(t,e){return t[e.property].toFixed(2)},fmtPrice:function(t){for(var e=t.toFixed(4),s=0;s0?"text-danger":t<0?"text-success":""},onClickPaintChart:function(){this.paintChart()},paintChart:function(){null==this.nvChart&&(this.nvChart=this.$echarts.init(document.getElementById("trend")));for(var t={title:{text:"净值走势"},tooltip:{trigger:"axis",axisPointer:{label:{backgroundColor:"#6a7985",formatter:function(t){var e=t.value+"";return e.substr(0,4)+"."+e.substr(4,2)+"."+e.substr(6,2)}}},formatter:function(t){if(0==t.length)return"收益曲线";var e=t[0].axisValueLabel;for(var s in t)"净值"==t[s].seriesName?e+="
净值: "+t[s].value.toFixed(4):e+="
"+t[s].seriesName+": "+t[s].value;return e}},grid:{top:42,left:"8",right:"8",bottom:"8",containLabel:!0},xAxis:{type:"category",data:[],boundaryGap:!1,axisLabel:{textStyle:{color:"#000"},formatter:function(t,e){return(t+="").substr(0,4)+"."+t.substr(4,2)+"."+t.substr(6,2)}},axisLine:{lineStyle:{color:"#000"}}},yAxis:[{type:"value",axisLabel:{textStyle:{color:"#000"},formatter:function(t,e){return t.toFixed(4)}},axisLine:{lineStyle:{color:"#000"}},scale:!0}],series:[{name:"净值",type:"line",smooth:!0,stack:"净值",areaStyle:{normal:{color:{type:"linear",x:0,y:0,x2:0,y2:1,colorStops:[{offset:0,color:"rgba(102,156,214,0.5)"},{offset:1,color:"rgba(242,242,242,0.3)"}],globalCoord:!1}}},data:[],lineStyle:{normal:{color:"rgb(102,156,214)",width:2}},itemStyle:{normal:{color:"rgb(102,156,214)",borderWidth:1}}}]},e=[],s=[],a=parseInt(this.capital),i=this.funds.length-1;i>=0;i--){var n=this.funds[i];e.push(n.date);var o=a+n.dynbalance;s.push(o/a)}var r=Math.max.apply(null,s),l=Math.min.apply(null,s);if(r==l)r*=1.05,l*=.95;else{var c=r-l;r+=.05*c,l=Math.max(0,l-.05*c)}t.xAxis.data=e,t.series[0].data=s,t.yAxis[0].max=r,t.yAxis[0].min=l,this.nvChart.setOption(t)},queryData:function(t){var e=this;t=t||!1;var s=this,a=this.selCat,i=this.groupid||"",n=this.strafilter||"";0!=i.length?0!=n.length?"trd"==a?(s.loading.trade=!0,setTimeout(function(){e.$api.getTrades(i,n,function(a){a.result<0?e.$notify.error("查询成交出错:"+a.message):(a.trades.forEach(function(t){var e="";"OPEN"==t.offset?e+="开":e+="平","LONG"==t.direction?e+="多":e+="空",t.action=e}),s.trades=a.trades,s.trades.reverse()),s.loading.trade=!1,s.refreshTime=(new Date).format("yyyy.MM.dd hh:mm:ss"),t&&s.resetDataInterval()})},300)):"sig"==a?(s.loading.signal=!0,setTimeout(function(){e.$api.getSignals(i,n,function(a){a.result<0?e.$notify.error("查询信号出错:"+a.message):(s.signals=a.signals,s.signals.reverse()),s.loading.signal=!1,s.refreshTime=(new Date).format("yyyy.MM.dd hh:mm:ss"),t&&s.resetDataInterval()})},300)):"rnd"==a?(s.loading.round=!0,setTimeout(function(){e.$api.getRounds(i,n,function(a){a.result<0?e.$notify.error("查询回合出错:"+a.message):(s.rounds=a.rounds,s.rounds.reverse()),s.loading.round=!1,s.refreshTime=(new Date).format("yyyy.MM.dd hh:mm:ss"),t&&s.resetDataInterval()})},300)):"pos"==a?(s.loading.position=!0,setTimeout(function(){e.$api.getPositions(i,n,function(a){a.result<0?e.$notify.error("查询持仓出错:"+a.message):(a.positions.forEach(function(t){t.qty=t.volume*(t.long?1:-1)}),s.positions=a.positions),s.loading.position=!1,s.refreshTime=(new Date).format("yyyy.MM.dd hh:mm:ss"),t&&s.resetDataInterval()})},300)):"fnd"==a&&(s.loading.fund=!0,setTimeout(function(){e.$api.getFunds(i,n,function(a){a.result<0?e.$notify.error("查询绩效出错:"+a.message):(s.funds=a.funds,s.funds.reverse(),s.paintChart()),s.loading.fund=!1,s.refreshTime=(new Date).format("yyyy.MM.dd hh:mm:ss"),t&&s.resetDataInterval()})},300)):this.$notify.error("策略ID不能为空"):this.$notify.error("组合ID不能为空")}},mounted:function(){window.onresize=function(){self.zooming||(self.zooming=!0,setTimeout(function(){self.myChart&&self.nvChart.resize(),self.zooming=!1},300))}}},x={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticStyle:{height:"100%",overflow:"auto"}},[s("el-container",{staticStyle:{height:"100%",overflow:"auto"}},[s("el-header",{staticStyle:{height:"40px","overflow-y":"hidden"}},[s("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"row"}},[s("div",{staticStyle:{flex:"0",height:"100%"}},[s("el-tabs",{staticStyle:{height:"100%",margin:"0"},attrs:{value:t.selCat,"tab-position":"top"},on:{"tab-click":t.handleCatChange}},[s("el-tab-pane",{attrs:{label:"持仓明细",name:"pos"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"成交明细",name:"trd"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"信号明细",name:"sig"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"回合明细",name:"rnd"}}),t._v(" "),t.isAdmin?s("el-tab-pane",{attrs:{label:"每日绩效",name:"fnd"}}):t._e()],1)],1),t._v(" "),s("div",{staticStyle:{flex:"1","border-bottom":"2px solid #E4E7ED","margin-top":"4px"}},[s("div",{staticStyle:{float:"right"}},[s("el-row",[s("el-col",{attrs:{span:4}},[s("el-tooltip",{staticClass:"item",attrs:{effect:"dark",content:"每隔30秒刷新一次",placement:"top"}},[s("el-checkbox",{staticStyle:{float:"right","margin-top":"6px"},on:{change:t.handleCheckAutoData},model:{value:t.autoData,callback:function(e){t.autoData=e},expression:"autoData"}},[t._v("自动刷新")])],1)],1),t._v(" "),s("el-col",{attrs:{offset:1,span:12}},[s("el-select",{attrs:{placeholder:"请选择",size:"mini"},on:{change:t.onStraSwitch},model:{value:t.strafilter,callback:function(e){t.strafilter=e},expression:"strafilter"}},[s("el-option",{directives:[{name:"show",rawName:"v-show",value:"pos"==t.selCat,expression:"selCat=='pos'"}],attrs:{label:"全部策略",value:"all"}},[s("i",{staticClass:"el-icon-collection"}),t._v(" "),s("span",[t._v("全部策略")])]),t._v(" "),t._l(t.strategies,function(e){return s("el-option",{key:e,attrs:{label:e,value:e}},[s("i",{staticClass:"el-icon-tickets"}),t._v(" "),s("span",[t._v(t._s(e))])])})],2)],1),t._v(" "),s("el-col",{attrs:{span:4}},[s("el-button",{attrs:{type:"primary",icon:"el-icon-refresh",size:"mini",plain:""},on:{click:function(e){return t.queryData()}}},[t._v("刷新")])],1)],1)],1)])])]),t._v(" "),s("el-main",{staticStyle:{overflow:"auto","border-bottom":"1px solid #E4E7ED"}},[s("div",{directives:[{name:"show",rawName:"v-show",value:"pos"==t.selCat,expression:"selCat=='pos'"},{name:"loading",rawName:"v-loading",value:t.loading.position,expression:"loading.position"}],staticStyle:{"max-height":"100%",overflow:"auto"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.positions,"summary-method":t.getPosSum,"show-summary":""}},[s("el-table-column",{attrs:{prop:"strategy",label:"策略",width:"140",sortable:""}}),t._v(" "),s("el-table-column",{attrs:{prop:"code",label:"品种",width:"120",sortable:""}}),t._v(" "),s("el-table-column",{attrs:{label:"数量",width:"64"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.qty>=0?"text-danger":"text-success"},[t._v(t._s(e.row.qty))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"浮盈",width:"100",sortable:""},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.profit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.profit.toFixed(1)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"开仓时间",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtTime(e.row.opentime)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"开仓价格",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtPrice(e.row.price)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"最大浮盈",width:"120",sortable:""},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{staticClass:"text-danger"},[t._v(t._s(e.row.maxprofit.toFixed(1)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"最大浮亏",width:"120",sortable:""},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{staticClass:"text-success"},[t._v(t._s(e.row.maxloss.toFixed(1)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"opentag",label:"标记",sortable:""}})],1)],1),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:"trd"==t.selCat,expression:"selCat=='trd'"},{name:"loading",rawName:"v-loading",value:t.loading.trade,expression:"loading.trade"}],staticStyle:{"max-height":"100%",overflow:"auto"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.trades,"summary-method":t.getTrdSum,"show-summary":""}},[s("el-table-column",{attrs:{prop:"strategy",label:"策略",width:"140"}}),t._v(" "),s("el-table-column",{attrs:{prop:"code",label:"品种",width:"120",sortable:""}}),t._v(" "),s("el-table-column",{attrs:{label:"时间",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtTime(e.row.time)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"动作",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:"开多"==e.row.action||"平空"==e.row.action?"text-danger":"text-success"},[t._v(t._s(e.row.action))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"价格",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtPrice(e.row.price)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"volume",label:"数量",width:"80"}}),t._v(" "),s("el-table-column",{attrs:{prop:"tag",label:"标记",sortable:""}})],1)],1),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:"sig"==t.selCat,expression:"selCat=='sig'"},{name:"loading",rawName:"v-loading",value:t.loading.signal,expression:"loading.signal"}],staticStyle:{"max-height":"100%",overflow:"auto"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.signals,"summary-method":t.getSigSum,"show-summary":""}},[s("el-table-column",{attrs:{prop:"strategy",label:"策略",width:"140"}}),t._v(" "),s("el-table-column",{attrs:{prop:"code",label:"品种",width:"120",sortable:""}}),t._v(" "),s("el-table-column",{attrs:{prop:"target",label:"目标数量",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:t.fmtProfit(e.row.target)},[t._v(t._s(e.row.target))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"触发价格",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtPrice(e.row.sigprice)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"触发时间",width:"180"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtTime(e.row.gentime,!0)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"tag",label:"标记",sortable:""}})],1)],1),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:"rnd"==t.selCat,expression:"selCat=='rnd'"},{name:"loading",rawName:"v-loading",value:t.loading.round,expression:"loading.round"}],staticStyle:{"max-height":"100%",overflow:"auto"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"","summary-method":t.getRndSum,"show-summary":"",data:t.rounds}},[s("el-table-column",{attrs:{prop:"strategy",label:"策略",width:"140"}}),t._v(" "),s("el-table-column",{attrs:{prop:"code",label:"品种",width:"120",sortable:""}}),t._v(" "),s("el-table-column",{attrs:{label:"方向",width:"64"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:"LONG"==e.row.direct?"text-danger":"text-success"},[t._v(t._s("LONG"==e.row.direct?"多":"空"))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"开仓时间",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtTime(e.row.opentime)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"开仓价格",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtPrice(e.row.openprice)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"平仓时间",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtTime(e.row.closetime)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"平仓价格",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtPrice(e.row.closeprice)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"qty",label:"数量",width:"64"}}),t._v(" "),s("el-table-column",{attrs:{label:"盈亏",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.profit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.profit.toFixed(1)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"entertag",label:"进场标记",width:"100"}}),t._v(" "),s("el-table-column",{attrs:{prop:"exittag",label:"出场标记",width:"100"}})],1)],1),t._v(" "),t.isAdmin?s("div",{directives:[{name:"show",rawName:"v-show",value:"fnd"==t.selCat,expression:"selCat=='fnd'"},{name:"loading",rawName:"v-loading",value:t.loading.fund,expression:"loading.fund"}],staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"1",width:"100%",overflow:"auto",height:"50%","border-bottom":"1px solid #E4E7ED"}},[s("div",{staticStyle:{"max-height":"100%"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.funds}},[s("el-table-column",{attrs:{prop:"strategy",label:"策略",width:"140"}}),t._v(" "),s("el-table-column",{attrs:{label:"日期",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtDate(e.row.date)))])]}}],null,!1,1157224088)}),t._v(" "),s("el-table-column",{attrs:{label:"平仓盈亏",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.closeprofit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.closeprofit.toFixed(1)))])]}}],null,!1,298912261)}),t._v(" "),s("el-table-column",{attrs:{label:"浮动盈亏",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.dynprofit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.dynprofit.toFixed(1)))])]}}],null,!1,103057029)}),t._v(" "),s("el-table-column",{attrs:{prop:"fee",label:"手续费",width:"120"}}),t._v(" "),s("el-table-column",{attrs:{prop:"dynbalance",label:"动态权益"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.dynbalance>=0?"text-danger":"text-success"},[t._v(t._s(e.row.dynbalance.toFixed(1)))])]}}],null,!1,1406323205)})],1)],1)]),t._v(" "),s("div",{staticStyle:{flex:"1",width:"100%",overflow:"auto",display:"flex","flex-direction":"column",margin:"4px"}},[s("div",{staticStyle:{height:"40px",display:"inline-block",flex:"0",margin:"4px"}},[s("el-row",[s("el-col",{staticStyle:{"margin-top":"2px"},attrs:{span:2}},[s("i",{staticClass:"el-icon-money"}),t._v(" "),s("a",[t._v("资金规模")])]),t._v(" "),s("el-col",{attrs:{span:3}},[s("el-input",{attrs:{placeholder:"请输入资金规模",size:"mini",type:"number",min:"1000000",step:"1000000"},model:{value:t.capital,callback:function(e){t.capital=e},expression:"capital"}})],1),t._v(" "),s("el-col",{staticStyle:{"margin-left":"2px"},attrs:{span:2}},[s("el-button",{attrs:{size:"mini",plain:"",type:"danger"},on:{click:t.onClickPaintChart}},[t._v("重新绘图")])],1)],1)],1),t._v(" "),s("div",{staticStyle:{flex:"1",width:"100%","border-top":"1px solid #E4E7ED"}},[s("div",{staticStyle:{width:"100%",height:"100%"},attrs:{id:"trend"}})])])]):t._e()]),t._v(" "),s("el-footer",{staticStyle:{height:"24px"}},[s("span",{staticStyle:{"font-size":"12px",color:"gray","line-height":"24px"}},[t._v("数据刷新时间: "+t._s(t.refreshTime))])])],1)],1)},staticRenderFns:[]};var _=s("VU/8")(w,x,!1,function(t){s("tvDO")},"data-v-d91c86fe",null).exports,k={name:"tradedata",props:{groupid:{type:String,default:function(){return""}}},components:{},watch:{groupid:function(t,e){var s=this;t=t||"",e=e||"",0!=t.length&&t!=e&&setTimeout(function(){s.$api.getChannels(t,function(t){if(t.result<0)s.$notify.error("拉取组合交易通道出错:"+t.message);else{s.channels=t.channels;var e=s.showAllChannels(s.selCat);s.chnlfilter=e?"all":s.channels[0],setTimeout(function(){s.queryData()},300)}})},300)}},data:function(){return{selCat:"pos",chnlfilter:"all",channels:[],loading:{trade:!1,order:!1,position:!1,fund:!1},trades:[],orders:[],positions:[],funds:[],autoData:!1,dataInterval:0,refreshTime:(new Date).format("yyyy.MM.dd hh:mm:ss")}},methods:{showAllChannels:function(t){return"fnd"==t||"pos"==t},getPosSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){if(0==e||1==e||2==e||3==e)if(0!=e){if(1==e)a[e]=s.length+"笔";else if(2==e){var i=s.map(function(t){return Number(t.long.newvol)}),n=s.map(function(t){return Number(t.long.prevol)});a[e]="昨"+n.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手 | 今"+i.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手"}else if(3==e){var o=s.map(function(t){return Number(t.short.newvol)}),r=s.map(function(t){return Number(t.short.prevol)});a[e]="昨"+r.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手 | 今"+o.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手"}}else a[e]="总计";else a[e]=""}),a},getTrdSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){if(e<4||e>6)a[e]="";else if(4!=e){if(5==e)a[e]=s.length+"笔";else if(6==e){var i=s.map(function(t){return Number(t.volume)});i.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=i.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手"}}else a[e]="总计"}),a},getOrdSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){if(e<4||e>7)a[e]="";else if(4!=e){if(5==e)a[e]=s.length+"笔";else if(6==e){var i=s.map(function(t){return Number(t.total)});i.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=i.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手"}else if(7==e){var n=s.map(function(t){return Number(t.traded)});n.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=n.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手"}}else a[e]="总计"}),a},getFndSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){if(0!=e){if(1==e)a[e]=s.length+"条";else if(2==e){var i=s.map(function(t){return Number(t.prebalance)});i.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=i.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(2)}else if(3==e){var n=s.map(function(t){return Number(t.balance)});n.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=n.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(2)}else if(4==e){var o=s.map(function(t){return Number(t.closeprofit)});o.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=o.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(2)}else if(5==e){var r=s.map(function(t){return Number(t.dynprofit)});r.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=r.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(2)}else if(6==e){var l=s.map(function(t){return Number(t.margin)});l.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=l.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(2)}else if(7==e){var c=s.map(function(t){return Number(t.fee)});c.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=c.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(2)}else if(8==e){var u=s.map(function(t){return Number(t.available)});u.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=u.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(2)}else if(9==e){var d=s.map(function(t){return Number(t.moneyio)});d.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=d.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(2)}}else a[e]="总计"}),a},handleCheckAutoData:function(t){this.resetDataInterval()},resetDataInterval:function(){var t=this;this.autoData?(0!=this.dataInterval&&clearInterval(this.dataInterval),this.dataInterval=setInterval(function(){t.queryData()},3e4)):0!=this.dataInterval&&clearInterval(this.dataInterval)},getActClr:function(t){return"开多"==t||"平空"==t||"平今空"==t?"text-danger":"text-success"},handleCatChange:function(t,e){if(this.selCat!=t.name){this.selCat=t.name;var s=this.showAllChannels(this.selCat);s||"all"!=this.chnlfilter?s&&(this.chnlfilter="all"):this.chnlfilter=this.channels[0],this.queryData()}},onChnlSwitch:function(){this.queryData()},formatTime:function(t,e){var s=t[e.property],a=new Date;return a.setTime(s),a.format("yyyy/MM/dd hh:mm:ss")},fmtPrice:function(t,e){for(var s=t[e.property].toFixed(4),a=0;a=0?"text-danger":"text-success"},[t._v(t._s(e.row.closeprofit.toFixed(2)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"浮动盈亏",width:"110",sortable:""},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.dynprofit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.dynprofit.toFixed(2)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"margin",label:"保证金",width:"120",sortable:"",formatter:t.fmtAmount}}),t._v(" "),s("el-table-column",{attrs:{prop:"fee",label:"手续费",width:"100",sortable:"",formatter:t.fmtAmount}}),t._v(" "),s("el-table-column",{attrs:{prop:"available",label:"可用资金",width:"120",sortable:"",formatter:t.fmtAmount}}),t._v(" "),s("el-table-column",{attrs:{label:"出入金",width:"100",sortable:""},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.moneyio>=0?"text-danger":"text-success"},[t._v(t._s(e.row.moneyio.toFixed(2)))])]}}])})],1)],1)]),t._v(" "),s("el-footer",{staticStyle:{height:"24px"}},[s("span",{staticStyle:{"font-size":"12px",color:"gray","line-height":"24px"}},[t._v("数据刷新时间: "+t._s(t.refreshTime))])])],1)],1)},staticRenderFns:[]};var S=s("VU/8")(k,C,!1,function(t){s("n+8b")},"data-v-b10ba514",null).exports,N={name:"portdata",props:{groupid:{type:String,default:function(){return""}}},computed:d()({},Object(p.b)(["cache"]),{isAdmin:function(){var t=this.cache.userinfo;return!!t&&("admin"==t.role||"superman"==t.role)}}),components:{},watch:{groupid:function(t,e){var s=this;t=t||"",e=e||"",0!=t.length&&t!=e&&setTimeout(function(){s.queryData()},300)}},data:function(){return{selCat:"pos",loading:{risk:!1,performance:!1,position:!1,fund:!1},capital:5e6,performances:[],positions:[],filters:{},funds:[],nvChart:null,pieChart:null,autoData:!1,dataInterval:0,refreshTime:(new Date).format("yyyy.MM.dd hh:mm:ss")}},methods:{handleCheckAutoData:function(t){this.resetDataInterval()},resetDataInterval:function(){var t=this;this.autoData?(0!=this.dataInterval&&clearInterval(this.dataInterval),this.dataInterval=setInterval(function(){"pos"==t.selCat&&t.queryData()},3e4)):0!=this.dataInterval&&clearInterval(this.dataInterval)},getPosSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){if(0==e||1==e||2==e||3==e)if(0!=e){if(1==e){var i=s.map(function(t){return Number(t.qty)});i.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=s.length+"笔 | "+i.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+Math.abs(e)},0)+"手"}else if(2==e){var n=s.map(function(t){return Number(t.profit)});n.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=n.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(1)}}else a[e]="总计";else a[e]=""}),a},handleCatChange:function(t,e){this.selCat!=t.name&&(this.selCat=t.name,this.queryData())},formatAmount:function(t,e){return t[e.property].toFixed(2)},fmtFundTime:function(t){var e=Math.floor(t/1e3)+"";return e.length<6&&(e="0"+e),e.substr(0,2)+":"+e.substr(2,2)},fmtFundDate:function(t){var e=t+"";return e.substr(2,2)+"."+e.substr(4,2)+"."+e.substr(6,2)},fmtPrice:function(t){for(var e=t.toFixed(4),s=0;s0?"text-danger":t<0?"text-success":""},onClickResetCapital:function(){var t=parseInt(this.capital);this.funds.forEach(function(e){e.capital=t}),this.paintChart()},paintChart:function(){null==this.nvChart&&(this.nvChart=this.$echarts.init(document.getElementById("grptrend")));for(var t={title:{text:"净值走势"},tooltip:{trigger:"axis",axisPointer:{label:{backgroundColor:"#6a7985",formatter:function(t){var e=t.value+"";return e.substr(0,4)+"."+e.substr(4,2)+"."+e.substr(6,2)}}},formatter:function(t){if(0==t.length)return"收益曲线";var e=t[0].axisValueLabel;for(var s in t)"净值"==t[s].seriesName?e+="
净值: "+t[s].value.toFixed(4):e+="
"+t[s].seriesName+": "+t[s].value;return e}},grid:{top:42,left:"8",right:"8",bottom:"8",containLabel:!0},xAxis:{type:"category",data:[],boundaryGap:!1,axisLabel:{textStyle:{color:"#000"},formatter:function(t,e){return(t+="").substr(0,4)+"."+t.substr(4,2)+"."+t.substr(6,2)}},axisLine:{lineStyle:{color:"#000"}}},yAxis:[{type:"value",axisLabel:{textStyle:{color:"#000"},formatter:function(t,e){return t.toFixed(4)}},axisLine:{lineStyle:{color:"#000"}},scale:!0}],series:[{name:"净值",type:"line",smooth:!0,stack:"净值",areaStyle:{normal:{color:{type:"linear",x:0,y:0,x2:0,y2:1,colorStops:[{offset:0,color:"rgba(102,156,214,0.5)"},{offset:1,color:"rgba(242,242,242,0.3)"}],globalCoord:!1}}},data:[],lineStyle:{normal:{color:"rgb(102,156,214)",width:2}},itemStyle:{normal:{color:"rgb(102,156,214)",borderWidth:1}}}]},e=[],s=[],a=parseInt(this.capital),i=this.funds.length-1;i>=0;i--){var n=this.funds[i];e.push(n.date);var o=a+n.dynbalance;s.push(o/a)}var r=Math.max.apply(null,s),l=Math.min.apply(null,s);if(r==l)r*=1.05,l*=.95;else{var c=r-l;r+=.05*c,l=Math.max(0,l-.05*c)}t.xAxis.data=e,t.series[0].data=s,t.yAxis[0].max=r,t.yAxis[0].min=l,this.nvChart.setOption(t)},paintPie:function(){if(this.isAdmin){null==this.pieChart&&(this.pieChart=this.$echarts.init(document.getElementById("pie")));var t=[];for(var e in this.performances){var s=this.performances[e],a=s.closeprofit+s.dynprofit;t.push({value:Math.abs(a),truevalue:a,name:e})}var i={title:{text:"组合绩效归因",subtext:"按品种",left:"center"},tooltip:{trigger:"item",formatter1:"{a}
{b} : {c} ({d}%)",formatter:function(t){return t.name+": "+(t.data.truevalue/1e4).toFixed(2)+"万("+t.percent.toFixed(1)+"%)"}},legend:{orient:"horizontal",left:"center",top:"bottom"},series:[{name:"分品种绩效",type:"pie",radius:"50%",emphasis:{itemStyle:{shadowBlur:10,shadowOffsetX:0,shadowColor:"rgba(0, 0, 0, 0.5)"}},data:t}]};this.pieChart.setOption(i)}},queryPerf:function(){var t=this,e=this.groupid||"";this.$api.getPortPerfs(e,function(e){e.result<0?t.$notify.error("查询绩效归因出错:"+e.message,"查询失败"):(t.performances=e.performance,setTimeout(function(){t.paintPie()},300))})},checkAllFilters:function(t,e){for(var s in this.filters[t])this.filters[t][s]=e},onAddCodeFilter:function(){var t=this;t.$prompt("请输入品种代码,格式如CFFEX.IF","新增代码过滤器",{confirmButtonText:"确定",cancelButtonText:"取消"}).then(function(e){var s=e.value;if(void 0!=t.filters.code_filters[s])return t.$alert("该品种代码已存在"),!0;t.filters.code_filters[s]=!0,t.$forceUpdate()}).catch(function(){})},onDelCodeFilter:function(t){var e=this;this.$confirm("确定要删除该代码过滤器吗?","删除代码过滤器",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){delete e.filters.code_filters[t],e.$forceUpdate()}).catch(function(){})},onCommitFilters:function(){var t=this,e=this;this.$confirm("确定要提交最新的过滤器配置吗?","提交过滤器",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){var s=t.groupid||"";e.$api.commitPortFilters(s,e.filters,function(e){e.result<0?t.$notify.error("提交过滤器出错:"+e.message):t.$notify({message:"过滤器提交成功"})})}).catch(function(){})},queryData:function(t){var e=this;t=t||!1;var s=this,a=this.selCat,i=this.groupid||"";0!=i.length?"pos"==a?(s.loading.position=!0,setTimeout(function(){e.$api.getPortPositions(i,function(a){a.result<0?e.$notify.error("查询持仓出错:"+a.message):(a.positions.forEach(function(t){t.qty=t.volume*(t.long?1:-1)}),s.positions=a.positions),s.loading.position=!1,s.refreshTime=(new Date).format("yyyy.MM.dd hh:mm:ss"),t&&s.resetDataInterval()}),e.queryPerf()},300)):"fnd"==a?(s.loading.fund=!0,setTimeout(function(){var a=parseInt(s.capital);e.$api.getPortFunds(i,function(i){i.result<0?e.$notify.error("查询绩效出错:"+i.message):(i.funds.forEach(function(t){t.dynbalance=t.balance+t.dynprofit,t.capital=a}),s.funds=i.funds,s.funds.reverse(),s.paintChart()),s.loading.fund=!1,s.refreshTime=(new Date).format("yyyy.MM.dd hh:mm:ss"),t&&s.resetDataInterval()})},300)):"rsk"==a&&(s.loading.risk=!0,setTimeout(function(){parseInt(s.capital);e.$api.getPortFilters(i,function(a){a.result<0?e.$notify.error("查询过滤器出错:"+a.message):(a.filters.strategy_filters=a.filters.strategy_filters||{},a.filters.code_filters=a.filters.code_filters||{},a.filters.channel_filters=a.filters.executer_filters||{},s.filters=a.filters),s.loading.risk=!1,s.refreshTime=(new Date).format("yyyy.MM.dd hh:mm:ss"),t&&s.resetDataInterval()})},300)):this.$notify.error("组合ID不能为空")}},mounted:function(){var t=this;window.onresize=function(){t.zooming||(t.zooming=!0,setTimeout(function(){t.myChart&&t.nvChart.resize(),t.pieChart&&t.pieChart.resize(),t.zooming=!1},300))},t.$nextTick(function(){t.$on("resize",function(){t.zooming||(t.zooming=!0,setTimeout(function(){t.myChart&&t.nvChart.resize(),t.pieChart&&t.pieChart.resize(),t.zooming=!1},150))})})}},A={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticStyle:{height:"100%",overflow:"auto"}},[s("el-container",{staticStyle:{height:"100%",overflow:"auto"}},[s("el-header",{staticStyle:{height:"40px","overflow-y":"hidden"}},[s("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"row"}},[s("div",{staticStyle:{flex:"0",height:"100%"}},[s("el-tabs",{staticStyle:{height:"100%",margin:"0"},attrs:{value:t.selCat,"tab-position":"top"},on:{"tab-click":t.handleCatChange}},[s("el-tab-pane",{attrs:{label:"持仓数据",name:"pos"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"组合风控",name:"rsk"}}),t._v(" "),t.isAdmin?s("el-tab-pane",{attrs:{label:"每日绩效",name:"fnd"}}):t._e()],1)],1),t._v(" "),s("div",{staticStyle:{flex:"1","border-bottom":"2px solid #E4E7ED","margin-top":"4px"}},[s("div",{staticStyle:{float:"right"}},[s("el-row",[s("el-col",{attrs:{offset:7,span:4}},[s("el-tooltip",{directives:[{name:"show",rawName:"v-show",value:"pos"==t.selCat,expression:"selCat=='pos'"}],staticClass:"item",attrs:{effect:"dark",content:"每隔30秒刷新一次",placement:"top"}},[s("el-checkbox",{staticStyle:{float:"right","margin-top":"6px"},on:{change:t.handleCheckAutoData},model:{value:t.autoData,callback:function(e){t.autoData=e},expression:"autoData"}},[t._v("自动刷新")])],1)],1),t._v(" "),s("el-col",{attrs:{offset:1,span:4}},[s("el-button",{attrs:{type:"primary",icon:"el-icon-refresh",size:"mini",plain:""},on:{click:function(e){return t.queryData()}}},[t._v("刷新")])],1)],1)],1)])])]),t._v(" "),s("el-main",{staticStyle:{overflow:"auto","border-bottom":"1px solid #E4E7ED"}},[s("div",{directives:[{name:"show",rawName:"v-show",value:"pos"==t.selCat,expression:"selCat=='pos'"},{name:"loading",rawName:"v-loading",value:t.loading.position,expression:"loading.position"}],staticStyle:{height:"100%",display:"flex","flex-direction":"row"}},[s("div",{staticStyle:{flex:"1",height:"100%",overflow:"auto","border-right":"1px solid #E4E7ED",width:"100%"}},[s("div",[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.positions,"summary-method":t.getPosSum,"show-summary":""}},[s("el-table-column",{attrs:{prop:"code",label:"品种",width:"120",sortable:""}}),t._v(" "),s("el-table-column",{attrs:{label:"数量",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.qty>=0?"text-danger":"text-success"},[t._v(t._s(e.row.qty))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"浮盈",width:"100",sortable:""},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.profit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.profit.toFixed(1)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"开仓时间",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtTime(e.row.opentime)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"开仓价格",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtPrice(e.row.price)))])]}}])})],1)],1)]),t._v(" "),t.isAdmin?s("div",{staticStyle:{flex:"1",height:"100%",overflow:"auto",width:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"1",width:"100%",height:"100%"}},[s("div",{staticStyle:{width:"100%",height:"100%"},attrs:{id:"pie"}})])]):t._e()]),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:"rsk"==t.selCat,expression:"selCat=='rsk'"},{name:"loading",rawName:"v-loading",value:t.loading.risk,expression:"loading.risk"}],staticStyle:{height:"100%",overflow:"auto"}},[s("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"1",display:"flex","flex-direction":"row"}},[s("div",{staticClass:"filter-pane el-card is-always-shadow"},[s("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"0 1 44px",padding:"18px 20px","border-bottom":"1px solid #EBEEF5","box-sizing":"border-box"}},[s("span",{staticClass:"filter"},[t._v("策略过滤器")]),t._v(" "),s("el-button",{staticStyle:{float:"right","margin-left":"1px"},attrs:{type:"danger",size:"mini",plain:""},on:{click:function(e){return t.checkAllFilters("strategy_filters",!0)}}},[t._v("一键过滤")]),t._v(" "),s("el-button",{staticStyle:{float:"right","margin-right":"1px"},attrs:{type:"success",size:"mini",plain:""},on:{click:function(e){return t.checkAllFilters("strategy_filters",!1)}}},[t._v("一键通过")])],1),t._v(" "),s("div",{staticStyle:{flex:"1 0 0",overflow:"auto",padding:"20px"}},t._l(t.filters.strategy_filters,function(e,a){return s("el-row",{key:a,staticClass:"filter-row"},[s("el-col",{attrs:{span:12}},[s("i",{staticClass:"el-icon-cpu"}),s("span",{staticClass:"filter-strategy"},[t._v(t._s(a))])]),t._v(" "),s("el-col",{attrs:{span:12}},[s("el-switch",{attrs:{"active-text":"过滤","inactive-text":"通过","inactive-color":"#13ce66","active-color":"#ff4949"},model:{value:t.filters.strategy_filters[a],callback:function(e){t.$set(t.filters.strategy_filters,a,e)},expression:"filters['strategy_filters'][id]"}})],1)],1)}),1)])]),t._v(" "),s("div",{staticClass:"filter-pane el-card is-always-shadow"},[s("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"0 1 44px",padding:"18px 20px","border-bottom":"1px solid #EBEEF5","box-sizing":"border-box"}},[s("span",{staticClass:"filter"},[t._v("代码过滤器")]),t._v(" "),s("el-button",{staticStyle:{float:"right","margin-left":"1px"},attrs:{type:"danger",size:"mini",plain:""},on:{click:function(e){return t.checkAllFilters("code_filters",!0)}}},[t._v("一键过滤")]),t._v(" "),s("el-button",{staticStyle:{float:"right","margin-right":"1px"},attrs:{type:"success",size:"mini",plain:""},on:{click:function(e){return t.checkAllFilters("code_filters",!1)}}},[t._v("一键通过")])],1),t._v(" "),s("div",{staticStyle:{flex:"1 0 0",overflow:"auto",padding:"20px"}},[t._l(t.filters.code_filters,function(e,a){return s("el-row",{key:a,staticClass:"filter-row"},[s("el-col",{staticStyle:{"margin-top":"4px"},attrs:{span:10}},[s("i",{staticClass:"el-icon-collection"}),s("span",{staticClass:"filter-code"},[t._v(t._s(a))])]),t._v(" "),s("el-col",{staticStyle:{"margin-top":"4px"},attrs:{span:10}},[s("el-switch",{staticStyle:{float:"right"},attrs:{"active-text":"过滤","inactive-text":"通过","inactive-color":"#13ce66","active-color":"#ff4949"},model:{value:t.filters.code_filters[a],callback:function(e){t.$set(t.filters.code_filters,a,e)},expression:"filters['code_filters'][id]"}})],1),t._v(" "),s("el-col",{attrs:{span:4}},[s("el-tooltip",{attrs:{placement:"top"}},[s("div",{attrs:{slot:"content"},slot:"content"},[t._v("删除代码过滤器")]),t._v(" "),s("el-button",{staticStyle:{float:"right"},attrs:{size:"mini",circle:"",icon:"el-icon-delete"},on:{click:function(e){return t.onDelCodeFilter(a)}}})],1)],1)],1)}),t._v(" "),s("el-row",{staticClass:"filter-row"},[s("el-tooltip",{attrs:{placement:"top"}},[s("div",{attrs:{slot:"content"},slot:"content"},[t._v("添加代码过滤器")]),t._v(" "),s("el-button",{staticStyle:{width:"100%"},attrs:{type:"primary",plain:"",icon:"el-icon-plus"},on:{click:function(e){return t.onAddCodeFilter()}}})],1)],1)],2)])]),t._v(" "),s("div",{staticClass:"filter-pane el-card is-always-shadow"},[s("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"0 1 44px",padding:"18px 20px","border-bottom":"1px solid #EBEEF5","box-sizing":"border-box"}},[s("span",{staticClass:"filter"},[t._v("通道过滤器")]),t._v(" "),s("el-button",{staticStyle:{float:"right","margin-left":"1px"},attrs:{type:"danger",size:"mini",plain:""},on:{click:function(e){return t.checkAllFilters("channel_filters",!0)}}},[t._v("一键过滤")]),t._v(" "),s("el-button",{staticStyle:{float:"right","margin-right":"1px"},attrs:{type:"success",size:"mini",plain:""},on:{click:function(e){return t.checkAllFilters("channel_filters",!1)}}},[t._v("一键通过")])],1),t._v(" "),s("div",{staticStyle:{flex:"1 0 0",overflow:"auto",padding:"20px"}},t._l(t.filters.channel_filters,function(e,a){return s("el-row",{key:a,staticClass:"filter-row"},[s("el-col",{attrs:{span:12}},[s("i",{staticClass:"el-icon-link"}),s("span",{staticClass:"filter-channel"},[t._v(t._s(a))])]),t._v(" "),s("el-col",{attrs:{span:12}},[s("el-switch",{attrs:{"active-text":"过滤","inactive-text":"通过","inactive-color":"#13ce66","active-color":"#ff4949"},model:{value:t.filters.channel_filters[a],callback:function(e){t.$set(t.filters.channel_filters,a,e)},expression:"filters['channel_filters'][id]"}})],1)],1)}),1)])])]),t._v(" "),s("div",{staticStyle:{flex:"0 28px",margin:"2px 0"}},[s("span",{staticStyle:{"font-size":"12px",color:"gray","line-height":"24px"}},[t._v("\n 当过滤器生效时,相关的信号都会被过滤掉!修改完过滤器,一定要记得"),s("strong",[t._v("提交")]),t._v("!提交以后会在"),s("strong",[t._v("1分钟内")]),t._v("生效!\n ")]),t._v(" "),s("el-button",{staticStyle:{float:"right"},attrs:{type:"danger",icon:"el-icon-finished"},on:{click:function(e){return t.onCommitFilters()}}},[t._v("提交")])],1)])]),t._v(" "),t.isAdmin?s("div",{directives:[{name:"show",rawName:"v-show",value:"fnd"==t.selCat,expression:"selCat=='fnd'"},{name:"loading",rawName:"v-loading",value:t.loading.fund,expression:"loading.fund"}],staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{height:"40px",display:"inline-block",flex:"0",margin:"4px"}},[s("el-row",[s("el-col",{staticStyle:{"margin-top":"2px"},attrs:{span:2}},[s("i",{staticClass:"el-icon-money"}),t._v(" "),s("a",[t._v("资金规模")])]),t._v(" "),s("el-col",{attrs:{span:3}},[s("el-input",{attrs:{placeholder:"请输入资金规模",size:"mini",type:"number",min:"1000000",step:"1000000"},model:{value:t.capital,callback:function(e){t.capital=e},expression:"capital"}})],1),t._v(" "),s("el-col",{staticStyle:{"margin-left":"2px"},attrs:{span:2}},[s("el-button",{attrs:{size:"mini",plain:"",type:"danger"},on:{click:t.onClickResetCapital}},[t._v("刷新数据")])],1)],1)],1),t._v(" "),s("div",{staticStyle:{flex:"1",width:"100%",overflow:"auto",height:"50%","border-bottom":"1px solid #E4E7ED"}},[s("div",{staticStyle:{"max-height":"100%"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.funds}},[s("el-table-column",{attrs:{label:"日期",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtDate(e.row.date)))])]}}],null,!1,1157224088)}),t._v(" "),s("el-table-column",{attrs:{label:"平仓盈亏",width:"90"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.closeprofit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.closeprofit.toFixed(1)))])]}}],null,!1,298912261)}),t._v(" "),s("el-table-column",{attrs:{label:"浮动盈亏",width:"90"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.dynprofit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.dynprofit.toFixed(1)))])]}}],null,!1,103057029)}),t._v(" "),s("el-table-column",{attrs:{prop:"fee",label:"佣金",width:"80",formatter:t.formatAmount}}),t._v(" "),s("el-table-column",{attrs:{label:"动态权益",width:"110"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.dynbalance+e.row.capital>=0?"text-danger":"text-success"},[t._v(t._s((e.row.dynbalance+e.row.capital).toFixed(1)))])]}}],null,!1,4057529733)}),t._v(" "),s("el-table-column",{attrs:{label:"日内最大市值",width:"140"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{staticClass:"text-danger"},[t._v(t._s((e.row.maxdynbalance+e.row.capital).toFixed(1)+"("+t.fmtFundTime(e.row.maxtime)+")"))])]}}],null,!1,2563446024)}),t._v(" "),s("el-table-column",{attrs:{label:"日内最小市值",width:"140"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{staticClass:"text-success"},[t._v(t._s((e.row.mindynbalance+e.row.capital).toFixed(1)+"("+t.fmtFundTime(e.row.mintime)+")"))])]}}],null,!1,1140179728)}),t._v(" "),s("el-table-column",{attrs:{label:"多日最大市值",width:"160"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{staticClass:"text-danger"},[t._v(t._s((e.row.mdmaxbalance+e.row.capital).toFixed(1)+"("+t.fmtFundDate(e.row.mdmaxdate)+")"))])]}}],null,!1,3597427227)}),t._v(" "),s("el-table-column",{attrs:{label:"多日最小市值",width:"160"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{staticClass:"text-success"},[t._v(t._s((e.row.mdminbalance+e.row.capital).toFixed(1)+"("+t.fmtFundDate(e.row.mdmindate)+")"))])]}}],null,!1,1635711075)})],1)],1)]),t._v(" "),s("div",{staticStyle:{flex:"1",width:"100%",overflow:"auto",display:"flex","flex-direction":"column",margin:"4px"}},[s("div",{staticStyle:{flex:"1",width:"100%","border-top":"1px solid #E4E7ED"}},[s("div",{staticStyle:{width:"100%",height:"100%"},attrs:{id:"grptrend"}})])])]):t._e()]),t._v(" "),s("el-footer",{staticStyle:{height:"24px"}},[s("span",{staticStyle:{"font-size":"12px",color:"gray","line-height":"24px"}},[t._v("数据刷新时间: "+t._s(t.refreshTime))])])],1)],1)},staticRenderFns:[]};var D=s("VU/8")(N,A,!1,function(t){s("b3w7")},"data-v-4147835e",null).exports,T=s("E5Az"),E=(s("tWbI"),s("5IAE"),s("4/hK"),s("Yokd"),s("GUiZ"),s("Z6qg"),s("aGTD"),s("7Xsf"),s("THjC"),s("Kk9m"),s("fo6W"),s("U3HU"),s("soCA"),s("aX1R"),s("vq+x"),{name:"editor",props:{groupid:{type:String,default:function(){return""}}},computed:{curFilePath:function(){return null==this.curFile?"":this.curFile.path}},components:{codemirror:T.codemirror},watch:{groupid:function(t,e){t=t||"",e=e||"",0!=t.length&&t!=e&&this.queryFiles()}},data:function(){return{folders:[],edit:!1,filename:"",content_s:"",content_backup:"",curFile:null,fileOnway:!1,cmOptions:{mode:"python",keyMap:"sublime",lineNumbers:!0,smartIndent:!0,indentUnit:4,indentWithTabs:!0,lineWrapping:!0,gutters:["CodeMirror-linenumbers","CodeMirror-foldgutter","CodeMirror-lint-markers"],foldGutter:!0,autofocus:!0,matchBrackets:!0,autoCloseBrackets:!0,styleActiveLine:!0,readOnly:!0}}},methods:{queryFiles:function(){var t=this,e=this;e.$api.getGroupDir(e.groupid,function(s){s.result<0?e.$notify.error("获取组合目录结构失败:"+s.message):t.folders=[s.tree]})},handleFileClick:function(t){var e=this;if(t.isfile){var s=t.path.split("."),a=s[s.length-1].toLowerCase();if(-1==["py","json","js","csv"].indexOf(a))return void this.$toast("该文件不可查看");e.fileOnway=!0,e.curFile=t,e.$api.getGroupFile(this.groupid,t.path,function(t){t.result<0?e.$notify.error("获取文件内容失败"+t.message):(e.content_s=t.content,e.cmOptions.mode=function(t){return"py"==t?"python":"json"==t?{name:"javascript",json:!0}:"js"==t?"javascript":"text/plain"}(a),e.fileOnway=!1)})}},onClickEdit:function(){this.content_backup=this.content_s,this.cmOptions.readOnly=!1,this.edit=!0},onClickCancel:function(){var t=this;this.content_s!=this.content_backup?this.$confirm("内容已被修改,确定要放弃修改吗?","提示",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){t.content_s=t.content_backup,t.cmOptions.readOnly=!0,t.edit=!1}).catch(function(){}):(this.content_s=this.content_backup,this.cmOptions.readOnly=!0,this.edit=!1)},onClickCommit:function(){var t=this;this.loading=!0,this.$api.commitGroupFile(this.groupid,this.curFile.path,this.content_s,function(e){e.result<0?t.$message.error("文件提交失败:"+e.message):t.$message({message:"文件提交成功",type:"success"}),t.edit=!1,t.cmOptions.readOnly=!0,t.loading=!1})}},mounted:function(){}}),I={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticStyle:{height:"100%",width:"100%",display:"flex","flex-direction":"row"}},[s("div",{staticStyle:{flex:"0 320px","min-width":"320px",border:"1px solid #DCDFE6","border-radius":"2px",margin:"4px"}},[s("div",{staticStyle:{height:"100%",overflow:"auto"}},[s("el-tree",{attrs:{data:t.folders},on:{"node-click":t.handleFileClick}})],1)]),t._v(" "),s("div",{staticStyle:{flex:"1",margin:"4px",display:"flex","flex-direction":"column",overflow:"auto"}},[s("div",{directives:[{name:"loading",rawName:"v-loading",value:t.fileOnway,expression:"fileOnway"}],staticStyle:{flex:"1",height:"100%",overflow:"auto",border:"1px solid #DCDFE6","border-radius":"2px"}},[s("div",{staticStyle:{height:"100%"}},[s("codemirror",{ref:"mycode",staticStyle:{height:"100% !important"},attrs:{options:t.cmOptions},model:{value:t.content_s,callback:function(e){t.content_s=e},expression:"content_s"}})],1)]),t._v(" "),s("div",{staticStyle:{flex:"0 32px","margin-top":"8px"}},[s("span",{staticStyle:{"font-size":"12px",color:"gray",float:"left"}},[t._v("当前文件:"+t._s(t.curFilePath))]),t._v(" "),s("el-button",{directives:[{name:"show",rawName:"v-show",value:!t.edit,expression:"!edit"}],staticStyle:{float:"right"},attrs:{size:"mini"},on:{click:function(e){return t.onClickEdit()}}},[s("i",{staticClass:"el-icon-edit"}),t._v("修改\n ")]),t._v(" "),s("el-button",{directives:[{name:"show",rawName:"v-show",value:t.edit,expression:"edit"}],staticStyle:{float:"right"},attrs:{size:"mini"},on:{click:function(e){return t.onClickCommit()}}},[s("i",{staticClass:"el-icon-set-up"}),t._v("提交\n ")]),t._v(" "),s("el-button",{directives:[{name:"show",rawName:"v-show",value:t.edit,expression:"edit"}],staticStyle:{float:"right"},attrs:{size:"mini"},on:{click:function(e){return t.onClickCancel()}}},[s("i",{staticClass:"el-icon-refresh-left"}),t._v("取消\n ")])],1)])])},staticRenderFns:[]};var G={name:"empty",components:{StrategyData:_,ChannelData:S,PorfolioData:D,Editor:s("VU/8")(E,I,!1,function(t){s("C17c")},null,null).exports},computed:d()({},Object(p.b)(["cache"]),{isAdmin:function(){var t=this.cache.userinfo;return!!t&&("admin"==t.role||"superman"==t.role)},groupid:function(){return null==this.groupinfo?"":this.groupinfo.id}}),props:{groupinfo:{type:Object,default:function(){return{id:"",name:"",path:"",info:"",gtype:"cta",datmod:"auto"}}}},watch:{groupinfo:function(t,e){var s=this;null!=t&&(null!=e&&t.id==e.id||(this.logScroll="auto"==t.datmod,setTimeout(function(){s.queryLogs(!0)},300)))}},data:function(){return{selData:"sdata",logs:"",logfilter:"total",logCache:"",logLines:0,logScroll:!0,logOnway:!1,autoLog:!1,logInterval:0,logTime:(new Date).format("yyyy.MM.dd hh:mm:ss")}},methods:{handleClickTab:function(t,e){var s=this;this.selData=t.name,"pdata"==t.name&&setTimeout(function(){s.$refs.portfolio.$emit("resize")},150)},handleClickQryLog:function(){var t=this;setTimeout(function(){t.queryLogs(!0)},300)},handleCheckAutoLog:function(t){this.resetLogInterval()},resetLogInterval:function(){var t=this;this.autoLog?(0!=this.logInterval&&clearInterval(this.logInterval),this.logInterval=setInterval(function(){t.queryLogs()},15e3)):0!=this.logInterval&&clearInterval(this.logInterval)},queryLogs:function(t){var e=this;t=t||!1;var s=this;""!=this.groupinfo.id&&(s.logOnway=!0,this.$api.getLogs(this.groupinfo.id,this.logfilter,function(a){a.result<0?(e.logs="",e.logLines=0,e.$notify.error("组合日志拉取失败:"+a.message),s.$nextTick(function(){s.$refs.logs.scrollTo(0,0)})):(e.logs=a.content,e.logLines=a.lines||0,s.$nextTick(function(){var t=s.$refs.logs.scrollHeight;s.$refs.logs.scrollTo(0,t)})),s.logTime=(new Date).format("yyyy.MM.dd hh:mm:ss"),s.logOnway=!1,t&&s.resetLogInterval()}))},processLog:function(t){var e=this,s=t.message||"";if(s.length>0){var a=0==e.logCache.length,i="["+new Date(t.time).format("yyyy.MM.dd hh:mm:ss")+" - "+t.tag+"] "+s+"\n";e.logCache+=i,e.logLines++,a&&e.$nextTick(function(){e.logLines>=200&&(e.logs="",e.logLines=0),e.logs+=e.logCache,e.logCache="",e.$nextTick(function(){var t=e.$refs.logs.scrollHeight;e.$refs.logs.scrollTo(0,t)})})}},processEvent:function(t){var e=t.evttype||"";if(""!=e)if("notify"==e)this.$notify({title:"订单回报",type:"error",message:"交易通道{0}错误:{1}".format(t.channel,t.message),duration:0});else if("order"==e){if(!t.data.canceled)return;this.$notify({title:"订单回报",type:"error",message:"交易通道{0}订单已撤销,本地订单号:{1}".format(t.channel,t.data.localid),duration:0})}else if("trade"==e){var s=(t.data.isopen?"开":"平")+(t.data.islong?"多":"空");this.$notify({title:"成交回报",type:"success",message:"交易通道:{0},操作:{1},代码:{2},数量:{3},成交价:{4},本地订单号:{5}".format(t.channel,s,t.data.code,t.data.volume,t.data.price,t.data.localid)})}}},mounted:function(){var t=this;t.$on("notify",function(e){if(e.groupid==t.groupinfo.id)if("gplog"==e.type){if(!t.logScroll)return;t.processLog(e)}else"chnlevt"==e.type&&t.processEvent(e)})}},F={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticStyle:{height:"100vh",width:"100%"}},[s("el-row",{staticStyle:{height:"100%"}},[s("el-col",{staticStyle:{height:"100%"},attrs:{span:8}},[s("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"0",margin:"2px 4px 0px 4px","min-height":"39px",display:"flex","flex-direction":"row"}},[s("div",{staticClass:"simtab",staticStyle:{flex:"0"}},[s("span",[t._v("滚动日志")])]),t._v(" "),s("div",{staticStyle:{flex:"1","border-bottom":"1px solid #E4E7ED","margin-top":"6px"}}),t._v(" "),s("div",{staticStyle:{flex:"0 160px","border-bottom":"1px solid #E4E7ED","margin-top":"6px",display:"inline-block"}},[s("el-row",{directives:[{name:"show",rawName:"v-show",value:!t.logScroll,expression:"!logScroll"}]},[s("el-col",{attrs:{span:11}},[s("el-tooltip",{staticClass:"item",attrs:{effect:"dark",content:"每个15秒刷新一次",placement:"top"}},[s("el-checkbox",{staticStyle:{float:"right","margin-top":"6px"},on:{change:t.handleCheckAutoLog},model:{value:t.autoLog,callback:function(e){t.autoLog=e},expression:"autoLog"}},[t._v("自动刷新")])],1)],1),t._v(" "),s("el-col",{attrs:{offset:1,span:12}},[s("el-button",{attrs:{type:"primary",icon:"el-icon-refresh",size:"mini",plain:""},on:{click:function(e){return t.handleClickQryLog()}}},[t._v("刷新")])],1)],1)],1)]),t._v(" "),s("div",{directives:[{name:"loading",rawName:"v-loading",value:t.logOnway,expression:"logOnway"}],staticStyle:{flex:"1",margin:"10px 4px"}},[s("textarea",{ref:"logs",staticClass:"el-textarea__inner",attrs:{readonly:"readonly",autocomplete:"off",placeholder:"这里是日志内容"},domProps:{value:t.logs}})]),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:!t.logScroll,expression:"!logScroll"}],staticStyle:{flex:"0 24px","align-items":"right"}},[s("span",{staticStyle:{"font-size":"12px",color:"gray"}},[t._v("日志刷新时间: "+t._s(t.logTime))])])])]),t._v(" "),s("el-col",{staticStyle:{height:"100%","border-left":"1px solid #E4E7ED"},attrs:{span:16}},[s("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"0",margin:"2px 4px 0px 4px","min-height":"44px"}},[s("el-tabs",{staticStyle:{height:"100%"},attrs:{value:t.selData,type:"card"},on:{"tab-click":t.handleClickTab}},[s("el-tab-pane",{attrs:{label:"策略管理",name:"sdata"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"组合管理",name:"pdata"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"通道管理",name:"tdata"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"配置管理",name:"editor"}})],1)],1),t._v(" "),s("div",{staticStyle:{flex:"1",margin:"2px",overflow:"auto"}},[s("StrategyData",{directives:[{name:"show",rawName:"v-show",value:"sdata"==t.selData,expression:"selData=='sdata'"}],attrs:{groupid:t.groupid}}),t._v(" "),s("ChannelData",{directives:[{name:"show",rawName:"v-show",value:"tdata"==t.selData,expression:"selData=='tdata'"}],attrs:{groupid:t.groupid}}),t._v(" "),s("PorfolioData",{directives:[{name:"show",rawName:"v-show",value:"pdata"==t.selData,expression:"selData=='pdata'"}],ref:"portfolio",attrs:{groupid:t.groupid}}),t._v(" "),s("Editor",{directives:[{name:"show",rawName:"v-show",value:"editor"==t.selData,expression:"selData=='editor'"}],attrs:{groupid:t.groupid}})],1)])])],1)],1)},staticRenderFns:[]};var M=s("VU/8")(G,F,!1,function(t){s("g1Tk")},"data-v-7f597c06",null).exports,q={render:function(){this.$createElement;this._self._c;return this._m(0)},staticRenderFns:[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"empty-box"},[e("div",{staticClass:"content"},[e("h2",[this._v("尚未添加组合")])])])}]};var $=s("VU/8")({name:"empty",components:{},data:function(){return{}},methods:{}},q,!1,function(t){s("SP1q")},"data-v-53f6add5",null).exports,B={name:"schedule",computed:d()({},Object(p.b)(["folders"])),props:{fixinfo:{type:Boolean,default:function(){return!1}},forapp:{type:Boolean,default:function(){return!1}},config:{type:Object,default:function(){return{id:"",folder:"",path:"",param:"",mqurl:"",span:3,guard:!1,redirect:!1,schedule:{active:!0,weekflag:"0111110",weekmask:[!1,!0,!0,!0,!0,!0,!1],tasks:[{active:!1,time:"00:00",action:0},{active:!1,time:"00:00",action:0},{active:!1,time:"00:00",action:0},{active:!1,time:"00:00",action:0},{active:!1,time:"00:00",action:0},{active:!1,time:"00:00",action:0}]}}}}},data:function(){return{showfolders:!1,selfolder:"",actions:["启动","停止","重启"]}},methods:{onLinkPython:function(){var t=this;this.$api.getPythonPath(function(e){e.result<0?t.$alert(e.message):t.config.path=e.path})},handlePickFolder:function(t){var e=this,s=this;s.selfolder="",0==s.folders.length?this.$api.getFolders(function(t){t.result<0?s.$alert(t.message):(e.$store.commit("setfolders",{folders:[t.tree]}),s.showfolders=!0)}):s.showfolders=!0},handleFolderPicked:function(){""==this.selfolder?this.$alert("请选择目录"):(this.config.folder=this.selfolder,this.showfolders=!1)},handleFolderClick:function(t){this.selfolder=t.path},onConfigCommit:function(){var t=this,e=JSON.parse(g()(this.config));if(0!=e.path.length)if(0!=e.id.length)if(0!=e.folder.length){e.schedule.weekflag="";for(var s=0;s<7;s++)e.schedule.weekflag+=e.schedule.weekmask[s]?"1":"0";for(var a=0;a<6;a++){var i=e.schedule.tasks[a].time;i=i.replace(":",""),e.schedule.tasks[a].time=parseInt(i)}e.redirect=!this.forapp,e.isapp=this.forapp,this.$confirm("确定要提交该调度配置?","自动调度",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then(function(){delete e.schedule.weekmask,t.$api.commitMonCfg(e,function(e){e.result<0?t.$message.error(e.message):(t.$message({message:"监控配置已提交成功",type:"success"}),t.$emit("cfgudt"))})})}else this.$alert("执行目录不能为空");else this.$alert("应用ID不能为空");else this.$alert("执行程序路径不能为空")}}},O={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticStyle:{display:"flex","flex-direction":"column",margin:"4px 20px"}},[t._m(0),t._v(" "),s("el-divider",{attrs:{"content-position":"left"}},[t._v("任务信息")]),t._v(" "),s("div",{staticClass:"config-label"},[s("el-row",[s("el-col",{attrs:{span:4}},[s("a",[t._v("任务ID:")])]),t._v(" "),s("el-col",{attrs:{span:7}},[s("el-input",{attrs:{size:"mini",disabled:t.fixinfo},model:{value:t.config.id,callback:function(e){t.$set(t.config,"id",e)},expression:"config.id"}})],1),t._v(" "),s("el-col",{attrs:{span:4,offset:2}},[s("a",[t._v("启动参数:")])]),t._v(" "),s("el-col",{attrs:{span:7}},[s("el-input",{attrs:{size:"mini",placeholder:"run.py"},model:{value:t.config.param,callback:function(e){t.$set(t.config,"param",e)},expression:"config.param"}})],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:4}},[s("a",[t._v("执行程序:")])]),t._v(" "),s("el-col",{attrs:{span:20}},[s("el-input",{attrs:{size:"mini",placeholder:"python执行程序所在目录,请上服务器查询"},model:{value:t.config.path,callback:function(e){t.$set(t.config,"path",e)},expression:"config.path"}},[s("el-tooltip",{attrs:{slot:"append",effect:"dark",content:"直接获取python路径",placement:"top"},slot:"append"},[s("el-button",{attrs:{icon:"el-icon-link"},on:{click:t.onLinkPython}})],1)],1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:4}},[s("a",[t._v("工作目录:")])]),t._v(" "),s("el-col",{attrs:{span:20}},[s("el-input",{attrs:{size:"mini",disabled:t.fixinfo},model:{value:t.config.folder,callback:function(e){t.$set(t.config,"folder",e)},expression:"config.folder"}},[s("el-tooltip",{attrs:{slot:"append",effect:"dark",content:"选择组合所在的目录",placement:"top"},slot:"append"},[s("el-button",{attrs:{icon:"el-icon-folder",disabled:t.fixinfo},on:{click:t.handlePickFolder}})],1)],1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:4}},[s("a",[t._v("消息地址:")])]),t._v(" "),s("el-col",{attrs:{span:20}},[s("el-input",{attrs:{size:"mini",placeholder:"请输入消息队列的URL",disabled:t.forapp},model:{value:t.config.mqurl,callback:function(e){t.$set(t.config,"mqurl",e)},expression:"config.mqurl"}})],1)],1)],1),t._v(" "),s("el-divider",{attrs:{"content-position":"left"}},[t._v("监控设置")]),t._v(" "),s("div",{staticClass:"config-row"},[s("div",{staticStyle:{flex:"0","margin-top":"6px","padding-right":"16px"}},[s("el-checkbox",{model:{value:t.config.guard,callback:function(e){t.$set(t.config,"guard",e)},expression:"config.guard"}},[t._v("进程守护")])],1),t._v(" "),s("div",{staticStyle:{flex:"0:display:inline"}},[s("span",{staticClass:"config-label"},[t._v("检测间隔")]),t._v(" "),s("el-input-number",{attrs:{size:"mini",min:1,max:100,label:"检测间隔"},model:{value:t.config.span,callback:function(e){t.$set(t.config,"span",e)},expression:"config.span"}}),t._v(" "),s("span",{staticClass:"config-label"},[t._v("s")])],1),t._v(" "),s("div",{staticStyle:{flex:"1"}}),t._v(" "),s("div",{staticStyle:{flex:"0","margin-top":"6px"}},[s("el-checkbox",{model:{value:t.config.schedule.active,callback:function(e){t.$set(t.config.schedule,"active",e)},expression:"config.schedule.active"}},[t._v("计划任务")])],1)]),t._v(" "),s("el-divider",{attrs:{"content-position":"left"}},[t._v("计划任务")]),t._v(" "),s("div",{staticClass:"config-row"},[s("div",{staticClass:"week-marker"},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.weekmask[0],callback:function(e){t.$set(t.config.schedule.weekmask,0,e)},expression:"config.schedule.weekmask[0]"}},[t._v("周日")])],1),t._v(" "),s("div",{staticClass:"week-marker"},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.weekmask[1],callback:function(e){t.$set(t.config.schedule.weekmask,1,e)},expression:"config.schedule.weekmask[1]"}},[t._v("周一")])],1),t._v(" "),s("div",{staticClass:"week-marker"},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.weekmask[2],callback:function(e){t.$set(t.config.schedule.weekmask,2,e)},expression:"config.schedule.weekmask[2]"}},[t._v("周二")])],1),t._v(" "),s("div",{staticClass:"week-marker"},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.weekmask[3],callback:function(e){t.$set(t.config.schedule.weekmask,3,e)},expression:"config.schedule.weekmask[3]"}},[t._v("周三")])],1),t._v(" "),s("div",{staticClass:"week-marker"},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.weekmask[4],callback:function(e){t.$set(t.config.schedule.weekmask,4,e)},expression:"config.schedule.weekmask[4]"}},[t._v("周四")])],1),t._v(" "),s("div",{staticClass:"week-marker"},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.weekmask[5],callback:function(e){t.$set(t.config.schedule.weekmask,5,e)},expression:"config.schedule.weekmask[5]"}},[t._v("周五")])],1),t._v(" "),s("div",{staticClass:"week-marker"},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.weekmask[6],callback:function(e){t.$set(t.config.schedule.weekmask,6,e)},expression:"config.schedule.weekmask[6]"}},[t._v("周六")])],1)]),t._v(" "),s("el-row",[s("el-col",{attrs:{span:5}},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.tasks[0].active,callback:function(e){t.$set(t.config.schedule.tasks[0],"active",e)},expression:"config.schedule.tasks[0].active"}},[t._v("任务一")])],1),t._v(" "),s("el-col",{attrs:{span:6}},[s("el-input",{attrs:{placeholder:"请输入时间","suffix-icon":"el-icon-time",size:"mini",type:"time",disabled:!t.config.schedule.tasks[0].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[0].time,callback:function(e){t.$set(t.config.schedule.tasks[0],"time",e)},expression:"config.schedule.tasks[0].time"}})],1),t._v(" "),s("el-col",{attrs:{span:5,offset:1}},[s("el-select",{attrs:{placeholder:"请选择",size:"mini",disabled:!t.config.schedule.tasks[0].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[0].action,callback:function(e){t.$set(t.config.schedule.tasks[0],"action",e)},expression:"config.schedule.tasks[0].action"}},t._l(t.actions,function(t,e){return s("el-option",{key:e,attrs:{label:t,value:e}})}),1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:5}},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.tasks[1].active,callback:function(e){t.$set(t.config.schedule.tasks[1],"active",e)},expression:"config.schedule.tasks[1].active"}},[t._v("任务二")])],1),t._v(" "),s("el-col",{attrs:{span:6}},[s("el-input",{attrs:{placeholder:"请输入时间","suffix-icon":"el-icon-time",size:"mini",type:"time",disabled:!t.config.schedule.tasks[1].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[1].time,callback:function(e){t.$set(t.config.schedule.tasks[1],"time",e)},expression:"config.schedule.tasks[1].time"}})],1),t._v(" "),s("el-col",{attrs:{span:5,offset:1}},[s("el-select",{attrs:{placeholder:"请选择",size:"mini",disabled:!t.config.schedule.tasks[1].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[1].action,callback:function(e){t.$set(t.config.schedule.tasks[1],"action",e)},expression:"config.schedule.tasks[1].action"}},t._l(t.actions,function(t,e){return s("el-option",{key:e,attrs:{label:t,value:e}})}),1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:5}},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.tasks[2].active,callback:function(e){t.$set(t.config.schedule.tasks[2],"active",e)},expression:"config.schedule.tasks[2].active"}},[t._v("任务三")])],1),t._v(" "),s("el-col",{attrs:{span:6}},[s("el-input",{attrs:{placeholder:"请输入时间","suffix-icon":"el-icon-time",size:"mini",type:"time",disabled:!t.config.schedule.tasks[2].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[2].time,callback:function(e){t.$set(t.config.schedule.tasks[2],"time",e)},expression:"config.schedule.tasks[2].time"}})],1),t._v(" "),s("el-col",{attrs:{span:5,offset:1}},[s("el-select",{attrs:{placeholder:"请选择",size:"mini",disabled:!t.config.schedule.tasks[2].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[2].action,callback:function(e){t.$set(t.config.schedule.tasks[2],"action",e)},expression:"config.schedule.tasks[2].action"}},t._l(t.actions,function(t,e){return s("el-option",{key:e,attrs:{label:t,value:e}})}),1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:5}},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.tasks[3].active,callback:function(e){t.$set(t.config.schedule.tasks[3],"active",e)},expression:"config.schedule.tasks[3].active"}},[t._v("任务四")])],1),t._v(" "),s("el-col",{attrs:{span:6}},[s("el-input",{attrs:{placeholder:"请输入时间","suffix-icon":"el-icon-time",size:"mini",type:"time",disabled:!t.config.schedule.tasks[3].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[3].time,callback:function(e){t.$set(t.config.schedule.tasks[3],"time",e)},expression:"config.schedule.tasks[3].time"}})],1),t._v(" "),s("el-col",{attrs:{span:5,offset:1}},[s("el-select",{attrs:{placeholder:"请选择",size:"mini",disabled:!t.config.schedule.tasks[3].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[3].action,callback:function(e){t.$set(t.config.schedule.tasks[3],"action",e)},expression:"config.schedule.tasks[3].action"}},t._l(t.actions,function(t,e){return s("el-option",{key:e,attrs:{label:t,value:e}})}),1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:5}},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.tasks[4].active,callback:function(e){t.$set(t.config.schedule.tasks[4],"active",e)},expression:"config.schedule.tasks[4].active"}},[t._v("任务五")])],1),t._v(" "),s("el-col",{attrs:{span:6}},[s("el-input",{attrs:{placeholder:"请输入时间","suffix-icon":"el-icon-time",size:"mini",type:"time",disabled:!t.config.schedule.tasks[4].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[4].time,callback:function(e){t.$set(t.config.schedule.tasks[4],"time",e)},expression:"config.schedule.tasks[4].time"}})],1),t._v(" "),s("el-col",{attrs:{span:5,offset:1}},[s("el-select",{attrs:{placeholder:"请选择",size:"mini",disabled:!t.config.schedule.tasks[4].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[4].action,callback:function(e){t.$set(t.config.schedule.tasks[4],"action",e)},expression:"config.schedule.tasks[4].action"}},t._l(t.actions,function(t,e){return s("el-option",{key:e,attrs:{label:t,value:e}})}),1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:5}},[s("el-checkbox",{attrs:{disabled:!t.config.schedule.active},model:{value:t.config.schedule.tasks[5].active,callback:function(e){t.$set(t.config.schedule.tasks[5],"active",e)},expression:"config.schedule.tasks[5].active"}},[t._v("任务六")])],1),t._v(" "),s("el-col",{attrs:{span:6}},[s("el-input",{attrs:{placeholder:"请输入时间","suffix-icon":"el-icon-time",size:"mini",type:"time",disabled:!t.config.schedule.tasks[5].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[5].time,callback:function(e){t.$set(t.config.schedule.tasks[5],"time",e)},expression:"config.schedule.tasks[5].time"}})],1),t._v(" "),s("el-col",{attrs:{span:5,offset:1}},[s("el-select",{attrs:{placeholder:"请选择",size:"mini",disabled:!t.config.schedule.tasks[5].active||!t.config.schedule.active},model:{value:t.config.schedule.tasks[5].action,callback:function(e){t.$set(t.config.schedule.tasks[5],"action",e)},expression:"config.schedule.tasks[5].action"}},t._l(t.actions,function(t,e){return s("el-option",{key:e,attrs:{label:t,value:e}})}),1)],1)],1),t._v(" "),s("div",{staticStyle:{padding:"8px 4px"}},[s("div",{staticStyle:{flex:"1:display:inline",float:"right"}},[s("el-button",{staticStyle:{float:"right"},attrs:{type:"primary",size:"mini",plain:""},on:{click:function(e){return t.onConfigCommit()}}},[t._v("提交设置")])],1)]),t._v(" "),s("el-dialog",{attrs:{title:"选择目录",visible:t.showfolders,width:"25%"},on:{"update:visible":function(e){t.showfolders=e}}},[s("div",{staticStyle:{width:"100%",height:"300px",overflow:"auto",border:"1px solid #E4E7ED"}},[s("el-tree",{attrs:{data:t.folders},on:{"node-click":t.handleFolderClick}})],1),t._v(" "),s("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[s("el-button",{on:{click:function(e){t.showfolders=!1}}},[t._v("取 消")]),t._v(" "),s("el-button",{attrs:{type:"primary",plain:""},on:{click:t.handleFolderPicked}},[t._v("确 定")])],1)])],1)},staticRenderFns:[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticStyle:{"padding-bottom":"12px"}},[e("h4",[this._v("调度设置")])])}]};var P=s("VU/8")(B,O,!1,function(t){s("+B9X")},"data-v-4b8c701c",null).exports,U={name:"monitor",computed:d()({},Object(p.b)(["folders","cache"]),{grpname:function(){return null==this.curGroup?"组合基本信息":this.curGroup.name},isAdmin:function(){var t=this.cache.userinfo;return!!t&&("admin"==t.role||"superman"==t.role)}}),components:{Group:M,Empty:$,Schedule:P},data:function(){return{groups:[],selectedIdx:"",schedule:!1,baseinfo:!1,showgrpdlg:!1,curGroup:null,addGroup:!1,editGroup:!1,copyGroup:{id:"",name:"",path:"",info:"",gtype:"cta",datmod:"mannual",env:"product",mqurl:""},showfolders:!1,selfolder:"",monitors:{},curMonCfg:{id:"",folder:"",path:"",param:"",span:3,guard:!1,redirect:!1,schedule:{active:!1,weekflag:"0111110",weekmask:[!1,!0,!0,!0,!0,!0,!1],tasks:[{active:!1,time:"00:00",action:0},{active:!1,time:"00:00",action:0},{active:!1,time:"00:00",action:0},{active:!1,time:"00:00",action:0},{active:!1,time:"00:00",action:0},{active:!1,time:"00:00",action:0}]}}}},methods:{handlePickFolder:function(t){var e=this,s=this;s.selfolder="",0==s.folders.length?this.$api.getFolders(function(t){t.result<0?s.$notify.error("查询目录结构出错:"+t.message):(e.$store.commit("setfolders",{folders:[t.tree]}),s.showfolders=!0)}):s.showfolders=!0},handleFolderPicked:function(){""==this.selfolder?this.$alert("请选择目录"):(this.copyGroup.path=this.selfolder,this.showfolders=!1)},handleFolderClick:function(t){this.selfolder=t.path},handleTabSel:function(t,e){var s=this;s.selectedIdx=t,s.groups.forEach(function(e){if(e.id==t)return s.curGroup=e,!0})},handleClickSchedule:function(){var t=this,e=this,s=e.curGroup.id;e.monitors[s]?(e.curMonCfg=e.monitors[s],e.schedule=!0):e.$api.getMonCfg(s,function(a){if(a.result<0)e.$notify.error(a.message);else if(a.config){var i=a.config;i.schedule.weekmask=[];for(var n=0;n0&&(t.selectedIdx=t.groups[0].id,t.curGroup=t.groups[0]),t.sockets.subscribe("notify",function(e){if("gpevt"==e.type&&("start"==e.evttype?setTimeout(function(){t.$notify({message:"组合"+e.groupid+"已启动",type:"success"})},300):"stop"==e.evttype&&setTimeout(function(){t.$notify.error("组合"+e.groupid+"已停止")},300)),"chnlevt"==e.type){var s=null,a=e.evttype||"",i=null;if(t.groups.forEach(function(t){t.id==e.groupid&&(i=t)}),null!=i){if("notify"==a)s={time:new Date,group:i.name,channel:e.channel,title:"事件通知",type:"error",message:"错误:{1}".format(e.channel,e.message)};else if("order"==a)e.data.canceled&&(s={time:new Date,group:i.name,channel:e.channel,title:"订单回报",type:"danger",message:"订单已撤销,本地订单号:{1}".format(e.channel,e.data.localid)});else if("trade"==a){var n=(e.data.isopen?"开":"平")+(e.data.islong?"多":"空");s={time:new Date,group:i.name,channel:e.channel,title:"成交回报",type:"success",message:"操作:{1},代码:{2},数量:{3},成交价:{4},本地订单号:{5}".format(e.channel,n,e.data.code,e.data.volume,e.data.price,e.data.localid)}}null!=s&&t.$emit("notify",s)}}null!=t.curGroup&&e.groupid==t.curGroup.id&&("gplog"==e.type||"chnlevt"==e.type?t.$refs.group&&t.$refs.group.$emit("notify",e):"gpevt"==e.type&&("start"==e.evttype?t.curGroup.running=!0:"stop"==e.evttype&&(t.curGroup.running=!1)))}))})})}},z={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticStyle:{height:"100%"}},[s("el-container",{staticStyle:{height:"100%"}},[s("el-header",{staticStyle:{height:"44px !important"}},[s("div",{staticStyle:{float:"left",height:"100%","margin-top":"7px"}},[s("el-menu",{staticClass:"el-menu-demo",attrs:{"default-active":t.selectedIdx,mode:"horizontal"},on:{select:t.handleTabSel}},t._l(t.groups,function(e,a){return s("el-menu-item",{key:a,attrs:{index:e.id}},[t._v("\n "+t._s(e.name)+"\n "),s("i",{class:"auto"==e.datmod?"el-icon-monitor":"el-icon-thumb"})])}),1)],1),t._v(" "),s("div",{staticStyle:{float:"right",margin:"6px 0px"}},[s("el-row",[s("el-tooltip",{directives:[{name:"show",rawName:"v-show",value:""!=t.selectedIdx&&t.curGroup.running,expression:"selectedIdx!='' && curGroup.running"}],staticClass:"item",attrs:{effect:"dark",content:"手动停止组合",placement:"top-start"}},[s("el-button",{attrs:{type:"danger",icon:"el-icon-magic-stick",size:"mini",plain:""},on:{click:t.handleStopGroup}},[t._v("停止")])],1),t._v(" "),s("el-tooltip",{directives:[{name:"show",rawName:"v-show",value:""!=t.selectedIdx&&!t.curGroup.running,expression:"selectedIdx!='' && !curGroup.running"}],staticClass:"item",attrs:{effect:"dark",content:"手动启动组合",placement:"top-start"}},[s("el-button",{attrs:{type:"success",icon:"el-icon-magic-stick",size:"mini",plain:""},on:{click:t.handleStartGroup}},[t._v("启动")])],1),t._v(" "),s("el-tooltip",{directives:[{name:"show",rawName:"v-show",value:""!=t.selectedIdx,expression:"selectedIdx!=''"}],staticClass:"item",attrs:{effect:"dark",content:"查看策略组合的基本信息",placement:"top-start"}},[s("el-button",{attrs:{type:"primary",icon:"el-icon-magic-stick",size:"mini",plain:""},on:{click:t.handleViewGrop}},[t._v("查看")])],1),t._v(" "),t.isAdmin?s("el-tooltip",{directives:[{name:"show",rawName:"v-show",value:""!=t.selectedIdx,expression:"selectedIdx!=''"}],staticClass:"item",attrs:{effect:"dark",content:"配置策略组合的自动调度",placement:"top-start"}},[s("el-button",{attrs:{type:"primary",icon:"el-icon-time",size:"mini",plain:""},on:{click:t.handleClickSchedule}},[t._v("调度")])],1):t._e(),t._v(" "),s("el-divider",{attrs:{direction:"vertical"}}),t._v(" "),s("el-tooltip",{staticClass:"item",attrs:{effect:"dark",content:"管理策略组合",placement:"top-start"}},[s("el-dropdown",{attrs:{"split-button":"",type:"danger",size:"mini",trigger:"click"},on:{command:t.handleGrpCmd}},[t._v("\n 组合管理\n "),s("el-dropdown-menu",{attrs:{slot:"dropdown"},slot:"dropdown"},[s("el-dropdown-item",{attrs:{command:"addgrp"}},[s("i",{staticClass:"el-icon-plus"}),t._v(" "),s("span",[t._v("添加组合")])]),t._v(" "),s("el-dropdown-item",{directives:[{name:"show",rawName:"v-show",value:null!=t.curGroup,expression:"curGroup!=null"}],attrs:{command:"delgrp"}},[s("i",{staticClass:"el-icon-delete"}),t._v(" "),s("span",[t._v("删除组合")])])],1)],1)],1)],1)],1)]),t._v(" "),s("el-main",[s("div",{staticStyle:{height:"100%",width:"100%"}},[s("Empty",{directives:[{name:"show",rawName:"v-show",value:0==t.groups.length,expression:"groups.length==0"}],staticStyle:{height:"100%",width:"100%"}}),t._v(" "),s("Group",{directives:[{name:"show",rawName:"v-show",value:0!=t.groups.length,expression:"groups.length!=0"}],ref:"group",staticStyle:{height:"100%",width:"100%"},attrs:{groupinfo:t.curGroup}})],1)])],1),t._v(" "),s("el-drawer",{attrs:{title:"计划任务","with-header":!1,visible:t.schedule,direction:"rtl",size:"500px"},on:{"update:visible":function(e){t.schedule=e}}},[s("Schedule",{attrs:{config:t.curMonCfg,fixinfo:!0}})],1),t._v(" "),s("el-dialog",{attrs:{title:"选择目录",visible:t.showfolders,width:"25%"},on:{"update:visible":function(e){t.showfolders=e}}},[s("div",{staticStyle:{width:"100%",height:"300px",overflow:"auto",border:"1px solid #E4E7ED"}},[s("el-tree",{attrs:{data:t.folders},on:{"node-click":t.handleFolderClick}})],1),t._v(" "),s("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[s("el-button",{on:{click:function(e){t.showfolders=!1}}},[t._v("取 消")]),t._v(" "),s("el-button",{attrs:{type:"primary",plain:""},on:{click:t.handleFolderPicked}},[t._v("确 定")])],1)]),t._v(" "),s("el-dialog",{staticClass:"dialog-group",attrs:{title:t.addGroup?"添加组合":"修改组合",visible:t.showgrpdlg,width:"25%","before-close":t.onCloseGrpDlg},on:{"update:visible":function(e){t.showgrpdlg=e}}},[s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("组合ID:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-input",{attrs:{size:"mini",disabled:!t.editGroup},model:{value:t.copyGroup.id,callback:function(e){t.$set(t.copyGroup,"id",e)},expression:"copyGroup.id"}})],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("组合名称:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-input",{attrs:{size:"mini",disabled:!t.editGroup},model:{value:t.copyGroup.name,callback:function(e){t.$set(t.copyGroup,"name",e)},expression:"copyGroup.name"}})],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("组合路径:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-input",{attrs:{size:"mini",disabled:!t.editGroup},model:{value:t.copyGroup.path,callback:function(e){t.$set(t.copyGroup,"path",e)},expression:"copyGroup.path"}},[s("el-tooltip",{attrs:{slot:"append",effect:"dark",content:"选择组合所在的目录",placement:"top"},slot:"append"},[s("el-button",{attrs:{icon:"el-icon-folder",disabled:!t.editGroup},on:{click:t.handlePickFolder}})],1)],1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("组合类型:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-radio",{attrs:{label:"cta",disabled:!t.editGroup},model:{value:t.copyGroup.gtype,callback:function(e){t.$set(t.copyGroup,"gtype",e)},expression:"copyGroup.gtype"}},[t._v("CTA组合")]),t._v(" "),s("el-radio",{attrs:{label:"hft",disabled:!t.editGroup},model:{value:t.copyGroup.gtype,callback:function(e){t.$set(t.copyGroup,"gtype",e)},expression:"copyGroup.gtype"}},[t._v("HFT组合")]),t._v(" "),s("el-radio",{attrs:{label:"sel",disabled:!t.editGroup},model:{value:t.copyGroup.gtype,callback:function(e){t.$set(t.copyGroup,"gtype",e)},expression:"copyGroup.gtype"}},[t._v("SEL组合")])],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("数据模式:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-tooltip",{staticClass:"item",attrs:{effect:"dark",content:"组合会自动向监控服务推送数据",placement:"top"}},[s("el-radio",{attrs:{label:"auto",disabled:!t.editGroup},model:{value:t.copyGroup.datmod,callback:function(e){t.$set(t.copyGroup,"datmod",e)},expression:"copyGroup.datmod"}},[t._v("自动")])],1),t._v(" "),s("el-tooltip",{staticClass:"item",attrs:{effect:"dark",content:"监控服务只在有请求的时候去读取数据",placement:"top"}},[s("el-radio",{attrs:{label:"mannual",disabled:!t.editGroup},model:{value:t.copyGroup.datmod,callback:function(e){t.$set(t.copyGroup,"datmod",e)},expression:"copyGroup.datmod"}},[t._v("手动")])],1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("消息地址:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-tooltip",{staticClass:"item",attrs:{effect:"dark",content:"消息订阅地址要和配置文件中的notifier一致",placement:"top"}},[s("el-input",{attrs:{size:"mini",disabled:!t.editGroup||"auto"!=t.copyGroup.datmod,placeholder:"请输入消息队列的订阅地址"},model:{value:t.copyGroup.mqurl,callback:function(e){t.$set(t.copyGroup,"mqurl",e)},expression:"copyGroup.mqurl"}})],1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("组合环境:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-radio",{attrs:{label:"product",disabled:!t.editGroup},model:{value:t.copyGroup.env,callback:function(e){t.$set(t.copyGroup,"env",e)},expression:"copyGroup.env"}},[t._v("生产环境")]),t._v(" "),s("el-radio",{attrs:{label:"backtest",disabled:!t.editGroup},model:{value:t.copyGroup.env,callback:function(e){t.$set(t.copyGroup,"env",e)},expression:"copyGroup.env"}},[t._v("回测环境")])],1)],1),t._v(" "),s("el-row",{staticStyle:{height:"60px"}},[s("el-col",{attrs:{span:6}},[s("a",[t._v("组合介绍:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-input",{staticStyle:{"min-height":"80px"},attrs:{type:"textarea",disabled:!t.editGroup},model:{value:t.copyGroup.info,callback:function(e){t.$set(t.copyGroup,"info",e)},expression:"copyGroup.info"}})],1)],1),t._v(" "),s("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[s("el-button",{directives:[{name:"show",rawName:"v-show",value:!t.addGroup&&!t.editGroup,expression:"!addGroup && !editGroup"}],attrs:{type:"primary",plain:"",size:"mini",icon:"el-icon-edit"},on:{click:function(e){t.editGroup=!0}}},[t._v("编辑")]),t._v(" "),s("el-button",{attrs:{type:"primary",plain:"",size:"mini",icon:"el-icon-thumb"},on:{click:function(e){return t.onCommitGroup()}}},[t._v("提交")])],1)],1),t._v(" "),s("el-dialog",{attrs:{title:"选择目录",visible:t.showfolders,width:"25%"},on:{"update:visible":function(e){t.showfolders=e}}},[s("div",{staticStyle:{width:"100%",height:"300px",overflow:"auto",border:"1px solid #E4E7ED"}},[s("el-tree",{attrs:{data:t.folders},on:{"node-click":t.handleFolderClick}})],1),t._v(" "),s("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[s("el-button",{attrs:{size:"mini"},on:{click:function(e){t.showfolders=!1}}},[t._v("取 消")]),t._v(" "),s("el-button",{attrs:{type:"primary",plain:"",size:"mini"},on:{click:t.handleFolderPicked}},[t._v("确 定")])],1)])],1)},staticRenderFns:[]};var L=s("VU/8")(U,z,!1,function(t){s("nZf+")},"data-v-6717c4fd",null).exports,j={render:function(){this.$createElement;this._self._c;return this._m(0)},staticRenderFns:[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"loginBox"},[e("div",{staticClass:"login"},[this._v("\n 在线部署功能建设中,敬请期待\n ")])])}]};var Z=s("VU/8")({name:"Deploy",data:function(){return{}},methods:{},mounted:function(){}},j,!1,function(t){s("DoUX")},"data-v-0cbbd64e",null).exports,R=(s("jQeI"),s("CK2l"),s("c+I8"),s("iHuw"),s("RkhK"),s("LxZp"),{name:"BTComp",props:{btInfo:{type:Object,default:function(){return null}},straInfo:{type:Object,default:function(){return null}}},watch:{btInfo:function(t,e){var s=this;null!=t&&(null!=e&&t.id==e.id||setTimeout(function(){s.queryTrades(),s.queryRounds(),s.queryFunds(),s.querySignals()},300))}},data:function(){return{loading:{signal:!1,round:!1,fund:!1,trade:!1},selCat:"summary",signals:[],rounds:[],funds:[],trades:[],kChart:null,tChart:null,bars:[]}},methods:{fmtBtTime:function(t){return(t+="").substr(0,4)+"."+t.substr(4,2)+"."+t.substr(6,2)+" "+t.substr(8,2)+":"+t.substr(10,2)},onCatSel:function(t,e){var s=this;this.selCat!=t.name&&(this.selCat=t.name,"kline"==t.name&&this.kChart?setTimeout(function(){s.kChart.resize()},300):"summary"==t.name&&this.tChart&&setTimeout(function(){s.tChart.resize()},300))},queryBars:function(){var t=this;if(null!=this.btInfo){var e=this.btInfo.state.code,s=this.btInfo.state.stime,a=this.btInfo.state.etime,i=this.btInfo.state.period;this.$api.getBtBars(e,i,s,a,function(e){e.result<0?t.$notify.error("拉取K线出错:"+e.message):(t.bars=e.bars,t.paintChart(!1))})}},querySignals:function(){var t=this,e=this.straInfo.id,s=this.btInfo.id;this.loading.signal=!0,this.$api.getBtSignals(e,s,function(e){e.result<0?t.$notify.error("查询信号出错:"+e.message):(t.signals=e.signals,t.signals.reverse()),t.loading.signal=!1})},queryTrades:function(){var t=this,e=this.straInfo.id,s=this.btInfo.id;this.loading.trade=!0,this.$api.getBtTrades(e,s,function(e){e.result<0?t.$notify.error("查询成交出错:"+e.message):(e.trades.forEach(function(t){var e="";"OPEN"==t.offset?e+="开":e+="平","LONG"==t.direction?e+="多":e+="空",t.action=e}),t.trades=e.trades,t.trades.reverse()),t.loading.trade=!1,setTimeout(function(){t.queryBars()},300)})},queryRounds:function(){var t=this,e=this.straInfo.id,s=this.btInfo.id;this.loading.round=!1,this.$api.getBtRounds(e,s,function(e){e.result<0?t.$notify.error("查询回合出错:"+e.message):(t.rounds=e.rounds,t.rounds.reverse()),t.loading.round=!1})},queryFunds:function(){var t=this,e=this.straInfo.id,s=this.btInfo.id;this.loading.fund=!0,this.$api.getBtFunds(e,s,function(e){e.result<0?t.$notify.error("查询绩效出错:"+e.message):(t.funds=e.funds,t.funds.reverse(),t.paintTrend()),t.loading.fund=!1})},getTrdSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){if(e<3||e>5)a[e]="";else if(3!=e){if(4==e)a[e]=s.length+"笔";else if(5==e){var i=s.map(function(t){return Number(t.volume)});i.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=i.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手"}}else a[e]="总计"}),a},getSigSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){e>1?a[e]="":0!=e?1==e&&(a[e]=s.length+"笔"):a[e]="总计"}),a},getRndSum:function(t){var e=t.columns,s=t.data,a=[];return e.forEach(function(t,e){if(5==e||6==e||7==e||8==e)if(5!=e)if(6!=e){if(7==e){var i=s.map(function(t){return Number(t.qty)});i.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=i.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0)+"手"}else if(8==e){var n=s.map(function(t){return Number(t.profit)});n.every(function(t){return isNaN(t)})?a[e]="N/A":a[e]=n.reduce(function(t,e){var s=Number(e);return isNaN(s)?t:t+e},0).toFixed(1)}}else a[e]=s.length+"笔";else a[e]="总计";else a[e]=""}),a},paintChart:function(t){var e=this.bars||[];t=t||!1;null==this.kChart&&(this.kChart=this.$echarts.init(document.getElementById("bt_kline")));var s=[],a=[];e.forEach(function(e,i){var n=e.date+"";if(n=n.substr(0,4)+"/"+parseInt(n.substr(4,2))+"/"+parseInt(n.substr(6,2)),!t){var o=e.time%1e4+"";3==o.length&&(o="0"+o),n+=" "+o.substr(0,2)+":"+o.substr(2,2)}s.push(n),a.push([e.open,e.close,e.low,e.high])});var i=[],n=[],o="";JSON.parse(g()(this.trades)).reverse().forEach(function(t){var e=t.time+"";e=e.substr(0,4)+"/"+parseInt(e.substr(4,2))+"/"+parseInt(e.substr(6,2))+" "+e.substr(8,2)+":"+e.substr(10,2);var s="开多"==t.action||"平空"==t.action||"OL"==t.action||"CS"==t.action,a="开"==t.action[0]||"O"==t.action[0];if(o==e){var r=i[i.length-1];r.data.volume+=t.volume,r.data.flag+=a?1:2}else i.push({value:[e,t.price],symbol:"triangle",symbolRotate:s?0:180,symbolOffset:[0,s?"50%":"-50%"],itemStyle:{color:s?"#f00":"#0f0"},data:{isBuy:s,volume:t.volume,price:t.price,flag:a?1:2}});var l=i[i.length-1],c=i[i.length-2];if(l.data.flag>=2){var u=(l.data.price-c.data.price)*(s?-1:1)>=0;n.push({source:i.length-2,target:i.length-1,lineStyle:{color:u?"#f00":"#0f0",type:"solid",width:1}})}o=e});var r={title:{text:this.btInfo.state.code+" "+this.btInfo.state.period},toolbox:{show:!0,orient:"horizontal",feature:{saveAsImage:{},restore:{}}},tooltip:{trigger:"axis",axisPointer:{type:"cross"},formatter:function(t){t.sort(function(t,e){return t.seriesIndex-e.seriesIndex});var e,s=t[0].axisValue,a=t[0].data,i=s+"
";if(i+=t[0].marker+"开: "+a[1].toFixed(2)+"
",i+=t[0].marker+"高: "+a[4].toFixed(2)+"
",i+=t[0].marker+"低: "+a[3].toFixed(2)+"
",i+=t[0].marker+"收: "+a[2].toFixed(2)+"
",t.length>1){var n=t[1].data.data,o=(e=n).isBuy?3==e.flag?"翻多: +":2==e.flag?"平空: +":"开多: +":3==e.flag?"翻空: -":2==e.flag?"平多: -":"开空: -";i+=t[1].marker+o+n.volume+"@"+n.price+"
"}return i}},axisPointer:{link:{xAxisIndex:"all"},label:{backgroundColor:"#777"}},grid:{left:"4%",right:"4%",bottom:"10%"},xAxis:{type:"category",data:s,scale:!0,boundaryGap:!0,axisLine:{onZero:!1},splitLine:{show:!1},splitNumber:20,min:"dataMin",max:"dataMax"},yAxis:{scale:!0,splitArea:{show:!0}},dataZoom:[{type:"inside",start:90,end:100},{show:!0,type:"slider",y:"95%",start:90,end:100}],series:[{name:"K线",type:"candlestick",data:a,yAxisIndex:"0",itemStyle:{normal:{color:"#ec0000",color0:"#00da3c",borderColor:"#8A0000",borderColor0:"#008F28"}},markLine:{symbol:["none","none"],data:[{name:"min line on close",type:"min",valueDim:"close"},{name:"max line on close",type:"max",valueDim:"close"}]}},{type:"graph",layout:"none",coordinateSystem:"cartesian2d",data:i,links:n,symbolSize:20,zlevel:9}]};this.kChart.setOption(r)},fmtDate:function(t){var e=t+"";return e.substr(0,4)+"."+e.substr(4,2)+"."+e.substr(6,2)},fmtProfit:function(t){return t>0?"text-danger":t<0?"text-success":""},fmtTime:function(t,e){if(e=e||!1){var s=t+"";return s.substr(2,2)+"."+s.substr(4,2)+"."+s.substr(6,2)+" "+s.substr(8,2)+":"+s.substr(10,2)+":"+s.substr(12,2)+","+s.substr(14,3)}var a=t+"";return a.substr(2,2)+"."+a.substr(4,2)+"."+a.substr(6,2)+" "+a.substr(8,2)+":"+a.substr(10,2)},formatAmount:function(t,e){return t[e.property].toFixed(2)},fmtPrice:function(t){for(var e=t.toFixed(4),s=0;s净值: "+t[s].value.toFixed(4):e+="
"+t[s].seriesName+": "+t[s].value;return e}},grid:{top:42,left:"8",right:"8",bottom:"8",containLabel:!0},xAxis:{type:"category",data:[],boundaryGap:!1,axisLabel:{textStyle:{color:"#000"},formatter:function(t,e){return(t+="").substr(0,4)+"."+t.substr(4,2)+"."+t.substr(6,2)}},axisLine:{lineStyle:{color:"#000"}}},yAxis:[{type:"value",axisLabel:{textStyle:{color:"#000"},formatter:function(t,e){return t.toFixed(4)}},axisLine:{lineStyle:{color:"#000"}},scale:!0}],series:[{name:"净值",type:"line",stack:"净值",areaStyle:{normal:{color:{type:"linear",x:0,y:0,x2:0,y2:1,colorStops:[{offset:0,color:"rgba(102,156,214,0.5)"},{offset:1,color:"rgba(242,242,242,0.3)"}],globalCoord:!1}}},data:[],lineStyle:{normal:{color:"rgb(102,156,214)",width:2}},itemStyle:{normal:{color:"rgb(102,156,214)",borderWidth:1}}}]},e=[],s=[],a=parseInt(this.btInfo.capital),i=this.funds.length-1;i>=0;i--){var n=this.funds[i];e.push(n.date);var o=a+n.dynbalance;s.push(o/a)}var r=Math.max.apply(null,s),l=Math.min.apply(null,s);if(r==l)r*=1.05,l*=.95;else{var c=r-l;r+=.05*c,l=Math.max(0,l-.05*c)}t.xAxis.data=e,t.series[0].data=s,t.yAxis[0].max=r,t.yAxis[0].min=l,this.tChart.setOption(t)},getClass:function(t){return t>=0?"text-danger":"text-success"}},mounted:function(){window.onresize=function(){var t=this;t.zooming||(t.zooming=!0,setTimeout(function(){t.kChart&&t.kChart.resize(),t.tChart&&t.tChart.resize(),t.zooming=!1},300))}}}),V={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticStyle:{height:"100%",width:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"0 44px"}},[s("el-tabs",{staticStyle:{height:"100%",margin:"0"},attrs:{value:t.selCat,"tab-position":"top"},on:{"tab-click":t.onCatSel}},[s("el-tab-pane",{attrs:{label:"绩效概览",name:"summary"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"信号分析",name:"kline"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"成交明细",name:"trd"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"信号明细",name:"sig"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"回合明细",name:"rnd"}}),t._v(" "),s("el-tab-pane",{attrs:{label:"每日绩效",name:"fnd"}})],1)],1),t._v(" "),s("div",{staticStyle:{flex:"1",overflow:"auto",width:"100%"}},[s("div",{directives:[{name:"show",rawName:"v-show",value:"kline"==t.selCat,expression:"selCat=='kline'"}],staticStyle:{height:"100%",width:"100%",overflow:"auto"}},[t._m(0)]),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:"summary"==t.selCat,expression:"selCat=='summary'"}],staticStyle:{height:"100%",width:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"0 200px",width:"100%"}},[s("el-row",[s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("回测天数")]),t._v(" "),s("p",{staticClass:"panel-val text-info"},[t._v(t._s(t.btInfo?t.btInfo.perform.days:0)+"天")])])]),t._v(" "),s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("开始时间")]),t._v(" "),s("p",{staticClass:"panel-val text-info"},[t._v(t._s(t.btInfo?t.fmtBtTime(t.btInfo.state.stime):"-"))])])]),t._v(" "),s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("结束时间")]),t._v(" "),s("p",{staticClass:"panel-val text-info"},[t._v(t._s(t.btInfo?t.fmtBtTime(t.btInfo.state.etime):"-"))])])]),t._v(" "),s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("初始资金")]),t._v(" "),s("p",{staticClass:"panel-val text-info"},[t._v(t._s(t.btInfo?t.btInfo.capital.toFixed(1):0))])])]),t._v(" "),s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("总收益率")]),t._v(" "),s("p",{staticClass:"panel-val",class:t.getClass(t.btInfo?t.btInfo.perform.total_return:0)},[t._v(t._s(t.btInfo?t.btInfo.perform.total_return.toFixed(2):"0.00")+"%")])])]),t._v(" "),s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("胜率")]),t._v(" "),s("p",{staticClass:"panel-val text-danger"},[t._v(t._s(t.btInfo?t.btInfo.perform.win_rate.toFixed(2):"0.00")+"%")])])])],1),t._v(" "),s("div",{staticClass:"divider"}),t._v(" "),s("el-row",[s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("年化收益率")]),t._v(" "),s("p",{staticClass:"panel-val",class:t.getClass(t.btInfo?t.btInfo.perform.annual_return:0)},[t._v(t._s(t.btInfo?t.btInfo.perform.annual_return.toFixed(2):"0.00")+"%")])])]),t._v(" "),s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("最大回撤")]),t._v(" "),s("p",{staticClass:"panel-val text-success"},[t._v(t._s(t.btInfo?t.btInfo.perform.max_falldown.toFixed(2):"0.00")+"%")])])]),t._v(" "),s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("最大盈利")]),t._v(" "),s("p",{staticClass:"panel-val text-danger"},[t._v(t._s(t.btInfo?t.btInfo.perform.max_profratio.toFixed(2):"0.00")+"%")])])]),t._v(" "),s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("夏普率")]),t._v(" "),s("p",{staticClass:"panel-val text-info"},[t._v(t._s(t.btInfo?t.btInfo.perform.sharpe_ratio.toFixed(2):"0.00"))])])]),t._v(" "),s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("索提诺比率")]),t._v(" "),s("p",{staticClass:"panel-val text-info"},[t._v(t._s(t.btInfo?t.btInfo.perform.sortino_ratio.toFixed(2):"0.00"))])])]),t._v(" "),s("el-col",{attrs:{span:4}},[s("div",{staticClass:"panel"},[s("p",{staticClass:"panel-tag"},[t._v("卡尔玛比率")]),t._v(" "),s("p",{staticClass:"panel-val text-info"},[t._v(t._s(t.btInfo?t.btInfo.perform.calmar_ratio.toFixed(2):"0.00"))])])])],1)],1),t._v(" "),s("div",{staticClass:"divider"}),t._v(" "),t._m(1)]),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:"sig"==t.selCat,expression:"selCat=='sig'"},{name:"loading",rawName:"v-loading",value:t.loading.signal,expression:"loading.signal"}],staticStyle:{"max-height":"100%",overflow:"auto"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.signals}},[s("el-table-column",{attrs:{prop:"code",label:"品种",width:"120",sortable:""}}),t._v(" "),s("el-table-column",{attrs:{prop:"target",label:"目标数量",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:t.fmtProfit(e.row.target)},[t._v(t._s(e.row.target))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"触发价格",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtPrice(e.row.sigprice)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"触发时间",width:"180"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtTime(e.row.gentime,!0)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"tag",label:"标记",sortable:"",width:"180"}})],1)],1),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:"rnd"==t.selCat,expression:"selCat=='rnd'"},{name:"loading",rawName:"v-loading",value:t.loading.round,expression:"loading.round"}],staticStyle:{"max-height":"100%",overflow:"auto"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.rounds}},[s("el-table-column",{attrs:{prop:"code",label:"品种",width:"120",sortable:""}}),t._v(" "),s("el-table-column",{attrs:{label:"方向",width:"64"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:"LONG"==e.row.direct?"text-danger":"text-success"},[t._v(t._s("LONG"==e.row.direct?"多":"空"))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"开仓时间",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtTime(e.row.opentime)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"开仓价格",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtPrice(e.row.openprice)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"平仓时间",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtTime(e.row.closetime)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"平仓价格",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtPrice(e.row.closeprice)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"qty",label:"数量",width:"64"}}),t._v(" "),s("el-table-column",{attrs:{label:"盈亏",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.profit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.profit.toFixed(1)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"潜在盈利",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{staticClass:"text-danger"},[t._v(t._s(e.row.maxprofit.toFixed(1)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"潜在亏损",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{staticClass:"text-success"},[t._v(t._s(e.row.maxloss.toFixed(1)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"entertag",label:"进场标记",width:"100"}}),t._v(" "),s("el-table-column",{attrs:{prop:"exittag",label:"出场标记",width:"100"}})],1)],1),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:"fnd"==t.selCat,expression:"selCat=='fnd'"},{name:"loading",rawName:"v-loading",value:t.loading.fund,expression:"loading.fund"}],staticStyle:{"max-height":"100%",overflow:"auto"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.funds}},[s("el-table-column",{attrs:{label:"日期",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtDate(e.row.date)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"平仓盈亏",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.closeprofit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.closeprofit.toFixed(1)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"浮动盈亏",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.dynprofit>=0?"text-danger":"text-success"},[t._v(t._s(e.row.dynprofit.toFixed(1)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"fee",label:"手续费",width:"120"}}),t._v(" "),s("el-table-column",{attrs:{prop:"dynbalance",label:"动态权益",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:e.row.dynbalance>=0?"text-danger":"text-success"},[t._v(t._s(e.row.dynbalance.toFixed(1)))])]}}])})],1)],1),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:"trd"==t.selCat,expression:"selCat=='trd'"},{name:"loading",rawName:"v-loading",value:t.loading.trade,expression:"loading.trade"}],staticStyle:{"max-height":"100%",overflow:"auto"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.trades,"summary-method":t.getTrdSum,"show-summary":""}},[s("el-table-column",{attrs:{prop:"code",label:"品种",width:"120",sortable:""}}),t._v(" "),s("el-table-column",{attrs:{label:"时间",width:"120"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtTime(e.row.time)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"动作",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",{class:"开多"==e.row.action||"平空"==e.row.action?"text-danger":"text-success"},[t._v(t._s(e.row.action))])]}}])}),t._v(" "),s("el-table-column",{attrs:{label:"价格",width:"80"},scopedSlots:t._u([{key:"default",fn:function(e){return[s("span",[t._v(t._s(t.fmtPrice(e.row.price)))])]}}])}),t._v(" "),s("el-table-column",{attrs:{prop:"volume",label:"数量",width:"80"}}),t._v(" "),s("el-table-column",{attrs:{prop:"tag",label:"标记",sortable:""}})],1)],1)])])},staticRenderFns:[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticStyle:{height:"100%",width:"100%"},attrs:{id:"bt_kline"}},[e("p",[this._v("这里绘制K线和信号列表")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticStyle:{flex:"1",width:"100%"}},[e("div",{staticStyle:{height:"100%",width:"100%"}},[e("div",{staticStyle:{height:"100%",width:"100%"},attrs:{id:"bt_fund"}},[e("p",[this._v("这里绘制每日收益曲线")])])])])}]};var Y=s("VU/8")(R,V,!1,function(t){s("JgyK")},"data-v-64b19f62",null).exports,J={name:"Backtest",components:{codemirror:T.codemirror,BTComp:Y},data:function(){var t=this;return{btConfig:{startTime:(new Date).addDays(-92),endTime:new Date,capital:5e5,slippage:0},pickerOption:{format:"yyyy-MM-dd HH:mm"},selData:"editor",strategies:[],strasOnWay:!1,showBTCfg:!1,curStra:null,curBT:null,backtests:[],edit:!1,content:"",content_bak:"",cmOptions:{mode:"python",keyMap:"sublime",lineNumbers:!0,smartIndent:!0,indentUnit:4,indentWithTabs:!1,lineWrapping:!0,gutters:["CodeMirror-linenumbers","CodeMirror-foldgutter","CodeMirror-lint-markers"],foldGutter:!0,autofocus:!0,matchBrackets:!0,autoCloseBrackets:!0,styleActiveLine:!0,extraKeys:{"Ctrl-S":function(){t.onSaveCode()}}}}},methods:{fmtTime:function(t){return(t+="").substr(0,4)+"."+t.substr(4,2)+"."+t.substr(6,2)+" "+t.substr(8,2)+":"+t.substr(10,2)},onDelBacktest:function(t){var e=this;100==t.progress?this.$confirm("确定要删除该回测记录吗","删除回测",{confirmButtonText:"确定",cancelButtonText:"取消"}).then(function(){e.$api.delBacktest(t.id,function(s){if(s.result<0)e.$message.error("回测任务删除失败:"+s.message);else{e.$message({message:"回测任务删除成功",type:"success"});for(var a=0;a=0&&(t.actions=e.actions)})},onCommitUser:function(){var t=this;this.$api.commitUser(this.curUser,this.addUser?"add":"mod",function(e){e.result<0?t.$notify.error(e.message):(t.$notify({message:"用户信息提交成功",type:"success"}),t.showUserDlg=!1,t.queryUsers())})},queryUsers:function(){var t=this;this.loading_user=!0,this.$api.getUsers(function(e){e.result>=0?t.users=e.users:t.$notify.error(e.message),t.loading_user=!1})},handleUserCmd:function(t){var e=this;if("add"==t)this.curUser={loginid:"",name:"",remark:"",role:"admin",passwd:"",iplist:""},this.addUser=!0,this.showUserDlg=!0;else if("mod"==t){if(""==this.curUser.loginid)return void this.$alert("请选择要修改的用户");this.curUser.passwd="********",this.addUser=!1,this.showUserDlg=!0}else if("del"==t){if(""==this.curUser.loginid)return void this.$alert("请选择要删除的用户");this.$confirm("确定要删除用户"+this.curUser.loginid+"吗?","删除用户",{confirmButtonText:"确定",cancelButtonText:"取消",type:"danger"}).then(function(){e.$api.delUser(e.curUser.loginid,function(t){t.result<0?e.$notify.error(t.message):(e.$notify({message:"用户已删除",type:"success"}),e.queryUsers())})})}else if("reset"==t){if(""==this.curUser.loginid)return void this.$alert("请选择要重置密码的用户");this.$prompt("请输入新的密码","重置密码",{confirmButtonText:"确定",cancelButtonText:"取消",inputType:"password"}).then(function(t){var s=t.value;e.$confirm("确定要重置用户"+e.curUser.loginid+"的密码吗?","重置密码",{confirmButtonText:"确定",cancelButtonText:"取消",type:"danger"}).then(function(){e.$api.resetpwd(e.curUser.loginid,s,function(t){t.result<0?e.$notify.error(t.message):e.$notify({message:"密码重置成功",type:"success"})})})}).catch(function(){})}else"refresh"==t&&this.queryUsers()}},mounted:function(){var t=this;this.$nextTick(function(){t.queryUsers()})}},at={render:function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticStyle:{height:"100%",width:"100%"}},[s("el-row",{staticStyle:{height:"100%"}},[s("el-col",{staticStyle:{height:"100%"},attrs:{span:12}},[s("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"0",margin:"2px 4px 0px 4px","min-height":"39px",display:"flex","flex-direction":"row"}},[s("div",{staticClass:"simtab",staticStyle:{flex:"0"}},[s("span",[t._v("用户列表")])]),t._v(" "),s("div",{staticStyle:{flex:"1","border-bottom":"1px solid #E4E7ED","margin-top":"6px"}},[s("el-dropdown",{staticStyle:{float:"right"},attrs:{"split-button":"",type:"danger",size:"mini",trigger:"click"},on:{command:t.handleUserCmd}},[s("i",{staticClass:"el-icon-edit-outline"}),t._v("管理\n "),s("el-dropdown-menu",{attrs:{slot:"dropdown"},slot:"dropdown"},[s("el-dropdown-item",{attrs:{command:"add"}},[s("i",{staticClass:"el-icon-circle-plus-outline"}),t._v("添加用户")]),t._v(" "),s("el-dropdown-item",{attrs:{command:"mod"}},[s("i",{staticClass:"el-icon-edit-outline"}),t._v("修改用户")]),t._v(" "),s("el-dropdown-item",{attrs:{command:"del"}},[s("i",{staticClass:"el-icon-delete"}),t._v("删除用户")]),t._v(" "),s("el-dropdown-item",{attrs:{command:"reset"}},[s("i",{staticClass:"el-icon-switch-button"}),t._v("重置密码")]),t._v(" "),s("el-dropdown-item",{attrs:{divided:"",command:"refresh"}},[s("i",{staticClass:"el-icon-refresh"}),t._v("刷新数据")])],1)],1)],1)]),t._v(" "),s("div",{staticStyle:{flex:"1",margin:"10px 4px",height:"100%"}},[s("div",{staticStyle:{"max-height":"100%",overflow:"auto"}},[s("el-table",{directives:[{name:"loading",rawName:"v-loading",value:t.loading_user,expression:"loading_user"}],staticClass:"table",attrs:{border:"",stripe:"",data:t.users,"highlight-current-row":""},on:{"update:data":function(e){t.users=e},"current-change":t.handleSelectUser}},[s("el-table-column",{attrs:{prop:"loginid",label:"登录名",width:"100"}}),t._v(" "),s("el-table-column",{attrs:{prop:"name",label:"姓名",width:"80"}}),t._v(" "),s("el-table-column",{attrs:{prop:"createtime",label:"创建时间",width:"100"}}),t._v(" "),s("el-table-column",{attrs:{prop:"createby",label:"创建人",width:"100"}}),t._v(" "),s("el-table-column",{attrs:{prop:"modifytime",label:"修改时间",width:"100"}}),t._v(" "),s("el-table-column",{attrs:{prop:"modifyby",label:"修改人",width:"100"}}),t._v(" "),s("el-table-column",{attrs:{prop:"remark",label:"备注"}})],1)],1)])])]),t._v(" "),s("el-col",{staticStyle:{height:"100%","border-left":"1px solid #E4E7ED"},attrs:{span:12}},[s("div",{staticStyle:{height:"100%",display:"flex","flex-direction":"column"}},[s("div",{staticStyle:{flex:"0",margin:"2px 4px 0px 4px","min-height":"39px",display:"flex","flex-direction":"row"}},[s("div",{staticClass:"simtab",staticStyle:{flex:"0"}},[s("span",[t._v("操作日志")])]),t._v(" "),s("div",{staticStyle:{flex:"1","border-bottom":"1px solid #E4E7ED","margin-top":"6px"}},[s("el-row",{staticStyle:{height:"100%"}},[s("el-col",{attrs:{span:12,offset:9}},[s("el-date-picker",{staticStyle:{float:"right"},attrs:{type:"daterange",align:"right",size:"mini","unlink-panels":"","range-separator":"至","start-placeholder":"开始日期","end-placeholder":"结束日期"},on:{change:t.handleDtRangeChange},model:{value:t.daterange,callback:function(e){t.daterange=e},expression:"daterange"}})],1),t._v(" "),s("el-col",{attrs:{span:3}},[s("el-button",{staticStyle:{float:"right"},attrs:{type:"primary",icon:"el-icon-refresh",size:"mini",plain:""},on:{click:t.onClickQryActions}},[t._v("刷新")])],1)],1)],1)]),t._v(" "),s("div",{staticStyle:{flex:"1",margin:"10px 4px",height:"100%"}},[s("div",{staticStyle:{"max-height":"100%",overflow:"auto"}},[s("el-table",{staticClass:"table",attrs:{border:"",stripe:"",data:t.actions}},[s("el-table-column",{attrs:{prop:"loginid",label:"登录名",width:"100"}}),t._v(" "),s("el-table-column",{attrs:{prop:"actiontime",label:"操作时间",width:"100"}}),t._v(" "),s("el-table-column",{attrs:{prop:"actionip",label:"来源IP",width:"120"}}),t._v(" "),s("el-table-column",{attrs:{prop:"action",label:"类型",width:"80"}}),t._v(" "),s("el-table-column",{attrs:{prop:"remark",label:"备注"}})],1)],1)])])])],1),t._v(" "),s("el-dialog",{staticClass:"dialog-user",attrs:{title:t.addUser?"添加用户":"修改用户",visible:t.showUserDlg,width:"25%"},on:{"update:visible":function(e){t.showUserDlg=e}}},[s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("登录名:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-input",{attrs:{size:"mini",disabled:!t.addUser},model:{value:t.curUser.loginid,callback:function(e){t.$set(t.curUser,"loginid",e)},expression:"curUser.loginid"}})],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("用户姓名:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-input",{attrs:{size:"mini"},model:{value:t.curUser.name,callback:function(e){t.$set(t.curUser,"name",e)},expression:"curUser.name"}})],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("用户类型:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-tooltip",{attrs:{effect:"dark",content:"拥有全部的管理权限",placement:"top-start"}},[s("el-radio",{attrs:{label:"admin"},model:{value:t.curUser.role,callback:function(e){t.$set(t.curUser,"role",e)},expression:"curUser.role"}},[t._v("管理员")])],1),t._v(" "),s("el-tooltip",{attrs:{effect:"dark",content:"拥有风控权限",placement:"top-start"}},[s("el-radio",{attrs:{label:"risker"},model:{value:t.curUser.role,callback:function(e){t.$set(t.curUser,"role",e)},expression:"curUser.role"}},[t._v("风控员")])],1)],1)],1),t._v(" "),s("el-row",[s("el-col",{attrs:{span:6}},[s("a",[t._v("登录密码:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-input",{attrs:{size:"mini",type:"password","show-password":"",disabled:!t.addUser},model:{value:t.curUser.passwd,callback:function(e){t.$set(t.curUser,"passwd",e)},expression:"curUser.passwd"}})],1)],1),t._v(" "),s("el-row",{staticStyle:{height:"60px"}},[s("el-col",{attrs:{span:6}},[s("a",[t._v("IP限制:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-input",{attrs:{type:"textarea",size:"mini"},model:{value:t.curUser.iplist,callback:function(e){t.$set(t.curUser,"iplist",e)},expression:"curUser.iplist"}})],1)],1),t._v(" "),s("el-row",{staticStyle:{height:"60px"}},[s("el-col",{attrs:{span:6}},[s("a",[t._v("备注信息:")])]),t._v(" "),s("el-col",{attrs:{span:18}},[s("el-input",{staticStyle:{"min-height":"80px"},attrs:{type:"textarea"},model:{value:t.curUser.remark,callback:function(e){t.$set(t.curUser,"remark",e)},expression:"curUser.remark"}})],1)],1),t._v(" "),s("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[s("el-button",{attrs:{type:"primary",plain:"",size:"mini"},on:{click:t.onCommitUser}},[t._v("提交数据")])],1)],1)],1)},staticRenderFns:[]};var it=s("VU/8")(st,at,!1,function(t){s("wpV1")},"data-v-c381bfac",null).exports;a.default.use(o.a);var nt=new o.a({routes:[{path:"/",redirect:"/login"},{path:"/login",name:"Login",component:c},{path:"/index",name:"Index",component:h,redirect:"/monitor",children:[{path:"/monitor",name:"monitor",component:L},{path:"/deploy",name:"deploy",component:Z},{path:"/backtest",name:"backtest",component:W},{path:"/schedule",name:"schedule",component:et},{path:"/admins",name:"admins",component:it}]}]});a.default.use(p.a);var ot=new p.a.Store({state:{cache:{userinfo:{loginid:"",name:"",loginip:"",logintime:""},isLogined:!1,loginid:""},folders:[]},getters:{cache:function(t){return t.cache},folders:function(t){return t.folders}},modules:{},actions:{},mutations:{loginok:function(t,e){t.cache.userinfo=e.userinfo,t.cache.isLogined=!0,t.cache.loginid=e.loginid},logoutok:function(t){t.cache.userinfo={loginid:"",name:"",loginip:"",logintime:""},t.cache.isLogined=!1,t.cache.loginid=""},setfolders:function(t,e){t.folders=e.folders}}}),rt=s("HI0L"),lt=s.n(rt),ct=s("zL8q"),ut=s.n(ct),dt=(s("wLZQ"),s("XLwt")),pt=s.n(dt),ft=s("woOf"),mt=s.n(ft),ht=s("pFYg"),vt=s.n(ht),gt=s("7t+N"),bt=s.n(gt),yt=s("NC6I"),wt=s.n(yt);Date.prototype.format=function(t){var e={"M+":this.getMonth()+1,"d+":this.getDate(),"h+":this.getHours(),"m+":this.getMinutes(),"s+":this.getSeconds(),"q+":Math.floor((this.getMonth()+3)/3),S:this.getMilliseconds()};for(var s in/(y+)/.test(t)&&(t=t.replace(RegExp.$1,(this.getFullYear()+"").substr(4-RegExp.$1.length))),e)new RegExp("("+s+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?e[s]:("00"+e[s]).substr((""+e[s]).length)));return t},Date.prototype.addDays=function(t){var e=this.valueOf();return new Date(e+=864e5*t)},String.prototype.format=function(t){if(arguments.length>0){var e=this;if(1==arguments.length&&"object"==(void 0===t?"undefined":vt()(t)))for(var s in t){var a=new RegExp("({"+s+"})","g");e=e.replace(a,t[s])}else for(var i=0;i"})},SP1q:function(t,e){},Yokd:function(t,e){},b3w7:function(t,e){},g1Tk:function(t,e){},iP0y:function(t,e){},"kk+i":function(t,e){},"n+8b":function(t,e){},"nZf+":function(t,e){},pKwi:function(t,e){},tvDO:function(t,e){},wLZQ:function(t,e){},wpV1:function(t,e){},wx9h:function(t,e){}},["NHnr"]);
//# sourceMappingURL=app.d3652914f874ea570f72.js.map
================================================
FILE: wtpy/monitor/static/console/static/js/manifest.3ad1d5771e9b13dbdad2.js
================================================
!function(r){var n=window.webpackJsonp;window.webpackJsonp=function(e,u,c){for(var f,i,p,a=0,l=[];an},ie64:function(){return b.ie()&&d},firefox:function(){return y()||i},opera:function(){return y()||r},webkit:function(){return y()||o},safari:function(){return b.webkit()},chrome:function(){return y()||a},windows:function(){return y()||u},osx:function(){return y()||l},linux:function(){return y()||c},iphone:function(){return y()||f},mobile:function(){return y()||f||p||h||m},nativeApp:function(){return y()||g},android:function(){return y()||h},ipad:function(){return y()||p}};e.exports=b},"+2Ke":function(e,t){t.SOURCE_FORMAT_ORIGINAL="original",t.SOURCE_FORMAT_ARRAY_ROWS="arrayRows",t.SOURCE_FORMAT_OBJECT_ROWS="objectRows",t.SOURCE_FORMAT_KEYED_COLUMNS="keyedColumns",t.SOURCE_FORMAT_UNKNOWN="unknown",t.SOURCE_FORMAT_TYPED_ARRAY="typedArray",t.SERIES_LAYOUT_BY_COLUMN="column",t.SERIES_LAYOUT_BY_ROW="row"},"+Dgo":function(e,t,n){var i=n("Y5nL"),r=n("Pgdp"),o=n("kdOt").detectSourceFormat,a=n("+2Ke").SERIES_LAYOUT_BY_COLUMN;i.extend({type:"dataset",defaultOption:{seriesLayoutBy:a,sourceHeader:null,dimensions:null,source:null},optionUpdated:function(){o(this)}}),r.extend({type:"dataset"})},"+E39":function(e,t,n){e.exports=!n("S82l")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},"+K7g":function(e,t,n){var i=n("Icdr");i.registerAction({type:"focusNodeAdjacency",event:"focusNodeAdjacency",update:"series:focusNodeAdjacency"},function(){}),i.registerAction({type:"unfocusNodeAdjacency",event:"unfocusNodeAdjacency",update:"series:unfocusNodeAdjacency"},function(){})},"+PQg":function(e,t,n){var i=n("Icdr"),r=n("/gxq"),o=n("3h1/"),a=n("dCQY"),s=n("0sHC"),l=n("Pdtn"),u=n("1Hui"),c=n("v/cD"),h=i.extendComponentView({type:"toolbox",render:function(e,t,n,i){var h=this.group;if(h.removeAll(),e.get("show")){var d=+e.get("itemSize"),f=e.get("feature")||{},p=this._features||(this._features={}),g=[];r.each(f,function(e,t){g.push(t)}),new u(this._featureNames||[],g).add(m).update(m).remove(r.curry(m,null)).execute(),this._featureNames=g,c.layout(h,e,n),h.add(c.makeBackground(h.getBoundingRect(),e)),h.eachChild(function(e){var t=e.__title,i=e.hoverStyle;if(i&&t){var r=o.getBoundingRect(t,o.makeFont(i)),a=e.position[0]+h.position[0],s=!1;e.position[1]+h.position[1]+d+r.height>n.getHeight()&&(i.textPosition="top",s=!0);var l=s?-5-r.height:d+8;a+r.width/2>n.getWidth()?(i.textPosition=["100%",l],i.textAlign="right"):a-r.width/2<0&&(i.textPosition=[0,l],i.textAlign="left")}})}function m(o,u){var c,m=g[o],v=g[u],y=f[m],b=new l(y,e,e.ecModel);if(i&&null!=i.newTitle&&i.featureName===m&&(y.title=i.newTitle),m&&!v){if(function(e){return 0===e.indexOf("my")}(m))c={model:b,onclick:b.option.onclick,featureName:m};else{var x=a.get(m);if(!x)return;c=new x(b,t,n)}p[m]=c}else{if(!(c=p[v]))return;c.model=b,c.ecModel=t,c.api=n}m||!v?b.get("show")&&!c.unusable?(!function(i,o,a){var l=i.getModel("iconStyle"),u=i.getModel("emphasis.iconStyle"),c=o.getIcons?o.getIcons():i.get("icon"),f=i.get("title")||{};if("string"==typeof c){var p=c,g=f;f={},(c={})[a]=p,f[a]=g}var m=i.iconPaths={};r.each(c,function(a,c){var p=s.createIcon(a,{},{x:-d/2,y:-d/2,width:d,height:d});p.setStyle(l.getItemStyle()),p.hoverStyle=u.getItemStyle(),p.setStyle({text:f[c],textAlign:u.get("textAlign"),textBorderRadius:u.get("textBorderRadius"),textPadding:u.get("textPadding"),textFill:null});var g=e.getModel("tooltip");g&&g.get("show")&&p.attr("tooltip",r.extend({content:f[c],formatter:g.get("formatter",!0)||function(){return f[c]},formatterParams:{componentType:"toolbox",name:c,title:f[c],$vars:["name","title"]},position:g.get("position",!0)||"bottom"},g.option)),s.setHoverStyle(p),e.get("showTitle")&&(p.__title=f[c],p.on("mouseover",function(){var t=u.getItemStyle(),n="vertical"===e.get("orient")?null==e.get("right")?"right":"left":null==e.get("bottom")?"bottom":"top";p.setStyle({textFill:u.get("textFill")||t.fill||t.stroke||"#000",textBackgroundColor:u.get("textBackgroundColor"),textPosition:u.get("textPosition")||n})}).on("mouseout",function(){p.setStyle({textFill:null,textBackgroundColor:null})})),p.trigger(i.get("iconStatus."+c)||"normal"),h.add(p),p.on("click",r.bind(o.onclick,o,t,n,c)),m[c]=p})}(b,c,m),b.setIconStatus=function(e,t){var n=this.option,i=this.iconPaths;n.iconStatus=n.iconStatus||{},n.iconStatus[e]=t,i[e]&&i[e].trigger(t)},c.render&&c.render(b,t,n,i)):c.remove&&c.remove(t,n):c.dispose&&c.dispose(t,n)}},updateView:function(e,t,n,i){r.each(this._features,function(e){e.updateView&&e.updateView(e.model,t,n,i)})},remove:function(e,t){r.each(this._features,function(n){n.remove&&n.remove(e,t)}),this.group.removeAll()},dispose:function(e,t){r.each(this._features,function(n){n.dispose&&n.dispose(e,t)})}});e.exports=h},"+UTs":function(e,t,n){var i=n("GxVO"),r=n("No7X"),o=i.extend({type:"polygon",shape:{points:null,smooth:!1,smoothConstraint:null},buildPath:function(e,t){r.buildPath(e,t,!0)}});e.exports=o},"+Y0c":function(e,t,n){var i=new(n("zMj2"))(50);function r(){var e=this.__cachedImgObj;this.onload=this.onerror=this.__cachedImgObj=null;for(var t=0;tt&&(t=i.height)}this.height=t+1},getNodeById:function(e){if(this.getId()===e)return this;for(var t=0,n=this.children,i=n.length;t=0&&this.hostTree.data.setItemLayout(this.dataIndex,e,t)},getLayout:function(){return this.hostTree.data.getItemLayout(this.dataIndex)},getModel:function(e){if(!(this.dataIndex<0)){var t=this.hostTree.data.getItemModel(this.dataIndex),n=this.getLevelModel();return n?t.getModel(e,n.getModel(e)):t.getModel(e)}},getLevelModel:function(){return(this.hostTree.levelModels||[])[this.depth]},setVisual:function(e,t){this.dataIndex>=0&&this.hostTree.data.setItemVisual(this.dataIndex,e,t)},getVisual:function(e,t){return this.hostTree.data.getItemVisual(this.dataIndex,e,t)},getRawIndex:function(){return this.hostTree.data.getRawIndex(this.dataIndex)},getId:function(){return this.hostTree.data.getId(this.dataIndex)},isAncestorOf:function(e){for(var t=e.parentNode;t;){if(t===this)return!0;t=t.parentNode}return!1},isDescendantOf:function(e){return e!==this&&e.isAncestorOf(this)}},u.prototype={constructor:u,type:"tree",eachNode:function(e,t,n){this.root.eachNode(e,t,n)},getNodeByDataIndex:function(e){var t=this.data.getRawIndex(e);return this._nodes[t]},getNodeByName:function(e){return this.root.getNodeByName(e)},update:function(){for(var e=this.data,t=this._nodes,n=0,i=t.length;n=0&&(s[o[l].depth]=new a(o[l],this,t));if(i&&n)return r(i,n,this,!0,function(e,t){e.wrapMethod("getItemModel",function(e,t){return e.customizeGetParent(function(e){var n=this.parentModel,i=n.getData().getItemLayout(t).depth,r=n.levelModels[i];return r||this.parentModel}),e}),t.wrapMethod("getItemModel",function(e,t){return e.customizeGetParent(function(e){var n=this.parentModel,i=n.getGraph().getEdgeByIndex(t),r=i.node1.getLayout().depth,o=n.levelModels[r];return o||this.parentModel}),e})}).data},setNodePosition:function(e,t){var n=this.option.data[e];n.localX=t[0],n.localY=t[1]},getGraph:function(){return this.getData().graph},getEdgeData:function(){return this.getGraph().edgeData},formatTooltip:function(e,t,n){if("edge"===n){var i=this.getDataParams(e,n),r=i.data,a=r.source+" -- "+r.target;return i.value&&(a+=" : "+i.value),o(a)}if("node"===n){var l=this.getGraph().getNodeByIndex(e).getLayout().value,u=this.getDataParams(e,n).data.name;if(l)a=u+" : "+l;return o(a)}return s.superCall(this,"formatTooltip",e,t)},optionUpdated:function(){var e=this.option;!0===e.focusNodeAdjacency&&(e.focusNodeAdjacency="allEdges")},getDataParams:function(e,t){var n=s.superCall(this,"getDataParams",e,t);if(null==n.value&&"node"===t){var i=this.getGraph().getNodeByIndex(e).getLayout().value;n.value=i}return n},defaultOption:{zlevel:0,z:2,coordinateSystem:"view",layout:null,left:"5%",top:"5%",right:"20%",bottom:"5%",orient:"horizontal",nodeWidth:20,nodeGap:8,draggable:!0,focusNodeAdjacency:!1,layoutIterations:32,label:{show:!0,position:"right",color:"#000",fontSize:12},levels:[],nodeAlign:"justify",itemStyle:{borderWidth:1,borderColor:"#333"},lineStyle:{color:"#314656",opacity:.2,curveness:.5},emphasis:{label:{show:!0},lineStyle:{opacity:.5}},animationEasing:"linear",animationDuration:1e3}})),l=s;e.exports=l},"/+sa":function(e,t,n){var i=n("BNYN");function r(e){this._setting=e||{},this._extent=[1/0,-1/0],this._interval=0,this.init&&this.init.apply(this,arguments)}r.prototype.parse=function(e){return e},r.prototype.getSetting=function(e){return this._setting[e]},r.prototype.contain=function(e){var t=this._extent;return e>=t[0]&&e<=t[1]},r.prototype.normalize=function(e){var t=this._extent;return t[1]===t[0]?.5:(e-t[0])/(t[1]-t[0])},r.prototype.scale=function(e){var t=this._extent;return e*(t[1]-t[0])+t[0]},r.prototype.unionExtent=function(e){var t=this._extent;e[0]t[1]&&(t[1]=e[1])},r.prototype.unionExtentFromData=function(e,t){this.unionExtent(e.getApproximateExtent(t))},r.prototype.getExtent=function(){return this._extent.slice()},r.prototype.setExtent=function(e,t){var n=this._extent;isNaN(e)||(n[0]=e),isNaN(t)||(n[1]=t)},r.prototype.isBlank=function(){return this._isBlank},r.prototype.setBlank=function(e){this._isBlank=e},r.prototype.getLabel=null,i.enableClassExtend(r),i.enableClassManagement(r,{registerWhenExtend:!0});var o=r;e.exports=o},"/86O":function(e,t,n){var i=n("9qnA"),r=n("/gxq"),o=n("3h1/"),a=n("qjrH"),s=n("28kU").ContextCachedBy,l=function(e){i.call(this,e)};l.prototype={constructor:l,type:"text",brush:function(e,t){var n=this.style;this.__dirty&&a.normalizeTextStyle(n,!0),n.fill=n.stroke=n.shadowBlur=n.shadowColor=n.shadowOffsetX=n.shadowOffsetY=null;var i=n.text;null!=i&&(i+=""),a.needDrawText(i,n)?(this.setTransform(e),a.renderText(this,e,i,n,null,t),this.restoreTransform(e)):e.__attrCachedBy=s.NONE},getBoundingRect:function(){var e=this.style;if(this.__dirty&&a.normalizeTextStyle(e,!0),!this._rect){var t=e.text;null!=t?t+="":t="";var n=o.getBoundingRect(e.text+"",e.font,e.textAlign,e.textVerticalAlign,e.textPadding,e.textLineHeight,e.rich);if(n.x+=e.x||0,n.y+=e.y||0,a.getStroke(e.textStroke,e.textStrokeWidth)){var i=e.textStrokeWidth;n.x-=i/2,n.y-=i/2,n.width+=i,n.height+=i}this._rect=n}return this._rect}},r.inherits(l,i);var u=l;e.exports=u},"/99E":function(e,t,n){n("0BOU"),n("yEXw"),n("w6Zv")},"/BOW":function(e,t,n){var i=n("/gxq"),r=n("2HcM"),o=function(e,t,n,i,o){r.call(this,e,t,n),this.type=i||"value",this.axisIndex=o};o.prototype={constructor:o,model:null,isHorizontal:function(){return"horizontal"!==this.coordinateSystem.getModel().get("layout")}},i.inherits(o,r);var a=o;e.exports=a},"/ZBO":function(e,t,n){var i=n("dOVI"),r=n("C7PF"),o=i.identity,a=5e-5;function s(e){return e>a||e<-a}var l=function(e){(e=e||{}).position||(this.position=[0,0]),null==e.rotation&&(this.rotation=0),e.scale||(this.scale=[1,1]),this.origin=this.origin||null},u=l.prototype;u.transform=null,u.needLocalTransform=function(){return s(this.rotation)||s(this.position[0])||s(this.position[1])||s(this.scale[0]-1)||s(this.scale[1]-1)};var c=[];u.updateTransform=function(){var e=this.parent,t=e&&e.transform,n=this.needLocalTransform(),r=this.transform;if(n||t){r=r||i.create(),n?this.getLocalTransform(r):o(r),t&&(n?i.mul(r,e.transform,r):i.copy(r,e.transform)),this.transform=r;var a=this.globalScaleRatio;if(null!=a&&1!==a){this.getGlobalScale(c);var s=c[0]<0?-1:1,l=c[1]<0?-1:1,u=((c[0]-s)*a+s)/c[0]||0,h=((c[1]-l)*a+l)/c[1]||0;r[0]*=u,r[1]*=u,r[2]*=h,r[3]*=h}this.invTransform=this.invTransform||i.create(),i.invert(this.invTransform,r)}else r&&o(r)},u.getLocalTransform=function(e){return l.getLocalTransform(this,e)},u.setTransform=function(e){var t=this.transform,n=e.dpr||1;t?e.setTransform(n*t[0],n*t[1],n*t[2],n*t[3],n*t[4],n*t[5]):e.setTransform(n,0,0,n,0,0)},u.restoreTransform=function(e){var t=e.dpr||1;e.setTransform(t,0,0,t,0,0)};var h=[],d=i.create();u.setLocalTransform=function(e){if(e){var t=e[0]*e[0]+e[1]*e[1],n=e[2]*e[2]+e[3]*e[3],i=this.position,r=this.scale;s(t-1)&&(t=Math.sqrt(t)),s(n-1)&&(n=Math.sqrt(n)),e[0]<0&&(t=-t),e[3]<0&&(n=-n),i[0]=e[4],i[1]=e[5],r[0]=t,r[1]=n,this.rotation=Math.atan2(-e[1]/n,e[0]/t)}},u.decomposeTransform=function(){if(this.transform){var e=this.parent,t=this.transform;e&&e.transform&&(i.mul(h,e.invTransform,t),t=h);var n=this.origin;n&&(n[0]||n[1])&&(d[4]=n[0],d[5]=n[1],i.mul(h,t,d),h[4]-=n[0],h[5]-=n[1],t=h),this.setLocalTransform(t)}},u.getGlobalScale=function(e){var t=this.transform;return e=e||[],t?(e[0]=Math.sqrt(t[0]*t[0]+t[1]*t[1]),e[1]=Math.sqrt(t[2]*t[2]+t[3]*t[3]),t[0]<0&&(e[0]=-e[0]),t[3]<0&&(e[1]=-e[1]),e):(e[0]=1,e[1]=1,e)},u.transformCoordToLocal=function(e,t){var n=[e,t],i=this.invTransform;return i&&r.applyTransform(n,n,i),n},u.transformCoordToGlobal=function(e,t){var n=[e,t],i=this.transform;return i&&r.applyTransform(n,n,i),n},l.getLocalTransform=function(e,t){o(t=t||[]);var n=e.origin,r=e.scale||[1,1],a=e.rotation||0,s=e.position||[0,0];return n&&(t[4]-=n[0],t[5]-=n[1]),i.scale(t,t,r),a&&i.rotate(t,t,a),n&&(t[4]+=n[0],t[5]+=n[1]),t[4]+=s[0],t[5]+=s[1],t};var f=l;e.exports=f},"/bQp":function(e,t){e.exports={}},"/gZK":function(e,t,n){var i=n("hcq/"),r=n("Rfu2"),o=n("/gxq"),a=o.extend,s=o.isArray;e.exports=function(e,t,n){t=s(t)&&{coordDimensions:t}||a({},t);var o=e.getSource(),l=i(o,t),u=new r(l,e);return u.initData(o,n),u}},"/gxq":function(e,t){var n={"[object Function]":1,"[object RegExp]":1,"[object Date]":1,"[object Error]":1,"[object CanvasGradient]":1,"[object CanvasPattern]":1,"[object Image]":1,"[object Canvas]":1},i={"[object Int8Array]":1,"[object Uint8Array]":1,"[object Uint8ClampedArray]":1,"[object Int16Array]":1,"[object Uint16Array]":1,"[object Int32Array]":1,"[object Uint32Array]":1,"[object Float32Array]":1,"[object Float64Array]":1},r=Object.prototype.toString,o=Array.prototype,a=o.forEach,s=o.filter,l=o.slice,u=o.map,c=o.reduce,h={};function d(e){if(null==e||"object"!=typeof e)return e;var t=e,o=r.call(e);if("[object Array]"===o){if(!S(e)){t=[];for(var a=0,s=e.length;a-1}function o(e,t){return r(e)&&e._isRouter&&(null==t||e.type===t)}function a(e,t){for(var n in t)e[n]=t[n];return e}var s={name:"RouterView",functional:!0,props:{name:{type:String,default:"default"}},render:function(e,t){var n=t.props,i=t.children,r=t.parent,o=t.data;o.routerView=!0;for(var s=r.$createElement,u=n.name,c=r.$route,h=r._routerViewCache||(r._routerViewCache={}),d=0,f=!1;r&&r._routerRoot!==r;){var p=r.$vnode?r.$vnode.data:{};p.routerView&&d++,p.keepAlive&&r._directInactive&&r._inactive&&(f=!0),r=r.$parent}if(o.routerViewDepth=d,f){var g=h[u],m=g&&g.component;return m?(g.configProps&&l(m,o,g.route,g.configProps),s(m,o,i)):s()}var v=c.matched[d],y=v&&v.components[u];if(!v||!y)return h[u]=null,s();h[u]={component:y},o.registerRouteInstance=function(e,t){var n=v.instances[u];(t&&n!==e||!t&&n===e)&&(v.instances[u]=t)},(o.hook||(o.hook={})).prepatch=function(e,t){v.instances[u]=t.componentInstance},o.hook.init=function(e){e.data.keepAlive&&e.componentInstance&&e.componentInstance!==v.instances[u]&&(v.instances[u]=e.componentInstance)};var b=v.props&&v.props[u];return b&&(a(h[u],{route:c,configProps:b}),l(y,o,c,b)),s(y,o,i)}};function l(e,t,n,i){var r=t.props=function(e,t){switch(typeof t){case"undefined":return;case"object":return t;case"function":return t(e);case"boolean":return t?e.params:void 0;default:0}}(n,i);if(r){r=t.props=a({},r);var o=t.attrs=t.attrs||{};for(var s in r)e.props&&s in e.props||(o[s]=r[s],delete r[s])}}var u=/[!'()*]/g,c=function(e){return"%"+e.charCodeAt(0).toString(16)},h=/%2C/g,d=function(e){return encodeURIComponent(e).replace(u,c).replace(h,",")},f=decodeURIComponent;function p(e){var t={};return(e=e.trim().replace(/^(\?|#|&)/,""))?(e.split("&").forEach(function(e){var n=e.replace(/\+/g," ").split("="),i=f(n.shift()),r=n.length>0?f(n.join("=")):null;void 0===t[i]?t[i]=r:Array.isArray(t[i])?t[i].push(r):t[i]=[t[i],r]}),t):t}function g(e){var t=e?Object.keys(e).map(function(t){var n=e[t];if(void 0===n)return"";if(null===n)return d(t);if(Array.isArray(n)){var i=[];return n.forEach(function(e){void 0!==e&&(null===e?i.push(d(t)):i.push(d(t)+"="+d(e)))}),i.join("&")}return d(t)+"="+d(n)}).filter(function(e){return e.length>0}).join("&"):null;return t?"?"+t:""}var m=/\/?$/;function v(e,t,n,i){var r=i&&i.options.stringifyQuery,o=t.query||{};try{o=y(o)}catch(e){}var a={name:t.name||e&&e.name,meta:e&&e.meta||{},path:t.path||"/",hash:t.hash||"",query:o,params:t.params||{},fullPath:x(t,r),matched:e?function(e){var t=[];for(;e;)t.unshift(e),e=e.parent;return t}(e):[]};return n&&(a.redirectedFrom=x(n,r)),Object.freeze(a)}function y(e){if(Array.isArray(e))return e.map(y);if(e&&"object"==typeof e){var t={};for(var n in e)t[n]=y(e[n]);return t}return e}var b=v(null,{path:"/"});function x(e,t){var n=e.path,i=e.query;void 0===i&&(i={});var r=e.hash;return void 0===r&&(r=""),(n||"/")+(t||g)(i)+r}function _(e,t){return t===b?e===t:!!t&&(e.path&&t.path?e.path.replace(m,"")===t.path.replace(m,"")&&e.hash===t.hash&&w(e.query,t.query):!(!e.name||!t.name)&&(e.name===t.name&&e.hash===t.hash&&w(e.query,t.query)&&w(e.params,t.params)))}function w(e,t){if(void 0===e&&(e={}),void 0===t&&(t={}),!e||!t)return e===t;var n=Object.keys(e),i=Object.keys(t);return n.length===i.length&&n.every(function(n){var i=e[n],r=t[n];return"object"==typeof i&&"object"==typeof r?w(i,r):String(i)===String(r)})}function C(e,t,n){var i=e.charAt(0);if("/"===i)return e;if("?"===i||"#"===i)return t+e;var r=t.split("/");n&&r[r.length-1]||r.pop();for(var o=e.replace(/^\//,"").split("/"),a=0;a=0&&(t=e.slice(i),e=e.slice(0,i));var r=e.indexOf("?");return r>=0&&(n=e.slice(r+1),e=e.slice(0,r)),{path:e,query:n,hash:t}}(r.path||""),c=t&&t.path||"/",h=u.path?C(u.path,c,n||r.append):c,d=function(e,t,n){void 0===t&&(t={});var i,r=n||p;try{i=r(e||"")}catch(e){i={}}for(var o in t)i[o]=t[o];return i}(u.query,r.query,i&&i.options.parseQuery),f=r.hash||u.hash;return f&&"#"!==f.charAt(0)&&(f="#"+f),{_normalized:!0,path:h,query:d,hash:f}}var W,q=[String,Object],G=[String,Array],U=function(){},Y={name:"RouterLink",props:{to:{type:q,required:!0},tag:{type:String,default:"a"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,ariaCurrentValue:{type:String,default:"page"},event:{type:G,default:"click"}},render:function(e){var t=this,n=this.$router,i=this.$route,r=n.resolve(this.to,i,this.append),o=r.location,s=r.route,l=r.href,u={},c=n.options.linkActiveClass,h=n.options.linkExactActiveClass,d=null==c?"router-link-active":c,f=null==h?"router-link-exact-active":h,p=null==this.activeClass?d:this.activeClass,g=null==this.exactActiveClass?f:this.exactActiveClass,y=s.redirectedFrom?v(null,H(s.redirectedFrom),null,n):s;u[g]=_(i,y),u[p]=this.exact?u[g]:function(e,t){return 0===e.path.replace(m,"/").indexOf(t.path.replace(m,"/"))&&(!t.hash||e.hash===t.hash)&&function(e,t){for(var n in t)if(!(n in e))return!1;return!0}(e.query,t.query)}(i,y);var b=u[g]?this.ariaCurrentValue:null,x=function(e){X(e)&&(t.replace?n.replace(o,U):n.push(o,U))},w={click:X};Array.isArray(this.event)?this.event.forEach(function(e){w[e]=x}):w[this.event]=x;var C={class:u},S=!this.$scopedSlots.$hasNormal&&this.$scopedSlots.default&&this.$scopedSlots.default({href:l,route:s,navigate:x,isActive:u[p],isExactActive:u[g]});if(S){if(1===S.length)return S[0];if(S.length>1||!S.length)return 0===S.length?e():e("span",{},S)}if("a"===this.tag)C.on=w,C.attrs={href:l,"aria-current":b};else{var k=function e(t){if(t)for(var n,i=0;i-1&&(s.params[d]=n.params[d]);return s.path=j(c.path,s.params),l(c,s,a)}if(s.path){s.params={};for(var f=0;f=e.length?n():e[r]?t(e[r],function(){i(r+1)}):i(r+1)};i(0)}function _e(e){return function(t,n,i){var o=!1,a=0,s=null;we(e,function(e,t,n,l){if("function"==typeof e&&void 0===e.cid){o=!0,a++;var u,c=ke(function(t){var r;((r=t).__esModule||Se&&"Module"===r[Symbol.toStringTag])&&(t=t.default),e.resolved="function"==typeof t?t:W.extend(t),n.components[l]=t,--a<=0&&i()}),h=ke(function(e){var t="Failed to resolve async component "+l+": "+e;s||(s=r(e)?e:new Error(t),i(s))});try{u=e(c,h)}catch(e){h(e)}if(u)if("function"==typeof u.then)u.then(c,h);else{var d=u.component;d&&"function"==typeof d.then&&d.then(c,h)}}}),o||i()}}function we(e,t){return Ce(e.map(function(e){return Object.keys(e.components).map(function(n){return t(e.components[n],e.instances[n],e,n)})}))}function Ce(e){return Array.prototype.concat.apply([],e)}var Se="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;function ke(e){var t=!1;return function(){for(var n=[],i=arguments.length;i--;)n[i]=arguments[i];if(!t)return t=!0,e.apply(this,n)}}var Te={redirected:1,aborted:2,cancelled:3,duplicated:4};function Me(e,t){return Ae(e,t,Te.redirected,'Redirected when going from "'+e.fullPath+'" to "'+function(e){if("string"==typeof e)return e;if("path"in e)return e.path;var t={};return Ie.forEach(function(n){n in e&&(t[n]=e[n])}),JSON.stringify(t,null,2)}(t)+'" via a navigation guard.')}function De(e,t){return Ae(e,t,Te.cancelled,'Navigation cancelled from "'+e.fullPath+'" to "'+t.fullPath+'" with a new navigation.')}function Ae(e,t,n,i){var r=new Error(i);return r._isRouter=!0,r.from=e,r.to=t,r.type=n,r}var Ie=["params","query","hash"];var Oe=function(e,t){this.router=e,this.base=function(e){if(!e)if(Z){var t=document.querySelector("base");e=(e=t&&t.getAttribute("href")||"/").replace(/^https?:\/\/[^\/]+/,"")}else e="/";"/"!==e.charAt(0)&&(e="/"+e);return e.replace(/\/$/,"")}(t),this.current=b,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[],this.listeners=[]};function Ee(e,t,n,i){var r=we(e,function(e,i,r,o){var a=function(e,t){"function"!=typeof e&&(e=W.extend(e));return e.options[t]}(e,t);if(a)return Array.isArray(a)?a.map(function(e){return n(e,i,r,o)}):n(a,i,r,o)});return Ce(i?r.reverse():r)}function Le(e,t){if(t)return function(){return e.apply(t,arguments)}}Oe.prototype.listen=function(e){this.cb=e},Oe.prototype.onReady=function(e,t){this.ready?e():(this.readyCbs.push(e),t&&this.readyErrorCbs.push(t))},Oe.prototype.onError=function(e){this.errorCbs.push(e)},Oe.prototype.transitionTo=function(e,t,n){var i=this,r=this.router.match(e,this.current);this.confirmTransition(r,function(){var e=i.current;i.updateRoute(r),t&&t(r),i.ensureURL(),i.router.afterHooks.forEach(function(t){t&&t(r,e)}),i.ready||(i.ready=!0,i.readyCbs.forEach(function(e){e(r)}))},function(e){n&&n(e),e&&!i.ready&&(i.ready=!0,o(e,Te.redirected)?i.readyCbs.forEach(function(e){e(r)}):i.readyErrorCbs.forEach(function(t){t(e)}))})},Oe.prototype.confirmTransition=function(e,t,n){var a,s=this,l=this.current,u=function(e){!o(e)&&r(e)&&(s.errorCbs.length?s.errorCbs.forEach(function(t){t(e)}):(i(),console.error(e))),n&&n(e)},c=e.matched.length-1,h=l.matched.length-1;if(_(e,l)&&c===h&&e.matched[c]===l.matched[h])return this.ensureURL(),u(Ae(a=l,e,Te.duplicated,'Avoided redundant navigation to current location: "'+a.fullPath+'".'));var d=function(e,t){var n,i=Math.max(e.length,t.length);for(n=0;n0)){var t=this.router,n=t.options.scrollBehavior,i=ve&&n;i&&this.listeners.push(se());var r=function(){var n=e.current,r=Ne(e.base);e.current===b&&r===e._startLocation||e.transitionTo(r,function(e){i&&le(t,e,n,!0)})};window.addEventListener("popstate",r),this.listeners.push(function(){window.removeEventListener("popstate",r)})}},t.prototype.go=function(e){window.history.go(e)},t.prototype.push=function(e,t,n){var i=this,r=this.current;this.transitionTo(e,function(e){ye(S(i.base+e.fullPath)),le(i.router,e,r,!1),t&&t(e)},n)},t.prototype.replace=function(e,t,n){var i=this,r=this.current;this.transitionTo(e,function(e){be(S(i.base+e.fullPath)),le(i.router,e,r,!1),t&&t(e)},n)},t.prototype.ensureURL=function(e){if(Ne(this.base)!==this.current.fullPath){var t=S(this.base+this.current.fullPath);e?ye(t):be(t)}},t.prototype.getCurrentLocation=function(){return Ne(this.base)},t}(Oe);function Ne(e){var t=decodeURI(window.location.pathname);return e&&0===t.toLowerCase().indexOf(e.toLowerCase())&&(t=t.slice(e.length)),(t||"/")+window.location.search+window.location.hash}var Re=function(e){function t(t,n,i){e.call(this,t,n),i&&function(e){var t=Ne(e);if(!/^\/#/.test(t))return window.location.replace(S(e+"/#"+t)),!0}(this.base)||Be()}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.setupListeners=function(){var e=this;if(!(this.listeners.length>0)){var t=this.router.options.scrollBehavior,n=ve&&t;n&&this.listeners.push(se());var i=function(){var t=e.current;Be()&&e.transitionTo(ze(),function(i){n&&le(e.router,i,t,!0),ve||Ve(i.fullPath)})},r=ve?"popstate":"hashchange";window.addEventListener(r,i),this.listeners.push(function(){window.removeEventListener(r,i)})}},t.prototype.push=function(e,t,n){var i=this,r=this.current;this.transitionTo(e,function(e){$e(e.fullPath),le(i.router,e,r,!1),t&&t(e)},n)},t.prototype.replace=function(e,t,n){var i=this,r=this.current;this.transitionTo(e,function(e){Ve(e.fullPath),le(i.router,e,r,!1),t&&t(e)},n)},t.prototype.go=function(e){window.history.go(e)},t.prototype.ensureURL=function(e){var t=this.current.fullPath;ze()!==t&&(e?$e(t):Ve(t))},t.prototype.getCurrentLocation=function(){return ze()},t}(Oe);function Be(){var e=ze();return"/"===e.charAt(0)||(Ve("/"+e),!1)}function ze(){var e=window.location.href,t=e.indexOf("#");if(t<0)return"";var n=(e=e.slice(t+1)).indexOf("?");if(n<0){var i=e.indexOf("#");e=i>-1?decodeURI(e.slice(0,i))+e.slice(i):decodeURI(e)}else e=decodeURI(e.slice(0,n))+e.slice(n);return e}function Fe(e){var t=window.location.href,n=t.indexOf("#");return(n>=0?t.slice(0,n):t)+"#"+e}function $e(e){ve?ye(Fe(e)):window.location.hash=e}function Ve(e){ve?be(Fe(e)):window.location.replace(Fe(e))}var je=function(e){function t(t,n){e.call(this,t,n),this.stack=[],this.index=-1}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.push=function(e,t,n){var i=this;this.transitionTo(e,function(e){i.stack=i.stack.slice(0,i.index+1).concat(e),i.index++,t&&t(e)},n)},t.prototype.replace=function(e,t,n){var i=this;this.transitionTo(e,function(e){i.stack=i.stack.slice(0,i.index).concat(e),t&&t(e)},n)},t.prototype.go=function(e){var t=this,n=this.index+e;if(!(n<0||n>=this.stack.length)){var i=this.stack[n];this.confirmTransition(i,function(){t.index=n,t.updateRoute(i)},function(e){o(e,Te.duplicated)&&(t.index=n)})}},t.prototype.getCurrentLocation=function(){var e=this.stack[this.stack.length-1];return e?e.fullPath:"/"},t.prototype.ensureURL=function(){},t}(Oe),He=function(e){void 0===e&&(e={}),this.app=null,this.apps=[],this.options=e,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=Q(e.routes||[],this);var t=e.mode||"hash";switch(this.fallback="history"===t&&!ve&&!1!==e.fallback,this.fallback&&(t="hash"),Z||(t="abstract"),this.mode=t,t){case"history":this.history=new Pe(this,e.base);break;case"hash":this.history=new Re(this,e.base,this.fallback);break;case"abstract":this.history=new je(this,e.base);break;default:0}},We={currentRoute:{configurable:!0}};function qe(e,t){return e.push(t),function(){var n=e.indexOf(t);n>-1&&e.splice(n,1)}}He.prototype.match=function(e,t,n){return this.matcher.match(e,t,n)},We.currentRoute.get=function(){return this.history&&this.history.current},He.prototype.init=function(e){var t=this;if(this.apps.push(e),e.$once("hook:destroyed",function(){var n=t.apps.indexOf(e);n>-1&&t.apps.splice(n,1),t.app===e&&(t.app=t.apps[0]||null),t.app||t.history.teardownListeners()}),!this.app){this.app=e;var n=this.history;if(n instanceof Pe||n instanceof Re){var i=function(){n.setupListeners()};n.transitionTo(n.getCurrentLocation(),i,i)}n.listen(function(e){t.apps.forEach(function(t){t._route=e})})}},He.prototype.beforeEach=function(e){return qe(this.beforeHooks,e)},He.prototype.beforeResolve=function(e){return qe(this.resolveHooks,e)},He.prototype.afterEach=function(e){return qe(this.afterHooks,e)},He.prototype.onReady=function(e,t){this.history.onReady(e,t)},He.prototype.onError=function(e){this.history.onError(e)},He.prototype.push=function(e,t,n){var i=this;if(!t&&!n&&"undefined"!=typeof Promise)return new Promise(function(t,n){i.history.push(e,t,n)});this.history.push(e,t,n)},He.prototype.replace=function(e,t,n){var i=this;if(!t&&!n&&"undefined"!=typeof Promise)return new Promise(function(t,n){i.history.replace(e,t,n)});this.history.replace(e,t,n)},He.prototype.go=function(e){this.history.go(e)},He.prototype.back=function(){this.go(-1)},He.prototype.forward=function(){this.go(1)},He.prototype.getMatchedComponents=function(e){var t=e?e.matched?e:this.resolve(e).route:this.currentRoute;return t?[].concat.apply([],t.matched.map(function(e){return Object.keys(e.components).map(function(t){return e.components[t]})})):[]},He.prototype.resolve=function(e,t,n){var i=H(e,t=t||this.history.current,n,this),r=this.match(i,t),o=r.redirectedFrom||r.fullPath;return{location:i,route:r,href:function(e,t,n){var i="hash"===n?"#"+t:t;return e?S(e+"/"+i):i}(this.history.base,o,this.mode),normalizedTo:i,resolved:r}},He.prototype.addRoutes=function(e){this.matcher.addRoutes(e),this.history.current!==b&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(He.prototype,We),He.install=K,He.version="3.3.4",Z&&window.Vue&&window.Vue.use(He),t.a=He},"/vN/":function(e,t,n){var i=n("Icdr"),r=n("/gZK"),o=n("/gxq"),a=n("vXqC"),s=n("wWR3").getPercentWithPrecision,l=n("kQD9"),u=n("5KBG").retrieveRawAttr,c=n("kdOt").makeSeriesEncodeForNameBased,h=n("FCaW"),d=i.extendSeriesModel({type:"series.pie",init:function(e){d.superApply(this,"init",arguments),this.legendVisualProvider=new h(o.bind(this.getData,this),o.bind(this.getRawData,this)),this.updateSelectedMap(this._createSelectableList()),this._defaultLabelLine(e)},mergeOption:function(e){d.superCall(this,"mergeOption",e),this.updateSelectedMap(this._createSelectableList())},getInitialData:function(e,t){return r(this,{coordDimensions:["value"],encodeDefaulter:o.curry(c,this)})},_createSelectableList:function(){for(var e=this.getRawData(),t=e.mapDimension("value"),n=[],i=0,r=e.count();i=t.length)break;r=t[i++]}else{if((i=t.next()).done)break;r=i.value}var o=r.target.__resizeListeners__||[];o.length&&o.forEach(function(e){e()})}};t.addResizeListener=function(e,t){a||(e.__resizeListeners__||(e.__resizeListeners__=[],e.__ro__=new o.default(s),e.__ro__.observe(e)),e.__resizeListeners__.push(t))},t.removeResizeListener=function(e,t){e&&e.__resizeListeners__&&(e.__resizeListeners__.splice(e.__resizeListeners__.indexOf(t),1),e.__resizeListeners__.length||e.__ro__.disconnect())}},"06OY":function(e,t,n){var i=n("3Eo+")("meta"),r=n("EqjI"),o=n("D2L2"),a=n("evD5").f,s=0,l=Object.isExtensible||function(){return!0},u=!n("S82l")(function(){return l(Object.preventExtensions({}))}),c=function(e){a(e,i,{value:{i:"O"+ ++s,w:{}}})},h=e.exports={KEY:i,NEED:!1,fastKey:function(e,t){if(!r(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!o(e,i)){if(!l(e))return"F";if(!t)return"E";c(e)}return e[i].i},getWeak:function(e,t){if(!o(e,i)){if(!l(e))return!0;if(!t)return!1;c(e)}return e[i].w},onFreeze:function(e){return u&&h.NEED&&l(e)&&!o(e,i)&&c(e),e}}},"0BNI":function(e,t,n){var i=n("/gxq"),r=n("0sHC"),o=n("Pdtn"),a=n("43ae"),s=n("vjPX"),l=["axisLine","axisLabel","axisTick","minorTick","splitLine","minorSplitLine","splitArea"];function u(e,t,n){t[1]>t[0]&&(t=t.slice().reverse());var i=e.coordToPoint([t[0],n]),r=e.coordToPoint([t[1],n]);return{x1:i[0],y1:i[1],x2:r[0],y2:r[1]}}function c(e){return e.getRadiusAxis().inverse?0:1}function h(e){var t=e[0],n=e[e.length-1];t&&n&&Math.abs(Math.abs(t.coord-n.coord)-360)<1e-4&&e.pop()}var d=a.extend({type:"angleAxis",axisPointerClass:"PolarAxisPointer",render:function(e,t){if(this.group.removeAll(),e.get("show")){var n=e.axis,r=n.polar,o=r.getRadiusAxis().getExtent(),a=n.getTicksCoords(),s=n.getMinorTicksCoords(),u=i.map(n.getViewLabels(),function(e){return(e=i.clone(e)).coord=n.dataToCoord(e.tickValue),e});h(u),h(a),i.each(l,function(t){!e.get(t+".show")||n.scale.isBlank()&&"axisLine"!==t||this["_"+t](e,r,a,s,o,u)},this)}},_axisLine:function(e,t,n,i,o){var a,s=e.getModel("axisLine.lineStyle"),l=c(t),u=l?0:1;(a=0===o[u]?new r.Circle({shape:{cx:t.cx,cy:t.cy,r:o[l]},style:s.getLineStyle(),z2:1,silent:!0}):new r.Ring({shape:{cx:t.cx,cy:t.cy,r:o[l],r0:o[u]},style:s.getLineStyle(),z2:1,silent:!0})).style.fill=null,this.group.add(a)},_axisTick:function(e,t,n,o,a){var s=e.getModel("axisTick"),l=(s.get("inside")?-1:1)*s.get("length"),h=a[c(t)],d=i.map(n,function(e){return new r.Line({shape:u(t,[h,h+l],e.coord)})});this.group.add(r.mergePath(d,{style:i.defaults(s.getModel("lineStyle").getLineStyle(),{stroke:e.get("axisLine.lineStyle.color")})}))},_minorTick:function(e,t,n,o,a){if(o.length){for(var s=e.getModel("axisTick"),l=e.getModel("minorTick"),h=(s.get("inside")?-1:1)*l.get("length"),d=a[c(t)],f=[],p=0;pv?"left":"right",x=Math.abs(m[1]-y)/g<.3?"middle":m[1]>y?"top":"bottom";h&&h[u]&&h[u].textStyle&&(a=new o(h[u].textStyle,d,d.ecModel));var _=new r.Text({silent:s.isLabelSilent(e)});this.group.add(_),r.setTextStyle(_.style,a,{x:m[0],y:m[1],textFill:a.getTextColor()||e.get("axisLine.lineStyle.color"),text:n.formattedLabel,textAlign:b,textVerticalAlign:x}),p&&(_.eventData=s.makeAxisEventDataBase(e),_.eventData.targetType="axisLabel",_.eventData.value=n.rawLabel)},this)},_splitLine:function(e,t,n,o,a){var s=e.getModel("splitLine").getModel("lineStyle"),l=s.get("color"),c=0;l=l instanceof Array?l:[l];for(var h=[],d=0;d=r.start.time&&n.timea.end.time&&e.reverse(),e},_getRangeInfo:function(e){var t;(e=[this.getDateInfo(e[0]),this.getDateInfo(e[1])])[0].time>e[1].time&&(t=!0,e.reverse());var n=Math.floor(e[1].time/864e5)-Math.floor(e[0].time/864e5)+1,i=new Date(e[0].time),r=i.getDate(),o=e[1].date.getDate();i.setDate(r+n-1);var a=i.getDate();if(a!==o)for(var s=i.getTime()-e[1].time>0?1:-1;(a=i.getDate())!==o&&(i.getTime()-e[1].time)*s>0;)n-=s,i.setDate(a-s);var l=Math.floor((n+e[0].day+6)/7),u=t?1-l:l-1;return t&&e.reverse(),{range:[e[0].formatedDate,e[1].formatedDate],start:e[0],end:e[1],allDay:n,weeks:l,nthWeek:u,fweek:e[0].day,lweek:e[1].day}},_getDateByWeeksAndDay:function(e,t,n){var i=this._getRangeInfo(n);if(e>i.weeks||0===e&&ti.lweek)return!1;var r=7*(e-1)-i.fweek+t,o=new Date(i.start.time);return o.setDate(i.start.d+r),this.getDateInfo(o)}},s.dimensions=s.prototype.dimensions,s.getDimensionsInfo=s.prototype.getDimensionsInfo,s.create=function(e,t){var n=[];return e.eachComponent("calendar",function(i){var r=new s(i,e,t);n.push(r),i.coordinateSystem=r}),e.eachSeries(function(e){"calendar"===e.get("coordinateSystem")&&(e.coordinateSystem=n[e.get("calendarIndex")||0])}),n},a.register("calendar",s);var u=s;e.exports=u},"0MNY":function(e,t,n){n("4Nz2").__DEV__;var i=n("/gxq"),r=i.createHashMap,o=i.isString,a=i.isArray,s=i.each,l=(i.assert,n("jDhh").parseXML),u=r(),c={registerMap:function(e,t,n){var i;return a(t)?i=t:t.svg?i=[{type:"svg",source:t.svg,specialAreas:t.specialAreas}]:(t.geoJson&&!t.features&&(n=t.specialAreas,t=t.geoJson),i=[{type:"geoJSON",source:t,specialAreas:n}]),s(i,function(e){var t=e.type;"geoJson"===t&&(t=e.type="geoJSON"),(0,h[t])(e)}),u.set(e,i)},retrieveMap:function(e){return u.get(e)}},h={geoJSON:function(e){var t=e.source;e.geoJSON=o(t)?"undefined"!=typeof JSON&&JSON.parse?JSON.parse(t):new Function("return ("+t+");")():t},svg:function(e){e.svgXML=l(e.source)}};e.exports=c},"0O1a":function(e,t,n){var i=n("Icdr"),r=n("DZTl");n("Osoq"),n("w2H/"),n("mlpt"),n("XiVP"),n("H4Wn"),i.registerPreprocessor(r)},"0fQF":function(e,t){function n(){}function i(e,t,n,i){for(var r=0,o=t.length,a=0,s=0;r=o&&c+1>=a){for(var h=[],d=0;d=o&&d+1>=a)return i(r,l.components,t,e);u[n]=l}else u[n]=void 0}var g;s++}for(;s<=l;){var p=f();if(p)return p}},pushComponent:function(e,t,n){var i=e[e.length-1];i&&i.added===t&&i.removed===n?e[e.length-1]={count:i.count+1,added:t,removed:n}:e.push({count:1,added:t,removed:n})},extractCommon:function(e,t,n,i){for(var r=t.length,o=n.length,a=e.newPos,s=a-i,l=0;a+1=0&&e===parseInt(e,10)}}},data:function(){return{currentValue:0,userInput:null}},watch:{value:{immediate:!0,handler:function(e){var t=void 0===e?e:Number(e);if(void 0!==t){if(isNaN(t))return;if(this.stepStrictly){var n=this.getPrecision(this.step),i=Math.pow(10,n);t=Math.round(t/this.step)*i*this.step/i}void 0!==this.precision&&(t=this.toPrecision(t,this.precision))}t>=this.max&&(t=this.max),t<=this.min&&(t=this.min),this.currentValue=t,this.userInput=null,this.$emit("input",t)}}},computed:{minDisabled:function(){return this._decrease(this.value,this.step)this.max},numPrecision:function(){var e=this.value,t=this.step,n=this.getPrecision,i=this.precision,r=n(t);return void 0!==i?(r>i&&console.warn("[Element Warn][InputNumber]precision should not be less than the decimal places of step"),i):Math.max(n(e),r)},controlsAtRight:function(){return this.controls&&"right"===this.controlsPosition},_elFormItemSize:function(){return(this.elFormItem||{}).elFormItemSize},inputNumberSize:function(){return this.size||this._elFormItemSize||(this.$ELEMENT||{}).size},inputNumberDisabled:function(){return this.disabled||!!(this.elForm||{}).disabled},displayValue:function(){if(null!==this.userInput)return this.userInput;var e=this.currentValue;if("number"==typeof e){if(this.stepStrictly){var t=this.getPrecision(this.step),n=Math.pow(10,t);e=Math.round(e/this.step)*n*this.step/n}void 0!==this.precision&&(e=e.toFixed(this.precision))}return e}},methods:{toPrecision:function(e,t){return void 0===t&&(t=this.numPrecision),parseFloat(Math.round(e*Math.pow(10,t))/Math.pow(10,t))},getPrecision:function(e){if(void 0===e)return 0;var t=e.toString(),n=t.indexOf("."),i=0;return-1!==n&&(i=t.length-n-1),i},_increase:function(e,t){if("number"!=typeof e&&void 0!==e)return this.currentValue;var n=Math.pow(10,this.numPrecision);return this.toPrecision((n*e+n*t)/n)},_decrease:function(e,t){if("number"!=typeof e&&void 0!==e)return this.currentValue;var n=Math.pow(10,this.numPrecision);return this.toPrecision((n*e-n*t)/n)},increase:function(){if(!this.inputNumberDisabled&&!this.maxDisabled){var e=this.value||0,t=this._increase(e,this.step);this.setCurrentValue(t)}},decrease:function(){if(!this.inputNumberDisabled&&!this.minDisabled){var e=this.value||0,t=this._decrease(e,this.step);this.setCurrentValue(t)}},handleBlur:function(e){this.$emit("blur",e)},handleFocus:function(e){this.$emit("focus",e)},setCurrentValue:function(e){var t=this.currentValue;"number"==typeof e&&void 0!==this.precision&&(e=this.toPrecision(e,this.precision)),e>=this.max&&(e=this.max),e<=this.min&&(e=this.min),t!==e&&(this.userInput=null,this.$emit("input",e),this.$emit("change",e,t),this.currentValue=e)},handleInput:function(e){this.userInput=e},handleInputChange:function(e){var t=""===e?void 0:Number(e);isNaN(t)&&""!==e||this.setCurrentValue(t),this.userInput=null},select:function(){this.$refs.input.select()}},mounted:function(){var e=this.$refs.input.$refs.input;e.setAttribute("role","spinbutton"),e.setAttribute("aria-valuemax",this.max),e.setAttribute("aria-valuemin",this.min),e.setAttribute("aria-valuenow",this.currentValue),e.setAttribute("aria-disabled",this.inputNumberDisabled)},updated:function(){this.$refs&&this.$refs.input&&this.$refs.input.$refs.input.setAttribute("aria-valuenow",this.currentValue)}},c=n(0),h=Object(c.a)(u,i,[],!1,null,null,null);h.options.__file="packages/input-number/src/input-number.vue";var d=h.exports;d.install=function(e){e.component(d.name,d)};t.default=d},2:function(e,t){e.exports=n("2kvA")},22:function(e,t){e.exports=n("1oZe")},30:function(e,t,n){"use strict";var i=n(2);t.a={bind:function(e,t,n){var r=null,o=void 0,a=function(){return n.context[t.expression].apply()},s=function(){Date.now()-o<100&&a(),clearInterval(r),r=null};Object(i.on)(e,"mousedown",function(e){0===e.button&&(o=Date.now(),Object(i.once)(document,"mouseup",s),clearInterval(r),r=setInterval(a,100))})}}}})},"0nGg":function(e,t,n){var i=n("Icdr"),r=n("/gxq"),o=n("dZZy"),a=n("6n1D"),s=n("5Mek"),l=n("YpIy"),u=n("NKek").onIrrelevantElement,c=n("0sHC"),h=n("Goha"),d=n("hD/x").getNodeGlobalScale,f="__focusNodeAdjacency",p=["itemStyle","opacity"],g=["lineStyle","opacity"];function m(e,t){var n=e.getVisual("opacity");return null!=n?n:e.getModel().get(t)}function v(e,t,n){var i=e.getGraphicEl(),r=m(e,t);null!=n&&(null==r&&(r=1),r*=n),i.downplay&&i.downplay(),i.traverse(function(e){if(!e.isGroup){var t=e.lineLabelOriginalOpacity;null!=t&&null==n||(t=r),e.setStyle("opacity",t)}})}function y(e,t){var n=m(e,t),i=e.getGraphicEl();i.traverse(function(e){!e.isGroup&&e.setStyle("opacity",n)}),i.highlight&&i.highlight()}var b=i.extendChartView({type:"graph",init:function(e,t){var n=new o,i=new a,r=this.group;this._controller=new s(t.getZr()),this._controllerHost={target:r},r.add(n.group),r.add(i.group),this._symbolDraw=n,this._lineDraw=i,this._firstRender=!0},render:function(e,t,n){var i=this,r=e.coordinateSystem;this._model=e;var o=this._symbolDraw,a=this._lineDraw,s=this.group;if("view"===r.type){var l={position:r.position,scale:r.scale};this._firstRender?s.attr(l):c.updateProps(s,l,e)}h(e.getGraph(),d(e));var u=e.getData();o.updateData(u);var p=e.getEdgeData();a.updateData(p),this._updateNodeAndLinkScale(),this._updateController(e,t,n),clearTimeout(this._layoutTimeout);var g=e.forceLayout,m=e.get("force.layoutAnimation");g&&this._startForceLayoutIteration(g,m),u.eachItemGraphicEl(function(t,r){var o=u.getItemModel(r);t.off("drag").off("dragend");var a=o.get("draggable");a&&t.on("drag",function(){g&&(g.warmUp(),!this._layouting&&this._startForceLayoutIteration(g,m),g.setFixed(r),u.setItemLayout(r,t.position))},this).on("dragend",function(){g&&g.setUnfixed(r)},this),t.setDraggable(a&&g),t[f]&&t.off("mouseover",t[f]),t.__unfocusNodeAdjacency&&t.off("mouseout",t.__unfocusNodeAdjacency),o.get("focusNodeAdjacency")&&(t.on("mouseover",t[f]=function(){i._clearTimer(),n.dispatchAction({type:"focusNodeAdjacency",seriesId:e.id,dataIndex:t.dataIndex})}),t.on("mouseout",t.__unfocusNodeAdjacency=function(){i._dispatchUnfocus(n)}))},this),u.graph.eachEdge(function(t){var r=t.getGraphicEl();r[f]&&r.off("mouseover",r[f]),r.__unfocusNodeAdjacency&&r.off("mouseout",r.__unfocusNodeAdjacency),t.getModel().get("focusNodeAdjacency")&&(r.on("mouseover",r[f]=function(){i._clearTimer(),n.dispatchAction({type:"focusNodeAdjacency",seriesId:e.id,edgeDataIndex:t.dataIndex})}),r.on("mouseout",r.__unfocusNodeAdjacency=function(){i._dispatchUnfocus(n)}))});var v="circular"===e.get("layout")&&e.get("circular.rotateLabel"),y=u.getLayout("cx"),b=u.getLayout("cy");u.eachItemGraphicEl(function(e,t){var n=u.getItemModel(t).get("label.rotate")||0,i=e.getSymbolPath();if(v){var r=u.getItemLayout(t),o=Math.atan2(r[1]-b,r[0]-y);o<0&&(o=2*Math.PI+o);var a=r[0]=0),l=!s&&null!=r;(s||l)&&(t={textFill:e.textFill,textStroke:e.textStroke,textStrokeWidth:e.textStrokeWidth}),s&&(e.textFill="#fff",null==e.textStroke&&(e.textStroke=r,null==e.textStrokeWidth&&(e.textStrokeWidth=2))),l&&(e.textFill=r)}e.insideRollback=t}function le(e){var t=e.insideRollback;t&&(e.textFill=t.textFill,e.textStroke=t.textStroke,e.textStrokeWidth=t.textStrokeWidth,e.insideRollback=null)}function ue(e,t,n,i,r,o){if("function"==typeof r&&(o=r,r=null),i&&i.isAnimationEnabled()){var a=e?"Update":"",s=i.getShallow("animationDuration"+a),l=i.getShallow("animationEasing"+a),u=i.getShallow("animationDelay"+a);"function"==typeof u&&(u=u(r,i.getAnimationDelayParams?i.getAnimationDelayParams(t,r):null)),"function"==typeof s&&(s=s(r)),s>0?t.animateTo(n,s,u||0,l,o,!!o):(t.stopAnimation(),t.attr(n),o&&o())}else t.stopAnimation(),t.attr(n),o&&o()}function ce(e,t,n,i,r){ue(!0,e,t,n,i,r)}function he(e,t,n){return t&&!i.isArrayLike(t)&&(t=u.getLocalTransform(t)),n&&(t=a.invert([],t)),s.applyTransform([],e,t)}function de(e,t,n,i,r,o,a,s){var l,u=n-e,c=i-t,h=a-r,d=s-o,f=fe(h,d,u,c);if((l=f)<=1e-6&&l>=-1e-6)return!1;var p=e-r,g=t-o,m=fe(p,g,u,c)/f;if(m<0||m>1)return!1;var v=fe(p,g,h,d)/f;return!(v<0||v>1)}function fe(e,t,n,i){return e*i-n*t}B("circle",f),B("sector",p),B("ring",g),B("polygon",m),B("polyline",v),B("rect",y),B("line",b),B("bezierCurve",x),B("arc",_),t.Z2_EMPHASIS_LIFT=O,t.CACHED_LABEL_STYLE_PROPERTIES={color:"textFill",textBorderColor:"textStroke",textBorderWidth:"textStrokeWidth"},t.extendShape=function(e){return l.extend(e)},t.extendPath=function(e,t){return r.extendFromString(e,t)},t.registerShape=B,t.getShapeClass=function(e){if(R.hasOwnProperty(e))return R[e]},t.makePath=z,t.makeImage=function(e,t,n){var i=new c({style:{image:e,x:t.x,y:t.y,width:t.width,height:t.height},onload:function(e){if("center"===n){var r={width:e.width,height:e.height};i.setStyle(F(t,r))}}});return i},t.mergePath=$,t.resizePath=V,t.subPixelOptimizeLine=function(e){return M.subPixelOptimizeLine(e.shape,e.shape,e.style),e},t.subPixelOptimizeRect=function(e){return M.subPixelOptimizeRect(e.shape,e.shape,e.style),e},t.subPixelOptimize=j,t.setElementHoverStyle=K,t.setHoverStyle=function(e,t){ne(e,!0),X(e,K,t)},t.setAsHighDownDispatcher=ne,t.isHighDownDispatcher=function(e){return!(!e||!e.__highDownDispatcher)},t.getHighlightDigit=function(e){var t=N[e];return null==t&&P<=32&&(t=N[e]=P++),t},t.setLabelStyle=function(e,t,n,r,o,a,s){var l,u=(o=o||I).labelFetcher,c=o.labelDataIndex,h=o.labelDimIndex,d=o.labelProp,f=n.getShallow("show"),p=r.getShallow("show");(f||p)&&(u&&(l=u.getFormattedLabel(c,"normal",null,h,d)),null==l&&(l=i.isFunction(o.defaultText)?o.defaultText(c,o):o.defaultText));var g=f?l:null,m=p?i.retrieve2(u?u.getFormattedLabel(c,"emphasis",null,h,d):null,l):null;null==g&&null==m||(ie(e,n,a,o),ie(t,r,s,o,!0)),e.text=g,t.text=m},t.modifyLabelStyle=function(e,t,n){var r=e.style;t&&(le(r),e.setStyle(t),se(r)),r=e.__hoverStl,n&&r&&(le(r),i.extend(r,n),se(r))},t.setTextStyle=ie,t.setText=function(e,t,n){var i,r={isRectText:!0};!1===n?i=!0:r.autoColor=n,re(e,t,r,i)},t.getFont=function(e,t){var n=t&&t.getModel("textStyle");return i.trim([e.fontStyle||n&&n.getShallow("fontStyle")||"",e.fontWeight||n&&n.getShallow("fontWeight")||"",(e.fontSize||n&&n.getShallow("fontSize")||12)+"px",e.fontFamily||n&&n.getShallow("fontFamily")||"sans-serif"].join(" "))},t.updateProps=ce,t.initProps=function(e,t,n,i,r){ue(!1,e,t,n,i,r)},t.getTransform=function(e,t){for(var n=a.identity([]);e&&e!==t;)a.mul(n,e.getLocalTransform(),n),e=e.parent;return n},t.applyTransform=he,t.transformDirection=function(e,t,n){var i=0===t[4]||0===t[5]||0===t[0]?1:Math.abs(2*t[4]/t[0]),r=0===t[4]||0===t[5]||0===t[2]?1:Math.abs(2*t[4]/t[2]),o=["left"===e?-i:"right"===e?i:0,"top"===e?-r:"bottom"===e?r:0];return o=he(o,t,n),Math.abs(o[0])>Math.abs(o[1])?o[0]>0?"right":"left":o[1]>0?"bottom":"top"},t.groupTransition=function(e,t,n,r){if(e&&t){var o,a=(o={},e.traverse(function(e){!e.isGroup&&e.anid&&(o[e.anid]=e)}),o);t.traverse(function(e){if(!e.isGroup&&e.anid){var t=a[e.anid];if(t){var i=l(e);e.attr(l(t)),ce(e,i,n,e.dataIndex)}}})}function l(e){var t={position:s.clone(e.position),rotation:e.rotation};return e.shape&&(t.shape=i.extend({},e.shape)),t}},t.clipPointsByRect=function(e,t){return i.map(e,function(e){var n=e[0];n=D(n,t.x),n=A(n,t.x+t.width);var i=e[1];return i=D(i,t.y),[n,i=A(i,t.y+t.height)]})},t.clipRectByRect=function(e,t){var n=D(e.x,t.x),i=A(e.x+e.width,t.x+t.width),r=D(e.y,t.y),o=A(e.y+e.height,t.y+t.height);if(i>=n&&o>=r)return{x:n,y:r,width:i-n,height:o-r}},t.createIcon=function(e,t,n){var r=(t=i.extend({rectHover:!0},t)).style={strokeNoScale:!0};if(n=n||{x:-1,y:-1,width:2,height:2},e)return 0===e.indexOf("image://")?(r.image=e.slice(8),i.defaults(r,n),new c(t)):z(e.replace("path://",""),t,n,"center")},t.linePolygonIntersect=function(e,t,n,i,r){for(var o=0,a=r[r.length-1];o0&&(h?"scale"!==d:"transition"!==f)){for(var m=o.getItemLayout(0),v=1;isNaN(m.startAngle)&&v=n.r0}}});e.exports=c},"1DJE":function(e,t,n){var i=n("/gxq");var r=function(e){null!=e&&i.extend(this,e),this.otherDims={}};e.exports=r},"1FNb":function(e,t,n){var i=n("Icdr");n("z81E"),n("0nGg"),n("iZVd");var r=n("T6W2"),o=n("AjK0"),a=n("akwy"),s=n("TXKS"),l=n("4RQY"),u=n("NAKW"),c=n("pzOI"),h=n("KGuM");i.registerProcessor(r),i.registerVisual(o("graph","circle",null)),i.registerVisual(a),i.registerVisual(s),i.registerLayout(l),i.registerLayout(i.PRIORITY.VISUAL.POST_CHART_LAYOUT,u),i.registerLayout(c),i.registerCoordinateSystem("graphView",{create:h})},"1Hui":function(e,t){function n(e){return e}function i(e,t,i,r,o){this._old=e,this._new=t,this._oldKeyGetter=i||n,this._newKeyGetter=r||n,this.context=o}function r(e,t,n,i,r){for(var o=0;o0?"P":"N",o=i.getVisual("borderColor"+r)||i.getVisual("color"+r),a=n.getModel(l).getItemStyle(c);t.useStyle(a),t.style.fill=null,t.style.stroke=o}var b=h;e.exports=b},"1Xuh":function(e,t,n){var i=n("/gxq"),r=n("8b51"),o=n("wWR3").parsePercent,a=n("HHfb"),s=i.each,l=["left","right","top","bottom","width","height"],u=[["width","left","right"],["height","top","bottom"]];function c(e,t,n,i,r){var o=0,a=0;null==i&&(i=1/0),null==r&&(r=1/0);var s=0;t.eachChild(function(l,u){var c,h,d=l.position,f=l.getBoundingRect(),p=t.childAt(u+1),g=p&&p.getBoundingRect();if("horizontal"===e){var m=f.width+(g?-g.x+f.x:0);(c=o+m)>i||l.newline?(o=0,c=m,a+=s+n,s=f.height):s=Math.max(s,f.height)}else{var v=f.height+(g?-g.y+f.y:0);(h=a+v)>r||l.newline?(o+=s+n,a=0,h=v,s=f.width):s=Math.max(s,f.width)}l.newline||(d[0]=o,d[1]=a,"horizontal"===e?o=c+n:a=h+n)})}var h=c,d=i.curry(c,"vertical"),f=i.curry(c,"horizontal");function p(e,t,n){n=a.normalizeCssArray(n||0);var i=t.width,s=t.height,l=o(e.left,i),u=o(e.top,s),c=o(e.right,i),h=o(e.bottom,s),d=o(e.width,i),f=o(e.height,s),p=n[2]+n[0],g=n[1]+n[3],m=e.aspect;switch(isNaN(d)&&(d=i-c-g-l),isNaN(f)&&(f=s-h-p-u),null!=m&&(isNaN(d)&&isNaN(f)&&(m>i/s?d=.8*i:f=.8*s),isNaN(d)&&(d=m*f),isNaN(f)&&(f=d/m)),isNaN(l)&&(l=i-c-d-g),isNaN(u)&&(u=s-h-f-p),e.left||e.right){case"center":l=i/2-d/2-n[3];break;case"right":l=i-d-g}switch(e.top||e.bottom){case"middle":case"center":u=s/2-f/2-n[0];break;case"bottom":u=s-f-p}l=l||0,u=u||0,isNaN(d)&&(d=i-g-l-(c||0)),isNaN(f)&&(f=s-p-u-(h||0));var v=new r(l+n[3],u+n[0],d,f);return v.margin=n,v}function g(e,t){return t&&e&&s(l,function(n){t.hasOwnProperty(n)&&(e[n]=t[n])}),e}t.LOCATION_PARAMS=l,t.HV_NAMES=u,t.box=h,t.vbox=d,t.hbox=f,t.getAvailableSize=function(e,t,n){var i=t.width,r=t.height,s=o(e.x,i),l=o(e.y,r),u=o(e.x2,i),c=o(e.y2,r);return(isNaN(s)||isNaN(parseFloat(e.x)))&&(s=0),(isNaN(u)||isNaN(parseFloat(e.x2)))&&(u=i),(isNaN(l)||isNaN(parseFloat(e.y)))&&(l=0),(isNaN(c)||isNaN(parseFloat(e.y2)))&&(c=r),n=a.normalizeCssArray(n||0),{width:Math.max(u-s-n[1]-n[3],0),height:Math.max(c-l-n[0]-n[2],0)}},t.getLayoutRect=p,t.positionElement=function(e,t,n,o,a){var s=!a||!a.hv||a.hv[0],l=!a||!a.hv||a.hv[1],u=a&&a.boundingMode||"all";if(s||l){var c;if("raw"===u)c="group"===e.type?new r(0,0,+t.width||0,+t.height||0):e.getBoundingRect();else if(c=e.getBoundingRect(),e.needLocalTransform()){var h=e.getLocalTransform();(c=c.clone()).applyTransform(h)}t=p(i.defaults({width:c.width,height:c.height},t),n,o);var d=e.position,f=s?t.x-c.x:0,g=l?t.y-c.y:0;e.attr("position","raw"===u?[f,g]:[d[0]+f,d[1]+g])}},t.sizeCalculable=function(e,t){return null!=e[u[t][0]]||null!=e[u[t][1]]&&null!=e[u[t][2]]},t.mergeLayoutParam=function(e,t,n){!i.isObject(n)&&(n={});var r=n.ignoreSize;!i.isArray(r)&&(r=[r,r]);var o=l(u[0],0),a=l(u[1],1);function l(n,i){var o={},a=0,l={},u=0;if(s(n,function(t){l[t]=e[t]}),s(n,function(e){c(t,e)&&(o[e]=l[e]=t[e]),h(o,e)&&a++,h(l,e)&&u++}),r[i])return h(t,n[1])?l[n[2]]=null:h(t,n[2])&&(l[n[1]]=null),l;if(2!==u&&a){if(a>=2)return o;for(var d=0;d=0&&e.call(t,n[r],r)},s.eachEdge=function(e,t){for(var n=this.edges,i=n.length,r=0;r=0&&n[r].node1.dataIndex>=0&&n[r].node2.dataIndex>=0&&e.call(t,n[r],r)},s.breadthFirstTraverse=function(e,t,n,i){if(l.isInstance(t)||(t=this._nodesMap[o(t)]),t){for(var r="out"===n?"outEdges":"in"===n?"inEdges":"edges",a=0;a=0&&n.node2.dataIndex>=0});for(r=0,o=i.length;r=0&&this[e][t].setItemVisual(this.dataIndex,n,i)},getVisual:function(n,i){return this[e][t].getItemVisual(this.dataIndex,n,i)},setLayout:function(n,i){this.dataIndex>=0&&this[e][t].setItemLayout(this.dataIndex,n,i)},getLayout:function(){return this[e][t].getItemLayout(this.dataIndex)},getGraphicEl:function(){return this[e][t].getItemGraphicEl(this.dataIndex)},getRawIndex:function(){return this[e][t].getRawIndex(this.dataIndex)}}};i.mixin(l,c("hostGraph","data")),i.mixin(u,c("hostGraph","edgeData")),a.Node=l,a.Edge=u,r(l),r(u);var h=a;e.exports=h},"28kU":function(e,t){t.ContextCachedBy={NONE:0,STYLE_BIND:1,PLAIN_TEXT:2},t.WILL_BE_RESTORED=9},"2HcM":function(e,t,n){var i=n("/gxq"),r=i.each,o=i.map,a=n("wWR3"),s=a.linearMap,l=a.getPixelPrecision,u=a.round,c=n("SiPa"),h=c.createAxisTicks,d=c.createAxisLabels,f=c.calculateCategoryInterval,p=[0,1],g=function(e,t,n){this.dim=e,this.scale=t,this._extent=n||[0,0],this.inverse=!1,this.onBand=!1};function m(e,t){var n=(e[1]-e[0])/t/2;e[0]+=n,e[1]-=n}g.prototype={constructor:g,contain:function(e){var t=this._extent,n=Math.min(t[0],t[1]),i=Math.max(t[0],t[1]);return e>=n&&e<=i},containData:function(e){return this.scale.contain(e)},getExtent:function(){return this._extent.slice()},getPixelPrecision:function(e){return l(e||this.scale.getExtent(),this._extent)},setExtent:function(e,t){var n=this._extent;n[0]=e,n[1]=t},dataToCoord:function(e,t){var n=this._extent,i=this.scale;return e=i.normalize(e),this.onBand&&"ordinal"===i.type&&m(n=n.slice(),i.count()),s(e,p,n,t)},coordToData:function(e,t){var n=this._extent,i=this.scale;this.onBand&&"ordinal"===i.type&&m(n=n.slice(),i.count());var r=s(e,n,p,t);return this.scale.scale(r)},pointToData:function(e,t){},getTicksCoords:function(e){var t=(e=e||{}).tickModel||this.getTickModel(),n=h(this,t).ticks,i=o(n,function(e){return{coord:this.dataToCoord(e),tickValue:e}},this);return function(e,t,n,i){var o=t.length;if(!e.onBand||n||!o)return;var a,s,l=e.getExtent();if(1===o)t[0].coord=l[0],a=t[1]={coord:l[0]};else{var c=t[o-1].tickValue-t[0].tickValue,h=(t[o-1].coord-t[0].coord)/c;r(t,function(e){e.coord-=h/2});var d=e.scale.getExtent();s=1+d[1]-t[o-1].tickValue,a={coord:t[o-1].coord+h*s},t.push(a)}var f=l[0]>l[1];p(t[0].coord,l[0])&&(i?t[0].coord=l[0]:t.shift());i&&p(l[0],t[0].coord)&&t.unshift({coord:l[0]});p(l[1],a.coord)&&(i?a.coord=l[1]:t.pop());i&&p(a.coord,l[1])&&t.push({coord:l[1]});function p(e,t){return e=u(e),t=u(t),f?e>t:e0&&e<100||(e=5);var t=this.scale.getMinorTicks(e);return o(t,function(e){return o(e,function(e){return{coord:this.dataToCoord(e),tickValue:e}},this)},this)},getViewLabels:function(){return d(this).labels},getLabelModel:function(){return this.model.getModel("axisLabel")},getTickModel:function(){return this.model.getModel("axisTick")},getBandWidth:function(){var e=this._extent,t=this.scale.getExtent(),n=t[1]-t[0]+(this.onBand?1:0);0===n&&(n=1);var i=Math.abs(e[1]-e[0]);return Math.abs(i)/n},isHorizontal:null,getRotate:null,calculateCategoryInterval:function(){return f(this)}};var v=g;e.exports=v},"2I/p":function(e,t,n){var i=n("ABnm").normalizeRadian,r=2*Math.PI;t.containStroke=function(e,t,n,o,a,s,l,u,c){if(0===l)return!1;var h=l;u-=e,c-=t;var d=Math.sqrt(u*u+c*c);if(d-h>n||d+ha&&(a+=r);var p=Math.atan2(c,u);return p<0&&(p+=r),p>=o&&p<=a||p+r>=o&&p+r<=a}},"2M5Q":function(e,t,n){var i=n("moDv"),r=n("u+XU"),o=n("LICT"),a=n("oBGI"),s=n("2I/p"),l=n("ABnm").normalizeRadian,u=n("AAi1"),c=n("QxFU"),h=i.CMD,d=2*Math.PI,f=1e-4;var p=[-1,-1,-1],g=[-1,-1];function m(e,t,n,i,r,o,a,s,l,c){if(c>t&&c>i&&c>o&&c>s||c1&&(void 0,h=g[0],g[0]=g[1],g[1]=h),f=u.cubicAt(t,i,o,s,g[0]),y>1&&(m=u.cubicAt(t,i,o,s,g[1]))),2===y?xt&&s>i&&s>o||s=0&&c<=1){for(var h=0,d=u.quadraticAt(t,i,o,c),f=0;fn||s<-n)return 0;var u=Math.sqrt(n*n-s*s);p[0]=-u,p[1]=u;var c=Math.abs(i-r);if(c<1e-4)return 0;if(c%d<1e-4){i=0,r=d;var h=o?1:-1;return a>=p[0]+e&&a<=p[1]+e?h:0}if(o){u=i;i=l(r),r=l(u)}else i=l(i),r=l(r);i>r&&(r+=d);for(var f=0,g=0;g<2;g++){var m=p[g];if(m+e>a){var v=Math.atan2(s,m);h=o?1:-1;v<0&&(v=d+v),(v>=i&&v<=r||v+d>=i&&v+d<=r)&&(v>Math.PI/2&&v<1.5*Math.PI&&(h=-h),f+=h)}}return f}function b(e,t,n,i,l){for(var u,d,p=0,g=0,b=0,x=0,_=0,w=0;w1&&(n||(p+=c(g,b,x,_,i,l))),1===w&&(x=g=e[w],_=b=e[w+1]),C){case h.M:g=x=e[w++],b=_=e[w++];break;case h.L:if(n){if(r.containStroke(g,b,e[w],e[w+1],t,i,l))return!0}else p+=c(g,b,e[w],e[w+1],i,l)||0;g=e[w++],b=e[w++];break;case h.C:if(n){if(o.containStroke(g,b,e[w++],e[w++],e[w++],e[w++],e[w],e[w+1],t,i,l))return!0}else p+=m(g,b,e[w++],e[w++],e[w++],e[w++],e[w],e[w+1],i,l)||0;g=e[w++],b=e[w++];break;case h.Q:if(n){if(a.containStroke(g,b,e[w++],e[w++],e[w],e[w+1],t,i,l))return!0}else p+=v(g,b,e[w++],e[w++],e[w],e[w+1],i,l)||0;g=e[w++],b=e[w++];break;case h.A:var S=e[w++],k=e[w++],T=e[w++],M=e[w++],D=e[w++],A=e[w++];w+=1;var I=1-e[w++],O=Math.cos(D)*T+S,E=Math.sin(D)*M+k;w>1?p+=c(g,b,O,E,i,l):(x=O,_=E);var L=(i-S)*M/T+S;if(n){if(s.containStroke(S,k,M,D,D+A,I,t,L,l))return!0}else p+=y(S,k,M,D,D+A,I,L,l);g=Math.cos(D+A)*T+S,b=Math.sin(D+A)*M+k;break;case h.R:x=g=e[w++],_=b=e[w++];O=x+e[w++],E=_+e[w++];if(n){if(r.containStroke(x,_,O,_,t,i,l)||r.containStroke(O,_,O,E,t,i,l)||r.containStroke(O,E,x,E,t,i,l)||r.containStroke(x,E,x,_,t,i,l))return!0}else p+=c(O,_,O,E,i,l),p+=c(x,E,x,_,i,l);break;case h.Z:if(n){if(r.containStroke(g,b,x,_,t,i,l))return!0}else p+=c(g,b,x,_,i,l);g=x,b=_}}return n||(u=b,d=_,Math.abs(u-d)n-2?n-1:f+1],h=e[f>n-3?n-1:f+2]);var m=p*p,v=p*m;o.push([r(u[0],g[0],c[0],h[0],p,m,v),r(u[1],g[1],c[1],h[1],p,m,v)])}return o}},"2kvA":function(e,t,n){"use strict";t.__esModule=!0,t.isInContainer=t.getScrollContainer=t.isScroll=t.getStyle=t.once=t.off=t.on=void 0;var i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};t.hasClass=p,t.addClass=function(e,t){if(!e)return;for(var n=e.className,i=(t||"").split(" "),r=0,o=i.length;r-1}var g=t.getStyle=u<9?function(e,t){if(!a){if(!e||!t)return null;"float"===(t=h(t))&&(t="styleFloat");try{switch(t){case"opacity":try{return e.filters.item("alpha").opacity/100}catch(e){return 1}default:return e.style[t]||e.currentStyle?e.currentStyle[t]:null}}catch(n){return e.style[t]}}}:function(e,t){if(!a){if(!e||!t)return null;"float"===(t=h(t))&&(t="cssFloat");try{var n=document.defaultView.getComputedStyle(e,"");return e.style[t]||n?n[t]:null}catch(n){return e.style[t]}}};var m=t.isScroll=function(e,t){if(!a)return g(e,null!==t||void 0!==t?t?"overflow-y":"overflow-x":"overflow").match(/(scroll|auto)/)};t.getScrollContainer=function(e,t){if(!a){for(var n=e;n;){if([window,document,document.documentElement].includes(n))return window;if(m(n,t))return n;n=n.parentNode}return n}},t.isInContainer=function(e,t){if(a||!e||!t)return!1;var n=e.getBoundingClientRect(),i=void 0;return i=[window,document,document.documentElement,null,void 0].includes(t)?{top:0,right:window.innerWidth,bottom:window.innerHeight,left:0}:t.getBoundingClientRect(),n.topi.top&&n.right>i.left&&n.leftf&&(d=0,h={}),d++,h[n]=r,r}function y(e,t,n){return"right"===n?e-=t:"center"===n&&(e-=t/2),e}function b(e,t,n){return"middle"===n?e-=t/2:"bottom"===n&&(e-=t),e}function x(e,t,n){var i=t.textPosition,r=t.textDistance,o=n.x,a=n.y;r=r||0;var s=n.height,l=n.width,u=s/2,c="left",h="top";switch(i){case"left":o-=r,a+=u,c="right",h="middle";break;case"right":o+=r+l,a+=u,h="middle";break;case"top":o+=l/2,a-=r,c="center",h="bottom";break;case"bottom":o+=l/2,a+=s+r,c="center";break;case"inside":o+=l/2,a+=u,c="center",h="middle";break;case"insideLeft":o+=r,a+=u,h="middle";break;case"insideRight":o+=l-r,a+=u,c="right",h="middle";break;case"insideTop":o+=l/2,a+=r,c="center";break;case"insideBottom":o+=l/2,a+=s-r,c="center",h="bottom";break;case"insideTopLeft":o+=r,a+=r;break;case"insideTopRight":o+=l-r,a+=r,c="right";break;case"insideBottomLeft":o+=r,a+=s-r,h="bottom";break;case"insideBottomRight":o+=l-r,a+=s-r,c="right",h="bottom"}return(e=e||{}).x=o,e.y=a,e.textAlign=c,e.textVerticalAlign=h,e}function _(e,t,n,i,r){if(!t)return"";var o=(e+"").split("\n");r=w(t,n,i,r);for(var a=0,s=o.length;a=o;u++)a-=o;var c=v(n,t);return c>a&&(n="",c=0),a=e-c,i.ellipsis=n,i.ellipsisWidth=c,i.contentWidth=a,i.containerWidth=e,i}function C(e,t){var n=t.containerWidth,i=t.font,r=t.contentWidth;if(!n)return"";var o=v(e,i);if(o<=n)return e;for(var a=0;;a++){if(o<=r||a>=t.maxIterations){e+=t.ellipsis;break}var s=0===a?S(e,r,t.ascCharWidth,t.cnCharWidth):o>0?Math.floor(e.length*r/o):0;o=v(e=e.substr(0,s),i)}return""===e&&(e=t.placeholder),e}function S(e,t,n,i){for(var r=0,o=0,a=e.length;oh)e="",a=[];else if(null!=d)for(var f=w(d-(n?n[1]+n[3]:0),t,r.ellipsis,{minChar:r.minChar,placeholder:r.placeholder}),p=0,g=a.length;po&&A(n,e.substring(o,a)),A(n,i[2],i[1]),o=p.lastIndex}oy)return{lines:[],width:0,height:0};B.textWidth=v(B.text,D);var O=T.textWidth,E=null==O||"auto"===O;if("string"==typeof O&&"%"===O.charAt(O.length-1))B.percentWidth=O,d.push(B),O=0;else{if(E){O=B.textWidth;var L=T.textBackgroundColor,P=L&&L.image;P&&(P=r.findExistImage(P),r.isImageReady(P)&&(O=Math.max(O,P.width*I/P.height)))}var N=M?M[1]+M[3]:0;O+=N;var R=null!=m?m-C:null;null!=R&&R0&&d>0&&!p&&(l=0),l<0&&d<0&&!g&&(d=0));var m=t.ecModel;if(m&&"time"===a){var v,y=u("bar",m);if(i.each(y,function(e){v|=e.getBaseAxis()===t.axis}),v){var b=c(y),x=function(e,t,n,r){var o=n.axis.getExtent(),a=o[1]-o[0],s=h(r,n.axis);if(void 0===s)return{min:e,max:t};var l=1/0;i.each(s,function(e){l=Math.min(e.offset,l)});var u=-1/0;i.each(s,function(e){u=Math.max(e.offset+e.width,u)}),l=Math.abs(l),u=Math.abs(u);var c=l+u,d=t-e,f=d/(1-(l+u)/a)-d;return{min:e-=f*(l/c),max:t+=f*(u/c)}}(l,d,t,b);l=x.min,d=x.max}}return{extent:[l,d],fixMin:p,fixMax:g}}function p(e){var t,n=e.getLabelModel().get("formatter"),i="category"===e.type?e.scale.getExtent()[0]:null;return"string"==typeof n?(t=n,n=function(n){return n=e.scale.getLabel(n),t.replace("{value}",null!=n?n:"")}):"function"==typeof n?function(t,r){return null!=i&&(r=t-i),n(g(e,t),r)}:function(t){return e.scale.getLabel(t)}}function g(e,t){return"category"===e.type?e.scale.getLabel(t):t}function m(e){var t=e.get("interval");return null==t?"auto":t}n("dDRy"),n("xCbH"),t.getScaleExtent=f,t.niceScaleExtent=function(e,t){var n=f(e,t),i=n.extent,r=t.get("splitNumber");"log"===e.type&&(e.base=t.get("logBase"));var o=e.type;e.setExtent(i[0],i[1]),e.niceExtent({splitNumber:r,fixMin:n.fixMin,fixMax:n.fixMax,minInterval:"interval"===o||"time"===o?t.get("minInterval"):null,maxInterval:"interval"===o||"time"===o?t.get("maxInterval"):null});var a=t.get("interval");null!=a&&e.setInterval&&e.setInterval(a)},t.createScaleByModel=function(e,t){if(t=t||e.get("type"))switch(t){case"category":return new r(e.getOrdinalMeta?e.getOrdinalMeta():e.getCategories(),[1/0,-1/0]);case"value":return new o;default:return(a.getClass(t)||o).create(e)}},t.ifAxisCrossZero=function(e){var t=e.scale.getExtent(),n=t[0],i=t[1];return!(n>0&&i>0||n<0&&i<0)},t.makeLabelFormatter=p,t.getAxisRawValue=g,t.estimateLabelUnionRect=function(e){var t=e.model,n=e.scale;if(t.get("axisLabel.show")&&!n.isBlank()){var i,r,o="category"===e.type,a=n.getExtent();r=o?n.count():(i=n.getTicks()).length;var s,l,u,c,h,f,g,m,v,y=e.getLabelModel(),b=p(e),x=1;r>40&&(x=Math.ceil(r/40));for(var _=0;_0&&t.animate(n,!1).when(null==o?500:o,c).delay(a||0)}(e,"",e,t,n,i,h);var d=e.animators.slice(),p=d.length;function g(){--p||o&&o()}p||o&&o();for(var m=0;m0,S=v.height-(C?-1:1),k=(f-d)/(S||1),T=e.get("clockwise"),M=e.get("stillShowZeroSum"),D=T?1:-1,A=function(e,t){if(e){var n=t;if(e!==m){var o=e.getValue(),a=0===_&&M?w:o*w;a-b}function w(e,t){t&&C(e,"transform","matrix("+h.call(t,",")+")")}function C(e,t,n){(!n||"linear"!==n.type&&"radial"!==n.type)&&e.setAttribute(t,n)}function S(e,t,n,i){if(function(e,t){var n=t?e.textFill:e.fill;return null!=n&&n!==d}(t,n)){var r=n?t.textFill:t.fill;C(e,"fill",r="transparent"===r?d:r),C(e,"fill-opacity",null!=t.fillOpacity?t.fillOpacity*t.opacity:t.opacity)}else C(e,"fill",d);if(function(e,t){var n=t?e.textStroke:e.stroke;return null!=n&&n!==d}(t,n)){var o=n?t.textStroke:t.stroke;C(e,"stroke",o="transparent"===o?d:o),C(e,"stroke-width",(n?t.textStrokeWidth:t.lineWidth)/(!n&&t.strokeNoScale?i.getLineScale():1)),C(e,"paint-order",n?"stroke":"fill"),C(e,"stroke-opacity",null!=t.strokeOpacity?t.strokeOpacity:t.opacity),t.lineDash?(C(e,"stroke-dasharray",t.lineDash.join(",")),C(e,"stroke-dashoffset",f(t.lineDashOffset||0))):C(e,"stroke-dasharray",""),t.lineCap&&C(e,"stroke-linecap",t.lineCap),t.lineJoin&&C(e,"stroke-linejoin",t.lineJoin),t.miterLimit&&C(e,"stroke-miterlimit",t.miterLimit)}else C(e,"stroke",d)}var k={};k.brush=function(e){var t=e.style,n=e.__svgEl;n||(n=i("path"),e.__svgEl=n),e.path||e.createPathProxy();var r=e.path;if(e.__dirtyPath){r.beginPath(),r.subPixelOptimize=!1,e.buildPath(r,e.shape),e.__dirtyPath=!1;var o=function(e){for(var t=[],n=e.data,i=e.len(),r=0;r=v:-b>=v),T=b>0?b%v:b%v+v,M=!1;M=!!k||!_(S)&&T>=m==!!C;var D=x(s+u*g(d)),A=x(l+h*p(d));k&&(b=C?v-1e-4:1e-4-v,M=!0,9===r&&t.push("M",D,A));var I=x(s+u*g(d+b)),O=x(l+h*p(d+b));t.push("A",x(u),x(h),f(w*y),+M,+C,I,O);break;case c.Z:o="Z";break;case c.R:I=x(n[r++]),O=x(n[r++]);var E=x(n[r++]),L=x(n[r++]);t.push("M",I,O,"L",I+E,O,"L",I+E,O+L,"L",I,O+L,"L",I,O)}o&&t.push(o);for(var P=0;PB){for(;N=0)&&n({type:"updateAxisPointer",currTrigger:e,x:t&&t.offsetX,y:t&&t.offsetY})})},remove:function(e,t){r.unregister(t.getZr(),"axisPointer"),o.superApply(this._model,"remove",arguments)},dispose:function(e,t){r.unregister("axisPointer",t),o.superApply(this._model,"dispose",arguments)}}),a=o;e.exports=a},"5Hn/":function(e,t,n){var i=n("/gxq"),r=n("UAiw"),o=n("0sHC"),a=n("QD+P"),s=n("ilLo"),l=n("wWR3"),u=n("1Xuh"),c=n("og9+"),h=o.Rect,d=l.linearMap,f=l.asc,p=i.bind,g=i.each,m="horizontal",v=5,y=["line","bar","candlestick","scatter"],b=s.extend({type:"dataZoom.slider",init:function(e,t){this._displayables={},this._orient,this._range,this._handleEnds,this._size,this._handleWidth,this._handleHeight,this._location,this._dragging,this._dataShadowInfo,this.api=t},render:function(e,t,n,i){b.superApply(this,"render",arguments),a.createOrUpdate(this,"_dispatchZoomAction",this.dataZoomModel.get("throttle"),"fixRate"),this._orient=e.get("orient"),!1!==this.dataZoomModel.get("show")?(i&&"dataZoom"===i.type&&i.from===this.uid||this._buildView(),this._updateView()):this.group.removeAll()},remove:function(){b.superApply(this,"remove",arguments),a.clear(this,"_dispatchZoomAction")},dispose:function(){b.superApply(this,"dispose",arguments),a.clear(this,"_dispatchZoomAction")},_buildView:function(){var e=this.group;e.removeAll(),this._resetLocation(),this._resetInterval();var t=this._displayables.barGroup=new o.Group;this._renderBackground(),this._renderHandle(),this._renderDataShadow(),e.add(t),this._positionGroup()},_resetLocation:function(){var e=this.dataZoomModel,t=this.api,n=this._findCoordRect(),r={width:t.getWidth(),height:t.getHeight()},o=this._orient===m?{right:r.width-n.x-n.width,top:r.height-30-7,width:n.width,height:30}:{right:7,top:n.y,width:30,height:n.height},a=u.getLayoutParams(e.option);i.each(["right","top","width","height"],function(e){"ph"===a[e]&&(a[e]=o[e])});var s=u.getLayoutRect(a,r,e.padding);this._location={x:s.x,y:s.y},this._size=[s.width,s.height],"vertical"===this._orient&&this._size.reverse()},_positionGroup:function(){var e=this.group,t=this._location,n=this._orient,i=this.dataZoomModel.getFirstTargetAxisModel(),r=i&&i.get("inverse"),o=this._displayables.barGroup,a=(this._dataShadowInfo||{}).otherAxisInverse;o.attr(n!==m||r?n===m&&r?{scale:a?[-1,1]:[-1,-1]}:"vertical"!==n||r?{scale:a?[-1,-1]:[-1,1],rotation:Math.PI/2}:{scale:a?[1,-1]:[1,1],rotation:Math.PI/2}:{scale:a?[1,1]:[1,-1]});var s=e.getBoundingRect([o]);e.attr("position",[t.x-s.x,t.y-s.y])},_getViewExtent:function(){return[0,this._size[0]]},_renderBackground:function(){var e=this.dataZoomModel,t=this._size,n=this._displayables.barGroup;n.add(new h({silent:!0,shape:{x:0,y:0,width:t[0],height:t[1]},style:{fill:e.get("backgroundColor")},z2:-40})),n.add(new h({shape:{x:0,y:0,width:t[0],height:t[1]},style:{fill:"transparent"},z2:0,onclick:i.bind(this._onClickPanelClick,this)}))},_renderDataShadow:function(){var e=this._dataShadowInfo=this._prepareDataShadowInfo();if(e){var t=this._size,n=e.series,r=n.getRawData(),a=n.getShadowDim?n.getShadowDim():e.otherDim;if(null!=a){var s=r.getDataExtent(a),l=.3*(s[1]-s[0]);s=[s[0]-l,s[1]+l];var u,c=[0,t[1]],h=[0,t[0]],f=[[t[0],0],[0,0]],p=[],g=h[1]/(r.count()-1),m=0,v=Math.round(r.count()/t[0]);r.each([a],function(e,t){if(v>0&&t%v)m+=g;else{var n=null==e||isNaN(e)||""===e,i=n?0:d(e,s,c,!0);n&&!u&&t?(f.push([f[f.length-1][0],0]),p.push([p[p.length-1][0],0])):!n&&u&&(f.push([m,0]),p.push([m,0])),f.push([m,i]),p.push([m,i]),m+=g,u=n}});var y=this.dataZoomModel;this._displayables.barGroup.add(new o.Polygon({shape:{points:f},style:i.defaults({fill:y.get("dataBackgroundColor")},y.getModel("dataBackground.areaStyle").getAreaStyle()),silent:!0,z2:-20})),this._displayables.barGroup.add(new o.Polyline({shape:{points:p},style:y.getModel("dataBackground.lineStyle").getLineStyle(),silent:!0,z2:-19}))}}},_prepareDataShadowInfo:function(){var e=this.dataZoomModel,t=e.get("showDataShadow");if(!1!==t){var n,r=this.ecModel;return e.eachTargetAxis(function(o,a){var s=e.getAxisProxy(o.name,a).getTargetSeriesModels();i.each(s,function(e){if(!(n||!0!==t&&i.indexOf(y,e.get("type"))<0)){var s,l=r.getComponent(o.axis,a).axis,u={x:"y",y:"x",radius:"angle",angle:"radius"}[o.name],c=e.coordinateSystem;null!=u&&c.getOtherAxis&&(s=c.getOtherAxis(l).inverse),u=e.getData().mapDimension(u),n={thisAxis:l,series:e,thisDim:o.name,otherDim:u,otherAxisInverse:s}}},this)},this),n}},_renderHandle:function(){var e=this._displayables,t=e.handles=[],n=e.handleLabels=[],i=this._displayables.barGroup,r=this._size,a=this.dataZoomModel;i.add(e.filler=new h({draggable:!0,cursor:x(this._orient),drift:p(this._onDragMove,this,"all"),ondragstart:p(this._showDataInfo,this,!0),ondragend:p(this._onDragEnd,this),onmouseover:p(this._showDataInfo,this,!0),onmouseout:p(this._showDataInfo,this,!1),style:{fill:a.get("fillerColor"),textPosition:"inside"}})),i.add(new h({silent:!0,subPixelOptimize:!0,shape:{x:0,y:0,width:r[0],height:r[1]},style:{stroke:a.get("dataBackgroundColor")||a.get("borderColor"),lineWidth:1,fill:"rgba(0,0,0,0)"}})),g([0,1],function(e){var r=o.createIcon(a.get("handleIcon"),{cursor:x(this._orient),draggable:!0,drift:p(this._onDragMove,this,e),ondragend:p(this._onDragEnd,this),onmouseover:p(this._showDataInfo,this,!0),onmouseout:p(this._showDataInfo,this,!1)},{x:-1,y:0,width:2,height:2}),s=r.getBoundingRect();this._handleHeight=l.parsePercent(a.get("handleSize"),this._size[1]),this._handleWidth=s.width/s.height*this._handleHeight,r.setStyle(a.getModel("handleStyle").getItemStyle());var u=a.get("handleColor");null!=u&&(r.style.fill=u),i.add(t[e]=r);var c=a.textStyleModel;this.group.add(n[e]=new o.Text({silent:!0,invisible:!0,style:{x:0,y:0,text:"",textVerticalAlign:"middle",textAlign:"center",textFill:c.getTextColor(),textFont:c.getFont()},z2:10}))},this)},_resetInterval:function(){var e=this._range=this.dataZoomModel.getPercentRange(),t=this._getViewExtent();this._handleEnds=[d(e[0],[0,100],t,!0),d(e[1],[0,100],t,!0)]},_updateInterval:function(e,t){var n=this.dataZoomModel,i=this._handleEnds,r=this._getViewExtent(),o=n.findRepresentativeAxisProxy().getMinMaxSpan(),a=[0,100];c(t,i,r,n.get("zoomLock")?"all":e,null!=o.minSpan?d(o.minSpan,a,r,!0):null,null!=o.maxSpan?d(o.maxSpan,a,r,!0):null);var s=this._range,l=this._range=f([d(i[0],r,a,!0),d(i[1],r,a,!0)]);return!s||s[0]!==l[0]||s[1]!==l[1]},_updateView:function(e){var t=this._displayables,n=this._handleEnds,i=f(n.slice()),r=this._size;g([0,1],function(e){var i=t.handles[e],o=this._handleHeight;i.attr({scale:[o/2,o/2],position:[n[e],r[1]/2-o/2]})},this),t.filler.setShape({x:i[0],y:0,width:i[1]-i[0],height:r[1]}),this._updateDataInfo(e)},_updateDataInfo:function(e){var t=this.dataZoomModel,n=this._displayables,i=n.handleLabels,r=this._orient,a=["",""];if(t.get("showDetail")){var s=t.findRepresentativeAxisProxy();if(s){var l=s.getAxisModel().axis,u=this._range,c=e?s.calculateDataWindow({start:u[0],end:u[1]}).valueWindow:s.getDataValueWindow();a=[this._formatLabel(c[0],l),this._formatLabel(c[1],l)]}}var h=f(this._handleEnds.slice());function d(e){var t=o.getTransform(n.handles[e].parent,this.group),s=o.transformDirection(0===e?"right":"left",t),l=this._handleWidth/2+v,u=o.applyTransform([h[e]+(0===e?-l:l),this._size[1]/2],t);i[e].setStyle({x:u[0],y:u[1],textVerticalAlign:r===m?"middle":s,textAlign:r===m?s:"center",text:a[e]})}d.call(this,0),d.call(this,1)},_formatLabel:function(e,t){var n=this.dataZoomModel,r=n.get("labelFormatter"),o=n.get("labelPrecision");null!=o&&"auto"!==o||(o=t.getPixelPrecision());var a=null==e||isNaN(e)?"":"category"===t.type||"time"===t.type?t.scale.getLabel(Math.round(e)):e.toFixed(Math.min(o,20));return i.isFunction(r)?r(e,a):i.isString(r)?r.replace("{value}",a):a},_showDataInfo:function(e){e=this._dragging||e;var t=this._displayables.handleLabels;t[0].attr("invisible",!e),t[1].attr("invisible",!e)},_onDragMove:function(e,t,n,i){this._dragging=!0,r.stop(i.event);var a=this._displayables.barGroup.getLocalTransform(),s=o.applyTransform([t,n],a,!0),l=this._updateInterval(e,s[0]),u=this.dataZoomModel.get("realtime");this._updateView(!u),l&&u&&this._dispatchZoomAction()},_onDragEnd:function(){this._dragging=!1,this._showDataInfo(!1),!this.dataZoomModel.get("realtime")&&this._dispatchZoomAction()},_onClickPanelClick:function(e){var t=this._size,n=this._displayables.barGroup.transformCoordToLocal(e.offsetX,e.offsetY);if(!(n[0]<0||n[0]>t[0]||n[1]<0||n[1]>t[1])){var i=this._handleEnds,r=(i[0]+i[1])/2,o=this._updateInterval("all",n[0]-r);this._updateView(),o&&this._dispatchZoomAction()}},_dispatchZoomAction:function(){var e=this._range;this.api.dispatchAction({type:"dataZoom",from:this.uid,dataZoomId:this.dataZoomModel.id,start:e[0],end:e[1]})},_findCoordRect:function(){var e;if(g(this.getTargetCoordInfo(),function(t){if(!e&&t.length){var n=t[0].model.coordinateSystem;e=n.getRect&&n.getRect()}}),!e){var t=this.api.getWidth(),n=this.api.getHeight();e={x:.2*t,y:.2*n,width:.6*t,height:.6*n}}return e}});function x(e){return"vertical"===e?"ns-resize":"ew-resize"}var _=b;e.exports=_},"5IAE":function(e,t,n){(function(e){"use strict";e.defineMode("javascript",function(t,n){var i,r,o=t.indentUnit,a=n.statementIndent,s=n.jsonld,l=n.json||s,u=n.typescript,c=n.wordCharacters||/[\w$\xa1-\uffff]/,h=function(){function e(e){return{type:e,style:"keyword"}}var t=e("keyword a"),n=e("keyword b"),i=e("keyword c"),r=e("keyword d"),o=e("operator"),a={type:"atom",style:"atom"};return{if:e("if"),while:t,with:t,else:n,do:n,try:n,finally:n,return:r,break:r,continue:r,new:e("new"),delete:i,void:i,throw:i,debugger:e("debugger"),var:e("var"),const:e("var"),let:e("var"),function:e("function"),catch:e("catch"),for:e("for"),switch:e("switch"),case:e("case"),default:e("default"),in:o,typeof:o,instanceof:o,true:a,false:a,null:a,undefined:a,NaN:a,Infinity:a,this:e("this"),class:e("class"),super:e("atom"),yield:i,export:e("export"),import:e("import"),extends:i,await:i}}(),d=/[+\-*&%=<>!?|~^@]/,f=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;function p(e,t,n){return i=e,r=n,t}function g(e,t){var n,i=e.next();if('"'==i||"'"==i)return t.tokenize=(n=i,function(e,t){var i,r=!1;if(s&&"@"==e.peek()&&e.match(f))return t.tokenize=g,p("jsonld-keyword","meta");for(;null!=(i=e.next())&&(i!=n||r);)r=!r&&"\\"==i;return r||(t.tokenize=g),p("string","string")}),t.tokenize(e,t);if("."==i&&e.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/))return p("number","number");if("."==i&&e.match(".."))return p("spread","meta");if(/[\[\]{}\(\),;\:\.]/.test(i))return p(i);if("="==i&&e.eat(">"))return p("=>","operator");if("0"==i&&e.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/))return p("number","number");if(/\d/.test(i))return e.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/),p("number","number");if("/"==i)return e.eat("*")?(t.tokenize=m,m(e,t)):e.eat("/")?(e.skipToEnd(),p("comment","comment")):Xe(e,t,1)?(function(e){for(var t,n=!1,i=!1;null!=(t=e.next());){if(!n){if("/"==t&&!i)return;"["==t?i=!0:i&&"]"==t&&(i=!1)}n=!n&&"\\"==t}}(e),e.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/),p("regexp","string-2")):(e.eat("="),p("operator","operator",e.current()));if("`"==i)return t.tokenize=v,v(e,t);if("#"==i&&"!"==e.peek())return e.skipToEnd(),p("meta","meta");if("#"==i&&e.eatWhile(c))return p("variable","property");if("<"==i&&e.match("!--")||"-"==i&&e.match("->")&&!/\S/.test(e.string.slice(0,e.start)))return e.skipToEnd(),p("comment","comment");if(d.test(i))return">"==i&&t.lexical&&">"==t.lexical.type||(e.eat("=")?"!"!=i&&"="!=i||e.eat("="):/[<>*+\-|&?]/.test(i)&&(e.eat(i),">"==i&&e.eat(i))),"?"==i&&e.eat(".")?p("."):p("operator","operator",e.current());if(c.test(i)){e.eatWhile(c);var r=e.current();if("."!=t.lastType){if(h.propertyIsEnumerable(r)){var o=h[r];return p(o.type,o.style,r)}if("async"==r&&e.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/,!1))return p("async","keyword",r)}return p("variable","variable",r)}}function m(e,t){for(var n,i=!1;n=e.next();){if("/"==n&&i){t.tokenize=g;break}i="*"==n}return p("comment","comment")}function v(e,t){for(var n,i=!1;null!=(n=e.next());){if(!i&&("`"==n||"$"==n&&e.eat("{"))){t.tokenize=g;break}i=!i&&"\\"==n}return p("quasi","string-2",e.current())}var y="([{}])";function b(e,t){t.fatArrowAt&&(t.fatArrowAt=null);var n=e.string.indexOf("=>",e.start);if(!(n<0)){if(u){var i=/:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(e.string.slice(e.start,n));i&&(n=i.index)}for(var r=0,o=!1,a=n-1;a>=0;--a){var s=e.string.charAt(a),l=y.indexOf(s);if(l>=0&&l<3){if(!r){++a;break}if(0==--r){"("==s&&(o=!0);break}}else if(l>=3&&l<6)++r;else if(c.test(s))o=!0;else if(/["'\/`]/.test(s))for(;;--a){if(0==a)return;if(e.string.charAt(a-1)==s&&"\\"!=e.string.charAt(a-2)){a--;break}}else if(o&&!r){++a;break}}o&&!r&&(t.fatArrowAt=a)}}var x={atom:!0,number:!0,variable:!0,string:!0,regexp:!0,this:!0,"jsonld-keyword":!0};function _(e,t,n,i,r,o){this.indented=e,this.column=t,this.type=n,this.prev=r,this.info=o,null!=i&&(this.align=i)}function w(e,t){for(var n=e.localVars;n;n=n.next)if(n.name==t)return!0;for(var i=e.context;i;i=i.prev)for(n=i.vars;n;n=n.next)if(n.name==t)return!0}var C={state:null,column:null,marked:null,cc:null};function S(){for(var e=arguments.length-1;e>=0;e--)C.cc.push(arguments[e])}function k(){return S.apply(null,arguments),!0}function T(e,t){for(var n=t;n;n=n.next)if(n.name==e)return!0;return!1}function M(e){var t=C.state;if(C.marked="def",t.context)if("var"==t.lexical.info&&t.context&&t.context.block){var i=function e(t,n){if(n){if(n.block){var i=e(t,n.prev);return i?i==n.prev?n:new A(i,n.vars,!0):null}return T(t,n.vars)?n:new A(n.prev,new I(t,n.vars),!1)}return null}(e,t.context);if(null!=i)return void(t.context=i)}else if(!T(e,t.localVars))return void(t.localVars=new I(e,t.localVars));n.globalVars&&!T(e,t.globalVars)&&(t.globalVars=new I(e,t.globalVars))}function D(e){return"public"==e||"private"==e||"protected"==e||"abstract"==e||"readonly"==e}function A(e,t,n){this.prev=e,this.vars=t,this.block=n}function I(e,t){this.name=e,this.next=t}var O=new I("this",new I("arguments",null));function E(){C.state.context=new A(C.state.context,C.state.localVars,!1),C.state.localVars=O}function L(){C.state.context=new A(C.state.context,C.state.localVars,!0),C.state.localVars=null}function P(){C.state.localVars=C.state.context.vars,C.state.context=C.state.context.prev}function N(e,t){var n=function(){var n=C.state,i=n.indented;if("stat"==n.lexical.type)i=n.lexical.indented;else for(var r=n.lexical;r&&")"==r.type&&r.align;r=r.prev)i=r.indented;n.lexical=new _(i,C.stream.column(),e,null,n.lexical,t)};return n.lex=!0,n}function R(){var e=C.state;e.lexical.prev&&(")"==e.lexical.type&&(e.indented=e.lexical.indented),e.lexical=e.lexical.prev)}function B(e){return function t(n){return n==e?k():";"==e||"}"==n||")"==n||"]"==n?S():k(t)}}function z(e,t){return"var"==e?k(N("vardef",t),xe,B(";"),R):"keyword a"==e?k(N("form"),j,z,R):"keyword b"==e?k(N("form"),z,R):"keyword d"==e?C.stream.match(/^\s*$/,!1)?k():k(N("stat"),W,B(";"),R):"debugger"==e?k(B(";")):"{"==e?k(N("}"),L,ae,R,P):";"==e?k():"if"==e?("else"==C.state.lexical.info&&C.state.cc[C.state.cc.length-1]==R&&C.state.cc.pop()(),k(N("form"),j,z,R,Te)):"function"==e?k(Ie):"for"==e?k(N("form"),Me,z,R):"class"==e||u&&"interface"==t?(C.marked="keyword",k(N("form","class"==e?e:t),Ne,R)):"variable"==e?u&&"declare"==t?(C.marked="keyword",k(z)):u&&("module"==t||"enum"==t||"type"==t)&&C.stream.match(/^\s*\w/,!1)?(C.marked="keyword","enum"==t?k(Ue):"type"==t?k(Ee,B("operator"),he,B(";")):k(N("form"),_e,B("{"),N("}"),ae,R,R)):u&&"namespace"==t?(C.marked="keyword",k(N("form"),$,z,R)):u&&"abstract"==t?(C.marked="keyword",k(z)):k(N("stat"),Q):"switch"==e?k(N("form"),j,B("{"),N("}","switch"),L,ae,R,R,P):"case"==e?k($,B(":")):"default"==e?k(B(":")):"catch"==e?k(N("form"),E,F,z,R,P):"export"==e?k(N("stat"),Fe,R):"import"==e?k(N("stat"),Ve,R):"async"==e?k(z):"@"==t?k($,z):S(N("stat"),$,B(";"),R)}function F(e){if("("==e)return k(Le,B(")"))}function $(e,t){return H(e,t,!1)}function V(e,t){return H(e,t,!0)}function j(e){return"("!=e?S():k(N(")"),W,B(")"),R)}function H(e,t,n){if(C.state.fatArrowAt==C.stream.start){var i=n?K:X;if("("==e)return k(E,N(")"),re(Le,")"),R,B("=>"),i,P);if("variable"==e)return S(E,_e,B("=>"),i,P)}var r=n?G:q;return x.hasOwnProperty(e)?k(r):"function"==e?k(Ie,r):"class"==e||u&&"interface"==t?(C.marked="keyword",k(N("form"),Pe,R)):"keyword c"==e||"async"==e?k(n?V:$):"("==e?k(N(")"),W,B(")"),R,r):"operator"==e||"spread"==e?k(n?V:$):"["==e?k(N("]"),Ge,R,r):"{"==e?oe(te,"}",null,r):"quasi"==e?S(U,r):"new"==e?k(function(e){return function(t){return"."==t?k(e?J:Z):"variable"==t&&u?k(ve,e?G:q):S(e?V:$)}}(n)):"import"==e?k($):k()}function W(e){return e.match(/[;\}\)\],]/)?S():S($)}function q(e,t){return","==e?k(W):G(e,t,!1)}function G(e,t,n){var i=0==n?q:G,r=0==n?$:V;return"=>"==e?k(E,n?K:X,P):"operator"==e?/\+\+|--/.test(t)||u&&"!"==t?k(i):u&&"<"==t&&C.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/,!1)?k(N(">"),re(he,">"),R,i):"?"==t?k($,B(":"),r):k(r):"quasi"==e?S(U,i):";"!=e?"("==e?oe(V,")","call",i):"."==e?k(ee,i):"["==e?k(N("]"),W,B("]"),R,i):u&&"as"==t?(C.marked="keyword",k(he,i)):"regexp"==e?(C.state.lastType=C.marked="operator",C.stream.backUp(C.stream.pos-C.stream.start-1),k(r)):void 0:void 0}function U(e,t){return"quasi"!=e?S():"${"!=t.slice(t.length-2)?k(U):k($,Y)}function Y(e){if("}"==e)return C.marked="string-2",C.state.tokenize=v,k(U)}function X(e){return b(C.stream,C.state),S("{"==e?z:$)}function K(e){return b(C.stream,C.state),S("{"==e?z:V)}function Z(e,t){if("target"==t)return C.marked="keyword",k(q)}function J(e,t){if("target"==t)return C.marked="keyword",k(G)}function Q(e){return":"==e?k(R,z):S(q,B(";"),R)}function ee(e){if("variable"==e)return C.marked="property",k()}function te(e,t){if("async"==e)return C.marked="property",k(te);if("variable"==e||"keyword"==C.style){return C.marked="property","get"==t||"set"==t?k(ne):(u&&C.state.fatArrowAt==C.stream.start&&(n=C.stream.match(/^\s*:\s*/,!1))&&(C.state.fatArrowAt=C.stream.pos+n[0].length),k(ie));var n}else{if("number"==e||"string"==e)return C.marked=s?"property":C.style+" property",k(ie);if("jsonld-keyword"==e)return k(ie);if(u&&D(t))return C.marked="keyword",k(te);if("["==e)return k($,se,B("]"),ie);if("spread"==e)return k(V,ie);if("*"==t)return C.marked="keyword",k(te);if(":"==e)return S(ie)}}function ne(e){return"variable"!=e?S(ie):(C.marked="property",k(Ie))}function ie(e){return":"==e?k(V):"("==e?S(Ie):void 0}function re(e,t,n){function i(r,o){if(n?n.indexOf(r)>-1:","==r){var a=C.state.lexical;return"call"==a.info&&(a.pos=(a.pos||0)+1),k(function(n,i){return n==t||i==t?S():S(e)},i)}return r==t||o==t?k():n&&n.indexOf(";")>-1?S(e):k(B(t))}return function(n,r){return n==t||r==t?k():S(e,i)}}function oe(e,t,n){for(var i=3;i"),he):void 0}function de(e){if("=>"==e)return k(he)}function fe(e){return e.match(/[\}\)\]]/)?k():","==e||";"==e?k(fe):S(pe,fe)}function pe(e,t){return"variable"==e||"keyword"==C.style?(C.marked="property",k(pe)):"?"==t||"number"==e||"string"==e?k(pe):":"==e?k(he):"["==e?k(B("variable"),le,B("]"),pe):"("==e?S(Oe,pe):e.match(/[;\}\)\],]/)?void 0:k()}function ge(e,t){return"variable"==e&&C.stream.match(/^\s*[?:]/,!1)||"?"==t?k(ge):":"==e?k(he):"spread"==e?k(ge):S(he)}function me(e,t){return"<"==t?k(N(">"),re(he,">"),R,me):"|"==t||"."==e||"&"==t?k(he):"["==e?k(he,B("]"),me):"extends"==t||"implements"==t?(C.marked="keyword",k(he)):"?"==t?k(he,B(":"),he):void 0}function ve(e,t){if("<"==t)return k(N(">"),re(he,">"),R,me)}function ye(){return S(he,be)}function be(e,t){if("="==t)return k(he)}function xe(e,t){return"enum"==t?(C.marked="keyword",k(Ue)):S(_e,se,Se,ke)}function _e(e,t){return u&&D(t)?(C.marked="keyword",k(_e)):"variable"==e?(M(t),k()):"spread"==e?k(_e):"["==e?oe(Ce,"]"):"{"==e?oe(we,"}"):void 0}function we(e,t){return"variable"!=e||C.stream.match(/^\s*:/,!1)?("variable"==e&&(C.marked="property"),"spread"==e?k(_e):"}"==e?S():"["==e?k($,B("]"),B(":"),we):k(B(":"),_e,Se)):(M(t),k(Se))}function Ce(){return S(_e,Se)}function Se(e,t){if("="==t)return k(V)}function ke(e){if(","==e)return k(xe)}function Te(e,t){if("keyword b"==e&&"else"==t)return k(N("form","else"),z,R)}function Me(e,t){return"await"==t?k(Me):"("==e?k(N(")"),De,R):void 0}function De(e){return"var"==e?k(xe,Ae):"variable"==e?k(Ae):S(Ae)}function Ae(e,t){return")"==e?k():";"==e?k(Ae):"in"==t||"of"==t?(C.marked="keyword",k($,Ae)):S($,Ae)}function Ie(e,t){return"*"==t?(C.marked="keyword",k(Ie)):"variable"==e?(M(t),k(Ie)):"("==e?k(E,N(")"),re(Le,")"),R,ue,z,P):u&&"<"==t?k(N(">"),re(ye,">"),R,Ie):void 0}function Oe(e,t){return"*"==t?(C.marked="keyword",k(Oe)):"variable"==e?(M(t),k(Oe)):"("==e?k(E,N(")"),re(Le,")"),R,ue,P):u&&"<"==t?k(N(">"),re(ye,">"),R,Oe):void 0}function Ee(e,t){return"keyword"==e||"variable"==e?(C.marked="type",k(Ee)):"<"==t?k(N(">"),re(ye,">"),R):void 0}function Le(e,t){return"@"==t&&k($,Le),"spread"==e?k(Le):u&&D(t)?(C.marked="keyword",k(Le)):u&&"this"==e?k(se,Se):S(_e,se,Se)}function Pe(e,t){return"variable"==e?Ne(e,t):Re(e,t)}function Ne(e,t){if("variable"==e)return M(t),k(Re)}function Re(e,t){return"<"==t?k(N(">"),re(ye,">"),R,Re):"extends"==t||"implements"==t||u&&","==e?("implements"==t&&(C.marked="keyword"),k(u?he:$,Re)):"{"==e?k(N("}"),Be,R):void 0}function Be(e,t){return"async"==e||"variable"==e&&("static"==t||"get"==t||"set"==t||u&&D(t))&&C.stream.match(/^\s+[\w$\xa1-\uffff]/,!1)?(C.marked="keyword",k(Be)):"variable"==e||"keyword"==C.style?(C.marked="property",k(ze,Be)):"number"==e||"string"==e?k(ze,Be):"["==e?k($,se,B("]"),ze,Be):"*"==t?(C.marked="keyword",k(Be)):u&&"("==e?S(Oe,Be):";"==e||","==e?k(Be):"}"==e?k():"@"==t?k($,Be):void 0}function ze(e,t){if("?"==t)return k(ze);if(":"==e)return k(he,Se);if("="==t)return k(V);var n=C.state.lexical.prev;return S(n&&"interface"==n.info?Oe:Ie)}function Fe(e,t){return"*"==t?(C.marked="keyword",k(qe,B(";"))):"default"==t?(C.marked="keyword",k($,B(";"))):"{"==e?k(re($e,"}"),qe,B(";")):S(z)}function $e(e,t){return"as"==t?(C.marked="keyword",k(B("variable"))):"variable"==e?S(V,$e):void 0}function Ve(e){return"string"==e?k():"("==e?S($):S(je,He,qe)}function je(e,t){return"{"==e?oe(je,"}"):("variable"==e&&M(t),"*"==t&&(C.marked="keyword"),k(We))}function He(e){if(","==e)return k(je,He)}function We(e,t){if("as"==t)return C.marked="keyword",k(je)}function qe(e,t){if("from"==t)return C.marked="keyword",k($)}function Ge(e){return"]"==e?k():S(re(V,"]"))}function Ue(){return S(N("form"),_e,B("{"),N("}"),re(Ye,"}"),R,R)}function Ye(){return S(_e,Se)}function Xe(e,t,n){return t.tokenize==g&&/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(t.lastType)||"quasi"==t.lastType&&/\{\s*$/.test(e.string.slice(0,e.pos-(n||0)))}return P.lex=!0,R.lex=!0,{startState:function(e){var t={tokenize:g,lastType:"sof",cc:[],lexical:new _((e||0)-o,0,"block",!1),localVars:n.localVars,context:n.localVars&&new A(null,null,!1),indented:e||0};return n.globalVars&&"object"==typeof n.globalVars&&(t.globalVars=n.globalVars),t},token:function(e,t){if(e.sol()&&(t.lexical.hasOwnProperty("align")||(t.lexical.align=!1),t.indented=e.indentation(),b(e,t)),t.tokenize!=m&&e.eatSpace())return null;var n=t.tokenize(e,t);return"comment"==i?n:(t.lastType="operator"!=i||"++"!=r&&"--"!=r?i:"incdec",function(e,t,n,i,r){var o=e.cc;for(C.state=e,C.stream=r,C.marked=null,C.cc=o,C.style=t,e.lexical.hasOwnProperty("align")||(e.lexical.align=!0);;)if((o.length?o.pop():l?$:z)(n,i)){for(;o.length&&o[o.length-1].lex;)o.pop()();return C.marked?C.marked:"variable"==n&&w(e,i)?"variable-2":t}}(t,n,i,r,e))},indent:function(t,i){if(t.tokenize==m||t.tokenize==v)return e.Pass;if(t.tokenize!=g)return 0;var r,s=i&&i.charAt(0),l=t.lexical;if(!/^\s*else\b/.test(i))for(var u=t.cc.length-1;u>=0;--u){var c=t.cc[u];if(c==R)l=l.prev;else if(c!=Te)break}for(;("stat"==l.type||"form"==l.type)&&("}"==s||(r=t.cc[t.cc.length-1])&&(r==q||r==G)&&!/^[,\.=+\-*:?[\(]/.test(i));)l=l.prev;a&&")"==l.type&&"stat"==l.prev.type&&(l=l.prev);var h=l.type,f=s==h;return"vardef"==h?l.indented+("operator"==t.lastType||","==t.lastType?l.info.length+1:0):"form"==h&&"{"==s?l.indented:"form"==h?l.indented+o:"stat"==h?l.indented+(function(e,t){return"operator"==e.lastType||","==e.lastType||d.test(t.charAt(0))||/[,.]/.test(t.charAt(0))}(t,i)?a||o:0):"switch"!=l.info||f||0==n.doubleIndentSwitch?l.align?l.column+(f?0:1):l.indented+(f?0:o):l.indented+(/^(?:case|default)\b/.test(i)?o:2*o)},electricInput:/^\s*(?:case .*?:|default:|\{|\})$/,blockCommentStart:l?null:"/*",blockCommentEnd:l?null:"*/",blockCommentContinue:l?null:" * ",lineComment:l?null:"//",fold:"brace",closeBrackets:"()[]{}''\"\"``",helperType:l?"json":"javascript",jsonldMode:s,jsonMode:l,expressionAllowed:Xe,skipExpression:function(e){var t=e.cc[e.cc.length-1];t!=$&&t!=V||e.cc.pop()}}}),e.registerHelper("wordChars","javascript",/[\w$]/),e.defineMIME("text/javascript","javascript"),e.defineMIME("text/ecmascript","javascript"),e.defineMIME("application/javascript","javascript"),e.defineMIME("application/x-javascript","javascript"),e.defineMIME("application/ecmascript","javascript"),e.defineMIME("application/json",{name:"javascript",json:!0}),e.defineMIME("application/x-json",{name:"javascript",json:!0}),e.defineMIME("application/manifest+json",{name:"javascript",json:!0}),e.defineMIME("application/ld+json",{name:"javascript",jsonld:!0}),e.defineMIME("text/typescript",{name:"javascript",typescript:!0}),e.defineMIME("application/typescript",{name:"javascript",typescript:!0})})(n("8U58"))},"5KBG":function(e,t,n){n("4Nz2").__DEV__;var i=n("/gxq"),r=(i.isTypedArray,i.extend),o=(i.assert,i.each),a=i.isObject,s=n("vXqC"),l=s.getDataItemValue,u=s.isDataItemOption,c=n("wWR3").parseDate,h=n("rrAD"),d=n("+2Ke"),f=d.SOURCE_FORMAT_TYPED_ARRAY,p=d.SOURCE_FORMAT_ARRAY_ROWS,g=d.SOURCE_FORMAT_ORIGINAL,m=d.SOURCE_FORMAT_OBJECT_ROWS;function v(e,t){h.isInstance(e)||(e=h.seriesDataToSource(e)),this._source=e;var n=this._data=e.data,i=e.sourceFormat;i===f&&(this._offset=0,this._dimSize=t,this._data=n);var o=b[i===p?i+"_"+e.seriesLayoutBy:i];r(this,o)}var y=v.prototype;y.pure=!1,y.persistent=!0,y.getSource=function(){return this._source};var b={arrayRows_column:{pure:!0,count:function(){return Math.max(0,this._data.length-this._source.startIndex)},getItem:function(e){return this._data[e+this._source.startIndex]},appendData:w},arrayRows_row:{pure:!0,count:function(){var e=this._data[0];return e?Math.max(0,e.length-this._source.startIndex):0},getItem:function(e){e+=this._source.startIndex;for(var t=[],n=this._data,i=0;i3?1.4:r>1?1.2:1.1;f(this,"zoom","zoomOnMouseWheel",e,{scale:i>0?s:1/s,originX:o,originY:a})}if(n){var l=Math.abs(i);f(this,"scrollMove","moveOnMouseWheel",e,{scrollDelta:(i>0?1:-1)*(l>3?.4:l>1?.15:.05),originX:o,originY:a})}}}function d(e){a.isTaken(this._zr,"globalPan")||f(this,"zoom",null,e,{scale:e.pinchScale>1?1.1:1/1.1,originX:e.pinchX,originY:e.pinchY})}function f(e,t,n,i,r){e.pointerChecker&&e.pointerChecker(i,r.originX,r.originY)&&(o.stop(i.event),p(e,t,n,i,r))}function p(e,t,n,r,o){o.isAvailableBehavior=i.bind(g,null,n,r),e.trigger(t,o)}function g(e,t,n){var r=n[e];return!e||r&&(!i.isString(r)||t.event[r+"Key"])}i.mixin(s,r);var m=s;e.exports=m},"5QRV":function(e,t,n){var i=n("/gxq"),r=n("ao1T"),o=n("3yJd"),a=n("2uoh"),s=n("Pdtn"),l=n("1Xuh");l.getLayoutRect;t.getLayoutRect=l.getLayoutRect;var u=n("qVJQ"),c=u.enableDataStack,h=u.isDimensionStacked,d=u.getStackedDimension,f=n("/n1K");t.completeDimensions=f;var p=n("hcq/");t.createDimensions=p;var g=n("kK7q");t.createSymbol=g.createSymbol;var m={isDimensionStacked:h,enableDataStack:c,getStackedDimension:d};t.createList=function(e){return r(e.getSource(),e)},t.dataStack=m,t.createScale=function(e,t){var n=t;s.isInstance(t)||(n=new s(t),i.mixin(n,a));var r=o.createScaleByModel(n);return r.setExtent(e[0],e[1]),o.niceScaleExtent(r,n),r},t.mixinAxisModelCommonMethods=function(e){i.mixin(e,a)}},"5QVw":function(e,t,n){e.exports={default:n("BwfY"),__esModule:!0}},"5dr1":function(e,t,n){var i=n("/gxq"),r=n("8b51"),o=n("ct4P");function a(e){o.call(this,e)}a.prototype={constructor:a,type:"cartesian2d",dimensions:["x","y"],getBaseAxis:function(){return this.getAxesByScale("ordinal")[0]||this.getAxesByScale("time")[0]||this.getAxis("x")},containPoint:function(e){var t=this.getAxis("x"),n=this.getAxis("y");return t.contain(t.toLocalCoord(e[0]))&&n.contain(n.toLocalCoord(e[1]))},containData:function(e){return this.getAxis("x").containData(e[0])&&this.getAxis("y").containData(e[1])},dataToPoint:function(e,t,n){var i=this.getAxis("x"),r=this.getAxis("y");return(n=n||[])[0]=i.toGlobalCoord(i.dataToCoord(e[0])),n[1]=r.toGlobalCoord(r.dataToCoord(e[1])),n},clampData:function(e,t){var n=this.getAxis("x").scale,i=this.getAxis("y").scale,r=n.getExtent(),o=i.getExtent(),a=n.parse(e[0]),s=i.parse(e[1]);return(t=t||[])[0]=Math.min(Math.max(Math.min(r[0],r[1]),a),Math.max(r[0],r[1])),t[1]=Math.min(Math.max(Math.min(o[0],o[1]),s),Math.max(o[0],o[1])),t},pointToData:function(e,t){var n=this.getAxis("x"),i=this.getAxis("y");return(t=t||[])[0]=n.coordToData(n.toLocalCoord(e[0])),t[1]=i.coordToData(i.toLocalCoord(e[1])),t},getOtherAxis:function(e){return this.getAxis("x"===e.dim?"y":"x")},getArea:function(){var e=this.getAxis("x").getGlobalExtent(),t=this.getAxis("y").getGlobalExtent(),n=Math.min(e[0],e[1]),i=Math.min(t[0],t[1]),o=Math.max(e[0],e[1])-n,a=Math.max(t[0],t[1])-i;return new r(n,i,o,a)}},i.inherits(a,o);var s=a;e.exports=s},"5vFd":function(e,t,n){n("4Nz2").__DEV__;var i=n("/gxq"),r=i.isObject,o=i.each,a=i.map,s=i.indexOf,l=(i.retrieve,n("1Xuh").getLayoutRect),u=n("3yJd"),c=u.createScaleByModel,h=u.ifAxisCrossZero,d=u.niceScaleExtent,f=u.estimateLabelUnionRect,p=n("5dr1"),g=n("RKzr"),m=n("rctg"),v=n("qVJQ").getStackedDimension;function y(e,t,n){return e.getCoordSysModel()===t}function b(e,t,n){this._coordsMap={},this._coordsList=[],this._axesMap={},this._axesList=[],this._initCartesian(e,t,n),this.model=e}n("BuI2");var x=b.prototype;function _(e,t,n,i){n.getAxesOnZeroOf=function(){return r?[r]:[]};var r,o=e[t],a=n.model,s=a.get("axisLine.onZero"),l=a.get("axisLine.onZeroAxisIndex");if(s){if(null!=l)w(o[l])&&(r=o[l]);else for(var u in o)if(o.hasOwnProperty(u)&&w(o[u])&&!i[c(o[u])]){r=o[u];break}r&&(i[c(r)]=!0)}function c(e){return e.dim+"_"+e.index}}function w(e){return e&&"category"!==e.type&&"time"!==e.type&&h(e)}x.type="grid",x.axisPointerEnabled=!0,x.getRect=function(){return this._rect},x.update=function(e,t){var n=this._axesMap;this._updateScale(e,this.model),o(n.x,function(e){d(e.scale,e.model)}),o(n.y,function(e){d(e.scale,e.model)});var i={};o(n.x,function(e){_(n,"y",e,i)}),o(n.y,function(e){_(n,"x",e,i)}),this.resize(this.model,t)},x.resize=function(e,t,n){var i=l(e.getBoxLayoutParams(),{width:t.getWidth(),height:t.getHeight()});this._rect=i;var r=this._axesList;function a(){o(r,function(e){var t=e.isHorizontal(),n=t?[0,i.width]:[0,i.height],r=e.inverse?1:0;e.setExtent(n[r],n[1-r]),function(e,t){var n=e.getExtent(),i=n[0]+n[1];e.toGlobalCoord="x"===e.dim?function(e){return e+t}:function(e){return i-e+t},e.toLocalCoord="x"===e.dim?function(e){return e-t}:function(e){return i-e+t}}(e,t?i.x:i.y)})}a(),!n&&e.get("containLabel")&&(o(r,function(e){if(!e.model.get("axisLabel.inside")){var t=f(e);if(t){var n=e.isHorizontal()?"height":"width",r=e.model.get("axisLabel.margin");i[n]-=t[n]+r,"top"===e.position?i.y+=t.height+r:"left"===e.position&&(i.x+=t.width+r)}}}),a())},x.getAxis=function(e,t){var n=this._axesMap[e];if(null!=n){if(null==t)for(var i in n)if(n.hasOwnProperty(i))return n[i];return n[t]}},x.getAxes=function(){return this._axesList.slice()},x.getCartesian=function(e,t){if(null!=e&&null!=t){var n="x"+e+"y"+t;return this._coordsMap[n]}r(e)&&(t=e.yAxisIndex,e=e.xAxisIndex);for(var i=0,o=this._coordsList;i=0},getOrient:function(){return"vertical"===this.get("orient")?{index:1,name:"vertical"}:{index:0,name:"horizontal"}},defaultOption:{zlevel:0,z:4,show:!0,orient:"horizontal",left:"center",top:0,align:"auto",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemGap:10,itemWidth:25,itemHeight:14,inactiveColor:"#ccc",inactiveBorderColor:"#ccc",itemStyle:{borderWidth:0},textStyle:{color:"#333"},selectedMode:!0,selector:!1,selectorLabel:{show:!0,borderRadius:10,padding:[3,5,3,5],fontSize:12,fontFamily:" sans-serif",color:"#666",borderWidth:1,borderColor:"#666"},emphasis:{selectorLabel:{show:!0,color:"#eee",backgroundColor:"#666"}},selectorPosition:"auto",selectorItemGap:7,selectorButtonGap:10,tooltip:{show:!1}}}),c=u;e.exports=c},"6Kqb":function(e,t,n){var i=n("GxVO").extend({type:"ring",shape:{cx:0,cy:0,r:0,r0:0},buildPath:function(e,t){var n=t.cx,i=t.cy,r=2*Math.PI;e.moveTo(n+t.r,i),e.arc(n,i,t.r,0,r,!1),e.moveTo(n+t.r0,i),e.arc(n,i,t.r0,0,r,!0)}});e.exports=i},"6MCj":function(e,t,n){var i=n("YNzw"),r=n("C7PF").applyTransform,o=n("8b51"),a=n("DRaW"),s=n("3h1/"),l=n("qjrH"),u=n("taS8"),c=n("9qnA"),h=n("MAom"),d=n("/86O"),f=n("GxVO"),p=n("moDv"),g=n("wRzc"),m=n("cI6i"),v=p.CMD,y=Math.round,b=Math.sqrt,x=Math.abs,_=Math.cos,w=Math.sin,C=Math.max;if(!i.canvasSupported){var S=21600,k=S/2,T=function(e){e.style.cssText="position:absolute;left:0;top:0;width:1px;height:1px;",e.coordsize=S+","+S,e.coordorigin="0,0"},M=function(e,t,n){return"rgb("+[e,t,n].join(",")+")"},D=function(e,t){t&&e&&t.parentNode!==e&&e.appendChild(t)},A=function(e,t){t&&e&&t.parentNode===e&&e.removeChild(t)},I=function(e,t,n){return 1e5*(parseFloat(e)||0)+1e3*(parseFloat(t)||0)+n},O=l.parsePercent,E=function(e,t,n){var i=a.parse(t);n=+n,isNaN(n)&&(n=1),i&&(e.color=M(i[0],i[1],i[2]),e.opacity=n*i[3])},L=function(e,t,n,i){var o="fill"===t,s=e.getElementsByTagName(t)[0];null!=n[t]&&"none"!==n[t]&&(o||!o&&n.lineWidth)?(e[o?"filled":"stroked"]="true",n[t]instanceof g&&A(e,s),s||(s=m.createNode(t)),o?function(e,t,n){var i,o,s=t.fill;if(null!=s)if(s instanceof g){var l,u=0,c=[0,0],h=0,d=1,f=n.getBoundingRect(),p=f.width,m=f.height;if("linear"===s.type){l="gradient";var v=n.transform,y=[s.x*p,s.y*m],b=[s.x2*p,s.y2*m];v&&(r(y,y,v),r(b,b,v));var x=b[0]-y[0],_=b[1]-y[1];(u=180*Math.atan2(x,_)/Math.PI)<0&&(u+=360),u<1e-6&&(u=0)}else{l="gradientradial",y=[s.x*p,s.y*m],v=n.transform;var w=n.scale,k=p,T=m;c=[(y[0]-f.x)/k,(y[1]-f.y)/T],v&&r(y,y,v),k/=w[0]*S,T/=w[1]*S;var D=C(k,T);h=0/D,d=2*s.r/D-h}var A=s.colorStops.slice();A.sort(function(e,t){return e.offset-t.offset});for(var I=A.length,O=[],L=[],P=0;P=2){var B=O[0][0],z=O[1][0],F=O[0][1]*t.opacity,$=O[1][1]*t.opacity;e.type=l,e.method="none",e.focus="100%",e.angle=u,e.color=B,e.color2=z,e.colors=L.join(","),e.opacity=$,e.opacity2=F}"radial"===l&&(e.focusposition=c.join(","))}else E(e,s,t.opacity)}(s,n,i):function(e,t){t.lineDash&&(e.dashstyle=t.lineDash.join(" ")),null==t.stroke||t.stroke instanceof g||E(e,t.stroke,t.opacity)}(s,n),D(e,s)):(e[o?"filled":"stroked"]="false",A(e,s))},P=[[],[],[]];f.prototype.brushVML=function(e){var t=this.style,n=this._vmlEl;n||(n=m.createNode("shape"),T(n),this._vmlEl=n),L(n,"fill",t,this),L(n,"stroke",t,this);var i=this.transform,o=null!=i,a=n.getElementsByTagName("stroke")[0];if(a){var s=t.lineWidth;if(o&&!t.strokeNoScale){var l=i[0]*i[3]-i[1]*i[2];s*=b(x(l))}a.weight=s+"px"}var u=this.path||(this.path=new p);this.__dirtyPath&&(u.beginPath(),u.subPixelOptimize=!1,this.buildPath(u,this.shape),u.toStatic(),this.__dirtyPath=!1),n.path=function(e,t){var n,i,o,a,s,l,u=v.M,c=v.C,h=v.L,d=v.A,f=v.Q,p=[],g=e.data,m=e.len();for(a=0;a.01?j&&(H+=.0125):Math.abs(W-B)<1e-4?j&&HR?M-=.0125:M+=.0125:j&&WB?T+=.0125:T-=.0125),p.push(q,y(((R-z)*E+I)*S-k),",",y(((B-F)*L+O)*S-k),",",y(((R+z)*E+I)*S-k),",",y(((B+F)*L+O)*S-k),",",y((H*E+I)*S-k),",",y((W*L+O)*S-k),",",y((T*E+I)*S-k),",",y((M*L+O)*S-k)),s=T,l=M;break;case v.R:var G=P[0],U=P[1];G[0]=g[a++],G[1]=g[a++],U[0]=G[0]+g[a++],U[1]=G[1]+g[a++],t&&(r(G,G,t),r(U,U,t)),G[0]=y(G[0]*S-k),U[0]=y(U[0]*S-k),G[1]=y(G[1]*S-k),U[1]=y(U[1]*S-k),p.push(" m ",G[0],",",G[1]," l ",U[0],",",G[1]," l ",U[0],",",U[1]," l ",G[0],",",U[1]);break;case v.Z:p.push(" x ")}if(n>0){p.push(i);for(var Y=0;Y100&&(B=0,R={});var n,i=z.style;try{i.font=e,n=i.fontFamily.split(",")[0]}catch(e){}t={style:i.fontStyle||"normal",variant:i.fontVariant||"normal",weight:i.fontWeight||"normal",size:0|parseFloat(i.fontSize||12),family:n||"Microsoft YaHei"},R[e]=t,B++}return t}(o.font),_=x.style+" "+x.variant+" "+x.weight+" "+x.size+'px "'+x.family+'"';n=n||s.getBoundingRect(a,_,v,b,o.textPadding,o.textLineHeight);var w=this.transform;if(w&&!i&&(F.copy(t),F.applyTransform(w),t=F),i)p=t.x,g=t.y;else{var C=o.textPosition;if(C instanceof Array)p=t.x+O(C[0],t.width),g=t.y+O(C[1],t.height),v=v||"left";else{var S=this.calculateTextPosition?this.calculateTextPosition({},o,t):s.calculateTextPosition({},o,t);p=S.x,g=S.y,v=v||S.textAlign,b=b||S.textVerticalAlign}}p=s.adjustTextX(p,n.width,v),g=s.adjustTextY(g,n.height,b),g+=n.height/2;var k,M,A,E=m.createNode,P=this._textVmlEl;P?M=(k=(A=P.firstChild).nextSibling).nextSibling:(P=E("line"),k=E("path"),M=E("textpath"),A=E("skew"),M.style["v-text-align"]="left",T(P),k.textpathok=!0,M.on=!0,P.from="0 0",P.to="1000 0.05",D(P,A),D(P,k),D(P,M),this._textVmlEl=P);var N=[p,g],$=P.style;w&&i?(r(N,N,w),A.on=!0,A.matrix=w[0].toFixed(3)+","+w[2].toFixed(3)+","+w[1].toFixed(3)+","+w[3].toFixed(3)+",0,0",A.offset=(y(N[0])||0)+","+(y(N[1])||0),A.origin="0 0",$.left="0px",$.top="0px"):(A.on=!1,$.left=y(p)+"px",$.top=y(g)+"px"),M.string=String(a).replace(/&/g,"&").replace(/"/g,""");try{M.style.font=_}catch(e){}L(P,"fill",{fill:o.textFill,opacity:o.opacity},this),L(P,"stroke",{stroke:o.textStroke,opacity:o.opacity,lineDash:o.lineDash||null},this),P.style.zIndex=I(this.zlevel,this.z,this.z2),D(e,P)}},V=function(e){A(e,this._textVmlEl),this._textVmlEl=null},j=function(e){D(e,this._textVmlEl)},H=[u,c,h,f,d],W=0;Wh?h=f:(d.lastTickCount=i,d.lastAutoInterval=h),h}},i.inherits(s,o);var l=s;e.exports=l},"6axr":function(e,t,n){var i=n("YqdL"),r=n("6UfY"),o=function(e){this.name=e||"",this.cx=0,this.cy=0,this._radiusAxis=new i,this._angleAxis=new r,this._radiusAxis.polar=this._angleAxis.polar=this};o.prototype={type:"polar",axisPointerEnabled:!0,constructor:o,dimensions:["radius","angle"],model:null,containPoint:function(e){var t=this.pointToCoord(e);return this._radiusAxis.contain(t[0])&&this._angleAxis.contain(t[1])},containData:function(e){return this._radiusAxis.containData(e[0])&&this._angleAxis.containData(e[1])},getAxis:function(e){return this["_"+e+"Axis"]},getAxes:function(){return[this._radiusAxis,this._angleAxis]},getAxesByScale:function(e){var t=[],n=this._angleAxis,i=this._radiusAxis;return n.scale.type===e&&t.push(n),i.scale.type===e&&t.push(i),t},getAngleAxis:function(){return this._angleAxis},getRadiusAxis:function(){return this._radiusAxis},getOtherAxis:function(e){var t=this._angleAxis;return e===t?this._radiusAxis:t},getBaseAxis:function(){return this.getAxesByScale("ordinal")[0]||this.getAxesByScale("time")[0]||this.getAngleAxis()},getTooltipAxes:function(e){var t=null!=e&&"auto"!==e?this.getAxis(e):this.getBaseAxis();return{baseAxes:[t],otherAxes:[this.getOtherAxis(t)]}},dataToPoint:function(e,t){return this.coordToPoint([this._radiusAxis.dataToRadius(e[0],t),this._angleAxis.dataToAngle(e[1],t)])},pointToData:function(e,t){var n=this.pointToCoord(e);return[this._radiusAxis.radiusToData(n[0],t),this._angleAxis.angleToData(n[1],t)]},pointToCoord:function(e){var t=e[0]-this.cx,n=e[1]-this.cy,i=this.getAngleAxis(),r=i.getExtent(),o=Math.min(r[0],r[1]),a=Math.max(r[0],r[1]);i.inverse?o=a-360:a=o+360;var s=Math.sqrt(t*t+n*n);t/=s,n/=s;for(var l=Math.atan2(-n,t)/Math.PI*180,u=la;)l+=360*u;return[s,l]},coordToPoint:function(e){var t=e[0],n=e[1]/180*Math.PI;return[Math.cos(n)*t+this.cx,-Math.sin(n)*t+this.cy]},getArea:function(){var e=this.getAngleAxis(),t=this.getRadiusAxis().getExtent().slice();t[0]>t[1]&&t.reverse();var n=e.getExtent(),i=Math.PI/180;return{cx:this.cx,cy:this.cy,r0:t[0],r:t[1],startAngle:-n[0]*i,endAngle:-n[1]*i,clockwise:e.inverse,contain:function(e,t){var n=e-this.cx,i=t-this.cy,r=n*n+i*i,o=this.r,a=this.r0;return r<=o*o&&r>=a*a}}}};var a=o;e.exports=a},"6f6q":function(e,t,n){var i=n("Icdr"),r=n("/gxq");function o(e,t,n){var i,o={},a="toggleSelected"===e;return n.eachComponent("legend",function(n){a&&null!=i?n[i?"select":"unSelect"](t.name):"allSelect"===e||"inverseSelect"===e?n[e]():(n[e](t.name),i=n.isSelected(t.name));var s=n.getData();r.each(s,function(e){var t=e.get("name");if("\n"!==t&&""!==t){var i=n.isSelected(t);o.hasOwnProperty(t)?o[t]=o[t]&&i:o[t]=i}})}),"allSelect"===e||"inverseSelect"===e?{selected:o}:{name:t.name,selected:o}}i.registerAction("legendToggleSelect","legendselectchanged",r.curry(o,"toggleSelected")),i.registerAction("legendAllSelect","legendselectall",r.curry(o,"allSelect")),i.registerAction("legendInverseSelect","legendinverseselect",r.curry(o,"inverseSelect")),i.registerAction("legendSelect","legendselected",r.curry(o,"select")),i.registerAction("legendUnSelect","legendunselected",r.curry(o,"unSelect"))},"6n1D":function(e,t,n){var i=n("0sHC"),r=n("bzOU");function o(e){this._ctor=e||r,this.group=new i.Group}var a=o.prototype;function s(e){var t=e.hostModel;return{lineStyle:t.getModel("lineStyle").getLineStyle(),hoverLineStyle:t.getModel("emphasis.lineStyle").getLineStyle(),labelModel:t.getModel("label"),hoverLabelModel:t.getModel("emphasis.label")}}function l(e){return isNaN(e[0])||isNaN(e[1])}function u(e){return!l(e[0])&&!l(e[1])}a.isPersistent=function(){return!0},a.updateData=function(e){var t=this,n=t.group,i=t._lineData;t._lineData=e,i||n.removeAll();var r=s(e);e.diff(i).add(function(n){!function(e,t,n,i){if(!u(t.getItemLayout(n)))return;var r=new e._ctor(t,n,i);t.setItemGraphicEl(n,r),e.group.add(r)}(t,e,n,r)}).update(function(n,o){!function(e,t,n,i,r,o){var a=t.getItemGraphicEl(i);if(!u(n.getItemLayout(r)))return void e.group.remove(a);a?a.updateData(n,r,o):a=new e._ctor(n,r,o);n.setItemGraphicEl(r,a),e.group.add(a)}(t,i,e,o,n,r)}).remove(function(e){n.remove(i.getItemGraphicEl(e))}).execute()},a.updateLayout=function(){var e=this._lineData;e&&e.eachItemGraphicEl(function(t,n){t.updateLayout(e,n)},this)},a.incrementalPrepareUpdate=function(e){this._seriesScope=s(e),this._lineData=null,this.group.removeAll()},a.incrementalUpdate=function(e,t){function n(e){e.isGroup||function(e){return e.animators&&e.animators.length>0}(e)||(e.incremental=e.useHoverLayer=!0)}for(var i=e.start;i=0&&Math.floor(t)===t&&isFinite(e)}function d(e){return r(e)&&"function"==typeof e.then&&"function"==typeof e.catch}function f(e){return null==e?"":Array.isArray(e)||u(e)&&e.toString===l?JSON.stringify(e,null,2):String(e)}function p(e){var t=parseFloat(e);return isNaN(t)?e:t}function g(e,t){for(var n=Object.create(null),i=e.split(","),r=0;r-1)return e.splice(n,1)}}var b=Object.prototype.hasOwnProperty;function x(e,t){return b.call(e,t)}function _(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}var w=/-(\w)/g,C=_(function(e){return e.replace(w,function(e,t){return t?t.toUpperCase():""})}),S=_(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),k=/\B([A-Z])/g,T=_(function(e){return e.replace(k,"-$1").toLowerCase()});var M=Function.prototype.bind?function(e,t){return e.bind(t)}:function(e,t){function n(n){var i=arguments.length;return i?i>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n};function D(e,t){t=t||0;for(var n=e.length-t,i=new Array(n);n--;)i[n]=e[n+t];return i}function A(e,t){for(var n in t)e[n]=t[n];return e}function I(e){for(var t={},n=0;n0,Q=K&&K.indexOf("edge/")>0,ee=(K&&K.indexOf("android"),K&&/iphone|ipad|ipod|ios/.test(K)||"ios"===X),te=(K&&/chrome\/\d+/.test(K),K&&/phantomjs/.test(K),K&&K.match(/firefox\/(\d+)/)),ne={}.watch,ie=!1;if(U)try{var re={};Object.defineProperty(re,"passive",{get:function(){ie=!0}}),window.addEventListener("test-passive",null,re)}catch(e){}var oe=function(){return void 0===q&&(q=!U&&!Y&&void 0!==e&&(e.process&&"server"===e.process.env.VUE_ENV)),q},ae=U&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function se(e){return"function"==typeof e&&/native code/.test(e.toString())}var le,ue="undefined"!=typeof Symbol&&se(Symbol)&&"undefined"!=typeof Reflect&&se(Reflect.ownKeys);le="undefined"!=typeof Set&&se(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var ce=O,he=0,de=function(){this.id=he++,this.subs=[]};de.prototype.addSub=function(e){this.subs.push(e)},de.prototype.removeSub=function(e){y(this.subs,e)},de.prototype.depend=function(){de.target&&de.target.addDep(this)},de.prototype.notify=function(){var e=this.subs.slice();for(var t=0,n=e.length;t-1)if(o&&!x(r,"default"))a=!1;else if(""===a||a===T(e)){var l=je(String,r.type);(l<0||s0&&(ht((u=e(u,(n||"")+"_"+l))[0])&&ht(h)&&(s[c]=be(h.text+u[0].text),u.shift()),s.push.apply(s,u)):a(u)?ht(h)?s[c]=be(h.text+u):""!==u&&s.push(be(u)):ht(u)&&ht(h)?s[c]=be(h.text+u.text):(o(t._isVList)&&r(u.tag)&&i(u.key)&&r(n)&&(u.key="__vlist"+n+"_"+l+"__"),s.push(u)));return s}(e):void 0}function ht(e){return r(e)&&r(e.text)&&!1===e.isComment}function dt(e,t){if(e){for(var n=Object.create(null),i=ue?Reflect.ownKeys(e):Object.keys(e),r=0;r0,a=e?!!e.$stable:!o,s=e&&e.$key;if(e){if(e._normalized)return e._normalized;if(a&&i&&i!==n&&s===i.$key&&!o&&!i.$hasNormal)return i;for(var l in r={},e)e[l]&&"$"!==l[0]&&(r[l]=mt(t,l,e[l]))}else r={};for(var u in t)u in r||(r[u]=vt(t,u));return e&&Object.isExtensible(e)&&(e._normalized=r),H(r,"$stable",a),H(r,"$key",s),H(r,"$hasNormal",o),r}function mt(e,t,n){var i=function(){var e=arguments.length?n.apply(null,arguments):n({});return(e=e&&"object"==typeof e&&!Array.isArray(e)?[e]:ct(e))&&(0===e.length||1===e.length&&e[0].isComment)?void 0:e};return n.proxy&&Object.defineProperty(e,t,{get:i,enumerable:!0,configurable:!0}),i}function vt(e,t){return function(){return e[t]}}function yt(e,t){var n,i,o,a,l;if(Array.isArray(e)||"string"==typeof e)for(n=new Array(e.length),i=0,o=e.length;idocument.createEvent("Event").timeStamp&&(hn=function(){return dn.now()})}function fn(){var e,t;for(cn=hn(),ln=!0,rn.sort(function(e,t){return e.id-t.id}),un=0;unun&&rn[n].id>e.id;)n--;rn.splice(n+1,0,e)}else rn.push(e);sn||(sn=!0,nt(fn))}}(this)},gn.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||s(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){He(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},gn.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},gn.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},gn.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||y(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var mn={enumerable:!0,configurable:!0,get:O,set:O};function vn(e,t,n){mn.get=function(){return this[t][n]},mn.set=function(e){this[t][n]=e},Object.defineProperty(e,n,mn)}function yn(e){e._watchers=[];var t=e.$options;t.props&&function(e,t){var n=e.$options.propsData||{},i=e._props={},r=e.$options._propKeys=[],o=!e.$parent;o||ke(!1);var a=function(o){r.push(o);var a=Fe(o,t,n,e);De(i,o,a),o in e||vn(e,"_props",o)};for(var s in t)a(s);ke(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]="function"!=typeof t[n]?O:M(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;u(t=e._data="function"==typeof t?function(e,t){pe();try{return e.call(t,t)}catch(e){return He(e,t,"data()"),{}}finally{ge()}}(t,e):t||{})||(t={});var n=Object.keys(t),i=e.$options.props,r=(e.$options.methods,n.length);for(;r--;){var o=n[r];0,i&&x(i,o)||j(o)||vn(e,"_data",o)}Me(t,!0)}(e):Me(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),i=oe();for(var r in t){var o=t[r],a="function"==typeof o?o:o.get;0,i||(n[r]=new gn(e,a||O,O,bn)),r in e||xn(e,r,o)}}(e,t.computed),t.watch&&t.watch!==ne&&function(e,t){for(var n in t){var i=t[n];if(Array.isArray(i))for(var r=0;r-1:"string"==typeof e?e.split(",").indexOf(t)>-1:!!c(e)&&e.test(t)}function In(e,t){var n=e.cache,i=e.keys,r=e._vnode;for(var o in n){var a=n[o];if(a){var s=Dn(a.componentOptions);s&&!t(s)&&On(n,o,i,r)}}}function On(e,t,n,i){var r=e[t];!r||i&&r.tag===i.tag||r.componentInstance.$destroy(),e[t]=null,y(n,t)}!function(e){e.prototype._init=function(e){var t=this;t._uid=Sn++,t._isVue=!0,e&&e._isComponent?function(e,t){var n=e.$options=Object.create(e.constructor.options),i=t._parentVnode;n.parent=t.parent,n._parentVnode=i;var r=i.componentOptions;n.propsData=r.propsData,n._parentListeners=r.listeners,n._renderChildren=r.children,n._componentTag=r.tag,t.render&&(n.render=t.render,n.staticRenderFns=t.staticRenderFns)}(t,e):t.$options=Be(kn(t.constructor),e||{},t),t._renderProxy=t,t._self=t,function(e){var t=e.$options,n=t.parent;if(n&&!t.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(e)}e.$parent=n,e.$root=n?n.$root:e,e.$children=[],e.$refs={},e._watcher=null,e._inactive=null,e._directInactive=!1,e._isMounted=!1,e._isDestroyed=!1,e._isBeingDestroyed=!1}(t),function(e){e._events=Object.create(null),e._hasHookEvent=!1;var t=e.$options._parentListeners;t&&Zt(e,t)}(t),function(e){e._vnode=null,e._staticTrees=null;var t=e.$options,i=e.$vnode=t._parentVnode,r=i&&i.context;e.$slots=ft(t._renderChildren,r),e.$scopedSlots=n,e._c=function(t,n,i,r){return jt(e,t,n,i,r,!1)},e.$createElement=function(t,n,i,r){return jt(e,t,n,i,r,!0)};var o=i&&i.data;De(e,"$attrs",o&&o.attrs||n,null,!0),De(e,"$listeners",t._parentListeners||n,null,!0)}(t),nn(t,"beforeCreate"),function(e){var t=dt(e.$options.inject,e);t&&(ke(!1),Object.keys(t).forEach(function(n){De(e,n,t[n])}),ke(!0))}(t),yn(t),function(e){var t=e.$options.provide;t&&(e._provided="function"==typeof t?t.call(e):t)}(t),nn(t,"created"),t.$options.el&&t.$mount(t.$options.el)}}(Tn),function(e){var t={get:function(){return this._data}},n={get:function(){return this._props}};Object.defineProperty(e.prototype,"$data",t),Object.defineProperty(e.prototype,"$props",n),e.prototype.$set=Ae,e.prototype.$delete=Ie,e.prototype.$watch=function(e,t,n){if(u(t))return Cn(this,e,t,n);(n=n||{}).user=!0;var i=new gn(this,e,t,n);if(n.immediate)try{t.call(this,i.value)}catch(e){He(e,this,'callback for immediate watcher "'+i.expression+'"')}return function(){i.teardown()}}}(Tn),function(e){var t=/^hook:/;e.prototype.$on=function(e,n){var i=this;if(Array.isArray(e))for(var r=0,o=e.length;r1?D(n):n;for(var i=D(arguments,1),r='event handler for "'+e+'"',o=0,a=n.length;oparseInt(this.max)&&On(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};!function(e){var t={get:function(){return $}};Object.defineProperty(e,"config",t),e.util={warn:ce,extend:A,mergeOptions:Be,defineReactive:De},e.set=Ae,e.delete=Ie,e.nextTick=nt,e.observable=function(e){return Me(e),e},e.options=Object.create(null),z.forEach(function(t){e.options[t+"s"]=Object.create(null)}),e.options._base=e,A(e.options.components,Ln),function(e){e.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(t.indexOf(e)>-1)return this;var n=D(arguments,1);return n.unshift(this),"function"==typeof e.install?e.install.apply(e,n):"function"==typeof e&&e.apply(null,n),t.push(e),this}}(e),function(e){e.mixin=function(e){return this.options=Be(this.options,e),this}}(e),Mn(e),function(e){z.forEach(function(t){e[t]=function(e,n){return n?("component"===t&&u(n)&&(n.name=n.name||e,n=this.options._base.extend(n)),"directive"===t&&"function"==typeof n&&(n={bind:n,update:n}),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}})}(e)}(Tn),Object.defineProperty(Tn.prototype,"$isServer",{get:oe}),Object.defineProperty(Tn.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(Tn,"FunctionalRenderContext",{value:Lt}),Tn.version="2.6.11";var Pn=g("style,class"),Nn=g("input,textarea,option,select,progress"),Rn=function(e,t,n){return"value"===n&&Nn(e)&&"button"!==t||"selected"===n&&"option"===e||"checked"===n&&"input"===e||"muted"===n&&"video"===e},Bn=g("contenteditable,draggable,spellcheck"),zn=g("events,caret,typing,plaintext-only"),Fn=function(e,t){return Wn(t)||"false"===t?"false":"contenteditable"===e&&zn(t)?t:"true"},$n=g("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),Vn="http://www.w3.org/1999/xlink",jn=function(e){return":"===e.charAt(5)&&"xlink"===e.slice(0,5)},Hn=function(e){return jn(e)?e.slice(6,e.length):""},Wn=function(e){return null==e||!1===e};function qn(e){for(var t=e.data,n=e,i=e;r(i.componentInstance);)(i=i.componentInstance._vnode)&&i.data&&(t=Gn(i.data,t));for(;r(n=n.parent);)n&&n.data&&(t=Gn(t,n.data));return function(e,t){if(r(e)||r(t))return Un(e,Yn(t));return""}(t.staticClass,t.class)}function Gn(e,t){return{staticClass:Un(e.staticClass,t.staticClass),class:r(e.class)?[e.class,t.class]:t.class}}function Un(e,t){return e?t?e+" "+t:e:t||""}function Yn(e){return Array.isArray(e)?function(e){for(var t,n="",i=0,o=e.length;i-1?bi(e,t,n):$n(t)?Wn(n)?e.removeAttribute(t):(n="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,n)):Bn(t)?e.setAttribute(t,Fn(t,n)):jn(t)?Wn(n)?e.removeAttributeNS(Vn,Hn(t)):e.setAttributeNS(Vn,t,n):bi(e,t,n)}function bi(e,t,n){if(Wn(n))e.removeAttribute(t);else{if(Z&&!J&&"TEXTAREA"===e.tagName&&"placeholder"===t&&""!==n&&!e.__ieph){var i=function(t){t.stopImmediatePropagation(),e.removeEventListener("input",i)};e.addEventListener("input",i),e.__ieph=!0}e.setAttribute(t,n)}}var xi={create:vi,update:vi};function _i(e,t){var n=t.elm,o=t.data,a=e.data;if(!(i(o.staticClass)&&i(o.class)&&(i(a)||i(a.staticClass)&&i(a.class)))){var s=qn(t),l=n._transitionClasses;r(l)&&(s=Un(s,Yn(l))),s!==n._prevClass&&(n.setAttribute("class",s),n._prevClass=s)}}var wi,Ci,Si,ki,Ti,Mi,Di={create:_i,update:_i},Ai=/[\w).+\-_$\]]/;function Ii(e){var t,n,i,r,o,a=!1,s=!1,l=!1,u=!1,c=0,h=0,d=0,f=0;for(i=0;i=0&&" "===(g=e.charAt(p));p--);g&&Ai.test(g)||(u=!0)}}else void 0===r?(f=i+1,r=e.slice(0,i).trim()):m();function m(){(o||(o=[])).push(e.slice(f,i).trim()),f=i+1}if(void 0===r?r=e.slice(0,i).trim():0!==f&&m(),o)for(i=0;i-1?{exp:e.slice(0,ki),key:'"'+e.slice(ki+1)+'"'}:{exp:e,key:null};Ci=e,ki=Ti=Mi=0;for(;!Yi();)Xi(Si=Ui())?Zi(Si):91===Si&&Ki(Si);return{exp:e.slice(0,Ti),key:e.slice(Ti+1,Mi)}}(e);return null===n.key?e+"="+t:"$set("+n.exp+", "+n.key+", "+t+")"}function Ui(){return Ci.charCodeAt(++ki)}function Yi(){return ki>=wi}function Xi(e){return 34===e||39===e}function Ki(e){var t=1;for(Ti=ki;!Yi();)if(Xi(e=Ui()))Zi(e);else if(91===e&&t++,93===e&&t--,0===t){Mi=ki;break}}function Zi(e){for(var t=e;!Yi()&&(e=Ui())!==t;);}var Ji,Qi="__r",er="__c";function tr(e,t,n){var i=Ji;return function r(){null!==t.apply(null,arguments)&&rr(e,r,n,i)}}var nr=Ye&&!(te&&Number(te[1])<=53);function ir(e,t,n,i){if(nr){var r=cn,o=t;t=o._wrapper=function(e){if(e.target===e.currentTarget||e.timeStamp>=r||e.timeStamp<=0||e.target.ownerDocument!==document)return o.apply(this,arguments)}}Ji.addEventListener(e,t,ie?{capture:n,passive:i}:n)}function rr(e,t,n,i){(i||Ji).removeEventListener(e,t._wrapper||t,n)}function or(e,t){if(!i(e.data.on)||!i(t.data.on)){var n=t.data.on||{},o=e.data.on||{};Ji=t.elm,function(e){if(r(e[Qi])){var t=Z?"change":"input";e[t]=[].concat(e[Qi],e[t]||[]),delete e[Qi]}r(e[er])&&(e.change=[].concat(e[er],e.change||[]),delete e[er])}(n),st(n,o,ir,rr,tr,t.context),Ji=void 0}}var ar,sr={create:or,update:or};function lr(e,t){if(!i(e.data.domProps)||!i(t.data.domProps)){var n,o,a=t.elm,s=e.data.domProps||{},l=t.data.domProps||{};for(n in r(l.__ob__)&&(l=t.data.domProps=A({},l)),s)n in l||(a[n]="");for(n in l){if(o=l[n],"textContent"===n||"innerHTML"===n){if(t.children&&(t.children.length=0),o===s[n])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===n&&"PROGRESS"!==a.tagName){a._value=o;var u=i(o)?"":String(o);ur(a,u)&&(a.value=u)}else if("innerHTML"===n&&Zn(a.tagName)&&i(a.innerHTML)){(ar=ar||document.createElement("div")).innerHTML="";for(var c=ar.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;c.firstChild;)a.appendChild(c.firstChild)}else if(o!==s[n])try{a[n]=o}catch(e){}}}}function ur(e,t){return!e.composing&&("OPTION"===e.tagName||function(e,t){var n=!0;try{n=document.activeElement!==e}catch(e){}return n&&e.value!==t}(e,t)||function(e,t){var n=e.value,i=e._vModifiers;if(r(i)){if(i.number)return p(n)!==p(t);if(i.trim)return n.trim()!==t.trim()}return n!==t}(e,t))}var cr={create:lr,update:lr},hr=_(function(e){var t={},n=/:(.+)/;return e.split(/;(?![^(]*\))/g).forEach(function(e){if(e){var i=e.split(n);i.length>1&&(t[i[0].trim()]=i[1].trim())}}),t});function dr(e){var t=fr(e.style);return e.staticStyle?A(e.staticStyle,t):t}function fr(e){return Array.isArray(e)?I(e):"string"==typeof e?hr(e):e}var pr,gr=/^--/,mr=/\s*!important$/,vr=function(e,t,n){if(gr.test(t))e.style.setProperty(t,n);else if(mr.test(n))e.style.setProperty(T(t),n.replace(mr,""),"important");else{var i=br(t);if(Array.isArray(n))for(var r=0,o=n.length;r-1?t.split(wr).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function Sr(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(wr).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t),e.classList.length||e.removeAttribute("class");else{for(var n=" "+(e.getAttribute("class")||"")+" ",i=" "+t+" ";n.indexOf(i)>=0;)n=n.replace(i," ");(n=n.trim())?e.setAttribute("class",n):e.removeAttribute("class")}}function kr(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&A(t,Tr(e.name||"v")),A(t,e),t}return"string"==typeof e?Tr(e):void 0}}var Tr=_(function(e){return{enterClass:e+"-enter",enterToClass:e+"-enter-to",enterActiveClass:e+"-enter-active",leaveClass:e+"-leave",leaveToClass:e+"-leave-to",leaveActiveClass:e+"-leave-active"}}),Mr=U&&!J,Dr="transition",Ar="animation",Ir="transition",Or="transitionend",Er="animation",Lr="animationend";Mr&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(Ir="WebkitTransition",Or="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Er="WebkitAnimation",Lr="webkitAnimationEnd"));var Pr=U?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(e){return e()};function Nr(e){Pr(function(){Pr(e)})}function Rr(e,t){var n=e._transitionClasses||(e._transitionClasses=[]);n.indexOf(t)<0&&(n.push(t),Cr(e,t))}function Br(e,t){e._transitionClasses&&y(e._transitionClasses,t),Sr(e,t)}function zr(e,t,n){var i=$r(e,t),r=i.type,o=i.timeout,a=i.propCount;if(!r)return n();var s=r===Dr?Or:Lr,l=0,u=function(){e.removeEventListener(s,c),n()},c=function(t){t.target===e&&++l>=a&&u()};setTimeout(function(){l0&&(n=Dr,c=a,h=o.length):t===Ar?u>0&&(n=Ar,c=u,h=l.length):h=(n=(c=Math.max(a,u))>0?a>u?Dr:Ar:null)?n===Dr?o.length:l.length:0,{type:n,timeout:c,propCount:h,hasTransform:n===Dr&&Fr.test(i[Ir+"Property"])}}function Vr(e,t){for(;e.length1}function Ur(e,t){!0!==t.data.show&&Hr(t)}var Yr=function(e){var t,n,s={},l=e.modules,u=e.nodeOps;for(t=0;tp?b(e,i(n[v+1])?null:n[v+1].elm,n,f,v,o):f>v&&_(t,d,p)}(d,g,v,n,c):r(v)?(r(e.text)&&u.setTextContent(d,""),b(d,null,v,0,v.length-1,n)):r(g)?_(g,0,g.length-1):r(e.text)&&u.setTextContent(d,""):e.text!==t.text&&u.setTextContent(d,t.text),r(p)&&r(f=p.hook)&&r(f=f.postpatch)&&f(e,t)}}}function k(e,t,n){if(o(n)&&r(e.parent))e.parent.data.pendingInsert=t;else for(var i=0;i-1,a.selected!==o&&(a.selected=o);else if(P(Qr(a),i))return void(e.selectedIndex!==s&&(e.selectedIndex=s));r||(e.selectedIndex=-1)}}function Jr(e,t){return t.every(function(t){return!P(t,e)})}function Qr(e){return"_value"in e?e._value:e.value}function eo(e){e.target.composing=!0}function to(e){e.target.composing&&(e.target.composing=!1,no(e.target,"input"))}function no(e,t){var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}function io(e){return!e.componentInstance||e.data&&e.data.transition?e:io(e.componentInstance._vnode)}var ro={model:Xr,show:{bind:function(e,t,n){var i=t.value,r=(n=io(n)).data&&n.data.transition,o=e.__vOriginalDisplay="none"===e.style.display?"":e.style.display;i&&r?(n.data.show=!0,Hr(n,function(){e.style.display=o})):e.style.display=i?o:"none"},update:function(e,t,n){var i=t.value;!i!=!t.oldValue&&((n=io(n)).data&&n.data.transition?(n.data.show=!0,i?Hr(n,function(){e.style.display=e.__vOriginalDisplay}):Wr(n,function(){e.style.display="none"})):e.style.display=i?e.__vOriginalDisplay:"none")},unbind:function(e,t,n,i,r){r||(e.style.display=e.__vOriginalDisplay)}}},oo={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function ao(e){var t=e&&e.componentOptions;return t&&t.Ctor.options.abstract?ao(Ut(t.children)):e}function so(e){var t={},n=e.$options;for(var i in n.propsData)t[i]=e[i];var r=n._parentListeners;for(var o in r)t[C(o)]=r[o];return t}function lo(e,t){if(/\d-keep-alive$/.test(t.tag))return e("keep-alive",{props:t.componentOptions.propsData})}var uo=function(e){return e.tag||Gt(e)},co=function(e){return"show"===e.name},ho={name:"transition",props:oo,abstract:!0,render:function(e){var t=this,n=this.$slots.default;if(n&&(n=n.filter(uo)).length){0;var i=this.mode;0;var r=n[0];if(function(e){for(;e=e.parent;)if(e.data.transition)return!0}(this.$vnode))return r;var o=ao(r);if(!o)return r;if(this._leaving)return lo(e,r);var s="__transition-"+this._uid+"-";o.key=null==o.key?o.isComment?s+"comment":s+o.tag:a(o.key)?0===String(o.key).indexOf(s)?o.key:s+o.key:o.key;var l=(o.data||(o.data={})).transition=so(this),u=this._vnode,c=ao(u);if(o.data.directives&&o.data.directives.some(co)&&(o.data.show=!0),c&&c.data&&!function(e,t){return t.key===e.key&&t.tag===e.tag}(o,c)&&!Gt(c)&&(!c.componentInstance||!c.componentInstance._vnode.isComment)){var h=c.data.transition=A({},l);if("out-in"===i)return this._leaving=!0,lt(h,"afterLeave",function(){t._leaving=!1,t.$forceUpdate()}),lo(e,r);if("in-out"===i){if(Gt(o))return u;var d,f=function(){d()};lt(l,"afterEnter",f),lt(l,"enterCancelled",f),lt(h,"delayLeave",function(e){d=e})}}return r}}},fo=A({tag:String,moveClass:String},oo);function po(e){e.elm._moveCb&&e.elm._moveCb(),e.elm._enterCb&&e.elm._enterCb()}function go(e){e.data.newPos=e.elm.getBoundingClientRect()}function mo(e){var t=e.data.pos,n=e.data.newPos,i=t.left-n.left,r=t.top-n.top;if(i||r){e.data.moved=!0;var o=e.elm.style;o.transform=o.WebkitTransform="translate("+i+"px,"+r+"px)",o.transitionDuration="0s"}}delete fo.mode;var vo={Transition:ho,TransitionGroup:{props:fo,beforeMount:function(){var e=this,t=this._update;this._update=function(n,i){var r=Qt(e);e.__patch__(e._vnode,e.kept,!1,!0),e._vnode=e.kept,r(),t.call(e,n,i)}},render:function(e){for(var t=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),i=this.prevChildren=this.children,r=this.$slots.default||[],o=this.children=[],a=so(this),s=0;s-1?ei[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:ei[e]=/HTMLUnknownElement/.test(t.toString())},A(Tn.options.directives,ro),A(Tn.options.components,vo),Tn.prototype.__patch__=U?Yr:O,Tn.prototype.$mount=function(e,t){return function(e,t,n){return e.$el=t,e.$options.render||(e.$options.render=ye),nn(e,"beforeMount"),new gn(e,function(){e._update(e._render(),n)},O,{before:function(){e._isMounted&&!e._isDestroyed&&nn(e,"beforeUpdate")}},!0),n=!1,null==e.$vnode&&(e._isMounted=!0,nn(e,"mounted")),e}(this,e=e&&U?ni(e):void 0,t)},U&&setTimeout(function(){$.devtools&&ae&&ae.emit("init",Tn)},0);var yo=/\{\{((?:.|\r?\n)+?)\}\}/g,bo=/[-.*+?^${}()|[\]\/\\]/g,xo=_(function(e){var t=e[0].replace(bo,"\\$&"),n=e[1].replace(bo,"\\$&");return new RegExp(t+"((?:.|\\n)+?)"+n,"g")});function _o(e,t){var n=t?xo(t):yo;if(n.test(e)){for(var i,r,o,a=[],s=[],l=n.lastIndex=0;i=n.exec(e);){(r=i.index)>l&&(s.push(o=e.slice(l,r)),a.push(JSON.stringify(o)));var u=Ii(i[1].trim());a.push("_s("+u+")"),s.push({"@binding":u}),l=r+i[0].length}return l\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Io=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Oo="[a-zA-Z_][\\-\\.0-9_a-zA-Z"+V.source+"]*",Eo="((?:"+Oo+"\\:)?"+Oo+")",Lo=new RegExp("^<"+Eo),Po=/^\s*(\/?)>/,No=new RegExp("^<\\/"+Eo+"[^>]*>"),Ro=/^]+>/i,Bo=/^",""":'"',"&":"&","
":"\n"," ":"\t","'":"'"},jo=/&(?:lt|gt|quot|amp|#39);/g,Ho=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,Wo=g("pre,textarea",!0),qo=function(e,t){return e&&Wo(e)&&"\n"===t[0]};function Go(e,t){var n=t?Ho:jo;return e.replace(n,function(e){return Vo[e]})}var Uo,Yo,Xo,Ko,Zo,Jo,Qo,ea,ta=/^@|^v-on:/,na=/^v-|^@|^:|^#/,ia=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,ra=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,oa=/^\(|\)$/g,aa=/^\[.*\]$/,sa=/:(.*)$/,la=/^:|^\.|^v-bind:/,ua=/\.[^.\]]+(?=[^\]]*$)/g,ca=/^v-slot(:|$)|^#/,ha=/[\r\n]/,da=/\s+/g,fa=_(ko),pa="_empty_";function ga(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:function(e){for(var t={},n=0,i=e.length;n]*>)","i")),d=e.replace(h,function(e,n,i){return u=i.length,Fo(c)||"noscript"===c||(n=n.replace(//g,"$1").replace(//g,"$1")),qo(c,n)&&(n=n.slice(1)),t.chars&&t.chars(n),""});l+=e.length-d.length,e=d,T(c,l-u,l)}else{var f=e.indexOf("<");if(0===f){if(Bo.test(e)){var p=e.indexOf("--\x3e");if(p>=0){t.shouldKeepComment&&t.comment(e.substring(4,p),l,l+p+3),C(p+3);continue}}if(zo.test(e)){var g=e.indexOf("]>");if(g>=0){C(g+2);continue}}var m=e.match(Ro);if(m){C(m[0].length);continue}var v=e.match(No);if(v){var y=l;C(v[0].length),T(v[1],y,l);continue}var b=S();if(b){k(b),qo(b.tagName,e)&&C(1);continue}}var x=void 0,_=void 0,w=void 0;if(f>=0){for(_=e.slice(f);!(No.test(_)||Lo.test(_)||Bo.test(_)||zo.test(_)||(w=_.indexOf("<",1))<0);)f+=w,_=e.slice(f);x=e.substring(0,f)}f<0&&(x=e),x&&C(x.length),t.chars&&x&&t.chars(x,l-x.length,l)}if(e===n){t.chars&&t.chars(e);break}}function C(t){l+=t,e=e.substring(t)}function S(){var t=e.match(Lo);if(t){var n,i,r={tagName:t[1],attrs:[],start:l};for(C(t[0].length);!(n=e.match(Po))&&(i=e.match(Io)||e.match(Ao));)i.start=l,C(i[0].length),i.end=l,r.attrs.push(i);if(n)return r.unarySlash=n[1],C(n[0].length),r.end=l,r}}function k(e){var n=e.tagName,l=e.unarySlash;o&&("p"===i&&Do(n)&&T(i),s(n)&&i===n&&T(n));for(var u=a(n)||!!l,c=e.attrs.length,h=new Array(c),d=0;d=0&&r[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=r.length-1;u>=a;u--)t.end&&t.end(r[u].tag,n,o);r.length=a,i=a&&r[a-1].tag}else"br"===s?t.start&&t.start(e,[],!0,n,o):"p"===s&&(t.start&&t.start(e,[],!1,n,o),t.end&&t.end(e,n,o))}T()}(e,{warn:Uo,expectHTML:t.expectHTML,isUnaryTag:t.isUnaryTag,canBeLeftOpenTag:t.canBeLeftOpenTag,shouldDecodeNewlines:t.shouldDecodeNewlines,shouldDecodeNewlinesForHref:t.shouldDecodeNewlinesForHref,shouldKeepComment:t.comments,outputSourceRange:t.outputSourceRange,start:function(e,n,a,s,h){var d=r&&r.ns||ea(e);Z&&"svg"===d&&(n=function(e){for(var t=[],n=0;n-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),Fi(e,"change","var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(i?"_n("+r+")":r)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Gi(t,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Gi(t,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Gi(t,"$$c")+"}",null,!0)}(e,i,r);else if("input"===o&&"radio"===a)!function(e,t,n){var i=n&&n.number,r=Vi(e,"value")||"null";Pi(e,"checked","_q("+t+","+(r=i?"_n("+r+")":r)+")"),Fi(e,"change",Gi(t,r),null,!0)}(e,i,r);else if("input"===o||"textarea"===o)!function(e,t,n){var i=e.attrsMap.type,r=n||{},o=r.lazy,a=r.number,s=r.trim,l=!o&&"range"!==i,u=o?"change":"range"===i?Qi:"input",c="$event.target.value";s&&(c="$event.target.value.trim()"),a&&(c="_n("+c+")");var h=Gi(t,c);l&&(h="if($event.target.composing)return;"+h),Pi(e,"value","("+t+")"),Fi(e,u,h,null,!0),(s||a)&&Fi(e,"blur","$forceUpdate()")}(e,i,r);else if(!$.isReservedTag(o))return qi(e,i,r),!1;return!0},text:function(e,t){t.value&&Pi(e,"textContent","_s("+t.value+")",t)},html:function(e,t){t.value&&Pi(e,"innerHTML","_s("+t.value+")",t)}},isPreTag:function(e){return"pre"===e},isUnaryTag:To,mustUseProp:Rn,canBeLeftOpenTag:Mo,isReservedTag:Jn,getTagNamespace:Qn,staticKeys:function(e){return e.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(",")}(ka)},Aa=_(function(e){return g("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(e?","+e:""))});function Ia(e,t){e&&(Ta=Aa(t.staticKeys||""),Ma=t.isReservedTag||E,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||m(e.tag)||!Ma(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every(Ta)))}(t);if(1===t.type){if(!Ma(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,i=t.children.length;n|^function(?:\s+[\w$]+)?\s*\(/,Ea=/\([^)]*?\);*$/,La=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,Pa={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},Na={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},Ra=function(e){return"if("+e+")return null;"},Ba={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:Ra("$event.target !== $event.currentTarget"),ctrl:Ra("!$event.ctrlKey"),shift:Ra("!$event.shiftKey"),alt:Ra("!$event.altKey"),meta:Ra("!$event.metaKey"),left:Ra("'button' in $event && $event.button !== 0"),middle:Ra("'button' in $event && $event.button !== 1"),right:Ra("'button' in $event && $event.button !== 2")};function za(e,t){var n=t?"nativeOn:":"on:",i="",r="";for(var o in e){var a=Fa(e[o]);e[o]&&e[o].dynamic?r+=o+","+a+",":i+='"'+o+'":'+a+","}return i="{"+i.slice(0,-1)+"}",r?n+"_d("+i+",["+r.slice(0,-1)+"])":n+i}function Fa(e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return Fa(e)}).join(",")+"]";var t=La.test(e.value),n=Oa.test(e.value),i=La.test(e.value.replace(Ea,""));if(e.modifiers){var r="",o="",a=[];for(var s in e.modifiers)if(Ba[s])o+=Ba[s],Pa[s]&&a.push(s);else if("exact"===s){var l=e.modifiers;o+=Ra(["ctrl","shift","alt","meta"].filter(function(e){return!l[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(r+=function(e){return"if(!$event.type.indexOf('key')&&"+e.map($a).join("&&")+")return null;"}(a)),o&&(r+=o),"function($event){"+r+(t?"return "+e.value+"($event)":n?"return ("+e.value+")($event)":i?"return "+e.value:e.value)+"}"}return t||n?e.value:"function($event){"+(i?"return "+e.value:e.value)+"}"}function $a(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=Pa[e],i=Na[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(i)+")"}var Va={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(e,t){e.wrapData=function(n){return"_b("+n+",'"+e.tag+"',"+t.value+","+(t.modifiers&&t.modifiers.prop?"true":"false")+(t.modifiers&&t.modifiers.sync?",true":"")+")"}},cloak:O},ja=function(e){this.options=e,this.warn=e.warn||Ei,this.transforms=Li(e.modules,"transformCode"),this.dataGenFns=Li(e.modules,"genData"),this.directives=A(A({},Va),e.directives);var t=e.isReservedTag||E;this.maybeComponent=function(e){return!!e.component||!t(e.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function Ha(e,t){var n=new ja(t);return{render:"with(this){return "+(e?Wa(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function Wa(e,t){if(e.parent&&(e.pre=e.pre||e.parent.pre),e.staticRoot&&!e.staticProcessed)return qa(e,t);if(e.once&&!e.onceProcessed)return Ga(e,t);if(e.for&&!e.forProcessed)return Ya(e,t);if(e.if&&!e.ifProcessed)return Ua(e,t);if("template"!==e.tag||e.slotTarget||t.pre){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',i=Ja(e,t),r="_t("+n+(i?","+i:""),o=e.attrs||e.dynamicAttrs?ts((e.attrs||[]).concat(e.dynamicAttrs||[]).map(function(e){return{name:C(e.name),value:e.value,dynamic:e.dynamic}})):null,a=e.attrsMap["v-bind"];!o&&!a||i||(r+=",null");o&&(r+=","+o);a&&(r+=(o?"":",null")+","+a);return r+")"}(e,t);var n;if(e.component)n=function(e,t,n){var i=t.inlineTemplate?null:Ja(t,n,!0);return"_c("+e+","+Xa(t,n)+(i?","+i:"")+")"}(e.component,e,t);else{var i;(!e.plain||e.pre&&t.maybeComponent(e))&&(i=Xa(e,t));var r=e.inlineTemplate?null:Ja(e,t,!0);n="_c('"+e.tag+"'"+(i?","+i:"")+(r?","+r:"")+")"}for(var o=0;o>>0}(a):"")+")"}(e,e.scopedSlots,t)+","),e.model&&(n+="model:{value:"+e.model.value+",callback:"+e.model.callback+",expression:"+e.model.expression+"},"),e.inlineTemplate){var o=function(e,t){var n=e.children[0];0;if(n&&1===n.type){var i=Ha(n,t.options);return"inlineTemplate:{render:function(){"+i.render+"},staticRenderFns:["+i.staticRenderFns.map(function(e){return"function(){"+e+"}"}).join(",")+"]}"}}(e,t);o&&(n+=o+",")}return n=n.replace(/,$/,"")+"}",e.dynamicAttrs&&(n="_b("+n+',"'+e.tag+'",'+ts(e.dynamicAttrs)+")"),e.wrapData&&(n=e.wrapData(n)),e.wrapListeners&&(n=e.wrapListeners(n)),n}function Ka(e){return 1===e.type&&("slot"===e.tag||e.children.some(Ka))}function Za(e,t){var n=e.attrsMap["slot-scope"];if(e.if&&!e.ifProcessed&&!n)return Ua(e,t,Za,"null");if(e.for&&!e.forProcessed)return Ya(e,t,Za);var i=e.slotScope===pa?"":String(e.slotScope),r="function("+i+"){return "+("template"===e.tag?e.if&&n?"("+e.if+")?"+(Ja(e,t)||"undefined")+":undefined":Ja(e,t)||"undefined":Wa(e,t))+"}",o=i?"":",proxy:true";return"{key:"+(e.slotTarget||'"default"')+",fn:"+r+o+"}"}function Ja(e,t,n,i,r){var o=e.children;if(o.length){var a=o[0];if(1===o.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=n?t.maybeComponent(a)?",1":",0":"";return""+(i||Wa)(a,t)+s}var l=n?function(e,t){for(var n=0,i=0;i':'',as.innerHTML.indexOf("
")>0}var cs=!!U&&us(!1),hs=!!U&&us(!0),ds=_(function(e){var t=ni(e);return t&&t.innerHTML}),fs=Tn.prototype.$mount;Tn.prototype.$mount=function(e,t){if((e=e&&ni(e))===document.body||e===document.documentElement)return this;var n=this.$options;if(!n.render){var i=n.template;if(i)if("string"==typeof i)"#"===i.charAt(0)&&(i=ds(i));else{if(!i.nodeType)return this;i=i.innerHTML}else e&&(i=function(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}(e));if(i){0;var r=ls(i,{outputSourceRange:!1,shouldDecodeNewlines:cs,shouldDecodeNewlinesForHref:hs,delimiters:n.delimiters,comments:n.comments},this),o=r.render,a=r.staticRenderFns;n.render=o,n.staticRenderFns=a}}return fs.call(this,e,t)},Tn.compile=ls,t.default=Tn}.call(t,n("DuR2"))},"77Pl":function(e,t,n){var i=n("EqjI");e.exports=function(e){if(!i(e))throw TypeError(e+" is not an object!");return e}},"7J9s":function(e,t,n){"use strict";t.__esModule=!0,t.PopupManager=void 0;var i=l(n("7+uW")),r=l(n("jmaC")),o=l(n("OAzY")),a=l(n("6Twh")),s=n("2kvA");function l(e){return e&&e.__esModule?e:{default:e}}var u=1,c=void 0;t.default={props:{visible:{type:Boolean,default:!1},openDelay:{},closeDelay:{},zIndex:{},modal:{type:Boolean,default:!1},modalFade:{type:Boolean,default:!0},modalClass:{},modalAppendToBody:{type:Boolean,default:!1},lockScroll:{type:Boolean,default:!0},closeOnPressEscape:{type:Boolean,default:!1},closeOnClickModal:{type:Boolean,default:!1}},beforeMount:function(){this._popupId="popup-"+u++,o.default.register(this._popupId,this)},beforeDestroy:function(){o.default.deregister(this._popupId),o.default.closeModal(this._popupId),this.restoreBodyStyle()},data:function(){return{opened:!1,bodyPaddingRight:null,computedBodyPaddingRight:0,withoutHiddenClass:!0,rendered:!1}},watch:{visible:function(e){var t=this;if(e){if(this._opening)return;this.rendered?this.open():(this.rendered=!0,i.default.nextTick(function(){t.open()}))}else this.close()}},methods:{open:function(e){var t=this;this.rendered||(this.rendered=!0);var n=(0,r.default)({},this.$props||this,e);this._closeTimer&&(clearTimeout(this._closeTimer),this._closeTimer=null),clearTimeout(this._openTimer);var i=Number(n.openDelay);i>0?this._openTimer=setTimeout(function(){t._openTimer=null,t.doOpen(n)},i):this.doOpen(n)},doOpen:function(e){if(!this.$isServer&&(!this.willOpen||this.willOpen())&&!this.opened){this._opening=!0;var t=this.$el,n=e.modal,i=e.zIndex;if(i&&(o.default.zIndex=i),n&&(this._closing&&(o.default.closeModal(this._popupId),this._closing=!1),o.default.openModal(this._popupId,o.default.nextZIndex(),this.modalAppendToBody?void 0:t,e.modalClass,e.modalFade),e.lockScroll)){this.withoutHiddenClass=!(0,s.hasClass)(document.body,"el-popup-parent--hidden"),this.withoutHiddenClass&&(this.bodyPaddingRight=document.body.style.paddingRight,this.computedBodyPaddingRight=parseInt((0,s.getStyle)(document.body,"paddingRight"),10)),c=(0,a.default)();var r=document.documentElement.clientHeight0&&(r||"scroll"===l)&&this.withoutHiddenClass&&(document.body.style.paddingRight=this.computedBodyPaddingRight+c+"px"),(0,s.addClass)(document.body,"el-popup-parent--hidden")}"static"===getComputedStyle(t).position&&(t.style.position="absolute"),t.style.zIndex=o.default.nextZIndex(),this.opened=!0,this.onOpen&&this.onOpen(),this.doAfterOpen()}},doAfterOpen:function(){this._opening=!1},close:function(){var e=this;if(!this.willClose||this.willClose()){null!==this._openTimer&&(clearTimeout(this._openTimer),this._openTimer=null),clearTimeout(this._closeTimer);var t=Number(this.closeDelay);t>0?this._closeTimer=setTimeout(function(){e._closeTimer=null,e.doClose()},t):this.doClose()}},doClose:function(){this._closing=!0,this.onClose&&this.onClose(),this.lockScroll&&setTimeout(this.restoreBodyStyle,200),this.opened=!1,this.doAfterClose()},doAfterClose:function(){o.default.closeModal(this._popupId),this._closing=!1},restoreBodyStyle:function(){this.modal&&this.withoutHiddenClass&&(document.body.style.paddingRight=this.bodyPaddingRight,(0,s.removeClass)(document.body,"el-popup-parent--hidden")),this.withoutHiddenClass=!0}}},t.PopupManager=o.default},"7KvD":function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},"7UMu":function(e,t,n){var i=n("R9M2");e.exports=Array.isArray||function(e){return"Array"==i(e)}},"7XrG":function(e,t,n){var i=n("Icdr").extendComponentModel({type:"tooltip",dependencies:["axisPointer"],defaultOption:{zlevel:0,z:60,show:!0,showContent:!0,trigger:"item",triggerOn:"mousemove|click",alwaysShowContent:!1,displayMode:"single",renderMode:"auto",confine:!1,showDelay:0,hideDelay:100,transitionDuration:.4,enterable:!1,backgroundColor:"rgba(50,50,50,0.7)",borderColor:"#333",borderRadius:4,borderWidth:0,padding:5,extraCssText:"",axisPointer:{type:"line",axis:"auto",animation:"auto",animationDurationUpdate:200,animationEasingUpdate:"exponentialOut",crossStyle:{color:"#999",width:1,type:"dashed",textStyle:{}}},textStyle:{color:"#fff",fontSize:14}}});e.exports=i},"7Xsf":function(e,t,n){(function(e){"use strict";var t=e.commands,n=e.Pos;function i(t,i){t.extendSelectionsBy(function(r){return t.display.shift||t.doc.extend||r.empty()?function(t,i,r){if(r<0&&0==i.ch)return t.clipPos(n(i.line-1));var o=t.getLine(i.line);if(r>0&&i.ch>=o.length)return t.clipPos(n(i.line+1,0));for(var a,s="start",l=i.ch,u=l,c=r<0?0:o.length,h=0;u!=c;u+=r,h++){var d=o.charAt(r<0?u-1:u),f="_"!=d&&e.isWordChar(d)?"w":"o";if("w"==f&&d.toUpperCase()==d&&(f="W"),"start"==s)"o"!=f?(s="in",a=f):l=u+r;else if("in"==s&&a!=f){if("w"==a&&"W"==f&&r<0&&u--,"W"==a&&"w"==f&&r>0){if(u==l+1){a="w";continue}u--}break}}return n(i.line,u)}(t.doc,r.head,i):i<0?r.from():r.to()})}function r(t,i){if(t.isReadOnly())return e.Pass;t.operation(function(){for(var e=t.listSelections().length,r=[],o=-1,a=0;a=n&&e.execCommand("goLineUp")}e.scrollTo(null,t.top-e.defaultTextHeight())},t.scrollLineDown=function(e){var t=e.getScrollInfo();if(!e.somethingSelected()){var n=e.lineAtHeight(t.top,"local")+1;e.getCursor().line<=n&&e.execCommand("goLineDown")}e.scrollTo(null,t.top+e.defaultTextHeight())},t.splitSelectionByLine=function(e){for(var t=e.listSelections(),i=[],r=0;ro.line&&s==a.line&&0==a.ch||i.push({anchor:s==o.line?o:n(s,0),head:s==a.line?a:n(s)});e.setSelections(i,0)},t.singleSelectionTop=function(e){var t=e.listSelections()[0];e.setSelection(t.anchor,t.head,{scroll:!1})},t.selectLine=function(e){for(var t=e.listSelections(),i=[],r=0;r=0;s--){var u=i[r[s]];if(!(l&&e.cmpPos(u.head,l)>0)){var c=o(t,u.head);l=c.from,t.replaceRange(n(c.word),c.from,c.to)}}})}function d(t){var n=t.getCursor("from"),i=t.getCursor("to");if(0==e.cmpPos(n,i)){var r=o(t,n);if(!r.word)return;n=r.from,i=r.to}return{from:n,to:i,query:t.getRange(n,i),word:r}}function f(e,t){var i=d(e);if(i){var r=i.query,o=e.getSearchCursor(r,t?i.to:i.from);(t?o.findNext():o.findPrevious())?e.setSelection(o.from(),o.to()):(o=e.getSearchCursor(r,t?n(e.firstLine(),0):e.clipPos(n(e.lastLine()))),(t?o.findNext():o.findPrevious())?e.setSelection(o.from(),o.to()):i.word&&e.setSelection(i.from,i.to))}}t.selectScope=function(e){l(e)||e.execCommand("selectAll")},t.selectBetweenBrackets=function(t){if(!l(t))return e.Pass},t.goToBracket=function(t){t.extendSelectionsBy(function(i){var r=t.scanForBracket(i.head,1,u(t.getTokenTypeAt(i.head)));if(r&&0!=e.cmpPos(r.pos,i.head))return r.pos;var o=t.scanForBracket(i.head,-1,u(t.getTokenTypeAt(n(i.head.line,i.head.ch+1))));return o&&n(o.pos.line,o.pos.ch+1)||i.head})},t.swapLineUp=function(t){if(t.isReadOnly())return e.Pass;for(var i=t.listSelections(),r=[],o=t.firstLine()-1,a=[],s=0;so?r.push(u,c):r.length&&(r[r.length-1]=c),o=c}t.operation(function(){for(var e=0;et.lastLine()?t.replaceRange("\n"+s,n(t.lastLine()),null,"+swapLine"):t.replaceRange(s+"\n",n(o,0),null,"+swapLine")}t.setSelections(a),t.scrollIntoView()})},t.swapLineDown=function(t){if(t.isReadOnly())return e.Pass;for(var i=t.listSelections(),r=[],o=t.lastLine()+1,a=i.length-1;a>=0;a--){var s=i[a],l=s.to().line+1,u=s.from().line;0!=s.to().ch||s.empty()||l--,l=0;e-=2){var i=r[e],o=r[e+1],a=t.getLine(i);i==t.lastLine()?t.replaceRange("",n(i-1),n(i),"+swapLine"):t.replaceRange("",n(i,0),n(i+1,0),"+swapLine"),t.replaceRange(a+"\n",n(o,0),null,"+swapLine")}t.scrollIntoView()})},t.toggleCommentIndented=function(e){e.toggleComment({indent:!0})},t.joinLines=function(e){for(var t=e.listSelections(),i=[],r=0;r=0;o--){var a=i[o].head,s=t.getRange({line:a.line,ch:0},a),l=e.countColumn(s,null,t.getOption("tabSize")),u=t.findPosH(a,-1,"char",!1);if(s&&!/\S/.test(s)&&l%r==0){var c=new n(a.line,e.findColumn(s,l-r,r));c.ch!=a.ch&&(u=c)}t.replaceRange("",u,a,"+delete")}})},t.delLineRight=function(e){e.operation(function(){for(var t=e.listSelections(),i=t.length-1;i>=0;i--)e.replaceRange("",t[i].anchor,n(t[i].to().line),"+delete");e.scrollIntoView()})},t.upcaseAtCursor=function(e){h(e,function(e){return e.toUpperCase()})},t.downcaseAtCursor=function(e){h(e,function(e){return e.toLowerCase()})},t.setSublimeMark=function(e){e.state.sublimeMark&&e.state.sublimeMark.clear(),e.state.sublimeMark=e.setBookmark(e.getCursor())},t.selectToSublimeMark=function(e){var t=e.state.sublimeMark&&e.state.sublimeMark.find();t&&e.setSelection(e.getCursor(),t)},t.deleteToSublimeMark=function(t){var n=t.state.sublimeMark&&t.state.sublimeMark.find();if(n){var i=t.getCursor(),r=n;if(e.cmpPos(i,r)>0){var o=r;r=i,i=o}t.state.sublimeKilled=t.getRange(i,r),t.replaceRange("",i,r)}},t.swapWithSublimeMark=function(e){var t=e.state.sublimeMark&&e.state.sublimeMark.find();t&&(e.state.sublimeMark.clear(),e.state.sublimeMark=e.setBookmark(e.getCursor()),e.setCursor(t))},t.sublimeYank=function(e){null!=e.state.sublimeKilled&&e.replaceSelection(e.state.sublimeKilled,null,"paste")},t.showInCenter=function(e){var t=e.cursorCoords(null,"local");e.scrollTo(null,(t.top+t.bottom)/2-e.getScrollInfo().clientHeight/2)},t.findUnder=function(e){f(e,!0)},t.findUnderPrevious=function(e){f(e,!1)},t.findAllUnder=function(e){var t=d(e);if(t){for(var n=e.getSearchCursor(t.query),i=[],r=-1;n.findNext();)i.push({anchor:n.from(),head:n.to()}),n.from().line<=t.from.line&&n.from().ch<=t.from.ch&&r++;e.setSelections(i,r)}};var p=e.keyMap;p.macSublime={"Cmd-Left":"goLineStartSmart","Shift-Tab":"indentLess","Shift-Ctrl-K":"deleteLine","Alt-Q":"wrapLines","Ctrl-Left":"goSubwordLeft","Ctrl-Right":"goSubwordRight","Ctrl-Alt-Up":"scrollLineUp","Ctrl-Alt-Down":"scrollLineDown","Cmd-L":"selectLine","Shift-Cmd-L":"splitSelectionByLine",Esc:"singleSelectionTop","Cmd-Enter":"insertLineAfter","Shift-Cmd-Enter":"insertLineBefore","Cmd-D":"selectNextOccurrence","Shift-Cmd-Space":"selectScope","Shift-Cmd-M":"selectBetweenBrackets","Cmd-M":"goToBracket","Cmd-Ctrl-Up":"swapLineUp","Cmd-Ctrl-Down":"swapLineDown","Cmd-/":"toggleCommentIndented","Cmd-J":"joinLines","Shift-Cmd-D":"duplicateLine",F5:"sortLines","Cmd-F5":"sortLinesInsensitive",F2:"nextBookmark","Shift-F2":"prevBookmark","Cmd-F2":"toggleBookmark","Shift-Cmd-F2":"clearBookmarks","Alt-F2":"selectBookmarks",Backspace:"smartBackspace","Cmd-K Cmd-D":"skipAndSelectNextOccurrence","Cmd-K Cmd-K":"delLineRight","Cmd-K Cmd-U":"upcaseAtCursor","Cmd-K Cmd-L":"downcaseAtCursor","Cmd-K Cmd-Space":"setSublimeMark","Cmd-K Cmd-A":"selectToSublimeMark","Cmd-K Cmd-W":"deleteToSublimeMark","Cmd-K Cmd-X":"swapWithSublimeMark","Cmd-K Cmd-Y":"sublimeYank","Cmd-K Cmd-C":"showInCenter","Cmd-K Cmd-G":"clearBookmarks","Cmd-K Cmd-Backspace":"delLineLeft","Cmd-K Cmd-1":"foldAll","Cmd-K Cmd-0":"unfoldAll","Cmd-K Cmd-J":"unfoldAll","Ctrl-Shift-Up":"addCursorToPrevLine","Ctrl-Shift-Down":"addCursorToNextLine","Cmd-F3":"findUnder","Shift-Cmd-F3":"findUnderPrevious","Alt-F3":"findAllUnder","Shift-Cmd-[":"fold","Shift-Cmd-]":"unfold","Cmd-I":"findIncremental","Shift-Cmd-I":"findIncrementalReverse","Cmd-H":"replace",F3:"findNext","Shift-F3":"findPrev",fallthrough:"macDefault"},e.normalizeKeyMap(p.macSublime),p.pcSublime={"Shift-Tab":"indentLess","Shift-Ctrl-K":"deleteLine","Alt-Q":"wrapLines","Ctrl-T":"transposeChars","Alt-Left":"goSubwordLeft","Alt-Right":"goSubwordRight","Ctrl-Up":"scrollLineUp","Ctrl-Down":"scrollLineDown","Ctrl-L":"selectLine","Shift-Ctrl-L":"splitSelectionByLine",Esc:"singleSelectionTop","Ctrl-Enter":"insertLineAfter","Shift-Ctrl-Enter":"insertLineBefore","Ctrl-D":"selectNextOccurrence","Shift-Ctrl-Space":"selectScope","Shift-Ctrl-M":"selectBetweenBrackets","Ctrl-M":"goToBracket","Shift-Ctrl-Up":"swapLineUp","Shift-Ctrl-Down":"swapLineDown","Ctrl-/":"toggleCommentIndented","Ctrl-J":"joinLines","Shift-Ctrl-D":"duplicateLine",F9:"sortLines","Ctrl-F9":"sortLinesInsensitive",F2:"nextBookmark","Shift-F2":"prevBookmark","Ctrl-F2":"toggleBookmark","Shift-Ctrl-F2":"clearBookmarks","Alt-F2":"selectBookmarks",Backspace:"smartBackspace","Ctrl-K Ctrl-D":"skipAndSelectNextOccurrence","Ctrl-K Ctrl-K":"delLineRight","Ctrl-K Ctrl-U":"upcaseAtCursor","Ctrl-K Ctrl-L":"downcaseAtCursor","Ctrl-K Ctrl-Space":"setSublimeMark","Ctrl-K Ctrl-A":"selectToSublimeMark","Ctrl-K Ctrl-W":"deleteToSublimeMark","Ctrl-K Ctrl-X":"swapWithSublimeMark","Ctrl-K Ctrl-Y":"sublimeYank","Ctrl-K Ctrl-C":"showInCenter","Ctrl-K Ctrl-G":"clearBookmarks","Ctrl-K Ctrl-Backspace":"delLineLeft","Ctrl-K Ctrl-1":"foldAll","Ctrl-K Ctrl-0":"unfoldAll","Ctrl-K Ctrl-J":"unfoldAll","Ctrl-Alt-Up":"addCursorToPrevLine","Ctrl-Alt-Down":"addCursorToNextLine","Ctrl-F3":"findUnder","Shift-Ctrl-F3":"findUnderPrevious","Alt-F3":"findAllUnder","Shift-Ctrl-[":"fold","Shift-Ctrl-]":"unfold","Ctrl-I":"findIncremental","Shift-Ctrl-I":"findIncrementalReverse","Ctrl-H":"replace",F3:"findNext","Shift-F3":"findPrev",fallthrough:"pcDefault"},e.normalizeKeyMap(p.pcSublime);var g=p.default==p.macDefault;p.sublime=g?p.macSublime:p.pcSublime})(n("8U58"),n("c+I8"),n("vq+x"))},"7bL3":function(e,t,n){var i=n("Icdr");n("4V7L"),n("8DFW"),n("6HoR");var r=n("nQkE"),o=n("h6Uy"),a=n("l4Op");i.registerLayout(r),i.registerVisual(o),i.registerProcessor(a("themeRiver"))},"7t+N":function(e,t,n){var i;
/*!
* jQuery JavaScript Library v3.5.1
* https://jquery.com/
*
* Includes Sizzle.js
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2020-05-04T22:49Z
*/
/*!
* jQuery JavaScript Library v3.5.1
* https://jquery.com/
*
* Includes Sizzle.js
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2020-05-04T22:49Z
*/
!function(t,n){"use strict";"object"==typeof e&&"object"==typeof e.exports?e.exports=t.document?n(t,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return n(e)}:n(t)}("undefined"!=typeof window?window:this,function(n,r){"use strict";var o=[],a=Object.getPrototypeOf,s=o.slice,l=o.flat?function(e){return o.flat.call(e)}:function(e){return o.concat.apply([],e)},u=o.push,c=o.indexOf,h={},d=h.toString,f=h.hasOwnProperty,p=f.toString,g=p.call(Object),m={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},y=function(e){return null!=e&&e===e.window},b=n.document,x={type:!0,src:!0,nonce:!0,noModule:!0};function _(e,t,n){var i,r,o=(n=n||b).createElement("script");if(o.text=e,t)for(i in x)(r=t[i]||t.getAttribute&&t.getAttribute(i))&&o.setAttribute(i,r);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?h[d.call(e)]||"object":typeof e}var C=function(e,t){return new C.fn.init(e,t)};function S(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}C.fn=C.prototype={jquery:"3.5.1",constructor:C,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=C.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return C.each(this,e)},map:function(e){return this.pushStack(C.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(C.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(C.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|"+B+")"+B+"*"),q=new RegExp(B+"|>"),G=new RegExp($),U=new RegExp("^"+z+"$"),Y={ID:new RegExp("^#("+z+")"),CLASS:new RegExp("^\\.("+z+")"),TAG:new RegExp("^("+z+"|[*])"),ATTR:new RegExp("^"+F),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+B+"*(even|odd|(([+-]|)(\\d*)n|)"+B+"*(?:([+-]|)"+B+"*(\\d+)|))"+B+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+B+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+B+"*((?:-\\d)?\\d*)"+B+"*\\)|)(?=[^-]|$)","i")},X=/HTML$/i,K=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,J=/^[^{]+\{\s*\[native \w/,Q=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+B+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},ie=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,re=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){d()},ae=xe(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{L.apply(I=P.call(_.childNodes),_.childNodes),I[_.childNodes.length].nodeType}catch(e){L={apply:I.length?function(e,t){E.apply(e,P.call(t))}:function(e,t){for(var n=e.length,i=0;e[n++]=t[i++];);e.length=n-1}}}function se(e,t,i,r){var o,s,u,c,h,p,v,y=t&&t.ownerDocument,_=t?t.nodeType:9;if(i=i||[],"string"!=typeof e||!e||1!==_&&9!==_&&11!==_)return i;if(!r&&(d(t),t=t||f,g)){if(11!==_&&(h=Q.exec(e)))if(o=h[1]){if(9===_){if(!(u=t.getElementById(o)))return i;if(u.id===o)return i.push(u),i}else if(y&&(u=y.getElementById(o))&&b(t,u)&&u.id===o)return i.push(u),i}else{if(h[2])return L.apply(i,t.getElementsByTagName(e)),i;if((o=h[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(i,t.getElementsByClassName(o)),i}if(n.qsa&&!M[e+" "]&&(!m||!m.test(e))&&(1!==_||"object"!==t.nodeName.toLowerCase())){if(v=e,y=t,1===_&&(q.test(e)||W.test(e))){for((y=ee.test(e)&&ve(t.parentNode)||t)===t&&n.scope||((c=t.getAttribute("id"))?c=c.replace(ie,re):t.setAttribute("id",c=x)),s=(p=a(e)).length;s--;)p[s]=(c?"#"+c:":scope")+" "+be(p[s]);v=p.join(",")}try{return L.apply(i,y.querySelectorAll(v)),i}catch(t){M(e,!0)}finally{c===x&&t.removeAttribute("id")}}}return l(e.replace(j,"$1"),t,i,r)}function le(){var e=[];return function t(n,r){return e.push(n+" ")>i.cacheLength&&delete t[e.shift()],t[n+" "]=r}}function ue(e){return e[x]=!0,e}function ce(e){var t=f.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function he(e,t){for(var n=e.split("|"),r=n.length;r--;)i.attrHandle[n[r]]=t}function de(e,t){var n=t&&e,i=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(i)return i;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ge(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ae(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function me(e){return ue(function(t){return t=+t,ue(function(n,i){for(var r,o=e([],n.length,t),a=o.length;a--;)n[r=o[a]]&&(n[r]=!(i[r]=n[r]))})})}function ve(e){return e&&void 0!==e.getElementsByTagName&&e}for(t in n=se.support={},o=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!X.test(t||n&&n.nodeName||"HTML")},d=se.setDocument=function(e){var t,r,a=e?e.ownerDocument||e:_;return a!=f&&9===a.nodeType&&a.documentElement?(p=(f=a).documentElement,g=!o(f),_!=f&&(r=f.defaultView)&&r.top!==r&&(r.addEventListener?r.addEventListener("unload",oe,!1):r.attachEvent&&r.attachEvent("onunload",oe)),n.scope=ce(function(e){return p.appendChild(e).appendChild(f.createElement("div")),void 0!==e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),n.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ce(function(e){return e.appendChild(f.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=J.test(f.getElementsByClassName),n.getById=ce(function(e){return p.appendChild(e).id=x,!f.getElementsByName||!f.getElementsByName(x).length}),n.getById?(i.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},i.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(i.filter.ID=function(e){var t=e.replace(te,ne);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},i.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n,i,r,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(r=t.getElementsByName(e),i=0;o=r[i++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),i.find.TAG=n.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,i=[],r=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[r++];)1===n.nodeType&&i.push(n);return i}return o},i.find.CLASS=n.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],m=[],(n.qsa=J.test(f.querySelectorAll))&&(ce(function(e){var t;p.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&m.push("[*^$]="+B+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||m.push("\\["+B+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+x+"-]").length||m.push("~="),(t=f.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||m.push("\\["+B+"*name"+B+"*="+B+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||m.push(":checked"),e.querySelectorAll("a#"+x+"+*").length||m.push(".#.+[+~]"),e.querySelectorAll("\\\f"),m.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=f.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&m.push("name"+B+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&m.push(":enabled",":disabled"),p.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&m.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),m.push(",.*:")})),(n.matchesSelector=J.test(y=p.matches||p.webkitMatchesSelector||p.mozMatchesSelector||p.oMatchesSelector||p.msMatchesSelector))&&ce(function(e){n.disconnectedMatch=y.call(e,"*"),y.call(e,"[s!='']:x"),v.push("!=",$)}),m=m.length&&new RegExp(m.join("|")),v=v.length&&new RegExp(v.join("|")),t=J.test(p.compareDocumentPosition),b=t||J.test(p.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,i=t&&t.parentNode;return e===i||!(!i||1!==i.nodeType||!(n.contains?n.contains(i):e.compareDocumentPosition&&16&e.compareDocumentPosition(i)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return h=!0,0;var i=!e.compareDocumentPosition-!t.compareDocumentPosition;return i||(1&(i=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===i?e==f||e.ownerDocument==_&&b(_,e)?-1:t==f||t.ownerDocument==_&&b(_,t)?1:c?N(c,e)-N(c,t):0:4&i?-1:1)}:function(e,t){if(e===t)return h=!0,0;var n,i=0,r=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!r||!o)return e==f?-1:t==f?1:r?-1:o?1:c?N(c,e)-N(c,t):0;if(r===o)return de(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;a[i]===s[i];)i++;return i?de(a[i],s[i]):a[i]==_?-1:s[i]==_?1:0},f):f},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(d(e),n.matchesSelector&&g&&!M[t+" "]&&(!v||!v.test(t))&&(!m||!m.test(t)))try{var i=y.call(e,t);if(i||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return i}catch(e){M(t,!0)}return se(t,f,null,[e]).length>0},se.contains=function(e,t){return(e.ownerDocument||e)!=f&&d(e),b(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=f&&d(e);var r=i.attrHandle[t.toLowerCase()],o=r&&A.call(i.attrHandle,t.toLowerCase())?r(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},se.escape=function(e){return(e+"").replace(ie,re)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,i=[],r=0,o=0;if(h=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),h){for(;t=e[o++];)t===e[o]&&(r=i.push(o));for(;r--;)e.splice(i[r],1)}return c=null,e},r=se.getText=function(e){var t,n="",i=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=r(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[i++];)n+=r(t);return n},(i=se.selectors={cacheLength:50,createPseudo:ue,match:Y,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Y.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&G.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=S[e+" "];return t||(t=new RegExp("(^|"+B+")"+e+"("+B+"|$)"))&&S(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(i){var r=se.attr(i,e);return null==r?"!="===t:!t||(r+="","="===t?r===n:"!="===t?r!==n:"^="===t?n&&0===r.indexOf(n):"*="===t?n&&r.indexOf(n)>-1:"$="===t?n&&r.slice(-n.length)===n:"~="===t?(" "+r.replace(V," ")+" ").indexOf(n)>-1:"|="===t&&(r===n||r.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,i,r){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===i&&0===r?function(e){return!!e.parentNode}:function(t,n,l){var u,c,h,d,f,p,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!l&&!s,b=!1;if(m){if(o){for(;g;){for(d=t;d=d[g];)if(s?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;p=g="only"===e&&!p&&"nextSibling"}return!0}if(p=[a?m.firstChild:m.lastChild],a&&y){for(b=(f=(u=(c=(h=(d=m)[x]||(d[x]={}))[d.uniqueID]||(h[d.uniqueID]={}))[e]||[])[0]===w&&u[1])&&u[2],d=f&&m.childNodes[f];d=++f&&d&&d[g]||(b=f=0)||p.pop();)if(1===d.nodeType&&++b&&d===t){c[e]=[w,f,b];break}}else if(y&&(b=f=(u=(c=(h=(d=t)[x]||(d[x]={}))[d.uniqueID]||(h[d.uniqueID]={}))[e]||[])[0]===w&&u[1]),!1===b)for(;(d=++f&&d&&d[g]||(b=f=0)||p.pop())&&((s?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++b||(y&&((c=(h=d[x]||(d[x]={}))[d.uniqueID]||(h[d.uniqueID]={}))[e]=[w,b]),d!==t)););return(b-=r)===i||b%i==0&&b/i>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ue(function(e,n){for(var i,o=r(e,t),a=o.length;a--;)e[i=N(e,o[a])]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ue(function(e){var t=[],n=[],i=s(e.replace(j,"$1"));return i[x]?ue(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:ue(function(e){return function(t){return se(e,t).length>0}}),contains:ue(function(e){return e=e.replace(te,ne),function(t){return(t.textContent||r(t)).indexOf(e)>-1}}),lang:ue(function(e){return U.test(e||"")||se.error("unsupported lang: "+e),e=e.replace(te,ne).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===p},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Z.test(e.nodeName)},input:function(e){return K.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:me(function(){return[0]}),last:me(function(e,t){return[t-1]}),eq:me(function(e,t,n){return[n<0?n+t:n]}),even:me(function(e,t){for(var n=0;nt?t:n;--i>=0;)e.push(i);return e}),gt:me(function(e,t,n){for(var i=n<0?n+t:n;++i1?function(t,n,i){for(var r=e.length;r--;)if(!e[r](t,n,i))return!1;return!0}:e[0]}function we(e,t,n,i,r){for(var o,a=[],s=0,l=e.length,u=null!=t;s-1&&(o[u]=!(a[u]=h))}}else v=we(v===a?v.splice(p,v.length):v),r?r(null,a,v,l):L.apply(a,v)})}function Se(e){for(var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],l=a?1:0,c=xe(function(e){return e===t},s,!0),h=xe(function(e){return N(t,e)>-1},s,!0),d=[function(e,n,i){var r=!a&&(i||n!==u)||((t=n).nodeType?c(e,n,i):h(e,n,i));return t=null,r}];l1&&_e(d),l>1&&be(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(j,"$1"),n,l0,r=e.length>0,o=function(o,a,s,l,c){var h,p,m,v=0,y="0",b=o&&[],x=[],_=u,C=o||r&&i.find.TAG("*",c),S=w+=null==_?1:Math.random()||.1,k=C.length;for(c&&(u=a==f||a||c);y!==k&&null!=(h=C[y]);y++){if(r&&h){for(p=0,a||h.ownerDocument==f||(d(h),s=!g);m=e[p++];)if(m(h,a||f,s)){l.push(h);break}c&&(w=S)}n&&((h=!m&&h)&&v--,o&&b.push(h))}if(v+=y,n&&y!==v){for(p=0;m=t[p++];)m(b,x,a,s);if(o){if(v>0)for(;y--;)b[y]||x[y]||(x[y]=O.call(l));x=we(x)}L.apply(l,x),c&&!o&&x.length>0&&v+t.length>1&&se.uniqueSort(l)}return c&&(w=S,u=_),b};return n?ue(o):o}(o,r))).selector=e}return s},l=se.select=function(e,t,n,r){var o,l,u,c,h,d="function"==typeof e&&e,f=!r&&a(e=d.selector||e);if(n=n||[],1===f.length){if((l=f[0]=f[0].slice(0)).length>2&&"ID"===(u=l[0]).type&&9===t.nodeType&&g&&i.relative[l[1].type]){if(!(t=(i.find.ID(u.matches[0].replace(te,ne),t)||[])[0]))return n;d&&(t=t.parentNode),e=e.slice(l.shift().value.length)}for(o=Y.needsContext.test(e)?0:l.length;o--&&(u=l[o],!i.relative[c=u.type]);)if((h=i.find[c])&&(r=h(u.matches[0].replace(te,ne),ee.test(l[0].type)&&ve(t.parentNode)||t))){if(l.splice(o,1),!(e=r.length&&be(l)))return L.apply(n,r),n;break}}return(d||s(e,f))(r,t,!g,n,!t||ee.test(e)&&ve(t.parentNode)||t),n},n.sortStable=x.split("").sort(D).join("")===x,n.detectDuplicates=!!h,d(),n.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(f.createElement("fieldset"))}),ce(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||he("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ce(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||he("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||he(R,function(e,t,n){var i;if(!n)return!0===e[t]?t.toLowerCase():(i=e.getAttributeNode(t))&&i.specified?i.value:null}),se}(n);C.find=k,C.expr=k.selectors,C.expr[":"]=C.expr.pseudos,C.uniqueSort=C.unique=k.uniqueSort,C.text=k.getText,C.isXMLDoc=k.isXML,C.contains=k.contains,C.escapeSelector=k.escape;var T=function(e,t,n){for(var i=[],r=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(r&&C(e).is(n))break;i.push(e)}return i},M=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=C.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var I=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function O(e,t,n){return v(t)?C.grep(e,function(e,i){return!!t.call(e,i,e)!==n}):t.nodeType?C.grep(e,function(e){return e===t!==n}):"string"!=typeof t?C.grep(e,function(e){return c.call(t,e)>-1!==n}):C.filter(t,e,n)}C.filter=function(e,t,n){var i=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===i.nodeType?C.find.matchesSelector(i,e)?[i]:[]:C.find.matches(e,C.grep(t,function(e){return 1===e.nodeType}))},C.fn.extend({find:function(e){var t,n,i=this.length,r=this;if("string"!=typeof e)return this.pushStack(C(e).filter(function(){for(t=0;t1?C.uniqueSort(n):n},filter:function(e){return this.pushStack(O(this,e||[],!1))},not:function(e){return this.pushStack(O(this,e||[],!0))},is:function(e){return!!O(this,"string"==typeof e&&D.test(e)?C(e):e||[],!1).length}});var E,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(C.fn.init=function(e,t,n){var i,r;if(!e)return this;if(n=n||E,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof C?t[0]:t,C.merge(this,C.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:b,!0)),I.test(i[1])&&C.isPlainObject(t))for(i in t)v(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(r=b.getElementById(i[2]))&&(this[0]=r,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(C):C.makeArray(e,this)}).prototype=C.fn,E=C(b);var P=/^(?:parents|prev(?:Until|All))/,N={children:!0,contents:!0,next:!0,prev:!0};function R(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}C.fn.extend({has:function(e){var t=C(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&C.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?C.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?c.call(C(e),this[0]):c.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(C.uniqueSort(C.merge(this.get(),C(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),C.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return T(e,"parentNode")},parentsUntil:function(e,t,n){return T(e,"parentNode",n)},next:function(e){return R(e,"nextSibling")},prev:function(e){return R(e,"previousSibling")},nextAll:function(e){return T(e,"nextSibling")},prevAll:function(e){return T(e,"previousSibling")},nextUntil:function(e,t,n){return T(e,"nextSibling",n)},prevUntil:function(e,t,n){return T(e,"previousSibling",n)},siblings:function(e){return M((e.parentNode||{}).firstChild,e)},children:function(e){return M(e.firstChild)},contents:function(e){return null!=e.contentDocument&&a(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),C.merge([],e.childNodes))}},function(e,t){C.fn[e]=function(n,i){var r=C.map(this,t,n);return"Until"!==e.slice(-5)&&(i=n),i&&"string"==typeof i&&(r=C.filter(i,r)),this.length>1&&(N[e]||C.uniqueSort(r),P.test(e)&&r.reverse()),this.pushStack(r)}});var B=/[^\x20\t\r\n\f]+/g;function z(e){return e}function F(e){throw e}function $(e,t,n,i){var r;try{e&&v(r=e.promise)?r.call(e).done(t).fail(n):e&&v(r=e.then)?r.call(e,t,n):t.apply(void 0,[e].slice(i))}catch(e){n.apply(void 0,[e])}}C.Callbacks=function(e){e="string"==typeof e?function(e){var t={};return C.each(e.match(B)||[],function(e,n){t[n]=!0}),t}(e):C.extend({},e);var t,n,i,r,o=[],a=[],s=-1,l=function(){for(r=r||e.once,i=t=!0;a.length;s=-1)for(n=a.shift();++s-1;)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?C.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return r=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return r=a=[],n||t||(o=n=""),this},locked:function(){return!!r},fireWith:function(e,n){return r||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||l()),this},fire:function(){return u.fireWith(this,arguments),this},fired:function(){return!!i}};return u},C.extend({Deferred:function(e){var t=[["notify","progress",C.Callbacks("memory"),C.Callbacks("memory"),2],["resolve","done",C.Callbacks("once memory"),C.Callbacks("once memory"),0,"resolved"],["reject","fail",C.Callbacks("once memory"),C.Callbacks("once memory"),1,"rejected"]],i="pending",r={state:function(){return i},always:function(){return o.done(arguments).fail(arguments),this},catch:function(e){return r.then(null,e)},pipe:function(){var e=arguments;return C.Deferred(function(n){C.each(t,function(t,i){var r=v(e[i[4]])&&e[i[4]];o[i[1]](function(){var e=r&&r.apply(this,arguments);e&&v(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[i[0]+"With"](this,r?[e]:arguments)})}),e=null}).promise()},then:function(e,i,r){var o=0;function a(e,t,i,r){return function(){var s=this,l=arguments,u=function(){var n,u;if(!(e=o&&(i!==F&&(s=void 0,l=[n]),t.rejectWith(s,l))}};e?c():(C.Deferred.getStackHook&&(c.stackTrace=C.Deferred.getStackHook()),n.setTimeout(c))}}return C.Deferred(function(n){t[0][3].add(a(0,n,v(r)?r:z,n.notifyWith)),t[1][3].add(a(0,n,v(e)?e:z)),t[2][3].add(a(0,n,v(i)?i:F))}).promise()},promise:function(e){return null!=e?C.extend(e,r):r}},o={};return C.each(t,function(e,n){var a=n[2],s=n[5];r[n[1]]=a.add,s&&a.add(function(){i=s},t[3-e][2].disable,t[3-e][3].disable,t[0][2].lock,t[0][3].lock),a.add(n[3].fire),o[n[0]]=function(){return o[n[0]+"With"](this===o?void 0:this,arguments),this},o[n[0]+"With"]=a.fireWith}),r.promise(o),e&&e.call(o,o),o},when:function(e){var t=arguments.length,n=t,i=Array(n),r=s.call(arguments),o=C.Deferred(),a=function(e){return function(n){i[e]=this,r[e]=arguments.length>1?s.call(arguments):n,--t||o.resolveWith(i,r)}};if(t<=1&&($(e,o.done(a(n)).resolve,o.reject,!t),"pending"===o.state()||v(r[n]&&r[n].then)))return o.then();for(;n--;)$(r[n],a(n),o.reject);return o.promise()}});var V=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;C.Deferred.exceptionHook=function(e,t){n.console&&n.console.warn&&e&&V.test(e.name)&&n.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},C.readyException=function(e){n.setTimeout(function(){throw e})};var j=C.Deferred();function H(){b.removeEventListener("DOMContentLoaded",H),n.removeEventListener("load",H),C.ready()}C.fn.ready=function(e){return j.then(e).catch(function(e){C.readyException(e)}),this},C.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--C.readyWait:C.isReady)||(C.isReady=!0,!0!==e&&--C.readyWait>0||j.resolveWith(b,[C]))}}),C.ready.then=j.then,"complete"===b.readyState||"loading"!==b.readyState&&!b.documentElement.doScroll?n.setTimeout(C.ready):(b.addEventListener("DOMContentLoaded",H),n.addEventListener("load",H));var W=function(e,t,n,i,r,o,a){var s=0,l=e.length,u=null==n;if("object"===w(n))for(s in r=!0,n)W(e,t,s,n[s],!0,o,a);else if(void 0!==i&&(r=!0,v(i)||(a=!0),u&&(a?(t.call(e,i),t=null):(u=t,t=function(e,t,n){return u.call(C(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){J.remove(this,e)})}}),C.extend({queue:function(e,t,n){var i;if(e)return t=(t||"fx")+"queue",i=Z.get(e,t),n&&(!i||Array.isArray(n)?i=Z.access(e,t,C.makeArray(n)):i.push(n)),i||[]},dequeue:function(e,t){t=t||"fx";var n=C.queue(e,t),i=n.length,r=n.shift(),o=C._queueHooks(e,t);"inprogress"===r&&(r=n.shift(),i--),r&&("fx"===t&&n.unshift("inprogress"),delete o.stop,r.call(e,function(){C.dequeue(e,t)},o)),!i&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Z.get(e,n)||Z.access(e,n,{empty:C.Callbacks("once memory").add(function(){Z.remove(e,[t+"queue",n])})})}}),C.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]*)/i,ve=/^$|^module$|\/(?:java|ecma)script/i;fe=b.createDocumentFragment().appendChild(b.createElement("div")),(pe=b.createElement("input")).setAttribute("type","radio"),pe.setAttribute("checked","checked"),pe.setAttribute("name","t"),fe.appendChild(pe),m.checkClone=fe.cloneNode(!0).cloneNode(!0).lastChild.checked,fe.innerHTML="",m.noCloneChecked=!!fe.cloneNode(!0).lastChild.defaultValue,fe.innerHTML="",m.option=!!fe.lastChild;var ye={thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};function be(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?C.merge([e],n):n}function xe(e,t){for(var n=0,i=e.length;n",""]);var _e=/<|?\w+;/;function we(e,t,n,i,r){for(var o,a,s,l,u,c,h=t.createDocumentFragment(),d=[],f=0,p=e.length;f-1)r&&r.push(o);else if(u=ae(o),a=be(h.appendChild(o),"script"),u&&xe(a),n)for(c=0;o=a[c++];)ve.test(o.type||"")&&n.push(o);return h}var Ce=/^key/,Se=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ke=/^([^.]*)(?:\.(.+)|)/;function Te(){return!0}function Me(){return!1}function De(e,t){return e===function(){try{return b.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,i,r,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(i=i||n,n=void 0),t)Ae(e,s,n,i,t[s],o);return e}if(null==i&&null==r?(r=n,i=n=void 0):null==r&&("string"==typeof n?(r=i,i=void 0):(r=i,i=n,n=void 0)),!1===r)r=Me;else if(!r)return e;return 1===o&&(a=r,(r=function(e){return C().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=C.guid++)),e.each(function(){C.event.add(this,t,r,i,n)})}function Ie(e,t,n){n?(Z.set(e,t,!1),C.event.add(e,t,{namespace:!1,handler:function(e){var i,r,o=Z.get(this,t);if(1&e.isTrigger&&this[t]){if(o.length)(C.event.special[t]||{}).delegateType&&e.stopPropagation();else if(o=s.call(arguments),Z.set(this,t,o),i=n(this,t),this[t](),o!==(r=Z.get(this,t))||i?Z.set(this,t,!1):r={},o!==r)return e.stopImmediatePropagation(),e.preventDefault(),r.value}else o.length&&(Z.set(this,t,{value:C.event.trigger(C.extend(o[0],C.Event.prototype),o.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Z.get(e,t)&&C.event.add(e,t,Te)}C.event={global:{},add:function(e,t,n,i,r){var o,a,s,l,u,c,h,d,f,p,g,m=Z.get(e);if(X(e))for(n.handler&&(n=(o=n).handler,r=o.selector),r&&C.find.matchesSelector(oe,r),n.guid||(n.guid=C.guid++),(l=m.events)||(l=m.events=Object.create(null)),(a=m.handle)||(a=m.handle=function(t){return void 0!==C&&C.event.triggered!==t.type?C.event.dispatch.apply(e,arguments):void 0}),u=(t=(t||"").match(B)||[""]).length;u--;)f=g=(s=ke.exec(t[u])||[])[1],p=(s[2]||"").split(".").sort(),f&&(h=C.event.special[f]||{},f=(r?h.delegateType:h.bindType)||f,h=C.event.special[f]||{},c=C.extend({type:f,origType:g,data:i,handler:n,guid:n.guid,selector:r,needsContext:r&&C.expr.match.needsContext.test(r),namespace:p.join(".")},o),(d=l[f])||((d=l[f]=[]).delegateCount=0,h.setup&&!1!==h.setup.call(e,i,p,a)||e.addEventListener&&e.addEventListener(f,a)),h.add&&(h.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),r?d.splice(d.delegateCount++,0,c):d.push(c),C.event.global[f]=!0)},remove:function(e,t,n,i,r){var o,a,s,l,u,c,h,d,f,p,g,m=Z.hasData(e)&&Z.get(e);if(m&&(l=m.events)){for(u=(t=(t||"").match(B)||[""]).length;u--;)if(f=g=(s=ke.exec(t[u])||[])[1],p=(s[2]||"").split(".").sort(),f){for(h=C.event.special[f]||{},d=l[f=(i?h.delegateType:h.bindType)||f]||[],s=s[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=d.length;o--;)c=d[o],!r&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||i&&i!==c.selector&&("**"!==i||!c.selector)||(d.splice(o,1),c.selector&&d.delegateCount--,h.remove&&h.remove.call(e,c));a&&!d.length&&(h.teardown&&!1!==h.teardown.call(e,p,m.handle)||C.removeEvent(e,f,m.handle),delete l[f])}else for(f in l)C.event.remove(e,f+t[u],n,i,!0);C.isEmptyObject(l)&&Z.remove(e,"handle events")}},dispatch:function(e){var t,n,i,r,o,a,s=new Array(arguments.length),l=C.event.fix(e),u=(Z.get(this,"events")||Object.create(null))[l.type]||[],c=C.event.special[l.type]||{};for(s[0]=l,t=1;t=1))for(;u!==this;u=u.parentNode||this)if(1===u.nodeType&&("click"!==e.type||!0!==u.disabled)){for(o=[],a={},n=0;n-1:C.find(r,this,null,[u]).length),a[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return u=this,l\s*$/g;function Pe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&C(e).children("tbody")[0]||e}function Ne(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Be(e,t){var n,i,r,o,a,s;if(1===t.nodeType){if(Z.hasData(e)&&(s=Z.get(e).events))for(r in Z.remove(t,"handle events"),s)for(n=0,i=s[r].length;n1&&"string"==typeof p&&!m.checkClone&&Ee.test(p))return e.each(function(r){var o=e.eq(r);g&&(t[0]=p.call(this,r,o.html())),ze(o,t,n,i)});if(d&&(o=(r=we(t,e[0].ownerDocument,!1,e,i)).firstChild,1===r.childNodes.length&&(r=o),o||i)){for(s=(a=C.map(be(r,"script"),Ne)).length;h0&&xe(a,!h&&be(e,"script")),c},cleanData:function(e){for(var t,n,i,r=C.event.special,o=0;void 0!==(n=e[o]);o++)if(X(n)){if(t=n[Z.expando]){if(t.events)for(i in t.events)r[i]?C.event.remove(n,i):C.removeEvent(n,i,t.handle);n[Z.expando]=void 0}n[J.expando]&&(n[J.expando]=void 0)}}}),C.fn.extend({detach:function(e){return Fe(this,e,!0)},remove:function(e){return Fe(this,e)},text:function(e){return W(this,function(e){return void 0===e?C.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return ze(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Pe(this,e).appendChild(e)})},prepend:function(){return ze(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Pe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return ze(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return ze(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(C.cleanData(be(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return C.clone(this,e,t)})},html:function(e){return W(this,function(e){var t=this[0]||{},n=0,i=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Oe.test(e)&&!ye[(me.exec(e)||["",""])[1].toLowerCase()]){e=C.htmlPrefilter(e);try{for(;n3,oe.removeChild(e)),s}}))}();var Ge=["Webkit","Moz","ms"],Ue=b.createElement("div").style,Ye={};function Xe(e){var t=C.cssProps[e]||Ye[e];return t||(e in Ue?e:Ye[e]=function(e){for(var t=e[0].toUpperCase()+e.slice(1),n=Ge.length;n--;)if((e=Ge[n]+t)in Ue)return e}(e)||e)}var Ke=/^(none|table(?!-c[ea]).+)/,Ze=/^--/,Je={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function et(e,t,n){var i=ie.exec(t);return i?Math.max(0,i[2]-(n||0))+(i[3]||"px"):t}function tt(e,t,n,i,r,o){var a="width"===t?1:0,s=0,l=0;if(n===(i?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(l+=C.css(e,n+re[a],!0,r)),i?("content"===n&&(l-=C.css(e,"padding"+re[a],!0,r)),"margin"!==n&&(l-=C.css(e,"border"+re[a]+"Width",!0,r))):(l+=C.css(e,"padding"+re[a],!0,r),"padding"!==n?l+=C.css(e,"border"+re[a]+"Width",!0,r):s+=C.css(e,"border"+re[a]+"Width",!0,r));return!i&&o>=0&&(l+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-l-s-.5))||0),l}function nt(e,t,n){var i=Ve(e),r=(!m.boxSizingReliable()||n)&&"border-box"===C.css(e,"boxSizing",!1,i),o=r,a=We(e,t,i),s="offset"+t[0].toUpperCase()+t.slice(1);if($e.test(a)){if(!n)return a;a="auto"}return(!m.boxSizingReliable()&&r||!m.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===C.css(e,"display",!1,i))&&e.getClientRects().length&&(r="border-box"===C.css(e,"boxSizing",!1,i),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+tt(e,t,n||(r?"border":"content"),o,i,a)+"px"}function it(e,t,n,i,r){return new it.prototype.init(e,t,n,i,r)}C.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=We(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var r,o,a,s=Y(t),l=Ze.test(t),u=e.style;if(l||(t=Xe(s)),a=C.cssHooks[t]||C.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(r=a.get(e,!1,i))?r:u[t];"string"===(o=typeof n)&&(r=ie.exec(n))&&r[1]&&(n=ue(e,t,r),o="number"),null!=n&&n==n&&("number"!==o||l||(n+=r&&r[3]||(C.cssNumber[s]?"":"px")),m.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,i))||(l?u.setProperty(t,n):u[t]=n))}},css:function(e,t,n,i){var r,o,a,s=Y(t);return Ze.test(t)||(t=Xe(s)),(a=C.cssHooks[t]||C.cssHooks[s])&&"get"in a&&(r=a.get(e,!0,n)),void 0===r&&(r=We(e,t,i)),"normal"===r&&t in Qe&&(r=Qe[t]),""===n||n?(o=parseFloat(r),!0===n||isFinite(o)?o||0:r):r}}),C.each(["height","width"],function(e,t){C.cssHooks[t]={get:function(e,n,i){if(n)return!Ke.test(C.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?nt(e,t,i):je(e,Je,function(){return nt(e,t,i)})},set:function(e,n,i){var r,o=Ve(e),a=!m.scrollboxSize()&&"absolute"===o.position,s=(a||i)&&"border-box"===C.css(e,"boxSizing",!1,o),l=i?tt(e,t,i,s,o):0;return s&&a&&(l-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-tt(e,t,"border",!1,o)-.5)),l&&(r=ie.exec(n))&&"px"!==(r[3]||"px")&&(e.style[t]=n,n=C.css(e,t)),et(0,n,l)}}}),C.cssHooks.marginLeft=qe(m.reliableMarginLeft,function(e,t){if(t)return(parseFloat(We(e,"marginLeft"))||e.getBoundingClientRect().left-je(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),C.each({margin:"",padding:"",border:"Width"},function(e,t){C.cssHooks[e+t]={expand:function(n){for(var i=0,r={},o="string"==typeof n?n.split(" "):[n];i<4;i++)r[e+re[i]+t]=o[i]||o[i-2]||o[0];return r}},"margin"!==e&&(C.cssHooks[e+t].set=et)}),C.fn.extend({css:function(e,t){return W(this,function(e,t,n){var i,r,o={},a=0;if(Array.isArray(t)){for(i=Ve(e),r=t.length;a1)}}),C.Tween=it,it.prototype={constructor:it,init:function(e,t,n,i,r,o){this.elem=e,this.prop=n,this.easing=r||C.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=i,this.unit=o||(C.cssNumber[n]?"":"px")},cur:function(){var e=it.propHooks[this.prop];return e&&e.get?e.get(this):it.propHooks._default.get(this)},run:function(e){var t,n=it.propHooks[this.prop];return this.options.duration?this.pos=t=C.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):it.propHooks._default.set(this),this}},it.prototype.init.prototype=it.prototype,it.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=C.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){C.fx.step[e.prop]?C.fx.step[e.prop](e):1!==e.elem.nodeType||!C.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:C.style(e.elem,e.prop,e.now+e.unit)}}},it.propHooks.scrollTop=it.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},C.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},C.fx=it.prototype.init,C.fx.step={};var rt,ot,at=/^(?:toggle|show|hide)$/,st=/queueHooks$/;function lt(){ot&&(!1===b.hidden&&n.requestAnimationFrame?n.requestAnimationFrame(lt):n.setTimeout(lt,C.fx.interval),C.fx.tick())}function ut(){return n.setTimeout(function(){rt=void 0}),rt=Date.now()}function ct(e,t){var n,i=0,r={height:e};for(t=t?1:0;i<4;i+=2-t)r["margin"+(n=re[i])]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function ht(e,t,n){for(var i,r=(dt.tweeners[t]||[]).concat(dt.tweeners["*"]),o=0,a=r.length;o1)},removeAttr:function(e){return this.each(function(){C.removeAttr(this,e)})}}),C.extend({attr:function(e,t,n){var i,r,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return void 0===e.getAttribute?C.prop(e,t,n):(1===o&&C.isXMLDoc(e)||(r=C.attrHooks[t.toLowerCase()]||(C.expr.match.bool.test(t)?ft:void 0)),void 0!==n?null===n?void C.removeAttr(e,t):r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:(e.setAttribute(t,n+""),n):r&&"get"in r&&null!==(i=r.get(e,t))?i:null==(i=C.find.attr(e,t))?void 0:i)},attrHooks:{type:{set:function(e,t){if(!m.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,i=0,r=t&&t.match(B);if(r&&1===e.nodeType)for(;n=r[i++];)e.removeAttribute(n)}}),ft={set:function(e,t,n){return!1===t?C.removeAttr(e,n):e.setAttribute(n,n),n}},C.each(C.expr.match.bool.source.match(/\w+/g),function(e,t){var n=pt[t]||C.find.attr;pt[t]=function(e,t,i){var r,o,a=t.toLowerCase();return i||(o=pt[a],pt[a]=r,r=null!=n(e,t,i)?a:null,pt[a]=o),r}});var gt=/^(?:input|select|textarea|button)$/i,mt=/^(?:a|area)$/i;function vt(e){return(e.match(B)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function bt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(B)||[]}C.fn.extend({prop:function(e,t){return W(this,C.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[C.propFix[e]||e]})}}),C.extend({prop:function(e,t,n){var i,r,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&C.isXMLDoc(e)||(t=C.propFix[t]||t,r=C.propHooks[t]),void 0!==n?r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:e[t]=n:r&&"get"in r&&null!==(i=r.get(e,t))?i:e[t]},propHooks:{tabIndex:{get:function(e){var t=C.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||mt.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),m.optSelected||(C.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),C.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){C.propFix[this.toLowerCase()]=this}),C.fn.extend({addClass:function(e){var t,n,i,r,o,a,s,l=0;if(v(e))return this.each(function(t){C(this).addClass(e.call(this,t,yt(this)))});if((t=bt(e)).length)for(;n=this[l++];)if(r=yt(n),i=1===n.nodeType&&" "+vt(r)+" "){for(a=0;o=t[a++];)i.indexOf(" "+o+" ")<0&&(i+=o+" ");r!==(s=vt(i))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,i,r,o,a,s,l=0;if(v(e))return this.each(function(t){C(this).removeClass(e.call(this,t,yt(this)))});if(!arguments.length)return this.attr("class","");if((t=bt(e)).length)for(;n=this[l++];)if(r=yt(n),i=1===n.nodeType&&" "+vt(r)+" "){for(a=0;o=t[a++];)for(;i.indexOf(" "+o+" ")>-1;)i=i.replace(" "+o+" "," ");r!==(s=vt(i))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,i="string"===n||Array.isArray(e);return"boolean"==typeof t&&i?t?this.addClass(e):this.removeClass(e):v(e)?this.each(function(n){C(this).toggleClass(e.call(this,n,yt(this),t),t)}):this.each(function(){var t,r,o,a;if(i)for(r=0,o=C(this),a=bt(e);t=a[r++];)o.hasClass(t)?o.removeClass(t):o.addClass(t);else void 0!==e&&"boolean"!==n||((t=yt(this))&&Z.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":Z.get(this,"__className__")||""))})},hasClass:function(e){var t,n,i=0;for(t=" "+e+" ";n=this[i++];)if(1===n.nodeType&&(" "+vt(yt(n))+" ").indexOf(t)>-1)return!0;return!1}});var xt=/\r/g;C.fn.extend({val:function(e){var t,n,i,r=this[0];return arguments.length?(i=v(e),this.each(function(n){var r;1===this.nodeType&&(null==(r=i?e.call(this,n,C(this).val()):e)?r="":"number"==typeof r?r+="":Array.isArray(r)&&(r=C.map(r,function(e){return null==e?"":e+""})),(t=C.valHooks[this.type]||C.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,r,"value")||(this.value=r))})):r?(t=C.valHooks[r.type]||C.valHooks[r.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(r,"value"))?n:"string"==typeof(n=r.value)?n.replace(xt,""):null==n?"":n:void 0}}),C.extend({valHooks:{option:{get:function(e){var t=C.find.attr(e,"value");return null!=t?t:vt(C.text(e))}},select:{get:function(e){var t,n,i,r=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],l=a?o+1:r.length;for(i=o<0?l:a?o:0;i-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),C.each(["radio","checkbox"],function(){C.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=C.inArray(C(e).val(),t)>-1}},m.checkOn||(C.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),m.focusin="onfocusin"in n;var _t=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};C.extend(C.event,{trigger:function(e,t,i,r){var o,a,s,l,u,c,h,d,p=[i||b],g=f.call(e,"type")?e.type:e,m=f.call(e,"namespace")?e.namespace.split("."):[];if(a=d=s=i=i||b,3!==i.nodeType&&8!==i.nodeType&&!_t.test(g+C.event.triggered)&&(g.indexOf(".")>-1&&(g=(m=g.split(".")).shift(),m.sort()),u=g.indexOf(":")<0&&"on"+g,(e=e[C.expando]?e:new C.Event(g,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=m.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=i),t=null==t?[e]:C.makeArray(t,[e]),h=C.event.special[g]||{},r||!h.trigger||!1!==h.trigger.apply(i,t))){if(!r&&!h.noBubble&&!y(i)){for(l=h.delegateType||g,_t.test(l+g)||(a=a.parentNode);a;a=a.parentNode)p.push(a),s=a;s===(i.ownerDocument||b)&&p.push(s.defaultView||s.parentWindow||n)}for(o=0;(a=p[o++])&&!e.isPropagationStopped();)d=a,e.type=o>1?l:h.bindType||g,(c=(Z.get(a,"events")||Object.create(null))[e.type]&&Z.get(a,"handle"))&&c.apply(a,t),(c=u&&a[u])&&c.apply&&X(a)&&(e.result=c.apply(a,t),!1===e.result&&e.preventDefault());return e.type=g,r||e.isDefaultPrevented()||h._default&&!1!==h._default.apply(p.pop(),t)||!X(i)||u&&v(i[g])&&!y(i)&&((s=i[u])&&(i[u]=null),C.event.triggered=g,e.isPropagationStopped()&&d.addEventListener(g,wt),i[g](),e.isPropagationStopped()&&d.removeEventListener(g,wt),C.event.triggered=void 0,s&&(i[u]=s)),e.result}},simulate:function(e,t,n){var i=C.extend(new C.Event,n,{type:e,isSimulated:!0});C.event.trigger(i,null,t)}}),C.fn.extend({trigger:function(e,t){return this.each(function(){C.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return C.event.trigger(e,t,n,!0)}}),m.focusin||C.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){C.event.simulate(t,e.target,C.event.fix(e))};C.event.special[t]={setup:function(){var i=this.ownerDocument||this.document||this,r=Z.access(i,t);r||i.addEventListener(e,n,!0),Z.access(i,t,(r||0)+1)},teardown:function(){var i=this.ownerDocument||this.document||this,r=Z.access(i,t)-1;r?Z.access(i,t,r):(i.removeEventListener(e,n,!0),Z.remove(i,t))}}});var Ct=n.location,St={guid:Date.now()},kt=/\?/;C.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new n.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||C.error("Invalid XML: "+e),t};var Tt=/\[\]$/,Mt=/\r?\n/g,Dt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function It(e,t,n,i){var r;if(Array.isArray(t))C.each(t,function(t,r){n||Tt.test(e)?i(e,r):It(e+"["+("object"==typeof r&&null!=r?t:"")+"]",r,n,i)});else if(n||"object"!==w(t))i(e,t);else for(r in t)It(e+"["+r+"]",t[r],n,i)}C.param=function(e,t){var n,i=[],r=function(e,t){var n=v(t)?t():t;i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!C.isPlainObject(e))C.each(e,function(){r(this.name,this.value)});else for(n in e)It(n,e[n],t,r);return i.join("&")},C.fn.extend({serialize:function(){return C.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=C.prop(this,"elements");return e?C.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!C(this).is(":disabled")&&At.test(this.nodeName)&&!Dt.test(e)&&(this.checked||!ge.test(e))}).map(function(e,t){var n=C(this).val();return null==n?null:Array.isArray(n)?C.map(n,function(e){return{name:t.name,value:e.replace(Mt,"\r\n")}}):{name:t.name,value:n.replace(Mt,"\r\n")}}).get()}});var Ot=/%20/g,Et=/#.*$/,Lt=/([?&])_=[^&]*/,Pt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Nt=/^(?:GET|HEAD)$/,Rt=/^\/\//,Bt={},zt={},Ft="*/".concat("*"),$t=b.createElement("a");function Vt(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var i,r=0,o=t.toLowerCase().match(B)||[];if(v(n))for(;i=o[r++];)"+"===i[0]?(i=i.slice(1)||"*",(e[i]=e[i]||[]).unshift(n)):(e[i]=e[i]||[]).push(n)}}function jt(e,t,n,i){var r={},o=e===zt;function a(s){var l;return r[s]=!0,C.each(e[s]||[],function(e,s){var u=s(t,n,i);return"string"!=typeof u||o||r[u]?o?!(l=u):void 0:(t.dataTypes.unshift(u),a(u),!1)}),l}return a(t.dataTypes[0])||!r["*"]&&a("*")}function Ht(e,t){var n,i,r=C.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((r[n]?e:i||(i={}))[n]=t[n]);return i&&C.extend(!0,e,i),e}$t.href=Ct.href,C.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Ft,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":C.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Ht(Ht(e,C.ajaxSettings),t):Ht(C.ajaxSettings,e)},ajaxPrefilter:Vt(Bt),ajaxTransport:Vt(zt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var i,r,o,a,s,l,u,c,h,d,f=C.ajaxSetup({},t),p=f.context||f,g=f.context&&(p.nodeType||p.jquery)?C(p):C.event,m=C.Deferred(),v=C.Callbacks("once memory"),y=f.statusCode||{},x={},_={},w="canceled",S={readyState:0,getResponseHeader:function(e){var t;if(u){if(!a)for(a={};t=Pt.exec(o);)a[t[1].toLowerCase()+" "]=(a[t[1].toLowerCase()+" "]||[]).concat(t[2]);t=a[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return u?o:null},setRequestHeader:function(e,t){return null==u&&(e=_[e.toLowerCase()]=_[e.toLowerCase()]||e,x[e]=t),this},overrideMimeType:function(e){return null==u&&(f.mimeType=e),this},statusCode:function(e){var t;if(e)if(u)S.always(e[S.status]);else for(t in e)y[t]=[y[t],e[t]];return this},abort:function(e){var t=e||w;return i&&i.abort(t),k(0,t),this}};if(m.promise(S),f.url=((e||f.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),f.type=t.method||t.type||f.method||f.type,f.dataTypes=(f.dataType||"*").toLowerCase().match(B)||[""],null==f.crossDomain){l=b.createElement("a");try{l.href=f.url,l.href=l.href,f.crossDomain=$t.protocol+"//"+$t.host!=l.protocol+"//"+l.host}catch(e){f.crossDomain=!0}}if(f.data&&f.processData&&"string"!=typeof f.data&&(f.data=C.param(f.data,f.traditional)),jt(Bt,f,t,S),u)return S;for(h in(c=C.event&&f.global)&&0==C.active++&&C.event.trigger("ajaxStart"),f.type=f.type.toUpperCase(),f.hasContent=!Nt.test(f.type),r=f.url.replace(Et,""),f.hasContent?f.data&&f.processData&&0===(f.contentType||"").indexOf("application/x-www-form-urlencoded")&&(f.data=f.data.replace(Ot,"+")):(d=f.url.slice(r.length),f.data&&(f.processData||"string"==typeof f.data)&&(r+=(kt.test(r)?"&":"?")+f.data,delete f.data),!1===f.cache&&(r=r.replace(Lt,"$1"),d=(kt.test(r)?"&":"?")+"_="+St.guid+++d),f.url=r+d),f.ifModified&&(C.lastModified[r]&&S.setRequestHeader("If-Modified-Since",C.lastModified[r]),C.etag[r]&&S.setRequestHeader("If-None-Match",C.etag[r])),(f.data&&f.hasContent&&!1!==f.contentType||t.contentType)&&S.setRequestHeader("Content-Type",f.contentType),S.setRequestHeader("Accept",f.dataTypes[0]&&f.accepts[f.dataTypes[0]]?f.accepts[f.dataTypes[0]]+("*"!==f.dataTypes[0]?", "+Ft+"; q=0.01":""):f.accepts["*"]),f.headers)S.setRequestHeader(h,f.headers[h]);if(f.beforeSend&&(!1===f.beforeSend.call(p,S,f)||u))return S.abort();if(w="abort",v.add(f.complete),S.done(f.success),S.fail(f.error),i=jt(zt,f,t,S)){if(S.readyState=1,c&&g.trigger("ajaxSend",[S,f]),u)return S;f.async&&f.timeout>0&&(s=n.setTimeout(function(){S.abort("timeout")},f.timeout));try{u=!1,i.send(x,k)}catch(e){if(u)throw e;k(-1,e)}}else k(-1,"No Transport");function k(e,t,a,l){var h,d,b,x,_,w=t;u||(u=!0,s&&n.clearTimeout(s),i=void 0,o=l||"",S.readyState=e>0?4:0,h=e>=200&&e<300||304===e,a&&(x=function(e,t,n){for(var i,r,o,a,s=e.contents,l=e.dataTypes;"*"===l[0];)l.shift(),void 0===i&&(i=e.mimeType||t.getResponseHeader("Content-Type"));if(i)for(r in s)if(s[r]&&s[r].test(i)){l.unshift(r);break}if(l[0]in n)o=l[0];else{for(r in n){if(!l[0]||e.converters[r+" "+l[0]]){o=r;break}a||(a=r)}o=o||a}if(o)return o!==l[0]&&l.unshift(o),n[o]}(f,S,a)),!h&&C.inArray("script",f.dataTypes)>-1&&(f.converters["text script"]=function(){}),x=function(e,t,n,i){var r,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&i&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(!(a=u[l+" "+o]||u["* "+o]))for(r in u)if((s=r.split(" "))[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){!0===a?a=u[r]:!0!==u[r]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e.throws)t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}(f,x,S,h),h?(f.ifModified&&((_=S.getResponseHeader("Last-Modified"))&&(C.lastModified[r]=_),(_=S.getResponseHeader("etag"))&&(C.etag[r]=_)),204===e||"HEAD"===f.type?w="nocontent":304===e?w="notmodified":(w=x.state,d=x.data,h=!(b=x.error))):(b=w,!e&&w||(w="error",e<0&&(e=0))),S.status=e,S.statusText=(t||w)+"",h?m.resolveWith(p,[d,w,S]):m.rejectWith(p,[S,w,b]),S.statusCode(y),y=void 0,c&&g.trigger(h?"ajaxSuccess":"ajaxError",[S,f,h?d:b]),v.fireWith(p,[S,w]),c&&(g.trigger("ajaxComplete",[S,f]),--C.active||C.event.trigger("ajaxStop")))}return S},getJSON:function(e,t,n){return C.get(e,t,n,"json")},getScript:function(e,t){return C.get(e,void 0,t,"script")}}),C.each(["get","post"],function(e,t){C[t]=function(e,n,i,r){return v(n)&&(r=r||i,i=n,n=void 0),C.ajax(C.extend({url:e,type:t,dataType:r,data:n,success:i},C.isPlainObject(e)&&e))}}),C.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),C._evalUrl=function(e,t,n){return C.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){C.globalEval(e,t,n)}})},C.fn.extend({wrapAll:function(e){var t;return this[0]&&(v(e)&&(e=e.call(this[0])),t=C(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return v(e)?this.each(function(t){C(this).wrapInner(e.call(this,t))}):this.each(function(){var t=C(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v(e);return this.each(function(n){C(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){C(this).replaceWith(this.childNodes)}),this}}),C.expr.pseudos.hidden=function(e){return!C.expr.pseudos.visible(e)},C.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},C.ajaxSettings.xhr=function(){try{return new n.XMLHttpRequest}catch(e){}};var Wt={0:200,1223:204},qt=C.ajaxSettings.xhr();m.cors=!!qt&&"withCredentials"in qt,m.ajax=qt=!!qt,C.ajaxTransport(function(e){var t,i;if(m.cors||qt&&!e.crossDomain)return{send:function(r,o){var a,s=e.xhr();if(s.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(a in e.xhrFields)s[a]=e.xhrFields[a];for(a in e.mimeType&&s.overrideMimeType&&s.overrideMimeType(e.mimeType),e.crossDomain||r["X-Requested-With"]||(r["X-Requested-With"]="XMLHttpRequest"),r)s.setRequestHeader(a,r[a]);t=function(e){return function(){t&&(t=i=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Wt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=t(),i=s.onerror=s.ontimeout=t("error"),void 0!==s.onabort?s.onabort=i:s.onreadystatechange=function(){4===s.readyState&&n.setTimeout(function(){t&&i()})},t=t("abort");try{s.send(e.hasContent&&e.data||null)}catch(e){if(t)throw e}},abort:function(){t&&t()}}}),C.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),C.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return C.globalEval(e),e}}}),C.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),C.ajaxTransport("script",function(e){var t,n;if(e.crossDomain||e.scriptAttrs)return{send:function(i,r){t=C("