├── 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 | 
38 |
39 |
40 |
41 | 
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 | 
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 | 
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**下,可以在系统菜单中看如下程序目录:. 在cmd里执行`ipython`命令,可以调出IPython调试器:.
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 | 
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 |
--------------------------------------------------------------------------------