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=""+o+"";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("