├── doc ├── img │ ├── anac.png │ ├── anac_m.png │ ├── jaqs.png │ ├── analyze.png │ ├── dataview.png │ ├── ic_report.png │ ├── jaqs_test.png │ ├── anac_ipython.png │ ├── further_analysis.png │ ├── returns_report.png │ ├── backtest_Graham_result.png │ ├── backtest_ICModel_result.png │ ├── event_drivent_illustration_dual.png │ ├── event_drivent_dual_thrust_result.png │ ├── event_driven_calendar_spread_result.png │ └── event_driven_roll_within_sector_result.png ├── source │ ├── modules.rst │ ├── overview.rst │ ├── jaqs.research.rst │ ├── user_guide.rst │ ├── jaqs.rst │ ├── api_reference.rst │ ├── jaqs.trade.event.rst │ ├── jaqs.trade.analyze.rst │ ├── index.rst │ ├── jaqs.data.dataapi.rst │ ├── jaqs.trade.tradeapi.rst │ ├── jaqs.research.signaldigger.rst │ ├── md2rst.py │ ├── jaqs.data.basic.rst │ ├── jaqs.util.rst │ ├── jaqs.data.rst │ ├── jaqs.trade.rst │ ├── how_to_ask_questions.rst │ ├── research.rst │ ├── base_data.rst │ ├── realtime.rst │ ├── trade_api.rst │ └── install.rst ├── overview.md ├── data_api.md ├── base_data.md ├── how_to_ask_questions.md ├── research.md ├── realtime.md ├── Makefile ├── install.md └── trade_api.md ├── MANIFEST.in ├── publish ├── publish_pypi.sh └── publish_conda.sh ├── requirements.txt ├── example ├── alpha │ ├── __init__.py │ ├── config_path.py │ ├── single_factor_weight.py │ ├── wine_industry_momentum.py │ ├── select_stocks_pe_profit.py │ ├── select_stocks_industry_head.py │ ├── FamaFrench.py │ └── first_example.py ├── eventdriven │ ├── __init__.py │ ├── config_path.py │ ├── custom_symbol_signal.py │ ├── CalendarSpread.py │ └── market_making.py └── research │ ├── config_path.py │ ├── signal_return_ic_analysis_single.py │ ├── event_analysis.py │ └── signal_return_ic_analysis.py ├── jaqs ├── research │ ├── __init__.py │ └── signaldigger │ │ └── __init__.py ├── util │ ├── __init__.py │ ├── sequence.py │ ├── numeric.py │ ├── pdutil.py │ ├── profile.py │ ├── fileio.py │ └── dtutil.py ├── trade │ ├── analyze │ │ ├── static │ │ │ ├── test_template.html │ │ │ ├── additional.css │ │ │ └── bootstrap-toc │ │ │ │ ├── bootstrap-toc.min.css │ │ │ │ └── bootstrap-toc.min.js │ │ ├── __init__.py │ │ └── report.py │ ├── event │ │ ├── __init__.py │ │ └── eventtype.py │ ├── tradeapi │ │ ├── __init__.py │ │ ├── README.md │ │ ├── .gitignore │ │ └── utils.py │ ├── __init__.py │ └── common.py ├── __init__.py └── data │ ├── dataapi │ ├── __init__.py │ ├── README.md │ ├── .gitignore │ └── utils.py │ ├── __init__.py │ ├── basic │ ├── __init__.py │ ├── instrument.py │ ├── position.py │ └── trade.py │ ├── align.py │ └── continue_contract.py ├── requirements_doc.txt ├── .gitattributes ├── requirements_test.txt ├── config ├── trade_config.json └── data_config.json ├── test ├── test_model.py ├── test_sequence_generator.py ├── config_path.py ├── test_report.py ├── test_strategy.py ├── test_data_api.py ├── test_research.py ├── test_align.py ├── test_backtest_alpha.py └── test_trade_api.py ├── .travis.yml ├── .gitignore ├── setup.py ├── README.rst └── TODO.md /doc/img/anac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/anac.png -------------------------------------------------------------------------------- /doc/img/anac_m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/anac_m.png -------------------------------------------------------------------------------- /doc/img/jaqs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/jaqs.png -------------------------------------------------------------------------------- /doc/img/analyze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/analyze.png -------------------------------------------------------------------------------- /doc/img/dataview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/dataview.png -------------------------------------------------------------------------------- /doc/img/ic_report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/ic_report.png -------------------------------------------------------------------------------- /doc/img/jaqs_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/jaqs_test.png -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | recursive-include jaqs/trade/analyze/static *.js *.html *.css -------------------------------------------------------------------------------- /doc/img/anac_ipython.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/anac_ipython.png -------------------------------------------------------------------------------- /doc/source/modules.rst: -------------------------------------------------------------------------------- 1 | jaqs 2 | ==== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | jaqs 8 | -------------------------------------------------------------------------------- /doc/img/further_analysis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/further_analysis.png -------------------------------------------------------------------------------- /doc/img/returns_report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/returns_report.png -------------------------------------------------------------------------------- /publish/publish_pypi.sh: -------------------------------------------------------------------------------- 1 | python setup.py sdist 2 | python setup.py bdist_wheel --universal 3 | twine upload dist/* 4 | -------------------------------------------------------------------------------- /doc/img/backtest_Graham_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/backtest_Graham_result.png -------------------------------------------------------------------------------- /doc/img/backtest_ICModel_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/backtest_ICModel_result.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | future 2 | Jinja2 3 | msgpack-python 4 | pandas >= 0.20.0 5 | pyzmq 6 | python-snappy 7 | seaborn 8 | six 9 | -------------------------------------------------------------------------------- /doc/overview.md: -------------------------------------------------------------------------------- 1 | 2 | ## Overview 3 | 4 | 框架概览: 5 | 6 | - 使用数据API,轻松获取研究数据 7 | - 根据策略模板,编写自己的量化策略 8 | - 使用回测框架,对策略进行回测和验证 9 | -------------------------------------------------------------------------------- /doc/img/event_drivent_illustration_dual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/event_drivent_illustration_dual.png -------------------------------------------------------------------------------- /example/alpha/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | """ 3 | The example module contains several examples of using py-hft-trade. 4 | """ 5 | -------------------------------------------------------------------------------- /jaqs/research/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from .signaldigger import SignalDigger 4 | 5 | 6 | __all__ = ['SignalDigger'] 7 | -------------------------------------------------------------------------------- /doc/img/event_drivent_dual_thrust_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/event_drivent_dual_thrust_result.png -------------------------------------------------------------------------------- /doc/source/overview.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | -------- 3 | 4 | 框架概览: 5 | 6 | - 使用数据API,轻松获取研究数据 7 | - 根据策略模板,编写自己的量化策略 8 | - 使用回测框架,对策略进行回测和验证 9 | -------------------------------------------------------------------------------- /example/eventdriven/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | """ 3 | The example module contains several examples of using py-hft-trade. 4 | """ 5 | -------------------------------------------------------------------------------- /jaqs/research/signaldigger/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from .digger import SignalDigger 4 | 5 | 6 | __all__ = ['SignalDigger'] 7 | -------------------------------------------------------------------------------- /doc/img/event_driven_calendar_spread_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/event_driven_calendar_spread_result.png -------------------------------------------------------------------------------- /doc/img/event_driven_roll_within_sector_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantOS-org/JAQS/HEAD/doc/img/event_driven_roll_within_sector_result.png -------------------------------------------------------------------------------- /requirements_doc.txt: -------------------------------------------------------------------------------- 1 | future 2 | enum34 3 | Jinja2 4 | msgpack_python 5 | pandas >= 0.20.0 6 | pyzmq 7 | seaborn 8 | six 9 | sphinx_rtd_theme 10 | -------------------------------------------------------------------------------- /jaqs/util/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from .dtutil import * 4 | from .fileio import * 5 | from .numeric import * 6 | from .pdutil import * 7 | from .profile import * 8 | from .sequence import * 9 | -------------------------------------------------------------------------------- /publish/publish_conda.sh: -------------------------------------------------------------------------------- 1 | conda skeleton pypi jaqs 2 | cp build1.sh jaqs 3 | grep -rl "^.*enum34.*$" jaqs | xargs sed -i "s|^.*enum34.*$||g" 4 | conda config --set anaconda_upload yes 5 | conda-build jaqs 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=Python 2 | *.css linguist-language=Python 3 | *.html linguist-language=Python 4 | *.ipynb linguist-language=Python 5 | *.md linguist-language=Python 6 | *.png linguist-language=Python 7 | -------------------------------------------------------------------------------- /requirements_test.txt: -------------------------------------------------------------------------------- 1 | future 2 | enum34 3 | Jinja2 4 | msgpack_python 5 | pandas >= 0.20.0 6 | pytest 7 | pytest-cov 8 | python-coveralls 9 | python-snappy 10 | pyzmq 11 | seaborn 12 | six 13 | sphinx_rtd_theme 14 | -------------------------------------------------------------------------------- /jaqs/trade/analyze/static/test_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ mytitle }} 6 | 7 | 8 |

Sales Funnel Report - National

9 | {{ mytable }} 10 | 11 | -------------------------------------------------------------------------------- /config/trade_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "remote.trade.address": "tcp://gw.quantos.org:8901", 3 | "remote.trade.username": "17621969269", 4 | "remote.trade.password": "eyJhbGciOiJIUzI1NiJ9.eyJjcmVhdGVfdGltZSI6IjE1MTIwMjA0OTQwMzciLCJpc3MiOiJhdXRoMCIsImlkIjoiMTc2MjE5NjkyNjkifQ.WQvI9k6dvXe5zIzQwyuPI4BM0Py1OSYFENIQ3z0RG6c" 5 | } -------------------------------------------------------------------------------- /jaqs/util/sequence.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from collections import defaultdict 4 | 5 | 6 | class SequenceGenerator(object): 7 | def __init__(self): 8 | self.__d = defaultdict(int) 9 | 10 | def get_next(self, key): 11 | self.__d[key] += 1 12 | return self.__d[key] 13 | -------------------------------------------------------------------------------- /config/data_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "remote.data.address": "tcp://data.quantos.org:8910", 3 | "remote.data.username": "17621969269", 4 | "remote.data.password": "eyJhbGciOiJIUzI1NiJ9.eyJjcmVhdGVfdGltZSI6IjE1MTIwMjA0OTQwMzciLCJpc3MiOiJhdXRoMCIsImlkIjoiMTc2MjE5NjkyNjkifQ.WQvI9k6dvXe5zIzQwyuPI4BM0Py1OSYFENIQ3z0RG6c" 5 | } 6 | -------------------------------------------------------------------------------- /doc/source/jaqs.research.rst: -------------------------------------------------------------------------------- 1 | jaqs\.research package 2 | ====================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | jaqs.research.signaldigger 10 | 11 | Module contents 12 | --------------- 13 | 14 | .. automodule:: jaqs.research 15 | :members: 16 | :undoc-members: 17 | :show-inheritance: 18 | -------------------------------------------------------------------------------- /doc/source/user_guide.rst: -------------------------------------------------------------------------------- 1 | 用户手册 2 | ======== 3 | 4 | 本页面给用户提供了一个简洁清晰的入门指南,涵盖各个功能模块 5 | 6 | .. include:: overview.rst 7 | 8 | .. include:: data_api.rst 9 | 10 | .. include:: data_view.rst 11 | 12 | .. include:: research.rst 13 | 14 | .. include:: backtest.rst 15 | 16 | .. include:: trade_api.rst 17 | 18 | .. include:: realtime.rst 19 | -------------------------------------------------------------------------------- /doc/source/jaqs.rst: -------------------------------------------------------------------------------- 1 | jaqs package 2 | ============ 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | jaqs.data 10 | jaqs.research 11 | jaqs.trade 12 | jaqs.util 13 | 14 | Module contents 15 | --------------- 16 | 17 | .. automodule:: jaqs 18 | :members: 19 | :undoc-members: 20 | :show-inheritance: 21 | -------------------------------------------------------------------------------- /jaqs/trade/event/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | """ 3 | The event processing engine is where the event is identified, 4 | and the appropriate reaction is selected and executed. 5 | 6 | Our framework utilizes event engine to run in an efficient way. 7 | 8 | """ 9 | from .engine import EventEngine, Event 10 | from .eventtype import EVENT_TYPE 11 | -------------------------------------------------------------------------------- /test/test_model.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from __future__ import absolute_import, division, print_function 4 | 5 | from jaqs.trade import model 6 | 7 | 8 | def test_model(): 9 | model.StockSelector() 10 | model.SimpleCostModel() 11 | model.AlphaContext() 12 | model.FactorRiskModel() 13 | model.FactorSignalModel() 14 | -------------------------------------------------------------------------------- /jaqs/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | JAQS 4 | ~~~~ 5 | 6 | Open source quantitative research&trading framework. 7 | 8 | copyright: (c) 2017 quantOS-org. 9 | license: Apache 2.0, see LICENSE for details. 10 | """ 11 | 12 | import os 13 | 14 | 15 | __version__ = '0.6.14' 16 | SOURCE_ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) 17 | -------------------------------------------------------------------------------- /jaqs/data/dataapi/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | dataapi defines standard APIs for communicating with data service. 4 | 5 | """ 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | from __future__ import unicode_literals 10 | 11 | from .data_api import DataApi 12 | 13 | __all__ = ['DataApi'] 14 | -------------------------------------------------------------------------------- /jaqs/trade/tradeapi/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | tradeapi defines standard APIs for communicating with 4 | algorithmic trading & executing system. 5 | """ 6 | from __future__ import absolute_import 7 | from __future__ import division 8 | from __future__ import print_function 9 | from __future__ import unicode_literals 10 | 11 | from .trade_api import TradeApi 12 | 13 | __all__ = ['TradeApi'] 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | - "3.6" 6 | 7 | branches: 8 | only: 9 | - master 10 | 11 | before_install: 12 | - sudo apt-get -qq update 13 | - sudo apt-get install -y libsnappy-dev 14 | 15 | install: 16 | - pip install -r requirements_test.txt 17 | 18 | before_script: 19 | - python setup.py install 20 | 21 | script: 22 | - cd test 23 | - py.test test_util.py --cov jaqs 24 | 25 | -------------------------------------------------------------------------------- /jaqs/data/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | """ 4 | Modules relevant to data. 5 | 6 | """ 7 | 8 | from .dataapi import DataApi 9 | from .dataservice import RemoteDataService, DataService 10 | from .dataview import DataView, EventDataView 11 | from .py_expression_eval import Parser 12 | 13 | 14 | # we do not expose align and basic 15 | __all__ = ['DataApi', 'DataService', 'RemoteDataService', 'DataView', 'Parser', 'EventDataView'] -------------------------------------------------------------------------------- /doc/source/api_reference.rst: -------------------------------------------------------------------------------- 1 | API说明文档 2 | ============ 3 | 4 | 行情数据API文档 5 | --------------- 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | market_data 10 | 11 | 12 | 参考数据API文档 13 | --------------- 14 | .. toctree:: 15 | :maxdepth: 2 16 | 17 | base_data 18 | 19 | 20 | Other Important APIs 21 | -------------------- 22 | 23 | .. toctree:: 24 | :maxdepth: 2 25 | 26 | jaqs.data 27 | jaqs.trade 28 | jaqs.research 29 | jaqs.util 30 | -------------------------------------------------------------------------------- /test/test_sequence_generator.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | from jaqs.util.sequence import SequenceGenerator 3 | 4 | 5 | def test_seq_gen(): 6 | sg = SequenceGenerator() 7 | for i in range(1, 999): 8 | assert sg.get_next('order') == i 9 | 10 | text = 'trade' 11 | sg.get_next(text) 12 | sg.get_next(text) 13 | for i in range(3, 999): 14 | assert sg.get_next(text) == i 15 | 16 | 17 | if __name__ == "__main__": 18 | test_seq_gen() 19 | -------------------------------------------------------------------------------- /test/config_path.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | from __future__ import print_function 4 | import os 5 | 6 | 7 | _test_dir = os.path.dirname(os.path.abspath(__file__)) 8 | DATA_CONFIG_PATH = os.path.abspath(os.path.join(_test_dir, '../config/data_config.json')) 9 | TRADE_CONFIG_PATH = os.path.abspath(os.path.join(_test_dir, '../config/trade_config.json')) 10 | 11 | print("Current data config file path: {}".format(DATA_CONFIG_PATH)) 12 | print("Current trade config file path: {}".format(TRADE_CONFIG_PATH)) 13 | -------------------------------------------------------------------------------- /example/alpha/config_path.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | from __future__ import print_function 4 | import os 5 | 6 | 7 | _test_dir = os.path.dirname(os.path.abspath(__file__)) 8 | DATA_CONFIG_PATH = os.path.abspath(os.path.join(_test_dir, '../../config/data_config.json')) 9 | TRADE_CONFIG_PATH = os.path.abspath(os.path.join(_test_dir, '../../config/trade_config.json')) 10 | 11 | print("Current data config file path: {}".format(DATA_CONFIG_PATH)) 12 | print("Current trade config file path: {}".format(TRADE_CONFIG_PATH)) 13 | -------------------------------------------------------------------------------- /example/research/config_path.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | from __future__ import print_function 4 | import os 5 | 6 | 7 | _test_dir = os.path.dirname(os.path.abspath(__file__)) 8 | DATA_CONFIG_PATH = os.path.abspath(os.path.join(_test_dir, '../../config/data_config.json')) 9 | TRADE_CONFIG_PATH = os.path.abspath(os.path.join(_test_dir, '../../config/trade_config.json')) 10 | 11 | print("Current data config file path: {}".format(DATA_CONFIG_PATH)) 12 | print("Current trade config file path: {}".format(TRADE_CONFIG_PATH)) 13 | -------------------------------------------------------------------------------- /example/eventdriven/config_path.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | from __future__ import print_function 4 | import os 5 | 6 | 7 | _test_dir = os.path.dirname(os.path.abspath(__file__)) 8 | DATA_CONFIG_PATH = os.path.abspath(os.path.join(_test_dir, '../../config/data_config.json')) 9 | TRADE_CONFIG_PATH = os.path.abspath(os.path.join(_test_dir, '../../config/trade_config.json')) 10 | 11 | print("Current data config file path: {}".format(DATA_CONFIG_PATH)) 12 | print("Current trade config file path: {}".format(TRADE_CONFIG_PATH)) 13 | -------------------------------------------------------------------------------- /doc/data_api.md: -------------------------------------------------------------------------------- 1 | ## 数据API 2 | 3 | 本产品提供了金融数据api,方便用户调用接口获取各种数据,通过python的api调用接口,返回DataFrame格式的数据和消息,以下是用法 4 | 5 | ### 导入接口 6 | 7 | 在python程序里面导入module,然后用注册的用户帐号登录就可以使用行情和参考数据的接口来获取数据了 8 | 9 | #### 引入模块 10 | 11 | ```python 12 | from jaqs.data import DataApi 13 | ``` 14 | #### 登录数据服务器 15 | ```python 16 | api = DataApi(addr='tcp://data.quantos.org:8910') 17 | api.login(phone, token) 18 | ``` 19 | 20 | ### 调用数据接口 21 | 22 | 主要数据主要分为两大类: 23 | 24 | - **市场数据**,目前可使用的数据包括日线,分钟线,实时行情等。 25 | - **参考数据**,包括财务数据、公司行为数据、指数成份数据等。 26 | 27 | 数据API使用方法参考下面的数据API说明。 28 | 29 | -------------------------------------------------------------------------------- /test/test_report.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | from jaqs.trade.analyze.report import Report 3 | import jaqs.util as jutil 4 | 5 | 6 | def test_output(): 7 | static_folder = jutil.join_relative_path('trade/analyze/static') 8 | 9 | r = Report({'mytitle': 'Test Title', 'mytable': 'Hello World!'}, 10 | source_dir=static_folder, 11 | template_fn='test_template.html', 12 | out_folder='../output') 13 | r.generate_html() 14 | r.output_html() 15 | r.output_pdf() 16 | 17 | 18 | if __name__ == "__main__": 19 | test_output() 20 | -------------------------------------------------------------------------------- /doc/source/jaqs.trade.event.rst: -------------------------------------------------------------------------------- 1 | jaqs\.trade\.event package 2 | ========================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | jaqs\.trade\.event\.engine module 8 | --------------------------------- 9 | 10 | .. automodule:: jaqs.trade.event.engine 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | jaqs\.trade\.event\.eventtype module 16 | ------------------------------------ 17 | 18 | .. automodule:: jaqs.trade.event.eventtype 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: jaqs.trade.event 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /jaqs/trade/analyze/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Classes defined in analyze module help automate analysis of trading results. 3 | 4 | It takes a CSV file, trades.csv, which contains trading records, and a JSON file, 5 | configs.json, which contains some necessary configurations. Then it can automatically 6 | calculate PnL, position and various trade statistics and generate an HTML report. 7 | 8 | Usage: 9 | ta.initialize(dataview=dv, file_folder=backtest_result_dir_path) 10 | ta.do_analyze(result_dir=backtest_result_dir_path, selected_sec=list(ta.universe)[:3], brinson_group='sw1') 11 | """ 12 | 13 | from .analyze import EventAnalyzer, AlphaAnalyzer 14 | 15 | 16 | __all__ = ['EventAnalyzer', 'AlphaAnalyzer'] -------------------------------------------------------------------------------- /doc/source/jaqs.trade.analyze.rst: -------------------------------------------------------------------------------- 1 | jaqs\.trade\.analyze package 2 | ============================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | jaqs\.trade\.analyze\.analyze module 8 | ------------------------------------ 9 | 10 | .. automodule:: jaqs.trade.analyze.analyze 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | jaqs\.trade\.analyze\.report module 16 | ----------------------------------- 17 | 18 | .. automodule:: jaqs.trade.analyze.report 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: jaqs.trade.analyze 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /jaqs/trade/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | """ 3 | Basic data types, classes and models for trade. 4 | 5 | """ 6 | 7 | from .tradeapi import TradeApi 8 | from .backtest import AlphaBacktestInstance, EventBacktestInstance 9 | from .portfoliomanager import PortfolioManager 10 | from .livetrade import EventLiveTradeInstance, AlphaLiveTradeInstance 11 | from .strategy import Strategy, AlphaStrategy, EventDrivenStrategy 12 | from .tradegateway import BaseTradeApi, RealTimeTradeApi, AlphaTradeApi, BacktestTradeApi 13 | 14 | 15 | __all__ = ['TradeApi', 16 | 'AlphaBacktestInstance', 'EventBacktestInstance', 17 | 'PortfolioManager', 18 | 'EventLiveTradeInstance', 'AlphaLiveTradeInstance', 19 | 'Strategy', 'AlphaStrategy', 'EventDrivenStrategy', 20 | 'BaseTradeApi', 'RealTimeTradeApi', 'AlphaTradeApi', 'BacktestTradeApi'] 21 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | 2 | JAQS 3 | ========================================================== 4 | 5 | 在这里,你将可以获得: 6 | 7 | - 使用数据API,轻松获取研究数据 8 | - 根据策略模板,编写自己的量化策略 9 | - 使用回测框架,对策略进行回测和验证 10 | 11 | 查看代码,请点击\ `GitHub `__\ 12 | 13 | Install Guide 14 | ------------- 15 | 16 | A detailed step-by-step description of installation. 17 | 18 | .. toctree:: 19 | :maxdepth: 2 20 | 21 | install 22 | 23 | 24 | 用户手册 25 | ------------- 26 | 27 | 一个简洁清晰的入门指南, 涵盖各个功能模块. 28 | 29 | .. toctree:: 30 | :maxdepth: 2 31 | 32 | user_guide 33 | 34 | 35 | API Refrence 36 | ------------- 37 | 38 | If you want to know about package hierarchy, specific class / function... 39 | 40 | .. toctree:: 41 | :maxdepth: 2 42 | 43 | api_reference 44 | 45 | 46 | License 47 | ------- 48 | 49 | Apache 2.0许可协议。版权所有(c)2017 quantOS-org(量化开源会)。 50 | -------------------------------------------------------------------------------- /doc/source/jaqs.data.dataapi.rst: -------------------------------------------------------------------------------- 1 | jaqs\.data\.dataapi package 2 | =========================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | jaqs\.data\.dataapi\.data\_api module 8 | ------------------------------------- 9 | 10 | .. automodule:: jaqs.data.dataapi.data_api 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | jaqs\.data\.dataapi\.jrpc\_py module 16 | ------------------------------------ 17 | 18 | .. automodule:: jaqs.data.dataapi.jrpc_py 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | jaqs\.data\.dataapi\.utils module 24 | --------------------------------- 25 | 26 | .. automodule:: jaqs.data.dataapi.utils 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | 32 | Module contents 33 | --------------- 34 | 35 | .. automodule:: jaqs.data.dataapi 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | -------------------------------------------------------------------------------- /jaqs/trade/analyze/static/additional.css: -------------------------------------------------------------------------------- 1 | img { 2 | max-width: 80%; 3 | max-height: 80%; 4 | } 5 | body { 6 | background-color: #FBFBFB !important; 7 | } 8 | table { 9 | width: auto !important; 10 | } 11 | 12 | h2 { 13 | margin: 3em 0 0.5em 0 !important; 14 | padding: 5px 15px !important; 15 | color: white !important; 16 | background: #476EA1 !important; 17 | border: 1px solid #fff !important; 18 | border-radius: 0 10px 0 10px !important; 19 | } 20 | h3 { 21 | margin: 1em 0 0.5em 0 !important; 22 | padding: 5px 15px !important; 23 | } 24 | 25 | .right-bottom { 26 | position: fixed; 27 | right: 5em; 28 | bottom: 1em; 29 | } 30 | 31 | #mytoc { 32 | position: fixed; 33 | right: 0; 34 | top: 0; 35 | background-color:#FFF; 36 | } 37 | 38 | #mytoc #full { display: none; } /* Hide the full TOC by default */ 39 | 40 | #mytoc:hover #full{ 41 | display: block; /* Show it on hover */ 42 | } -------------------------------------------------------------------------------- /doc/source/jaqs.trade.tradeapi.rst: -------------------------------------------------------------------------------- 1 | jaqs\.trade\.tradeapi package 2 | ============================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | jaqs\.trade\.tradeapi\.jrpc\_py module 8 | -------------------------------------- 9 | 10 | .. automodule:: jaqs.trade.tradeapi.jrpc_py 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | jaqs\.trade\.tradeapi\.trade\_api module 16 | ---------------------------------------- 17 | 18 | .. automodule:: jaqs.trade.tradeapi.trade_api 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | jaqs\.trade\.tradeapi\.utils module 24 | ----------------------------------- 25 | 26 | .. automodule:: jaqs.trade.tradeapi.utils 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | 32 | Module contents 33 | --------------- 34 | 35 | .. automodule:: jaqs.trade.tradeapi 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | -------------------------------------------------------------------------------- /doc/source/jaqs.research.signaldigger.rst: -------------------------------------------------------------------------------- 1 | jaqs\.research\.signaldigger package 2 | ==================================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | jaqs\.research\.signaldigger\.digger module 8 | ------------------------------------------- 9 | 10 | .. automodule:: jaqs.research.signaldigger.digger 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | jaqs\.research\.signaldigger\.performance module 16 | ------------------------------------------------ 17 | 18 | .. automodule:: jaqs.research.signaldigger.performance 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | jaqs\.research\.signaldigger\.plotting module 24 | --------------------------------------------- 25 | 26 | .. automodule:: jaqs.research.signaldigger.plotting 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | 32 | Module contents 33 | --------------- 34 | 35 | .. automodule:: jaqs.research.signaldigger 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | -------------------------------------------------------------------------------- /test/test_strategy.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from __future__ import print_function 4 | from jaqs.trade import model 5 | import jaqs.util as jutil 6 | import random 7 | 8 | 9 | def test_context(): 10 | r = random.random() 11 | path = '../../output/tests/storage{:.6f}.pic'.format(r) 12 | context = model.Context() 13 | context.load_store(path) 14 | assert len(context.storage) == 0 15 | context.storage['me'] = 1.0 16 | context.save_store(path) 17 | 18 | context = model.Context() 19 | context.load_store(path) 20 | assert context.storage['me'] == 1.0 21 | 22 | 23 | if __name__ == "__main__": 24 | import time 25 | t_start = time.time() 26 | 27 | g = globals() 28 | g = {k: v for k, v in g.items() if k.startswith('test_') and callable(v)} 29 | 30 | for test_name, test_func in g.items(): 31 | print("\n==========\nTesting {:s}...".format(test_name)) 32 | test_func() 33 | print("Test Complete.") 34 | 35 | t3 = time.time() - t_start 36 | print("\n\n\nTime lapsed in total: {:.1f}".format(t3)) 37 | -------------------------------------------------------------------------------- /jaqs/data/dataapi/README.md: -------------------------------------------------------------------------------- 1 | # DataApi 2 | 3 | 标准数据API定义。 4 | 5 | # 安装步骤 6 | 7 | ## 1、安装Python环境 8 | 9 | 如果本地还没有安装Python环境,强烈建议安装Anaconda(Python的集成开发环境,包含众多常用包,且易于安装,避免不必要的麻烦)。打开[Anaconda官网](http://www.continuum.io/downloads),选择相应的操作系统,确定要安装的Python版本,进行下载。 10 | 11 | 下载完成以后,按照图形界面步骤完成安装。在默认情况下,Anaconda会自动设置PATH环境。 12 | 13 | ***注***:如果安装过程遇到问题,或需要更详细的步骤,请参见[安装Anaconda Python环境教程](https://github.com/quantOS-org/JAQS/blob/master/doc/install.md#1安装python环境) 14 | 15 | ## 2、安装依赖包 16 | 17 | 如果Python环境不是类似Anaconda的集成开发环境,我们需要单独安装依赖包,在已经有pandas/numpy包前提下,还需要有以下几个包: 18 | - `pyzmq` 19 | - `msgpack_python` 20 | - `python-snappy` 21 | 22 | 可以通过单个安装完成,例如: `pip install pyzmq` 23 | 24 | 需要注意的是,`python-snappy`的安装需要比较多的编译依赖,请按照[如何安装python-snappy包](https://github.com/quantOS-org/JAQS/blob/master/doc/install.md#如何安装python-snappy包)所述安装。 25 | 26 | 27 | ## 3、使用DataApi 28 | 29 | ```python 30 | from DataApi import DataApi # 这里假设项目目录名为DataApi, 且存放在工作目录下 31 | 32 | api = DataApi(addr="tcp://data.quantos.org:8910") 33 | result, msg = api.login("phone", "token") # 示例账户,用户需要改为自己在www.quantos.org上注册的账户 34 | print(result) 35 | print(msg) 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /jaqs/data/basic/__init__.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | jaqs.data.basic module includes basic definitions of basic data types 4 | that are used across jaqs package. 5 | 6 | Basic data types: 7 | - `Instrument`: An Instrument represents a specific financial contract. 8 | - `InstManager`: InstManager query information of instruments from data server and store them. 9 | - `Bar`: A Bar is a summary of information of price and volume during a certain length of time span. 10 | - `Quote`: Quote represents a snapshot of price and volume information. 11 | - `Order`: Basic order class. 12 | - `OrderStatusInd`: OrderStatusInd is a indication of status change of an order. 13 | - `Task`: Task is a high-level trading target, which may contain many orders. 14 | - `Position`: Basic position class. 15 | - `Trade`: Trade represents fill/partial fill of an order. 16 | - `TradeInd`: TaskInd is a indication of status change of a task. 17 | - `TradeStat`: TradeStat stores statistics of trading of a certain symbol. 18 | 19 | """ 20 | from .marketdata import * 21 | from .order import * 22 | from .trade import * 23 | from .position import * 24 | from .instrument import * 25 | -------------------------------------------------------------------------------- /jaqs/trade/tradeapi/README.md: -------------------------------------------------------------------------------- 1 | # TradeApi 2 | 3 | 标准交易API定义 4 | 5 | # 安装步骤 6 | 7 | ## 1、安装Python环境 8 | 9 | 如果本地还没有安装Python环境,强烈建议安装Anaconda(Python的集成开发环境,包含众多常用包,且易于安装,避免不必要的麻烦)。打开[Anaconda官网](http://www.continuum.io/downloads),选择相应的操作系统,确定要安装的Python版本,进行下载。 10 | 11 | 下载完成以后,按照图形界面步骤完成安装。在默认情况下,Anaconda会自动设置PATH环境。 12 | 13 | ***注***:如果安装过程遇到问题,或需要更详细的步骤,请参见[安装Anaconda Python环境教程](https://github.com/quantOS-org/JAQS/blob/master/doc/install.md#1安装python环境) 14 | 15 | ## 2、安装依赖包 16 | 17 | 如果Python环境不是类似Anaconda的集成开发环境,我们需要单独安装依赖包,在已经有pandas/numpy包前提下,还需要有以下几个包: 18 | - `pyzmq` 19 | - `msgpack_python` 20 | - `python-snappy` 21 | 22 | 可以通过单个安装完成,例如: `pip install pyzmq` 23 | 24 | 需要注意的是,`python-snappy`的安装需要比较多的编译依赖,请按照[如何安装python-snappy包](https://github.com/quantOS-org/JAQS/blob/master/doc/install.md#如何安装python-snappy包)所述安装。 25 | 26 | 27 | ## 3、使用TradeApi 28 | 29 | 在项目目录,验证`TradeApi`是否能够正常使用。 30 | 31 | ```python 32 | from TradeApi import TradeApi # 这里假设项目目录名为TradeApi, 且存放在工作目录下 33 | 34 | api = TradeApi(addr="tcp://gw.quantos.org:8901") 35 | result, msg = api.login("username", "token") # 示例账户,用户需要改为自己在www.quantos.org上注册的账户 36 | print result 37 | print msg 38 | ``` 39 | -------------------------------------------------------------------------------- /doc/source/md2rst.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | import os 4 | from os.path import join 5 | import jaqs.util as jutil 6 | import subprocess 7 | 8 | 9 | def md2rst(): 10 | input_dir = '..' 11 | output_dir = '.' 12 | 13 | for dir_path, dir_names, file_names in os.walk(input_dir): 14 | for fn in file_names: 15 | if fn.endswith('.md'): 16 | print("Converting {:s}...".format(fn)) 17 | 18 | fn_pure = fn[:-2] 19 | fn_md = join(input_dir, fn) 20 | fn_html = join(input_dir, fn_pure+'html') 21 | fn_rst = join(output_dir, fn_pure+'rst') 22 | 23 | subprocess.check_output(['pandoc', fn_md, 24 | '-f', 'markdown_github', 25 | '-t', 'html', '-s', '-o', fn_html]) 26 | subprocess.check_output(['pandoc', fn_html, 27 | '-f', 'html', 28 | '-t', 'rst', '-s', '-o', fn_rst]) 29 | os.remove(fn_html) 30 | 31 | 32 | if __name__ == "__main__": 33 | md2rst() -------------------------------------------------------------------------------- /jaqs/trade/event/eventtype.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | from jaqs.trade import common 4 | 5 | ''' 6 | 本文件仅用于存放对于事件类型常量的定义。 7 | 8 | 由于python中不存在真正的常量概念,因此选择使用全大写的变量名来代替常量。 9 | 这里设计的命名规则以EVENT_前缀开头。 10 | 11 | 常量的内容通常选择一个能够代表真实意义的字符串(便于理解)。 12 | 13 | 建议将所有的常量定义放在该文件中,便于检查是否存在重复的现象。 14 | ''' 15 | 16 | 17 | class EVENT_TYPE(common.ReprStrEnum): 18 | TIMER = 'timer' # 计时器事件,每隔1秒发送一次 19 | MARKET_DATA = 'market_data' # 行情事件 20 | 21 | ORDER_RSP = 'order_rsp' 22 | TASK_RSP = 'task_rsp' 23 | TASK_STATUS_IND = 'task_callback' 24 | TRADE_IND = 'trade_ind' # 成交回报 25 | ORDER_STATUS_IND = 'order_status_ind' # 状态回报 26 | 27 | PLACE_ORDER = 'place_order' 28 | CANCEL_ORDER = 'cancel_order' 29 | 30 | QUERY_ACCOUNT = 'query_account' 31 | QUERY_UNIVERSE = 'query_universe' 32 | QUERY_PORTFOLIO = 'query_portfolio' 33 | QUERY_POSITION = 'query_position' 34 | QUERY_ORDER = 'query_order' 35 | QUERY_TASK = 'query_task' 36 | QUERY_TRADE = 'query_trade' 37 | 38 | GOAL_PORTFOLIO = 'goal_portfolio' 39 | STOP_PORTFOLIO = 'stop_portfolio' 40 | 41 | TRADE_API_DISCONNECTED = 'trade_api_disconnected' 42 | TRADE_API_CONNECTED = 'trade_api_connected' 43 | -------------------------------------------------------------------------------- /doc/source/jaqs.data.basic.rst: -------------------------------------------------------------------------------- 1 | jaqs\.data\.basic package 2 | ========================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | jaqs\.data\.basic\.instrument module 8 | ------------------------------------ 9 | 10 | .. automodule:: jaqs.data.basic.instrument 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | jaqs\.data\.basic\.marketdata module 16 | ------------------------------------ 17 | 18 | .. automodule:: jaqs.data.basic.marketdata 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | jaqs\.data\.basic\.order module 24 | ------------------------------- 25 | 26 | .. automodule:: jaqs.data.basic.order 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | jaqs\.data\.basic\.position module 32 | ---------------------------------- 33 | 34 | .. automodule:: jaqs.data.basic.position 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | jaqs\.data\.basic\.trade module 40 | ------------------------------- 41 | 42 | .. automodule:: jaqs.data.basic.trade 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | 48 | Module contents 49 | --------------- 50 | 51 | .. automodule:: jaqs.data.basic 52 | :members: 53 | :undoc-members: 54 | :show-inheritance: 55 | -------------------------------------------------------------------------------- /doc/source/jaqs.util.rst: -------------------------------------------------------------------------------- 1 | jaqs\.util package 2 | ================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | jaqs\.util\.dtutil module 8 | ------------------------- 9 | 10 | .. automodule:: jaqs.util.dtutil 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | jaqs\.util\.fileio module 16 | ------------------------- 17 | 18 | .. automodule:: jaqs.util.fileio 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | jaqs\.util\.numeric module 24 | -------------------------- 25 | 26 | .. automodule:: jaqs.util.numeric 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | jaqs\.util\.pdutil module 32 | ------------------------- 33 | 34 | .. automodule:: jaqs.util.pdutil 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | jaqs\.util\.profile module 40 | -------------------------- 41 | 42 | .. automodule:: jaqs.util.profile 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | jaqs\.util\.sequence module 48 | --------------------------- 49 | 50 | .. automodule:: jaqs.util.sequence 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | 56 | Module contents 57 | --------------- 58 | 59 | .. automodule:: jaqs.util 60 | :members: 61 | :undoc-members: 62 | :show-inheritance: 63 | -------------------------------------------------------------------------------- /doc/source/jaqs.data.rst: -------------------------------------------------------------------------------- 1 | jaqs\.data package 2 | ================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | jaqs.data.basic 10 | jaqs.data.dataapi 11 | 12 | Submodules 13 | ---------- 14 | 15 | jaqs\.data\.align module 16 | ------------------------ 17 | 18 | .. automodule:: jaqs.data.align 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | jaqs\.data\.continueContract module 24 | ----------------------------------- 25 | 26 | .. automodule:: jaqs.data.continueContract 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | jaqs\.data\.dataservice module 32 | ------------------------------ 33 | 34 | .. automodule:: jaqs.data.dataservice 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | jaqs\.data\.dataview module 40 | --------------------------- 41 | 42 | .. automodule:: jaqs.data.dataview 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | jaqs\.data\.py\_expression\_eval module 48 | --------------------------------------- 49 | 50 | .. automodule:: jaqs.data.py_expression_eval 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | 56 | Module contents 57 | --------------- 58 | 59 | .. automodule:: jaqs.data 60 | :members: 61 | :undoc-members: 62 | :show-inheritance: 63 | -------------------------------------------------------------------------------- /test/test_data_api.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | from __future__ import print_function 4 | from jaqs.data import DataApi 5 | from jaqs.trade import common 6 | import jaqs.util as jutil 7 | 8 | from config_path import DATA_CONFIG_PATH 9 | data_config = jutil.read_json(DATA_CONFIG_PATH) 10 | 11 | 12 | def test_data_api(): 13 | address = data_config.get("remote.data.address", None) 14 | username = data_config.get("remote.data.username", None) 15 | password = data_config.get("remote.data.password", None) 16 | if address is None or username is None or password is None: 17 | raise ValueError("no data service config available!") 18 | 19 | api = DataApi(address, use_jrpc=False) 20 | login_msg = api.login(username=username, password=password) 21 | print(login_msg) 22 | 23 | daily, msg = api.daily(symbol="600030.SH,000002.SZ", start_date=20170103, end_date=20170708, 24 | fields="open,high,low,close,volume,last,trade_date,settle") 25 | daily2, msg2 = api.daily(symbol="600030.SH", start_date=20170103, end_date=20170708, 26 | fields="open,high,low,close,volume,last,trade_date,settle") 27 | # err_code, err_msg = msg.split(',') 28 | assert msg == '0,' 29 | assert msg2 == '0,' 30 | assert daily.shape == (248, 9) 31 | assert daily2.shape == (124, 9) 32 | 33 | df, msg = api.bar(symbol="600030.SH", trade_date=20170904, freq=common.QUOTE_TYPE.MIN, start_time=90000, end_time=150000) 34 | print(df.columns) 35 | assert df.shape == (240, 15) 36 | 37 | print("test passed") 38 | 39 | 40 | if __name__ == "__main__": 41 | test_data_api() 42 | -------------------------------------------------------------------------------- /jaqs/data/dataapi/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /jaqs/trade/tradeapi/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /doc/source/jaqs.trade.rst: -------------------------------------------------------------------------------- 1 | jaqs\.trade package 2 | =================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | jaqs.trade.analyze 10 | jaqs.trade.event 11 | jaqs.trade.tradeapi 12 | 13 | Submodules 14 | ---------- 15 | 16 | jaqs\.trade\.backtest module 17 | ---------------------------- 18 | 19 | .. automodule:: jaqs.trade.backtest 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: 23 | 24 | jaqs\.trade\.common module 25 | -------------------------- 26 | 27 | .. automodule:: jaqs.trade.common 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | 32 | jaqs\.trade\.livetrade module 33 | ----------------------------- 34 | 35 | .. automodule:: jaqs.trade.livetrade 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | 40 | jaqs\.trade\.model module 41 | ------------------------- 42 | 43 | .. automodule:: jaqs.trade.model 44 | :members: 45 | :undoc-members: 46 | :show-inheritance: 47 | 48 | jaqs\.trade\.portfoliomanager module 49 | ------------------------------------ 50 | 51 | .. automodule:: jaqs.trade.portfoliomanager 52 | :members: 53 | :undoc-members: 54 | :show-inheritance: 55 | 56 | jaqs\.trade\.strategy module 57 | ---------------------------- 58 | 59 | .. automodule:: jaqs.trade.strategy 60 | :members: 61 | :undoc-members: 62 | :show-inheritance: 63 | 64 | jaqs\.trade\.tradegateway module 65 | -------------------------------- 66 | 67 | .. automodule:: jaqs.trade.tradegateway 68 | :members: 69 | :undoc-members: 70 | :show-inheritance: 71 | 72 | 73 | Module contents 74 | --------------- 75 | 76 | .. automodule:: jaqs.trade 77 | :members: 78 | :undoc-members: 79 | :show-inheritance: 80 | -------------------------------------------------------------------------------- /doc/base_data.md: -------------------------------------------------------------------------------- 1 | # 基础数据 2 | 3 | 4 | ## 调用说明 5 | - 通过api.query函数调用,第一个参数view需填入对应的接口名,如:`view="jz.instrumentInfo"` 6 | - 输入参数指的是filter参数里面的内容,通过'&'符号拼接,如:`filter="inst_type=&status=1&symbol="` 7 | - 输出参数指的是fields里面的内容,通过','隔开 8 | 9 | 样例代码:获取上市股票列表 10 | ```python 11 | df, msg = api.query( 12 | view="jz.instrumentInfo", 13 | fields="status,list_date, fullname_en, market", 14 | filter="inst_type=1&status=1&symbol=", 15 | data_format='pandas') 16 | ``` 17 | 18 | ## 目前支持的接口及其含义 19 | 20 | | 接口 | view | 分类 | 21 | | ------------------ | --------------------- | ---------- | 22 | | 证券基础信息表 | jz.instrumentInfo | 基础信息 | 23 | | 交易日历表 | jz.secTradeCal | 基础信息 | 24 | | 分配除权信息表 | lb.secDividend | 股票 | 25 | | 复权因子表 | lb.secAdjFactor | 股票 | 26 | | 停复牌信息表 | lb.secSusp | 股票 | 27 | | 行业分类表 | lb.secIndustry | 股票 | 28 | | 日行情估值表 | lb.secDailyIndicator | 股票 | 29 | | 资产负债表 | lb.balanceSheet | 股票 | 30 | | 利润表 | lb.income | 股票 | 31 | | 现金流量表 | lb.cashFlow | 股票 | 32 | | 业绩快报 | lb.profitExpress | 股票 | 33 | | 限售股解禁表 | lb.secRestricted | 股票 | 34 | | 指数基本信息表 | lb.indexInfo | 指数 | 35 | | 指数成份股表 | lb.indexCons | 指数 | 36 | | 公募基金净值表 | lb.mfNav | 基金 | 37 | | 公募基金分红表 | lb.mfDividend | 基金 | 38 | | 公募基金股票组合表 | lb.mfPortfolio | 基金 | 39 | | 公募基金债券组合表 | lb.mfBondPortfolio | 基金 | 40 | 41 | 接口还在进一步的丰富过程中。 42 | 43 | 详细的说明文档,please refer to [quantos](https://www.quantos.org) 44 | -------------------------------------------------------------------------------- /jaqs/util/numeric.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | import numpy as np 3 | import pandas as pd 4 | 5 | 6 | def quantilize_without_nan(mat, n_quantiles=5, axis=-1): 7 | mask = pd.isnull(mat) 8 | res = mat.copy() 9 | 10 | rank = res.argsort(axis=axis).argsort(axis=axis) 11 | count = np.sum(~mask, axis=axis) # int 12 | 13 | divisor = count * 1. / n_quantiles # float 14 | shape = list(res.shape) 15 | shape[axis] = 1 16 | divisor = divisor.reshape(*shape) 17 | res = np.floor(rank / divisor) + 1.0 18 | 19 | res[mask] = np.nan 20 | 21 | return res 22 | 23 | # res[~mask] = pd.qcut(mat[~mask], n_quantiles, labels = False) + 1 24 | # rank = mat.argsort(axis=axis).argsort(axis=axis) # int 25 | # 26 | # count = np.sum(~mask, axis=axis) # int 27 | # divisor = count * 1. / n_quantiles # float 28 | # shape = list(mat.shape) 29 | # shape[axis] = 1 30 | # divisor = divisor.reshape(*shape) 31 | # 32 | # res = np.floor(rank / divisor) + 1.0 33 | # res[mask] = np.nan 34 | 35 | #return res 36 | 37 | 38 | # Boolean, unsigned integer, signed integer, float, complex. 39 | _NUMERIC_KINDS = set('buifc') 40 | 41 | 42 | def is_numeric(array): 43 | """ 44 | Determine whether the argument has a numeric datatype, when 45 | converted to a NumPy array. 46 | 47 | Booleans, unsigned integers, signed integers, floats and complex 48 | numbers are the kinds of numeric datatype. 49 | 50 | Parameters 51 | ---------- 52 | array : array-like 53 | The array to check. 54 | 55 | Returns 56 | ------- 57 | is_numeric : `bool` 58 | True if the array has a numeric datatype, False if not. 59 | 60 | """ 61 | return np.asarray(array).dtype.kind in _NUMERIC_KINDS 62 | -------------------------------------------------------------------------------- /doc/how_to_ask_questions.md: -------------------------------------------------------------------------------- 1 | # 如何正确提问 2 | 3 | 感谢大家的使用和建议,我们会持续改进。为了更高效地解决问题,请在提问时遵循一定的规范。 4 | 5 | ## 总结 6 | 7 | 附上代码、附上报错信息、附上Python版本和系统信息。 8 | 9 | ## 提问模板 10 | 11 | ``` 12 | What steps will reproduce the problem? 13 | 该问题的重现步骤是什么? 14 | 1. 15 | 2. 16 | 3. 17 | 18 | What is the expected output? What do you see instead? 19 | 你期待的结果是什么?实际看到的又是什么? 20 | 21 | 22 | What version of the product are you using? On what operating system? 23 | 你正在使用产品的哪个版本?在什么操作系统上? 24 | 25 | Please provide any additional information below. 26 | 如果有的话,请在下面提供更多信息。 27 | ``` 28 | 29 | ## 具体解释 30 | 31 | 什么是“最短代码-错误重现”提问方式? 32 | 33 | ### 帖子标题 = 帖子内容的提炼 34 | 35 | 简洁清晰,言之有物。让他人看了标题就能大概知道是否能帮助你,切勿使用“求助、急、帮忙、新手、高手、在线等”等无任何意义的词语。 36 | 37 | > 我遇到了一个 DataView的问题 38 | > TradeApi 在我的系统上运行不了 39 | 40 | 上面的标题很糟糕,光看标题作者无法知道发生了什么事。当开源社区的问题很多时,上面这类标题,经常会让作者直接忽视或将优先级降到很低。更妥当的标题是 41 | 42 | > DataView 在prepare_data时报NotLoginError 43 | > TradeApi 在 Ubuntu 14.04 上运行时提示-1,no connection 44 | 45 | ### 帖子正文 = 场景描述 + 代码 + 报错信息 46 | 47 | - 粘贴一个简单的程序程序代码 48 | - 贴出你的错误或警告信息(特别重要),以及你已经尝试过的方法、步骤(必要的时候,可以上传一些截图); 49 | - 写上你使用的是哪个MATLAB版本(如:64位R2012a等),以及操作系统(如:32位XP系统、64位Windows 7系统等)。 50 | 51 | #### 描述事实、而不是猜测 52 | 53 | 事实是指,依次进行了哪些操作、产生了怎样的结果。比如 54 | 55 | > 我在 Windows 10 下用 pip 打开JAQS后报错no snappy,按照安装指南安装python-snappy后,pandas包无法使用。 56 | 57 | 上面是一段比较好的事实描述(更好的是把错误提示也截图上来),而不要像下面这样猜测: 58 | 59 | > 安装JAQS导致pandas报错,我怀疑项目对Win10的兼容不好。 60 | 61 | 上面的描述,会让作者一头雾水。尽量避免猜测性描述,除非你能先描述事实,在事实描述清楚之后,再给出合理的猜测是欢迎的。 62 | 63 | #### 描述目标、而不是过程 64 | 65 | 经常会有这种情况,提问者在脑袋里有个更高层次的目标,他们在自以为能达到目标的特定道路上卡住了,然后跑来问该怎么走。举一个生活中的例子 66 | 67 | > 咖啡馆应该为每种咖啡提供5档不同甜度的版本 68 | 69 | 上面这个问题的背后,提问者认为不同顾客对甜度有不同偏好,咖啡馆在做咖啡时应满足。但事实上,正确的方式是提供糖,让顾客自己选择加多少。如果只是描述过程(提供不同甜度的咖啡)不描述目的(满足人们的甜度偏好),往往会把其他人也绕进去。 70 | 71 | 即很多情况下,对于要解决的问题,实际上有更好的方式。这种情况下,描述清楚目标,讲清楚要干什么非常重要。 72 | 73 | 74 | 75 | 本文参考了:[如何向开源社区提问题](https://github.com/seajs/seajs/issues/545) 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Private files 2 | example/private/ 3 | 4 | # cProfile results 5 | *.prof 6 | 7 | # Personal Configs 8 | # etc/*.json 9 | 10 | # IDE configs 11 | .idea/ 12 | .eclipse/ 13 | 14 | # Output Results 15 | output/ 16 | 17 | # Byte-compiled / optimized / DLL files 18 | __pycache__/ 19 | *.py[cod] 20 | *$py.class 21 | 22 | # C extensions 23 | *.so 24 | 25 | # Distribution / packaging 26 | .Python 27 | build/ 28 | develop-eggs/ 29 | dist/ 30 | downloads/ 31 | eggs/ 32 | .eggs/ 33 | lib/ 34 | lib64/ 35 | parts/ 36 | sdist/ 37 | var/ 38 | wheels/ 39 | *.egg-info/ 40 | .installed.cfg 41 | *.egg 42 | 43 | # PyInstaller 44 | # Usually these files are written by a python script from a template 45 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 46 | *.manifest 47 | *.spec 48 | 49 | # Installer logs 50 | pip-log.txt 51 | pip-delete-this-directory.txt 52 | 53 | # Unit test / coverage reports 54 | htmlcov/ 55 | .tox/ 56 | .coverage 57 | .coverage.* 58 | .cache 59 | nosetests.xml 60 | coverage.xml 61 | *.cover 62 | .hypothesis/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | 72 | # Flask stuff: 73 | instance/ 74 | .webassets-cache 75 | 76 | # Scrapy stuff: 77 | .scrapy 78 | 79 | # Sphinx documentation 80 | docs/_build/ 81 | 82 | # PyBuilder 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # pyenv 89 | .python-version 90 | 91 | # celery beat schedule file 92 | celerybeat-schedule 93 | 94 | # SageMath parsed files 95 | *.sage.py 96 | 97 | # Environments 98 | .env 99 | .venv 100 | env/ 101 | venv/ 102 | ENV/ 103 | 104 | # Spyder project settings 105 | .spyderproject 106 | .spyproject 107 | 108 | # Rope project settings 109 | .ropeproject 110 | 111 | # mkdocs documentation 112 | /site 113 | 114 | # mypy 115 | .mypy_cache/ 116 | 117 | # output 118 | example/alpha/out/ 119 | example/alpha/out2 120 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from jaqs import __version__ as ver 3 | import codecs 4 | import os 5 | 6 | 7 | def read(fname): 8 | return codecs.open(os.path.join(os.path.dirname(__file__), fname), 'r', encoding='utf-8').read() 9 | 10 | 11 | def readme(): 12 | with codecs.open('README.rst', 'r', encoding='utf-8') as f: 13 | return f.read() 14 | 15 | 16 | def read_install_requires(): 17 | with codecs.open('requirements.txt', 'r', encoding='utf-8') as f: 18 | res = f.readlines() 19 | res = list(map(lambda s: s.replace('\n', ''), res)) 20 | return res 21 | 22 | setup( 23 | # Install data files specified in MANIFEST.in file. 24 | include_package_data=True, 25 | #package_data={'': ['*.json', '*.css', '*.html']}, 26 | # Package Information 27 | name='jaqs', 28 | url='https://github.com/quantOS-org/JAQS', 29 | version=ver, 30 | license='Apache 2.0', 31 | # information 32 | description='Open source quantitative research&trading framework.', 33 | long_description=readme(), 34 | keywords="quantiatitive trading research finance", 35 | classifiers=[ 36 | "Development Status :: 4 - Beta", 37 | "Intended Audience :: Developers", 38 | "Intended Audience :: Education", 39 | "Intended Audience :: End Users/Desktop", 40 | "Intended Audience :: Financial and Insurance Industry", 41 | "Intended Audience :: Information Technology", 42 | "Intended Audience :: Science/Research", 43 | "License :: OSI Approved :: Apache Software License", 44 | "Natural Language :: Chinese (Simplified)", 45 | "Natural Language :: English", 46 | "Operating System :: Microsoft :: Windows", 47 | "Operating System :: Unix", 48 | "Programming Language :: Python :: 2.7", 49 | "Programming Language :: Python :: 3.6", 50 | ], 51 | # install 52 | install_requires=read_install_requires(), 53 | packages=find_packages(), 54 | # author 55 | author='quantOS' 56 | ) 57 | -------------------------------------------------------------------------------- /doc/source/how_to_ask_questions.rst: -------------------------------------------------------------------------------- 1 | 如何正确提问 2 | ============ 3 | 4 | 感谢大家的使用和建议,我们会持续改进。为了更高效地解决问题,请在提问时遵循一定的规范。 5 | 6 | 总结 7 | ---- 8 | 9 | 附上代码、附上报错信息、附上Python版本和系统信息。 10 | 11 | 提问模板 12 | -------- 13 | 14 | :: 15 | 16 | What steps will reproduce the problem? 17 | 该问题的重现步骤是什么? 18 | 1. 19 | 2. 20 | 3. 21 | 22 | What is the expected output? What do you see instead? 23 | 你期待的结果是什么?实际看到的又是什么? 24 | 25 | 26 | What version of the product are you using? On what operating system? 27 | 你正在使用产品的哪个版本?在什么操作系统上? 28 | 29 | Please provide any additional information below. 30 | 如果有的话,请在下面提供更多信息。 31 | 32 | 具体解释 33 | -------- 34 | 35 | 什么是“最短代码-错误重现”提问方式? 36 | 37 | 帖子标题 = 帖子内容的提炼 38 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 39 | 40 | 简洁清晰,言之有物。让他人看了标题就能大概知道是否能帮助你,切勿使用“求助、急、帮忙、新手、高手、在线等”等无任何意义的词语。 41 | 42 | | 我遇到了一个 DataView的问题 43 | | TradeApi 在我的系统上运行不了 44 | 45 | 上面的标题很糟糕,光看标题作者无法知道发生了什么事。当开源社区的问题很多时,上面这类标题,经常会让作者直接忽视或将优先级降到很低。更妥当的标题是 46 | 47 | | DataView 在prepare\_data时报NotLoginError 48 | | TradeApi 在 Ubuntu 14.04 上运行时提示-1,no connection 49 | 50 | 帖子正文 = 场景描述 + 代码 + 报错信息 51 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 52 | 53 | - 粘贴一个简单的程序程序代码 54 | - 贴出你的错误或警告信息(特别重要),以及你已经尝试过的方法、步骤(必要的时候,可以上传一些截图); 55 | - 写上你使用的是哪个MATLAB版本(如:64位R2012a等),以及操作系统(如:32位XP系统、64位Windows 56 | 7系统等)。 57 | 58 | 描述事实、而不是猜测 59 | ^^^^^^^^^^^^^^^^^^^^ 60 | 61 | 事实是指,依次进行了哪些操作、产生了怎样的结果。比如 62 | 63 | 我在 Windows 10 下用 pip 打开JAQS后报错no 64 | snappy,按照安装指南安装python-snappy后,pandas包无法使用。 65 | 66 | 上面是一段比较好的事实描述(更好的是把错误提示也截图上来),而不要像下面这样猜测: 67 | 68 | 安装JAQS导致pandas报错,我怀疑项目对Win10的兼容不好。 69 | 70 | 上面的描述,会让作者一头雾水。尽量避免猜测性描述,除非你能先描述事实,在事实描述清楚之后,再给出合理的猜测是欢迎的。 71 | 72 | 描述目标、而不是过程 73 | ^^^^^^^^^^^^^^^^^^^^ 74 | 75 | 经常会有这种情况,提问者在脑袋里有个更高层次的目标,他们在自以为能达到目标的特定道路上卡住了,然后跑来问该怎么走。举一个生活中的例子 76 | 77 | 咖啡馆应该为每种咖啡提供5档不同甜度的版本 78 | 79 | 上面这个问题的背后,提问者认为不同顾客对甜度有不同偏好,咖啡馆在做咖啡时应满足。但事实上,正确的方式是提供糖,让顾客自己选择加多少。如果只是描述过程(提供不同甜度的咖啡)不描述目的(满足人们的甜度偏好),往往会把其他人也绕进去。 80 | 81 | 即很多情况下,对于要解决的问题,实际上有更好的方式。这种情况下,描述清楚目标,讲清楚要干什么非常重要。 82 | 83 | 本文参考了:\ `如何向开源社区提问题 `__ 84 | -------------------------------------------------------------------------------- /jaqs/trade/analyze/static/bootstrap-toc/bootstrap-toc.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v<%= version %> (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | 6 | /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */ 7 | 8 | /* All levels of nav */ 9 | nav[data-toggle='toc'] .nav > li > a { 10 | display: block; 11 | padding: 4px 20px; 12 | font-size: 14px; 13 | font-weight: 500; 14 | color: #767676; 15 | } 16 | nav[data-toggle='toc'] .nav > li > a:hover, 17 | nav[data-toggle='toc'] .nav > li > a:focus { 18 | padding-left: 19px; 19 | color: #563d7c; 20 | text-decoration: none; 21 | background-color: transparent; 22 | border-left: 1px solid #563d7c; 23 | } 24 | nav[data-toggle='toc'] .nav > .active > a, 25 | nav[data-toggle='toc'] .nav > .active:hover > a, 26 | nav[data-toggle='toc'] .nav > .active:focus > a { 27 | padding-left: 18px; 28 | font-weight: bold; 29 | color: #563d7c; 30 | background-color: transparent; 31 | border-left: 2px solid #563d7c; 32 | } 33 | 34 | /* Nav: second level (shown on .active) */ 35 | nav[data-toggle='toc'] .nav .nav { 36 | display: none; /* Hide by default, but at >768px, show it */ 37 | padding-bottom: 10px; 38 | } 39 | nav[data-toggle='toc'] .nav .nav > li > a { 40 | padding-top: 1px; 41 | padding-bottom: 1px; 42 | padding-left: 30px; 43 | font-size: 12px; 44 | font-weight: normal; 45 | } 46 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 47 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 48 | padding-left: 29px; 49 | } 50 | nav[data-toggle='toc'] .nav .nav > .active > a, 51 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 52 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 53 | padding-left: 28px; 54 | font-weight: 500; 55 | } 56 | 57 | /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ 58 | nav[data-toggle='toc'] .nav > .active > ul { 59 | display: block; 60 | } 61 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | |pypi peoject version| 4 | |pypi pyversion| 5 | |pypi license| 6 | |travis ci| 7 | |covergae| 8 | 9 | JAQS是一个开源量化策略研究平台,由交易专家和金融技术专家共同设计,实现了自动化信号研究、高效策略开发和多维度回测分析,支持Alpha、CTA、套利等策略的实现。JAQS从实战而来,经实盘检验,本地化开发部署,保障策略安全。 10 | 11 | |jaqsflowchart| 12 | 13 | Features 14 | ======== 15 | 16 | - 通过统一的DataApi,获取、存储和管理数据。 17 | - 通过数学公式快速定义并分析信号;实现Alpha选股、CTA、套利等各类量化交易策略,对策略进行历史回测。 18 | - 通过统一的TradeApi,接入在线仿真系统进行仿真交易,跟踪策略表现。对接实盘通道实现实盘交易(当然需要用户搞定交易通道)。 19 | - 完全本地化,代码可以部署在任意个人电脑或服务器上,本地化运行,策略安全性有保证。 20 | - 模块化设计,通过标准的输入输出接口,做到数据与回测分离,交易与分析分离, 每一个环节都清晰可控,达到机构级别的标准化、流程化。 21 | - 面向实盘编程,数据构建时进行严格的对齐,回测时提供当前快照而不是数据查询接口,防止未来函数的出现;通过对策略类的精巧设计,使回测与实盘/仿真交易可使用同一套策略代码,始于开展严谨的研究、回测。 22 | 23 | Installation 24 | ============ 25 | 26 | 参见 \ `安装指南 `__\ 27 | 28 | Quickstart 29 | ========== 30 | 31 | 参见 \ `用户手册 `__\. 32 | 33 | 更多示例可在项目的 ``example`` 文件夹下找到,如 ``example/alpha/select_stocks_pe_profit.py`` . 34 | 35 | 查看完整文档,请访问: \ `jaqs.readthedocs.io `__\ 36 | 37 | Contribute 38 | =========== 39 | 40 | 欢迎参与开发!可以通过Pull Request的方式提交代码。 41 | 42 | 43 | Questions 44 | ========== 45 | 46 | - 如果您发现BUG,请到\ `这里 `__\提交。 47 | - 如果有问题、建议,也可以到 \ `社区 `__\ 参与讨论。 48 | 49 | 提问前,请查看 \ `如何正确提问 `__\ 。 50 | 51 | 52 | License 53 | ======= 54 | 55 | Apache 2.0许可协议。版权所有(c)2017 quantOS-org. 56 | 57 | 58 | 59 | .. |jaqsflowchart| image:: https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/jaqs.png 60 | 61 | .. |pypi peoject version| image:: https://img.shields.io/pypi/v/jaqs.svg 62 | :target: https://pypi.python.org/pypi/jaqs 63 | .. |pypi license| image:: https://img.shields.io/pypi/l/jaqs.svg 64 | :target: https://opensource.org/licenses/Apache-2.0 65 | .. |pypi pyversion| image:: https://img.shields.io/pypi/pyversions/jaqs.svg 66 | :target: https://pypi.python.org/pypi/jaqs 67 | .. |travis ci| image:: https://travis-ci.org/quantOS-org/JAQS.svg?branch=master 68 | :target: https://travis-ci.org/quantOS-org/JAQS 69 | .. |covergae| image:: https://coveralls.io/repos/github/quantOS-org/JAQS/badge.svg?branch=master 70 |   :target: https://coveralls.io/github/quantOS-org/JAQS?branch=master 71 | -------------------------------------------------------------------------------- /doc/research.md: -------------------------------------------------------------------------------- 1 | ## 研究 2 | 3 | 信号研究与回测: `SignalDigger`模块 4 | 5 | ### 功能 6 | 7 | - 收益分析:分组收益、加权组合收益等 8 | - 相关性分析:每日IC、IC分布等 9 | - 排除涨跌停、停牌、非指数成分等 10 | 11 | ### 特性 12 | - 计算与绘图分离 13 | - 绘图输出格式可选、可关闭,数据计算结果可返回 14 | 15 | 完整代码及样例见[这里](https://github.com/quantOS-org/JAQS/blob/release-0.6.0/example/research/signal_return_ic_analysis.py),安装JAQS后即可直接运行。**请勿直接复制下方代码运行**。 16 | 17 | ### 测试量价背离因子 18 | 19 | - **输入**:两个`DataFrame`:因子值,标的价格/收益 20 | - **设置**:period,quantile个数 21 | 22 | ```python 23 | factor = -dv.get_ts('price_volume_divert').shift(1, axis=0) # avoid look-ahead bias 24 | price = dv.get_ts('close_adj') 25 | price_bench = dv.data_benchmark 26 | 27 | my_period = 5 28 | obj = SignalDigger(output_folder='.', output_format='plot') 29 | obj.process_signal_before_analysis(factor, price=price, 30 | mask=mask_all, 31 | n_quantiles=5, period=my_period, 32 | benchmark_price=price_bench, 33 | ) 34 | res = obj.create_full_report() 35 | ``` 36 | 37 | ![returnsreport](https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/returns_report.png) 38 | 39 | 40 | 41 | ![icreport](https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/ic_report.png) 42 | 43 | 44 | ### 利用输出数据做进一步分析 45 | 46 | 47 | ```python 48 | def performance(ret): 49 | cum = ret.add(1.0).cumprod(axis=0) 50 | std = np.std(ret) 51 | 52 | start = pd.to_datetime(ser.index[0], format="%Y%m%d") 53 | end = pd.to_datetime(ser.index[-1], format="%Y%m%d") 54 | years = (end - start).days / 365.0 55 | 56 | yearly_return = np.power(cum.values[-1], 1. / years) - 1 57 | yearly_vol = std * np.sqrt(225.) 58 | # beta = np.corrcoef(df_returns.loc[:, 'bench'], df_returns.loc[:, 'strat'])[0, 1] 59 | sharpe = yearly_return / yearly_vol 60 | print "ann. ret = {:.1f}%; ann. vol = {:.1f}%, sharpe = {:.2f}".format(yearly_return*100, yearly_vol*100, sharpe) 61 | 62 | ``` 63 | 64 | 65 | ```python 66 | ser = res['quantile_active_ret_correct'][1]['mean']#.iloc[90:] 67 | print ser.index[0], ser.index[-1] 68 | plt.figure(figsize=(14, 5)) 69 | plt.plot(ser.add(1.0).cumprod().values) 70 | performance(ser) 71 | ``` 72 | 73 | 20160105 20171013 74 | ann. ret = -17.2%; ann. vol = 3.7%, sharpe = -4.63 75 | 76 | 77 | ![furtheranalysis](https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/further_analysis.png) 78 | -------------------------------------------------------------------------------- /jaqs/util/pdutil.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | import numpy as np 4 | import pandas as pd 5 | 6 | from jaqs.util import numeric 7 | 8 | 9 | def to_quantile(df, n_quantiles=5, axis=1): 10 | """ 11 | Convert cross-section values to the quantile number they belong. 12 | Small values get small quantile numbers. 13 | 14 | Parameters 15 | ---------- 16 | df : pd.DataFrame or pd.Series 17 | index date, column symbols 18 | n_quantiles : int 19 | The number of quantile to be divided to. 20 | axis : int 21 | Axis to apply quantilize. 22 | 23 | Returns 24 | ------- 25 | res : DataFrame 26 | index date, column symbols 27 | 28 | """ 29 | # TODO: unnecesssary warnings 30 | # import warnings 31 | # warnings.filterwarnings(action='ignore', category=RuntimeWarning, module='py_exp') 32 | res_arr = numeric.quantilize_without_nan(df.values, n_quantiles=n_quantiles, axis=axis) 33 | if isinstance(df, pd.DataFrame): 34 | res = pd.DataFrame(index=df.index, columns=df.columns, data=res_arr) 35 | elif isinstance(df, pd.Series): 36 | res = pd.Series(index=df.index, data=res_arr) 37 | else: 38 | raise ValueError 39 | return res 40 | 41 | 42 | def fillinf(df): 43 | return df.replace([np.inf, -np.inf], np.nan) 44 | 45 | 46 | def group_df_to_dict(df, by): 47 | gp = df.groupby(by=by) 48 | res = {key: value for key, value in gp} 49 | return res 50 | 51 | 52 | def rank_with_mask(df, axis=1, mask=None, normalize=False, method='min'): 53 | """ 54 | 55 | Parameters 56 | ---------- 57 | df : pd.DataFrame 58 | axis : {0, 1} 59 | mask : pd.DataFrame 60 | normalize : bool 61 | method : {'min', 'average', 'max', 'dense'} 62 | 63 | Returns 64 | ------- 65 | pd.DataFrame 66 | 67 | Notes 68 | ----- 69 | If calculate rank, use 'min' method by default; 70 | If normalize, result will range in [0.0, 1.0] 71 | 72 | """ 73 | not_nan_mask = (~df.isnull()) 74 | 75 | if mask is None: 76 | mask = not_nan_mask 77 | else: 78 | mask = np.logical_and(not_nan_mask, mask) 79 | 80 | rank = df[mask].rank(axis=axis, na_option='keep', method=method) 81 | 82 | if normalize: 83 | dividend = rank.max(axis=axis) 84 | SUB = 1 85 | # for dividend = 1, do not subtract 1, otherwise there will be NaN 86 | dividend.loc[dividend > SUB] = dividend.loc[dividend > SUB] - SUB 87 | rank = rank.sub(SUB).div(dividend, axis=(1 - axis)) 88 | return rank 89 | -------------------------------------------------------------------------------- /jaqs/trade/analyze/report.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | report module defines Report class which fill and render pre-defined 4 | HTML templates. 5 | 6 | """ 7 | 8 | from __future__ import print_function 9 | try: 10 | basestring 11 | except NameError: 12 | basestring = str 13 | import os 14 | import codecs 15 | 16 | import jinja2 17 | 18 | import jaqs.util as jutil 19 | # from weasyprint import HTML 20 | 21 | 22 | class Report(object): 23 | def __init__(self, dic, source_dir, template_fn, out_folder='.'): 24 | """ 25 | 26 | Parameters 27 | ---------- 28 | dic : dict 29 | source_dir : str 30 | path of directory where HTML template and css files are stored. 31 | template_fn : str 32 | File name of HTML template. 33 | out_folder : str 34 | Output folder of report. 35 | 36 | """ 37 | self.dic = dic 38 | self.template_fn = template_fn 39 | self.out_folder = os.path.abspath(out_folder) 40 | 41 | self.env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath=[source_dir])) 42 | self._update_env() 43 | 44 | self.template = self.env.get_template(self.template_fn) 45 | self.html = "" 46 | 47 | def _update_env(self): 48 | """Define custom functions we use in HTML template.""" 49 | def cut_if_too_long(s, n): 50 | if isinstance(s, basestring): 51 | if len(s) > n: 52 | return s[:n] 53 | else: 54 | return s 55 | else: 56 | return s 57 | 58 | def round_if_float(x, n): 59 | if isinstance(x, float): 60 | return round(x, n) 61 | else: 62 | return x 63 | self.env.filters.update({'round_if_float': round_if_float}) 64 | self.env.filters.update({'cut_if_too_long': cut_if_too_long}) 65 | 66 | def generate_html(self): 67 | self.html = self.template.render(self.dic) 68 | 69 | def output_html(self, fn='test_out.html'): 70 | path = os.path.abspath(os.path.join(self.out_folder, fn)) 71 | 72 | jutil.create_dir(path) 73 | with codecs.open(path, 'w', encoding='utf-8') as f: 74 | f.write(self.html) 75 | 76 | print("HTML report: {:s}".format(path)) 77 | 78 | def output_pdf(self, fn='test_out.html'): 79 | pass 80 | # h = HTML(string=self.html) 81 | # h.write_pdf(os.path.join(self.out_folder, fn))#, stylesheets=[self.fp_css]) 82 | -------------------------------------------------------------------------------- /jaqs/util/profile.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from __future__ import print_function 4 | import time 5 | 6 | 7 | class SimpleTimer(object): 8 | """All time are counted relative to start.""" 9 | def __init__(self): 10 | self.start = 0 11 | self.events = [] 12 | 13 | def tick(self, event_name="Untitled"): 14 | now = time.time() 15 | if not self.start: 16 | self.start = now 17 | last = now 18 | else: 19 | _, last = self.events[-1] 20 | total = now - self.start 21 | delta = now - last 22 | 23 | self.events.append((event_name, now)) 24 | 25 | print("Total {:3.1f} | Delta {:3.1f} | {:s}".format(total, delta, event_name)) 26 | 27 | 28 | 29 | _prof_data = {} 30 | 31 | class ProfData : 32 | def __init__(self, name): 33 | self.name = name 34 | self.begin_time = time.time() 35 | self.used_time = 0 36 | 37 | def end(self): 38 | self.end_time = time.time() 39 | self.used_time = self.end_time - self.begin_time 40 | 41 | 42 | 43 | def prof_sample_begin(name): 44 | data = ProfData(name) 45 | if name in _prof_data: 46 | _prof_data[name].append(data) 47 | else: 48 | _prof_data[name] = [data] 49 | return data 50 | 51 | def prof_sample_end(data): 52 | data.end() 53 | 54 | def prof_sample(name, func): 55 | d = prof_sample_begin(name) 56 | v = func() 57 | d.end() 58 | return v 59 | 60 | def prof_print(): 61 | 62 | print_data = [] 63 | for data in _prof_data.values(): 64 | name = data[0].name 65 | max_time = 0.0 66 | min_time = 10000000000 67 | total_time = 0 68 | for d in data: 69 | total_time += d.used_time 70 | if d.used_time > max_time: max_time = d.used_time 71 | if d.used_time < min_time: min_time = d.used_time 72 | 73 | print_data.append( { 'name' : name, 74 | 'max': max_time, 75 | 'min': min_time, 76 | 'total': total_time, 77 | 'count':len(data), 78 | 'avg': total_time / len(data) 79 | }) 80 | 81 | print_data = sorted(print_data, key=lambda x : -x['total']) 82 | 83 | for d in print_data: 84 | print ("prof: {:<30} count {:8d} total {:8.4f}s, min {:8.4f}s, max {:8.4f}s, avg {:8.4f}" 85 | .format(d['name'], d['count'], d['total'], d['min'], d['max'], d['avg'])) 86 | 87 | 88 | -------------------------------------------------------------------------------- /example/research/signal_return_ic_analysis_single.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from __future__ import unicode_literals 4 | import numpy as np 5 | import pandas as pd 6 | 7 | from jaqs.data import DataView 8 | from jaqs.data import RemoteDataService 9 | from jaqs.research import SignalDigger 10 | import jaqs.util as jutil 11 | 12 | from config_path import DATA_CONFIG_PATH 13 | data_config = jutil.read_json(DATA_CONFIG_PATH) 14 | 15 | dataview_folder = '../../output/prepared/test_signal' 16 | 17 | 18 | def save_dataview(): 19 | ds = RemoteDataService() 20 | ds.init_from_config(data_config) 21 | dv = DataView() 22 | 23 | props = {'start_date': 20160101, 'end_date': 20171001, 'universe': '000300.SH', 24 | 'fields': 'volume,turnover', 25 | 'freq': 1} 26 | 27 | dv.init_from_config(props, ds) 28 | dv.prepare_data() 29 | 30 | dv.save_dataview(dataview_folder) 31 | 32 | 33 | def analyze_event(): 34 | # -------------------------------------------------------------------------------- 35 | # Step.1 load dataview 36 | dv = DataView() 37 | dv.load_dataview(dataview_folder) 38 | 39 | # -------------------------------------------------------------------------------- 40 | # Step.3 get signal, benchmark and price data 41 | target_symbol = '600519.SH' 42 | price = dv.get_ts('close_adj', symbol=target_symbol) 43 | dv.add_formula('in_', 'open_adj / Delay(close_adj, 1)', is_quarterly=False) 44 | signal = dv.get_ts('in_', symbol=target_symbol).shift(1, axis=0) # avoid look-ahead bias 45 | 46 | # Step.4 analyze! 47 | obj = SignalDigger(output_folder='../../output', output_format='pdf') 48 | 49 | obj.create_single_signal_report(signal, price, [1, 5, 9, 21], 6, mask=None, 50 | trade_condition={'cond1': {'column': 'quantile', 51 | 'filter': lambda x: x > 3, 52 | 'hold': 5}, 53 | 'cond2': {'column': 'quantile', 54 | 'filter': lambda x: x > 5, 55 | 'hold': 5}, 56 | 'cond3': {'column': 'quantile', 57 | 'filter': lambda x: x > 5, 58 | 'hold': 9}, 59 | }) 60 | 61 | 62 | if __name__ == "__main__": 63 | save_dataview() 64 | analyze_event() 65 | -------------------------------------------------------------------------------- /jaqs/data/align.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | """ 3 | Function align is used by DataView, to expand and re-arrange data in a DataFrame 4 | according to their available time, which is stored in another DataFrame. 5 | 6 | """ 7 | from __future__ import print_function 8 | import numpy as np 9 | import pandas as pd 10 | from jaqs.util import is_numeric 11 | 12 | 13 | def _get_neareast(df_ann, df_value, date): 14 | """ 15 | Get the value whose ann_date is earlier and nearest to date. 16 | 17 | Parameters 18 | ---------- 19 | df_ann : np.ndarray 20 | announcement dates. shape = (n_quarters, n_securities) 21 | df_value : np.ndarray 22 | announcement values. shape = (n_quarters, n_securities) 23 | date : np.ndarray 24 | shape = (1,) 25 | 26 | Returns 27 | ------- 28 | res : np.array 29 | The value whose ann_date is earlier and nearest to date. shape (n_securities) 30 | 31 | """ 32 | """ 33 | df_ann.fillna(99999999, inplace=True) # IMPORTANT: At cells where no quarterly data is available, 34 | # we know nothing, thus it will be filled nan in the next step 35 | """ 36 | if is_numeric(df_value): 37 | df_value = df_value.astype(float) 38 | mask = date[0] >= df_ann 39 | # res = np.where(mask, df_value, np.nan) 40 | n = df_value.shape[1] 41 | res = np.empty(n, dtype=df_value.dtype) 42 | 43 | # for each column, get the last True value 44 | for i in range(n): 45 | v = df_value[:, i] 46 | m = mask[:, i] 47 | r = v[m] 48 | res[i] = r[-1] if len(r) else np.nan 49 | 50 | return res 51 | 52 | 53 | def align(df_value, df_ann, date_arr): 54 | """ 55 | Expand low frequency DataFrame df_value to frequency of data_arr using announcement date from df_ann. 56 | 57 | Parameters 58 | ---------- 59 | df_ann : pd.DataFrame 60 | DataFrame of announcement dates. shape = (n_quarters, n_securities) 61 | df_value : pd.DataFrame 62 | DataFrame of announcement values. shape = (n_quarters, n_securities) 63 | date_arr : list or np.array 64 | Target date array. dtype = int 65 | 66 | Returns 67 | ------- 68 | df_res : pd.DataFrame 69 | Expanded DataFrame. shape = (n_days, n_securities) 70 | 71 | """ 72 | df_ann = df_ann.fillna(99999999).astype(int) 73 | 74 | date_arr = np.asarray(date_arr, dtype=int) 75 | 76 | res = np.apply_along_axis(lambda date: _get_neareast(df_ann.values, df_value.values, date), 1, date_arr.reshape(-1, 1)) 77 | 78 | df_res = pd.DataFrame(index=date_arr, columns=df_value.columns, data=res) 79 | return df_res 80 | -------------------------------------------------------------------------------- /doc/source/research.rst: -------------------------------------------------------------------------------- 1 | 研究 2 | ---- 3 | 4 | 信号研究与回测: ``SignalDigger``\ 模块 5 | 6 | 功能 7 | ~~~~ 8 | 9 | - 收益分析:分组收益、加权组合收益等 10 | - 相关性分析:每日IC、IC分布等 11 | - 排除涨跌停、停牌、非指数成分等 12 | 13 | 特性 14 | ~~~~ 15 | 16 | - 计算与绘图分离 17 | - 绘图输出格式可选、可关闭,数据计算结果可返回 18 | 19 | 完整代码及样例见\ `这里 `__\ ,安装JAQS后即可直接运行。\ **请勿直接复制下方代码运行**\ 。 20 | 21 | 测试量价背离因子 22 | ~~~~~~~~~~~~~~~~ 23 | 24 | - **输入**\ :两个\ ``DataFrame``\ :因子值,标的价格/收益 25 | - **设置**\ :period,quantile个数 26 | 27 | .. code:: python 28 | 29 | factor = -dv.get_ts('price_volume_divert').shift(1, axis=0) # avoid look-ahead bias 30 | price = dv.get_ts('close_adj') 31 | price_bench = dv.data_benchmark 32 | 33 | my_period = 5 34 | obj = SignalDigger(output_folder='.', output_format='plot') 35 | obj.process_signal_before_analysis(factor, price=price, 36 | mask=mask_all, 37 | n_quantiles=5, period=my_period, 38 | benchmark_price=price_bench, 39 | ) 40 | res = obj.create_full_report() 41 | 42 | |returnsreport| 43 | 44 | |icreport| 45 | 46 | 利用输出数据做进一步分析 47 | ~~~~~~~~~~~~~~~~~~~~~~~~ 48 | 49 | .. code:: python 50 | 51 | def performance(ret): 52 | cum = ret.add(1.0).cumprod(axis=0) 53 | std = np.std(ret) 54 | 55 | start = pd.to_datetime(ser.index[0], format="%Y%m%d") 56 | end = pd.to_datetime(ser.index[-1], format="%Y%m%d") 57 | years = (end - start).days / 365.0 58 | 59 | yearly_return = np.power(cum.values[-1], 1. / years) - 1 60 | yearly_vol = std * np.sqrt(225.) 61 | # beta = np.corrcoef(df_returns.loc[:, 'bench'], df_returns.loc[:, 'strat'])[0, 1] 62 | sharpe = yearly_return / yearly_vol 63 | print "ann. ret = {:.1f}%; ann. vol = {:.1f}%, sharpe = {:.2f}".format(yearly_return*100, yearly_vol*100, sharpe) 64 | 65 | .. code:: python 66 | 67 | ser = res['quantile_active_ret_correct'][1]['mean']#.iloc[90:] 68 | print ser.index[0], ser.index[-1] 69 | plt.figure(figsize=(14, 5)) 70 | plt.plot(ser.add(1.0).cumprod().values) 71 | performance(ser) 72 | 73 | :: 74 | 75 | 20160105 20171013 76 | ann. ret = -17.2%; ann. vol = 3.7%, sharpe = -4.63 77 | 78 | |furtheranalysis| 79 | 80 | .. |returnsreport| image:: https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/returns_report.png 81 | .. |icreport| image:: https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/ic_report.png 82 | .. |furtheranalysis| image:: https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/further_analysis.png 83 | -------------------------------------------------------------------------------- /jaqs/data/basic/instrument.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | from __future__ import print_function 3 | 4 | 5 | class Instrument(object): 6 | """ 7 | An Instrument represents a specific financial contract. 8 | 9 | Attributes 10 | ---------- 11 | symbol : str 12 | Symbol (ticker) of the instrument. 13 | multiplier : float 14 | Contract multiplier (size). 15 | inst_type : int 16 | Integer used to represent category of the instrument. 17 | market : str 18 | The market (exchange) where the instrument is traded. 19 | list_date : int 20 | Date when the instrument is listed. 21 | delist_date : int 22 | Date when the instrument is de-listed. 23 | 24 | """ 25 | def __init__(self): 26 | self.symbol = "" 27 | self.multiplier = 0.0 28 | self.inst_type = 0 29 | self.market = "" 30 | self.list_date = 0 31 | self.delist_date = 99999999 32 | 33 | @property 34 | def is_stock(self): 35 | return self.inst_type == 1 36 | 37 | @property 38 | def is_future(self): 39 | return (self.inst_type == 101 40 | or self.inst_type == 102 41 | or self.inst_type == 103) 42 | 43 | 44 | class InstManager(object): 45 | """ 46 | InstManager query information of instruments from data server and store them. 47 | 48 | Attributes 49 | ---------- 50 | data_api : RemoteDataService 51 | Used to query data. 52 | _inst_map : dict 53 | Used to store information of instruments. 54 | 55 | Methods 56 | ------- 57 | load_instruments 58 | 59 | """ 60 | def __init__(self, data_api, inst_type="", symbol=""): 61 | self.data_api = data_api 62 | 63 | self._inst_map = {} 64 | self.load_instruments(inst_type=inst_type, symbol=symbol) 65 | 66 | @property 67 | def inst_map(self): 68 | return self._inst_map 69 | 70 | def load_instruments(self, inst_type="", symbol=""): 71 | fields = ['symbol', 'name', 'inst_type', 'market', 72 | 'status', 'multiplier', 'list_date', 'delist_date', 'pricetick', 'buylot', 'selllot'] 73 | res = self.data_api.query_inst_info(symbol=symbol, fields=','.join(fields), inst_type=inst_type) 74 | res = res.reset_index() 75 | 76 | dic_of_dic = res.to_dict(orient='index') 77 | res = {v['symbol']: {v_key: v_value for v_key, v_value in v.items() if v_key in fields} 78 | for _, v in dic_of_dic.items()} 79 | for k, v in res.items(): 80 | inst = Instrument() 81 | inst.__dict__.update(v) 82 | self._inst_map[k] = inst 83 | 84 | def get_instrument(self, code): 85 | return self._inst_map.get(code, None) 86 | -------------------------------------------------------------------------------- /test/test_research.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from __future__ import unicode_literals 4 | import numpy as np 5 | import pandas as pd 6 | 7 | from jaqs.data import DataView 8 | from jaqs.data import RemoteDataService 9 | from jaqs.research import SignalDigger 10 | import jaqs.util as jutil 11 | 12 | from config_path import DATA_CONFIG_PATH 13 | data_config = jutil.read_json(DATA_CONFIG_PATH) 14 | 15 | dataview_folder = '../output/prepared/test_signal' 16 | 17 | 18 | def test_save_dataview(): 19 | ds = RemoteDataService() 20 | ds.init_from_config(data_config) 21 | dv = DataView() 22 | 23 | props = {'start_date': 20170301, 'end_date': 20171001, 'universe': '000300.SH', 24 | 'fields': 'volume,turnover,float_mv,pb,total_mv', 25 | 'freq': 1} 26 | 27 | dv.init_from_config(props, ds) 28 | dv.prepare_data() 29 | 30 | dv.save_dataview(dataview_folder) 31 | 32 | 33 | def test_analyze_signal(): 34 | # -------------------------------------------------------------------------------- 35 | # Step.1 load dataview 36 | dv = DataView() 37 | dv.load_dataview(dataview_folder) 38 | 39 | # -------------------------------------------------------------------------------- 40 | # Step.2 calculate mask (to mask those ill data points) 41 | trade_status = dv.get_ts('trade_status') 42 | mask_sus = trade_status == u'停牌' 43 | 44 | df_index_member = dv.get_ts('index_member') 45 | mask_index_member = ~(df_index_member > 0) 46 | 47 | dv.add_formula('limit_reached', 'Abs((open - Delay(close, 1)) / Delay(close, 1)) > 0.095', is_quarterly=False) 48 | df_limit_reached = dv.get_ts('limit_reached') 49 | mask_limit_reached = df_limit_reached > 0 50 | 51 | mask_all = np.logical_or(mask_sus, np.logical_or(mask_index_member, mask_limit_reached)) 52 | 53 | # -------------------------------------------------------------------------------- 54 | # Step.3 get signal, benchmark and price data 55 | dv.add_formula('divert', '- Correlation(vwap_adj, volume, 10)', is_quarterly=False) 56 | 57 | signal = dv.get_ts('divert').shift(1, axis=0) # avoid look-ahead bias 58 | price = dv.get_ts('close_adj') 59 | price_bench = dv.data_benchmark 60 | 61 | # Step.4 analyze! 62 | my_period = 5 63 | obj = SignalDigger(output_folder='../output/test_signal', output_format='pdf') 64 | obj.process_signal_before_analysis(signal, price=price, 65 | mask=mask_all, 66 | n_quantiles=5, period=my_period, 67 | benchmark_price=price_bench, 68 | ) 69 | res = obj.create_full_report() 70 | 71 | 72 | if __name__ == "__main__": 73 | test_save_dataview() 74 | test_analyze_signal() 75 | -------------------------------------------------------------------------------- /example/research/event_analysis.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from __future__ import unicode_literals 4 | import numpy as np 5 | import pandas as pd 6 | 7 | from jaqs.data import DataView 8 | from jaqs.data import RemoteDataService 9 | from jaqs.research import SignalDigger 10 | import jaqs.util as jutil 11 | 12 | from config_path import DATA_CONFIG_PATH 13 | data_config = jutil.read_json(DATA_CONFIG_PATH) 14 | 15 | dataview_folder = '../../output/prepared/test_signal' 16 | 17 | 18 | def save_dataview(): 19 | ds = RemoteDataService() 20 | ds.init_from_config(data_config) 21 | dv = DataView() 22 | 23 | props = {'start_date': 20160101, 'end_date': 20171001, 'universe': '000300.SH', 24 | 'fields': 'volume,turnover', 25 | 'freq': 1} 26 | 27 | dv.init_from_config(props, ds) 28 | dv.prepare_data() 29 | 30 | # for convenience to check limit reachers 31 | dv.add_formula('limit_reached', 'Abs((open - Delay(close, 1)) / Delay(close, 1)) > 0.095', is_quarterly=False) 32 | dv.add_formula('mask_limit_reached', 'limit_reached > 0', is_quarterly=False) 33 | 34 | dv.add_formula('mask_index_member', '!(index_member > 0)', is_quarterly=False) 35 | 36 | trade_status = dv.get_ts('trade_status') 37 | mask_sus = trade_status == u'停牌' 38 | dv.append_df(mask_sus, 'mask_sus', is_quarterly=False) 39 | 40 | # dv.add_formula('size', '', is_quarterly=False) 41 | 42 | dv.save_dataview(dataview_folder) 43 | 44 | 45 | def analyze_event(): 46 | # -------------------------------------------------------------------------------- 47 | # Step.1 load dataview 48 | dv = DataView() 49 | dv.load_dataview(dataview_folder) 50 | 51 | # -------------------------------------------------------------------------------- 52 | # Step.2 calculate mask (to mask those ill data points) 53 | mask_limit_reached = dv.get_ts('mask_limit_reached') 54 | mask_index_member = dv.get_ts('mask_index_member') 55 | mask_sus = dv.get_ts('mask_sus') 56 | 57 | mask_all = np.logical_or(mask_sus, np.logical_or(mask_index_member, mask_limit_reached)) 58 | 59 | # -------------------------------------------------------------------------------- 60 | # Step.3 get signal, benchmark and price data 61 | price = dv.get_ts('close_adj') 62 | price_bench = dv.data_benchmark 63 | 64 | dv.add_formula('in_', '(Delay(index_weight, 1) == 0) && (index_weight > 0)', is_quarterly=False) 65 | 66 | signal = dv.get_ts('in_').shift(1, axis=0) # avoid look-ahead bias 67 | # Step.4 analyze! 68 | obj = SignalDigger(output_folder='../../output', output_format='pdf') 69 | 70 | obj.create_binary_event_report(signal, price, mask_all, price_bench, periods=[20, 60, 121, 242], group_by=None) 71 | 72 | 73 | if __name__ == "__main__": 74 | save_dataview() 75 | analyze_event() 76 | -------------------------------------------------------------------------------- /test/test_align.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | from __future__ import print_function 3 | import pandas as pd 4 | from jaqs.data import RemoteDataService 5 | from jaqs.data import Parser 6 | import jaqs.util as jutil 7 | 8 | from config_path import DATA_CONFIG_PATH 9 | data_config = jutil.read_json(DATA_CONFIG_PATH) 10 | 11 | 12 | def test_align(): 13 | # ------------------------------------------------------------------------------------- 14 | # input and pre-process demo data 15 | ds = RemoteDataService() 16 | ds.init_from_config(data_config) 17 | raw, msg = ds.query_lb_fin_stat('income', '600000.SH', 20151225, 20170501, 'oper_rev') 18 | assert msg == '0,' 19 | 20 | idx_list = ['report_date', 'symbol'] 21 | raw_idx = raw.set_index(idx_list) 22 | raw_idx.sort_index(axis=0, level=idx_list, inplace=True) 23 | 24 | df_ann = raw_idx.loc[pd.IndexSlice[:, :], 'ann_date'] 25 | df_ann = df_ann.unstack(level=1) 26 | 27 | df_value = raw_idx.loc[pd.IndexSlice[:, :], 'oper_rev'] 28 | df_value = df_value.unstack(level=1) 29 | 30 | date_arr = ds.query_trade_dates(20160101, 20170501) 31 | df_close = pd.DataFrame(index=date_arr, columns=df_value.columns, data=1e3) 32 | 33 | # ------------------------------------------------------------------------------------- 34 | # demo usage of parser 35 | parser = Parser() 36 | parser.register_function('Myfunc', lambda x: x * 0 + 1) # simultaneously test register function and align 37 | expr_formula = 'signal / Myfunc(close)' 38 | expression = parser.parse(expr_formula) 39 | for i in range(100): 40 | df_res = parser.evaluate({'signal': df_value, 'close': df_close}, df_ann, date_arr) 41 | 42 | # ------------------------------------------------------------------------------------- 43 | sec = '600000.SH' 44 | """ 45 | # print to validate results 46 | print "\n======Expression Formula:\n{:s}".format(expr_formula) 47 | 48 | print "\n======Report date, ann_date and evaluation value:" 49 | tmp = pd.concat([df_ann.loc[:, sec], df_value.loc[:, sec]], axis=1) 50 | tmp.columns = ['df_ann', 'df_value'] 51 | print tmp 52 | 53 | print "\n======Selection of result of expansion:" 54 | print "20161028 {:.4f}".format(df_res.loc[20161028, sec]) 55 | print "20161031 {:.4f}".format(df_res.loc[20161031, sec]) 56 | print "20170427 {:.4f}".format(df_res.loc[20170427, sec]) 57 | 58 | """ 59 | assert abs(df_res.loc[20161028, sec] - 82172000000) < 1 60 | assert abs(df_res.loc[20161031, sec] - 120928000000) < 1 61 | assert abs(df_res.loc[20170427, sec] - 42360000000) < 1 62 | 63 | 64 | if __name__ == "__main__": 65 | import time 66 | t_start = time.time() 67 | 68 | test_align() 69 | 70 | t3 = time.time() - t_start 71 | print("\n\n\nTime lapsed in total: {:.1f}".format(t3)) 72 | -------------------------------------------------------------------------------- /example/research/signal_return_ic_analysis.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from __future__ import unicode_literals 4 | import numpy as np 5 | import pandas as pd 6 | 7 | from jaqs.data import DataView 8 | from jaqs.data import RemoteDataService 9 | from jaqs.research import SignalDigger 10 | import jaqs.util as jutil 11 | 12 | # from config_path.py, we import the path of config files 13 | from config_path import DATA_CONFIG_PATH 14 | # we use read_json to read the config file to a dictionary 15 | data_config = jutil.read_json(DATA_CONFIG_PATH) 16 | 17 | dataview_folder = '../../output/prepared/test_signal' 18 | 19 | 20 | def save_dataview(): 21 | ds = RemoteDataService() 22 | ds.init_from_config(data_config) 23 | dv = DataView() 24 | 25 | props = {'start_date': 20150101, 'end_date': 20171001, 'universe': '000300.SH', 26 | 'fields': 'volume,turnover,float_mv,pb,total_mv', 27 | 'freq': 1} 28 | 29 | dv.init_from_config(props, ds) 30 | dv.prepare_data() 31 | 32 | trade_status = dv.get_ts('trade_status') 33 | mask_sus = trade_status == '停牌' 34 | dv.append_df(mask_sus, 'suspended', is_quarterly=False) 35 | 36 | dv.add_formula('not_index_member', '!index_member', is_quarterly=False) 37 | 38 | dv.add_formula('limit_reached', 'Abs((open - Delay(close, 1)) / Delay(close, 1)) > 0.095', is_quarterly=False) 39 | 40 | dv.save_dataview(dataview_folder) 41 | 42 | 43 | def analyze_signal(dv, signal_name, output_format='pdf'): 44 | # Step.2 calculate mask (to mask those ill data points) 45 | mask_sus = dv.get_ts('suspended') 46 | mask_index_member = dv.get_ts('not_index_member') 47 | mask_limit_reached = dv.get_ts('limit_reached') 48 | mask_all = np.logical_or(mask_sus, np.logical_or(mask_index_member, mask_limit_reached)) 49 | 50 | signal = dv.get_ts(signal_name) # avoid look-ahead bias 51 | price = dv.get_ts('close_adj') 52 | price_bench = dv.data_benchmark 53 | 54 | # Step.4 analyze! 55 | my_period = 5 56 | obj = SignalDigger(output_folder='../../output/test_signal', 57 | output_format=output_format) 58 | obj.process_signal_before_analysis(signal, price=price, 59 | mask=mask_all, 60 | n_quantiles=5, period=my_period, 61 | benchmark_price=price_bench, 62 | ) 63 | res = obj.create_full_report() 64 | # print(res) 65 | 66 | 67 | def simple_test_signal(): 68 | dv = DataView() 69 | dv.load_dataview(dataview_folder) 70 | 71 | dv.add_formula('open_jump', 'open_adj / Delay(close_adj, 1)', is_quarterly=False) # good 72 | analyze_signal(dv, 'open_jump', 'pdf') 73 | 74 | print("Signal return & IC test finished.") 75 | 76 | if __name__ == "__main__": 77 | save_dataview() 78 | simple_test_signal() 79 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | -[] Order Matching: 2 | LIMIT ORDER mathing for event-driven; generate trades for alpha strategy. 3 | -[] Extract PortfolioManager from a member of Strategy 4 | -[x] Adjust time of re-balance, send orders and mathcing: 5 | plan after close, send orders after open 6 | -[] Record necessary information during backtest 7 | -[] improve code: ongoine 8 | -[x] conversion between trade results and dict/list 9 | -[x] Separate PnL analysis module, can be combined with DataRecorder 10 | backtest -> trades & configs -> analysis 11 | -[] Resolution of fill price of stocks in China is 0.01 12 | -[] Calendar Class 13 | 14 | # single factor test: 15 | -[] add industry neutral option 16 | -[x] automatically expand data of low frequency when OP2 encountere 17 | 1. Binary Operators (+ - * /): isinstance(x, df) and isinstance(y, df) is df and x.freq != y.freq 18 | 2. Cross Section Functions (Max, Rank): must expand to daily 19 | -[x] how to store quarterly data in dataview object 20 | -[x] provide a simple API to analyze formula 21 | -[x] relative return to benchmark 22 | -[x] return array of data 23 | -[x] adjust mode: default post 24 | -[] factor = close - close of benchmark: how? 25 | 26 | - modification of financial statement data 27 | for 1 security, 1 report_date, multiple ann_date exist because of modification. 28 | This can be separated by their statement type. 29 | For each modification, we modify the raw field value, then calculate formula again, finally modify results after new ann_date. 30 | 31 | After modification, original entry will be deleted, then, two new entries with different report_type will be added, 32 | one is the same with the original, the other is new. 33 | 34 | -[] when should we add trade_date, ann_date, report_date fields 35 | 36 | # DataView 37 | -[] when fetching data, cache fetched data. So if fail, we do not need to fetch all data again. 38 | -[x] if data of some symbols is missing, dv.data_d or dv.data_q will be wrong 39 | -[x] '&&' operator can not be True in isOps2() 40 | -[x] when should it fetches price_adj 41 | 42 | # Code Improvement of DataView 43 | -[x] improve get, get_quarter_ts methods. 44 | 45 | # Latest Plan 46 | 1. single factor pre-process: extreme, standardize, neutral 47 | 2. study alpha signal research (IC distribution on time series and securities) and factor -> weights -> portfolio return 48 | 3. multi-factor combination 49 | 4. backtest analysis: portfolio return attribution -- at each time, contribution of each return. 50 | 51 | -[] API rename 52 | 53 | # Data 54 | - self defined index; commidity index 55 | 56 | # Backtest 57 | -[x] do not trade symbols that are not index members 58 | 59 | # Analyze 60 | -[x] jinja2 search path does not work on Windows 61 | 62 | # on branch alphabacktest 63 | -[x] register signal function, no activation needed 64 | -[x] limit reachers: in re_balance_plan_after_open, set weights to zero 65 | -[x] index members: in re_balance_plan_before_open, set weights to zero 66 | -[x] stock selection: use StockSelector class. 67 | 68 | # Future 69 | -[] Python3 support 70 | -[] pandas api version 71 | -------------------------------------------------------------------------------- /doc/realtime.md: -------------------------------------------------------------------------------- 1 | ## 实盘交易 2 | 3 | 使用**JAQS**进行回测与实盘运行的代码具有高一致性,回测满意后,只需以下几点改动,即可接入实盘/模拟撮合: 4 | 5 | 1. 使用实盘交易的交易接口:将`BacktestTradeApi`替换为`RealTimeTradeApi` 6 | 2. 使用实盘交易主程序:将`EventBacktestInstance`替换为`EventLiveTradeInstance` 7 | 3. 在数据接口`RemoteDataService`中订阅所要交易品种的行情 8 | 4. 在主程序最后添加`time.sleep(9999999)`. 保证在事件循环运行中,主程序不会提前终止 9 | 5. 实时行情均为逐笔或Tick数据,即使订阅多个品种,行情数据仍会逐个到达`strategy.on_tick()`函数 10 | 11 | 这里我们以双均线策略为例,展示实盘运行的代码: 12 | 13 | ```python 14 | props = {'symbol': 'rb1801.SHF'} 15 | tapi = RealTimeTradeApi() 16 | ins = EventLiveTradeInstance() 17 | 18 | tapi.use_strategy(3) 19 | 20 | ds = RemoteDataService() 21 | strat = DoubleMaStrategy() 22 | pm = PortfolioManager() 23 | 24 | context = model.Context(data_api=ds, trade_api=tapi, 25 | instance=ins, strategy=strat, pm=pm) 26 | 27 | ins.init_from_config(props) 28 | ds.subscribe(props['symbol']) 29 | 30 | ins.run() 31 | 32 | time.sleep(999999) 33 | 34 | ``` 35 | 36 | 程序运行后,行情、策略、交易会在不同的线程内运行,我们只需要在`on_tick`中进行下单,在`on_trade`, `on_order_status`中处理交易回报即可。 37 | 38 | 正常收到行情如下: 39 | ``` 40 | rb1801.SHF 20171116-211319500 (BID) 229@3889.00 | 3891.00@12 (ASK) 41 | Fast MA = 3889.89 Slow MA = 3889.81 42 | rb1801.SHF 20171116-211320000 (BID) 224@3889.00 | 3891.00@13 (ASK) 43 | Fast MA = 3889.93 Slow MA = 3889.83 44 | rb1801.SHF 20171116-211320500 (BID) 223@3889.00 | 3891.00@5 (ASK) 45 | Fast MA = 3889.89 Slow MA = 3889.85 46 | rb1801.SHF 20171116-211321000 (BID) 223@3889.00 | 3891.00@5 (ASK) 47 | Fast MA = 3889.93 Slow MA = 3889.88 48 | rb1801.SHF 20171116-211321500 (BID) 24@3890.00 | 3891.00@5 (ASK) 49 | Fast MA = 3890.00 Slow MA = 3889.92 50 | ``` 51 | 52 | 如果发生下单、成交,则会收到如下回报: 53 | 54 | ``` 55 | 56 | rb1801.SHF 20171116-211319000 (BID) 230@3889.00 | 3891.00@6 (ASK) 57 | Fast MA = 3889.93 Slow MA = 3889.79 58 | 59 | Strategy on order status: 60 | New | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1 61 | 62 | Strategy on order status: 63 | Accepted | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1 64 | 65 | Strategy on trade: 66 | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1 67 | 68 | Strategy on order status: 69 | Filled | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1 70 | 71 | Strategy on task ind: 72 | task_id = 81116000001 | task_status = Stopped | task_algo = 73 | task_msg = 74 | DONE Execution Report (00'00"039): 75 | +---------+------+----------+------+----+---------+-----+----------+----+---------+------------+-------+-------+--------+---------+ 76 | |underlyer| ba_id| symbol|jzcode|side|is_filled|price|fill_price|size|fill_size|pending_size|cancels|rejects|entrusts| duration| 77 | +---------+------+----------+------+----+---------+-----+----------+----+---------+------------+-------+-------+--------+---------+ 78 | | 0|800000|rb1801.SHF| 27509|Long| Y| 3.0| 3.0000| 1| 1| 0| 0| 0| 1|00'00"033| 79 | +---------+------+----------+------+----+---------+-----+----------+----+---------+------------+-------+-------+--------+---------+ 80 | 81 | ``` 82 | -------------------------------------------------------------------------------- /doc/source/base_data.rst: -------------------------------------------------------------------------------- 1 | 基础数据 2 | ======== 3 | 4 | 调用说明 5 | -------- 6 | 7 | - 通过api.query函数调用,第一个参数view需填入对应的接口名,如:\ ``view="jz.instrumentInfo"`` 8 | - 输入参数指的是filter参数里面的内容,通过'&'符号拼接,如:\ ``filter="inst_type=&status=1&symbol="`` 9 | - 输出参数指的是fields里面的内容,通过','隔开 10 | 11 | 样例代码:获取上市股票列表 12 | 13 | .. code:: python 14 | 15 | df, msg = api.query( 16 | view="jz.instrumentInfo", 17 | fields="status,list_date, fullname_en, market", 18 | filter="inst_type=1&status=1&symbol=", 19 | data_format='pandas') 20 | 21 | 目前支持的接口及其含义 22 | ---------------------- 23 | 24 | +----------------------+------------------------+------------+ 25 | | 接口 | view | 分类 | 26 | +======================+========================+============+ 27 | | 证券基础信息表 | jz.instrumentInfo | 基础信息 | 28 | +----------------------+------------------------+------------+ 29 | | 交易日历表 | jz.secTradeCal | 基础信息 | 30 | +----------------------+------------------------+------------+ 31 | | 分配除权信息表 | lb.secDividend | 股票 | 32 | +----------------------+------------------------+------------+ 33 | | 复权因子表 | lb.secAdjFactor | 股票 | 34 | +----------------------+------------------------+------------+ 35 | | 停复牌信息表 | lb.secSusp | 股票 | 36 | +----------------------+------------------------+------------+ 37 | | 行业分类表 | lb.secIndustry | 股票 | 38 | +----------------------+------------------------+------------+ 39 | | 日行情估值表 | lb.secDailyIndicator | 股票 | 40 | +----------------------+------------------------+------------+ 41 | | 资产负债表 | lb.balanceSheet | 股票 | 42 | +----------------------+------------------------+------------+ 43 | | 利润表 | lb.income | 股票 | 44 | +----------------------+------------------------+------------+ 45 | | 现金流量表 | lb.cashFlow | 股票 | 46 | +----------------------+------------------------+------------+ 47 | | 业绩快报 | lb.profitExpress | 股票 | 48 | +----------------------+------------------------+------------+ 49 | | 限售股解禁表 | lb.secRestricted | 股票 | 50 | +----------------------+------------------------+------------+ 51 | | 指数基本信息表 | lb.indexInfo | 指数 | 52 | +----------------------+------------------------+------------+ 53 | | 指数成份股表 | lb.indexCons | 指数 | 54 | +----------------------+------------------------+------------+ 55 | | 公募基金净值表 | lb.mfNav | 基金 | 56 | +----------------------+------------------------+------------+ 57 | | 公募基金分红表 | lb.mfDividend | 基金 | 58 | +----------------------+------------------------+------------+ 59 | | 公募基金股票组合表 | lb.mfPortfolio | 基金 | 60 | +----------------------+------------------------+------------+ 61 | | 公募基金债券组合表 | lb.mfBondPortfolio | 基金 | 62 | +----------------------+------------------------+------------+ 63 | 64 | 接口还在进一步的丰富过程中。 65 | 66 | 详细的说明文档,please refer to `quantos `__ 67 | -------------------------------------------------------------------------------- /doc/source/realtime.rst: -------------------------------------------------------------------------------- 1 | 实盘交易 2 | -------- 3 | 4 | 使用\ **JAQS**\ 进行回测与实盘运行的代码具有高一致性,回测满意后,只需以下几点改动,即可接入实盘/模拟撮合: 5 | 6 | #. 使用实盘交易的交易接口:将\ ``BacktestTradeApi``\ 替换为\ ``RealTimeTradeApi`` 7 | #. 使用实盘交易主程序:将\ ``EventBacktestInstance``\ 替换为\ ``EventLiveTradeInstance`` 8 | #. 在数据接口\ ``RemoteDataService``\ 中订阅所要交易品种的行情 9 | #. 在主程序最后添加\ ``time.sleep(9999999)``. 10 | 保证在事件循环运行中,主程序不会提前终止 11 | #. 实时行情均为逐笔或Tick数据,即使订阅多个品种,行情数据仍会逐个到达\ ``strategy.on_tick()``\ 函数 12 | 13 | 这里我们以双均线策略为例,展示实盘运行的代码: 14 | 15 | .. code:: python 16 | 17 | props = {'symbol': 'rb1801.SHF'} 18 | tapi = RealTimeTradeApi() 19 | ins = EventLiveTradeInstance() 20 | 21 | tapi.use_strategy(3) 22 | 23 | ds = RemoteDataService() 24 | strat = DoubleMaStrategy() 25 | pm = PortfolioManager() 26 | 27 | context = model.Context(data_api=ds, trade_api=tapi, 28 | instance=ins, strategy=strat, pm=pm) 29 | 30 | ins.init_from_config(props) 31 | ds.subscribe(props['symbol']) 32 | 33 | ins.run() 34 | 35 | time.sleep(999999) 36 | 37 | 程序运行后,行情、策略、交易会在不同的线程内运行,我们只需要在\ ``on_tick``\ 中进行下单,在\ ``on_trade``, 38 | ``on_order_status``\ 中处理交易回报即可。 39 | 40 | 正常收到行情如下: 41 | 42 | :: 43 | 44 | rb1801.SHF 20171116-211319500 (BID) 229@3889.00 | 3891.00@12 (ASK) 45 | Fast MA = 3889.89 Slow MA = 3889.81 46 | rb1801.SHF 20171116-211320000 (BID) 224@3889.00 | 3891.00@13 (ASK) 47 | Fast MA = 3889.93 Slow MA = 3889.83 48 | rb1801.SHF 20171116-211320500 (BID) 223@3889.00 | 3891.00@5 (ASK) 49 | Fast MA = 3889.89 Slow MA = 3889.85 50 | rb1801.SHF 20171116-211321000 (BID) 223@3889.00 | 3891.00@5 (ASK) 51 | Fast MA = 3889.93 Slow MA = 3889.88 52 | rb1801.SHF 20171116-211321500 (BID) 24@3890.00 | 3891.00@5 (ASK) 53 | Fast MA = 3890.00 Slow MA = 3889.92 54 | 55 | 如果发生下单、成交,则会收到如下回报: 56 | 57 | :: 58 | 59 | rb1801.SHF 20171116-211319000 (BID) 230@3889.00 | 3891.00@6 (ASK) 60 | Fast MA = 3889.93 Slow MA = 3889.79 61 | 62 | Strategy on order status: 63 | New | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1 64 | 65 | Strategy on order status: 66 | Accepted | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1 67 | 68 | Strategy on trade: 69 | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1 70 | 71 | Strategy on order status: 72 | Filled | 20171115( 211319) Buy rb1801.SHF@3.000 size = 1 73 | 74 | Strategy on task ind: 75 | task_id = 81116000001 | task_status = Stopped | task_algo = 76 | task_msg = 77 | DONE Execution Report (00'00"039): 78 | +---------+------+----------+------+----+---------+-----+----------+----+---------+------------+-------+-------+--------+---------+ 79 | |underlyer| ba_id| symbol|jzcode|side|is_filled|price|fill_price|size|fill_size|pending_size|cancels|rejects|entrusts| duration| 80 | +---------+------+----------+------+----+---------+-----+----------+----+---------+------------+-------+-------+--------+---------+ 81 | | 0|800000|rb1801.SHF| 27509|Long| Y| 3.0| 3.0000| 1| 1| 0| 0| 0| 1|00'00"033| 82 | +---------+------+----------+------+----+---------+-----+----------+----+---------+------------+-------+-------+--------+---------+ 83 | -------------------------------------------------------------------------------- /jaqs/data/basic/position.py: -------------------------------------------------------------------------------- 1 | # encoding:utf-8 2 | 3 | 4 | class Position(object): 5 | """ 6 | Basic position class. 7 | 8 | Attributes 9 | ---------- 10 | symbol : str 11 | List of securities. 12 | side : str 13 | ("Long", "Short"). Positions of different sides will not be merged. 14 | cost_price : float 15 | Average cost price of current net position. 16 | close_pnl : float 17 | float_pnl : float 18 | trading_pnl : float 19 | holding_pnl : float 20 | init_size : int 21 | Position at the start of the day. 22 | enable_size : int 23 | Position that can be closed. 24 | frozen_size : int 25 | Positions to be closed. 26 | want_size : int 27 | Positions to be opened. 28 | pre_size : int 29 | Last day's position. 30 | today_size : int 31 | Today's position. 32 | current_size : int 33 | Current position. 34 | 35 | Methods 36 | ------- 37 | 38 | """ 39 | ''' 40 | __slots__ = ['symbol', 'side', 'cost_price', 41 | 'close_pnl', 'float_pnl', 'trading_pnl', 'holding_pnl', 42 | 'enable_size', 'frozen_size', 'want_size', 43 | 'today_size', 'pre_size', 'current_size', 'init_size', 44 | 'commission'] 45 | ''' 46 | 47 | def __init__(self, symbol=""): 48 | self.symbol = symbol 49 | 50 | self.side = "" 51 | self.cost_price = 0.0 52 | 53 | self.close_pnl = 0.0 54 | self.float_pnl = 0.0 55 | self.trading_pnl = 0.0 56 | self.holding_pnl = 0.0 57 | 58 | self.enable_size = 0 59 | self.frozen_size = 0 60 | self.want_size = 0 61 | 62 | self.today_size = 0 63 | self.pre_size = 0 64 | self.current_size = 0 65 | self.init_size = 0 66 | 67 | self.commission = 0.0 68 | 69 | def __repr__(self): 70 | return "{0.side:7s} {0.current_size:5d} of {0.symbol:10s}".format(self) 71 | 72 | def __str__(self): 73 | return self.__repr__() 74 | 75 | @classmethod 76 | def create_from_df(cls, df): 77 | bar_list = [] 78 | for _, row in df.iterrows(): 79 | dic = row.to_dict() 80 | bar = cls.create_from_dict(dic) 81 | bar_list.append(bar) 82 | return bar_list 83 | 84 | @classmethod 85 | def create_from_dict(cls, dic): 86 | bar = cls() 87 | bar.__dict__.update(dic) 88 | return bar 89 | 90 | 91 | class GoalPosition(object): 92 | """ 93 | Used in goal_portfolio function to generate orders. 94 | 95 | Attributes 96 | ---------- 97 | symbol : str 98 | ref_price : float 99 | Reference price, used by risk management, not by order. 100 | size : int 101 | Target position size. 102 | urgency : int 103 | The urgency to adjust position, used to determine trading algorithm. 104 | 105 | """ 106 | 107 | def __init__(self): 108 | self.symbol = "" 109 | self.ref_price = 0.0 110 | self.size = 0 111 | self.urgency = 0 112 | 113 | def __repr__(self): 114 | return "{0.size:5d} of {0.symbol:10s}".format(self) 115 | 116 | def __str__(self): 117 | return self.__repr__() 118 | -------------------------------------------------------------------------------- /jaqs/util/fileio.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | import json 3 | import os 4 | import errno 5 | try: 6 | import cPickle as pickle 7 | except ImportError: 8 | import pickle 9 | import codecs 10 | 11 | from .. import SOURCE_ROOT_DIR 12 | 13 | 14 | def create_dir(filename): 15 | """ 16 | Create dir if directory of filename does not exist. 17 | 18 | Parameters 19 | ---------- 20 | filename : str 21 | 22 | """ 23 | if not os.path.exists(os.path.dirname(filename)): 24 | try: 25 | os.makedirs(os.path.dirname(filename)) 26 | except OSError as exc: # Guard against race condition 27 | if exc.errno != errno.EEXIST: 28 | raise 29 | 30 | 31 | def read_json(fp): 32 | """ 33 | Read JSON file to dict. Return None if file not found. 34 | 35 | Parameters 36 | ---------- 37 | fp : str 38 | Path of the JSON file. 39 | 40 | Returns 41 | ------- 42 | dict 43 | 44 | """ 45 | content = dict() 46 | try: 47 | with codecs.open(fp, 'r', encoding='utf-8') as f: 48 | content = json.load(f) 49 | except IOError as e: 50 | if e.errno not in (errno.ENOENT, errno.EISDIR, errno.EINVAL): 51 | raise 52 | return content 53 | 54 | 55 | def save_json(serializable, file_name): 56 | """ 57 | Save an serializable object to JSON file. 58 | 59 | Parameters 60 | ---------- 61 | serializable : object 62 | file_name : str 63 | 64 | """ 65 | fn = os.path.abspath(file_name) 66 | create_dir(fn) 67 | 68 | with codecs.open(fn, 'w', encoding='utf-8') as f: 69 | json.dump(serializable, f, separators=(',\n', ': ')) 70 | 71 | 72 | def load_pickle(fp): 73 | """ 74 | Read Pickle file. Return None if file not found. 75 | 76 | Parameters 77 | ---------- 78 | fp : str 79 | Path of the Pickle file. 80 | 81 | Returns 82 | ------- 83 | object or None 84 | 85 | """ 86 | content = None 87 | try: 88 | with open(fp, 'rb') as f: 89 | content = pickle.load(f) 90 | except IOError as e: 91 | if e.errno not in (errno.ENOENT, errno.EISDIR, errno.EINVAL): 92 | raise 93 | return content 94 | 95 | 96 | def save_pickle(obj, file_name): 97 | """ 98 | Save an object to Pickle file. 99 | 100 | Parameters 101 | ---------- 102 | obj : object 103 | file_name : str 104 | 105 | """ 106 | fn = os.path.abspath(file_name) 107 | create_dir(fn) 108 | 109 | with open(fn, 'wb') as f: 110 | pickle.dump(obj, f) 111 | 112 | 113 | def join_relative_path(*paths): 114 | """Get absolute path using paths that are relative to project root.""" 115 | return os.path.abspath(os.path.join(SOURCE_ROOT_DIR, *paths)) 116 | 117 | 118 | def fig2base64(fig, format='png'): 119 | """ 120 | 121 | Parameters 122 | ---------- 123 | fig : matplotlib.fig.Figure 124 | format : str 125 | Eg. png, jpg 126 | 127 | Returns 128 | ------- 129 | 130 | """ 131 | try: 132 | import BytesIO as io 133 | except ImportError: 134 | # from io import StringIO as StringIO 135 | import io 136 | import base64 137 | bytes_io = io.BytesIO() 138 | fig.savefig(bytes_io, format=format) 139 | bytes_io.seek(0) 140 | s = bytes_io.read() 141 | res = base64.b64encode(s) 142 | return res 143 | -------------------------------------------------------------------------------- /jaqs/trade/common.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | from __future__ import print_function 3 | from enum import Enum, unique 4 | 5 | 6 | class ReprEnum(Enum): 7 | def __repr__(self): 8 | return "{0:s}_{1:s}".format(self.__class__.__name__, 9 | self._name_) 10 | 11 | def __str__(self): 12 | return self.value 13 | 14 | @property 15 | def full_name(self): 16 | return self.__repr__() 17 | 18 | @classmethod 19 | def to_enum(cls, key): 20 | return cls.__members__.get(key.upper(), None) 21 | 22 | 23 | class ReprIntEnum(int, ReprEnum): 24 | """Enum where members are also (and must be) ints""" 25 | pass 26 | 27 | 28 | class ReprStrEnum(str, ReprEnum): 29 | """Enum where members are also (and must be) ints""" 30 | pass 31 | 32 | 33 | @unique 34 | class QUOTE_TYPE(ReprStrEnum): 35 | TICK = '0' 36 | MIN = '1M' 37 | FIVEMIN = '5M' 38 | QUARTERMIN = '15M' 39 | DAILY = '1d' 40 | SPECIALBAR = '-1' 41 | # %Y%m%d%H%M%S 42 | 43 | 44 | @unique 45 | class RUN_MODE(ReprIntEnum): 46 | REALTIME = 0 47 | BACKTEST = 1 48 | 49 | 50 | @unique 51 | class EXCHANGE(ReprStrEnum): 52 | SHENZHEN_STOCK_EXCHANGE = 'SZ' 53 | SHANGHAI_STOCK_EXCHANGE = 'SH' 54 | 55 | SHANGHAI_FUTURES_EXCHANGE = 'SHF' 56 | ZHENGZHOU_COMMODITIES_EXCHANGE = 'CZC' 57 | DALIAN_COMMODITIES_EXCHANGE = 'DCE' 58 | 59 | CHINA_FINANCIAL_FUTURES_EXCHANGE = 'CFE' 60 | 61 | SHANGHAI_GOLD_EXCHANGE = 'SGE' 62 | 63 | CHINA_SECURITY_INDEX = 'CSI' 64 | 65 | HONGKONG_EXCHANGES_AND_CLEARING_LIMITED = 'HK' 66 | 67 | 68 | @unique 69 | class ORDER_TYPE(ReprStrEnum): 70 | """We recommend use limit order everywhere.""" 71 | MARKET = "market" 72 | LIMIT = "limit" 73 | STOP = "stop" # convert to market order once symbol price meet certain conditions. 74 | VWAP = "vwap" 75 | 76 | 77 | @unique 78 | class ORDER_ACTION(ReprStrEnum): 79 | """ 80 | buy and sell for long; short and cover for short. 81 | other 4 actions are automatically generated by EMS.""" 82 | BUY = "Buy" 83 | SELL = "Sell" 84 | SHORT = "Short" 85 | COVER = "Cover" 86 | SELLTODAY = "SellToday" 87 | SELLYESTERDAY = "SellYesterday" 88 | COVERYESTERDAY = "CoverYesterday" 89 | COVERTODAY = "CoverToday" 90 | 91 | @classmethod 92 | def is_positive(cls, action): 93 | return (action == cls.BUY 94 | or action == cls.COVER 95 | or action == cls.COVERYESTERDAY 96 | or action == cls.COVERTODAY) 97 | 98 | @classmethod 99 | def is_negative(cls, action): 100 | return (action == cls.SELL 101 | or action == cls.SHORT 102 | or action == cls.SELLTODAY 103 | or action == cls.SELLYESTERDAY) 104 | 105 | 106 | @unique 107 | class ORDER_STATUS(ReprStrEnum): 108 | NEW = "New" 109 | REJECTED = "Rejected" 110 | ACCEPTED = "Accepted" 111 | FILLED = "Filled" 112 | CANCELLED = "Cancelled" 113 | 114 | 115 | @unique 116 | class TASK_STATUS(ReprStrEnum): 117 | NEW = "New" 118 | REJECTED = "Rejected" 119 | ACCEPTED = "Accepted" 120 | DONE = "Done" 121 | CANCELLED = "Cancelled" 122 | 123 | 124 | @unique 125 | class ORDER_TIME_IN_FORCE(ReprStrEnum): 126 | FOK = 'fok' 127 | FAK = 'fak' 128 | IOC = 'ioc' 129 | 130 | 131 | @unique 132 | class CALENDAR_CONST(ReprIntEnum): 133 | TRADE_DAYS_PER_YEAR = 242 134 | -------------------------------------------------------------------------------- /example/alpha/single_factor_weight.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | """ 4 | Signal_model : 流通市值 5 | 按月调仓,factor_value_weight 6 | 7 | universe : hs300 8 | init_balance = 1e8 9 | start_date 20170101 10 | end_date 20171001 11 | 12 | """ 13 | from __future__ import print_function 14 | from __future__ import absolute_import 15 | import time 16 | 17 | from jaqs.data import RemoteDataService 18 | from jaqs.trade import AlphaBacktestInstance 19 | from jaqs.trade import PortfolioManager 20 | 21 | import jaqs.util as jutil 22 | import jaqs.trade.analyze as ana 23 | from jaqs.trade import AlphaStrategy 24 | from jaqs.trade import AlphaTradeApi 25 | from jaqs.trade import model 26 | from jaqs.data import DataView 27 | 28 | from config_path import DATA_CONFIG_PATH, TRADE_CONFIG_PATH 29 | data_config = jutil.read_json(DATA_CONFIG_PATH) 30 | trade_config = jutil.read_json(TRADE_CONFIG_PATH) 31 | 32 | dataview_dir_path = '../../output/single_factor_weight/dataview' 33 | backtest_result_dir_path = '../../output/single_factor_weight' 34 | 35 | 36 | def test_save_dataview(): 37 | ds = RemoteDataService() 38 | ds.init_from_config(data_config) 39 | dv = DataView() 40 | 41 | props = {'start_date': 20170201, 'end_date': 20171001, 'universe': '000300.SH', 42 | 'fields': ('float_mv,sw2,sw1'), 43 | 'freq': 1} 44 | 45 | dv.init_from_config(props, ds) 46 | dv.prepare_data() 47 | 48 | factor_formula = 'GroupQuantile(float_mv, sw1, 10)' 49 | dv.add_formula('gq30', factor_formula, is_quarterly=False) 50 | 51 | dv.save_dataview(folder_path=dataview_dir_path) 52 | 53 | 54 | def test_alpha_strategy_dataview(): 55 | dv = DataView() 56 | 57 | dv.load_dataview(folder_path=dataview_dir_path) 58 | 59 | props = { 60 | "benchmark": "000300.SH", 61 | "universe": ','.join(dv.symbol), 62 | 63 | "start_date": 20170131, 64 | "end_date": dv.end_date, 65 | 66 | "period": "month", 67 | "days_delay": 0, 68 | 69 | "init_balance": 1e9, 70 | "position_ratio": 1.0, 71 | } 72 | props.update(data_config) 73 | props.update(trade_config) 74 | 75 | trade_api = AlphaTradeApi() 76 | 77 | def singal_gq30(context, user_options=None): 78 | import numpy as np 79 | res = np.power(context.snapshot['gq30'], 8) 80 | return res 81 | 82 | signal_model = model.FactorSignalModel() 83 | signal_model.add_signal('signal_gq30', singal_gq30) 84 | 85 | strategy = AlphaStrategy(signal_model=signal_model, pc_method='factor_value_weight') 86 | pm = PortfolioManager() 87 | 88 | bt = AlphaBacktestInstance() 89 | 90 | context = model.Context(dataview=dv, instance=bt, strategy=strategy, trade_api=trade_api, pm=pm) 91 | 92 | signal_model.register_context(context) 93 | 94 | bt.init_from_config(props) 95 | 96 | bt.run_alpha() 97 | 98 | bt.save_results(folder_path=backtest_result_dir_path) 99 | 100 | 101 | def test_backtest_analyze(): 102 | ta = ana.AlphaAnalyzer() 103 | dv = DataView() 104 | dv.load_dataview(folder_path=dataview_dir_path) 105 | 106 | ta.initialize(dataview=dv, file_folder=backtest_result_dir_path) 107 | 108 | ta.do_analyze(result_dir=backtest_result_dir_path, selected_sec=list(ta.universe)[:3]) 109 | 110 | 111 | if __name__ == "__main__": 112 | t_start = time.time() 113 | 114 | test_save_dataview() 115 | test_alpha_strategy_dataview() 116 | test_backtest_analyze() 117 | 118 | t3 = time.time() - t_start 119 | print("\n\n\nTime lapsed in total: {:.1f}".format(t3)) 120 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = -v # verbose output 6 | SPHINXPROJ = quantOS 7 | SPHINXBUILD = sphinx-build 8 | SOURCEDIR = source 9 | PAPER = 10 | BUILDDIR = build 11 | 12 | # User-friendly check for sphinx-build 13 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 14 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 15 | endif 16 | 17 | # Internal variables. 18 | PAPEROPT_a4 = -D latex_paper_size=a4 19 | PAPEROPT_letter = -D latex_paper_size=letter 20 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCEDIR) 21 | # the i18n builder cannot share the environment and doctrees with the others 22 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 23 | 24 | .PHONY: help 25 | help: 26 | @echo "Please use \`make ' where is one of" 27 | @echo " html to make standalone HTML files" 28 | @echo " dirhtml to make HTML files named index.html in directories" 29 | @echo " singlehtml to make a single large HTML file" 30 | @echo " pickle to make pickle files" 31 | @echo " json to make JSON files" 32 | @echo " htmlhelp to make HTML files and a HTML help project" 33 | @echo " qthelp to make HTML files and a qthelp project" 34 | @echo " applehelp to make an Apple Help Book" 35 | @echo " devhelp to make HTML files and a Devhelp project" 36 | @echo " epub to make an epub" 37 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 38 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 39 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 40 | @echo " text to make text files" 41 | @echo " man to make manual pages" 42 | @echo " texinfo to make Texinfo files" 43 | @echo " info to make Texinfo files and run them through makeinfo" 44 | @echo " gettext to make PO message catalogs" 45 | @echo " changes to make an overview of all changed/added/deprecated items" 46 | @echo " xml to make Docutils-native XML files" 47 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 48 | @echo " linkcheck to check all external links for integrity" 49 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 50 | @echo " coverage to run coverage check of the documentation (if enabled)" 51 | 52 | .PHONY: clean 53 | clean: 54 | rm -rf $(BUILDDIR)/* 55 | 56 | # @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 57 | .PHONY: html 58 | html: 59 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 60 | @echo 61 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 62 | 63 | .PHONY: dirhtml 64 | dirhtml: 65 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 66 | @echo 67 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 68 | 69 | .PHONY: singlehtml 70 | singlehtml: 71 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 72 | @echo 73 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 74 | 75 | # Catch-all target: route all unknown targets to Sphinx using the new 76 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 77 | %: Makefile 78 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 79 | -------------------------------------------------------------------------------- /jaqs/util/dtutil.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | import datetime 3 | import numpy as np 4 | import pandas as pd 5 | 6 | 7 | def get_next_period_day(current, period, n=1, extra_offset=0): 8 | """ 9 | Get the n'th day in next period from current day. 10 | 11 | Parameters 12 | ---------- 13 | current : int 14 | Current date in format "%Y%m%d". 15 | period : str 16 | Interval between current and next. {'day', 'week', 'month'} 17 | n : int 18 | n times period. 19 | extra_offset : int 20 | n'th business day after next period. 21 | 22 | Returns 23 | ------- 24 | nxt : int 25 | 26 | """ 27 | current_dt = convert_int_to_datetime(current) 28 | if period == 'day': 29 | offset = pd.tseries.offsets.BDay() # move to next business day 30 | # offset = offsets.Day 31 | elif period == 'week': 32 | offset = pd.tseries.offsets.Week(weekday=0) # move to next Monday 33 | elif period == 'month': 34 | offset = pd.tseries.offsets.BMonthBegin() # move to first business day of next month 35 | # offset = offsets.MonthBegin 36 | else: 37 | raise NotImplementedError("Frequency as {} not support".format(period)) 38 | offset = offset * n 39 | 40 | next_dt = current_dt + offset 41 | if extra_offset: 42 | next_dt = next_dt + extra_offset * pd.tseries.offsets.BDay() 43 | nxt = convert_datetime_to_int(next_dt) 44 | return nxt 45 | 46 | 47 | def convert_int_to_datetime(dt): 48 | """Convert int date (%Y%m%d) to datetime.datetime object.""" 49 | if isinstance(dt, pd.Series): 50 | dt = dt.astype(str) 51 | elif isinstance(dt, int): 52 | dt = str(dt) 53 | return pd.to_datetime(dt, format="%Y%m%d") 54 | 55 | 56 | def convert_datetime_to_int(dt): 57 | f = lambda x: x.year * 10000 + x.month * 100 + x.day 58 | if isinstance(dt, (datetime.datetime, datetime.date)): 59 | dt = pd.Timestamp(dt) 60 | res = f(dt) 61 | elif isinstance(dt, np.datetime64): 62 | dt = pd.Timestamp(dt) 63 | res = f(dt) 64 | else: 65 | dt = pd.Series(dt) 66 | res = dt.apply(f) 67 | return res 68 | 69 | 70 | def shift(date, n_weeks=0): 71 | """Shift date backward or forward for n weeks. 72 | 73 | Parameters 74 | ---------- 75 | date : int or datetime 76 | The date to be shifted. 77 | n_weeks : int, optional 78 | Positive for increasing date, negative for decreasing date. 79 | Default 0 (no shift). 80 | 81 | Returns 82 | ------- 83 | res : int or datetime 84 | 85 | """ 86 | delta = pd.Timedelta(weeks=n_weeks) 87 | 88 | is_int = isinstance(date, (int, np.integer)) 89 | if is_int: 90 | dt = convert_int_to_datetime(date) 91 | else: 92 | dt = date 93 | res = dt + delta 94 | if is_int: 95 | res = convert_datetime_to_int(res) 96 | return res 97 | 98 | 99 | def combine_date_time(date, time): 100 | return np.int64(date) * 1000000 + np.int64(time) 101 | 102 | 103 | def split_date_time(dt): 104 | date = dt // 1000000 105 | time = dt % 1000000 106 | return date, time 107 | 108 | 109 | def date_to_month(ser): 110 | # ser = pd.Series(ser) 111 | res = ser % 10000 // 100 112 | MONTH_MAP = {1: 'Jan', 113 | 2: 'Feb', 114 | 3: 'Mar', 115 | 4: 'Apr', 116 | 5: 'May', 117 | 6: 'Jun', 118 | 7: 'Jul', 119 | 8: 'Aug', 120 | 9: 'Sep', 121 | 10: 'Oct', 122 | 11: 'Nov', 123 | 12: 'Dec'} 124 | # res = res.replace(MONTH_MAP) 125 | return res 126 | 127 | 128 | def date_to_year(ser): 129 | return ser // 10000 130 | -------------------------------------------------------------------------------- /example/alpha/wine_industry_momentum.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Both backtest and live trading are included. 4 | """ 5 | 6 | from __future__ import print_function 7 | from __future__ import absolute_import 8 | import time 9 | 10 | from jaqs.data import RemoteDataService 11 | from jaqs.trade import AlphaBacktestInstance, AlphaLiveTradeInstance 12 | 13 | import jaqs.util as jutil 14 | from jaqs.trade import PortfolioManager 15 | import jaqs.trade.analyze as ana 16 | from jaqs.trade import AlphaStrategy 17 | from jaqs.trade import AlphaTradeApi, RealTimeTradeApi 18 | from jaqs.trade import model 19 | from jaqs.data import DataView 20 | 21 | from config_path import DATA_CONFIG_PATH, TRADE_CONFIG_PATH 22 | data_config = jutil.read_json(DATA_CONFIG_PATH) 23 | trade_config = jutil.read_json(TRADE_CONFIG_PATH) 24 | 25 | dataview_dir_path = '../../output/wine_industry_momentum/dataview' 26 | backtest_result_dir_path = '../../output/wine_industry_momentum' 27 | 28 | BENCHMARK = '399997.SZ' 29 | is_backtest = False 30 | 31 | 32 | def test_save_dataview(): 33 | ds = RemoteDataService() 34 | ds.init_from_config(data_config) 35 | dv = DataView() 36 | 37 | props = {'start_date': 20170901, 'end_date': 20171129, 'universe': BENCHMARK, 38 | 'fields': 'close,volume,sw1', 39 | 'freq': 1} 40 | 41 | dv.init_from_config(props, ds) 42 | dv.prepare_data() 43 | 44 | dv.add_formula('ret', 'Return(close_adj, 20)', is_quarterly=False) 45 | dv.add_formula('rank_ret', 'Rank(ret)', is_quarterly=False) 46 | 47 | dv.save_dataview(folder_path=dataview_dir_path) 48 | 49 | 50 | def my_selector(context, user_options=None): 51 | rank_ret = context.snapshot['rank_ret'] 52 | 53 | return rank_ret >= 0.9 54 | 55 | 56 | def test_alpha_strategy_dataview(): 57 | dv = DataView() 58 | dv.load_dataview(folder_path=dataview_dir_path) 59 | 60 | props = { 61 | "benchmark": BENCHMARK, 62 | "universe": ','.join(dv.symbol), 63 | 64 | "start_date": dv.start_date, 65 | "end_date": dv.end_date, 66 | 67 | "period": "day", 68 | "days_delay": 0, 69 | 70 | "init_balance": 1e8, 71 | "position_ratio": 1.0, 72 | "strategy_no": 44 73 | } 74 | props.update(data_config) 75 | props.update(trade_config) 76 | 77 | stock_selector = model.StockSelector() 78 | stock_selector.add_filter(name='rank_ret_top10', func=my_selector) 79 | 80 | strategy = AlphaStrategy(stock_selector=stock_selector, pc_method='equal_weight') 81 | pm = PortfolioManager() 82 | 83 | if is_backtest: 84 | bt = AlphaBacktestInstance() 85 | trade_api = AlphaTradeApi() 86 | ds = None 87 | else: 88 | bt = AlphaLiveTradeInstance() 89 | trade_api = RealTimeTradeApi(props) 90 | ds = RemoteDataService() 91 | 92 | context = model.Context(dataview=dv, instance=bt, strategy=strategy, trade_api=trade_api, pm=pm, data_api=ds) 93 | stock_selector.register_context(context) 94 | 95 | bt.init_from_config(props) 96 | bt.run_alpha() 97 | 98 | if is_backtest: 99 | bt.save_results(folder_path=backtest_result_dir_path) 100 | else: 101 | goal_positions = strategy.goal_positions 102 | print(goal_positions) 103 | 104 | 105 | def test_backtest_analyze(): 106 | ta = ana.AlphaAnalyzer() 107 | dv = DataView() 108 | dv.load_dataview(folder_path=dataview_dir_path) 109 | 110 | ta.initialize(dataview=dv, file_folder=backtest_result_dir_path) 111 | 112 | ta.do_analyze(result_dir=backtest_result_dir_path, selected_sec=list(ta.universe)[:3], brinson_group='sw1') 113 | 114 | 115 | if __name__ == "__main__": 116 | t_start = time.time() 117 | 118 | test_save_dataview() 119 | test_alpha_strategy_dataview() 120 | if is_backtest: 121 | test_backtest_analyze() 122 | 123 | t3 = time.time() - t_start 124 | print("\n\n\nTime lapsed in total: {:.1f}".format(t3)) 125 | -------------------------------------------------------------------------------- /example/alpha/select_stocks_pe_profit.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | """ 4 | pe_ttm between 10 and 20 5 | net profit growth between 0.2 and 4 6 | equal weight 7 | universe : hs300 8 | init_balance = 1e8 9 | start_date 20170101 10 | end_date 20171001 11 | """ 12 | from __future__ import print_function 13 | from __future__ import absolute_import 14 | import time 15 | 16 | from jaqs.data import RemoteDataService 17 | from jaqs.trade import AlphaBacktestInstance 18 | 19 | import jaqs.util as jutil 20 | import jaqs.trade.analyze as ana 21 | from jaqs.trade import PortfolioManager 22 | from jaqs.trade import AlphaStrategy 23 | from jaqs.trade import AlphaTradeApi 24 | from jaqs.trade import model 25 | from jaqs.data import DataView 26 | 27 | from config_path import DATA_CONFIG_PATH, TRADE_CONFIG_PATH 28 | data_config = jutil.read_json(DATA_CONFIG_PATH) 29 | trade_config = jutil.read_json(TRADE_CONFIG_PATH) 30 | 31 | dataview_dir_path = '../../output/select_stocks_pe_profit/dataview' 32 | backtest_result_dir_path = '../../output/select_stocks_pe_profit' 33 | 34 | 35 | def test_save_dataview(): 36 | ds = RemoteDataService() 37 | ds.init_from_config(data_config) 38 | dv = DataView() 39 | 40 | props = {'start_date': 20170101, 'end_date': 20171001, 'universe': '000300.SH', 41 | 'fields': 'pe_ttm,net_profit_incl_min_int_inc', 42 | 'freq': 1} 43 | 44 | dv.init_from_config(props, ds) 45 | dv.prepare_data() 46 | 47 | factor_formula = 'Return(net_profit_incl_min_int_inc, 4)' 48 | factor_name = 'net_profit_growth' 49 | dv.add_formula(factor_name, factor_formula, is_quarterly=True) 50 | 51 | dv.save_dataview(folder_path=dataview_dir_path) 52 | 53 | 54 | def test_alpha_strategy_dataview(): 55 | dv = DataView() 56 | 57 | dv.load_dataview(folder_path=dataview_dir_path) 58 | 59 | props = { 60 | "benchmark": "000300.SH", 61 | "universe": ','.join(dv.symbol), 62 | 63 | "start_date": dv.start_date, 64 | "end_date": dv.end_date, 65 | 66 | "period": "month", 67 | "days_delay": 0, 68 | 69 | "init_balance": 1e8, 70 | "position_ratio": 1.0, 71 | } 72 | props.update(data_config) 73 | props.update(trade_config) 74 | 75 | trade_api = AlphaTradeApi() 76 | trade_api.init_from_config(props) 77 | 78 | def selector_growth(context, user_options=None): 79 | growth_rate = context.snapshot['net_profit_growth'] 80 | return (growth_rate >= 0.2) & (growth_rate <= 4) 81 | 82 | def selector_pe(context, user_options=None): 83 | pe_ttm = context.snapshot['pe_ttm'] 84 | return (pe_ttm >= 10) & (pe_ttm <= 20) 85 | 86 | stock_selector = model.StockSelector() 87 | stock_selector.add_filter(name='net_profit_growth', func=selector_growth) 88 | stock_selector.add_filter(name='pe', func=selector_pe) 89 | 90 | strategy = AlphaStrategy(stock_selector=stock_selector, pc_method='equal_weight') 91 | pm = PortfolioManager() 92 | 93 | bt = AlphaBacktestInstance() 94 | 95 | context = model.Context(dataview=dv, instance=bt, strategy=strategy, trade_api=trade_api, pm=pm) 96 | stock_selector.register_context(context) 97 | 98 | bt.init_from_config(props) 99 | bt.run_alpha() 100 | 101 | bt.save_results(folder_path=backtest_result_dir_path) 102 | 103 | 104 | def test_backtest_analyze(): 105 | ta = ana.AlphaAnalyzer() 106 | dv = DataView() 107 | dv.load_dataview(folder_path=dataview_dir_path) 108 | 109 | ta.initialize(dataview=dv, file_folder=backtest_result_dir_path) 110 | 111 | ta.do_analyze(result_dir=backtest_result_dir_path, selected_sec=list(ta.universe)[:3]) 112 | 113 | 114 | if __name__ == "__main__": 115 | t_start = time.time() 116 | 117 | test_save_dataview() 118 | test_alpha_strategy_dataview() 119 | test_backtest_analyze() 120 | 121 | t3 = time.time() - t_start 122 | print("\n\n\nTime lapsed in total: {:.1f}".format(t3)) 123 | 124 | -------------------------------------------------------------------------------- /doc/install.md: -------------------------------------------------------------------------------- 1 | # JAQS安装步骤 2 | 3 | ## Windows下一键安装脚本 4 | 如果没有安装Python包的经验,或者不熟悉相关操作,可以使用我们的一键安装脚本。该脚本仅适用于Windows系统,使用步骤为: 5 | 1. 先按照下方**如何安装Anaconda**操作,在系统中安装Anaconda; 6 | 2. 下载[一键安装脚本](http://www.quantos.org/downloads/install_scripts/onekey_install_jaqs.zip),解压后运行`onekey_install_jaqs.bat`,看到"Successfully installed jaqs"字样,说明安装成功。 7 | 8 | ## 1、安装Python环境 9 | 运行JAQS需要Python环境,可通过在控制台(Windows系统为命令提示符,Linux系统为终端)运行`python`命令确定系统是否安装。 10 | 11 | 如果本地还没有安装Python环境,或已安装的Python不是[Anaconda](http://www.continuum.io/downloads "Anaconda"),强烈建议按下方步骤安装,Anaconda是集成开发环境,其中包含稳定版Python和众多常用包,且易于安装,避免不必要的麻烦;如果已经装好了Anaconda,可直接看下一步骤**安装依赖包**。 12 | 13 | ### 如何安装Anaconda 14 | 15 | 1. **下载安装包**:打开[Anaconda官网](http://www.continuum.io/downloads),选择相应的操作系统,确定要安装的Python版本,目前JAQS同时支持Python2/3. 16 | ![anac](https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/anac.png) 17 | 18 | 2. **安装**:下载完成后,运行下载好的文件,按提示完成安装(**注**:对于Windows系统,在默认情况下,Anaconda不会自动设置`PATH`环境,请确保选择“add Anaconda to system PATH”选项。)。具体安装教程参见[官方文档](https://conda.io/docs/user-guide/install/index.html#regular-installation)。 19 | 20 | 21 | 22 | 3. **检查安装是否成功**:安装完成后, 23 | 24 | - **windows**下,可以在系统菜单中看如下程序目录:![anacm](https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/anac_m.png). 在cmd里执行`ipython`命令,可以调出IPython调试器:![anacipython](https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/anac_ipython.png). 25 | - **Linux**下,可以在Terminal运行`Python`,可看到'Anaconda'字样。 26 | 27 | ## 2、安装依赖包 28 | 29 | 除Anaconda中已包含的常用包外,JAQS还有些额外的依赖,除`python-snappy`这个包外,其他依赖可在使用`pip install jaqs`安装JAQS的过程中**自动安装**。 30 | 31 | 请首先按照**如何安装`python-snappy`包**所述操作,然后直接安装JAQS。 32 | 33 | 如果希望手动安装依赖包(不推荐),请按照**如何手动安装依赖包**所述操作。 34 | 35 | ### 如何安装`python-snappy`包 36 | 37 | 这个包的安装需要比较多的编译依赖,直接使用`pip install python-snappy`安装可能会报错,请根据自己的操作系统,按下方步骤安装。 38 | 39 | #### Windows 40 | 41 | 在Windows上准备编译环境较为复杂,建议从[这个网页](https://www.lfd.uci.edu/~gohlke/pythonlibs/#python-snappys)下载编译好的包并直接安装。 42 | 43 | 下载时,需选择适合自己系统版本和Python版本的包,其中 44 | 45 | - cp27代表Python2.7,cp36代表Python3.6,以此类推 46 | - 64位系统选择带"amd64"字样的安装文件,32位系统选择不带的 47 | 48 | 安装时,使用`pip install 文件名`进行安装,如: 49 | 50 | ```shell 51 | pip install python_snappy-0.5.1-cp27-cp27m-win_amd64.whl # 具体文件名可能不同, 取决于系统版本 52 | ``` 53 | 54 | #### Linux 55 | 56 | 首先,安装snappy的开发包`libsnappy-dev`: 57 | 58 | ```shell 59 | sudo apt-get install libsnappy-dev 60 | ``` 61 | 62 | *注*:`libsnappy-dev`适用于Ubuntu系统,CentOS/RedHat/Fedora/SUSE等系统请使用`libsnappy-devel`,其他Linux系统请自行查找。 63 | 64 | 之后即可通过`pip`安装`python-snappy`: 65 | 66 | ```shell 67 | pip install python-snappy 68 | ``` 69 | #### OS-X 70 | 参见[官方说明](https://github.com/andrix/python-snappy#frequently-asked-questions) 71 | 72 | 73 | 装好`python-snappy`后,即可使用`pip`直接安装JAQS和其他依赖包,见下一节**安装JAQS**。 74 | 75 | ### 如何手动安装依赖包 76 | 77 | - 可以在jaqs程序目录下,执行 `pip install -r requirements.txt` 一次完成所有依赖的安装。 78 | - 也可以通过单个安装完成,例如: `pip install pyzmq` 79 | 80 | 81 | ## 3、安装JAQS 82 | 可以使用`pip`安装或下载源代码安装。由于项目处于不断开发中,使用`pip`安装的一般为稳定版本;而使用源码安装,则可以自由选择版本(如最新开发版)。一般情况下,我们推荐使用`pip`. 83 | 84 | ### 使用`pip`进行安装 85 | ```sheel 86 | pip install jaqs 87 | ``` 88 | 89 | ### 通过源代码安装 90 | #### 1. 下载源代码 91 | 打开[项目的GitHub首页](https://github.com/quantOS-org/JAQS),选择一个分支(稳定版为`release-x.x.x`,最新开发版为`master`),再点击“Clone or Download”-“Download ZIP”即可下载。或者,也可以通过Git命令克隆: 92 | ```shell 93 | git clone https://github.com/quantOS-org/jaqs.git 94 | ``` 95 | 96 | #### 2. 从源代码安装 97 | 进入项目目录(如果是直接下载,项目目录名为`JAQS-分支名`,如果是git克隆,项目目录名为`JAQS`),打开命令提示符/终端,执行安装命令: 98 | ```shell 99 | python setup.py install 100 | ``` 101 | 102 | ***注***:如果已经通过`pip`安装了稳定版JAQS,希望通过源代码安装最新版本,则先通过`pip uninstall jaqs`卸载,在下载源代码安装即可。 103 | 104 | ### 4、确认安装成功 105 | 完成安装以后,在命令行中运行`python`并执行`import jaqs`确认安装是否成功: 106 | ![jaqstest](https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/jaqs_test.png) 107 | 108 | ## 5、升级JAQS 109 | 如果有新的release,可通过如下命令升级: 110 | ```shell 111 | pip uninstall jaqs 112 | pip install jaqs 113 | ``` 114 | 115 | *注*:不使用`pip install --upgrade jaqs`是为了保护使用者现有环境,使用该命令会同时升级`pandas`等依赖包,导致改变用户当前包环境。 116 | 117 | ## 6、策略样例 118 | 119 | 策略参考样例,请访问[https://github.com/quantOS-org/JAQS/tree/master/example](https://github.com/quantOS-org/JAQS/tree/master/example) 120 | -------------------------------------------------------------------------------- /doc/trade_api.md: -------------------------------------------------------------------------------- 1 | ## 交易API 2 | 3 | 本产品提供了交易API,可用于委托、撤单、查询账户、查询持仓、查询委托、查询成交、批量下单等,以下是用法 4 | 5 | ### 导入接口 6 | 7 | 在python程序里面导入module,然后用注册的用户帐号登录就可以使用交易接口进行交易。 8 | 9 | #### 引入模块 10 | 11 | ```python 12 | from jaqs.trade.tradeapi import TradeApi 13 | ``` 14 | #### 登录 15 | ```python 16 | tapi = TradeApi(addr="tcp://gw.quantos.org:8901") # tcp://gw.quantos.org:8901是仿真系统地址 17 | user_info, msg = tapi.login("demo", "666666") # 示例账户,用户需要改为自己注册的账户 18 | ``` 19 | 20 | 使用用户名、密码登陆, 如果成功,返回用户可用的策略帐号列表 21 | 22 | ### 定义并设置回调接口 23 | 24 | TradeApi通过回调函数方式通知用户事件。事件包括三种:订单状态、成交回报、委托任务执行状态。 25 | 26 | - 订单状态推送 27 | ```python 28 | def on_orderstatus(order): 29 | print "on_orderstatus:" #, order 30 | for key in order: print "%20s : %s" % (key, str(order[key])) 31 | print "" 32 | ``` 33 | 34 | - 成交回报推送 35 | ```python 36 | def on_trade(trade): 37 | print "on_trade:" 38 | for key in trade: print "%20s : %s" % (key, str(trade[key])) 39 | print "" 40 | ``` 41 | 42 | - 委托任务执行状态推送,通常可以忽略该回调函数 43 | ```python 44 | def on_taskstatus(task): 45 | print "on_taskstatus:" 46 | for key in task: print "%20s : %s" % (key, str(task[key])) 47 | print "" 48 | ``` 49 | 50 | 设置回调函数 51 | ```python 52 | tapi.set_ordstatus_callback(on_orderstatus) 53 | tapi.set_trade_callback(on_trade) 54 | tapi.set_task_callback(on_taskstatus) 55 | ``` 56 | 57 | ### 选择使用的策略帐号 58 | 59 | 该函数成功后,下单、查持仓等和策略帐号有关的操作都和该策略帐号绑定。 60 | 没有必要每次下单、查询都调用该函数。重复调用该函数可以选择新的策略帐号。 61 | 62 | 如果成功,返回(strategy_id, msg) 63 | 否则返回 (0, err_msg) 64 | ```python 65 | sid, msg = tapi.use_strategy(1) 66 | print "msg: ", msg 67 | print "sid: ", sid 68 | ``` 69 | 70 | ### 查询账户信息 71 | 返回当前的策略帐号的账户资金信息。 72 | ```python 73 | df, msg = tapi.query_account() 74 | print "msg: ", msg 75 | print df 76 | ``` 77 | 78 | ### 查询Portfolio 79 | 返回当前的策略帐号的Universe中所有标的的净持仓,包括持仓为0的标的。 80 | ```python 81 | df, msg = tapi.query_portfolio() 82 | print "msg: ", msg 83 | print df 84 | ``` 85 | 86 | ### 查询当前策略帐号的所有持仓 87 | 和 query_portfolio接口不一样。如果莫个期货合约 Long, Short两个方向都有持仓,这里是返回两条记录 88 | 返回的 size 不带方向,全部为 正 89 | ```python 90 | df, msg = tapi.query_position() 91 | print "msg: ", msg 92 | print df 93 | ``` 94 | 95 | ### 单标的下单 96 | task_id, msg = place_order(code, action, price, size ) 97 | action: Buy, Short, Cover, Sell, CoverToday, CoverYesterday, SellToday, SellYesterday 98 | 返回 task_id 99 | 100 | ```python 101 | task_id, msg = tapi.place_order("000025.SZ", "Buy", 57, 100) 102 | print "msg:", msg 103 | print "task_id:", task_id 104 | ``` 105 | 106 | ### 撤单 107 | cancel_order(task_id) 108 | ```python 109 | tapi.cancel_order(task_id) 110 | ``` 111 | 112 | ### 查询委托 113 | 返回委托信息 114 | ```python 115 | df, msg = tapi.query_order(task_id = task_id, format = 'pandas') 116 | ``` 117 | 118 | ### 查询成交 119 | 返回成交信息 120 | ```python 121 | df, msg = tapi.query_trade(task_id = task_id, format = 'pandas') 122 | ``` 123 | 124 | ### 目标持仓下单 125 | ```python 126 | # goal_protfolio 127 | # 参数:目标持仓 128 | # 返回:(result, msg) 129 | # result: 成功或失败 130 | # msg: 错误原因 131 | # 注意:目标持仓中必须包括所有的代码的持仓,即使不修改 132 | 133 | # 先查询当前的持仓, 134 | portfolio, msg = tapi.goal_portfolio(goal, algo, algo_param) 135 | print "msg", msg 136 | print "portfolio", portfolio 137 | ``` 138 | 139 | ### portfolio撤单 140 | ```python 141 | # stop_portfolio 142 | # 撤单, 撤销所有portfolio订单 143 | tapi.stop_portfolio() 144 | ```` 145 | 146 | ### 批量下单(1) 147 | place_batch_order,指定绝对size和交易类型 148 | ```python 149 | # place_batch_order 150 | # 返回task_id, msg。 151 | orders = [ 152 | {"security":"600030.SH", "action" : "Buy", "price": 16, "size":1000}, 153 | {"security":"600519.SH", "action" : "Buy", "price": 320, "size":1000}, 154 | ] 155 | 156 | task_id, msg = tapi.place_batch_order(orders) 157 | print task_id 158 | print msg 159 | ``` 160 | 161 | ### 批量下单(2) 162 | basket_order,指定变化量,不指定交易方向,由系统根据正负号来确定 163 | ```python 164 | # 批量下单2:basket_order 165 | # 166 | # 返回task_id, msg。 167 | orders = [ 168 | {"security":"601857.SH", "ref_price": 8.40, "inc_size":1000}, 169 | {"security":"601997.SH", "ref_price": 14.540, "inc_size":20000}, 170 | ] 171 | 172 | task_id, msg = tapi.basket_order(orders) 173 | print task_id 174 | print msg 175 | ``` 176 | -------------------------------------------------------------------------------- /example/alpha/select_stocks_industry_head.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | """ 4 | 选择行业龙头股(价值白马) 5 | 1. 流通市值从大到小排列 (float_mv) 6 | 2. PB从小到大 (pb) 7 | 3. PE从小到大 (pe_ttm) 8 | 分别在行业内排序,然后计算每支股票的分数,取总分靠前的20%, 9 | 等权重,按月调仓 10 | universe : hs300 11 | init_balance = 1e8 12 | start_date 20170101 13 | end_date 20171001 14 | """ 15 | from __future__ import print_function 16 | from __future__ import absolute_import 17 | import time 18 | 19 | import pandas as pd 20 | 21 | from jaqs.data import RemoteDataService 22 | from jaqs.trade import AlphaBacktestInstance 23 | 24 | import jaqs.util as jutil 25 | from jaqs.trade import PortfolioManager 26 | import jaqs.trade.analyze as ana 27 | from jaqs.trade import AlphaStrategy 28 | from jaqs.trade import AlphaTradeApi 29 | from jaqs.trade import model 30 | from jaqs.data import DataView 31 | 32 | from config_path import DATA_CONFIG_PATH, TRADE_CONFIG_PATH 33 | data_config = jutil.read_json(DATA_CONFIG_PATH) 34 | trade_config = jutil.read_json(TRADE_CONFIG_PATH) 35 | 36 | dataview_dir_path = '../../output/select_stocks_industry_head/dataview' 37 | backtest_result_dir_path = '../../output/select_stocks_industry_head' 38 | 39 | 40 | def test_save_dataview(): 41 | ds = RemoteDataService() 42 | ds.init_from_config(data_config) 43 | dv = DataView() 44 | 45 | props = {'start_date': 20170101, 'end_date': 20171001, 'universe': '000300.SH', 46 | 'fields': ('float_mv,pb,pe_ttm,sw2'), 47 | 'freq': 1} 48 | 49 | dv.init_from_config(props, ds) 50 | dv.prepare_data() 51 | 52 | factor_formula = 'GroupQuantile(-float_mv, sw2, 10)' 53 | dv.add_formula('rank_mv', factor_formula, is_quarterly=False) 54 | 55 | factor_formula = 'GroupQuantile(If(pb >= 0.2, pb, 100), sw2, 10)' 56 | dv.add_formula('rank_pb', factor_formula, is_quarterly=False) 57 | 58 | factor_formula = 'GroupQuantile(If(pe_ttm >= 3, pe_ttm, 9999.0), sw2, 10)' 59 | dv.add_formula('rank_pe', factor_formula, is_quarterly=False) 60 | 61 | dv.save_dataview(folder_path=dataview_dir_path) 62 | 63 | 64 | def my_selector(context, user_options=None): 65 | rank_mv = context.snapshot['rank_mv'] 66 | rank_pb = context.snapshot['rank_pb'] 67 | rank_pe = context.snapshot['rank_pe'] 68 | 69 | rank = pd.DataFrame() 70 | rank['rank_total'] = rank_mv + rank_pb + rank_pe 71 | 72 | rank = rank.sort_values('rank_total', ascending=True) 73 | length = int(rank.shape[0] * 0.2) 74 | rank.iloc[: length] = 1.0 75 | rank.iloc[length: ] = 0.0 76 | return rank 77 | 78 | 79 | def test_alpha_strategy_dataview(): 80 | dv = DataView() 81 | dv.load_dataview(folder_path=dataview_dir_path) 82 | 83 | props = { 84 | "benchmark": "000300.SH", 85 | "universe": ','.join(dv.symbol), 86 | 87 | "start_date": dv.start_date, 88 | "end_date": dv.end_date, 89 | 90 | "period": "month", 91 | "days_delay": 0, 92 | 93 | "init_balance": 1e8, 94 | "position_ratio": 1.0, 95 | } 96 | props.update(data_config) 97 | props.update(trade_config) 98 | 99 | trade_api = AlphaTradeApi() 100 | 101 | stock_selector = model.StockSelector() 102 | stock_selector.add_filter(name='myrank', func=my_selector) 103 | 104 | strategy = AlphaStrategy(stock_selector=stock_selector, pc_method='equal_weight') 105 | pm = PortfolioManager() 106 | 107 | bt = AlphaBacktestInstance() 108 | 109 | context = model.Context(dataview=dv, instance=bt, strategy=strategy, trade_api=trade_api, pm=pm) 110 | stock_selector.register_context(context) 111 | 112 | bt.init_from_config(props) 113 | bt.run_alpha() 114 | 115 | bt.save_results(folder_path=backtest_result_dir_path) 116 | 117 | 118 | def test_backtest_analyze(): 119 | ta = ana.AlphaAnalyzer() 120 | dv = DataView() 121 | dv.load_dataview(folder_path=dataview_dir_path) 122 | 123 | ta.initialize(dataview=dv, file_folder=backtest_result_dir_path) 124 | 125 | ta.do_analyze(result_dir=backtest_result_dir_path, selected_sec=list(ta.universe)[:3]) 126 | 127 | 128 | if __name__ == "__main__": 129 | t_start = time.time() 130 | 131 | test_save_dataview() 132 | test_alpha_strategy_dataview() 133 | test_backtest_analyze() 134 | 135 | t3 = time.time() - t_start 136 | print("\n\n\nTime lapsed in total: {:.1f}".format(t3)) 137 | -------------------------------------------------------------------------------- /example/alpha/FamaFrench.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | """ 4 | Weekly rebalance 5 | 6 | 1. size: float market value 7 | 2. value: book equity: stockholders equity + redemption/liquidation + deferred tax - postretirement benefits 8 | 9 | universe : hs300 10 | init_balance = 1e8 11 | start_date 20140101 12 | end_date 20170301 13 | """ 14 | from __future__ import print_function 15 | from __future__ import absolute_import 16 | import time 17 | 18 | import pandas as pd 19 | 20 | import jaqs.trade.analyze as ana 21 | from jaqs.data import RemoteDataService 22 | from jaqs.data import DataView 23 | from jaqs.trade import model 24 | from jaqs.trade import PortfolioManager 25 | from jaqs.trade import AlphaBacktestInstance 26 | from jaqs.trade import AlphaTradeApi 27 | from jaqs.trade import AlphaStrategy 28 | import jaqs.util as jutil 29 | 30 | from config_path import DATA_CONFIG_PATH, TRADE_CONFIG_PATH 31 | data_config = jutil.read_json(DATA_CONFIG_PATH) 32 | trade_config = jutil.read_json(TRADE_CONFIG_PATH) 33 | 34 | dataview_dir_path = '../../output/fama_french/dataview' 35 | backtest_result_dir_path = '../../output/fama_french' 36 | 37 | 38 | def test_save_dataview(sub_folder='test_dataview'): 39 | ds = RemoteDataService() 40 | ds.init_from_config(data_config) 41 | dv = DataView() 42 | 43 | props = {'start_date': 20150101, 'end_date': 20170930, 'universe': '000905.SH', 44 | 'fields': ('float_mv,tot_shrhldr_eqy_excl_min_int,deferred_tax_assets,sw2'), 45 | 'freq': 1} 46 | 47 | dv.init_from_config(props, ds) 48 | dv.prepare_data() 49 | 50 | factor_formula = 'Quantile(-float_mv,5)' 51 | dv.add_formula('rank_mv', factor_formula, is_quarterly=False) 52 | 53 | factor_formula = 'Quantile(float_mv/(tot_shrhldr_eqy_excl_min_int+deferred_tax_assets), 5)' 54 | dv.add_formula('rank_pb', factor_formula, is_quarterly=False) 55 | 56 | dv.save_dataview(folder_path=dataview_dir_path) 57 | 58 | 59 | def my_selector(context, user_options=None): 60 | dv = context.dataview 61 | 62 | rank_mv = dv.get_snapshot(context.trade_date, fields='rank_mv') 63 | rank_pb = dv.get_snapshot(context.trade_date, fields='rank_pb') 64 | # rank_pe = dv.get_snapshot(context.trade_date, fields='rank_pe') 65 | 66 | rank = pd.DataFrame() 67 | rank['rank_total'] = rank_mv['rank_mv'] + rank_pb['rank_pb'] 68 | 69 | rank = rank.sort_values('rank_total', ascending=True) 70 | length = int(rank.shape[0] * 0.2) 71 | return rank.isin(rank.head(length)) 72 | 73 | 74 | def test_alpha_strategy_dataview(): 75 | dv = DataView() 76 | 77 | dv.load_dataview(folder_path=dataview_dir_path) 78 | 79 | props = { 80 | "start_date": dv.start_date, 81 | "end_date": dv.end_date, 82 | 83 | "period": "week", 84 | "days_delay": 0, 85 | 86 | "init_balance": 1e8, 87 | "position_ratio": 1.0, 88 | } 89 | props.update(data_config) 90 | props.update(trade_config) 91 | 92 | trade_api = AlphaTradeApi() 93 | 94 | stock_selector = model.StockSelector() 95 | stock_selector.add_filter(name='myselector', func=my_selector) 96 | 97 | strategy = AlphaStrategy(stock_selector=stock_selector, pc_method='equal_weight') 98 | pm = PortfolioManager() 99 | 100 | bt = AlphaBacktestInstance() 101 | 102 | context = model.Context(dataview=dv, instance=bt, strategy=strategy, trade_api=trade_api, pm=pm) 103 | stock_selector.register_context(context) 104 | 105 | bt.init_from_config(props) 106 | 107 | bt.run_alpha() 108 | 109 | bt.save_results(folder_path=backtest_result_dir_path) 110 | 111 | 112 | def test_backtest_analyze(): 113 | ta = ana.AlphaAnalyzer() 114 | dv = DataView() 115 | dv.load_dataview(folder_path=dataview_dir_path) 116 | 117 | ta.initialize(dataview=dv, file_folder=backtest_result_dir_path) 118 | 119 | ta.do_analyze(result_dir=backtest_result_dir_path, selected_sec=list(ta.universe)[:3]) 120 | 121 | 122 | if __name__ == "__main__": 123 | dv_subfolder_name = 'graham' 124 | t_start = time.time() 125 | 126 | test_save_dataview() 127 | test_alpha_strategy_dataview() 128 | test_backtest_analyze() 129 | 130 | t3 = time.time() - t_start 131 | print("\n\n\nTime lapsed in total: {:.1f}".format(t3)) 132 | -------------------------------------------------------------------------------- /jaqs/trade/tradeapi/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | from __future__ import unicode_literals 5 | 6 | try: 7 | basestring 8 | except NameError: 9 | basestring = str 10 | from collections import namedtuple 11 | import datetime as dt 12 | import pandas as pd 13 | import numpy as np 14 | 15 | long_nan = 9223372036854775807 16 | 17 | def is_long_nan(v): 18 | if v == long_nan: 19 | return True 20 | else: 21 | return False 22 | 23 | 24 | def to_nan(x): 25 | if is_long_nan(x): 26 | return np.nan 27 | else: 28 | return x 29 | 30 | 31 | def _to_date(row): 32 | date = int(row['DATE']) 33 | return pd.datetime(year=date // 10000, month=date // 100 % 100, day=date % 100) 34 | 35 | 36 | def _to_datetime(row): 37 | date = int(row['DATE']) 38 | time = int(row['TIME']) // 1000 39 | return pd.datetime(year=date // 10000, month=date // 100 % 100, day=date % 100, 40 | hour=time // 10000, minute=time // 100 % 100, second=time % 100) 41 | 42 | 43 | def _to_dataframe(cloumset, index_func=None, index_column=None): 44 | df = pd.DataFrame(cloumset) 45 | for col in df.columns: 46 | if df.dtypes.loc[col] == np.int64: 47 | df.loc[:, col] = df.loc[:, col].apply(to_nan) 48 | if index_func: 49 | df.index = df.apply(index_func, axis=1) 50 | elif index_column: 51 | df.index = df[index_column] 52 | del df.index.name 53 | 54 | return df 55 | 56 | 57 | def _error_to_str(error): 58 | if error: 59 | if 'message' in error: 60 | return str(error['error']) + "," + error['message'] 61 | else: 62 | return str(error['error']) + "," 63 | else: 64 | return "," 65 | 66 | 67 | def to_obj(class_name, data): 68 | try: 69 | if isinstance(data, (list, tuple)): 70 | result = [] 71 | for d in data: 72 | result.append(namedtuple(class_name, list(d.keys()))(*list(d.values()))) 73 | return result 74 | 75 | elif type(data) == dict: 76 | result = namedtuple(class_name, list(data.keys()))(*list(data.values())) 77 | return result 78 | else: 79 | return data 80 | except Exception as e: 81 | print(class_name, data, e) 82 | return data 83 | 84 | 85 | def to_date_int(date): 86 | if isinstance(date, basestring): 87 | t = dt.datetime.strptime(date, "%Y-%m-%d") 88 | date_int = t.year * 10000 + t.month * 100 + t.day 89 | return date_int 90 | elif isinstance(date, (int, np.integer)): 91 | return date 92 | else: 93 | return -1 94 | 95 | 96 | def to_time_int(time): 97 | if isinstance(time, basestring): 98 | t = dt.datetime.strptime(time, "%H:%M:%S") 99 | time_int = t.hour * 10000 + t.minute * 100 + t.second 100 | return time_int 101 | elif isinstance(time, (int, np.integer)): 102 | return time 103 | else: 104 | return -1 105 | 106 | 107 | def extract_result(cr, data_format="", index_column=None, class_name=""): 108 | """ 109 | format supports pandas, obj. 110 | """ 111 | 112 | err = _error_to_str(cr['error']) if 'error' in cr else None 113 | if 'result' in cr: 114 | if data_format == "pandas": 115 | if index_column: 116 | return (_to_dataframe(cr['result'], None, index_column), err) 117 | # if 'TIME' in cr['result']: 118 | # return (_to_dataframe(cr['result'], _to_datetime), err) 119 | # elif 'DATE' in cr['result']: 120 | # return (_to_dataframe(cr['result'], _to_date), err) 121 | else: 122 | return (_to_dataframe(cr['result']), err) 123 | 124 | elif data_format == "obj" and cr['result'] and class_name: 125 | r = cr['result'] 126 | if isinstance(r, (list, tuple)): 127 | result = [] 128 | for d in r: 129 | result.append(namedtuple(class_name, list(d.keys()))(*list(d.values()))) 130 | elif isinstance(r, dict): 131 | result = namedtuple(class_name, list(r.keys()))(*list(r.values())) 132 | else: 133 | result = r 134 | 135 | return (result, err) 136 | else: 137 | return (cr['result'], err) 138 | else: 139 | return (None, err) 140 | -------------------------------------------------------------------------------- /jaqs/data/dataapi/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | from __future__ import unicode_literals 5 | 6 | try: 7 | basestring 8 | except NameError: 9 | basestring = str 10 | from collections import namedtuple 11 | import datetime as dt 12 | import pandas as pd 13 | import numpy as np 14 | 15 | long_nan = 9223372036854775807 16 | 17 | def is_long_nan(v): 18 | if v == long_nan: 19 | return True 20 | else: 21 | return False 22 | 23 | 24 | def to_nan(x): 25 | if is_long_nan(x): 26 | return np.nan 27 | else: 28 | return x 29 | 30 | 31 | def _to_date(row): 32 | date = int(row['DATE']) 33 | return pd.datetime(year=date // 10000, month=date // 100 % 100, day=date % 100) 34 | 35 | 36 | def _to_datetime(row): 37 | date = int(row['DATE']) 38 | time = int(row['TIME']) // 1000 39 | return pd.datetime(year=date // 10000, month=date // 100 % 100, day=date % 100, 40 | hour=time // 10000, minute=time // 100 % 100, second=time % 100) 41 | 42 | 43 | def _to_dataframe(cloumset, index_func=None, index_column=None): 44 | df = pd.DataFrame(cloumset) 45 | for col in df.columns: 46 | if df.dtypes.loc[col] == np.int64: 47 | df.loc[:, col] = df.loc[:, col].apply(to_nan) 48 | if index_func: 49 | df.index = df.apply(index_func, axis=1) 50 | elif index_column: 51 | df.index = df[index_column] 52 | del df.index.name 53 | 54 | return df 55 | 56 | 57 | def _error_to_str(error): 58 | if error: 59 | if 'message' in error: 60 | return str(error['error']) + "," + error['message'] 61 | else: 62 | return str(error['error']) + "," 63 | else: 64 | return "," 65 | 66 | 67 | def to_obj(class_name, data): 68 | try: 69 | if isinstance(data, (list, tuple)): 70 | result = [] 71 | for d in data: 72 | result.append(namedtuple(class_name, list(d.keys()))(*list(d.values()))) 73 | return result 74 | 75 | elif type(data) == dict: 76 | result = namedtuple(class_name, list(data.keys()))(*list(data.values())) 77 | return result 78 | else: 79 | return data 80 | except Exception as e: 81 | print(class_name, data, e) 82 | return data 83 | 84 | 85 | def to_date_int(date): 86 | if isinstance(date, basestring): 87 | tmp = date.replace('-','') 88 | return int(tmp) 89 | elif isinstance(date, (int, np.integer)): 90 | return date 91 | else: 92 | return -1 93 | 94 | 95 | def to_time_int(time): 96 | if isinstance(time, basestring): 97 | if ':' in time: 98 | tmp = time.replace(':','') 99 | return int(tmp) 100 | else: 101 | t = dt.datetime.strptime(time, "%H:%M:%S") 102 | time_int = t.hour * 10000 + t.minute * 100 + t.second 103 | return time_int 104 | 105 | elif isinstance(time, (int, np.integer)): 106 | return time 107 | else: 108 | return -1 109 | 110 | 111 | def extract_result(cr, data_format="", index_column=None, class_name=""): 112 | """ 113 | format supports pandas, obj. 114 | """ 115 | 116 | err = _error_to_str(cr['error']) if 'error' in cr else None 117 | if 'result' in cr and cr['result'] is not None: 118 | if data_format == "pandas": 119 | if index_column: 120 | return (_to_dataframe(cr['result'], None, index_column), err) 121 | # if 'TIME' in cr['result']: 122 | # return (_to_dataframe(cr['result'], _to_datetime), err) 123 | # elif 'DATE' in cr['result']: 124 | # return (_to_dataframe(cr['result'], _to_date), err) 125 | else: 126 | return (_to_dataframe(cr['result']), err) 127 | 128 | elif data_format == "obj" and cr['result'] and class_name: 129 | r = cr['result'] 130 | if isinstance(r, (list, tuple)): 131 | result = [] 132 | for d in r: 133 | result.append(namedtuple(class_name, list(d.keys()))(*list(d.values()))) 134 | elif isinstance(r, dict): 135 | result = namedtuple(class_name, list(r.keys()))(*list(r.values())) 136 | else: 137 | result = r 138 | 139 | return (result, err) 140 | else: 141 | return (cr['result'], err) 142 | else: 143 | return (None, err) 144 | -------------------------------------------------------------------------------- /test/test_backtest_alpha.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Both backtest and live trading are included. 4 | """ 5 | 6 | from __future__ import print_function 7 | from __future__ import absolute_import 8 | import time 9 | 10 | from jaqs.data import RemoteDataService 11 | from jaqs.trade import AlphaBacktestInstance, AlphaLiveTradeInstance 12 | 13 | import jaqs.util as jutil 14 | from jaqs.trade import PortfolioManager 15 | import jaqs.trade.analyze as ana 16 | from jaqs.trade import AlphaStrategy 17 | from jaqs.trade import AlphaTradeApi, RealTimeTradeApi 18 | from jaqs.trade import model 19 | from jaqs.data import DataView 20 | 21 | from config_path import DATA_CONFIG_PATH, TRADE_CONFIG_PATH 22 | data_config = jutil.read_json(DATA_CONFIG_PATH) 23 | trade_config = jutil.read_json(TRADE_CONFIG_PATH) 24 | 25 | dataview_dir_path = '../output/wine_industry_momentum/dataview' 26 | backtest_result_dir_path = '../output/wine_industry_momentum' 27 | 28 | BENCHMARK = '399997.SZ' 29 | 30 | 31 | def test_save_dataview(): 32 | ds = RemoteDataService() 33 | ds.init_from_config(data_config) 34 | dv = DataView() 35 | 36 | props = {'start_date': 20170901, 'end_date': 20171129, 'universe': BENCHMARK, 37 | 'fields': 'close,volume,sw1', 38 | 'freq': 1} 39 | 40 | dv.init_from_config(props, ds) 41 | dv.prepare_data() 42 | 43 | dv.add_formula('ret', 'Return(close_adj, 20)', is_quarterly=False) 44 | dv.add_formula('rank_ret', 'Rank(ret)', is_quarterly=False) 45 | 46 | dv.save_dataview(folder_path=dataview_dir_path) 47 | 48 | 49 | def my_selector(context, user_options=None): 50 | rank_ret = context.snapshot['rank_ret'] 51 | 52 | return rank_ret >= 0.9 53 | 54 | 55 | def test_backtest(): 56 | dv = DataView() 57 | dv.load_dataview(folder_path=dataview_dir_path) 58 | 59 | props = { 60 | "benchmark": BENCHMARK, 61 | "universe": ','.join(dv.symbol), 62 | 63 | "start_date": dv.start_date, 64 | "end_date": dv.end_date, 65 | 66 | "period": "day", 67 | "days_delay": 0, 68 | 69 | "init_balance": 1e8, 70 | "position_ratio": 1.0, 71 | } 72 | props.update(data_config) 73 | props.update(trade_config) 74 | 75 | stock_selector = model.StockSelector() 76 | stock_selector.add_filter(name='rank_ret_top10', func=my_selector) 77 | 78 | strategy = AlphaStrategy(stock_selector=stock_selector, pc_method='equal_weight') 79 | pm = PortfolioManager() 80 | bt = AlphaBacktestInstance() 81 | trade_api = AlphaTradeApi() 82 | ds = None 83 | 84 | context = model.Context(dataview=dv, instance=bt, strategy=strategy, trade_api=trade_api, pm=pm, data_api=ds) 85 | stock_selector.register_context(context) 86 | 87 | bt.init_from_config(props) 88 | bt.run_alpha() 89 | 90 | bt.save_results(folder_path=backtest_result_dir_path) 91 | 92 | do_analyze() 93 | 94 | 95 | def test_livetrade(): 96 | dv = DataView() 97 | dv.load_dataview(folder_path=dataview_dir_path) 98 | 99 | props = { 100 | "benchmark": BENCHMARK, 101 | "universe": ','.join(dv.symbol), 102 | 103 | "start_date": dv.start_date, 104 | "end_date": dv.end_date, 105 | 106 | "period": "day", 107 | "days_delay": 0, 108 | 109 | "init_balance": 1e8, 110 | "position_ratio": 1.0, 111 | "strategy_no": 1044 112 | } 113 | props.update(data_config) 114 | props.update(trade_config) 115 | 116 | stock_selector = model.StockSelector() 117 | stock_selector.add_filter(name='rank_ret_top10', func=my_selector) 118 | 119 | strategy = AlphaStrategy(stock_selector=stock_selector, pc_method='equal_weight') 120 | pm = PortfolioManager() 121 | bt = AlphaLiveTradeInstance() 122 | trade_api = RealTimeTradeApi(props) 123 | ds = RemoteDataService() 124 | 125 | context = model.Context(dataview=dv, instance=bt, strategy=strategy, trade_api=trade_api, pm=pm, data_api=ds) 126 | stock_selector.register_context(context) 127 | 128 | bt.init_from_config(props) 129 | bt.run_alpha() 130 | 131 | goal_positions = strategy.goal_positions 132 | print(goal_positions) 133 | 134 | do_analyze() 135 | 136 | 137 | def do_analyze(): 138 | ta = ana.AlphaAnalyzer() 139 | dv = DataView() 140 | dv.load_dataview(folder_path=dataview_dir_path) 141 | 142 | ta.initialize(dataview=dv, file_folder=backtest_result_dir_path) 143 | 144 | ta.do_analyze(result_dir=backtest_result_dir_path, selected_sec=list(ta.universe)[:3], brinson_group='sw1') 145 | 146 | 147 | if __name__ == "__main__": 148 | t_start = time.time() 149 | 150 | test_save_dataview() 151 | test_backtest() 152 | test_livetrade() 153 | 154 | t3 = time.time() - t_start 155 | print("\n\n\nTime lapsed in total: {:.1f}".format(t3)) 156 | -------------------------------------------------------------------------------- /jaqs/data/basic/trade.py: -------------------------------------------------------------------------------- 1 | # encoding:utf-8 2 | """ 3 | Classes defined in trade module are relevant to trades, including: 4 | - Trade 5 | - TradeInd 6 | - TradeStat 7 | 8 | """ 9 | 10 | from .order import Order 11 | 12 | 13 | class Trade(object): 14 | """ 15 | Trade represents fill/partial fill of an order. 16 | 17 | Attributes 18 | ---------- 19 | task_id : str 20 | Id of the task. 21 | entrust_no : str 22 | ID of the order. 23 | entrust_action : str 24 | symbol : str 25 | fill_price : float 26 | fill_size : int 27 | fill_date : int 28 | fill_time : int 29 | fill_no : str 30 | ID of this trade. 31 | 32 | """ 33 | 34 | def __init__(self, order=None): 35 | self.task_id = 0 36 | self.entrust_no = "" 37 | 38 | self.entrust_action = "" 39 | 40 | self.symbol = "" 41 | 42 | self.fill_no = "" 43 | self.fill_price = 0.0 44 | self.fill_size = 0 45 | self.fill_date = 0 46 | self.fill_time = 0 47 | 48 | self.commission = 0.0 49 | 50 | if order is not None: 51 | if isinstance(order, Order): 52 | self.init_from_order(order) 53 | else: 54 | raise ValueError("init_from_order only accept argument of type Order.") 55 | 56 | def init_from_order(self, order): 57 | """Update information from a given order.""" 58 | self.task_id = order.task_id 59 | self.entrust_no = order.entrust_no 60 | self.symbol = order.symbol 61 | self.entrust_action = order.entrust_action 62 | 63 | def set_fill_info(self, price, size, date, time, no, trade_date=0): 64 | """Update filling information.""" 65 | self.fill_price = price 66 | self.fill_size = size 67 | self.fill_date = date 68 | self.fill_time = time 69 | self.fill_no = no 70 | self.trade_date = trade_date if trade_date else date 71 | 72 | @classmethod 73 | def create_from_dict(cls, dic): 74 | trade_ind = cls() 75 | trade_ind.__dict__.update(dic) 76 | return trade_ind 77 | 78 | def __repr__(self): 79 | return "{0.fill_date:8d}({0.fill_time:8d}) " \ 80 | "{0.entrust_action:6s} {0.symbol:10s}@{0.fill_price:.3f}" \ 81 | " size = {0.fill_size}".format(self) 82 | 83 | def __str__(self): 84 | return self.__repr__() 85 | 86 | 87 | class TaskInd(object): 88 | """ 89 | TaskInd is a indication of status change of a task. 90 | 91 | Attributes 92 | ---------- 93 | task_id : int 94 | Id of the task. 95 | task_status : str 96 | Current status of the task. 97 | task_algo : str 98 | Algorithm of the task. 99 | task_msg : str 100 | Message relevant to the task. 101 | 102 | """ 103 | def __init__(self, task_id=0, task_status="", task_algo="", task_msg=""): 104 | self.task_id = task_id 105 | self.task_status = task_status 106 | self.task_algo = task_algo 107 | self.task_msg = task_msg 108 | 109 | @classmethod 110 | def create_from_dict(cls, dic): 111 | rsp = cls() 112 | rsp.__dict__.update(dic) 113 | return rsp 114 | 115 | def __repr__(self): 116 | return "task_id = {0.task_id:d} | task_status = {0.task_status:s} | task_algo = {0.task_algo:s}" \ 117 | "\ntask_msg = \n{0.task_msg:s}".format(self) 118 | 119 | def __str__(self): 120 | return self.__repr__() 121 | 122 | 123 | class TradeStat(object): 124 | """ 125 | TradeStat stores statistics of trading of a certain symbol. 126 | 127 | Attributes 128 | ---------- 129 | symbol : str 130 | buy_filled_size : int 131 | Amount of long position that is already filled. 132 | buy_want_size : int 133 | Amount of long position that is not yet filled. 134 | sell_filled_size : int 135 | Amount of short position that is already filled. 136 | sell_want_size : int 137 | Amount of short position that is not yet filled. 138 | 139 | """ 140 | def __init__(self, symbol=""): 141 | self.symbol = symbol 142 | self.buy_filled_size = 0 143 | self.buy_want_size = 0 144 | self.sell_filled_size = 0 145 | self.sell_want_size = 0 146 | 147 | def __repr__(self): 148 | return (" Want Size Filled Size \n" 149 | "====================================\n" 150 | "Buy {0:8.0f} {1:8.0f} \n" 151 | "------------------------------------\n" 152 | "Sell {2:8.0f} {3:8.0f} \n" 153 | "".format(self.buy_want_size, self.buy_filled_size, 154 | self.sell_want_size, self.sell_filled_size)) 155 | 156 | def __str__(self): 157 | return self.__repr__() 158 | -------------------------------------------------------------------------------- /doc/source/trade_api.rst: -------------------------------------------------------------------------------- 1 | 交易API 2 | ------- 3 | 4 | 本产品提供了交易API,可用于委托、撤单、查询账户、查询持仓、查询委托、查询成交、批量下单等,以下是用法 5 | 6 | 导入接口 7 | ~~~~~~~~ 8 | 9 | 在python程序里面导入module,然后用注册的用户帐号登录就可以使用交易接口进行交易。 10 | 11 | 引入模块 12 | ^^^^^^^^ 13 | 14 | .. code:: python 15 | 16 | from jaqs.trade.tradeapi import TradeApi 17 | 18 | 登录 19 | ^^^^ 20 | 21 | .. code:: python 22 | 23 | tapi = TradeApi(addr="tcp://gw.quantos.org:8901") # tcp://gw.quantos.org:8901是仿真系统地址 24 | user_info, msg = tapi.login("demo", "666666") # 示例账户,用户需要改为自己注册的账户 25 | 26 | 使用用户名、密码登陆, 如果成功,返回用户可用的策略帐号列表 27 | 28 | 定义并设置回调接口 29 | ~~~~~~~~~~~~~~~~~~ 30 | 31 | TradeApi通过回调函数方式通知用户事件。事件包括三种:订单状态、成交回报、委托任务执行状态。 32 | 33 | - 订单状态推送 34 | 35 | .. code:: python 36 | 37 | def on_orderstatus(order): 38 | print "on_orderstatus:" #, order 39 | for key in order: print "%20s : %s" % (key, str(order[key])) 40 | print "" 41 | 42 | - 成交回报推送 43 | 44 | .. code:: python 45 | 46 | def on_trade(trade): 47 | print "on_trade:" 48 | for key in trade: print "%20s : %s" % (key, str(trade[key])) 49 | print "" 50 | 51 | - 委托任务执行状态推送,通常可以忽略该回调函数 52 | 53 | .. code:: python 54 | 55 | def on_taskstatus(task): 56 | print "on_taskstatus:" 57 | for key in task: print "%20s : %s" % (key, str(task[key])) 58 | print "" 59 | 60 | 设置回调函数 61 | 62 | .. code:: python 63 | 64 | tapi.set_ordstatus_callback(on_orderstatus) 65 | tapi.set_trade_callback(on_trade) 66 | tapi.set_task_callback(on_taskstatus) 67 | 68 | 选择使用的策略帐号 69 | ~~~~~~~~~~~~~~~~~~ 70 | 71 | | 该函数成功后,下单、查持仓等和策略帐号有关的操作都和该策略帐号绑定。 72 | | 没有必要每次下单、查询都调用该函数。重复调用该函数可以选择新的策略帐号。 73 | 74 | | 如果成功,返回(strategy\_id, msg) 75 | | 否则返回 (0, err\_msg) 76 | 77 | .. code:: python 78 | 79 | sid, msg = tapi.use_strategy(1) 80 | print "msg: ", msg 81 | print "sid: ", sid 82 | 83 | 查询账户信息 84 | ~~~~~~~~~~~~ 85 | 86 | 返回当前的策略帐号的账户资金信息。 87 | 88 | .. code:: python 89 | 90 | df, msg = tapi.query_account() 91 | print "msg: ", msg 92 | print df 93 | 94 | 查询Portfolio 95 | ~~~~~~~~~~~~~ 96 | 97 | 返回当前的策略帐号的Universe中所有标的的净持仓,包括持仓为0的标的。 98 | 99 | .. code:: python 100 | 101 | df, msg = tapi.query_portfolio() 102 | print "msg: ", msg 103 | print df 104 | 105 | 查询当前策略帐号的所有持仓 106 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 107 | 108 | | 和 query\_portfolio接口不一样。如果莫个期货合约 Long, 109 | Short两个方向都有持仓,这里是返回两条记录 110 | | 返回的 size 不带方向,全部为 正 111 | 112 | .. code:: python 113 | 114 | df, msg = tapi.query_position() 115 | print "msg: ", msg 116 | print df 117 | 118 | 单标的下单 119 | ~~~~~~~~~~ 120 | 121 | | task\_id, msg = place\_order(code, action, price, size ) 122 | | action: Buy, Short, Cover, Sell, CoverToday, CoverYesterday, 123 | SellToday, SellYesterday 124 | | 返回 task\_id 125 | 126 | .. code:: python 127 | 128 | task_id, msg = tapi.place_order("000025.SZ", "Buy", 57, 100) 129 | print "msg:", msg 130 | print "task_id:", task_id 131 | 132 | 撤单 133 | ~~~~ 134 | 135 | cancel\_order(task\_id) 136 | 137 | .. code:: python 138 | 139 | tapi.cancel_order(task_id) 140 | 141 | 查询委托 142 | ~~~~~~~~ 143 | 144 | 返回委托信息 145 | 146 | .. code:: python 147 | 148 | df, msg = tapi.query_order(task_id = task_id, format = 'pandas') 149 | 150 | 查询成交 151 | ~~~~~~~~ 152 | 153 | 返回成交信息 154 | 155 | .. code:: python 156 | 157 | df, msg = tapi.query_trade(task_id = task_id, format = 'pandas') 158 | 159 | 目标持仓下单 160 | ~~~~~~~~~~~~ 161 | 162 | .. code:: python 163 | 164 | # goal_protfolio 165 | # 参数:目标持仓 166 | # 返回:(result, msg) 167 | # result: 成功或失败 168 | # msg: 错误原因 169 | # 注意:目标持仓中必须包括所有的代码的持仓,即使不修改 170 | 171 | # 先查询当前的持仓, 172 | portfolio, msg = tapi.goal_portfolio(goal, algo, algo_param) 173 | print "msg", msg 174 | print "portfolio", portfolio 175 | 176 | portfolio撤单 177 | ~~~~~~~~~~~~~ 178 | 179 | .. code:: python 180 | 181 | # stop_portfolio 182 | # 撤单, 撤销所有portfolio订单 183 | tapi.stop_portfolio() 184 | 185 | 批量下单(1) 186 | ~~~~~~~~~~~ 187 | 188 | place\_batch\_order,指定绝对size和交易类型 189 | 190 | .. code:: python 191 | 192 | # place_batch_order 193 | # 返回task_id, msg。 194 | orders = [ 195 | {"security":"600030.SH", "action" : "Buy", "price": 16, "size":1000}, 196 | {"security":"600519.SH", "action" : "Buy", "price": 320, "size":1000}, 197 | ] 198 | 199 | task_id, msg = tapi.place_batch_order(orders) 200 | print task_id 201 | print msg 202 | 203 | 批量下单(2) 204 | ~~~~~~~~~~~ 205 | 206 | basket\_order,指定变化量,不指定交易方向,由系统根据正负号来确定 207 | 208 | .. code:: python 209 | 210 | # 批量下单2:basket_order 211 | # 212 | # 返回task_id, msg。 213 | orders = [ 214 | {"security":"601857.SH", "ref_price": 8.40, "inc_size":1000}, 215 | {"security":"601997.SH", "ref_price": 14.540, "inc_size":20000}, 216 | ] 217 | 218 | task_id, msg = tapi.basket_order(orders) 219 | print task_id 220 | print msg 221 | -------------------------------------------------------------------------------- /doc/source/install.rst: -------------------------------------------------------------------------------- 1 | JAQS安装步骤 2 | ============ 3 | 4 | Windows下一键安装脚本 5 | --------------------- 6 | 7 | 如果没有安装Python包的经验,或者极度不擅长相关操作,可以使用我们的一键安装脚本。该脚本仅适用于Windows系统,使用步骤为: 8 | 9 | #. 先按照下方\ **如何安装Anaconda**\ 操作,在系统中安装Anaconda; 10 | #. 下载\ `一键安装脚本 `__\ ,解压后运行\ ``onekey_install_jaqs.bat``\ ,看到"Successfully 11 | installed jaqs"字样,说明安装成功。 12 | 13 | 1、安装Python环境 14 | ----------------- 15 | 16 | 运行JAQS需要Python环境,可通过在控制台(Windows系统为命令提示符,Linux系统为终端)运行\ ``python``\ 命令确定系统是否安装。 17 | 18 | 如果本地还没有安装Python环境,或已安装的Python不是\ `Anaconda `__\ ,强烈建议按下方步骤安装,Anaconda是集成开发环境,其中包含稳定版Python和众多常用包,且易于安装,避免不必要的麻烦;如果已经装好了Anaconda,可直接看下一步骤\ **安装依赖包**\ 。 19 | 20 | 如何安装Anaconda 21 | ~~~~~~~~~~~~~~~~ 22 | 23 | #. | **下载安装包**\ :打开\ `Anaconda官网 `__\ ,选择相应的操作系统,确定要安装的Python版本,目前JAQS同时支持Python2/3. 24 | | |anac| 25 | 26 | #. **安装**\ :下载完成后,运行下载好的文件,按提示完成安装(\ **注**\ :对于Windows系统,在默认情况下,Anaconda不会自动设置\ ``PATH``\ 环境,请确保选择“add 27 | Anaconda to system 28 | PATH”选项。)。具体安装教程参见\ `官方文档 `__\ 。 29 | 30 | #. **检查安装是否成功**\ :安装完成后, 31 | 32 | - **windows**\ 下,可以在系统菜单中看如下程序目录:\ |anacm|. 33 | 在cmd里执行\ ``ipython``\ 命令,可以调出IPython调试器:\ |anacipython|. 34 | - **Linux**\ 下,可以在Terminal运行\ ``Python``\ ,可看到'Anaconda'字样。 35 | 36 | 2、安装依赖包 37 | ------------- 38 | 39 | 除Anaconda中已包含的常用包外,JAQS还有些额外的依赖,除\ ``python-snappy``\ 这个包外,其他依赖可在使用\ ``pip install jaqs``\ 安装JAQS的过程中\ **自动安装**\ 。 40 | 41 | 请首先按照\ **如何安装\ ``python-snappy``\ 包**\ 所述操作,然后直接安装JAQS。 42 | 43 | 如果希望手动安装依赖包(不推荐),请按照\ **如何手动安装依赖包**\ 所述操作。 44 | 45 | 如何安装\ ``python-snappy``\ 包 46 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 47 | 48 | 这个包的安装需要比较多的编译依赖,直接使用\ ``pip install python-snappy``\ 安装可能会报错,请根据自己的操作系统,按下方步骤安装。 49 | 50 | Windows 51 | ^^^^^^^ 52 | 53 | 在Windows上准备编译环境较为复杂,建议从\ `这个网页 `__\ 下载编译好的包并直接安装。 54 | 55 | 下载时,需选择适合自己系统版本和Python版本的包,其中 56 | 57 | - cp27代表Python2.7,cp36代表Python3.6,以此类推 58 | - 64位系统选择带"amd64"字样的安装文件,32位系统选择不带的 59 | 60 | 安装时,使用\ ``pip install 文件名``\ 进行安装,如: 61 | 62 | .. code:: shell 63 | 64 | pip install python_snappy-0.5.1-cp27-cp27m-win_amd64.whl # 具体文件名可能不同, 取决于系统版本 65 | 66 | Linux 67 | ^^^^^ 68 | 69 | 首先,安装snappy的开发包\ ``libsnappy-dev``\ : 70 | 71 | .. code:: shell 72 | 73 | sudo apt-get install libsnappy-dev 74 | 75 | *注*\ :\ ``libsnappy-dev``\ 适用于Ubuntu系统,CentOS/RedHat/Fedora/SUSE等系统请使用\ ``libsnappy-devel``\ ,其他Linux系统请自行查找。 76 | 77 | 之后即可通过\ ``pip``\ 安装\ ``python-snappy``\ : 78 | 79 | .. code:: shell 80 | 81 | pip install python-snappy 82 | 83 | OS-X 84 | ^^^^ 85 | 86 | 参见\ `官方说明 `__ 87 | 88 | 装好\ ``python-snappy``\ 后,即可使用\ ``pip``\ 直接安装JAQS和其他依赖包,见下一节\ **安装JAQS**\ 。 89 | 90 | 如何手动安装依赖包 91 | ~~~~~~~~~~~~~~~~~~ 92 | 93 | - 可以在jaqs程序目录下,执行 ``pip install -r requirements.txt`` 94 | 一次完成所有依赖的安装。 95 | - 也可以通过单个安装完成,例如: ``pip install pyzmq`` 96 | 97 | 3、安装JAQS 98 | ----------- 99 | 100 | 可以使用\ ``pip``\ 安装或下载源代码安装。由于项目处于不断开发中,使用\ ``pip``\ 安装的一般为稳定版本;而使用源码安装,则可以自由选择版本(如最新开发版)。一般情况下,我们推荐使用\ ``pip``. 101 | 102 | 使用\ ``pip``\ 进行安装 103 | ~~~~~~~~~~~~~~~~~~~~~~~ 104 | 105 | .. code:: sheel 106 | 107 | pip install jaqs 108 | 109 | 通过源代码安装 110 | ~~~~~~~~~~~~~~ 111 | 112 | 1. 下载源代码 113 | ^^^^^^^^^^^^^ 114 | 115 | 打开\ `项目的GitHub首页 `__\ ,选择一个分支(稳定版为\ ``release-x.x.x``\ ,最新开发版为\ ``master``\ ),再点击“Clone 116 | or Download”-“Download ZIP”即可下载。或者,也可以通过Git命令克隆: 117 | 118 | .. code:: shell 119 | 120 | git clone https://github.com/quantOS-org/jaqs.git 121 | 122 | 2. 从源代码安装 123 | ^^^^^^^^^^^^^^^ 124 | 125 | 进入项目目录(如果是直接下载,项目目录名为\ ``JAQS-分支名``\ ,如果是git克隆,项目目录名为\ ``JAQS``\ ),打开命令提示符/终端,执行安装命令: 126 | 127 | .. code:: shell 128 | 129 | python setup.py install 130 | 131 | ***注***\ :如果已经通过\ ``pip``\ 安装了稳定版JAQS,希望通过源代码安装最新版本,则先通过\ ``pip uninstall jaqs``\ 卸载,在下载源代码安装即可。 132 | 133 | 4、确认安装成功 134 | ~~~~~~~~~~~~~~~ 135 | 136 | | 完成安装以后,在命令行中运行\ ``python``\ 并执行\ ``import jaqs``\ 确认安装是否成功: 137 | | |jaqstest| 138 | 139 | 5、升级JAQS 140 | ----------- 141 | 142 | 如果有新的release,可通过如下命令升级: 143 | 144 | .. code:: shell 145 | 146 | pip uninstall jaqs 147 | pip install jaqs 148 | 149 | *注*\ :不使用\ ``pip install --upgrade jaqs``\ 是为了保护使用者现有环境,使用该命令会同时升级\ ``pandas``\ 等依赖包,导致改变用户当前包环境。 150 | 151 | 6、策略样例 152 | ----------- 153 | 154 | 策略参考样例,请访问\ ` `__\ `https://github.com/quantOS-org/JAQS/tree/master/example `__ 155 | 156 | .. |anac| image:: https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/anac.png 157 | .. |anacm| image:: https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/anac_m.png 158 | .. |anacipython| image:: https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/anac_ipython.png 159 | .. |jaqstest| image:: https://raw.githubusercontent.com/quantOS-org/jaqs/master/doc/img/jaqs_test.png 160 | -------------------------------------------------------------------------------- /example/eventdriven/custom_symbol_signal.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from __future__ import print_function 4 | from __future__ import absolute_import 5 | 6 | from jaqs.trade import EventDrivenStrategy 7 | from jaqs.trade import model 8 | 9 | from jaqs.data import RemoteDataService, EventDataView 10 | from jaqs.trade import EventBacktestInstance, BacktestTradeApi, PortfolioManager 11 | import jaqs.trade.analyze as ana 12 | import jaqs.util as jutil 13 | 14 | from config_path import DATA_CONFIG_PATH, TRADE_CONFIG_PATH 15 | data_config = jutil.read_json(DATA_CONFIG_PATH) 16 | trade_config = jutil.read_json(TRADE_CONFIG_PATH) 17 | 18 | result_dir_path = '../../output/calendar_spread' 19 | dataview_dir_path = '../../output/calendar_spread/dataview' 20 | 21 | backtest_props = { 22 | "symbol" : "001.JZ", 23 | "start_date" : 20170601, 24 | "end_date" : 20171231, 25 | "bar_type" : "1d", 26 | "init_balance" : 1e3, 27 | "commission_rate": 0.0 28 | } 29 | backtest_props.update(data_config) 30 | backtest_props.update(trade_config) 31 | 32 | 33 | def prepare_dataview(): 34 | ds = RemoteDataService() 35 | ds.init_from_config(data_config) 36 | 37 | symbols = ['600036.SH', '000001.SZ'] 38 | dv_props = {'symbol': ','.join(symbols), 39 | 'start_date': backtest_props['start_date'], 40 | 'end_date': backtest_props['end_date'], 41 | 'benchmark': '000300.SH'} 42 | dv = EventDataView() 43 | 44 | dv.init_from_config(dv_props, ds) 45 | dv.prepare_data() 46 | 47 | import pandas as pd 48 | # target security 49 | diff_cols = ['open', 'high', 'low', 'close'] 50 | df0 = dv.get_symbol(symbols[0], fields=','.join(diff_cols)) 51 | df1 = dv.get_symbol(symbols[1], fields=','.join(diff_cols)) 52 | df_diff = df0 - df1 53 | 54 | # calculate signal 55 | df_signal = pd.concat([df0[['close']], df1[['close']], df_diff[['close']]], axis=1) 56 | df_signal.columns = symbols + ['diff'] 57 | roll = df_signal['diff'].rolling(window=10) 58 | df_signal.loc[:, 'signal'] = (df_signal['diff'] - roll.mean()) / roll.std() 59 | 60 | dv.append_df_symbol(df_diff, '001.JZ') 61 | dv.data_custom = df_signal 62 | dv.save_dataview(dataview_dir_path) 63 | 64 | 65 | class DoubleMaStrategy(EventDrivenStrategy): 66 | """""" 67 | def __init__(self): 68 | super(DoubleMaStrategy, self).__init__() 69 | 70 | # 标的 71 | self.symbol = '' 72 | 73 | # 快线和慢线周期 74 | self.fast_ma_len = 0 75 | self.slow_ma_len = 0 76 | 77 | # 记录当前已经过的天数 78 | self.window_count = 0 79 | self.window = 0 80 | 81 | # 快线和慢线均值 82 | self.fast_ma = 0 83 | self.slow_ma = 0 84 | 85 | # 固定长度的价格序列 86 | self.price_arr = None 87 | 88 | # 当前仓位 89 | self.pos = 0 90 | 91 | # 下单量乘数 92 | self.buy_size_unit = 1 93 | self.output = True 94 | 95 | self.df_signal = None 96 | 97 | def init_from_config(self, props): 98 | """ 99 | 将props中的用户设置读入 100 | """ 101 | super(DoubleMaStrategy, self).init_from_config(props) 102 | # 标的 103 | self.symbol = props.get('symbol') 104 | 105 | def on_bar(self, quote_dic): 106 | quote = quote_dic.get(self.symbol) 107 | 108 | df_signal = self.ctx.dataview.data_custom 109 | row = df_signal.loc[quote.trade_date] 110 | signal = row['signal'] 111 | 112 | THRESHOLD = 1.05 113 | if signal < -THRESHOLD: 114 | if self.pos == 0: 115 | self.buy(quote, 100) 116 | elif self.pos < 0: 117 | self.buy(quote, 200) 118 | elif signal > THRESHOLD: 119 | if self.pos == 0: 120 | self.sell(quote, 100) 121 | elif self.pos > 0: 122 | self.sell(quote, 200) 123 | 124 | def on_trade(self, ind): 125 | """ 126 | 交易完成后通过self.ctx.pm.get_pos得到最新仓位并更新self.pos 127 | """ 128 | print("\nStrategy on trade: ") 129 | print(ind) 130 | self.pos = self.ctx.pm.get_pos(self.symbol) 131 | 132 | 133 | def run_strategy(): 134 | dv = EventDataView() 135 | dv.load_dataview(dataview_dir_path) 136 | 137 | tapi = BacktestTradeApi() 138 | ins = EventBacktestInstance() 139 | 140 | strat = DoubleMaStrategy() 141 | pm = PortfolioManager() 142 | 143 | context = model.Context(dataview=dv, # data_api=ds, 144 | trade_api=tapi, instance=ins, 145 | strategy=strat, pm=pm) 146 | 147 | ins.init_from_config(backtest_props) 148 | ins.run() 149 | ins.save_results(folder_path=result_dir_path) 150 | 151 | 152 | def analyze(): 153 | ta = ana.EventAnalyzer() 154 | 155 | dv = EventDataView() 156 | dv.load_dataview(dataview_dir_path) 157 | ta.initialize(dataview=dv, 158 | file_folder=result_dir_path) 159 | 160 | ta.do_analyze(result_dir=result_dir_path, selected_sec=backtest_props['symbol'].split(',')) 161 | 162 | if __name__ == "__main__": 163 | #prepare_dataview() 164 | run_strategy() 165 | analyze() 166 | -------------------------------------------------------------------------------- /jaqs/data/continue_contract.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import datetime 4 | from jaqs.data import RemoteDataService 5 | import re 6 | 7 | # ------------------------------------------------------------------- 8 | # Settings 9 | # ------------------------------------------------------------------- 10 | dataview_dir_path = '../../output/continue_contract' 11 | 12 | ds = RemoteDataService() 13 | ds.init_from_config(data_config) 14 | 15 | 16 | def get_symbol_date_map(data_service, df_inst_, start_date, end_date, days_to_delist): 17 | """ 18 | Get a map {trade_date -> front month contract symbol} 19 | 20 | """ 21 | # get the trade date list between start_date and end_date 22 | dates = data_service.query_trade_dates(start_date, end_date) 23 | symbols_list = [] 24 | 25 | mask_within_range = (df_inst_['delist_date'] > dates[0]) & (df_inst_['list_date'] < dates[-1]) 26 | df_inst_ = df_inst_.loc[mask_within_range] 27 | 28 | j = 0 29 | for i, td in enumerate(dates): 30 | delist_date = df_inst_['delist_date'].iat[j] 31 | idx = np.nonzero(dates == delist_date) 32 | 33 | if (delist_date <= dates[-1]) and (idx[0][0] - i <= days_to_delist): 34 | j += 1 35 | delist_date = df_inst_['delist_date'].iat[j] 36 | symbol = df_inst_['symbol'].iat[j] 37 | symbols_list.append(symbol) 38 | 39 | res = pd.DataFrame(data={'trade_date': dates, 'symbol': symbols_list}) 40 | res.loc[:, 'symbol_next'] = res['symbol'].shift(-1).fillna(method='ffill') 41 | return res 42 | 43 | 44 | def get_continuous_contract(symbol, start_date, end_date, change_date, fields): 45 | # start_date = ds.get_last_trade_date(start_date) 46 | 47 | # get information of all instruments and filter only relevant contracts 48 | df_inst = ds.query_inst_info(symbol="", fields=','.join(['symbol', 'inst_type', 49 | 'market', 'status', 50 | 'multiplier', 'list_date', 51 | 'delist_date'])) 52 | df_inst = df_inst.reset_index() 53 | def is_target_symbol(s): 54 | return len(re.findall(symbol + r'\d+', s)) > 0 55 | mask_stock_index_future = df_inst['symbol'].apply(is_target_symbol) 56 | df_inst = df_inst.loc[mask_stock_index_future].sort_values('delist_date') 57 | # df_inst.index = range(len(df_inst)) 58 | 59 | # get the front month contract for each trading date 60 | # prevent the case that no contracts exist near start_date 61 | first_list_date = df_inst['list_date'].min() 62 | if start_date < first_list_date: 63 | start_date = first_list_date 64 | 65 | df_inst_map = get_symbol_date_map(ds, df_inst, start_date, end_date, change_date) 66 | # df_inst_map['pre_trade_date'] = df_inst_map['trade_date'].shift(1) 67 | # df_inst_map = df_inst_map.dropna() 68 | # df_inst_map['pre_trade_date'] = df_inst_map['pre_trade_date'].astype(np.integer) 69 | 70 | # get the daily info 71 | df_future_daily = query_daily_data(ds, df_inst_map, fields) 72 | 73 | df_future_daily = df_future_daily.drop(['freq'], axis=1) 74 | ''' 75 | df_future_daily['move'] = np.where(df_future_daily['symbol'] != df_future_daily['symbol'].shift(-1).fillna(method='ffill'), 1, 0) 76 | # df_future_daily['ret_shift'] = 0.0 77 | 78 | #df_future_daily.index = range(len(df_future_daily)) 79 | 80 | for i in range(len(df_future_daily) - 1): 81 | if df_future_daily.ix[i, 'move'] == 1.0: 82 | df_future_daily.ix[i, 'ret_shift'] = np.log(df_future_daily.ix[i+1, 'close']) - np.log(df_future_daily.ix[i, 'close']) 83 | 84 | df_future_daily.index = df_future_daily['trade_date'] 85 | df_future_daily['trade_date'] = df_future_daily['trade_date'].apply(lambda x: datetime.datetime.strptime(str(x), '%Y%m%d').date()) 86 | ''' 87 | return df_future_daily 88 | 89 | 90 | def query_daily_data(data_service, df_inst_map_, fields_): 91 | l = [] 92 | 93 | for symbol in df_inst_map_['symbol'].unique(): 94 | df_symbol = df_inst_map_.loc[df_inst_map_['symbol'] == symbol] 95 | start_date, end_date = df_symbol['trade_date'].iat[0], df_symbol['trade_date'].iat[-1] 96 | symbol_next = df_symbol['symbol_next'].iat[-1] 97 | 98 | df, _ = data_service.daily(symbol, start_date=start_date, end_date=end_date, fields=fields_) 99 | df_next, _ = data_service.daily(symbol_next, start_date=end_date, end_date=end_date, fields=fields_) 100 | 101 | close_diff = df_next['close'].iat[-1] - df['close'].iat[-1] 102 | for col in ['open', 'high', 'low', 'close']: 103 | df.loc[:, col] = df[col] + close_diff 104 | 105 | df.loc[:, 'symbol_next'] = df['symbol'] 106 | df.loc[:, 'symbol_next'].iat[-1] = symbol_next 107 | df.loc[:, 'close_diff'] = 0.0 108 | df.loc[:, 'close_diff'].iat[-1] = close_diff 109 | # df_future['pre_close'] = df_future['close'].shift(1) 110 | # df_future['log_ret'] = np.log(df_future['close']).diff() 111 | # df_future = df_future.dropna() 112 | l.append(df) 113 | 114 | res = pd.concat(l, axis=0) 115 | return res 116 | 117 | 118 | if __name__ == '__main__': 119 | future_df = get_continuous_contract('rb', 20170101, 20171225, 3, 'open,high,low,close') -------------------------------------------------------------------------------- /example/eventdriven/CalendarSpread.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from __future__ import print_function 4 | from __future__ import absolute_import 5 | import numpy as np 6 | import statsmodels.api as sm 7 | 8 | from jaqs.trade import EventDrivenStrategy 9 | from jaqs.trade import common, model 10 | 11 | from jaqs.data import RemoteDataService 12 | from jaqs.trade import EventBacktestInstance 13 | from jaqs.trade import BacktestTradeApi 14 | from jaqs.trade import PortfolioManager 15 | import jaqs.trade.analyze as ana 16 | import jaqs.util as jutil 17 | 18 | from config_path import DATA_CONFIG_PATH, TRADE_CONFIG_PATH 19 | data_config = jutil.read_json(DATA_CONFIG_PATH) 20 | trade_config = jutil.read_json(TRADE_CONFIG_PATH) 21 | 22 | result_dir_path = '../../output/calendar_spread' 23 | 24 | 25 | class CalendarSpread(EventDrivenStrategy): 26 | def __init__(self): 27 | super(CalendarSpread, self).__init__() 28 | 29 | self.symbol = '' 30 | self.s1 = '' 31 | self.s2 = '' 32 | self.quote1 = None 33 | self.quote2 = None 34 | 35 | self.bufferSize = 0 36 | self.bufferCount = 0 37 | self.spreadList = '' 38 | 39 | def init_from_config(self, props): 40 | super(CalendarSpread, self).init_from_config(props) 41 | 42 | self.symbol = props.get('symbol') 43 | self.init_balance = props.get('init_balance') 44 | self.bufferSize = props.get('bufferSize') 45 | self.s1, self.s2 = self.symbol.split(',') 46 | self.spreadList = np.zeros(self.bufferSize) 47 | 48 | self.output = True 49 | 50 | def on_cycle(self): 51 | pass 52 | 53 | def on_tick(self, quote): 54 | pass 55 | 56 | def buy(self, quote, price, size): 57 | self.ctx.trade_api.place_order(quote.symbol, 'Buy', price, size) 58 | 59 | def sell(self, quote, price, size): 60 | self.ctx.trade_api.place_order(quote.symbol, 'Sell', price, size) 61 | 62 | def long_spread(self, quote1, quote2): 63 | self.buy(quote1, quote1.close, 1) 64 | self.sell(quote2, quote2.close, 1) 65 | 66 | def short_spread(self, quote1, quote2): 67 | self.buy(quote2, quote2.close, 1) 68 | self.sell(quote1, quote1.close, 1) 69 | 70 | def on_bar(self, quote): 71 | 72 | q1 = quote.get(self.s1) 73 | q2 = quote.get(self.s2) 74 | self.quote1 = q1 75 | self.quote2 = q2 76 | 77 | spread = q1.close - q2.close 78 | 79 | self.spreadList[0:self.bufferSize - 1] = self.spreadList[1:self.bufferSize] 80 | self.spreadList[-1] = spread 81 | self.bufferCount += 1 82 | 83 | if self.bufferCount <= self.bufferSize: 84 | return 85 | 86 | X, y = np.array(range(self.bufferSize)), np.array(self.spreadList) 87 | X = X.reshape(-1, 1) 88 | y = y.reshape(-1, 1) 89 | X = sm.add_constant(X) 90 | 91 | est = sm.OLS(y, X) 92 | est = est.fit() 93 | 94 | if est.pvalues[1] < 0.05: 95 | if est.params[1] < 0: 96 | self.short_spread(q1, q2) 97 | else: 98 | self.long_spread(q1, q2) 99 | 100 | def on_trade(self, ind): 101 | if self.output: 102 | print("\nStrategy on trade: ") 103 | print(ind) 104 | print(self.ctx.pm.get_trade_stat(ind.symbol)) 105 | self.pos = self.ctx.pm.get_pos(ind.symbol) 106 | 107 | def on_order_status(self, ind): 108 | if self.output: 109 | print("\nStrategy on order status: ") 110 | print(ind) 111 | 112 | def on_task_rsp(self, rsp): 113 | if self.output: 114 | print("\nStrategy on task rsp: ") 115 | print(rsp) 116 | 117 | def on_order_rsp(self, rsp): 118 | if self.output: 119 | print("\nStrategy on order rsp: ") 120 | print(rsp) 121 | 122 | def on_task_status(self, ind): 123 | if self.output: 124 | print("\nStrategy on task ind: ") 125 | print(ind) 126 | 127 | 128 | props = { 129 | "symbol" : "ru1801.SHF,ru1805.SHF", 130 | "start_date" : 20170701, 131 | "end_date" : 20171030, 132 | "bar_type" : "1d", 133 | "init_balance" : 2e4, 134 | "bufferSize" : 20, 135 | "commission_rate": 2E-4 136 | } 137 | props.update(data_config) 138 | props.update(trade_config) 139 | 140 | 141 | def run_strategy(): 142 | tapi = BacktestTradeApi() 143 | ins = EventBacktestInstance() 144 | 145 | ds = RemoteDataService() 146 | strat = CalendarSpread() 147 | pm = PortfolioManager() 148 | 149 | context = model.Context(data_api=ds, trade_api=tapi, instance=ins, 150 | strategy=strat, pm=pm) 151 | 152 | ins.init_from_config(props) 153 | ins.run() 154 | ins.save_results(folder_path=result_dir_path) 155 | 156 | 157 | def analyze(): 158 | ta = ana.EventAnalyzer() 159 | 160 | ds = RemoteDataService() 161 | ds.init_from_config(data_config) 162 | 163 | ta.initialize(data_server_=ds, file_folder=result_dir_path) 164 | 165 | ta.do_analyze(result_dir=result_dir_path, selected_sec=props['symbol'].split(',')) 166 | 167 | 168 | if __name__ == "__main__": 169 | run_strategy() 170 | analyze() 171 | -------------------------------------------------------------------------------- /example/eventdriven/market_making.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import numpy as np 3 | 4 | from jaqs.trade import EventDrivenStrategy 5 | from jaqs.trade import common 6 | 7 | 8 | class RealStrategy(EventDrivenStrategy): 9 | """ 10 | 11 | """ 12 | def __init__(self): 13 | super(RealStrategy, self).__init__() 14 | self.symbol = '' 15 | self.symbol_list = [] 16 | self.s1 = "" 17 | 18 | self.pos = 0 19 | 20 | self.bid_orders = [] 21 | self.ask_orders = [] 22 | 23 | self.counter = 0 24 | 25 | def init_from_config(self, props): 26 | super(RealStrategy, self).init_from_config(props) 27 | self.symbol = props.get('symbol') 28 | self.init_balance = props.get('init_balance') 29 | 30 | # self.s1, self.s2 = self.symbol.split(',') 31 | self.symbol_list = self.symbol.split(',') 32 | self.s1 = self.symbol_list[0] 33 | 34 | self.tick_size1 = 1.0 35 | self.open_size = 1 36 | 37 | self.output = True 38 | 39 | # self.liquidate() 40 | self._update_pos() 41 | 42 | def on_cycle(self): 43 | pass 44 | 45 | def on_tick(self, quote): 46 | if quote.symbol != self.s1: 47 | return 48 | 49 | self.counter += 1 50 | 51 | # if self.counter % 5 == 0: 52 | if self.counter == 1: 53 | print(quote) 54 | # task_id, msg = self.ctx.trade_api.place_order(quote.symbol, 'Buy', quote.askprice1, 1) 55 | # if task_id is None or task_id == 0: 56 | # print(msg) 57 | # else: 58 | # print(" place_order: task_id = {}".format(task_id)) 59 | task_id, msg = self.ask(quote) 60 | if task_id is None or task_id == 0: 61 | print(msg) 62 | else: 63 | print(" place_order: task_id = {}".format(task_id)) 64 | 65 | return 66 | 67 | def bid(self, quote): 68 | if hasattr(quote, 'bidprice1'): 69 | price = quote.bidprice1 70 | else: 71 | price = quote.vwap - self.tick_size1 72 | action = common.ORDER_ACTION.BUY 73 | self.ctx.trade_api.place_order(quote.symbol, action, price, self.open_size) 74 | 75 | def ask(self, quote): 76 | if hasattr(quote, 'askprice1'): 77 | price = quote.askprice1 78 | else: 79 | price = quote.vwap + self.tick_size1 80 | action = common.ORDER_ACTION.SHORT 81 | return self.ctx.trade_api.place_order(quote.symbol, action, price, self.open_size) 82 | 83 | def cancel_all_orders(self): 84 | for task_id, task in self.ctx.pm.tasks.items(): 85 | if task.trade_date == self.ctx.trade_date and not task.is_finished: 86 | self.ctx.trade_api.cancel_order(task_id) 87 | 88 | def liquidate(self, quote, n): 89 | self._update_pos() 90 | # self.cancel_all_orders() 91 | 92 | if self.pos == 0: 93 | return 94 | 95 | ref_price = quote.close 96 | if self.pos < 0: 97 | action = common.ORDER_ACTION.BUY 98 | price = ref_price + n * self.tick_size1 99 | else: 100 | action = common.ORDER_ACTION.SELL 101 | price = ref_price - n * self.tick_size1 102 | self.ctx.trade_api.place_order(quote.symbol, action, price, abs(self.pos)) 103 | 104 | def on_bar(self, quote): 105 | quote = quote[self.s1] 106 | if quote.time > 145600 and quote.time < 200000: 107 | self.liquidate(quote, 5) 108 | return 109 | 110 | ts = self.ctx.pm.get_trade_stat(self.s1) 111 | if (abs(self.pos) > 5 112 | or ((ts is not None) and (ts.buy_want_size > 5 or ts.sell_want_size > 5))): 113 | # self.cancel_all_orders() 114 | self.liquidate(quote, 1) 115 | else: 116 | self.bid(quote) 117 | self.ask(quote) 118 | 119 | if self.output: 120 | pass 121 | # self.show() 122 | 123 | def show(self): 124 | ts = self.ctx.pm.get_trade_stat(self.s1) 125 | p = self.ctx.pm.get_position(self.s1) 126 | print(ts) 127 | print(p) 128 | 129 | def _update_pos(self): 130 | p = self.ctx.pm.get_position(self.s1) 131 | if p is None: 132 | self.pos = 0 133 | else: 134 | self.pos = p.current_size 135 | 136 | def on_trade(self, ind): 137 | print("\nStrategy on trade: ") 138 | self._update_pos() 139 | # print(ind) 140 | 141 | # print(p) 142 | ts = self.ctx.pm.get_trade_stat(self.s1) 143 | print(ts) 144 | 145 | def on_order_status(self, ind): 146 | if self.output: 147 | print("\nStrategy on order status: ") 148 | print(ind) 149 | 150 | def on_task_rsp(self, rsp): 151 | if self.output: 152 | print("\nStrategy on task rsp: ") 153 | print(rsp) 154 | 155 | def on_order_rsp(self, rsp): 156 | if self.output: 157 | print("\nStrategy on order rsp: ") 158 | print(rsp) 159 | 160 | def on_task_status(self, ind): 161 | if self.output: 162 | print("\nStrategy on task ind: ") 163 | print(ind) 164 | -------------------------------------------------------------------------------- /jaqs/trade/analyze/static/bootstrap-toc/bootstrap-toc.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v<%= version %> (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | (function($) { 6 | 'use strict'; 7 | 8 | window.Toc = { 9 | helpers: { 10 | // return all matching elements in the set, or their descendants 11 | findOrFilter: function($el, selector) { 12 | // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/ 13 | // http://stackoverflow.com/a/12731439/358804 14 | var $descendants = $el.find(selector); 15 | return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])'); 16 | }, 17 | 18 | generateUniqueIdBase: function(el) { 19 | var text = $(el).text(); 20 | var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-'); 21 | return anchor || el.tagName.toLowerCase(); 22 | }, 23 | 24 | generateUniqueId: function(el) { 25 | var anchorBase = this.generateUniqueIdBase(el); 26 | for (var i = 0; ; i++) { 27 | var anchor = anchorBase; 28 | if (i > 0) { 29 | // add suffix 30 | anchor += '-' + i; 31 | } 32 | // check if ID already exists 33 | if (!document.getElementById(anchor)) { 34 | return anchor; 35 | } 36 | } 37 | }, 38 | 39 | generateAnchor: function(el) { 40 | if (el.id) { 41 | return el.id; 42 | } else { 43 | var anchor = this.generateUniqueId(el); 44 | el.id = anchor; 45 | return anchor; 46 | } 47 | }, 48 | 49 | createNavList: function() { 50 | return $(''); 51 | }, 52 | 53 | createChildNavList: function($parent) { 54 | var $childList = this.createNavList(); 55 | $parent.append($childList); 56 | return $childList; 57 | }, 58 | 59 | generateNavEl: function(anchor, text) { 60 | var $a = $(''); 61 | $a.attr('href', '#' + anchor); 62 | $a.text(text); 63 | var $li = $('
  • '); 64 | $li.append($a); 65 | return $li; 66 | }, 67 | 68 | generateNavItem: function(headingEl) { 69 | var anchor = this.generateAnchor(headingEl); 70 | var $heading = $(headingEl); 71 | var text = $heading.data('toc-text') || $heading.text(); 72 | return this.generateNavEl(anchor, text); 73 | }, 74 | 75 | // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). 76 | getTopLevel: function($scope) { 77 | for (var i = 1; i <= 6; i++) { 78 | var $headings = this.findOrFilter($scope, 'h' + i); 79 | if ($headings.length > 1) { 80 | return i; 81 | } 82 | } 83 | 84 | return 1; 85 | }, 86 | 87 | // returns the elements for the top level, and the next below it 88 | getHeadings: function($scope, topLevel) { 89 | var topSelector = 'h' + topLevel; 90 | 91 | var secondaryLevel = topLevel + 1; 92 | var secondarySelector = 'h' + secondaryLevel; 93 | 94 | return this.findOrFilter($scope, topSelector + ',' + secondarySelector); 95 | }, 96 | 97 | getNavLevel: function(el) { 98 | return parseInt(el.tagName.charAt(1), 10); 99 | }, 100 | 101 | populateNav: function($topContext, topLevel, $headings) { 102 | var $context = $topContext; 103 | var $prevNav; 104 | 105 | var helpers = this; 106 | $headings.each(function(i, el) { 107 | var $newNav = helpers.generateNavItem(el); 108 | var navLevel = helpers.getNavLevel(el); 109 | 110 | // determine the proper $context 111 | if (navLevel === topLevel) { 112 | // use top level 113 | $context = $topContext; 114 | } else if ($prevNav && $context === $topContext) { 115 | // create a new level of the tree and switch to it 116 | $context = helpers.createChildNavList($prevNav); 117 | } // else use the current $context 118 | 119 | $context.append($newNav); 120 | 121 | $prevNav = $newNav; 122 | }); 123 | }, 124 | 125 | parseOps: function(arg) { 126 | var opts; 127 | if (arg.jquery) { 128 | opts = { 129 | $nav: arg 130 | }; 131 | } else { 132 | opts = arg; 133 | } 134 | opts.$scope = opts.$scope || $(document.body); 135 | return opts; 136 | } 137 | }, 138 | 139 | // accepts a jQuery object, or an options object 140 | init: function(opts) { 141 | opts = this.helpers.parseOps(opts); 142 | 143 | // ensure that the data attribute is in place for styling 144 | opts.$nav.attr('data-toggle', 'toc'); 145 | 146 | var $topContext = this.helpers.createChildNavList(opts.$nav); 147 | var topLevel = this.helpers.getTopLevel(opts.$scope); 148 | var $headings = this.helpers.getHeadings(opts.$scope, topLevel); 149 | this.helpers.populateNav($topContext, topLevel, $headings); 150 | } 151 | }; 152 | 153 | $(function() { 154 | $('nav[data-toggle="toc"]').each(function(i, el) { 155 | var $nav = $(el); 156 | Toc.init($nav); 157 | }); 158 | }); 159 | })(jQuery); 160 | -------------------------------------------------------------------------------- /example/alpha/first_example.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | """ 4 | A very first example of AlphaStrategy back-test: 5 | Market value weight among UNIVERSE. 6 | Benchmark is HS300. 7 | 8 | """ 9 | from __future__ import print_function, unicode_literals, division, absolute_import 10 | 11 | from jaqs.data import RemoteDataService, DataView 12 | 13 | import jaqs.util as jutil 14 | 15 | from jaqs.trade import model 16 | from jaqs.trade import (AlphaStrategy, AlphaBacktestInstance, AlphaTradeApi, 17 | PortfolioManager, AlphaLiveTradeInstance, RealTimeTradeApi) 18 | import jaqs.trade.analyze as ana 19 | 20 | data_config = { 21 | "remote.data.address": "tcp://data.quantos.org:8910", 22 | "remote.data.username": "YourTelephoneNumber", 23 | "remote.data.password": "YourToken" 24 | } 25 | trade_config = { 26 | "remote.trade.address": "tcp://gw.quantos.org:8901", 27 | "remote.trade.username": "YourTelephoneNumber", 28 | "remote.trade.password": "YourToken" 29 | } 30 | 31 | # Data files are stored in this folder: 32 | dataview_store_folder = '../../output/simplest/dataview' 33 | 34 | # Back-test and analysis results are stored here 35 | backtest_result_folder = '../../output/simplest' 36 | 37 | UNIVERSE = '000807.SH' 38 | 39 | 40 | def save_data(): 41 | """ 42 | This function fetches data from remote server and stores them locally. 43 | Then we can use local data to do back-test. 44 | 45 | """ 46 | dataview_props = {'start_date': 20170101, # Start and end date of back-test 47 | 'end_date': 20171030, 48 | 'universe': UNIVERSE, # Investment universe and performance benchmark 49 | 'benchmark': '000300.SH', 50 | 'fields': 'total_mv,turnover', # Data fields that we need 51 | 'freq': 1 # freq = 1 means we use daily data. Please do not change this. 52 | } 53 | 54 | # RemoteDataService communicates with a remote server to fetch data 55 | ds = RemoteDataService() 56 | 57 | # Use username and password in data_config to login 58 | ds.init_from_config(data_config) 59 | 60 | # DataView utilizes RemoteDataService to get various data and store them 61 | dv = DataView() 62 | dv.init_from_config(dataview_props, ds) 63 | dv.prepare_data() 64 | dv.save_dataview(folder_path=dataview_store_folder) 65 | 66 | 67 | def do_backtest(): 68 | # Load local data file that we just stored. 69 | dv = DataView() 70 | dv.load_dataview(folder_path=dataview_store_folder) 71 | 72 | backtest_props = {"start_date" : dv.start_date, # start and end date of back-test 73 | "end_date" : dv.end_date, 74 | "period" : "month", # re-balance period length 75 | "benchmark" : dv.benchmark, # benchmark and universe 76 | "universe" : dv.universe, 77 | "init_balance" : 1e8, # Amount of money at the start of back-test 78 | "position_ratio" : 1.0, # Amount of money at the start of back-test 79 | } 80 | backtest_props.update(data_config) 81 | backtest_props.update(trade_config) 82 | 83 | # Create model context using AlphaTradeApi, AlphaStrategy, PortfolioManager and AlphaBacktestInstance. 84 | # We can store anything, e.g., public variables in context. 85 | 86 | trade_api = AlphaTradeApi() 87 | strategy = AlphaStrategy(pc_method='market_value_weight') 88 | pm = PortfolioManager() 89 | bt = AlphaBacktestInstance() 90 | context = model.Context(dataview=dv, instance=bt, strategy=strategy, trade_api=trade_api, pm=pm) 91 | 92 | bt.init_from_config(backtest_props) 93 | bt.run_alpha() 94 | 95 | # After finishing back-test, we save trade results into a folder 96 | bt.save_results(folder_path=backtest_result_folder) 97 | 98 | 99 | def do_livetrade(): 100 | dv = DataView() 101 | dv.load_dataview(folder_path=dataview_store_folder) 102 | 103 | props = {"period": "day", 104 | "strategy_no": 1044, 105 | "init_balance": 1e6} 106 | props.update(data_config) 107 | props.update(trade_config) 108 | 109 | strategy = AlphaStrategy(pc_method='market_value_weight') 110 | pm = PortfolioManager() 111 | 112 | bt = AlphaLiveTradeInstance() 113 | trade_api = RealTimeTradeApi(props) 114 | ds = RemoteDataService() 115 | 116 | context = model.Context(dataview=dv, instance=bt, strategy=strategy, trade_api=trade_api, pm=pm, data_api=ds) 117 | 118 | bt.init_from_config(props) 119 | bt.run_alpha() 120 | 121 | goal_positions = strategy.goal_positions 122 | print("Length of goal positions:", len(goal_positions)) 123 | task_id, msg = trade_api.goal_portfolio(goal_positions) 124 | print(task_id, msg) 125 | 126 | 127 | def analyze_backtest_results(): 128 | # Analyzer help us calculate various trade statistics according to trade results. 129 | # All the calculation results will be stored as its members. 130 | ta = ana.AlphaAnalyzer() 131 | dv = DataView() 132 | dv.load_dataview(folder_path=dataview_store_folder) 133 | 134 | ta.initialize(dataview=dv, file_folder=backtest_result_folder) 135 | 136 | ta.do_analyze(result_dir=backtest_result_folder, 137 | selected_sec=list(ta.universe)[:3]) 138 | 139 | 140 | if __name__ == "__main__": 141 | is_backtest = True 142 | 143 | if is_backtest: 144 | save_data() 145 | do_backtest() 146 | analyze_backtest_results() 147 | else: 148 | save_data() 149 | do_livetrade() 150 | -------------------------------------------------------------------------------- /test/test_trade_api.py: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | from __future__ import print_function 4 | import time 5 | 6 | from jaqs.trade.tradeapi import TradeApi 7 | import pandas as pd 8 | import jaqs.util as jutil 9 | 10 | from config_path import TRADE_CONFIG_PATH 11 | trade_config = jutil.read_json(TRADE_CONFIG_PATH) 12 | 13 | 14 | def test_trade_api(): 15 | address = trade_config.get("remote.trade.address", None) 16 | username = trade_config.get("remote.trade.username", None) 17 | password = trade_config.get("remote.trade.password", None) 18 | print(address) 19 | print(username) 20 | # print password 21 | 22 | if address is None or username is None or password is None: 23 | raise ValueError("no trade service config available!") 24 | 25 | tapi = TradeApi(address) 26 | 27 | # TradeApi通过回调函数方式通知用户事件。事件包括三种:订单状态、成交回报、委托任务执行状态。 28 | 29 | # 订单状态推送 30 | def on_orderstatus(order): 31 | print("on_orderstatus:") #, order 32 | for key in order: print("%20s : %s" % (key, str(order[key]))) 33 | print("") 34 | 35 | 36 | # 成交回报推送 37 | def on_trade(trade): 38 | print("on_trade:") 39 | for key in trade: print("%20s : %s" % (key, str(trade[key]))) 40 | print("") 41 | 42 | # 委托任务执行状态推送 43 | # 通常可以忽略该回调函数 44 | def on_taskstatus(task): 45 | print("on_taskstatus:") 46 | for key in task: print("%20s : %s" % (key, str(task[key]))) 47 | print("") 48 | 49 | tapi.set_ordstatus_callback(on_orderstatus) 50 | tapi.set_trade_callback(on_trade) 51 | tapi.set_task_callback(on_taskstatus) 52 | 53 | # 使用用户名、密码登陆, 如果成功,返回用户可用的策略帐号列表 54 | user_info, msg = tapi.login(username, password) 55 | print("msg: ", msg) 56 | assert check_err_msg(msg) 57 | print("user_info:", user_info) 58 | user_strats = user_info['strategies'] 59 | 60 | # 选择使用的策略帐号 61 | # 62 | # 该函数成功后,下单、查持仓等和策略帐号有关的操作都和该策略帐号绑定。 63 | # 没有必要每次下单、查询都调用该函数。重复调用该函数可以选择新的策略帐号。 64 | # 65 | # 如果成功,返回(strategy_id, msg) 66 | # 否则返回 (0, err_msg) 67 | if user_strats: 68 | sid, msg = tapi.use_strategy(user_strats[0]) 69 | assert check_err_msg(msg) 70 | print("sid: ", sid) 71 | time.sleep(1) 72 | 73 | # 查询Portfolio 74 | # 75 | # 返回当前的策略帐号的Universe中所有标的的净持仓,包括持仓为0的标的。 76 | 77 | df, msg = tapi.query_account() 78 | print(msg) 79 | assert check_err_msg(msg) 80 | print(df) 81 | 82 | # 查询当前策略帐号的所有持仓 83 | # 84 | # 和 query_portfolio接口不一样。如果莫个期货合约 Long, Short两个方向都有持仓,这里是返回两条记录 85 | # 返回的 size 不带方向,全部为 正 86 | df, msg = tapi.query_position() 87 | assert check_err_msg(msg) 88 | print(df) 89 | 90 | # Query Universe 91 | df_univ, msg = tapi.query_universe() 92 | 93 | # 查询Portfolio 94 | # 95 | # 返回当前的策略帐号的Universe中所有标的的净持仓,包括持仓为0的标的。 96 | 97 | df_portfolio, msg = tapi.query_portfolio() 98 | assert check_err_msg(msg) 99 | assert len(df_univ) == len(df_portfolio) 100 | 101 | # 下单接口 102 | # (task_id, msg) = place_order(code, action, price, size ) 103 | # action: Buy, Short, Cover, Sell, CoverToday, CoverYesterday, SellToday, SellYesterday 104 | # 返回 task_id 可以用改 task_id 105 | task_id, msg = tapi.place_order("000718.SZ", "Buy", 4.59, 100) 106 | if msg.endswith('market has closed'): 107 | pass 108 | task_id = -1 109 | else: 110 | assert check_err_msg(msg) 111 | print("task_id:", task_id) 112 | 113 | df_order, msg = tapi.query_order(task_id=task_id) 114 | assert check_err_msg(msg) 115 | print(df_order) 116 | 117 | df_trade, msg = tapi.query_trade(task_id=task_id) 118 | assert check_err_msg(msg) 119 | print(df_trade) 120 | 121 | # 批量下单1:place_batch_order 122 | # 123 | # 返回task_id, msg。 124 | orders = [ 125 | {"security":"600030.SH", "action" : "Buy", "price": 16, "size":1000}, 126 | {"security":"600519.SH", "action" : "Buy", "price": 320, "size":1000}, 127 | ] 128 | 129 | task_id, msg = tapi.place_batch_order(orders, "", dict()) 130 | print(task_id) 131 | print(msg) 132 | 133 | # cancel_order 134 | # 撤单 135 | tapi.cancel_order(task_id) 136 | 137 | # 批量下单2:basket_order 138 | # 139 | # 返回task_id, msg。 140 | orders = [ 141 | {"security":"601857.SH", "ref_price": 8.40, "inc_size":1000}, 142 | {"security":"601997.SH", "ref_price": 14.540, "inc_size":20000}, 143 | ] 144 | 145 | task_id, msg = tapi.basket_order(orders, "", {}) 146 | print(task_id) 147 | print(msg) 148 | 149 | # goal_protfolio 150 | # 参数:目标持仓 151 | # 返回:(result, msg) 152 | # result: 成功或失败 153 | # msg: 错误原因 154 | # 注意:目标持仓中必须包括所有的代码的持仓,即使不修改 155 | 156 | # 先查询当前的持仓, 157 | portfolio, msg = tapi.query_portfolio() 158 | print("msg", msg) 159 | print("portfolio", portfolio) 160 | 161 | goal = pd.DataFrame(portfolio['current_size']) 162 | goal.loc[:, 'size'] = goal['current_size'] 163 | goal.loc[:, 'ref_price'] = 0.0 164 | goal.loc[:, 'urgency'] = 5 165 | 166 | # 然后修改目标持仓 167 | code = '601857.SH' 168 | goal.loc[code, 'ref_price'] = 8.38 169 | goal.loc[code, 'size'] += 20000 170 | 171 | code = '601997.SH' 172 | goal.loc[code, 'ref_price'] = 14.40 173 | goal.loc[code, 'size'] += 10000 174 | 175 | # stop_portfolio 176 | # 撤单, 撤销所有portfolio订单 177 | tapi.stop_portfolio() 178 | 179 | # 发送请求 180 | result, msg = tapi.goal_portfolio(goal) 181 | print(result, msg) 182 | 183 | 184 | def check_err_msg(err_msg): 185 | l = err_msg.split(',') 186 | return l and (l[0] == '0') 187 | 188 | if __name__ == "__main__": 189 | test_trade_api() 190 | --------------------------------------------------------------------------------