Documentation first build
@@ -1,49 +0,0 @@
|
||||
Eddie Hebert
|
||||
fawce
|
||||
Thomas Wiecki
|
||||
Stephen Diehl
|
||||
scottsanderson
|
||||
Scott Sanderson
|
||||
Richard Frank
|
||||
Jonathan Kamens
|
||||
twiecki
|
||||
Joe Jevnik
|
||||
Delaney Granizo-Mackenzie
|
||||
Tobias Brandt
|
||||
Ben McCann
|
||||
John Ricklefs
|
||||
Jenkins T. Quantopian, III
|
||||
Jeremiah Lowin
|
||||
jbredeche
|
||||
Brian Fink
|
||||
David Edwards
|
||||
Matti Hanninen
|
||||
Ryan Day
|
||||
llllllllll
|
||||
David Stephens
|
||||
Tim
|
||||
Dale Jung
|
||||
Jamie Kirkpatrick
|
||||
Jean Bredeche
|
||||
Wes McKinney
|
||||
jikamens
|
||||
Aidan
|
||||
Colin Alexander
|
||||
Elektra58
|
||||
Jason Kölker
|
||||
Jeremi Joslin
|
||||
Luke Schiefelbein
|
||||
Martin Dengler
|
||||
Mete Atamel
|
||||
Michael Schatzow
|
||||
Moises Trovo
|
||||
Nicholas Pezolano
|
||||
Pankaj Garg
|
||||
Paolo Bernardi
|
||||
Peter Cawthron
|
||||
Philipp Kosel
|
||||
Suminda Dharmasena
|
||||
The Gitter Badger
|
||||
Tony Lambiris
|
||||
Tony Worm
|
||||
stanh
|
||||
@@ -1,93 +0,0 @@
|
||||
#
|
||||
# Dockerfile for an image with the currently checked out version of catalyst installed. To build:
|
||||
#
|
||||
# docker build -t quantopian/catalyst .
|
||||
#
|
||||
# To run the container:
|
||||
#
|
||||
# docker run -v /path/to/your/notebooks:/projects -v ~/.catalyst:/root/.catalyst -p 8888:8888/tcp --name catalyst -it quantopian/catalyst
|
||||
#
|
||||
# To access Jupyter when running docker locally (you may need to add NAT rules):
|
||||
#
|
||||
# https://127.0.0.1
|
||||
#
|
||||
# default password is jupyter. to provide another, see:
|
||||
# http://jupyter-notebook.readthedocs.org/en/latest/public_server.html#preparing-a-hashed-password
|
||||
#
|
||||
# once generated, you can pass the new value via `docker run --env` the first time
|
||||
# you start the container.
|
||||
#
|
||||
# You can also run an algo using the docker exec command. For example:
|
||||
#
|
||||
# docker exec -it catalyst catalyst run -f /projects/my_algo.py --start 2015-1-1 --end 2016-1-1 /projects/result.pickle
|
||||
#
|
||||
FROM python:3.5
|
||||
|
||||
#
|
||||
# set up environment
|
||||
#
|
||||
ENV TINI_VERSION v0.10.0
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
|
||||
RUN chmod +x /tini
|
||||
ENTRYPOINT ["/tini", "--"]
|
||||
|
||||
ENV PROJECT_DIR=/projects \
|
||||
NOTEBOOK_PORT=8888 \
|
||||
SSL_CERT_PEM=/root/.jupyter/jupyter.pem \
|
||||
SSL_CERT_KEY=/root/.jupyter/jupyter.key \
|
||||
PW_HASH="u'sha1:31cb67870a35:1a2321318481f00b0efdf3d1f71af523d3ffc505'" \
|
||||
CONFIG_PATH=/root/.jupyter/jupyter_notebook_config.py
|
||||
|
||||
#
|
||||
# install TA-Lib and other prerequisites
|
||||
#
|
||||
|
||||
RUN mkdir ${PROJECT_DIR} \
|
||||
&& apt-get -y update \
|
||||
&& apt-get -y install libfreetype6-dev libpng-dev libopenblas-dev liblapack-dev gfortran \
|
||||
&& curl -L https://downloads.sourceforge.net/project/ta-lib/ta-lib/0.4.0/ta-lib-0.4.0-src.tar.gz | tar xvz
|
||||
|
||||
#
|
||||
# build and install catalyst from source. install TA-Lib after to ensure
|
||||
# numpy is available.
|
||||
#
|
||||
|
||||
WORKDIR /ta-lib
|
||||
|
||||
RUN pip install 'numpy>=1.11.1,<2.0.0' \
|
||||
&& pip install 'scipy>=0.17.1,<1.0.0' \
|
||||
&& pip install 'pandas>=0.18.1,<1.0.0' \
|
||||
&& ./configure --prefix=/usr \
|
||||
&& make \
|
||||
&& make install \
|
||||
&& pip install TA-Lib \
|
||||
&& pip install matplotlib \
|
||||
&& pip install jupyter
|
||||
|
||||
#
|
||||
# This is then only file we need from source to remain in the
|
||||
# image after build and install.
|
||||
#
|
||||
|
||||
ADD ./etc/docker_cmd.sh /
|
||||
|
||||
#
|
||||
# make port available. /catalyst is made a volume
|
||||
# for developer testing.
|
||||
#
|
||||
EXPOSE ${NOTEBOOK_PORT}
|
||||
|
||||
#
|
||||
# build and install the catalyst package into the image
|
||||
#
|
||||
|
||||
ADD . /catalyst
|
||||
WORKDIR /catalyst
|
||||
RUN pip install -e .
|
||||
|
||||
#
|
||||
# start the jupyter server
|
||||
#
|
||||
|
||||
WORKDIR ${PROJECT_DIR}
|
||||
CMD /docker_cmd.sh
|
||||
@@ -1,34 +0,0 @@
|
||||
#
|
||||
# Dockerfile for an image with the currently checked out version of catalyst installed. To build:
|
||||
#
|
||||
# docker build -t quantopian/catalystdev -f Dockerfile-dev .
|
||||
#
|
||||
# Note: the dev build requires a quantopian/catalyst image, which you can build as follows:
|
||||
#
|
||||
# docker build -t quantopian/catalyst -f Dockerfile
|
||||
#
|
||||
# To run the container:
|
||||
#
|
||||
# docker run -v /path/to/your/notebooks:/projects -v ~/.catalyst:/root/.catalyst -p 8888:8888/tcp --name catalystdev -it quantopian/catalystdev
|
||||
#
|
||||
# To access Jupyter when running docker locally (you may need to add NAT rules):
|
||||
#
|
||||
# https://127.0.0.1
|
||||
#
|
||||
# default password is jupyter. to provide another, see:
|
||||
# http://jupyter-notebook.readthedocs.org/en/latest/public_server.html#preparing-a-hashed-password
|
||||
#
|
||||
# once generated, you can pass the new value via `docker run --env` the first time
|
||||
# you start the container.
|
||||
#
|
||||
# You can also run an algo using the docker exec command. For example:
|
||||
#
|
||||
# docker exec -it catalystdev catalyst run -f /projects/my_algo.py --start 2015-1-1 --end 2016-1-1 /projects/result.pickle
|
||||
#
|
||||
FROM quantopian/catalyst
|
||||
|
||||
WORKDIR /catalyst
|
||||
|
||||
RUN pip install -r etc/requirements_dev.txt -r etc/requirements_blaze.txt
|
||||
# Clean out any cython assets. The pip install re-builds them.
|
||||
RUN find . -type f -name '*.c' -exec rm {} + && pip install -e .[all]
|
||||
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,9 +0,0 @@
|
||||
include LICENSE
|
||||
|
||||
include etc/requirements*.txt
|
||||
recursive-include catalyst *.pyi
|
||||
recursive-include catalyst *.pxi
|
||||
|
||||
recursive-include catalyst/resources *.*
|
||||
include versioneer.py
|
||||
include catalyst/_version.py
|
||||
@@ -1 +0,0 @@
|
||||
All the documentation for `Catalyst <https://github.com/enigmampc/catalyst>`_ can be found in the `catalyst-docs wiki <https://github.com/enigmampc/catalyst-docs/wiki>`_.
|
||||
@@ -1,10 +0,0 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "ubuntu/trusty64"
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.customize ["modifyvm", :id, "--memory", 2048, "--cpus", 2]
|
||||
end
|
||||
config.vm.provision "shell", path: "vagrant_init.sh"
|
||||
end
|
||||
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
@@ -4,12 +4,12 @@ Install
|
||||
Installing with ``pip``
|
||||
-----------------------
|
||||
|
||||
Installing Zipline via ``pip`` is slightly more involved than the average
|
||||
Installing Catalyst via ``pip`` is slightly more involved than the average
|
||||
Python package.
|
||||
|
||||
There are two reasons for the additional complexity:
|
||||
|
||||
1. Zipline ships several C extensions that require access to the CPython C API.
|
||||
1. Catalyst ships several C extensions that require access to the CPython C API.
|
||||
In order to build the C extensions, ``pip`` needs access to the CPython
|
||||
header files for your Python installation.
|
||||
|
||||
@@ -28,13 +28,19 @@ your particular platform), you should be able to simply run
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install zipline
|
||||
$ pip install enigma-catalyst
|
||||
|
||||
If you use Python for anything other than Zipline, we **strongly** recommend
|
||||
If you use Python for anything other than Catalyst, we **strongly** recommend
|
||||
that you install in a `virtualenv
|
||||
<https://virtualenv.readthedocs.org/en/latest>`_. The `Hitchhiker's Guide to
|
||||
Python`_ provides an `excellent tutorial on virtualenv
|
||||
<http://docs.python-guide.org/en/latest/dev/virtualenvs/>`_.
|
||||
<http://docs.python-guide.org/en/latest/dev/virtualenvs/>`_. Here's a summarized
|
||||
version:
|
||||
|
||||
.. code-block:: bash
|
||||
$ virtualenv catalyst-venv
|
||||
$ source ./catalyst-venv/bin/activate
|
||||
$ pip install enigma-catalyst
|
||||
|
||||
GNU/Linux
|
||||
~~~~~~~~~
|
||||
|
After Width: | Height: | Size: 673 B |
@@ -0,0 +1,599 @@
|
||||
/*
|
||||
* basic.css
|
||||
* ~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- basic theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* -- main layout ----------------------------------------------------------- */
|
||||
|
||||
div.clearer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* -- relbar ---------------------------------------------------------------- */
|
||||
|
||||
div.related {
|
||||
width: 100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.related h3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
margin: 0;
|
||||
padding: 0 0 0 10px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.related li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.related li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* -- sidebar --------------------------------------------------------------- */
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 10px 5px 0 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
float: left;
|
||||
width: 230px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul,
|
||||
div.sphinxsidebar ul.want-points {
|
||||
margin-left: 20px;
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #98dbcc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox input[type="text"] {
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox input[type="submit"] {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* -- search page ----------------------------------------------------------- */
|
||||
|
||||
ul.search {
|
||||
margin: 10px 0 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.search li {
|
||||
padding: 5px 0 5px 20px;
|
||||
background-image: url(file.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 7px;
|
||||
}
|
||||
|
||||
ul.search li a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul.search li div.context {
|
||||
color: #888;
|
||||
margin: 2px 0 0 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* -- index page ------------------------------------------------------------ */
|
||||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a.biglink {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
span.linkdescr {
|
||||
font-style: italic;
|
||||
padding-top: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* -- general index --------------------------------------------------------- */
|
||||
|
||||
table.indextable {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.indextable td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable dl, table.indextable dd {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
table.indextable tr.cap {
|
||||
margin-top: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
img.toggler {
|
||||
margin-right: 3px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.modindex-jumpbox {
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
div.genindex-jumpbox {
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
/* -- general body styles --------------------------------------------------- */
|
||||
|
||||
a.headerlink {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
h1:hover > a.headerlink,
|
||||
h2:hover > a.headerlink,
|
||||
h3:hover > a.headerlink,
|
||||
h4:hover > a.headerlink,
|
||||
h5:hover > a.headerlink,
|
||||
h6:hover > a.headerlink,
|
||||
dt:hover > a.headerlink,
|
||||
caption:hover > a.headerlink,
|
||||
p.caption:hover > a.headerlink,
|
||||
div.code-block-caption:hover > a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
div.body p.caption {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
div.body td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
p.rubric {
|
||||
margin-top: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left {
|
||||
clear: left;
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right {
|
||||
clear: right;
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* -- sidebars -------------------------------------------------------------- */
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em;
|
||||
border: 1px solid #ddb;
|
||||
padding: 7px 7px 0 7px;
|
||||
background-color: #ffe;
|
||||
width: 40%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
p.sidebar-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* -- topics ---------------------------------------------------------------- */
|
||||
|
||||
div.topic {
|
||||
border: 1px solid #ccc;
|
||||
padding: 7px 7px 0 7px;
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
p.topic-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* -- admonitions ----------------------------------------------------------- */
|
||||
|
||||
div.admonition {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
div.admonition dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.admonition dl {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
margin: 0px 10px 5px 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.body p.centered {
|
||||
text-align: center;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
/* -- tables ---------------------------------------------------------------- */
|
||||
|
||||
table.docutils {
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table caption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
table caption span.caption-text {
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
padding: 1px 8px 1px 5px;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
table.footnote td, table.footnote th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
table.citation td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* -- figures --------------------------------------------------------------- */
|
||||
|
||||
div.figure {
|
||||
margin: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.figure p.caption {
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
div.figure p.caption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.figure p.caption span.caption-text {
|
||||
}
|
||||
|
||||
|
||||
/* -- other body styles ----------------------------------------------------- */
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha;
|
||||
}
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha;
|
||||
}
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman;
|
||||
}
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
dd p {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
dd ul, dd table {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
dt:target, .highlighted {
|
||||
background-color: #fbe54e;
|
||||
}
|
||||
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.sig-paren {
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.versionmodified {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.system-message {
|
||||
background-color: #fda;
|
||||
padding: 5px;
|
||||
border: 3px solid red;
|
||||
}
|
||||
|
||||
.footnote:target {
|
||||
background-color: #ffa;
|
||||
}
|
||||
|
||||
.line-block {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.line-block .line-block {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
.guilabel, .menuselection {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.accelerator {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.classifier {
|
||||
font-style: oblique;
|
||||
}
|
||||
|
||||
abbr, acronym {
|
||||
border-bottom: dotted 1px;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* -- code displays --------------------------------------------------------- */
|
||||
|
||||
pre {
|
||||
overflow: auto;
|
||||
overflow-y: hidden; /* fixes display issues on Chrome browsers */
|
||||
}
|
||||
|
||||
td.linenos pre {
|
||||
padding: 5px 0px;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
table.highlighttable {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
table.highlighttable td {
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
div.code-block-caption {
|
||||
padding: 2px 5px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
div.code-block-caption code {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
div.code-block-caption + div > div.highlight > pre {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
div.code-block-caption span.caption-number {
|
||||
padding: 0.1em 0.3em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.code-block-caption span.caption-text {
|
||||
}
|
||||
|
||||
div.literal-block-wrapper {
|
||||
padding: 1em 1em 0;
|
||||
}
|
||||
|
||||
div.literal-block-wrapper div.highlight {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
code.descname {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
code.descclassname {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
code.xref, a code {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.viewcode-link {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.viewcode-back {
|
||||
float: right;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
margin: -1px -10px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/* -- math display ---------------------------------------------------------- */
|
||||
|
||||
img.math {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.body div.math p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span.eqno {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* -- printout stylesheet --------------------------------------------------- */
|
||||
|
||||
@media print {
|
||||
div.document,
|
||||
div.documentwrapper,
|
||||
div.bodywrapper {
|
||||
margin: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar,
|
||||
div.related,
|
||||
div.footer,
|
||||
#top-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,2 @@
|
||||
.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}
|
||||
/*# sourceMappingURL=badge_only.css.map */
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* doctools.js
|
||||
* ~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilities for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* select a different prefix for underscore
|
||||
*/
|
||||
$u = _.noConflict();
|
||||
|
||||
/**
|
||||
* make the code below compatible with browsers without
|
||||
* an installed firebug like debugger
|
||||
if (!window.console || !console.firebug) {
|
||||
var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
|
||||
"dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
|
||||
"profile", "profileEnd"];
|
||||
window.console = {};
|
||||
for (var i = 0; i < names.length; ++i)
|
||||
window.console[names[i]] = function() {};
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* small helper function to urldecode strings
|
||||
*/
|
||||
jQuery.urldecode = function(x) {
|
||||
return decodeURIComponent(x).replace(/\+/g, ' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* small helper function to urlencode strings
|
||||
*/
|
||||
jQuery.urlencode = encodeURIComponent;
|
||||
|
||||
/**
|
||||
* This function returns the parsed url parameters of the
|
||||
* current request. Multiple values per key are supported,
|
||||
* it will always return arrays of strings for the value parts.
|
||||
*/
|
||||
jQuery.getQueryParameters = function(s) {
|
||||
if (typeof s == 'undefined')
|
||||
s = document.location.search;
|
||||
var parts = s.substr(s.indexOf('?') + 1).split('&');
|
||||
var result = {};
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var tmp = parts[i].split('=', 2);
|
||||
var key = jQuery.urldecode(tmp[0]);
|
||||
var value = jQuery.urldecode(tmp[1]);
|
||||
if (key in result)
|
||||
result[key].push(value);
|
||||
else
|
||||
result[key] = [value];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* highlight a given string on a jquery object by wrapping it in
|
||||
* span elements with the given class name.
|
||||
*/
|
||||
jQuery.fn.highlightText = function(text, className) {
|
||||
function highlight(node) {
|
||||
if (node.nodeType == 3) {
|
||||
var val = node.nodeValue;
|
||||
var pos = val.toLowerCase().indexOf(text);
|
||||
if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) {
|
||||
var span = document.createElement("span");
|
||||
span.className = className;
|
||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
||||
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
|
||||
document.createTextNode(val.substr(pos + text.length)),
|
||||
node.nextSibling));
|
||||
node.nodeValue = val.substr(0, pos);
|
||||
}
|
||||
}
|
||||
else if (!jQuery(node).is("button, select, textarea")) {
|
||||
jQuery.each(node.childNodes, function() {
|
||||
highlight(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
return this.each(function() {
|
||||
highlight(this);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* backward compatibility for jQuery.browser
|
||||
* This will be supported until firefox bug is fixed.
|
||||
*/
|
||||
if (!jQuery.browser) {
|
||||
jQuery.uaMatch = function(ua) {
|
||||
ua = ua.toLowerCase();
|
||||
|
||||
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(msie) ([\w.]+)/.exec(ua) ||
|
||||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
|
||||
[];
|
||||
|
||||
return {
|
||||
browser: match[ 1 ] || "",
|
||||
version: match[ 2 ] || "0"
|
||||
};
|
||||
};
|
||||
jQuery.browser = {};
|
||||
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Small JavaScript module for the documentation.
|
||||
*/
|
||||
var Documentation = {
|
||||
|
||||
init : function() {
|
||||
this.fixFirefoxAnchorBug();
|
||||
this.highlightSearchWords();
|
||||
this.initIndexTable();
|
||||
},
|
||||
|
||||
/**
|
||||
* i18n support
|
||||
*/
|
||||
TRANSLATIONS : {},
|
||||
PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; },
|
||||
LOCALE : 'unknown',
|
||||
|
||||
// gettext and ngettext don't access this so that the functions
|
||||
// can safely bound to a different name (_ = Documentation.gettext)
|
||||
gettext : function(string) {
|
||||
var translated = Documentation.TRANSLATIONS[string];
|
||||
if (typeof translated == 'undefined')
|
||||
return string;
|
||||
return (typeof translated == 'string') ? translated : translated[0];
|
||||
},
|
||||
|
||||
ngettext : function(singular, plural, n) {
|
||||
var translated = Documentation.TRANSLATIONS[singular];
|
||||
if (typeof translated == 'undefined')
|
||||
return (n == 1) ? singular : plural;
|
||||
return translated[Documentation.PLURALEXPR(n)];
|
||||
},
|
||||
|
||||
addTranslations : function(catalog) {
|
||||
for (var key in catalog.messages)
|
||||
this.TRANSLATIONS[key] = catalog.messages[key];
|
||||
this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
|
||||
this.LOCALE = catalog.locale;
|
||||
},
|
||||
|
||||
/**
|
||||
* add context elements like header anchor links
|
||||
*/
|
||||
addContextElements : function() {
|
||||
$('div[id] > :header:first').each(function() {
|
||||
$('<a class="headerlink">\u00B6</a>').
|
||||
attr('href', '#' + this.id).
|
||||
attr('title', _('Permalink to this headline')).
|
||||
appendTo(this);
|
||||
});
|
||||
$('dt[id]').each(function() {
|
||||
$('<a class="headerlink">\u00B6</a>').
|
||||
attr('href', '#' + this.id).
|
||||
attr('title', _('Permalink to this definition')).
|
||||
appendTo(this);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* workaround a firefox stupidity
|
||||
* see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
|
||||
*/
|
||||
fixFirefoxAnchorBug : function() {
|
||||
if (document.location.hash)
|
||||
window.setTimeout(function() {
|
||||
document.location.href += '';
|
||||
}, 10);
|
||||
},
|
||||
|
||||
/**
|
||||
* highlight the search words provided in the url in the text
|
||||
*/
|
||||
highlightSearchWords : function() {
|
||||
var params = $.getQueryParameters();
|
||||
var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
|
||||
if (terms.length) {
|
||||
var body = $('div.body');
|
||||
if (!body.length) {
|
||||
body = $('body');
|
||||
}
|
||||
window.setTimeout(function() {
|
||||
$.each(terms, function() {
|
||||
body.highlightText(this.toLowerCase(), 'highlighted');
|
||||
});
|
||||
}, 10);
|
||||
$('<p class="highlight-link"><a href="javascript:Documentation.' +
|
||||
'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>')
|
||||
.appendTo($('#searchbox'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* init the domain index toggle buttons
|
||||
*/
|
||||
initIndexTable : function() {
|
||||
var togglers = $('img.toggler').click(function() {
|
||||
var src = $(this).attr('src');
|
||||
var idnum = $(this).attr('id').substr(7);
|
||||
$('tr.cg-' + idnum).toggle();
|
||||
if (src.substr(-9) == 'minus.png')
|
||||
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
|
||||
else
|
||||
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
|
||||
}).css('display', '');
|
||||
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
|
||||
togglers.click();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* helper function to hide the search marks again
|
||||
*/
|
||||
hideSearchWords : function() {
|
||||
$('#searchbox .highlight-link').fadeOut(300);
|
||||
$('span.highlighted').removeClass('highlighted');
|
||||
},
|
||||
|
||||
/**
|
||||
* make the url absolute
|
||||
*/
|
||||
makeURL : function(relativeURL) {
|
||||
return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
|
||||
},
|
||||
|
||||
/**
|
||||
* get the current relative url
|
||||
*/
|
||||
getCurrentURL : function() {
|
||||
var path = document.location.pathname;
|
||||
var parts = path.split(/\//);
|
||||
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
|
||||
if (this == '..')
|
||||
parts.pop();
|
||||
});
|
||||
var url = parts.join('/');
|
||||
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
|
||||
}
|
||||
};
|
||||
|
||||
// quick alias for translations
|
||||
_ = Documentation.gettext;
|
||||
|
||||
$(document).ready(function() {
|
||||
Documentation.init();
|
||||
});
|
||||
|
After Width: | Height: | Size: 347 B |
|
After Width: | Height: | Size: 347 B |
|
After Width: | Height: | Size: 358 B |
@@ -0,0 +1,414 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata></metadata>
|
||||
<defs>
|
||||
<font id="fontawesomeregular" horiz-adv-x="1536" >
|
||||
<font-face units-per-em="1792" ascent="1536" descent="-256" />
|
||||
<missing-glyph horiz-adv-x="448" />
|
||||
<glyph unicode=" " horiz-adv-x="448" />
|
||||
<glyph unicode="	" horiz-adv-x="448" />
|
||||
<glyph unicode=" " horiz-adv-x="448" />
|
||||
<glyph unicode="¨" horiz-adv-x="1792" />
|
||||
<glyph unicode="©" horiz-adv-x="1792" />
|
||||
<glyph unicode="®" horiz-adv-x="1792" />
|
||||
<glyph unicode="´" horiz-adv-x="1792" />
|
||||
<glyph unicode="Æ" horiz-adv-x="1792" />
|
||||
<glyph unicode=" " horiz-adv-x="768" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode=" " horiz-adv-x="768" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode=" " horiz-adv-x="512" />
|
||||
<glyph unicode=" " horiz-adv-x="384" />
|
||||
<glyph unicode=" " horiz-adv-x="256" />
|
||||
<glyph unicode=" " horiz-adv-x="256" />
|
||||
<glyph unicode=" " horiz-adv-x="192" />
|
||||
<glyph unicode=" " horiz-adv-x="307" />
|
||||
<glyph unicode=" " horiz-adv-x="85" />
|
||||
<glyph unicode=" " horiz-adv-x="307" />
|
||||
<glyph unicode=" " horiz-adv-x="384" />
|
||||
<glyph unicode="™" horiz-adv-x="1792" />
|
||||
<glyph unicode="∞" horiz-adv-x="1792" />
|
||||
<glyph unicode="≠" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="500" d="M0 0z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1699 1350q0 -35 -43 -78l-632 -632v-768h320q26 0 45 -19t19 -45t-19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45t45 19h320v768l-632 632q-43 43 -43 78q0 23 18 36.5t38 17.5t43 4h1408q23 0 43 -4t38 -17.5t18 -36.5z" />
|
||||
<glyph unicode="" d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89 t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49 35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -52 -38 -90t-90 -38q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5 t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 32v768q-32 -36 -69 -66q-268 -206 -426 -338q-51 -43 -83 -67t-86.5 -48.5t-102.5 -24.5h-1h-1q-48 0 -102.5 24.5t-86.5 48.5t-83 67q-158 132 -426 338q-37 30 -69 66v-768q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1664 1083v11v13.5t-0.5 13 t-3 12.5t-5.5 9t-9 7.5t-14 2.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5q0 -168 147 -284q193 -152 401 -317q6 -5 35 -29.5t46 -37.5t44.5 -31.5t50.5 -27.5t43 -9h1h1q20 0 43 9t50.5 27.5t44.5 31.5t46 37.5t35 29.5q208 165 401 317q54 43 100.5 115.5t46.5 131.5z M1792 1120v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M896 -128q-26 0 -44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124t127 -344q0 -221 -229 -450l-623 -600 q-18 -18 -44 -18z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -21 -10.5 -35.5t-30.5 -14.5q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455 l502 -73q56 -9 56 -46z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -50 -41 -50q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500 l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455l502 -73q56 -9 56 -46z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 131q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q9 0 42 -21.5t74.5 -48t108 -48t133.5 -21.5t133.5 21.5t108 48t74.5 48t42 21.5q61 0 111.5 -20t85.5 -53.5t62 -81 t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M384 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 320v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 704v128q0 26 -19 45t-45 19h-128 q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 -64v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM384 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45 t45 -19h128q26 0 45 19t19 45zM1792 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 704v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1792 320v128 q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 704v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19 t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1920 1248v-1344q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1344q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M768 512v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM768 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 512v-384q0 -52 -38 -90t-90 -38 h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 288v-192q0 -40 -28 -68t-68 -28h-320 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-960 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h960q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1671 970q0 -40 -28 -68l-724 -724l-136 -136q-28 -28 -68 -28t-68 28l-136 136l-362 362q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -295l656 657q28 28 68 28t68 -28l136 -136q28 -28 28 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1298 214q0 -40 -28 -68l-136 -136q-28 -28 -68 -28t-68 28l-294 294l-294 -294q-28 -28 -68 -28t-68 28l-136 136q-28 28 -28 68t28 68l294 294l-294 294q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -294l294 294q28 28 68 28t68 -28l136 -136q28 -28 28 -68 t-28 -68l-294 -294l294 -294q28 -28 28 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-224q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v224h-224q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h224v224q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5v-224h224 q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5 t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-576q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h576q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5z M1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z " />
|
||||
<glyph unicode="" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61t-298 61t-245 164t-164 245t-61 298q0 182 80.5 343t226.5 270q43 32 95.5 25t83.5 -50q32 -42 24.5 -94.5t-49.5 -84.5q-98 -74 -151.5 -181t-53.5 -228q0 -104 40.5 -198.5t109.5 -163.5t163.5 -109.5 t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5q0 121 -53.5 228t-151.5 181q-42 32 -49.5 84.5t24.5 94.5q31 43 84 50t95 -25q146 -109 226.5 -270t80.5 -343zM896 1408v-640q0 -52 -38 -90t-90 -38t-90 38t-38 90v640q0 52 38 90t90 38t90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M256 96v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 224v-320q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 480v-576q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1408 864v-960q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1376v-1472q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1472q0 14 9 23t23 9h192q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1536 749v-222q0 -12 -8 -23t-20 -13l-185 -28q-19 -54 -39 -91q35 -50 107 -138q10 -12 10 -25t-9 -23q-27 -37 -99 -108t-94 -71q-12 0 -26 9l-138 108q-44 -23 -91 -38 q-16 -136 -29 -186q-7 -28 -36 -28h-222q-14 0 -24.5 8.5t-11.5 21.5l-28 184q-49 16 -90 37l-141 -107q-10 -9 -25 -9q-14 0 -25 11q-126 114 -165 168q-7 10 -7 23q0 12 8 23q15 21 51 66.5t54 70.5q-27 50 -41 99l-183 27q-13 2 -21 12.5t-8 23.5v222q0 12 8 23t19 13 l186 28q14 46 39 92q-40 57 -107 138q-10 12 -10 24q0 10 9 23q26 36 98.5 107.5t94.5 71.5q13 0 26 -10l138 -107q44 23 91 38q16 136 29 186q7 28 36 28h222q14 0 24.5 -8.5t11.5 -21.5l28 -184q49 -16 90 -37l142 107q9 9 24 9q13 0 25 -10q129 -119 165 -170q7 -8 7 -22 q0 -12 -8 -23q-15 -21 -51 -66.5t-54 -70.5q26 -50 41 -98l183 -28q13 -2 21 -12.5t8 -23.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M512 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM768 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1024 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1152 76v948h-896v-948q0 -22 7 -40.5t14.5 -27t10.5 -8.5h832q3 0 10.5 8.5t14.5 27t7 40.5zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832 q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1408 544v-480q0 -26 -19 -45t-45 -19h-384v384h-256v-384h-384q-26 0 -45 19t-19 45v480q0 1 0.5 3t0.5 3l575 474l575 -474q1 -2 1 -6zM1631 613l-62 -74q-8 -9 -21 -11h-3q-13 0 -21 7l-692 577l-692 -577q-12 -8 -24 -7q-13 2 -21 11l-62 74q-8 10 -7 23.5t11 21.5 l719 599q32 26 76 26t76 -26l244 -204v195q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-408l219 -182q10 -8 11 -21.5t-7 -23.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M128 0h1024v768h-416q-40 0 -68 28t-28 68v416h-512v-1280zM768 896h376q-10 29 -22 41l-313 313q-12 12 -41 22v-376zM1280 864v-896q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h640q40 0 88 -20t76 -48l312 -312q28 -28 48 -76t20 -88z " />
|
||||
<glyph unicode="" d="M896 992v-448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1111 540v4l-24 320q-1 13 -11 22.5t-23 9.5h-186q-13 0 -23 -9.5t-11 -22.5l-24 -320v-4q-1 -12 8 -20t21 -8h244q12 0 21 8t8 20zM1870 73q0 -73 -46 -73h-704q13 0 22 9.5t8 22.5l-20 256q-1 13 -11 22.5t-23 9.5h-272q-13 0 -23 -9.5t-11 -22.5l-20 -256 q-1 -13 8 -22.5t22 -9.5h-704q-46 0 -46 73q0 54 26 116l417 1044q8 19 26 33t38 14h339q-13 0 -23 -9.5t-11 -22.5l-15 -192q-1 -14 8 -23t22 -9h166q13 0 22 9t8 23l-15 192q-1 13 -11 22.5t-23 9.5h339q20 0 38 -14t26 -33l417 -1044q26 -62 26 -116z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1280 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 416v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h465l135 -136 q58 -56 136 -56t136 56l136 136h464q40 0 68 -28t28 -68zM1339 985q17 -41 -14 -70l-448 -448q-18 -19 -45 -19t-45 19l-448 448q-31 29 -14 70q17 39 59 39h256v448q0 26 19 45t45 19h256q26 0 45 -19t19 -45v-448h256q42 0 59 -39z" />
|
||||
<glyph unicode="" d="M1120 608q0 -12 -10 -24l-319 -319q-11 -9 -23 -9t-23 9l-320 320q-15 16 -7 35q8 20 30 20h192v352q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-352h192q14 0 23 -9t9 -23zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273 t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1118 660q-8 -20 -30 -20h-192v-352q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v352h-192q-14 0 -23 9t-9 23q0 12 10 24l319 319q11 9 23 9t23 -9l320 -320q15 -16 7 -35zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198 t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1023 576h316q-1 3 -2.5 8t-2.5 8l-212 496h-708l-212 -496q-1 -2 -2.5 -8t-2.5 -8h316l95 -192h320zM1536 546v-482q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v482q0 62 25 123l238 552q10 25 36.5 42t52.5 17h832q26 0 52.5 -17t36.5 -42l238 -552 q25 -61 25 -123z" />
|
||||
<glyph unicode="" d="M1184 640q0 -37 -32 -55l-544 -320q-15 -9 -32 -9q-16 0 -32 8q-32 19 -32 56v640q0 37 32 56q33 18 64 -1l544 -320q32 -18 32 -55zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l138 138q-148 137 -349 137q-104 0 -198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5q119 0 225 52t179 147q7 10 23 12q14 0 25 -9 l137 -138q9 -8 9.5 -20.5t-7.5 -22.5q-109 -132 -264 -204.5t-327 -72.5q-156 0 -298 61t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q147 0 284.5 -55.5t244.5 -156.5l130 129q29 31 70 14q39 -17 39 -59z" />
|
||||
<glyph unicode="" d="M1511 480q0 -5 -1 -7q-64 -268 -268 -434.5t-478 -166.5q-146 0 -282.5 55t-243.5 157l-129 -129q-19 -19 -45 -19t-45 19t-19 45v448q0 26 19 45t45 19h448q26 0 45 -19t19 -45t-19 -45l-137 -137q71 -66 161 -102t187 -36q134 0 250 65t186 179q11 17 53 117 q8 23 30 23h192q13 0 22.5 -9.5t9.5 -22.5zM1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-26 0 -45 19t-19 45t19 45l138 138q-148 137 -349 137q-134 0 -250 -65t-186 -179q-11 -17 -53 -117q-8 -23 -30 -23h-199q-13 0 -22.5 9.5t-9.5 22.5v7q65 268 270 434.5t480 166.5 q146 0 284 -55.5t245 -156.5l130 129q19 19 45 19t45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M384 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M384 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1536 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5z M1536 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5zM1536 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5 t9.5 -22.5zM1664 160v832q0 13 -9.5 22.5t-22.5 9.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 1248v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47 t47 -113z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M320 768h512v192q0 106 -75 181t-181 75t-181 -75t-75 -181v-192zM1152 672v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v192q0 184 132 316t316 132t316 -132t132 -316v-192h32q40 0 68 -28t28 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48 t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 650q0 -166 -60 -314l-20 -49l-185 -33q-22 -83 -90.5 -136.5t-156.5 -53.5v-32q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-32q71 0 130 -35.5t93 -95.5l68 12q29 95 29 193q0 148 -88 279t-236.5 209t-315.5 78 t-315.5 -78t-236.5 -209t-88 -279q0 -98 29 -193l68 -12q34 60 93 95.5t130 35.5v32q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v32q-88 0 -156.5 53.5t-90.5 136.5l-185 33l-20 49q-60 148 -60 314q0 151 67 291t179 242.5 t266 163.5t320 61t320 -61t266 -163.5t179 -242.5t67 -291z" />
|
||||
<glyph unicode="" horiz-adv-x="768" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142zM1408 640q0 -153 -85 -282.5t-225 -188.5q-13 -5 -25 -5q-27 0 -46 19t-19 45q0 39 39 59q56 29 76 44q74 54 115.5 135.5t41.5 173.5t-41.5 173.5 t-115.5 135.5q-20 15 -76 44q-39 20 -39 59q0 26 19 45t45 19q13 0 26 -5q140 -59 225 -188.5t85 -282.5zM1664 640q0 -230 -127 -422.5t-338 -283.5q-13 -5 -26 -5q-26 0 -45 19t-19 45q0 36 39 59q7 4 22.5 10.5t22.5 10.5q46 25 82 51q123 91 192 227t69 289t-69 289 t-192 227q-36 26 -82 51q-7 4 -22.5 10.5t-22.5 10.5q-39 23 -39 59q0 26 19 45t45 19q13 0 26 -5q211 -91 338 -283.5t127 -422.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M384 384v-128h-128v128h128zM384 1152v-128h-128v128h128zM1152 1152v-128h-128v128h128zM128 129h384v383h-384v-383zM128 896h384v384h-384v-384zM896 896h384v384h-384v-384zM640 640v-640h-640v640h640zM1152 128v-128h-128v128h128zM1408 128v-128h-128v128h128z M1408 640v-384h-384v128h-128v-384h-128v640h384v-128h128v128h128zM640 1408v-640h-640v640h640zM1408 1408v-640h-640v640h640z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M63 0h-63v1408h63v-1408zM126 1h-32v1407h32v-1407zM220 1h-31v1407h31v-1407zM377 1h-31v1407h31v-1407zM534 1h-62v1407h62v-1407zM660 1h-31v1407h31v-1407zM723 1h-31v1407h31v-1407zM786 1h-31v1407h31v-1407zM943 1h-63v1407h63v-1407zM1100 1h-63v1407h63v-1407z M1226 1h-63v1407h63v-1407zM1352 1h-63v1407h63v-1407zM1446 1h-63v1407h63v-1407zM1635 1h-94v1407h94v-1407zM1698 1h-32v1407h32v-1407zM1792 0h-63v1408h63v-1408z" />
|
||||
<glyph unicode="" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91zM1899 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-36 0 -59 14t-53 45l470 470q37 37 37 90q0 52 -37 91l-715 714q-38 38 -102 64.5t-117 26.5h224q53 0 117 -26.5t102 -64.5l715 -714q37 -39 37 -91z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1639 1058q40 -57 18 -129l-275 -906q-19 -64 -76.5 -107.5t-122.5 -43.5h-923q-77 0 -148.5 53.5t-99.5 131.5q-24 67 -2 127q0 4 3 27t4 37q1 8 -3 21.5t-3 19.5q2 11 8 21t16.5 23.5t16.5 23.5q23 38 45 91.5t30 91.5q3 10 0.5 30t-0.5 28q3 11 17 28t17 23 q21 36 42 92t25 90q1 9 -2.5 32t0.5 28q4 13 22 30.5t22 22.5q19 26 42.5 84.5t27.5 96.5q1 8 -3 25.5t-2 26.5q2 8 9 18t18 23t17 21q8 12 16.5 30.5t15 35t16 36t19.5 32t26.5 23.5t36 11.5t47.5 -5.5l-1 -3q38 9 51 9h761q74 0 114 -56t18 -130l-274 -906 q-36 -119 -71.5 -153.5t-128.5 -34.5h-869q-27 0 -38 -15q-11 -16 -1 -43q24 -70 144 -70h923q29 0 56 15.5t35 41.5l300 987q7 22 5 57q38 -15 59 -43zM575 1056q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5 t-16.5 -22.5zM492 800q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5t-16.5 -22.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M384 0h896v256h-896v-256zM384 640h896v384h-160q-40 0 -68 28t-28 68v160h-640v-640zM1536 576q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 576v-416q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-160q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68 v160h-224q-13 0 -22.5 9.5t-9.5 22.5v416q0 79 56.5 135.5t135.5 56.5h64v544q0 40 28 68t68 28h672q40 0 88 -20t76 -48l152 -152q28 -28 48 -76t20 -88v-256h64q79 0 135.5 -56.5t56.5 -135.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M960 864q119 0 203.5 -84.5t84.5 -203.5t-84.5 -203.5t-203.5 -84.5t-203.5 84.5t-84.5 203.5t84.5 203.5t203.5 84.5zM1664 1280q106 0 181 -75t75 -181v-896q0 -106 -75 -181t-181 -75h-1408q-106 0 -181 75t-75 181v896q0 106 75 181t181 75h224l51 136 q19 49 69.5 84.5t103.5 35.5h512q53 0 103.5 -35.5t69.5 -84.5l51 -136h224zM960 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M725 977l-170 -450q73 -1 153.5 -2t119 -1.5t52.5 -0.5l29 2q-32 95 -92 241q-53 132 -92 211zM21 -128h-21l2 79q22 7 80 18q89 16 110 31q20 16 48 68l237 616l280 724h75h53l11 -21l205 -480q103 -242 124 -297q39 -102 96 -235q26 -58 65 -164q24 -67 65 -149 q22 -49 35 -57q22 -19 69 -23q47 -6 103 -27q6 -39 6 -57q0 -14 -1 -26q-80 0 -192 8q-93 8 -189 8q-79 0 -135 -2l-200 -11l-58 -2q0 45 4 78l131 28q56 13 68 23q12 12 12 27t-6 32l-47 114l-92 228l-450 2q-29 -65 -104 -274q-23 -64 -23 -84q0 -31 17 -43 q26 -21 103 -32q3 0 13.5 -2t30 -5t40.5 -6q1 -28 1 -58q0 -17 -2 -27q-66 0 -349 20l-48 -8q-81 -14 -167 -14z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M555 15q76 -32 140 -32q131 0 216 41t122 113q38 70 38 181q0 114 -41 180q-58 94 -141 126q-80 32 -247 32q-74 0 -101 -10v-144l-1 -173l3 -270q0 -15 12 -44zM541 761q43 -7 109 -7q175 0 264 65t89 224q0 112 -85 187q-84 75 -255 75q-52 0 -130 -13q0 -44 2 -77 q7 -122 6 -279l-1 -98q0 -43 1 -77zM0 -128l2 94q45 9 68 12q77 12 123 31q17 27 21 51q9 66 9 194l-2 497q-5 256 -9 404q-1 87 -11 109q-1 4 -12 12q-18 12 -69 15q-30 2 -114 13l-4 83l260 6l380 13l45 1q5 0 14 0.5t14 0.5q1 0 21.5 -0.5t40.5 -0.5h74q88 0 191 -27 q43 -13 96 -39q57 -29 102 -76q44 -47 65 -104t21 -122q0 -70 -32 -128t-95 -105q-26 -20 -150 -77q177 -41 267 -146q92 -106 92 -236q0 -76 -29 -161q-21 -62 -71 -117q-66 -72 -140 -108q-73 -36 -203 -60q-82 -15 -198 -11l-197 4q-84 2 -298 -11q-33 -3 -272 -11z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M0 -126l17 85q4 1 77 20q76 19 116 39q29 37 41 101l27 139l56 268l12 64q8 44 17 84.5t16 67t12.5 46.5t9 30.5t3.5 11.5l29 157l16 63l22 135l8 50v38q-41 22 -144 28q-28 2 -38 4l19 103l317 -14q39 -2 73 -2q66 0 214 9q33 2 68 4.5t36 2.5q-2 -19 -6 -38 q-7 -29 -13 -51q-55 -19 -109 -31q-64 -16 -101 -31q-12 -31 -24 -88q-9 -44 -13 -82q-44 -199 -66 -306l-61 -311l-38 -158l-43 -235l-12 -45q-2 -7 1 -27q64 -15 119 -21q36 -5 66 -10q-1 -29 -7 -58q-7 -31 -9 -41q-18 0 -23 -1q-24 -2 -42 -2q-9 0 -28 3q-19 4 -145 17 l-198 2q-41 1 -174 -11q-74 -7 -98 -9z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M81 1407l54 -27q20 -5 211 -5h130l19 3l115 1l215 -1h293l34 -2q14 -1 28 7t21 16l7 8l42 1q15 0 28 -1v-104.5t1 -131.5l1 -100l-1 -58q0 -32 -4 -51q-39 -15 -68 -18q-25 43 -54 128q-8 24 -15.5 62.5t-11.5 65.5t-6 29q-13 15 -27 19q-7 2 -42.5 2t-103.5 -1t-111 -1 q-34 0 -67 -5q-10 -97 -8 -136l1 -152v-332l3 -359l-1 -147q-1 -46 11 -85q49 -25 89 -32q2 0 18 -5t44 -13t43 -12q30 -8 50 -18q5 -45 5 -50q0 -10 -3 -29q-14 -1 -34 -1q-110 0 -187 10q-72 8 -238 8q-88 0 -233 -14q-48 -4 -70 -4q-2 22 -2 26l-1 26v9q21 33 79 49 q139 38 159 50q9 21 12 56q8 192 6 433l-5 428q-1 62 -0.5 118.5t0.5 102.5t-2 57t-6 15q-6 5 -14 6q-38 6 -148 6q-43 0 -100 -13.5t-73 -24.5q-13 -9 -22 -33t-22 -75t-24 -84q-6 -19 -19.5 -32t-20.5 -13q-44 27 -56 44v297v86zM1744 128q33 0 42 -18.5t-11 -44.5 l-126 -162q-20 -26 -49 -26t-49 26l-126 162q-20 26 -11 44.5t42 18.5h80v1024h-80q-33 0 -42 18.5t11 44.5l126 162q20 26 49 26t49 -26l126 -162q20 -26 11 -44.5t-42 -18.5h-80v-1024h80z" />
|
||||
<glyph unicode="" d="M81 1407l54 -27q20 -5 211 -5h130l19 3l115 1l446 -1h318l34 -2q14 -1 28 7t21 16l7 8l42 1q15 0 28 -1v-104.5t1 -131.5l1 -100l-1 -58q0 -32 -4 -51q-39 -15 -68 -18q-25 43 -54 128q-8 24 -15.5 62.5t-11.5 65.5t-6 29q-13 15 -27 19q-7 2 -58.5 2t-138.5 -1t-128 -1 q-94 0 -127 -5q-10 -97 -8 -136l1 -152v52l3 -359l-1 -147q-1 -46 11 -85q49 -25 89 -32q2 0 18 -5t44 -13t43 -12q30 -8 50 -18q5 -45 5 -50q0 -10 -3 -29q-14 -1 -34 -1q-110 0 -187 10q-72 8 -238 8q-82 0 -233 -13q-45 -5 -70 -5q-2 22 -2 26l-1 26v9q21 33 79 49 q139 38 159 50q9 21 12 56q6 137 6 433l-5 44q0 265 -2 278q-2 11 -6 15q-6 5 -14 6q-38 6 -148 6q-50 0 -168.5 -14t-132.5 -24q-13 -9 -22 -33t-22 -75t-24 -84q-6 -19 -19.5 -32t-20.5 -13q-44 27 -56 44v297v86zM1505 113q26 -20 26 -49t-26 -49l-162 -126 q-26 -20 -44.5 -11t-18.5 42v80h-1024v-80q0 -33 -18.5 -42t-44.5 11l-162 126q-26 20 -26 49t26 49l162 126q26 20 44.5 11t18.5 -42v-80h1024v80q0 33 18.5 42t44.5 -11z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45t-45 -19 h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h640q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M256 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM256 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5 t9.5 -22.5zM256 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344 q13 0 22.5 -9.5t9.5 -22.5zM256 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192 q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M384 992v-576q0 -13 -9.5 -22.5t-22.5 -9.5q-14 0 -23 9l-288 288q-9 9 -9 23t9 23l288 288q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M352 704q0 -14 -9 -23l-288 -288q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v576q0 13 9.5 22.5t22.5 9.5q14 0 23 -9l288 -288q9 -9 9 -23zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 1184v-1088q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-403 403v-166q0 -119 -84.5 -203.5t-203.5 -84.5h-704q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h704q119 0 203.5 -84.5t84.5 -203.5v-165l403 402q18 19 45 19q12 0 25 -5 q39 -17 39 -59z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M640 960q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 576v-448h-1408v192l320 320l160 -160l512 512zM1760 1280h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5v1216 q0 13 -9.5 22.5t-22.5 9.5zM1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
|
||||
<glyph unicode="" d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928q0 22 -22 22q-10 0 -17 -7l-542 -542q-7 -7 -7 -17q0 -22 22 -22q10 0 17 7l542 542q7 7 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024q0 -53 -37 -90l-166 -166l-416 416l166 165q36 38 90 38 q53 0 91 -38l235 -234q37 -39 37 -91z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M768 896q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1024 896q0 -109 -33 -179l-364 -774q-16 -33 -47.5 -52t-67.5 -19t-67.5 19t-46.5 52l-365 774q-33 70 -33 179q0 212 150 362t362 150t362 -150t150 -362z" />
|
||||
<glyph unicode="" d="M768 96v1088q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M512 384q0 36 -20 69q-1 1 -15.5 22.5t-25.5 38t-25 44t-21 50.5q-4 16 -21 16t-21 -16q-7 -23 -21 -50.5t-25 -44t-25.5 -38t-15.5 -22.5q-20 -33 -20 -69q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 512q0 -212 -150 -362t-362 -150t-362 150t-150 362 q0 145 81 275q6 9 62.5 90.5t101 151t99.5 178t83 201.5q9 30 34 47t51 17t51.5 -17t33.5 -47q28 -93 83 -201.5t99.5 -178t101 -151t62.5 -90.5q81 -127 81 -275z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M888 352l116 116l-152 152l-116 -116v-56h96v-96h56zM1328 1072q-16 16 -33 -1l-350 -350q-17 -17 -1 -33t33 1l350 350q17 17 1 33zM1408 478v-190q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-14 -14 -32 -8q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v126q0 13 9 22l64 64q15 15 35 7t20 -29zM1312 1216l288 -288l-672 -672h-288v288zM1756 1084l-92 -92 l-288 288l92 92q28 28 68 28t68 -28l152 -152q28 -28 28 -68t-28 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1408 547v-259q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h255v0q13 0 22.5 -9.5t9.5 -22.5q0 -27 -26 -32q-77 -26 -133 -60q-10 -4 -16 -4h-112q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832 q66 0 113 47t47 113v214q0 19 18 29q28 13 54 37q16 16 35 8q21 -9 21 -29zM1645 1043l-384 -384q-18 -19 -45 -19q-12 0 -25 5q-39 17 -39 59v192h-160q-323 0 -438 -131q-119 -137 -74 -473q3 -23 -20 -34q-8 -2 -12 -2q-16 0 -26 13q-10 14 -21 31t-39.5 68.5t-49.5 99.5 t-38.5 114t-17.5 122q0 49 3.5 91t14 90t28 88t47 81.5t68.5 74t94.5 61.5t124.5 48.5t159.5 30.5t196.5 11h160v192q0 42 39 59q13 5 25 5q26 0 45 -19l384 -384q19 -19 19 -45t-19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1408 606v-318q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-10 -10 -23 -10q-3 0 -9 2q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832 q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v254q0 13 9 22l64 64q10 10 23 10q6 0 12 -3q20 -8 20 -29zM1639 1095l-814 -814q-24 -24 -57 -24t-57 24l-430 430q-24 24 -24 57t24 57l110 110q24 24 57 24t57 -24l263 -263l647 647q24 24 57 24t57 -24l110 -110 q24 -24 24 -57t-24 -57z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-384v-384h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v384h-384v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45 t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h384v384h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45t-19 -45t-45 -19h-128v-384h384v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M979 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19l710 710 q19 19 32 13t13 -32v-710q4 11 13 19z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1619 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-8 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-19 19 -19 45t19 45l710 710q19 19 32 13t13 -32v-710q5 11 13 19z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1384 609l-1328 -738q-23 -13 -39.5 -3t-16.5 36v1472q0 26 16.5 36t39.5 -3l1328 -738q23 -13 23 -31t-23 -31z" />
|
||||
<glyph unicode="" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45zM640 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q19 -19 19 -45t-19 -45l-710 -710q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19l-710 -710 q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19z" />
|
||||
<glyph unicode="" horiz-adv-x="1538" d="M14 557l710 710q19 19 45 19t45 -19l710 -710q19 -19 13 -32t-32 -13h-1472q-26 0 -32 13t13 32zM1473 0h-1408q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1408q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M742 -37l-652 651q-37 37 -37 90.5t37 90.5l652 651q37 37 90.5 37t90.5 -37l75 -75q37 -37 37 -90.5t-37 -90.5l-486 -486l486 -485q37 -38 37 -91t-37 -90l-75 -75q-37 -37 -90.5 -37t-90.5 37z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M1099 704q0 -52 -37 -91l-652 -651q-37 -37 -90 -37t-90 37l-76 75q-37 39 -37 91q0 53 37 90l486 486l-486 485q-37 39 -37 91q0 53 37 90l76 75q36 38 90 38t90 -38l652 -651q37 -37 37 -90z" />
|
||||
<glyph unicode="" d="M1216 576v128q0 26 -19 45t-45 19h-256v256q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-256h-256q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h256v-256q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v256h256q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5 t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1216 576v128q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
|
||||
<glyph unicode="" d="M1149 414q0 26 -19 45l-181 181l181 181q19 19 19 45q0 27 -19 46l-90 90q-19 19 -46 19q-26 0 -45 -19l-181 -181l-181 181q-19 19 -45 19q-27 0 -46 -19l-90 -90q-19 -19 -19 -46q0 -26 19 -45l181 -181l-181 -181q-19 -19 -19 -45q0 -27 19 -46l90 -90q19 -19 46 -19 q26 0 45 19l181 181l181 -181q19 -19 45 -19q27 0 46 19l90 90q19 19 19 46zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1284 802q0 28 -18 46l-91 90q-19 19 -45 19t-45 -19l-408 -407l-226 226q-19 19 -45 19t-45 -19l-91 -90q-18 -18 -18 -46q0 -27 18 -45l362 -362q19 -19 45 -19q27 0 46 19l543 543q18 18 18 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M896 160v192q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1152 832q0 88 -55.5 163t-138.5 116t-170 41q-243 0 -371 -213q-15 -24 8 -42l132 -100q7 -6 19 -6q16 0 25 12q53 68 86 92q34 24 86 24q48 0 85.5 -26t37.5 -59 q0 -38 -20 -61t-68 -45q-63 -28 -115.5 -86.5t-52.5 -125.5v-36q0 -14 9 -23t23 -9h192q14 0 23 9t9 23q0 19 21.5 49.5t54.5 49.5q32 18 49 28.5t46 35t44.5 48t28 60.5t12.5 81zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1024 160v160q0 14 -9 23t-23 9h-96v512q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h96v-320h-96q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h448q14 0 23 9t9 23zM896 1056v160q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23 t23 -9h192q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1197 512h-109q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h109q-32 108 -112.5 188.5t-188.5 112.5v-109q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v109q-108 -32 -188.5 -112.5t-112.5 -188.5h109q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-109 q32 -108 112.5 -188.5t188.5 -112.5v109q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-109q108 32 188.5 112.5t112.5 188.5zM1536 704v-128q0 -26 -19 -45t-45 -19h-143q-37 -161 -154.5 -278.5t-278.5 -154.5v-143q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v143 q-161 37 -278.5 154.5t-154.5 278.5h-143q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h143q37 161 154.5 278.5t278.5 154.5v143q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-143q161 -37 278.5 -154.5t154.5 -278.5h143q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" d="M1097 457l-146 -146q-10 -10 -23 -10t-23 10l-137 137l-137 -137q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l137 137l-137 137q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l137 -137l137 137q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23 l-137 -137l137 -137q10 -10 10 -23t-10 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5 t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1171 723l-422 -422q-19 -19 -45 -19t-45 19l-294 294q-19 19 -19 45t19 45l102 102q19 19 45 19t45 -19l147 -147l275 275q19 19 45 19t45 -19l102 -102q19 -19 19 -45t-19 -45zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198 t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1312 643q0 161 -87 295l-754 -753q137 -89 297 -89q111 0 211.5 43.5t173.5 116.5t116 174.5t43 212.5zM313 344l755 754q-135 91 -300 91q-148 0 -273 -73t-198 -199t-73 -274q0 -162 89 -299zM1536 643q0 -157 -61 -300t-163.5 -246t-245 -164t-298.5 -61t-298.5 61 t-245 164t-163.5 246t-61 300t61 299.5t163.5 245.5t245 164t298.5 61t298.5 -61t245 -164t163.5 -245.5t61 -299.5z" />
|
||||
<glyph unicode="" d="M1536 640v-128q0 -53 -32.5 -90.5t-84.5 -37.5h-704l293 -294q38 -36 38 -90t-38 -90l-75 -76q-37 -37 -90 -37q-52 0 -91 37l-651 652q-37 37 -37 90q0 52 37 91l651 650q38 38 91 38q52 0 90 -38l75 -74q38 -38 38 -91t-38 -91l-293 -293h704q52 0 84.5 -37.5 t32.5 -90.5z" />
|
||||
<glyph unicode="" d="M1472 576q0 -54 -37 -91l-651 -651q-39 -37 -91 -37q-51 0 -90 37l-75 75q-38 38 -38 91t38 91l293 293h-704q-52 0 -84.5 37.5t-32.5 90.5v128q0 53 32.5 90.5t84.5 37.5h704l-293 294q-38 36 -38 90t38 90l75 75q38 38 90 38q53 0 91 -38l651 -651q37 -35 37 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1611 565q0 -51 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-294 293v-704q0 -52 -37.5 -84.5t-90.5 -32.5h-128q-53 0 -90.5 32.5t-37.5 84.5v704l-294 -293q-36 -38 -90 -38t-90 38l-75 75q-38 38 -38 90q0 53 38 91l651 651q35 37 90 37q54 0 91 -37l651 -651 q37 -39 37 -91z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1611 704q0 -53 -37 -90l-651 -652q-39 -37 -91 -37q-53 0 -90 37l-651 652q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l294 -294v704q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-704l294 294q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 896q0 -26 -19 -45l-512 -512q-19 -19 -45 -19t-45 19t-19 45v256h-224q-98 0 -175.5 -6t-154 -21.5t-133 -42.5t-105.5 -69.5t-80 -101t-48.5 -138.5t-17.5 -181q0 -55 5 -123q0 -6 2.5 -23.5t2.5 -26.5q0 -15 -8.5 -25t-23.5 -10q-16 0 -28 17q-7 9 -13 22 t-13.5 30t-10.5 24q-127 285 -127 451q0 199 53 333q162 403 875 403h224v256q0 26 19 45t45 19t45 -19l512 -512q19 -19 19 -45z" />
|
||||
<glyph unicode="" d="M755 480q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23zM1536 1344v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332 q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" d="M768 576v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45zM1523 1248q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45 t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-416v-416q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v416h-416q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h416v416q0 40 28 68t68 28h192q40 0 68 -28t28 -68v-416h416q40 0 68 -28t28 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-1216q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h1216q40 0 68 -28t28 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1482 486q46 -26 59.5 -77.5t-12.5 -97.5l-64 -110q-26 -46 -77.5 -59.5t-97.5 12.5l-266 153v-307q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v307l-266 -153q-46 -26 -97.5 -12.5t-77.5 59.5l-64 110q-26 46 -12.5 97.5t59.5 77.5l266 154l-266 154 q-46 26 -59.5 77.5t12.5 97.5l64 110q26 46 77.5 59.5t97.5 -12.5l266 -153v307q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-307l266 153q46 26 97.5 12.5t77.5 -59.5l64 -110q26 -46 12.5 -97.5t-59.5 -77.5l-266 -154z" />
|
||||
<glyph unicode="" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM896 161v190q0 14 -9 23.5t-22 9.5h-192q-13 0 -23 -10t-10 -23v-190q0 -13 10 -23t23 -10h192 q13 0 22 9.5t9 23.5zM894 505l18 621q0 12 -10 18q-10 8 -24 8h-220q-14 0 -24 -8q-10 -6 -10 -18l17 -621q0 -10 10 -17.5t24 -7.5h185q14 0 23.5 7.5t10.5 17.5z" />
|
||||
<glyph unicode="" d="M928 180v56v468v192h-320v-192v-468v-56q0 -25 18 -38.5t46 -13.5h192q28 0 46 13.5t18 38.5zM472 1024h195l-126 161q-26 31 -69 31q-40 0 -68 -28t-28 -68t28 -68t68 -28zM1160 1120q0 40 -28 68t-68 28q-43 0 -69 -31l-125 -161h194q40 0 68 28t28 68zM1536 864v-320 q0 -14 -9 -23t-23 -9h-96v-416q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v416h-96q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h440q-93 0 -158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5q107 0 168 -77l128 -165l128 165q61 77 168 77q93 0 158.5 -65.5t65.5 -158.5 t-65.5 -158.5t-158.5 -65.5h440q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 832q0 26 -19 45t-45 19q-172 0 -318 -49.5t-259.5 -134t-235.5 -219.5q-19 -21 -19 -45q0 -26 19 -45t45 -19q24 0 45 19q27 24 74 71t67 66q137 124 268.5 176t313.5 52q26 0 45 19t19 45zM1792 1030q0 -95 -20 -193q-46 -224 -184.5 -383t-357.5 -268 q-214 -108 -438 -108q-148 0 -286 47q-15 5 -88 42t-96 37q-16 0 -39.5 -32t-45 -70t-52.5 -70t-60 -32q-30 0 -51 11t-31 24t-27 42q-2 4 -6 11t-5.5 10t-3 9.5t-1.5 13.5q0 35 31 73.5t68 65.5t68 56t31 48q0 4 -14 38t-16 44q-9 51 -9 104q0 115 43.5 220t119 184.5 t170.5 139t204 95.5q55 18 145 25.5t179.5 9t178.5 6t163.5 24t113.5 56.5l29.5 29.5t29.5 28t27 20t36.5 16t43.5 4.5q39 0 70.5 -46t47.5 -112t24 -124t8 -96z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 -160v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1152 896q0 -78 -24.5 -144t-64 -112.5t-87.5 -88t-96 -77.5t-87.5 -72t-64 -81.5t-24.5 -96.5q0 -96 67 -224l-4 1l1 -1 q-90 41 -160 83t-138.5 100t-113.5 122.5t-72.5 150.5t-27.5 184q0 78 24.5 144t64 112.5t87.5 88t96 77.5t87.5 72t64 81.5t24.5 96.5q0 94 -66 224l3 -1l-1 1q90 -41 160 -83t138.5 -100t113.5 -122.5t72.5 -150.5t27.5 -184z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 576q-152 236 -381 353q61 -104 61 -225q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 121 61 225q-229 -117 -381 -353q133 -205 333.5 -326.5t434.5 -121.5t434.5 121.5t333.5 326.5zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5 t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1792 576q0 -34 -20 -69q-140 -230 -376.5 -368.5t-499.5 -138.5t-499.5 139t-376.5 368q-20 35 -20 69t20 69q140 229 376.5 368t499.5 139t499.5 -139t376.5 -368q20 -35 20 -69z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M555 201l78 141q-87 63 -136 159t-49 203q0 121 61 225q-229 -117 -381 -353q167 -258 427 -375zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1307 1151q0 -7 -1 -9 q-105 -188 -315 -566t-316 -567l-49 -89q-10 -16 -28 -16q-12 0 -134 70q-16 10 -16 28q0 12 44 87q-143 65 -263.5 173t-208.5 245q-20 31 -20 69t20 69q153 235 380 371t496 136q89 0 180 -17l54 97q10 16 28 16q5 0 18 -6t31 -15.5t33 -18.5t31.5 -18.5t19.5 -11.5 q16 -10 16 -27zM1344 704q0 -139 -79 -253.5t-209 -164.5l280 502q8 -45 8 -84zM1792 576q0 -35 -20 -69q-39 -64 -109 -145q-150 -172 -347.5 -267t-419.5 -95l74 132q212 18 392.5 137t301.5 307q-115 179 -282 294l63 112q95 -64 182.5 -153t144.5 -184q20 -34 20 -69z " />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1024 161v190q0 14 -9.5 23.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -23.5v-190q0 -14 9.5 -23.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 23.5zM1022 535l18 459q0 12 -10 19q-13 11 -24 11h-220q-11 0 -24 -11q-10 -7 -10 -21l17 -457q0 -10 10 -16.5t24 -6.5h185 q14 0 23.5 6.5t10.5 16.5zM1008 1469l768 -1408q35 -63 -2 -126q-17 -29 -46.5 -46t-63.5 -17h-1536q-34 0 -63.5 17t-46.5 46q-37 63 -2 126l768 1408q17 31 47 49t65 18t65 -18t47 -49z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1376 1376q44 -52 12 -148t-108 -172l-161 -161l160 -696q5 -19 -12 -33l-128 -96q-7 -6 -19 -6q-4 0 -7 1q-15 3 -21 16l-279 508l-259 -259l53 -194q5 -17 -8 -31l-96 -96q-9 -9 -23 -9h-2q-15 2 -24 13l-189 252l-252 189q-11 7 -13 23q-1 13 9 25l96 97q9 9 23 9 q6 0 8 -1l194 -53l259 259l-508 279q-14 8 -17 24q-2 16 9 27l128 128q14 13 30 8l665 -159l160 160q76 76 172 108t148 -12z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M128 -128h288v288h-288v-288zM480 -128h320v288h-320v-288zM128 224h288v320h-288v-320zM480 224h320v320h-320v-320zM128 608h288v288h-288v-288zM864 -128h320v288h-320v-288zM480 608h320v288h-320v-288zM1248 -128h288v288h-288v-288zM864 224h320v320h-320v-320z M512 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1248 224h288v320h-288v-320zM864 608h320v288h-320v-288zM1248 608h288v288h-288v-288zM1280 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64 q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47 h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M666 1055q-60 -92 -137 -273q-22 45 -37 72.5t-40.5 63.5t-51 56.5t-63 35t-81.5 14.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q250 0 410 -225zM1792 256q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192q-32 0 -85 -0.5t-81 -1t-73 1 t-71 5t-64 10.5t-63 18.5t-58 28.5t-59 40t-55 53.5t-56 69.5q59 93 136 273q22 -45 37 -72.5t40.5 -63.5t51 -56.5t63 -35t81.5 -14.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1792 1152q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5 v192h-256q-48 0 -87 -15t-69 -45t-51 -61.5t-45 -77.5q-32 -62 -78 -171q-29 -66 -49.5 -111t-54 -105t-64 -100t-74 -83t-90 -68.5t-106.5 -42t-128 -16.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q48 0 87 15t69 45t51 61.5t45 77.5q32 62 78 171q29 66 49.5 111 t54 105t64 100t74 83t90 68.5t106.5 42t128 16.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22q-17 -2 -30.5 9t-17.5 29v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281 q0 130 71 248.5t191 204.5t286 136.5t348 50.5q244 0 450 -85.5t326 -233t120 -321.5z" />
|
||||
<glyph unicode="" d="M1536 704v-128q0 -201 -98.5 -362t-274 -251.5t-395.5 -90.5t-395.5 90.5t-274 251.5t-98.5 362v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-128q0 -52 23.5 -90t53.5 -57t71 -30t64 -13t44 -2t44 2t64 13t71 30t53.5 57t23.5 90v128q0 26 19 45t45 19h384 q26 0 45 -19t19 -45zM512 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45zM1536 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1611 320q0 -53 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-486 485l-486 -485q-36 -38 -90 -38t-90 38l-75 75q-38 36 -38 90q0 53 38 91l651 651q37 37 90 37q52 0 91 -37l650 -651q38 -38 38 -91z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1611 832q0 -53 -37 -90l-651 -651q-38 -38 -91 -38q-54 0 -90 38l-651 651q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l486 -486l486 486q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -11 7 -21 zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z " />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5 l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5 t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" />
|
||||
<glyph unicode="" horiz-adv-x="768" d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M512 512v-384h-256v384h256zM896 1024v-896h-256v896h256zM1280 768v-640h-256v640h256zM1664 1152v-1024h-256v1024h256zM1792 32v1216q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5z M1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
|
||||
<glyph unicode="" d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1307 618l23 219h-198v109q0 49 15.5 68.5t71.5 19.5h110v219h-175q-152 0 -218 -72t-66 -213v-131h-131v-219h131v-635h262v635h175zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -10 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -9 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1408 768q0 -139 -94 -257t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224 q0 139 94 257t256.5 186.5t353.5 68.5t353.5 -68.5t256.5 -186.5t94 -257zM1792 512q0 -120 -71 -224.5t-195 -176.5q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7 q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230z" />
|
||||
<glyph unicode="" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 768q0 51 -39 89.5t-89 38.5h-352q0 58 48 159.5t48 160.5q0 98 -32 145t-128 47q-26 -26 -38 -85t-30.5 -125.5t-59.5 -109.5q-22 -23 -77 -91q-4 -5 -23 -30t-31.5 -41t-34.5 -42.5 t-40 -44t-38.5 -35.5t-40 -27t-35.5 -9h-32v-640h32q13 0 31.5 -3t33 -6.5t38 -11t35 -11.5t35.5 -12.5t29 -10.5q211 -73 342 -73h121q192 0 192 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5q32 1 53.5 47t21.5 81zM1536 769 q0 -89 -49 -163q9 -33 9 -69q0 -77 -38 -144q3 -21 3 -43q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5h-36h-93q-96 0 -189.5 22.5t-216.5 65.5q-116 40 -138 40h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h274q36 24 137 155q58 75 107 128 q24 25 35.5 85.5t30.5 126.5t62 108q39 37 90 37q84 0 151 -32.5t102 -101.5t35 -186q0 -93 -48 -192h176q104 0 180 -76t76 -179z" />
|
||||
<glyph unicode="" d="M256 1088q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 512q0 35 -21.5 81t-53.5 47q15 17 25 47.5t10 55.5q0 69 -53 119q18 32 18 69t-17.5 73.5t-47.5 52.5q5 30 5 56q0 85 -49 126t-136 41h-128q-131 0 -342 -73q-5 -2 -29 -10.5 t-35.5 -12.5t-35 -11.5t-38 -11t-33 -6.5t-31.5 -3h-32v-640h32q16 0 35.5 -9t40 -27t38.5 -35.5t40 -44t34.5 -42.5t31.5 -41t23 -30q55 -68 77 -91q41 -43 59.5 -109.5t30.5 -125.5t38 -85q96 0 128 47t32 145q0 59 -48 160.5t-48 159.5h352q50 0 89 38.5t39 89.5z M1536 511q0 -103 -76 -179t-180 -76h-176q48 -99 48 -192q0 -118 -35 -186q-35 -69 -102 -101.5t-151 -32.5q-51 0 -90 37q-34 33 -54 82t-25.5 90.5t-17.5 84.5t-31 64q-48 50 -107 127q-101 131 -137 155h-274q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5 h288q22 0 138 40q128 44 223 66t200 22h112q140 0 226.5 -79t85.5 -216v-5q60 -77 60 -178q0 -22 -3 -43q38 -67 38 -144q0 -36 -9 -69q49 -74 49 -163z" />
|
||||
<glyph unicode="" horiz-adv-x="896" d="M832 1504v-1339l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 940q0 81 -21.5 143t-55 98.5t-81.5 59.5t-94 31t-98 8t-112 -25.5t-110.5 -64t-86.5 -72t-60 -61.5q-18 -22 -49 -22t-49 22q-24 28 -60 61.5t-86.5 72t-110.5 64t-112 25.5t-98 -8t-94 -31t-81.5 -59.5t-55 -98.5t-21.5 -143q0 -168 187 -355l581 -560l580 559 q188 188 188 356zM1792 940q0 -221 -229 -450l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5 q224 0 351 -124t127 -344z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M640 96q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h320q13 0 22.5 -9.5t9.5 -22.5q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-66 0 -113 -47t-47 -113v-704 q0 -66 47 -113t113 -47h288h11h13t11.5 -1t11.5 -3t8 -5.5t7 -9t2 -13.5zM1568 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45z" />
|
||||
<glyph unicode="" d="M237 122h231v694h-231v-694zM483 1030q-1 52 -36 86t-93 34t-94.5 -34t-36.5 -86q0 -51 35.5 -85.5t92.5 -34.5h1q59 0 95 34.5t36 85.5zM1068 122h231v398q0 154 -73 233t-193 79q-136 0 -209 -117h2v101h-231q3 -66 0 -694h231v388q0 38 7 56q15 35 45 59.5t74 24.5 q116 0 116 -157v-371zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M480 672v448q0 14 -9 23t-23 9t-23 -9t-9 -23v-448q0 -14 9 -23t23 -9t23 9t9 23zM1152 320q0 -26 -19 -45t-45 -19h-429l-51 -483q-2 -12 -10.5 -20.5t-20.5 -8.5h-1q-27 0 -32 27l-76 485h-404q-26 0 -45 19t-19 45q0 123 78.5 221.5t177.5 98.5v512q-52 0 -90 38 t-38 90t38 90t90 38h640q52 0 90 -38t38 -90t-38 -90t-90 -38v-512q99 0 177.5 -98.5t78.5 -221.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" />
|
||||
<glyph unicode="" d="M394 184q-8 -9 -20 3q-13 11 -4 19q8 9 20 -3q12 -11 4 -19zM352 245q9 -12 0 -19q-8 -6 -17 7t0 18q9 7 17 -6zM291 305q-5 -7 -13 -2q-10 5 -7 12q3 5 13 2q10 -5 7 -12zM322 271q-6 -7 -16 3q-9 11 -2 16q6 6 16 -3q9 -11 2 -16zM451 159q-4 -12 -19 -6q-17 4 -13 15 t19 7q16 -5 13 -16zM514 154q0 -11 -16 -11q-17 -2 -17 11q0 11 16 11q17 2 17 -11zM572 164q2 -10 -14 -14t-18 8t14 15q16 2 18 -9zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-224q-16 0 -24.5 1t-19.5 5t-16 14.5t-5 27.5v239q0 97 -52 142q57 6 102.5 18t94 39 t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103 q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -103t0.5 -68q0 -22 -11 -33.5t-22 -13t-33 -1.5 h-224q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" />
|
||||
<glyph unicode="" d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -10 1 -18.5t3 -17t4 -13.5t6.5 -16t6.5 -17q16 -40 25 -118.5t9 -136.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -52.5 3.5t-57.5 12.5t-47.5 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-128 79 -264.5 215.5t-215.5 264.5q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47.5t-12.5 57.5t-3.5 52.5 q0 92 51 186q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174 q2 -1 19 -11.5t24 -14t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1120 1280h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v832q0 66 -47 113t-113 47zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1152 1280h-1024v-1242l423 406l89 85l89 -85l423 -406v1242zM1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289 q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
|
||||
<glyph unicode="" d="M1280 343q0 11 -2 16q-3 8 -38.5 29.5t-88.5 49.5l-53 29q-5 3 -19 13t-25 15t-21 5q-18 0 -47 -32.5t-57 -65.5t-44 -33q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170.5 126.5t-126.5 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5t-3.5 16.5q0 13 20.5 33.5t45 38.5 t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5t320.5 -216.5q6 -2 30 -11t33 -12.5 t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" />
|
||||
<glyph unicode="" horiz-adv-x="768" d="M511 980h257l-30 -284h-227v-824h-341v824h-170v284h170v171q0 182 86 275.5t283 93.5h227v-284h-142q-39 0 -62.5 -6.5t-34 -23.5t-13.5 -34.5t-3 -49.5v-142z" />
|
||||
<glyph unicode="" d="M1536 640q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -39.5 7t-12.5 30v211q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5 q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23 q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -89t0.5 -54q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 t316.5 -131.5t131.5 -316.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 q187 -186 294 -425.5t120 -501.5z" />
|
||||
<glyph unicode="" d="M1040 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1296 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1408 160v320q0 13 -9.5 22.5t-22.5 9.5 h-1216q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5zM178 640h1180l-157 482q-4 13 -16 21.5t-26 8.5h-782q-14 0 -26 -8.5t-16 -21.5zM1536 480v-320q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v320q0 25 16 75 l197 606q17 53 63 86t101 33h782q55 0 101 -33t63 -86l197 -606q16 -50 16 -75z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 896q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5v-384q0 -52 -38 -90t-90 -38q-417 347 -812 380q-58 -19 -91 -66t-31 -100.5t40 -92.5q-20 -33 -23 -65.5t6 -58t33.5 -55t48 -50t61.5 -50.5q-29 -58 -111.5 -83t-168.5 -11.5t-132 55.5q-7 23 -29.5 87.5 t-32 94.5t-23 89t-15 101t3.5 98.5t22 110.5h-122q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h480q435 0 896 384q52 0 90 -38t38 -90v-384zM1536 292v954q-394 -302 -768 -343v-270q377 -42 768 -341z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M848 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM183 128h1298q-164 181 -246.5 411.5t-82.5 484.5q0 256 -320 256t-320 -256q0 -254 -82.5 -484.5t-246.5 -411.5zM1664 128q0 -52 -38 -90t-90 -38 h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q190 161 287 397.5t97 498.5q0 165 96 262t264 117q-8 18 -8 37q0 40 28 68t68 28t68 -28t28 -68q0 -19 -8 -37q168 -20 264 -117t96 -262q0 -262 97 -498.5t287 -397.5z" />
|
||||
<glyph unicode="" d="M1376 640l138 -135q30 -28 20 -70q-12 -41 -52 -51l-188 -48l53 -186q12 -41 -19 -70q-29 -31 -70 -19l-186 53l-48 -188q-10 -40 -51 -52q-12 -2 -19 -2q-31 0 -51 22l-135 138l-135 -138q-28 -30 -70 -20q-41 11 -51 52l-48 188l-186 -53q-41 -12 -70 19q-31 29 -19 70 l53 186l-188 48q-40 10 -52 51q-10 42 20 70l138 135l-138 135q-30 28 -20 70q12 41 52 51l188 48l-53 186q-12 41 19 70q29 31 70 19l186 -53l48 188q10 41 51 51q41 12 70 -19l135 -139l135 139q29 30 70 19q41 -10 51 -51l48 -188l186 53q41 12 70 -19q31 -29 19 -70 l-53 -186l188 -48q40 -10 52 -51q10 -42 -20 -70z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 768q0 51 -39 89.5t-89 38.5h-576q0 20 15 48.5t33 55t33 68t15 84.5q0 67 -44.5 97.5t-115.5 30.5q-24 0 -90 -139q-24 -44 -37 -65q-40 -64 -112 -145q-71 -81 -101 -106 q-69 -57 -140 -57h-32v-640h32q72 0 167 -32t193.5 -64t179.5 -32q189 0 189 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5h331q52 0 90 38t38 90zM1792 769q0 -105 -75.5 -181t-180.5 -76h-169q-4 -62 -37 -119q3 -21 3 -43 q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5q-133 0 -322 69q-164 59 -223 59h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h288q10 0 21.5 4.5t23.5 14t22.5 18t24 22.5t20.5 21.5t19 21.5t14 17q65 74 100 129q13 21 33 62t37 72t40.5 63t55 49.5 t69.5 17.5q125 0 206.5 -67t81.5 -189q0 -68 -22 -128h374q104 0 180 -76t76 -179z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1376 128h32v640h-32q-35 0 -67.5 12t-62.5 37t-50 46t-49 54q-2 3 -3.5 4.5t-4 4.5t-4.5 5q-72 81 -112 145q-14 22 -38 68q-1 3 -10.5 22.5t-18.5 36t-20 35.5t-21.5 30.5t-18.5 11.5q-71 0 -115.5 -30.5t-44.5 -97.5q0 -43 15 -84.5t33 -68t33 -55t15 -48.5h-576 q-50 0 -89 -38.5t-39 -89.5q0 -52 38 -90t90 -38h331q-15 -17 -25 -47.5t-10 -55.5q0 -69 53 -119q-18 -32 -18 -69t17.5 -73.5t47.5 -52.5q-4 -24 -4 -56q0 -85 48.5 -126t135.5 -41q84 0 183 32t194 64t167 32zM1664 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45 t45 -19t45 19t19 45zM1792 768v-640q0 -53 -37.5 -90.5t-90.5 -37.5h-288q-59 0 -223 -59q-190 -69 -317 -69q-142 0 -230 77.5t-87 217.5l1 5q-61 76 -61 178q0 22 3 43q-33 57 -37 119h-169q-105 0 -180.5 76t-75.5 181q0 103 76 179t180 76h374q-22 60 -22 128 q0 122 81.5 189t206.5 67q38 0 69.5 -17.5t55 -49.5t40.5 -63t37 -72t33 -62q35 -55 100 -129q2 -3 14 -17t19 -21.5t20.5 -21.5t24 -22.5t22.5 -18t23.5 -14t21.5 -4.5h288q53 0 90.5 -37.5t37.5 -90.5z" />
|
||||
<glyph unicode="" d="M1280 -64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 700q0 189 -167 189q-26 0 -56 -5q-16 30 -52.5 47.5t-73.5 17.5t-69 -18q-50 53 -119 53q-25 0 -55.5 -10t-47.5 -25v331q0 52 -38 90t-90 38q-51 0 -89.5 -39t-38.5 -89v-576 q-20 0 -48.5 15t-55 33t-68 33t-84.5 15q-67 0 -97.5 -44.5t-30.5 -115.5q0 -24 139 -90q44 -24 65 -37q64 -40 145 -112q81 -71 106 -101q57 -69 57 -140v-32h640v32q0 72 32 167t64 193.5t32 179.5zM1536 705q0 -133 -69 -322q-59 -164 -59 -223v-288q0 -53 -37.5 -90.5 t-90.5 -37.5h-640q-53 0 -90.5 37.5t-37.5 90.5v288q0 10 -4.5 21.5t-14 23.5t-18 22.5t-22.5 24t-21.5 20.5t-21.5 19t-17 14q-74 65 -129 100q-21 13 -62 33t-72 37t-63 40.5t-49.5 55t-17.5 69.5q0 125 67 206.5t189 81.5q68 0 128 -22v374q0 104 76 180t179 76 q105 0 181 -75.5t76 -180.5v-169q62 -4 119 -37q21 3 43 3q101 0 178 -60q139 1 219.5 -85t80.5 -227z" />
|
||||
<glyph unicode="" d="M1408 576q0 84 -32 183t-64 194t-32 167v32h-640v-32q0 -35 -12 -67.5t-37 -62.5t-46 -50t-54 -49q-9 -8 -14 -12q-81 -72 -145 -112q-22 -14 -68 -38q-3 -1 -22.5 -10.5t-36 -18.5t-35.5 -20t-30.5 -21.5t-11.5 -18.5q0 -71 30.5 -115.5t97.5 -44.5q43 0 84.5 15t68 33 t55 33t48.5 15v-576q0 -50 38.5 -89t89.5 -39q52 0 90 38t38 90v331q46 -35 103 -35q69 0 119 53q32 -18 69 -18t73.5 17.5t52.5 47.5q24 -4 56 -4q85 0 126 48.5t41 135.5zM1280 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 580 q0 -142 -77.5 -230t-217.5 -87l-5 1q-76 -61 -178 -61q-22 0 -43 3q-54 -30 -119 -37v-169q0 -105 -76 -180.5t-181 -75.5q-103 0 -179 76t-76 180v374q-54 -22 -128 -22q-121 0 -188.5 81.5t-67.5 206.5q0 38 17.5 69.5t49.5 55t63 40.5t72 37t62 33q55 35 129 100 q3 2 17 14t21.5 19t21.5 20.5t22.5 24t18 22.5t14 23.5t4.5 21.5v288q0 53 37.5 90.5t90.5 37.5h640q53 0 90.5 -37.5t37.5 -90.5v-288q0 -59 59 -223q69 -190 69 -317z" />
|
||||
<glyph unicode="" d="M1280 576v128q0 26 -19 45t-45 19h-502l189 189q19 19 19 45t-19 45l-91 91q-18 18 -45 18t-45 -18l-362 -362l-91 -91q-18 -18 -18 -45t18 -45l91 -91l362 -362q18 -18 45 -18t45 18l91 91q18 18 18 45t-18 45l-189 189h502q26 0 45 19t19 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1285 640q0 27 -18 45l-91 91l-362 362q-18 18 -45 18t-45 -18l-91 -91q-18 -18 -18 -45t18 -45l189 -189h-502q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h502l-189 -189q-19 -19 -19 -45t19 -45l91 -91q18 -18 45 -18t45 18l362 362l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1284 641q0 27 -18 45l-362 362l-91 91q-18 18 -45 18t-45 -18l-91 -91l-362 -362q-18 -18 -18 -45t18 -45l91 -91q18 -18 45 -18t45 18l189 189v-502q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v502l189 -189q19 -19 45 -19t45 19l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1284 639q0 27 -18 45l-91 91q-18 18 -45 18t-45 -18l-189 -189v502q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-502l-189 189q-19 19 -45 19t-45 -19l-91 -91q-18 -18 -18 -45t18 -45l362 -362l91 -91q18 -18 45 -18t45 18l91 91l362 362q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1042 887q-2 -1 -9.5 -9.5t-13.5 -9.5q2 0 4.5 5t5 11t3.5 7q6 7 22 15q14 6 52 12q34 8 51 -11 q-2 2 9.5 13t14.5 12q3 2 15 4.5t15 7.5l2 22q-12 -1 -17.5 7t-6.5 21q0 -2 -6 -8q0 7 -4.5 8t-11.5 -1t-9 -1q-10 3 -15 7.5t-8 16.5t-4 15q-2 5 -9.5 10.5t-9.5 10.5q-1 2 -2.5 5.5t-3 6.5t-4 5.5t-5.5 2.5t-7 -5t-7.5 -10t-4.5 -5q-3 2 -6 1.5t-4.5 -1t-4.5 -3t-5 -3.5 q-3 -2 -8.5 -3t-8.5 -2q15 5 -1 11q-10 4 -16 3q9 4 7.5 12t-8.5 14h5q-1 4 -8.5 8.5t-17.5 8.5t-13 6q-8 5 -34 9.5t-33 0.5q-5 -6 -4.5 -10.5t4 -14t3.5 -12.5q1 -6 -5.5 -13t-6.5 -12q0 -7 14 -15.5t10 -21.5q-3 -8 -16 -16t-16 -12q-5 -8 -1.5 -18.5t10.5 -16.5 q2 -2 1.5 -4t-3.5 -4.5t-5.5 -4t-6.5 -3.5l-3 -2q-11 -5 -20.5 6t-13.5 26q-7 25 -16 30q-23 8 -29 -1q-5 13 -41 26q-25 9 -58 4q6 1 0 15q-7 15 -19 12q3 6 4 17.5t1 13.5q3 13 12 23q1 1 7 8.5t9.5 13.5t0.5 6q35 -4 50 11q5 5 11.5 17t10.5 17q9 6 14 5.5t14.5 -5.5 t14.5 -5q14 -1 15.5 11t-7.5 20q12 -1 3 17q-5 7 -8 9q-12 4 -27 -5q-8 -4 2 -8q-1 1 -9.5 -10.5t-16.5 -17.5t-16 5q-1 1 -5.5 13.5t-9.5 13.5q-8 0 -16 -15q3 8 -11 15t-24 8q19 12 -8 27q-7 4 -20.5 5t-19.5 -4q-5 -7 -5.5 -11.5t5 -8t10.5 -5.5t11.5 -4t8.5 -3 q14 -10 8 -14q-2 -1 -8.5 -3.5t-11.5 -4.5t-6 -4q-3 -4 0 -14t-2 -14q-5 5 -9 17.5t-7 16.5q7 -9 -25 -6l-10 1q-4 0 -16 -2t-20.5 -1t-13.5 8q-4 8 0 20q1 4 4 2q-4 3 -11 9.5t-10 8.5q-46 -15 -94 -41q6 -1 12 1q5 2 13 6.5t10 5.5q34 14 42 7l5 5q14 -16 20 -25 q-7 4 -30 1q-20 -6 -22 -12q7 -12 5 -18q-4 3 -11.5 10t-14.5 11t-15 5q-16 0 -22 -1q-146 -80 -235 -222q7 -7 12 -8q4 -1 5 -9t2.5 -11t11.5 3q9 -8 3 -19q1 1 44 -27q19 -17 21 -21q3 -11 -10 -18q-1 2 -9 9t-9 4q-3 -5 0.5 -18.5t10.5 -12.5q-7 0 -9.5 -16t-2.5 -35.5 t-1 -23.5l2 -1q-3 -12 5.5 -34.5t21.5 -19.5q-13 -3 20 -43q6 -8 8 -9q3 -2 12 -7.5t15 -10t10 -10.5q4 -5 10 -22.5t14 -23.5q-2 -6 9.5 -20t10.5 -23q-1 0 -2.5 -1t-2.5 -1q3 -7 15.5 -14t15.5 -13q1 -3 2 -10t3 -11t8 -2q2 20 -24 62q-15 25 -17 29q-3 5 -5.5 15.5 t-4.5 14.5q2 0 6 -1.5t8.5 -3.5t7.5 -4t2 -3q-3 -7 2 -17.5t12 -18.5t17 -19t12 -13q6 -6 14 -19.5t0 -13.5q9 0 20 -10t17 -20q5 -8 8 -26t5 -24q2 -7 8.5 -13.5t12.5 -9.5l16 -8t13 -7q5 -2 18.5 -10.5t21.5 -11.5q10 -4 16 -4t14.5 2.5t13.5 3.5q15 2 29 -15t21 -21 q36 -19 55 -11q-2 -1 0.5 -7.5t8 -15.5t9 -14.5t5.5 -8.5q5 -6 18 -15t18 -15q6 4 7 9q-3 -8 7 -20t18 -10q14 3 14 32q-31 -15 -49 18q0 1 -2.5 5.5t-4 8.5t-2.5 8.5t0 7.5t5 3q9 0 10 3.5t-2 12.5t-4 13q-1 8 -11 20t-12 15q-5 -9 -16 -8t-16 9q0 -1 -1.5 -5.5t-1.5 -6.5 q-13 0 -15 1q1 3 2.5 17.5t3.5 22.5q1 4 5.5 12t7.5 14.5t4 12.5t-4.5 9.5t-17.5 2.5q-19 -1 -26 -20q-1 -3 -3 -10.5t-5 -11.5t-9 -7q-7 -3 -24 -2t-24 5q-13 8 -22.5 29t-9.5 37q0 10 2.5 26.5t3 25t-5.5 24.5q3 2 9 9.5t10 10.5q2 1 4.5 1.5t4.5 0t4 1.5t3 6q-1 1 -4 3 q-3 3 -4 3q7 -3 28.5 1.5t27.5 -1.5q15 -11 22 2q0 1 -2.5 9.5t-0.5 13.5q5 -27 29 -9q3 -3 15.5 -5t17.5 -5q3 -2 7 -5.5t5.5 -4.5t5 0.5t8.5 6.5q10 -14 12 -24q11 -40 19 -44q7 -3 11 -2t4.5 9.5t0 14t-1.5 12.5l-1 8v18l-1 8q-15 3 -18.5 12t1.5 18.5t15 18.5q1 1 8 3.5 t15.5 6.5t12.5 8q21 19 15 35q7 0 11 9q-1 0 -5 3t-7.5 5t-4.5 2q9 5 2 16q5 3 7.5 11t7.5 10q9 -12 21 -2q7 8 1 16q5 7 20.5 10.5t18.5 9.5q7 -2 8 2t1 12t3 12q4 5 15 9t13 5l17 11q3 4 0 4q18 -2 31 11q10 11 -6 20q3 6 -3 9.5t-15 5.5q3 1 11.5 0.5t10.5 1.5 q15 10 -7 16q-17 5 -43 -12zM879 10q206 36 351 189q-3 3 -12.5 4.5t-12.5 3.5q-18 7 -24 8q1 7 -2.5 13t-8 9t-12.5 8t-11 7q-2 2 -7 6t-7 5.5t-7.5 4.5t-8.5 2t-10 -1l-3 -1q-3 -1 -5.5 -2.5t-5.5 -3t-4 -3t0 -2.5q-21 17 -36 22q-5 1 -11 5.5t-10.5 7t-10 1.5t-11.5 -7 q-5 -5 -6 -15t-2 -13q-7 5 0 17.5t2 18.5q-3 6 -10.5 4.5t-12 -4.5t-11.5 -8.5t-9 -6.5t-8.5 -5.5t-8.5 -7.5q-3 -4 -6 -12t-5 -11q-2 4 -11.5 6.5t-9.5 5.5q2 -10 4 -35t5 -38q7 -31 -12 -48q-27 -25 -29 -40q-4 -22 12 -26q0 -7 -8 -20.5t-7 -21.5q0 -6 2 -16z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5 t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1024 128h640v128h-640v-128zM640 640h1024v128h-1024v-128zM1280 1152h384v128h-384v-128zM1792 320v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 832v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19 t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1403 1241q17 -41 -14 -70l-493 -493v-742q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-256 256q-19 19 -19 45v486l-493 493q-31 29 -14 70q17 39 59 39h1280q42 0 59 -39z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M640 1280h512v128h-512v-128zM1792 640v-480q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v480h672v-160q0 -26 19 -45t45 -19h320q26 0 45 19t19 45v160h672zM1024 640v-128h-256v128h256zM1792 1120v-384h-1792v384q0 66 47 113t113 47h352v160q0 40 28 68 t68 28h576q40 0 68 -28t28 -68v-160h352q66 0 113 -47t47 -113z" />
|
||||
<glyph unicode="" d="M1283 995l-355 -355l355 -355l144 144q29 31 70 14q39 -17 39 -59v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l144 144l-355 355l-355 -355l144 -144q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l144 -144 l355 355l-355 355l-144 -144q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v448q0 26 19 45t45 19h448q42 0 59 -40q17 -39 -14 -69l-144 -144l355 -355l355 355l-144 144q-31 30 -14 69q17 40 59 40h448q26 0 45 -19t19 -45v-448q0 -42 -39 -59q-13 -5 -25 -5q-26 0 -45 19z " />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M593 640q-162 -5 -265 -128h-134q-82 0 -138 40.5t-56 118.5q0 353 124 353q6 0 43.5 -21t97.5 -42.5t119 -21.5q67 0 133 23q-5 -37 -5 -66q0 -139 81 -256zM1664 3q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q10 0 43 -21.5t73 -48t107 -48t135 -21.5t135 21.5t107 48t73 48t43 21.5q61 0 111.5 -20t85.5 -53.5t62 -81t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM640 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75 t75 -181zM1344 896q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5zM1920 671q0 -78 -56 -118.5t-138 -40.5h-134q-103 123 -265 128q81 117 81 256q0 29 -5 66q66 -23 133 -23q59 0 119 21.5t97.5 42.5 t43.5 21q124 0 124 -353zM1792 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1456 320q0 40 -28 68l-208 208q-28 28 -68 28q-42 0 -72 -32q3 -3 19 -18.5t21.5 -21.5t15 -19t13 -25.5t3.5 -27.5q0 -40 -28 -68t-68 -28q-15 0 -27.5 3.5t-25.5 13t-19 15t-21.5 21.5t-18.5 19q-33 -31 -33 -73q0 -40 28 -68l206 -207q27 -27 68 -27q40 0 68 26 l147 146q28 28 28 67zM753 1025q0 40 -28 68l-206 207q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l208 -208q27 -27 68 -27q42 0 72 31q-3 3 -19 18.5t-21.5 21.5t-15 19t-13 25.5t-3.5 27.5q0 40 28 68t68 28q15 0 27.5 -3.5t25.5 -13t19 -15 t21.5 -21.5t18.5 -19q33 31 33 73zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-206 207q-83 83 -83 203q0 123 88 209l-88 88q-86 -88 -208 -88q-120 0 -204 84l-208 208q-84 84 -84 204t85 203l147 146q83 83 203 83q121 0 204 -85l206 -207 q83 -83 83 -203q0 -123 -88 -209l88 -88q86 88 208 88q120 0 204 -84l208 -208q84 -84 84 -204z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088q-185 0 -316.5 131.5t-131.5 316.5q0 132 71 241.5t187 163.5q-2 28 -2 43q0 212 150 362t362 150q158 0 286.5 -88t187.5 -230q70 62 166 62q106 0 181 -75t75 -181q0 -75 -41 -138q129 -30 213 -134.5t84 -239.5z " />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1527 88q56 -89 21.5 -152.5t-140.5 -63.5h-1152q-106 0 -140.5 63.5t21.5 152.5l503 793v399h-64q-26 0 -45 19t-19 45t19 45t45 19h512q26 0 45 -19t19 -45t-19 -45t-45 -19h-64v-399zM748 813l-272 -429h712l-272 429l-20 31v37v399h-128v-399v-37z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M960 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1260 576l507 -398q28 -20 25 -56q-5 -35 -35 -51l-128 -64q-13 -7 -29 -7q-17 0 -31 8l-690 387l-110 -66q-8 -4 -12 -5q14 -49 10 -97q-7 -77 -56 -147.5t-132 -123.5q-132 -84 -277 -84 q-136 0 -222 78q-90 84 -79 207q7 76 56 147t131 124q132 84 278 84q83 0 151 -31q9 13 22 22l122 73l-122 73q-13 9 -22 22q-68 -31 -151 -31q-146 0 -278 84q-82 53 -131 124t-56 147q-5 59 15.5 113t63.5 93q85 79 222 79q145 0 277 -84q83 -52 132 -123t56 -148 q4 -48 -10 -97q4 -1 12 -5l110 -66l690 387q14 8 31 8q16 0 29 -7l128 -64q30 -16 35 -51q3 -36 -25 -56zM579 836q46 42 21 108t-106 117q-92 59 -192 59q-74 0 -113 -36q-46 -42 -21 -108t106 -117q92 -59 192 -59q74 0 113 36zM494 91q81 51 106 117t-21 108 q-39 36 -113 36q-100 0 -192 -59q-81 -51 -106 -117t21 -108q39 -36 113 -36q100 0 192 59zM672 704l96 -58v11q0 36 33 56l14 8l-79 47l-26 -26q-3 -3 -10 -11t-12 -12q-2 -2 -4 -3.5t-3 -2.5zM896 480l96 -32l736 576l-128 64l-768 -431v-113l-160 -96l9 -8q2 -2 7 -6 q4 -4 11 -12t11 -12l26 -26zM1600 64l128 64l-520 408l-177 -138q-2 -3 -13 -7z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1696 1152q40 0 68 -28t28 -68v-1216q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v288h-544q-40 0 -68 28t-28 68v672q0 40 20 88t48 76l408 408q28 28 76 48t88 20h416q40 0 68 -28t28 -68v-328q68 40 128 40h416zM1152 939l-299 -299h299v299zM512 1323l-299 -299 h299v299zM708 676l316 316v416h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h512v256q0 40 20 88t48 76zM1664 -128v1152h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h896z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1404 151q0 -117 -79 -196t-196 -79q-135 0 -235 100l-777 776q-113 115 -113 271q0 159 110 270t269 111q158 0 273 -113l605 -606q10 -10 10 -22q0 -16 -30.5 -46.5t-46.5 -30.5q-13 0 -23 10l-606 607q-79 77 -181 77q-106 0 -179 -75t-73 -181q0 -105 76 -181 l776 -777q63 -63 145 -63q64 0 106 42t42 106q0 82 -63 145l-581 581q-26 24 -60 24q-29 0 -48 -19t-19 -48q0 -32 25 -59l410 -410q10 -10 10 -22q0 -16 -31 -47t-47 -31q-12 0 -22 10l-410 410q-63 61 -63 149q0 82 57 139t139 57q88 0 149 -63l581 -581q100 -98 100 -235 z" />
|
||||
<glyph unicode="" d="M384 0h768v384h-768v-384zM1280 0h128v896q0 14 -10 38.5t-20 34.5l-281 281q-10 10 -34 20t-39 10v-416q0 -40 -28 -68t-68 -28h-576q-40 0 -68 28t-28 68v416h-128v-1280h128v416q0 40 28 68t68 28h832q40 0 68 -28t28 -68v-416zM896 928v320q0 13 -9.5 22.5t-22.5 9.5 h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5zM1536 896v-928q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h928q40 0 88 -20t76 -48l280 -280q28 -28 48 -76t20 -88z" />
|
||||
<glyph unicode="" d="M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1536 192v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 704v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 1216v-128q0 -26 -19 -45 t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M384 128q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 640q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1152q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z M1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M381 -84q0 -80 -54.5 -126t-135.5 -46q-106 0 -172 66l57 88q49 -45 106 -45q29 0 50.5 14.5t21.5 42.5q0 64 -105 56l-26 56q8 10 32.5 43.5t42.5 54t37 38.5v1q-16 0 -48.5 -1t-48.5 -1v-53h-106v152h333v-88l-95 -115q51 -12 81 -49t30 -88zM383 543v-159h-362 q-6 36 -6 54q0 51 23.5 93t56.5 68t66 47.5t56.5 43.5t23.5 45q0 25 -14.5 38.5t-39.5 13.5q-46 0 -81 -58l-85 59q24 51 71.5 79.5t105.5 28.5q73 0 123 -41.5t50 -112.5q0 -50 -34 -91.5t-75 -64.5t-75.5 -50.5t-35.5 -52.5h127v60h105zM1792 224v-192q0 -13 -9.5 -22.5 t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1123v-99h-335v99h107q0 41 0.5 122t0.5 121v12h-2q-8 -17 -50 -54l-71 76l136 127h106v-404h108zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5 t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1760 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h1728zM483 704q-28 35 -51 80q-48 97 -48 188q0 181 134 309q133 127 393 127q50 0 167 -19q66 -12 177 -48q10 -38 21 -118q14 -123 14 -183q0 -18 -5 -45l-12 -3l-84 6 l-14 2q-50 149 -103 205q-88 91 -210 91q-114 0 -182 -59q-67 -58 -67 -146q0 -73 66 -140t279 -129q69 -20 173 -66q58 -28 95 -52h-743zM990 448h411q7 -39 7 -92q0 -111 -41 -212q-23 -55 -71 -104q-37 -35 -109 -81q-80 -48 -153 -66q-80 -21 -203 -21q-114 0 -195 23 l-140 40q-57 16 -72 28q-8 8 -8 22v13q0 108 -2 156q-1 30 0 68l2 37v44l102 2q15 -34 30 -71t22.5 -56t12.5 -27q35 -57 80 -94q43 -36 105 -57q59 -22 132 -22q64 0 139 27q77 26 122 86q47 61 47 129q0 84 -81 157q-34 29 -137 71z" />
|
||||
<glyph unicode="" d="M48 1313q-37 2 -45 4l-3 88q13 1 40 1q60 0 112 -4q132 -7 166 -7q86 0 168 3q116 4 146 5q56 0 86 2l-1 -14l2 -64v-9q-60 -9 -124 -9q-60 0 -79 -25q-13 -14 -13 -132q0 -13 0.5 -32.5t0.5 -25.5l1 -229l14 -280q6 -124 51 -202q35 -59 96 -92q88 -47 177 -47 q104 0 191 28q56 18 99 51q48 36 65 64q36 56 53 114q21 73 21 229q0 79 -3.5 128t-11 122.5t-13.5 159.5l-4 59q-5 67 -24 88q-34 35 -77 34l-100 -2l-14 3l2 86h84l205 -10q76 -3 196 10l18 -2q6 -38 6 -51q0 -7 -4 -31q-45 -12 -84 -13q-73 -11 -79 -17q-15 -15 -15 -41 q0 -7 1.5 -27t1.5 -31q8 -19 22 -396q6 -195 -15 -304q-15 -76 -41 -122q-38 -65 -112 -123q-75 -57 -182 -89q-109 -33 -255 -33q-167 0 -284 46q-119 47 -179 122q-61 76 -83 195q-16 80 -16 237v333q0 188 -17 213q-25 36 -147 39zM1536 -96v64q0 14 -9 23t-23 9h-1472 q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h1472q14 0 23 9t9 23z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M512 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23 v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 160v192 q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192 q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1664 1248v-1088q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1344q66 0 113 -47t47 -113 z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1190 955l293 293l-107 107l-293 -293zM1637 1248q0 -27 -18 -45l-1286 -1286q-18 -18 -45 -18t-45 18l-198 198q-18 18 -18 45t18 45l1286 1286q18 18 45 18t45 -18l198 -198q18 -18 18 -45zM286 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM636 1276 l196 -60l-196 -60l-60 -196l-60 196l-196 60l196 60l60 196zM1566 798l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM926 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
|
||||
<glyph unicode="" d="M678 -57q0 -38 -10 -71h-380q-95 0 -171.5 56.5t-103.5 147.5q24 45 69 77.5t100 49.5t107 24t107 7q32 0 49 -2q6 -4 30.5 -21t33 -23t31 -23t32 -25.5t27.5 -25.5t26.5 -29.5t21 -30.5t17.5 -34.5t9.5 -36t4.5 -40.5zM385 294q-234 -7 -385 -85v433q103 -118 273 -118 q32 0 70 5q-21 -61 -21 -86q0 -67 63 -149zM558 805q0 -100 -43.5 -160.5t-140.5 -60.5q-51 0 -97 26t-78 67.5t-56 93.5t-35.5 104t-11.5 99q0 96 51.5 165t144.5 69q66 0 119 -41t84 -104t47 -130t16 -128zM1536 896v-736q0 -119 -84.5 -203.5t-203.5 -84.5h-468 q39 73 39 157q0 66 -22 122.5t-55.5 93t-72 71t-72 59.5t-55.5 54.5t-22 59.5q0 36 23 68t56 61.5t65.5 64.5t55.5 93t23 131t-26.5 145.5t-75.5 118.5q-6 6 -14 11t-12.5 7.5t-10 9.5t-10.5 17h135l135 64h-437q-138 0 -244.5 -38.5t-182.5 -133.5q0 126 81 213t207 87h960 q119 0 203.5 -84.5t84.5 -203.5v-96h-256v256h-128v-256h-256v-128h256v-256h128v256h256z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M876 71q0 21 -4.5 40.5t-9.5 36t-17.5 34.5t-21 30.5t-26.5 29.5t-27.5 25.5t-32 25.5t-31 23t-33 23t-30.5 21q-17 2 -50 2q-54 0 -106 -7t-108 -25t-98 -46t-69 -75t-27 -107q0 -68 35.5 -121.5t93 -84t120.5 -45.5t127 -15q59 0 112.5 12.5t100.5 39t74.5 73.5 t27.5 110zM756 933q0 60 -16.5 127.5t-47 130.5t-84 104t-119.5 41q-93 0 -144 -69t-51 -165q0 -47 11.5 -99t35.5 -104t56 -93.5t78 -67.5t97 -26q97 0 140.5 60.5t43.5 160.5zM625 1408h437l-135 -79h-135q71 -45 110 -126t39 -169q0 -74 -23 -131.5t-56 -92.5t-66 -64.5 t-56 -61t-23 -67.5q0 -26 16.5 -51t43 -48t58.5 -48t64 -55.5t58.5 -66t43 -85t16.5 -106.5q0 -160 -140 -282q-152 -131 -420 -131q-59 0 -119.5 10t-122 33.5t-108.5 58t-77 89t-30 121.5q0 61 37 135q32 64 96 110.5t145 71t155 36t150 13.5q-64 83 -64 149q0 12 2 23.5 t5 19.5t8 21.5t7 21.5q-40 -5 -70 -5q-149 0 -255.5 98t-106.5 246q0 140 95 250.5t234 141.5q94 20 187 20zM1664 1152v-128h-256v-256h-128v256h-256v128h256v256h128v-256h256z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="640" d="M640 1088v-896q0 -26 -19 -45t-45 -19t-45 19l-448 448q-19 19 -19 45t19 45l448 448q19 19 45 19t45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="640" d="M576 640q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19t-19 45v896q0 26 19 45t45 19t45 -19l448 -448q19 -19 19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M160 0h608v1152h-640v-1120q0 -13 9.5 -22.5t22.5 -9.5zM1536 32v1120h-640v-1152h608q13 0 22.5 9.5t9.5 22.5zM1664 1248v-1216q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1344q66 0 113 -47t47 -113z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45zM1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 826v-794q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v794q44 -49 101 -87q362 -246 497 -345q57 -42 92.5 -65.5t94.5 -48t110 -24.5h1h1q51 0 110 24.5t94.5 48t92.5 65.5q170 123 498 345q57 39 100 87zM1792 1120q0 -79 -49 -151t-122 -123 q-376 -261 -468 -325q-10 -7 -42.5 -30.5t-54 -38t-52 -32.5t-57.5 -27t-50 -9h-1h-1q-23 0 -50 9t-57.5 27t-52 32.5t-54 38t-42.5 30.5q-91 64 -262 182.5t-205 142.5q-62 42 -117 115.5t-55 136.5q0 78 41.5 130t118.5 52h1472q65 0 112.5 -47t47.5 -113z" />
|
||||
<glyph unicode="" d="M349 911v-991h-330v991h330zM370 1217q1 -73 -50.5 -122t-135.5 -49h-2q-82 0 -132 49t-50 122q0 74 51.5 122.5t134.5 48.5t133 -48.5t51 -122.5zM1536 488v-568h-329v530q0 105 -40.5 164.5t-126.5 59.5q-63 0 -105.5 -34.5t-63.5 -85.5q-11 -30 -11 -81v-553h-329 q2 399 2 647t-1 296l-1 48h329v-144h-2q20 32 41 56t56.5 52t87 43.5t114.5 15.5q171 0 275 -113.5t104 -332.5z" />
|
||||
<glyph unicode="" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1771 0q0 -53 -37 -90l-107 -108q-39 -37 -91 -37q-53 0 -90 37l-363 364q-38 36 -38 90q0 53 43 96l-256 256l-126 -126q-14 -14 -34 -14t-34 14q2 -2 12.5 -12t12.5 -13t10 -11.5t10 -13.5t6 -13.5t5.5 -16.5t1.5 -18q0 -38 -28 -68q-3 -3 -16.5 -18t-19 -20.5 t-18.5 -16.5t-22 -15.5t-22 -9t-26 -4.5q-40 0 -68 28l-408 408q-28 28 -28 68q0 13 4.5 26t9 22t15.5 22t16.5 18.5t20.5 19t18 16.5q30 28 68 28q10 0 18 -1.5t16.5 -5.5t13.5 -6t13.5 -10t11.5 -10t13 -12.5t12 -12.5q-14 14 -14 34t14 34l348 348q14 14 34 14t34 -14 q-2 2 -12.5 12t-12.5 13t-10 11.5t-10 13.5t-6 13.5t-5.5 16.5t-1.5 18q0 38 28 68q3 3 16.5 18t19 20.5t18.5 16.5t22 15.5t22 9t26 4.5q40 0 68 -28l408 -408q28 -28 28 -68q0 -13 -4.5 -26t-9 -22t-15.5 -22t-16.5 -18.5t-20.5 -19t-18 -16.5q-30 -28 -68 -28 q-10 0 -18 1.5t-16.5 5.5t-13.5 6t-13.5 10t-11.5 10t-13 12.5t-12 12.5q14 -14 14 -34t-14 -34l-126 -126l256 -256q43 43 96 43q52 0 91 -37l363 -363q37 -39 37 -91z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM576 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1004 351l101 382q6 26 -7.5 48.5t-38.5 29.5 t-48 -6.5t-30 -39.5l-101 -382q-60 -5 -107 -43.5t-63 -98.5q-20 -77 20 -146t117 -89t146 20t89 117q16 60 -6 117t-72 91zM1664 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 1024q0 53 -37.5 90.5 t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1472 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 384q0 -261 -141 -483q-19 -29 -54 -29h-1402q-35 0 -54 29 q-141 221 -141 483q0 182 71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640 q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 174 120 321.5 t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M704 1152q-153 0 -286 -52t-211.5 -141t-78.5 -191q0 -82 53 -158t149 -132l97 -56l-35 -84q34 20 62 39l44 31l53 -10q78 -14 153 -14q153 0 286 52t211.5 141t78.5 191t-78.5 191t-211.5 141t-286 52zM704 1280q191 0 353.5 -68.5t256.5 -186.5t94 -257t-94 -257 t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224q0 139 94 257t256.5 186.5 t353.5 68.5zM1526 111q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129 q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230q0 -120 -71 -224.5t-195 -176.5z" />
|
||||
<glyph unicode="" horiz-adv-x="896" d="M885 970q18 -20 7 -44l-540 -1157q-13 -25 -42 -25q-4 0 -14 2q-17 5 -25.5 19t-4.5 30l197 808l-406 -101q-4 -1 -12 -1q-18 0 -31 11q-18 15 -13 39l201 825q4 14 16 23t28 9h328q19 0 32 -12.5t13 -29.5q0 -8 -5 -18l-171 -463l396 98q8 2 12 2q19 0 34 -15z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 288v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192q0 52 38 90t90 38h512v192h-96q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h320q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-96v-192h512q52 0 90 -38t38 -90v-192h96q40 0 68 -28t28 -68 z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M896 708v-580q0 -104 -76 -180t-180 -76t-180 76t-76 180q0 26 19 45t45 19t45 -19t19 -45q0 -50 39 -89t89 -39t89 39t39 89v580q33 11 64 11t64 -11zM1664 681q0 -13 -9.5 -22.5t-22.5 -9.5q-11 0 -23 10q-49 46 -93 69t-102 23q-68 0 -128 -37t-103 -97 q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -28 -17q-18 0 -29 17q-4 6 -14.5 24t-17.5 28q-43 60 -102.5 97t-127.5 37t-127.5 -37t-102.5 -97q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -29 -17q-17 0 -28 17q-4 6 -14.5 24t-17.5 28q-43 60 -103 97t-128 37q-58 0 -102 -23t-93 -69 q-12 -10 -23 -10q-13 0 -22.5 9.5t-9.5 22.5q0 5 1 7q45 183 172.5 319.5t298 204.5t360.5 68q140 0 274.5 -40t246.5 -113.5t194.5 -187t115.5 -251.5q1 -2 1 -7zM896 1408v-98q-42 2 -64 2t-64 -2v98q0 26 19 45t45 19t45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M768 -128h896v640h-416q-40 0 -68 28t-28 68v416h-384v-1152zM1024 1312v64q0 13 -9.5 22.5t-22.5 9.5h-704q-13 0 -22.5 -9.5t-9.5 -22.5v-64q0 -13 9.5 -22.5t22.5 -9.5h704q13 0 22.5 9.5t9.5 22.5zM1280 640h299l-299 299v-299zM1792 512v-672q0 -40 -28 -68t-68 -28 h-960q-40 0 -68 28t-28 68v160h-544q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1088q40 0 68 -28t28 -68v-328q21 -13 36 -28l408 -408q28 -28 48 -76t20 -88z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M736 960q0 -13 -9.5 -22.5t-22.5 -9.5t-22.5 9.5t-9.5 22.5q0 46 -54 71t-106 25q-13 0 -22.5 9.5t-9.5 22.5t9.5 22.5t22.5 9.5q50 0 99.5 -16t87 -54t37.5 -90zM896 960q0 72 -34.5 134t-90 101.5t-123 62t-136.5 22.5t-136.5 -22.5t-123 -62t-90 -101.5t-34.5 -134 q0 -101 68 -180q10 -11 30.5 -33t30.5 -33q128 -153 141 -298h228q13 145 141 298q10 11 30.5 33t30.5 33q68 79 68 180zM1024 960q0 -155 -103 -268q-45 -49 -74.5 -87t-59.5 -95.5t-34 -107.5q47 -28 47 -82q0 -37 -25 -64q25 -27 25 -64q0 -52 -45 -81q13 -23 13 -47 q0 -46 -31.5 -71t-77.5 -25q-20 -44 -60 -70t-87 -26t-87 26t-60 70q-46 0 -77.5 25t-31.5 71q0 24 13 47q-45 29 -45 81q0 37 25 64q-25 27 -25 64q0 54 47 82q-4 50 -34 107.5t-59.5 95.5t-74.5 87q-103 113 -103 268q0 99 44.5 184.5t117 142t164 89t186.5 32.5 t186.5 -32.5t164 -89t117 -142t44.5 -184.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 352v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5q-12 0 -24 10l-319 320q-9 9 -9 22q0 14 9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h1376q13 0 22.5 -9.5t9.5 -22.5zM1792 896q0 -14 -9 -23l-320 -320q-9 -9 -23 -9 q-13 0 -22.5 9.5t-9.5 22.5v192h-1376q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1376v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1280 608q0 14 -9 23t-23 9h-224v352q0 13 -9.5 22.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-352h-224q-13 0 -22.5 -9.5t-9.5 -22.5q0 -14 9 -23l352 -352q9 -9 23 -9t23 9l351 351q10 12 10 24zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1280 672q0 14 -9 23l-352 352q-9 9 -23 9t-23 -9l-351 -351q-10 -12 -10 -24q0 -14 9 -23t23 -9h224v-352q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5v352h224q13 0 22.5 9.5t9.5 22.5zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M384 192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 68 5.5 131t24 138t47.5 132.5t81 103t120 60.5q-22 -52 -22 -120v-203q-58 -20 -93 -70t-35 -111q0 -80 56 -136t136 -56 t136 56t56 136q0 61 -35.5 111t-92.5 70v203q0 62 25 93q132 -104 295 -104t295 104q25 -31 25 -93v-64q-106 0 -181 -75t-75 -181v-89q-32 -29 -32 -71q0 -40 28 -68t68 -28t68 28t28 68q0 42 -32 71v89q0 52 38 90t90 38t90 -38t38 -90v-89q-32 -29 -32 -71q0 -40 28 -68 t68 -28t68 28t28 68q0 42 -32 71v89q0 68 -34.5 127.5t-93.5 93.5q0 10 0.5 42.5t0 48t-2.5 41.5t-7 47t-13 40q68 -15 120 -60.5t81 -103t47.5 -132.5t24 -138t5.5 -131zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1280 832q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 832q0 -62 -35.5 -111t-92.5 -70v-395q0 -159 -131.5 -271.5t-316.5 -112.5t-316.5 112.5t-131.5 271.5v132q-164 20 -274 128t-110 252v512q0 26 19 45t45 19q6 0 16 -2q17 30 47 48 t65 18q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5q-33 0 -64 18v-402q0 -106 94 -181t226 -75t226 75t94 181v402q-31 -18 -64 -18q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5q35 0 65 -18t47 -48q10 2 16 2q26 0 45 -19t19 -45v-512q0 -144 -110 -252 t-274 -128v-132q0 -106 94 -181t226 -75t226 75t94 181v395q-57 21 -92.5 70t-35.5 111q0 80 56 136t136 56t136 -56t56 -136z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M640 1152h512v128h-512v-128zM288 1152v-1280h-64q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h64zM1408 1152v-1280h-1024v1280h128v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h128zM1792 928v-832q0 -92 -66 -158t-158 -66h-64v1280h64q92 0 158 -66 t66 -158z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M848 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1664 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q190 161 287 397.5t97 498.5 q0 165 96 262t264 117q-8 18 -8 37q0 40 28 68t68 28t68 -28t28 -68q0 -19 -8 -37q168 -20 264 -117t96 -262q0 -262 97 -498.5t287 -397.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1664 896q0 80 -56 136t-136 56h-64v-384h64q80 0 136 56t56 136zM0 128h1792q0 -106 -75 -181t-181 -75h-1280q-106 0 -181 75t-75 181zM1856 896q0 -159 -112.5 -271.5t-271.5 -112.5h-64v-32q0 -92 -66 -158t-158 -66h-704q-92 0 -158 66t-66 158v736q0 26 19 45 t45 19h1152q159 0 271.5 -112.5t112.5 -271.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M640 1472v-640q0 -61 -35.5 -111t-92.5 -70v-779q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v779q-57 20 -92.5 70t-35.5 111v640q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45 t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45zM1408 1472v-1600q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v512h-224q-13 0 -22.5 9.5t-9.5 22.5v800q0 132 94 226t226 94h256q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1024 352v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM1024 608v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM128 0h1024v768h-416q-40 0 -68 28t-28 68v416h-512v-1280z M768 896h376q-10 29 -22 41l-313 313q-12 12 -41 22v-376zM1280 864v-896q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h640q40 0 88 -20t76 -48l312 -312q28 -28 48 -76t20 -88z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1536h-1152v-1536h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM1408 1472v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1152h-256v-32q0 -40 -28 -68t-68 -28h-448q-40 0 -68 28t-28 68v32h-256v-1152h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM896 1056v320q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-96h-128v96q0 13 -9.5 22.5 t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5v96h128v-96q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1408 1088v-1280q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1280q0 26 19 45t45 19h320 v288q0 40 28 68t68 28h448q40 0 68 -28t28 -68v-288h320q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M640 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM256 640h384v256h-158q-14 -2 -22 -9l-195 -195q-7 -12 -9 -22v-30zM1536 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1664 800v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM1920 1344v-1152 q0 -26 -19 -45t-45 -19h-192q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-128q-26 0 -45 19t-19 45t19 45t45 19v416q0 26 13 58t32 51l198 198q19 19 51 32t58 13h160v320q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 416v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM640 1152h512v128h-512v-128zM256 1152v-1280h-32 q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h32zM1440 1152v-1280h-1088v1280h160v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h160zM1792 928v-832q0 -92 -66 -158t-158 -66h-32v1280h32q92 0 158 -66t66 -158z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1920 576q-1 -32 -288 -96l-352 -32l-224 -64h-64l-293 -352h69q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-96h-160h-64v32h64v416h-160l-192 -224h-96l-32 32v192h32v32h128v8l-192 24v128l192 24v8h-128v32h-32v192l32 32h96l192 -224h160v416h-64v32h64h160h96 q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-69l293 -352h64l224 -64l352 -32q261 -58 287 -93z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M640 640v384h-256v-256q0 -53 37.5 -90.5t90.5 -37.5h128zM1664 192v-192h-1152v192l128 192h-128q-159 0 -271.5 112.5t-112.5 271.5v320l-64 64l32 128h480l32 128h960l32 -192l-64 -32v-800z" />
|
||||
<glyph unicode="" d="M1280 192v896q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-512v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-896q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h512v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M627 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23zM1011 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM979 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23 l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M1075 224q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM1075 608q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393 q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M1075 672q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23zM1075 1056q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="640" d="M627 992q0 -13 -10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="640" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M1075 352q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M1075 800q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1792 544v832q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1376v-1088q0 -66 -47 -113t-113 -47h-544q0 -37 16 -77.5t32 -71t16 -43.5q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19 t-19 45q0 14 16 44t32 70t16 78h-544q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M416 256q-66 0 -113 47t-47 113v704q0 66 47 113t113 47h1088q66 0 113 -47t47 -113v-704q0 -66 -47 -113t-113 -47h-1088zM384 1120v-704q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5v704q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5z M1760 192h160v-96q0 -40 -47 -68t-113 -28h-1600q-66 0 -113 28t-47 68v96h160h1600zM1040 96q16 0 16 16t-16 16h-160q-16 0 -16 -16t16 -16h160z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M640 128q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1024 288v960q0 13 -9.5 22.5t-22.5 9.5h-832q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h832q13 0 22.5 9.5t9.5 22.5zM1152 1248v-1088q0 -66 -47 -113t-113 -47h-832 q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h832q66 0 113 -47t47 -113z" />
|
||||
<glyph unicode="" horiz-adv-x="768" d="M464 128q0 33 -23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5t56.5 23.5t23.5 56.5zM672 288v704q0 13 -9.5 22.5t-22.5 9.5h-512q-13 0 -22.5 -9.5t-9.5 -22.5v-704q0 -13 9.5 -22.5t22.5 -9.5h512q13 0 22.5 9.5t9.5 22.5zM480 1136 q0 16 -16 16h-160q-16 0 -16 -16t16 -16h160q16 0 16 16zM768 1152v-1024q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v1024q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" />
|
||||
<glyph unicode="" horiz-adv-x="1568" d="M496 192q0 -60 -42.5 -102t-101.5 -42q-60 0 -102 42t-42 102t42 102t102 42q59 0 101.5 -42t42.5 -102zM928 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -66 -47 -113t-113 -47t-113 47t-47 113 t47 113t113 47t113 -47t47 -113zM1360 192q0 -46 -33 -79t-79 -33t-79 33t-33 79t33 79t79 33t79 -33t33 -79zM528 1088q0 -73 -51.5 -124.5t-124.5 -51.5t-124.5 51.5t-51.5 124.5t51.5 124.5t124.5 51.5t124.5 -51.5t51.5 -124.5zM992 1280q0 -80 -56 -136t-136 -56 t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1536 640q0 -40 -28 -68t-68 -28t-68 28t-28 68t28 68t68 28t68 -28t28 -68zM1328 1088q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5z" />
|
||||
<glyph unicode="" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 q0 -87 -27 -168q136 -160 136 -398z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1536 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68v-960q0 -40 28 -68t68 -28h1216q40 0 68 28t28 68zM1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320 q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1781 605q0 35 -53 35h-1088q-40 0 -85.5 -21.5t-71.5 -52.5l-294 -363q-18 -24 -18 -40q0 -35 53 -35h1088q40 0 86 22t71 53l294 363q18 22 18 39zM640 768h768v160q0 40 -28 68t-68 28h-576q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68 v-853l256 315q44 53 116 87.5t140 34.5zM1909 605q0 -62 -46 -120l-295 -363q-43 -53 -116 -87.5t-140 -34.5h-1088q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158v-160h192q54 0 99 -24.5t67 -70.5q15 -32 15 -68z " />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" d="M1134 461q-37 -121 -138 -195t-228 -74t-228 74t-138 195q-8 25 4 48.5t38 31.5q25 8 48.5 -4t31.5 -38q25 -80 92.5 -129.5t151.5 -49.5t151.5 49.5t92.5 129.5q8 26 32 38t49 4t37 -31.5t4 -48.5zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5 t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1134 307q8 -25 -4 -48.5t-37 -31.5t-49 4t-32 38q-25 80 -92.5 129.5t-151.5 49.5t-151.5 -49.5t-92.5 -129.5q-8 -26 -31.5 -38t-48.5 -4q-26 8 -38 31.5t-4 48.5q37 121 138 195t228 74t228 -74t138 -195zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204 t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1152 448q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h640q26 0 45 -19t19 -45zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M832 448v128q0 14 -9 23t-23 9h-192v192q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-192h-192q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h192v-192q0 -14 9 -23t23 -9h128q14 0 23 9t9 23v192h192q14 0 23 9t9 23zM1408 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1920 512q0 -212 -150 -362t-362 -150q-192 0 -338 128h-220q-146 -128 -338 -128q-212 0 -362 150 t-150 362t150 362t362 150h896q212 0 362 -150t150 -362z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M384 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM512 624v-96q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h224q16 0 16 -16zM384 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 368v-96q0 -16 -16 -16 h-864q-16 0 -16 16v96q0 16 16 16h864q16 0 16 -16zM768 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM640 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1024 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16 h96q16 0 16 -16zM896 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1280 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1152 880v-96 q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 880v-352q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h112v240q0 16 16 16h96q16 0 16 -16zM1792 128v896h-1664v-896 h1664zM1920 1024v-896q0 -53 -37.5 -90.5t-90.5 -37.5h-1664q-53 0 -90.5 37.5t-37.5 90.5v896q0 53 37.5 90.5t90.5 37.5h1664q53 0 90.5 -37.5t37.5 -90.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 491v616q-169 -91 -306 -91q-82 0 -145 32q-100 49 -184 76.5t-178 27.5q-173 0 -403 -127v-599q245 113 433 113q55 0 103.5 -7.5t98 -26t77 -31t82.5 -39.5l28 -14q44 -22 101 -22q120 0 293 92zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9 h-64q-14 0 -23 9t-9 23v1266q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102 q-15 -9 -33 -9q-16 0 -32 8q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M832 536v192q-181 -16 -384 -117v-185q205 96 384 110zM832 954v197q-172 -8 -384 -126v-189q215 111 384 118zM1664 491v184q-235 -116 -384 -71v224q-20 6 -39 15q-5 3 -33 17t-34.5 17t-31.5 15t-34.5 15.5t-32.5 13t-36 12.5t-35 8.5t-39.5 7.5t-39.5 4t-44 2 q-23 0 -49 -3v-222h19q102 0 192.5 -29t197.5 -82q19 -9 39 -15v-188q42 -17 91 -17q120 0 293 92zM1664 918v189q-169 -91 -306 -91q-45 0 -78 8v-196q148 -42 384 90zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v1266 q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102q-15 -9 -33 -9q-16 0 -32 8 q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M585 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23zM1664 96v-64q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h960q14 0 23 -9 t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M617 137l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23zM1208 1204l-373 -1291q-4 -13 -15.5 -19.5t-23.5 -2.5l-62 17q-13 4 -19.5 15.5t-2.5 24.5 l373 1291q4 13 15.5 19.5t23.5 2.5l62 -17q13 -4 19.5 -15.5t2.5 -24.5zM1865 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M640 454v-70q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-69l-397 -398q-19 -19 -19 -45t19 -45zM1792 416q0 -58 -17 -133.5t-38.5 -138t-48 -125t-40.5 -90.5l-20 -40q-8 -17 -28 -17q-6 0 -9 1 q-25 8 -23 34q43 400 -106 565q-64 71 -170.5 110.5t-267.5 52.5v-251q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-262q411 -28 599 -221q169 -173 169 -509z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1186 579l257 250l-356 52l-66 10l-30 60l-159 322v-963l59 -31l318 -168l-60 355l-12 66zM1638 841l-363 -354l86 -500q5 -33 -6 -51.5t-34 -18.5q-17 0 -40 12l-449 236l-449 -236q-23 -12 -40 -12q-23 0 -34 18.5t-6 51.5l86 500l-364 354q-32 32 -23 59.5t54 34.5 l502 73l225 455q20 41 49 41q28 0 49 -41l225 -455l502 -73q45 -7 54 -34.5t-24 -59.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1401 1187l-640 -1280q-17 -35 -57 -35q-5 0 -15 2q-22 5 -35.5 22.5t-13.5 39.5v576h-576q-22 0 -39.5 13.5t-22.5 35.5t4 42t29 30l1280 640q13 7 29 7q27 0 45 -19q15 -14 18.5 -34.5t-6.5 -39.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M557 256h595v595zM512 301l595 595h-595v-595zM1664 224v-192q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v224h-864q-14 0 -23 9t-9 23v864h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224v224q0 14 9 23t23 9h192q14 0 23 -9t9 -23 v-224h851l246 247q10 9 23 9t23 -9q9 -10 9 -23t-9 -23l-247 -246v-851h224q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M288 64q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM288 1216q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM928 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1024 1088q0 -52 -26 -96.5t-70 -69.5 q-2 -287 -226 -414q-68 -38 -203 -81q-128 -40 -169.5 -71t-41.5 -100v-26q44 -25 70 -69.5t26 -96.5q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 52 26 96.5t70 69.5v820q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136q0 -52 -26 -96.5t-70 -69.5v-497 q54 26 154 57q55 17 87.5 29.5t70.5 31t59 39.5t40.5 51t28 69.5t8.5 91.5q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M439 265l-256 -256q-10 -9 -23 -9q-12 0 -23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23zM608 224v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM384 448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23t9 23t23 9h320 q14 0 23 -9t9 -23zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-334 335q-21 21 -42 56l239 18l273 -274q27 -27 68 -27.5t68 26.5l147 146q28 28 28 67q0 40 -28 68l-274 275l18 239q35 -21 56 -42l336 -336q84 -86 84 -204zM1031 1044l-239 -18 l-273 274q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l274 -274l-18 -240q-35 21 -56 42l-336 336q-84 86 -84 204q0 120 85 203l147 146q83 83 203 83q121 0 204 -85l334 -335q21 -21 42 -56zM1664 960q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9 t-9 23t9 23t23 9h320q14 0 23 -9t9 -23zM1120 1504v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM1527 1353l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M704 280v-240q0 -16 -12 -28t-28 -12h-240q-16 0 -28 12t-12 28v240q0 16 12 28t28 12h240q16 0 28 -12t12 -28zM1020 880q0 -54 -15.5 -101t-35 -76.5t-55 -59.5t-57.5 -43.5t-61 -35.5q-41 -23 -68.5 -65t-27.5 -67q0 -17 -12 -32.5t-28 -15.5h-240q-15 0 -25.5 18.5 t-10.5 37.5v45q0 83 65 156.5t143 108.5q59 27 84 56t25 76q0 42 -46.5 74t-107.5 32q-65 0 -108 -29q-35 -25 -107 -115q-13 -16 -31 -16q-12 0 -25 8l-164 125q-13 10 -15.5 25t5.5 28q160 266 464 266q80 0 161 -31t146 -83t106 -127.5t41 -158.5z" />
|
||||
<glyph unicode="" horiz-adv-x="640" d="M640 192v-128q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64v384h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-576h64q26 0 45 -19t19 -45zM512 1344v-192q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v192 q0 26 19 45t45 19h256q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="640" d="M512 288v-224q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v224q0 26 19 45t45 19h256q26 0 45 -19t19 -45zM542 1344l-28 -768q-1 -26 -20.5 -45t-45.5 -19h-256q-26 0 -45.5 19t-20.5 45l-28 768q-1 26 17.5 45t44.5 19h320q26 0 44.5 -19t17.5 -45z" />
|
||||
<glyph unicode="" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1534 846v-206h-514l-3 27 q-4 28 -4 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q83 65 188 65q110 0 178 -59.5t68 -158.5q0 -56 -24.5 -103t-62 -76.5t-81.5 -58.5t-82 -50.5t-65.5 -51.5t-30.5 -63h232v80 h126z" />
|
||||
<glyph unicode="" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1536 -50v-206h-514l-4 27 q-3 45 -3 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q80 65 188 65q110 0 178 -59.5t68 -158.5q0 -66 -34.5 -118.5t-84 -86t-99.5 -62.5t-87 -63t-41 -73h232v80h126z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M896 128l336 384h-768l-336 -384h768zM1909 1205q15 -34 9.5 -71.5t-30.5 -65.5l-896 -1024q-38 -44 -96 -44h-768q-38 0 -69.5 20.5t-47.5 54.5q-15 34 -9.5 71.5t30.5 65.5l896 1024q38 44 96 44h768q38 0 69.5 -20.5t47.5 -54.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 438q0 -81 -44.5 -135t-123.5 -54q-41 0 -77.5 17.5t-59 38t-56.5 38t-71 17.5q-110 0 -110 -124q0 -39 16 -115t15 -115v-5q-22 0 -33 -1q-34 -3 -97.5 -11.5t-115.5 -13.5t-98 -5q-61 0 -103 26.5t-42 83.5q0 37 17.5 71t38 56.5t38 59t17.5 77.5q0 79 -54 123.5 t-135 44.5q-84 0 -143 -45.5t-59 -127.5q0 -43 15 -83t33.5 -64.5t33.5 -53t15 -50.5q0 -45 -46 -89q-37 -35 -117 -35q-95 0 -245 24q-9 2 -27.5 4t-27.5 4l-13 2q-1 0 -3 1q-2 0 -2 1v1024q2 -1 17.5 -3.5t34 -5t21.5 -3.5q150 -24 245 -24q80 0 117 35q46 44 46 89 q0 22 -15 50.5t-33.5 53t-33.5 64.5t-15 83q0 82 59 127.5t144 45.5q80 0 134 -44.5t54 -123.5q0 -41 -17.5 -77.5t-38 -59t-38 -56.5t-17.5 -71q0 -57 42 -83.5t103 -26.5q64 0 180 15t163 17v-2q-1 -2 -3.5 -17.5t-5 -34t-3.5 -21.5q-24 -150 -24 -245q0 -80 35 -117 q44 -46 89 -46q22 0 50.5 15t53 33.5t64.5 33.5t83 15q82 0 127.5 -59t45.5 -143z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M1152 832v-128q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-217 24 -364.5 187.5t-147.5 384.5v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -185 131.5 -316.5t316.5 -131.5 t316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45zM896 1216v-512q0 -132 -94 -226t-226 -94t-226 94t-94 226v512q0 132 94 226t226 94t226 -94t94 -226z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M271 591l-101 -101q-42 103 -42 214v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -53 15 -113zM1385 1193l-361 -361v-128q0 -132 -94 -226t-226 -94q-55 0 -109 19l-96 -96q97 -51 205 -51q185 0 316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45v-128 q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-125 13 -235 81l-254 -254q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l1234 1234q10 10 23 10t23 -10l82 -82q10 -10 10 -23 t-10 -23zM1005 1325l-621 -621v512q0 132 94 226t226 94q102 0 184.5 -59t116.5 -152z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1088 576v640h-448v-1137q119 63 213 137q235 184 235 360zM1280 1344v-768q0 -86 -33.5 -170.5t-83 -150t-118 -127.5t-126.5 -103t-121 -77.5t-89.5 -49.5t-42.5 -20q-12 -6 -26 -6t-26 6q-16 7 -42.5 20t-89.5 49.5t-121 77.5t-126.5 103t-118 127.5t-83 150 t-33.5 170.5v768q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M512 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 1376v-320q0 -16 -12 -25q-8 -7 -20 -7q-4 0 -7 1l-448 96q-11 2 -18 11t-7 20h-256v-102q111 -23 183.5 -111t72.5 -203v-800q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v800 q0 106 62.5 190.5t161.5 114.5v111h-32q-59 0 -115 -23.5t-91.5 -53t-66 -66.5t-40.5 -53.5t-14 -24.5q-17 -35 -57 -35q-16 0 -29 7q-23 12 -31.5 37t3.5 49q5 10 14.5 26t37.5 53.5t60.5 70t85 67t108.5 52.5q-25 42 -25 86q0 66 47 113t113 47t113 -47t47 -113 q0 -33 -14 -64h302q0 11 7 20t18 11l448 96q3 1 7 1q12 0 20 -7q12 -9 12 -25z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1440 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1664 1376q0 -249 -75.5 -430.5t-253.5 -360.5q-81 -80 -195 -176l-20 -379q-2 -16 -16 -26l-384 -224q-7 -4 -16 -4q-12 0 -23 9l-64 64q-13 14 -8 32l85 276l-281 281l-276 -85q-3 -1 -9 -1 q-14 0 -23 9l-64 64q-17 19 -5 39l224 384q10 14 26 16l379 20q96 114 176 195q188 187 358 258t431 71q14 0 24 -9.5t10 -22.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1745 763l-164 -763h-334l178 832q13 56 -15 88q-27 33 -83 33h-169l-204 -953h-334l204 953h-286l-204 -953h-334l204 953l-153 327h1276q101 0 189.5 -40.5t147.5 -113.5q60 -73 81 -168.5t0 -194.5z" />
|
||||
<glyph unicode="" d="M909 141l102 102q19 19 19 45t-19 45l-307 307l307 307q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M717 141l454 454q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l307 -307l-307 -307q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1165 397l102 102q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l307 307l307 -307q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M813 237l454 454q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-307 -307l-307 307q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1130 939l16 175h-884l47 -534h612l-22 -228l-197 -53l-196 53l-13 140h-175l22 -278l362 -100h4v1l359 99l50 544h-644l-15 181h674zM0 1408h1408l-128 -1438l-578 -162l-574 162z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M275 1408h1505l-266 -1333l-804 -267l-698 267l71 356h297l-29 -147l422 -161l486 161l68 339h-1208l58 297h1209l38 191h-1208z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M960 1280q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1792 352v-352q0 -22 -20 -30q-8 -2 -12 -2q-13 0 -23 9l-93 93q-119 -143 -318.5 -226.5t-429.5 -83.5t-429.5 83.5t-318.5 226.5l-93 -93q-9 -9 -23 -9q-4 0 -12 2q-20 8 -20 30v352 q0 14 9 23t23 9h352q22 0 30 -20q8 -19 -7 -35l-100 -100q67 -91 189.5 -153.5t271.5 -82.5v647h-192q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h192v163q-58 34 -93 92.5t-35 128.5q0 106 75 181t181 75t181 -75t75 -181q0 -70 -35 -128.5t-93 -92.5v-163h192q26 0 45 -19 t19 -45v-128q0 -26 -19 -45t-45 -19h-192v-647q149 20 271.5 82.5t189.5 153.5l-100 100q-15 16 -7 35q8 20 30 20h352q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M1056 768q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v320q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45q0 106 -75 181t-181 75t-181 -75t-75 -181 v-320h736z" />
|
||||
<glyph unicode="" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM1152 640q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1280 640q0 -212 -150 -362t-362 -150t-362 150 t-150 362t150 362t362 150t362 -150t150 -362zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM896 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM1408 800v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="384" d="M384 288v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 1312v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
|
||||
<glyph unicode="" d="M512 256q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM863 162q-13 232 -177 396t-396 177q-14 1 -24 -9t-10 -23v-128q0 -13 8.5 -22t21.5 -10q154 -11 264 -121t121 -264q1 -13 10 -21.5t22 -8.5h128q13 0 23 10 t9 24zM1247 161q-5 154 -56 297.5t-139.5 260t-205 205t-260 139.5t-297.5 56q-14 1 -23 -9q-10 -10 -10 -23v-128q0 -13 9 -22t22 -10q204 -7 378 -111.5t278.5 -278.5t111.5 -378q1 -13 10 -22t22 -9h128q13 0 23 10q11 9 9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1152 585q32 18 32 55t-32 55l-544 320q-31 19 -64 1q-32 -19 -32 -56v-640q0 -37 32 -56 q16 -8 32 -8q17 0 32 9z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1024 1084l316 -316l-572 -572l-316 316zM813 105l618 618q19 19 19 45t-19 45l-362 362q-18 18 -45 18t-45 -18l-618 -618q-19 -19 -19 -45t19 -45l362 -362q18 -18 45 -18t45 18zM1702 742l-907 -908q-37 -37 -90.5 -37t-90.5 37l-126 126q56 56 56 136t-56 136 t-136 56t-136 -56l-125 126q-37 37 -37 90.5t37 90.5l907 906q37 37 90.5 37t90.5 -37l125 -125q-56 -56 -56 -136t56 -136t136 -56t136 56l126 -125q37 -37 37 -90.5t-37 -90.5z" />
|
||||
<glyph unicode="" d="M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h832q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5 t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M1018 933q-18 -37 -58 -37h-192v-864q0 -14 -9 -23t-23 -9h-704q-21 0 -29 18q-8 20 4 35l160 192q9 11 25 11h320v640h-192q-40 0 -58 37q-17 37 9 68l320 384q18 22 49 22t49 -22l320 -384q27 -32 9 -68z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M32 1280h704q13 0 22.5 -9.5t9.5 -23.5v-863h192q40 0 58 -37t-9 -69l-320 -384q-18 -22 -49 -22t-49 22l-320 384q-26 31 -9 69q18 37 58 37h192v640h-320q-14 0 -25 11l-160 192q-13 14 -4 34q9 19 29 19z" />
|
||||
<glyph unicode="" d="M685 237l614 614q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-467 -467l-211 211q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l358 -358q19 -19 45 -19t45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5 t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M404 428l152 -152l-52 -52h-56v96h-96v56zM818 818q14 -13 -3 -30l-291 -291q-17 -17 -30 -3q-14 13 3 30l291 291q17 17 30 3zM544 128l544 544l-288 288l-544 -544v-288h288zM1152 736l92 92q28 28 28 68t-28 68l-152 152q-28 28 -68 28t-68 -28l-92 -92zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1280 608v480q0 26 -19 45t-45 19h-480q-42 0 -59 -39q-17 -41 14 -70l144 -144l-534 -534q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l534 534l144 -144q18 -19 45 -19q12 0 25 5q39 17 39 59zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1005 435l352 352q19 19 19 45t-19 45l-352 352q-30 31 -69 14q-40 -17 -40 -59v-160q-119 0 -216 -19.5t-162.5 -51t-114 -79t-76.5 -95.5t-44.5 -109t-21.5 -111.5t-5 -110.5q0 -181 167 -404q10 -12 25 -12q7 0 13 3q22 9 19 33q-44 354 62 473q46 52 130 75.5 t224 23.5v-160q0 -42 40 -59q12 -5 24 -5q26 0 45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M640 448l256 128l-256 128v-256zM1024 1039v-542l-512 -256v542zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1145 861q18 -35 -5 -66l-320 -448q-19 -27 -52 -27t-52 27l-320 448q-23 31 -5 66q17 35 57 35h640q40 0 57 -35zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1145 419q-17 -35 -57 -35h-640q-40 0 -57 35q-18 35 5 66l320 448q19 27 52 27t52 -27l320 -448q23 -31 5 -66zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1088 640q0 -33 -27 -52l-448 -320q-31 -23 -66 -5q-35 17 -35 57v640q0 40 35 57q35 18 66 -5l448 -320q27 -19 27 -52zM1280 160v960q0 14 -9 23t-23 9h-960q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h960q14 0 23 9t9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M976 229l35 -159q3 -12 -3 -22.5t-17 -14.5l-5 -1q-4 -2 -10.5 -3.5t-16 -4.5t-21.5 -5.5t-25.5 -5t-30 -5t-33.5 -4.5t-36.5 -3t-38.5 -1q-234 0 -409 130.5t-238 351.5h-95q-13 0 -22.5 9.5t-9.5 22.5v113q0 13 9.5 22.5t22.5 9.5h66q-2 57 1 105h-67q-14 0 -23 9 t-9 23v114q0 14 9 23t23 9h98q67 210 243.5 338t400.5 128q102 0 194 -23q11 -3 20 -15q6 -11 3 -24l-43 -159q-3 -13 -14 -19.5t-24 -2.5l-4 1q-4 1 -11.5 2.5l-17.5 3.5t-22.5 3.5t-26 3t-29 2.5t-29.5 1q-126 0 -226 -64t-150 -176h468q16 0 25 -12q10 -12 7 -26 l-24 -114q-5 -26 -32 -26h-488q-3 -37 0 -105h459q15 0 25 -12q9 -12 6 -27l-24 -112q-2 -11 -11 -18.5t-20 -7.5h-387q48 -117 149.5 -185.5t228.5 -68.5q18 0 36 1.5t33.5 3.5t29.5 4.5t24.5 5t18.5 4.5l12 3l5 2q13 5 26 -2q12 -7 15 -21z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M1020 399v-367q0 -14 -9 -23t-23 -9h-956q-14 0 -23 9t-9 23v150q0 13 9.5 22.5t22.5 9.5h97v383h-95q-14 0 -23 9.5t-9 22.5v131q0 14 9 23t23 9h95v223q0 171 123.5 282t314.5 111q185 0 335 -125q9 -8 10 -20.5t-7 -22.5l-103 -127q-9 -11 -22 -12q-13 -2 -23 7 q-5 5 -26 19t-69 32t-93 18q-85 0 -137 -47t-52 -123v-215h305q13 0 22.5 -9t9.5 -23v-131q0 -13 -9.5 -22.5t-22.5 -9.5h-305v-379h414v181q0 13 9 22.5t23 9.5h162q14 0 23 -9.5t9 -22.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M978 351q0 -153 -99.5 -263.5t-258.5 -136.5v-175q0 -14 -9 -23t-23 -9h-135q-13 0 -22.5 9.5t-9.5 22.5v175q-66 9 -127.5 31t-101.5 44.5t-74 48t-46.5 37.5t-17.5 18q-17 21 -2 41l103 135q7 10 23 12q15 2 24 -9l2 -2q113 -99 243 -125q37 -8 74 -8q81 0 142.5 43 t61.5 122q0 28 -15 53t-33.5 42t-58.5 37.5t-66 32t-80 32.5q-39 16 -61.5 25t-61.5 26.5t-62.5 31t-56.5 35.5t-53.5 42.5t-43.5 49t-35.5 58t-21 66.5t-8.5 78q0 138 98 242t255 134v180q0 13 9.5 22.5t22.5 9.5h135q14 0 23 -9t9 -23v-176q57 -6 110.5 -23t87 -33.5 t63.5 -37.5t39 -29t15 -14q17 -18 5 -38l-81 -146q-8 -15 -23 -16q-14 -3 -27 7q-3 3 -14.5 12t-39 26.5t-58.5 32t-74.5 26t-85.5 11.5q-95 0 -155 -43t-60 -111q0 -26 8.5 -48t29.5 -41.5t39.5 -33t56 -31t60.5 -27t70 -27.5q53 -20 81 -31.5t76 -35t75.5 -42.5t62 -50 t53 -63.5t31.5 -76.5t13 -94z" />
|
||||
<glyph unicode="" horiz-adv-x="898" d="M898 1066v-102q0 -14 -9 -23t-23 -9h-168q-23 -144 -129 -234t-276 -110q167 -178 459 -536q14 -16 4 -34q-8 -18 -29 -18h-195q-16 0 -25 12q-306 367 -498 571q-9 9 -9 22v127q0 13 9.5 22.5t22.5 9.5h112q132 0 212.5 43t102.5 125h-427q-14 0 -23 9t-9 23v102 q0 14 9 23t23 9h413q-57 113 -268 113h-145q-13 0 -22.5 9.5t-9.5 22.5v133q0 14 9 23t23 9h832q14 0 23 -9t9 -23v-102q0 -14 -9 -23t-23 -9h-233q47 -61 64 -144h171q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1027" d="M603 0h-172q-13 0 -22.5 9t-9.5 23v330h-288q-13 0 -22.5 9t-9.5 23v103q0 13 9.5 22.5t22.5 9.5h288v85h-288q-13 0 -22.5 9t-9.5 23v104q0 13 9.5 22.5t22.5 9.5h214l-321 578q-8 16 0 32q10 16 28 16h194q19 0 29 -18l215 -425q19 -38 56 -125q10 24 30.5 68t27.5 61 l191 420q8 19 29 19h191q17 0 27 -16q9 -14 1 -31l-313 -579h215q13 0 22.5 -9.5t9.5 -22.5v-104q0 -14 -9.5 -23t-22.5 -9h-290v-85h290q13 0 22.5 -9.5t9.5 -22.5v-103q0 -14 -9.5 -23t-22.5 -9h-290v-330q0 -13 -9.5 -22.5t-22.5 -9.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1043 971q0 100 -65 162t-171 62h-320v-448h320q106 0 171 62t65 162zM1280 971q0 -193 -126.5 -315t-326.5 -122h-340v-118h505q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-505v-192q0 -14 -9.5 -23t-22.5 -9h-167q-14 0 -23 9t-9 23v192h-224q-14 0 -23 9t-9 23v128 q0 14 9 23t23 9h224v118h-224q-14 0 -23 9t-9 23v149q0 13 9 22.5t23 9.5h224v629q0 14 9 23t23 9h539q200 0 326.5 -122t126.5 -315z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M514 341l81 299h-159l75 -300q1 -1 1 -3t1 -3q0 1 0.5 3.5t0.5 3.5zM630 768l35 128h-292l32 -128h225zM822 768h139l-35 128h-70zM1271 340l78 300h-162l81 -299q0 -1 0.5 -3.5t1.5 -3.5q0 1 0.5 3t0.5 3zM1382 768l33 128h-297l34 -128h230zM1792 736v-64q0 -14 -9 -23 t-23 -9h-213l-164 -616q-7 -24 -31 -24h-159q-24 0 -31 24l-166 616h-209l-167 -616q-7 -24 -31 -24h-159q-11 0 -19.5 7t-10.5 17l-160 616h-208q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h175l-33 128h-142q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h109l-89 344q-5 15 5 28 q10 12 26 12h137q26 0 31 -24l90 -360h359l97 360q7 24 31 24h126q24 0 31 -24l98 -360h365l93 360q5 24 31 24h137q16 0 26 -12q10 -13 5 -28l-91 -344h111q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-145l-34 -128h179q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1167 896q18 -182 -131 -258q117 -28 175 -103t45 -214q-7 -71 -32.5 -125t-64.5 -89t-97 -58.5t-121.5 -34.5t-145.5 -15v-255h-154v251q-80 0 -122 1v-252h-154v255q-18 0 -54 0.5t-55 0.5h-200l31 183h111q50 0 58 51v402h16q-6 1 -16 1v287q-13 68 -89 68h-111v164 l212 -1q64 0 97 1v252h154v-247q82 2 122 2v245h154v-252q79 -7 140 -22.5t113 -45t82.5 -78t36.5 -114.5zM952 351q0 36 -15 64t-37 46t-57.5 30.5t-65.5 18.5t-74 9t-69 3t-64.5 -1t-47.5 -1v-338q8 0 37 -0.5t48 -0.5t53 1.5t58.5 4t57 8.5t55.5 14t47.5 21t39.5 30 t24.5 40t9.5 51zM881 827q0 33 -12.5 58.5t-30.5 42t-48 28t-55 16.5t-61.5 8t-58 2.5t-54 -1t-39.5 -0.5v-307q5 0 34.5 -0.5t46.5 0t50 2t55 5.5t51.5 11t48.5 18.5t37 27t27 38.5t9 51z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1280 768v-800q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h544v-544q0 -40 28 -68t68 -28h544zM1277 896h-509v509q82 -15 132 -65l312 -312q50 -50 65 -132z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1024 160v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1024 416v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1280 768v-800q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28 t-28 68v1344q0 40 28 68t68 28h544v-544q0 -40 28 -68t68 -28h544zM1277 896h-509v509q82 -15 132 -65l312 -312q50 -50 65 -132z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1191 1128h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1572 -23 v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -11v-2l14 2q9 2 30 2h248v119h121zM1661 874v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162 l230 -662h70z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1191 104h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1661 -150 v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162l230 -662h70zM1572 1001v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -10v-3l14 3q9 1 30 1h248 v119h121z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1792 -32v-192q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832 q14 0 23 -9t9 -23zM1600 480v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1408 992v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1216 1504v-192q0 -14 -9 -23t-23 -9h-256 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1216 -32v-192q0 -14 -9 -23t-23 -9h-256q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192 q14 0 23 -9t9 -23zM1408 480v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1600 992v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1792 1504v-192q0 -14 -9 -23t-23 -9h-832 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" d="M1346 223q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23 zM1486 165q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5 t82 -252.5zM1456 882v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165z" />
|
||||
<glyph unicode="" d="M1346 1247q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9 t9 -23zM1456 -142v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165zM1486 1189q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13 q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5t82 -252.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M256 192q0 26 -19 45t-45 19q-27 0 -45.5 -19t-18.5 -45q0 -27 18.5 -45.5t45.5 -18.5q26 0 45 18.5t19 45.5zM416 704v-640q0 -26 -19 -45t-45 -19h-288q-26 0 -45 19t-19 45v640q0 26 19 45t45 19h288q26 0 45 -19t19 -45zM1600 704q0 -86 -55 -149q15 -44 15 -76 q3 -76 -43 -137q17 -56 0 -117q-15 -57 -54 -94q9 -112 -49 -181q-64 -76 -197 -78h-36h-76h-17q-66 0 -144 15.5t-121.5 29t-120.5 39.5q-123 43 -158 44q-26 1 -45 19.5t-19 44.5v641q0 25 18 43.5t43 20.5q24 2 76 59t101 121q68 87 101 120q18 18 31 48t17.5 48.5 t13.5 60.5q7 39 12.5 61t19.5 52t34 50q19 19 45 19q46 0 82.5 -10.5t60 -26t40 -40.5t24 -45t12 -50t5 -45t0.5 -39q0 -38 -9.5 -76t-19 -60t-27.5 -56q-3 -6 -10 -18t-11 -22t-8 -24h277q78 0 135 -57t57 -135z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M256 960q0 -26 -19 -45t-45 -19q-27 0 -45.5 19t-18.5 45q0 27 18.5 45.5t45.5 18.5q26 0 45 -18.5t19 -45.5zM416 448v640q0 26 -19 45t-45 19h-288q-26 0 -45 -19t-19 -45v-640q0 -26 19 -45t45 -19h288q26 0 45 19t19 45zM1545 597q55 -61 55 -149q-1 -78 -57.5 -135 t-134.5 -57h-277q4 -14 8 -24t11 -22t10 -18q18 -37 27 -57t19 -58.5t10 -76.5q0 -24 -0.5 -39t-5 -45t-12 -50t-24 -45t-40 -40.5t-60 -26t-82.5 -10.5q-26 0 -45 19q-20 20 -34 50t-19.5 52t-12.5 61q-9 42 -13.5 60.5t-17.5 48.5t-31 48q-33 33 -101 120q-49 64 -101 121 t-76 59q-25 2 -43 20.5t-18 43.5v641q0 26 19 44.5t45 19.5q35 1 158 44q77 26 120.5 39.5t121.5 29t144 15.5h17h76h36q133 -2 197 -78q58 -69 49 -181q39 -37 54 -94q17 -61 0 -117q46 -61 43 -137q0 -32 -15 -76z" />
|
||||
<glyph unicode="" d="M919 233v157q0 50 -29 50q-17 0 -33 -16v-224q16 -16 33 -16q29 0 29 49zM1103 355h66v34q0 51 -33 51t-33 -51v-34zM532 621v-70h-80v-423h-74v423h-78v70h232zM733 495v-367h-67v40q-39 -45 -76 -45q-33 0 -42 28q-6 16 -6 54v290h66v-270q0 -24 1 -26q1 -15 15 -15 q20 0 42 31v280h67zM985 384v-146q0 -52 -7 -73q-12 -42 -53 -42q-35 0 -68 41v-36h-67v493h67v-161q32 40 68 40q41 0 53 -42q7 -21 7 -74zM1236 255v-9q0 -29 -2 -43q-3 -22 -15 -40q-27 -40 -80 -40q-52 0 -81 38q-21 27 -21 86v129q0 59 20 86q29 38 80 38t78 -38 q21 -28 21 -86v-76h-133v-65q0 -51 34 -51q24 0 30 26q0 1 0.5 7t0.5 16.5v21.5h68zM785 1079v-156q0 -51 -32 -51t-32 51v156q0 52 32 52t32 -52zM1318 366q0 177 -19 260q-10 44 -43 73.5t-76 34.5q-136 15 -412 15q-275 0 -411 -15q-44 -5 -76.5 -34.5t-42.5 -73.5 q-20 -87 -20 -260q0 -176 20 -260q10 -43 42.5 -73t75.5 -35q137 -15 412 -15t412 15q43 5 75.5 35t42.5 73q20 84 20 260zM563 1017l90 296h-75l-51 -195l-53 195h-78l24 -69t23 -69q35 -103 46 -158v-201h74v201zM852 936v130q0 58 -21 87q-29 38 -78 38q-51 0 -78 -38 q-21 -29 -21 -87v-130q0 -58 21 -87q27 -38 78 -38q49 0 78 38q21 27 21 87zM1033 816h67v370h-67v-283q-22 -31 -42 -31q-15 0 -16 16q-1 2 -1 26v272h-67v-293q0 -37 6 -55q11 -27 43 -27q36 0 77 45v-40zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M971 292v-211q0 -67 -39 -67q-23 0 -45 22v301q22 22 45 22q39 0 39 -67zM1309 291v-46h-90v46q0 68 45 68t45 -68zM343 509h107v94h-312v-94h105v-569h100v569zM631 -60h89v494h-89v-378q-30 -42 -57 -42q-18 0 -21 21q-1 3 -1 35v364h-89v-391q0 -49 8 -73 q12 -37 58 -37q48 0 102 61v-54zM1060 88v197q0 73 -9 99q-17 56 -71 56q-50 0 -93 -54v217h-89v-663h89v48q45 -55 93 -55q54 0 71 55q9 27 9 100zM1398 98v13h-91q0 -51 -2 -61q-7 -36 -40 -36q-46 0 -46 69v87h179v103q0 79 -27 116q-39 51 -106 51q-68 0 -107 -51 q-28 -37 -28 -116v-173q0 -79 29 -116q39 -51 108 -51q72 0 108 53q18 27 21 54q2 9 2 58zM790 1011v210q0 69 -43 69t-43 -69v-210q0 -70 43 -70t43 70zM1509 260q0 -234 -26 -350q-14 -59 -58 -99t-102 -46q-184 -21 -555 -21t-555 21q-58 6 -102.5 46t-57.5 99 q-26 112 -26 350q0 234 26 350q14 59 58 99t103 47q183 20 554 20t555 -20q58 -7 102.5 -47t57.5 -99q26 -112 26 -350zM511 1536h102l-121 -399v-271h-100v271q-14 74 -61 212q-37 103 -65 187h106l71 -263zM881 1203v-175q0 -81 -28 -118q-37 -51 -106 -51q-67 0 -105 51 q-28 38 -28 118v175q0 80 28 117q38 51 105 51q69 0 106 -51q28 -37 28 -117zM1216 1365v-499h-91v55q-53 -62 -103 -62q-46 0 -59 37q-8 24 -8 75v394h91v-367q0 -33 1 -35q3 -22 21 -22q27 0 57 43v381h91z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M597 869q-10 -18 -257 -456q-27 -46 -65 -46h-239q-21 0 -31 17t0 36l253 448q1 0 0 1l-161 279q-12 22 -1 37q9 15 32 15h239q40 0 66 -45zM1403 1511q11 -16 0 -37l-528 -934v-1l336 -615q11 -20 1 -37q-10 -15 -32 -15h-239q-42 0 -66 45l-339 622q18 32 531 942 q25 45 64 45h241q22 0 31 -15z" />
|
||||
<glyph unicode="" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M928 135v-151l-707 -1v151zM1169 481v-701l-1 -35v-1h-1132l-35 1h-1v736h121v-618h928v618h120zM241 393l704 -65l-13 -150l-705 65zM309 709l683 -183l-39 -146l-683 183zM472 1058l609 -360l-77 -130l-609 360zM832 1389l398 -585l-124 -85l-399 584zM1285 1536 l121 -697l-149 -26l-121 697z" />
|
||||
<glyph unicode="" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
|
||||
<glyph unicode="" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
|
||||
<glyph unicode="" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22 t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18 t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5 t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" />
|
||||
<glyph unicode="" d="M848 666q0 43 -41 66t-77 1q-43 -20 -42.5 -72.5t43.5 -70.5q39 -23 81 4t36 72zM928 682q8 -66 -36 -121t-110 -61t-119 40t-56 113q-2 49 25.5 93t72.5 64q70 31 141.5 -10t81.5 -118zM1100 1073q-20 -21 -53.5 -34t-53 -16t-63.5 -8q-155 -20 -324 0q-44 6 -63 9.5 t-52.5 16t-54.5 32.5q13 19 36 31t40 15.5t47 8.5q198 35 408 1q33 -5 51 -8.5t43 -16t39 -31.5zM1142 327q0 7 5.5 26.5t3 32t-17.5 16.5q-161 -106 -365 -106t-366 106l-12 -6l-5 -12q26 -154 41 -210q47 -81 204 -108q249 -46 428 53q34 19 49 51.5t22.5 85.5t12.5 71z M1272 1020q9 53 -8 75q-43 55 -155 88q-216 63 -487 36q-132 -12 -226 -46q-38 -15 -59.5 -25t-47 -34t-29.5 -54q8 -68 19 -138t29 -171t24 -137q1 -5 5 -31t7 -36t12 -27t22 -28q105 -80 284 -100q259 -28 440 63q24 13 39.5 23t31 29t19.5 40q48 267 80 473zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M390 1408h219v-388h364v-241h-364v-394q0 -136 14 -172q13 -37 52 -60q50 -31 117 -31q117 0 232 76v-242q-102 -48 -178 -65q-77 -19 -173 -19q-105 0 -186 27q-78 25 -138 75q-58 51 -79 105q-22 54 -22 161v539h-170v217q91 30 155 84q64 55 103 132q39 78 54 196z " />
|
||||
<glyph unicode="" d="M1123 127v181q-88 -56 -174 -56q-51 0 -88 23q-29 17 -39 45q-11 30 -11 129v295h274v181h-274v291h-164q-11 -90 -40 -147t-78 -99q-48 -40 -116 -63v-163h127v-404q0 -78 17 -121q17 -42 59 -78q43 -37 104 -57q62 -20 140 -20q67 0 129 14q57 13 134 49zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="768" d="M765 237q8 -19 -5 -35l-350 -384q-10 -10 -23 -10q-14 0 -24 10l-355 384q-13 16 -5 35q9 19 29 19h224v1248q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1248h224q21 0 29 -19z" />
|
||||
<glyph unicode="" horiz-adv-x="768" d="M765 1043q-9 -19 -29 -19h-224v-1248q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1248h-224q-21 0 -29 19t5 35l350 384q10 10 23 10q14 0 24 -10l355 -384q13 -16 5 -35z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 736v-192q0 -14 -9 -23t-23 -9h-1248v-224q0 -21 -19 -29t-35 5l-384 350q-10 10 -10 23q0 14 10 24l384 354q16 14 35 6q19 -9 19 -29v-224h1248q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1728 643q0 -14 -10 -24l-384 -354q-16 -14 -35 -6q-19 9 -19 29v224h-1248q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h1248v224q0 21 19 29t35 -5l384 -350q10 -10 10 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1393 321q-39 -125 -123 -250q-129 -196 -257 -196q-49 0 -140 32q-86 32 -151 32q-61 0 -142 -33q-81 -34 -132 -34q-152 0 -301 259q-147 261 -147 503q0 228 113 374q112 144 284 144q72 0 177 -30q104 -30 138 -30q45 0 143 34q102 34 173 34q119 0 213 -65 q52 -36 104 -100q-79 -67 -114 -118q-65 -94 -65 -207q0 -124 69 -223t158 -126zM1017 1494q0 -61 -29 -136q-30 -75 -93 -138q-54 -54 -108 -72q-37 -11 -104 -17q3 149 78 257q74 107 250 148q1 -3 2.5 -11t2.5 -11q0 -4 0.5 -10t0.5 -10z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M682 530v-651l-682 94v557h682zM682 1273v-659h-682v565zM1664 530v-786l-907 125v661h907zM1664 1408v-794h-907v669z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M493 1053q16 0 27.5 11.5t11.5 27.5t-11.5 27.5t-27.5 11.5t-27 -11.5t-11 -27.5t11 -27.5t27 -11.5zM915 1053q16 0 27 11.5t11 27.5t-11 27.5t-27 11.5t-27.5 -11.5t-11.5 -27.5t11.5 -27.5t27.5 -11.5zM103 869q42 0 72 -30t30 -72v-430q0 -43 -29.5 -73t-72.5 -30 t-73 30t-30 73v430q0 42 30 72t73 30zM1163 850v-666q0 -46 -32 -78t-77 -32h-75v-227q0 -43 -30 -73t-73 -30t-73 30t-30 73v227h-138v-227q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73l-1 227h-74q-46 0 -78 32t-32 78v666h918zM931 1255q107 -55 171 -153.5t64 -215.5 h-925q0 117 64 215.5t172 153.5l-71 131q-7 13 5 20q13 6 20 -6l72 -132q95 42 201 42t201 -42l72 132q7 12 20 6q12 -7 5 -20zM1408 767v-430q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73v430q0 43 30 72.5t72 29.5q43 0 73 -29.5t30 -72.5z" />
|
||||
<glyph unicode="" d="M663 1125q-11 -1 -15.5 -10.5t-8.5 -9.5q-5 -1 -5 5q0 12 19 15h10zM750 1111q-4 -1 -11.5 6.5t-17.5 4.5q24 11 32 -2q3 -6 -3 -9zM399 684q-4 1 -6 -3t-4.5 -12.5t-5.5 -13.5t-10 -13q-7 -10 -1 -12q4 -1 12.5 7t12.5 18q1 3 2 7t2 6t1.5 4.5t0.5 4v3t-1 2.5t-3 2z M1254 325q0 18 -55 42q4 15 7.5 27.5t5 26t3 21.5t0.5 22.5t-1 19.5t-3.5 22t-4 20.5t-5 25t-5.5 26.5q-10 48 -47 103t-72 75q24 -20 57 -83q87 -162 54 -278q-11 -40 -50 -42q-31 -4 -38.5 18.5t-8 83.5t-11.5 107q-9 39 -19.5 69t-19.5 45.5t-15.5 24.5t-13 15t-7.5 7 q-14 62 -31 103t-29.5 56t-23.5 33t-15 40q-4 21 6 53.5t4.5 49.5t-44.5 25q-15 3 -44.5 18t-35.5 16q-8 1 -11 26t8 51t36 27q37 3 51 -30t4 -58q-11 -19 -2 -26.5t30 -0.5q13 4 13 36v37q-5 30 -13.5 50t-21 30.5t-23.5 15t-27 7.5q-107 -8 -89 -134q0 -15 -1 -15 q-9 9 -29.5 10.5t-33 -0.5t-15.5 5q1 57 -16 90t-45 34q-27 1 -41.5 -27.5t-16.5 -59.5q-1 -15 3.5 -37t13 -37.5t15.5 -13.5q10 3 16 14q4 9 -7 8q-7 0 -15.5 14.5t-9.5 33.5q-1 22 9 37t34 14q17 0 27 -21t9.5 -39t-1.5 -22q-22 -15 -31 -29q-8 -12 -27.5 -23.5 t-20.5 -12.5q-13 -14 -15.5 -27t7.5 -18q14 -8 25 -19.5t16 -19t18.5 -13t35.5 -6.5q47 -2 102 15q2 1 23 7t34.5 10.5t29.5 13t21 17.5q9 14 20 8q5 -3 6.5 -8.5t-3 -12t-16.5 -9.5q-20 -6 -56.5 -21.5t-45.5 -19.5q-44 -19 -70 -23q-25 -5 -79 2q-10 2 -9 -2t17 -19 q25 -23 67 -22q17 1 36 7t36 14t33.5 17.5t30 17t24.5 12t17.5 2.5t8.5 -11q0 -2 -1 -4.5t-4 -5t-6 -4.5t-8.5 -5t-9 -4.5t-10 -5t-9.5 -4.5q-28 -14 -67.5 -44t-66.5 -43t-49 -1q-21 11 -63 73q-22 31 -25 22q-1 -3 -1 -10q0 -25 -15 -56.5t-29.5 -55.5t-21 -58t11.5 -63 q-23 -6 -62.5 -90t-47.5 -141q-2 -18 -1.5 -69t-5.5 -59q-8 -24 -29 -3q-32 31 -36 94q-2 28 4 56q4 19 -1 18l-4 -5q-36 -65 10 -166q5 -12 25 -28t24 -20q20 -23 104 -90.5t93 -76.5q16 -15 17.5 -38t-14 -43t-45.5 -23q8 -15 29 -44.5t28 -54t7 -70.5q46 24 7 92 q-4 8 -10.5 16t-9.5 12t-2 6q3 5 13 9.5t20 -2.5q46 -52 166 -36q133 15 177 87q23 38 34 30q12 -6 10 -52q-1 -25 -23 -92q-9 -23 -6 -37.5t24 -15.5q3 19 14.5 77t13.5 90q2 21 -6.5 73.5t-7.5 97t23 70.5q15 18 51 18q1 37 34.5 53t72.5 10.5t60 -22.5zM626 1152 q3 17 -2.5 30t-11.5 15q-9 2 -9 -7q2 -5 5 -6q10 0 7 -15q-3 -20 8 -20q3 0 3 3zM1045 955q-2 8 -6.5 11.5t-13 5t-14.5 5.5q-5 3 -9.5 8t-7 8t-5.5 6.5t-4 4t-4 -1.5q-14 -16 7 -43.5t39 -31.5q9 -1 14.5 8t3.5 20zM867 1168q0 11 -5 19.5t-11 12.5t-9 3q-14 -1 -7 -7l4 -2 q14 -4 18 -31q0 -3 8 2zM921 1401q0 2 -2.5 5t-9 7t-9.5 6q-15 15 -24 15q-9 -1 -11.5 -7.5t-1 -13t-0.5 -12.5q-1 -4 -6 -10.5t-6 -9t3 -8.5q4 -3 8 0t11 9t15 9q1 1 9 1t15 2t9 7zM1486 60q20 -12 31 -24.5t12 -24t-2.5 -22.5t-15.5 -22t-23.5 -19.5t-30 -18.5 t-31.5 -16.5t-32 -15.5t-27 -13q-38 -19 -85.5 -56t-75.5 -64q-17 -16 -68 -19.5t-89 14.5q-18 9 -29.5 23.5t-16.5 25.5t-22 19.5t-47 9.5q-44 1 -130 1q-19 0 -57 -1.5t-58 -2.5q-44 -1 -79.5 -15t-53.5 -30t-43.5 -28.5t-53.5 -11.5q-29 1 -111 31t-146 43q-19 4 -51 9.5 t-50 9t-39.5 9.5t-33.5 14.5t-17 19.5q-10 23 7 66.5t18 54.5q1 16 -4 40t-10 42.5t-4.5 36.5t10.5 27q14 12 57 14t60 12q30 18 42 35t12 51q21 -73 -32 -106q-32 -20 -83 -15q-34 3 -43 -10q-13 -15 5 -57q2 -6 8 -18t8.5 -18t4.5 -17t1 -22q0 -15 -17 -49t-14 -48 q3 -17 37 -26q20 -6 84.5 -18.5t99.5 -20.5q24 -6 74 -22t82.5 -23t55.5 -4q43 6 64.5 28t23 48t-7.5 58.5t-19 52t-20 36.5q-121 190 -169 242q-68 74 -113 40q-11 -9 -15 15q-3 16 -2 38q1 29 10 52t24 47t22 42q8 21 26.5 72t29.5 78t30 61t39 54q110 143 124 195 q-12 112 -16 310q-2 90 24 151.5t106 104.5q39 21 104 21q53 1 106 -13.5t89 -41.5q57 -42 91.5 -121.5t29.5 -147.5q-5 -95 30 -214q34 -113 133 -218q55 -59 99.5 -163t59.5 -191q8 -49 5 -84.5t-12 -55.5t-20 -22q-10 -2 -23.5 -19t-27 -35.5t-40.5 -33.5t-61 -14 q-18 1 -31.5 5t-22.5 13.5t-13.5 15.5t-11.5 20.5t-9 19.5q-22 37 -41 30t-28 -49t7 -97q20 -70 1 -195q-10 -65 18 -100.5t73 -33t85 35.5q59 49 89.5 66.5t103.5 42.5q53 18 77 36.5t18.5 34.5t-25 28.5t-51.5 23.5q-33 11 -49.5 48t-15 72.5t15.5 47.5q1 -31 8 -56.5 t14.5 -40.5t20.5 -28.5t21 -19t21.5 -13t16.5 -9.5z" />
|
||||
<glyph unicode="" d="M1024 36q-42 241 -140 498h-2l-2 -1q-16 -6 -43 -16.5t-101 -49t-137 -82t-131 -114.5t-103 -148l-15 11q184 -150 418 -150q132 0 256 52zM839 643q-21 49 -53 111q-311 -93 -673 -93q-1 -7 -1 -21q0 -124 44 -236.5t124 -201.5q50 89 123.5 166.5t142.5 124.5t130.5 81 t99.5 48l37 13q4 1 13 3.5t13 4.5zM732 855q-120 213 -244 378q-138 -65 -234 -186t-128 -272q302 0 606 80zM1416 536q-210 60 -409 29q87 -239 128 -469q111 75 185 189.5t96 250.5zM611 1277q-1 0 -2 -1q1 1 2 1zM1201 1132q-185 164 -433 164q-76 0 -155 -19 q131 -170 246 -382q69 26 130 60.5t96.5 61.5t65.5 57t37.5 40.5zM1424 647q-3 232 -149 410l-1 -1q-9 -12 -19 -24.5t-43.5 -44.5t-71 -60.5t-100 -65t-131.5 -64.5q25 -53 44 -95q2 -6 6.5 -17.5t7.5 -16.5q36 5 74.5 7t73.5 2t69 -1.5t64 -4t56.5 -5.5t48 -6.5t36.5 -6 t25 -4.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1173 473q0 50 -19.5 91.5t-48.5 68.5t-73 49t-82.5 34t-87.5 23l-104 24q-30 7 -44 10.5t-35 11.5t-30 16t-16.5 21t-7.5 30q0 77 144 77q43 0 77 -12t54 -28.5t38 -33.5t40 -29t48 -12q47 0 75.5 32t28.5 77q0 55 -56 99.5t-142 67.5t-182 23q-68 0 -132 -15.5 t-119.5 -47t-89 -87t-33.5 -128.5q0 -61 19 -106.5t56 -75.5t80 -48.5t103 -32.5l146 -36q90 -22 112 -36q32 -20 32 -60q0 -39 -40 -64.5t-105 -25.5q-51 0 -91.5 16t-65 38.5t-45.5 45t-46 38.5t-54 16q-50 0 -75.5 -30t-25.5 -75q0 -92 122 -157.5t291 -65.5 q73 0 140 18.5t122.5 53.5t88.5 93.5t33 131.5zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5q-130 0 -234 80q-77 -16 -150 -16q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5q0 73 16 150q-80 104 -80 234q0 159 112.5 271.5t271.5 112.5q130 0 234 -80 q77 16 150 16q143 0 273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -73 -16 -150q80 -104 80 -234z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1483 512l-587 -587q-52 -53 -127.5 -53t-128.5 53l-587 587q-53 53 -53 128t53 128l587 587q53 53 128 53t128 -53l265 -265l-398 -399l-188 188q-42 42 -99 42q-59 0 -100 -41l-120 -121q-42 -40 -42 -99q0 -58 42 -100l406 -408q30 -28 67 -37l6 -4h28q60 0 99 41 l619 619l2 -3q53 -53 53 -128t-53 -128zM1406 1138l120 -120q14 -15 14 -36t-14 -36l-730 -730q-17 -15 -37 -15v0q-4 0 -6 1q-18 2 -30 14l-407 408q-14 15 -14 36t14 35l121 120q13 15 35 15t36 -15l252 -252l574 575q15 15 36 15t36 -15z" />
|
||||
<glyph unicode="" d="M704 192v1024q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-1024q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1376 576v640q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-640q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408 q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1280 480q0 -40 -28 -68t-68 -28q-51 0 -80 43l-227 341h-45v-132l247 -411q9 -15 9 -33q0 -26 -19 -45t-45 -19h-192v-272q0 -46 -33 -79t-79 -33h-160q-46 0 -79 33t-33 79v272h-192q-26 0 -45 19t-19 45q0 18 9 33l247 411v132h-45l-227 -341q-29 -43 -80 -43 q-40 0 -68 28t-28 68q0 29 16 53l256 384q73 107 176 107h384q103 0 176 -107l256 -384q16 -24 16 -53zM864 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 832v-416q0 -40 -28 -68t-68 -28t-68 28t-28 68v352h-64v-912q0 -46 -33 -79t-79 -33t-79 33t-33 79v464h-64v-464q0 -46 -33 -79t-79 -33t-79 33t-33 79v912h-64v-352q0 -40 -28 -68t-68 -28t-68 28t-28 68v416q0 80 56 136t136 56h640q80 0 136 -56t56 -136z M736 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
|
||||
<glyph unicode="" d="M773 234l350 473q16 22 24.5 59t-6 85t-61.5 79q-40 26 -83 25.5t-73.5 -17.5t-54.5 -45q-36 -40 -96 -40q-59 0 -95 40q-24 28 -54.5 45t-73.5 17.5t-84 -25.5q-46 -31 -60.5 -79t-6 -85t24.5 -59zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1472 640q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5zM1748 363q-4 -15 -20 -20l-292 -96v-306q0 -16 -13 -26q-15 -10 -29 -4 l-292 94l-180 -248q-10 -13 -26 -13t-26 13l-180 248l-292 -94q-14 -6 -29 4q-13 10 -13 26v306l-292 96q-16 5 -20 20q-5 17 4 29l180 248l-180 248q-9 13 -4 29q4 15 20 20l292 96v306q0 16 13 26q15 10 29 4l292 -94l180 248q9 12 26 12t26 -12l180 -248l292 94 q14 6 29 -4q13 -10 13 -26v-306l292 -96q16 -5 20 -20q5 -16 -4 -29l-180 -248l180 -248q9 -12 4 -29z" />
|
||||
<glyph unicode="" d="M1262 233q-54 -9 -110 -9q-182 0 -337 90t-245 245t-90 337q0 192 104 357q-201 -60 -328.5 -229t-127.5 -384q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51q144 0 273.5 61.5t220.5 171.5zM1465 318q-94 -203 -283.5 -324.5t-413.5 -121.5q-156 0 -298 61 t-245 164t-164 245t-61 298q0 153 57.5 292.5t156 241.5t235.5 164.5t290 68.5q44 2 61 -39q18 -41 -15 -72q-86 -78 -131.5 -181.5t-45.5 -218.5q0 -148 73 -273t198 -198t273 -73q118 0 228 51q41 18 72 -13q14 -14 17.5 -34t-4.5 -38z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1088 704q0 26 -19 45t-45 19h-256q-26 0 -45 -19t-19 -45t19 -45t45 -19h256q26 0 45 19t19 45zM1664 896v-960q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v960q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1728 1344v-256q0 -26 -19 -45t-45 -19h-1536 q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1536q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1632 576q0 -26 -19 -45t-45 -19h-224q0 -171 -67 -290l208 -209q19 -19 19 -45t-19 -45q-18 -19 -45 -19t-45 19l-198 197q-5 -5 -15 -13t-42 -28.5t-65 -36.5t-82 -29t-97 -13v896h-128v-896q-51 0 -101.5 13.5t-87 33t-66 39t-43.5 32.5l-15 14l-183 -207 q-20 -21 -48 -21q-24 0 -43 16q-19 18 -20.5 44.5t15.5 46.5l202 227q-58 114 -58 274h-224q-26 0 -45 19t-19 45t19 45t45 19h224v294l-173 173q-19 19 -19 45t19 45t45 19t45 -19l173 -173h844l173 173q19 19 45 19t45 -19t19 -45t-19 -45l-173 -173v-294h224q26 0 45 -19 t19 -45zM1152 1152h-640q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1917 1016q23 -64 -150 -294q-24 -32 -65 -85q-78 -100 -90 -131q-17 -41 14 -81q17 -21 81 -82h1l1 -1l1 -1l2 -2q141 -131 191 -221q3 -5 6.5 -12.5t7 -26.5t-0.5 -34t-25 -27.5t-59 -12.5l-256 -4q-24 -5 -56 5t-52 22l-20 12q-30 21 -70 64t-68.5 77.5t-61 58 t-56.5 15.5q-3 -1 -8 -3.5t-17 -14.5t-21.5 -29.5t-17 -52t-6.5 -77.5q0 -15 -3.5 -27.5t-7.5 -18.5l-4 -5q-18 -19 -53 -22h-115q-71 -4 -146 16.5t-131.5 53t-103 66t-70.5 57.5l-25 24q-10 10 -27.5 30t-71.5 91t-106 151t-122.5 211t-130.5 272q-6 16 -6 27t3 16l4 6 q15 19 57 19l274 2q12 -2 23 -6.5t16 -8.5l5 -3q16 -11 24 -32q20 -50 46 -103.5t41 -81.5l16 -29q29 -60 56 -104t48.5 -68.5t41.5 -38.5t34 -14t27 5q2 1 5 5t12 22t13.5 47t9.5 81t0 125q-2 40 -9 73t-14 46l-6 12q-25 34 -85 43q-13 2 5 24q17 19 38 30q53 26 239 24 q82 -1 135 -13q20 -5 33.5 -13.5t20.5 -24t10.5 -32t3.5 -45.5t-1 -55t-2.5 -70.5t-1.5 -82.5q0 -11 -1 -42t-0.5 -48t3.5 -40.5t11.5 -39t22.5 -24.5q8 -2 17 -4t26 11t38 34.5t52 67t68 107.5q60 104 107 225q4 10 10 17.5t11 10.5l4 3l5 2.5t13 3t20 0.5l288 2 q39 5 64 -2.5t31 -16.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M675 252q21 34 11 69t-45 50q-34 14 -73 1t-60 -46q-22 -34 -13 -68.5t43 -50.5t74.5 -2.5t62.5 47.5zM769 373q8 13 3.5 26.5t-17.5 18.5q-14 5 -28.5 -0.5t-21.5 -18.5q-17 -31 13 -45q14 -5 29 0.5t22 18.5zM943 266q-45 -102 -158 -150t-224 -12 q-107 34 -147.5 126.5t6.5 187.5q47 93 151.5 139t210.5 19q111 -29 158.5 -119.5t2.5 -190.5zM1255 426q-9 96 -89 170t-208.5 109t-274.5 21q-223 -23 -369.5 -141.5t-132.5 -264.5q9 -96 89 -170t208.5 -109t274.5 -21q223 23 369.5 141.5t132.5 264.5zM1563 422 q0 -68 -37 -139.5t-109 -137t-168.5 -117.5t-226 -83t-270.5 -31t-275 33.5t-240.5 93t-171.5 151t-65 199.5q0 115 69.5 245t197.5 258q169 169 341.5 236t246.5 -7q65 -64 20 -209q-4 -14 -1 -20t10 -7t14.5 0.5t13.5 3.5l6 2q139 59 246 59t153 -61q45 -63 0 -178 q-2 -13 -4.5 -20t4.5 -12.5t12 -7.5t17 -6q57 -18 103 -47t80 -81.5t34 -116.5zM1489 1046q42 -47 54.5 -108.5t-6.5 -117.5q-8 -23 -29.5 -34t-44.5 -4q-23 8 -34 29.5t-4 44.5q20 63 -24 111t-107 35q-24 -5 -45 8t-25 37q-5 24 8 44.5t37 25.5q60 13 119 -5.5t101 -65.5z M1670 1209q87 -96 112.5 -222.5t-13.5 -241.5q-9 -27 -34 -40t-52 -4t-40 34t-5 52q28 82 10 172t-80 158q-62 69 -148 95.5t-173 8.5q-28 -6 -52 9.5t-30 43.5t9.5 51.5t43.5 29.5q123 26 244 -11.5t208 -134.5z" />
|
||||
<glyph unicode="" d="M1133 -34q-171 -94 -368 -94q-196 0 -367 94q138 87 235.5 211t131.5 268q35 -144 132.5 -268t235.5 -211zM638 1394v-485q0 -252 -126.5 -459.5t-330.5 -306.5q-181 215 -181 495q0 187 83.5 349.5t229.5 269.5t325 137zM1536 638q0 -280 -181 -495 q-204 99 -330.5 306.5t-126.5 459.5v485q179 -30 325 -137t229.5 -269.5t83.5 -349.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1402 433q-32 -80 -76 -138t-91 -88.5t-99 -46.5t-101.5 -14.5t-96.5 8.5t-86.5 22t-69.5 27.5t-46 22.5l-17 10q-113 -228 -289.5 -359.5t-384.5 -132.5q-19 0 -32 13t-13 32t13 31.5t32 12.5q173 1 322.5 107.5t251.5 294.5q-36 -14 -72 -23t-83 -13t-91 2.5t-93 28.5 t-92 59t-84.5 100t-74.5 146q114 47 214 57t167.5 -7.5t124.5 -56.5t88.5 -77t56.5 -82q53 131 79 291q-7 -1 -18 -2.5t-46.5 -2.5t-69.5 0.5t-81.5 10t-88.5 23t-84 42.5t-75 65t-54.5 94.5t-28.5 127.5q70 28 133.5 36.5t112.5 -1t92 -30t73.5 -50t56 -61t42 -63t27.5 -56 t16 -39.5l4 -16q12 122 12 195q-8 6 -21.5 16t-49 44.5t-63.5 71.5t-54 93t-33 112.5t12 127t70 138.5q73 -25 127.5 -61.5t84.5 -76.5t48 -85t20.5 -89t-0.5 -85.5t-13 -76.5t-19 -62t-17 -42l-7 -15q1 -5 1 -50.5t-1 -71.5q3 7 10 18.5t30.5 43t50.5 58t71 55.5t91.5 44.5 t112 14.5t132.5 -24q-2 -78 -21.5 -141.5t-50 -104.5t-69.5 -71.5t-81.5 -45.5t-84.5 -24t-80 -9.5t-67.5 1t-46.5 4.5l-17 3q-23 -147 -73 -283q6 7 18 18.5t49.5 41t77.5 52.5t99.5 42t117.5 20t129 -23.5t137 -77.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1259 283v-66q0 -85 -57.5 -144.5t-138.5 -59.5h-57l-260 -269v269h-529q-81 0 -138.5 59.5t-57.5 144.5v66h1238zM1259 609v-255h-1238v255h1238zM1259 937v-255h-1238v255h1238zM1259 1077v-67h-1238v67q0 84 57.5 143.5t138.5 59.5h846q81 0 138.5 -59.5t57.5 -143.5z " />
|
||||
<glyph unicode="" d="M1152 640q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1152 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-192q0 -14 -9 -23t-23 -9q-12 0 -24 10l-319 319q-9 9 -9 23t9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h352q13 0 22.5 -9.5t9.5 -22.5zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5 t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128 q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 16 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" />
|
||||
<glyph unicode="" d="M1254 899q16 85 -21 132q-52 65 -187 45q-17 -3 -41 -12.5t-57.5 -30.5t-64.5 -48.5t-59.5 -70t-44.5 -91.5q80 7 113.5 -16t26.5 -99q-5 -52 -52 -143q-43 -78 -71 -99q-44 -32 -87 14q-23 24 -37.5 64.5t-19 73t-10 84t-8.5 71.5q-23 129 -34 164q-12 37 -35.5 69 t-50.5 40q-57 16 -127 -25q-54 -32 -136.5 -106t-122.5 -102v-7q16 -8 25.5 -26t21.5 -20q21 -3 54.5 8.5t58 10.5t41.5 -30q11 -18 18.5 -38.5t15 -48t12.5 -40.5q17 -46 53 -187q36 -146 57 -197q42 -99 103 -125q43 -12 85 -1.5t76 31.5q131 77 250 237 q104 139 172.5 292.5t82.5 226.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160 q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832 q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 197 KiB |
@@ -0,0 +1,113 @@
|
||||
function toggleCurrent (elem) {
|
||||
var parent_li = elem.closest('li');
|
||||
parent_li.siblings('li.current').removeClass('current');
|
||||
parent_li.siblings().find('li.current').removeClass('current');
|
||||
parent_li.find('> ul li.current').removeClass('current');
|
||||
parent_li.toggleClass('current');
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Shift nav in mobile when clicking the menu.
|
||||
$(document).on('click', "[data-toggle='wy-nav-top']", function() {
|
||||
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
});
|
||||
// Nav menu link click operations
|
||||
$(document).on('click', ".wy-menu-vertical .current ul li a", function() {
|
||||
var target = $(this);
|
||||
// Close menu when you click a link.
|
||||
$("[data-toggle='wy-nav-shift']").removeClass("shift");
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift");
|
||||
// Handle dynamic display of l3 and l4 nav lists
|
||||
toggleCurrent(target);
|
||||
if (typeof(window.SphinxRtdTheme) != 'undefined') {
|
||||
window.SphinxRtdTheme.StickyNav.hashChange();
|
||||
}
|
||||
});
|
||||
$(document).on('click', "[data-toggle='rst-current-version']", function() {
|
||||
$("[data-toggle='rst-versions']").toggleClass("shift-up");
|
||||
});
|
||||
// Make tables responsive
|
||||
$("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
|
||||
|
||||
// Add expand links to all parents of nested ul
|
||||
$('.wy-menu-vertical ul').siblings('a').each(function () {
|
||||
var link = $(this);
|
||||
expand = $('<span class="toctree-expand"></span>');
|
||||
expand.on('click', function (ev) {
|
||||
toggleCurrent(link);
|
||||
ev.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
link.prepend(expand);
|
||||
});
|
||||
});
|
||||
|
||||
// Sphinx theme state
|
||||
window.SphinxRtdTheme = (function (jquery) {
|
||||
var stickyNav = (function () {
|
||||
var navBar,
|
||||
win,
|
||||
winScroll = false,
|
||||
linkScroll = false,
|
||||
winPosition = 0,
|
||||
enable = function () {
|
||||
init();
|
||||
reset();
|
||||
win.on('hashchange', reset);
|
||||
|
||||
// Set scrolling
|
||||
win.on('scroll', function () {
|
||||
if (!linkScroll) {
|
||||
winScroll = true;
|
||||
}
|
||||
});
|
||||
setInterval(function () {
|
||||
if (winScroll) {
|
||||
winScroll = false;
|
||||
var newWinPosition = win.scrollTop(),
|
||||
navPosition = navBar.scrollTop(),
|
||||
newNavPosition = navPosition + (newWinPosition - winPosition);
|
||||
navBar.scrollTop(newNavPosition);
|
||||
winPosition = newWinPosition;
|
||||
}
|
||||
}, 25);
|
||||
},
|
||||
init = function () {
|
||||
navBar = jquery('nav.wy-nav-side:first');
|
||||
win = jquery(window);
|
||||
},
|
||||
reset = function () {
|
||||
// Get anchor from URL and open up nested nav
|
||||
var anchor = encodeURI(window.location.hash);
|
||||
if (anchor) {
|
||||
try {
|
||||
var link = $('.wy-menu-vertical')
|
||||
.find('[href="' + anchor + '"]');
|
||||
$('.wy-menu-vertical li.toctree-l1 li.current')
|
||||
.removeClass('current');
|
||||
link.closest('li.toctree-l2').addClass('current');
|
||||
link.closest('li.toctree-l3').addClass('current');
|
||||
link.closest('li.toctree-l4').addClass('current');
|
||||
}
|
||||
catch (err) {
|
||||
console.log("Error expanding nav for anchor", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
hashChange = function () {
|
||||
linkScroll = true;
|
||||
win.one('hashchange', function () {
|
||||
linkScroll = false;
|
||||
});
|
||||
};
|
||||
jquery(init);
|
||||
return {
|
||||
enable: enable,
|
||||
hashChange: hashChange
|
||||
};
|
||||
}());
|
||||
return {
|
||||
StickyNav: stickyNav
|
||||
};
|
||||
}($));
|
||||
|
After Width: | Height: | Size: 173 B |
|
After Width: | Height: | Size: 173 B |
@@ -0,0 +1,2 @@
|
||||
.highlight .hll { background-color: #ffffcc }
|
||||
.highlight { background: #ffffff; }
|
||||
@@ -0,0 +1,651 @@
|
||||
/*
|
||||
* searchtools.js_t
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilties for the full-text search.
|
||||
*
|
||||
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* Non-minified version JS is _stemmer.js if file is provided */
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
var Stemmer = function() {
|
||||
|
||||
var step2list = {
|
||||
ational: 'ate',
|
||||
tional: 'tion',
|
||||
enci: 'ence',
|
||||
anci: 'ance',
|
||||
izer: 'ize',
|
||||
bli: 'ble',
|
||||
alli: 'al',
|
||||
entli: 'ent',
|
||||
eli: 'e',
|
||||
ousli: 'ous',
|
||||
ization: 'ize',
|
||||
ation: 'ate',
|
||||
ator: 'ate',
|
||||
alism: 'al',
|
||||
iveness: 'ive',
|
||||
fulness: 'ful',
|
||||
ousness: 'ous',
|
||||
aliti: 'al',
|
||||
iviti: 'ive',
|
||||
biliti: 'ble',
|
||||
logi: 'log'
|
||||
};
|
||||
|
||||
var step3list = {
|
||||
icate: 'ic',
|
||||
ative: '',
|
||||
alize: 'al',
|
||||
iciti: 'ic',
|
||||
ical: 'ic',
|
||||
ful: '',
|
||||
ness: ''
|
||||
};
|
||||
|
||||
var c = "[^aeiou]"; // consonant
|
||||
var v = "[aeiouy]"; // vowel
|
||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
||||
var V = v + "[aeiou]*"; // vowel sequence
|
||||
|
||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
||||
|
||||
this.stemWord = function (w) {
|
||||
var stem;
|
||||
var suffix;
|
||||
var firstch;
|
||||
var origword = w;
|
||||
|
||||
if (w.length < 3)
|
||||
return w;
|
||||
|
||||
var re;
|
||||
var re2;
|
||||
var re3;
|
||||
var re4;
|
||||
|
||||
firstch = w.substr(0,1);
|
||||
if (firstch == "y")
|
||||
w = firstch.toUpperCase() + w.substr(1);
|
||||
|
||||
// Step 1a
|
||||
re = /^(.+?)(ss|i)es$/;
|
||||
re2 = /^(.+?)([^s])s$/;
|
||||
|
||||
if (re.test(w))
|
||||
w = w.replace(re,"$1$2");
|
||||
else if (re2.test(w))
|
||||
w = w.replace(re2,"$1$2");
|
||||
|
||||
// Step 1b
|
||||
re = /^(.+?)eed$/;
|
||||
re2 = /^(.+?)(ed|ing)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(fp[1])) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1];
|
||||
re2 = new RegExp(s_v);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
re2 = /(at|bl|iz)$/;
|
||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re2.test(w))
|
||||
w = w + "e";
|
||||
else if (re3.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
else if (re4.test(w))
|
||||
w = w + "e";
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1c
|
||||
re = /^(.+?)y$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(s_v);
|
||||
if (re.test(stem))
|
||||
w = stem + "i";
|
||||
}
|
||||
|
||||
// Step 2
|
||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step2list[suffix];
|
||||
}
|
||||
|
||||
// Step 3
|
||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step3list[suffix];
|
||||
}
|
||||
|
||||
// Step 4
|
||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
||||
re2 = /^(.+?)(s|t)(ion)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
if (re.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1] + fp[2];
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re2.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
|
||||
// Step 5
|
||||
re = /^(.+?)e$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
re2 = new RegExp(meq1);
|
||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
|
||||
w = stem;
|
||||
}
|
||||
re = /ll$/;
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re.test(w) && re2.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
|
||||
// and turn initial Y back to y
|
||||
if (firstch == "y")
|
||||
w = firstch.toLowerCase() + w.substr(1);
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Simple result scoring code.
|
||||
*/
|
||||
var Scorer = {
|
||||
// Implement the following function to further tweak the score for each result
|
||||
// The function takes a result array [filename, title, anchor, descr, score]
|
||||
// and returns the new score.
|
||||
/*
|
||||
score: function(result) {
|
||||
return result[4];
|
||||
},
|
||||
*/
|
||||
|
||||
// query matches the full name of an object
|
||||
objNameMatch: 11,
|
||||
// or matches in the last dotted part of the object name
|
||||
objPartialMatch: 6,
|
||||
// Additive scores depending on the priority of the object
|
||||
objPrio: {0: 15, // used to be importantResults
|
||||
1: 5, // used to be objectResults
|
||||
2: -5}, // used to be unimportantResults
|
||||
// Used when the priority is not in the mapping.
|
||||
objPrioDefault: 0,
|
||||
|
||||
// query found in title
|
||||
title: 15,
|
||||
// query found in terms
|
||||
term: 5
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Search Module
|
||||
*/
|
||||
var Search = {
|
||||
|
||||
_index : null,
|
||||
_queued_query : null,
|
||||
_pulse_status : -1,
|
||||
|
||||
init : function() {
|
||||
var params = $.getQueryParameters();
|
||||
if (params.q) {
|
||||
var query = params.q[0];
|
||||
$('input[name="q"]')[0].value = query;
|
||||
this.performSearch(query);
|
||||
}
|
||||
},
|
||||
|
||||
loadIndex : function(url) {
|
||||
$.ajax({type: "GET", url: url, data: null,
|
||||
dataType: "script", cache: true,
|
||||
complete: function(jqxhr, textstatus) {
|
||||
if (textstatus != "success") {
|
||||
document.getElementById("searchindexloader").src = url;
|
||||
}
|
||||
}});
|
||||
},
|
||||
|
||||
setIndex : function(index) {
|
||||
var q;
|
||||
this._index = index;
|
||||
if ((q = this._queued_query) !== null) {
|
||||
this._queued_query = null;
|
||||
Search.query(q);
|
||||
}
|
||||
},
|
||||
|
||||
hasIndex : function() {
|
||||
return this._index !== null;
|
||||
},
|
||||
|
||||
deferQuery : function(query) {
|
||||
this._queued_query = query;
|
||||
},
|
||||
|
||||
stopPulse : function() {
|
||||
this._pulse_status = 0;
|
||||
},
|
||||
|
||||
startPulse : function() {
|
||||
if (this._pulse_status >= 0)
|
||||
return;
|
||||
function pulse() {
|
||||
var i;
|
||||
Search._pulse_status = (Search._pulse_status + 1) % 4;
|
||||
var dotString = '';
|
||||
for (i = 0; i < Search._pulse_status; i++)
|
||||
dotString += '.';
|
||||
Search.dots.text(dotString);
|
||||
if (Search._pulse_status > -1)
|
||||
window.setTimeout(pulse, 500);
|
||||
}
|
||||
pulse();
|
||||
},
|
||||
|
||||
/**
|
||||
* perform a search for something (or wait until index is loaded)
|
||||
*/
|
||||
performSearch : function(query) {
|
||||
// create the required interface elements
|
||||
this.out = $('#search-results');
|
||||
this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
|
||||
this.dots = $('<span></span>').appendTo(this.title);
|
||||
this.status = $('<p style="display: none"></p>').appendTo(this.out);
|
||||
this.output = $('<ul class="search"/>').appendTo(this.out);
|
||||
|
||||
$('#search-progress').text(_('Preparing search...'));
|
||||
this.startPulse();
|
||||
|
||||
// index already loaded, the browser was quick!
|
||||
if (this.hasIndex())
|
||||
this.query(query);
|
||||
else
|
||||
this.deferQuery(query);
|
||||
},
|
||||
|
||||
/**
|
||||
* execute search (requires search index to be loaded)
|
||||
*/
|
||||
query : function(query) {
|
||||
var i;
|
||||
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
|
||||
|
||||
// stem the searchterms and add them to the correct list
|
||||
var stemmer = new Stemmer();
|
||||
var searchterms = [];
|
||||
var excluded = [];
|
||||
var hlterms = [];
|
||||
var tmp = query.split(/\s+/);
|
||||
var objectterms = [];
|
||||
for (i = 0; i < tmp.length; i++) {
|
||||
if (tmp[i] !== "") {
|
||||
objectterms.push(tmp[i].toLowerCase());
|
||||
}
|
||||
|
||||
if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i].match(/^\d+$/) ||
|
||||
tmp[i] === "") {
|
||||
// skip this "word"
|
||||
continue;
|
||||
}
|
||||
// stem the word
|
||||
var word = stemmer.stemWord(tmp[i].toLowerCase());
|
||||
var toAppend;
|
||||
// select the correct list
|
||||
if (word[0] == '-') {
|
||||
toAppend = excluded;
|
||||
word = word.substr(1);
|
||||
}
|
||||
else {
|
||||
toAppend = searchterms;
|
||||
hlterms.push(tmp[i].toLowerCase());
|
||||
}
|
||||
// only add if not already in the list
|
||||
if (!$u.contains(toAppend, word))
|
||||
toAppend.push(word);
|
||||
}
|
||||
var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
|
||||
|
||||
// console.debug('SEARCH: searching for:');
|
||||
// console.info('required: ', searchterms);
|
||||
// console.info('excluded: ', excluded);
|
||||
|
||||
// prepare search
|
||||
var terms = this._index.terms;
|
||||
var titleterms = this._index.titleterms;
|
||||
|
||||
// array of [filename, title, anchor, descr, score]
|
||||
var results = [];
|
||||
$('#search-progress').empty();
|
||||
|
||||
// lookup as object
|
||||
for (i = 0; i < objectterms.length; i++) {
|
||||
var others = [].concat(objectterms.slice(0, i),
|
||||
objectterms.slice(i+1, objectterms.length));
|
||||
results = results.concat(this.performObjectSearch(objectterms[i], others));
|
||||
}
|
||||
|
||||
// lookup as search terms in fulltext
|
||||
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
|
||||
|
||||
// let the scorer override scores with a custom scoring function
|
||||
if (Scorer.score) {
|
||||
for (i = 0; i < results.length; i++)
|
||||
results[i][4] = Scorer.score(results[i]);
|
||||
}
|
||||
|
||||
// now sort the results by score (in opposite order of appearance, since the
|
||||
// display function below uses pop() to retrieve items) and then
|
||||
// alphabetically
|
||||
results.sort(function(a, b) {
|
||||
var left = a[4];
|
||||
var right = b[4];
|
||||
if (left > right) {
|
||||
return 1;
|
||||
} else if (left < right) {
|
||||
return -1;
|
||||
} else {
|
||||
// same score: sort alphabetically
|
||||
left = a[1].toLowerCase();
|
||||
right = b[1].toLowerCase();
|
||||
return (left > right) ? -1 : ((left < right) ? 1 : 0);
|
||||
}
|
||||
});
|
||||
|
||||
// for debugging
|
||||
//Search.lastresults = results.slice(); // a copy
|
||||
//console.info('search results:', Search.lastresults);
|
||||
|
||||
// print the results
|
||||
var resultCount = results.length;
|
||||
function displayNextItem() {
|
||||
// results left, load the summary and display it
|
||||
if (results.length) {
|
||||
var item = results.pop();
|
||||
var listItem = $('<li style="display:none"></li>');
|
||||
if (DOCUMENTATION_OPTIONS.FILE_SUFFIX === '') {
|
||||
// dirhtml builder
|
||||
var dirname = item[0] + '/';
|
||||
if (dirname.match(/\/index\/$/)) {
|
||||
dirname = dirname.substring(0, dirname.length-6);
|
||||
} else if (dirname == 'index/') {
|
||||
dirname = '';
|
||||
}
|
||||
listItem.append($('<a/>').attr('href',
|
||||
DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
} else {
|
||||
// normal html builders
|
||||
listItem.append($('<a/>').attr('href',
|
||||
item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
}
|
||||
if (item[3]) {
|
||||
listItem.append($('<span> (' + item[3] + ')</span>'));
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
|
||||
$.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] + '.txt',
|
||||
dataType: "text",
|
||||
complete: function(jqxhr, textstatus) {
|
||||
var data = jqxhr.responseText;
|
||||
if (data !== '' && data !== undefined) {
|
||||
listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
|
||||
}
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}});
|
||||
} else {
|
||||
// no source available, just display title
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}
|
||||
}
|
||||
// search finished, update title and status message
|
||||
else {
|
||||
Search.stopPulse();
|
||||
Search.title.text(_('Search Results'));
|
||||
if (!resultCount)
|
||||
Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
|
||||
else
|
||||
Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
|
||||
Search.status.fadeIn(500);
|
||||
}
|
||||
}
|
||||
displayNextItem();
|
||||
},
|
||||
|
||||
/**
|
||||
* search for object names
|
||||
*/
|
||||
performObjectSearch : function(object, otherterms) {
|
||||
var filenames = this._index.filenames;
|
||||
var objects = this._index.objects;
|
||||
var objnames = this._index.objnames;
|
||||
var titles = this._index.titles;
|
||||
|
||||
var i;
|
||||
var results = [];
|
||||
|
||||
for (var prefix in objects) {
|
||||
for (var name in objects[prefix]) {
|
||||
var fullname = (prefix ? prefix + '.' : '') + name;
|
||||
if (fullname.toLowerCase().indexOf(object) > -1) {
|
||||
var score = 0;
|
||||
var parts = fullname.split('.');
|
||||
// check for different match types: exact matches of full name or
|
||||
// "last name" (i.e. last dotted part)
|
||||
if (fullname == object || parts[parts.length - 1] == object) {
|
||||
score += Scorer.objNameMatch;
|
||||
// matches in last name
|
||||
} else if (parts[parts.length - 1].indexOf(object) > -1) {
|
||||
score += Scorer.objPartialMatch;
|
||||
}
|
||||
var match = objects[prefix][name];
|
||||
var objname = objnames[match[1]][2];
|
||||
var title = titles[match[0]];
|
||||
// If more than one term searched for, we require other words to be
|
||||
// found in the name/title/description
|
||||
if (otherterms.length > 0) {
|
||||
var haystack = (prefix + ' ' + name + ' ' +
|
||||
objname + ' ' + title).toLowerCase();
|
||||
var allfound = true;
|
||||
for (i = 0; i < otherterms.length; i++) {
|
||||
if (haystack.indexOf(otherterms[i]) == -1) {
|
||||
allfound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allfound) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var descr = objname + _(', in ') + title;
|
||||
|
||||
var anchor = match[3];
|
||||
if (anchor === '')
|
||||
anchor = fullname;
|
||||
else if (anchor == '-')
|
||||
anchor = objnames[match[1]][1] + '-' + fullname;
|
||||
// add custom score for some objects according to scorer
|
||||
if (Scorer.objPrio.hasOwnProperty(match[2])) {
|
||||
score += Scorer.objPrio[match[2]];
|
||||
} else {
|
||||
score += Scorer.objPrioDefault;
|
||||
}
|
||||
results.push([filenames[match[0]], fullname, '#'+anchor, descr, score]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
/**
|
||||
* search for full-text terms in the index
|
||||
*/
|
||||
performTermsSearch : function(searchterms, excluded, terms, titleterms) {
|
||||
var filenames = this._index.filenames;
|
||||
var titles = this._index.titles;
|
||||
|
||||
var i, j, file;
|
||||
var fileMap = {};
|
||||
var scoreMap = {};
|
||||
var results = [];
|
||||
|
||||
// perform the search on the required terms
|
||||
for (i = 0; i < searchterms.length; i++) {
|
||||
var word = searchterms[i];
|
||||
var files = [];
|
||||
var _o = [
|
||||
{files: terms[word], score: Scorer.term},
|
||||
{files: titleterms[word], score: Scorer.title}
|
||||
];
|
||||
|
||||
// no match but word was a required one
|
||||
if ($u.every(_o, function(o){return o.files === undefined;})) {
|
||||
break;
|
||||
}
|
||||
// found search word in contents
|
||||
$u.each(_o, function(o) {
|
||||
var _files = o.files;
|
||||
if (_files === undefined)
|
||||
return
|
||||
|
||||
if (_files.length === undefined)
|
||||
_files = [_files];
|
||||
files = files.concat(_files);
|
||||
|
||||
// set score for the word in each file to Scorer.term
|
||||
for (j = 0; j < _files.length; j++) {
|
||||
file = _files[j];
|
||||
if (!(file in scoreMap))
|
||||
scoreMap[file] = {}
|
||||
scoreMap[file][word] = o.score;
|
||||
}
|
||||
});
|
||||
|
||||
// create the mapping
|
||||
for (j = 0; j < files.length; j++) {
|
||||
file = files[j];
|
||||
if (file in fileMap)
|
||||
fileMap[file].push(word);
|
||||
else
|
||||
fileMap[file] = [word];
|
||||
}
|
||||
}
|
||||
|
||||
// now check if the files don't contain excluded terms
|
||||
for (file in fileMap) {
|
||||
var valid = true;
|
||||
|
||||
// check if all requirements are matched
|
||||
if (fileMap[file].length != searchterms.length)
|
||||
continue;
|
||||
|
||||
// ensure that none of the excluded terms is in the search result
|
||||
for (i = 0; i < excluded.length; i++) {
|
||||
if (terms[excluded[i]] == file ||
|
||||
titleterms[excluded[i]] == file ||
|
||||
$u.contains(terms[excluded[i]] || [], file) ||
|
||||
$u.contains(titleterms[excluded[i]] || [], file)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have still a valid result we can add it to the result list
|
||||
if (valid) {
|
||||
// select one (max) score for the file.
|
||||
// for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
|
||||
var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
|
||||
results.push([filenames[file], titles[file], '', null, score]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
},
|
||||
|
||||
/**
|
||||
* helper function to return a node containing the
|
||||
* search summary for a given text. keywords is a list
|
||||
* of stemmed words, hlwords is the list of normal, unstemmed
|
||||
* words. the first one is used to find the occurance, the
|
||||
* latter for highlighting it.
|
||||
*/
|
||||
makeSearchSummary : function(text, keywords, hlwords) {
|
||||
var textLower = text.toLowerCase();
|
||||
var start = 0;
|
||||
$.each(keywords, function() {
|
||||
var i = textLower.indexOf(this.toLowerCase());
|
||||
if (i > -1)
|
||||
start = i;
|
||||
});
|
||||
start = Math.max(start - 120, 0);
|
||||
var excerpt = ((start > 0) ? '...' : '') +
|
||||
$.trim(text.substr(start, 240)) +
|
||||
((start + 240 - text.length) ? '...' : '');
|
||||
var rv = $('<div class="context"></div>').text(excerpt);
|
||||
$.each(hlwords, function() {
|
||||
rv = rv.highlightText(this, 'highlighted');
|
||||
});
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
Search.init();
|
||||
});
|
||||
@@ -0,0 +1,999 @@
|
||||
// Underscore.js 1.3.1
|
||||
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Underscore is freely distributable under the MIT license.
|
||||
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||
// For all details and documentation:
|
||||
// http://documentcloud.github.com/underscore
|
||||
|
||||
(function() {
|
||||
|
||||
// Baseline setup
|
||||
// --------------
|
||||
|
||||
// Establish the root object, `window` in the browser, or `global` on the server.
|
||||
var root = this;
|
||||
|
||||
// Save the previous value of the `_` variable.
|
||||
var previousUnderscore = root._;
|
||||
|
||||
// Establish the object that gets returned to break out of a loop iteration.
|
||||
var breaker = {};
|
||||
|
||||
// Save bytes in the minified (but not gzipped) version:
|
||||
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
||||
|
||||
// Create quick reference variables for speed access to core prototypes.
|
||||
var slice = ArrayProto.slice,
|
||||
unshift = ArrayProto.unshift,
|
||||
toString = ObjProto.toString,
|
||||
hasOwnProperty = ObjProto.hasOwnProperty;
|
||||
|
||||
// All **ECMAScript 5** native function implementations that we hope to use
|
||||
// are declared here.
|
||||
var
|
||||
nativeForEach = ArrayProto.forEach,
|
||||
nativeMap = ArrayProto.map,
|
||||
nativeReduce = ArrayProto.reduce,
|
||||
nativeReduceRight = ArrayProto.reduceRight,
|
||||
nativeFilter = ArrayProto.filter,
|
||||
nativeEvery = ArrayProto.every,
|
||||
nativeSome = ArrayProto.some,
|
||||
nativeIndexOf = ArrayProto.indexOf,
|
||||
nativeLastIndexOf = ArrayProto.lastIndexOf,
|
||||
nativeIsArray = Array.isArray,
|
||||
nativeKeys = Object.keys,
|
||||
nativeBind = FuncProto.bind;
|
||||
|
||||
// Create a safe reference to the Underscore object for use below.
|
||||
var _ = function(obj) { return new wrapper(obj); };
|
||||
|
||||
// Export the Underscore object for **Node.js**, with
|
||||
// backwards-compatibility for the old `require()` API. If we're in
|
||||
// the browser, add `_` as a global object via a string identifier,
|
||||
// for Closure Compiler "advanced" mode.
|
||||
if (typeof exports !== 'undefined') {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
exports = module.exports = _;
|
||||
}
|
||||
exports._ = _;
|
||||
} else {
|
||||
root['_'] = _;
|
||||
}
|
||||
|
||||
// Current version.
|
||||
_.VERSION = '1.3.1';
|
||||
|
||||
// Collection Functions
|
||||
// --------------------
|
||||
|
||||
// The cornerstone, an `each` implementation, aka `forEach`.
|
||||
// Handles objects with the built-in `forEach`, arrays, and raw objects.
|
||||
// Delegates to **ECMAScript 5**'s native `forEach` if available.
|
||||
var each = _.each = _.forEach = function(obj, iterator, context) {
|
||||
if (obj == null) return;
|
||||
if (nativeForEach && obj.forEach === nativeForEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (obj.length === +obj.length) {
|
||||
for (var i = 0, l = obj.length; i < l; i++) {
|
||||
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
|
||||
}
|
||||
} else {
|
||||
for (var key in obj) {
|
||||
if (_.has(obj, key)) {
|
||||
if (iterator.call(context, obj[key], key, obj) === breaker) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Return the results of applying the iterator to each element.
|
||||
// Delegates to **ECMAScript 5**'s native `map` if available.
|
||||
_.map = _.collect = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
results[results.length] = iterator.call(context, value, index, list);
|
||||
});
|
||||
if (obj.length === +obj.length) results.length = obj.length;
|
||||
return results;
|
||||
};
|
||||
|
||||
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
||||
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
||||
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
||||
var initial = arguments.length > 2;
|
||||
if (obj == null) obj = [];
|
||||
if (nativeReduce && obj.reduce === nativeReduce) {
|
||||
if (context) iterator = _.bind(iterator, context);
|
||||
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
||||
}
|
||||
each(obj, function(value, index, list) {
|
||||
if (!initial) {
|
||||
memo = value;
|
||||
initial = true;
|
||||
} else {
|
||||
memo = iterator.call(context, memo, value, index, list);
|
||||
}
|
||||
});
|
||||
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
|
||||
return memo;
|
||||
};
|
||||
|
||||
// The right-associative version of reduce, also known as `foldr`.
|
||||
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
||||
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
||||
var initial = arguments.length > 2;
|
||||
if (obj == null) obj = [];
|
||||
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
||||
if (context) iterator = _.bind(iterator, context);
|
||||
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
||||
}
|
||||
var reversed = _.toArray(obj).reverse();
|
||||
if (context && !initial) iterator = _.bind(iterator, context);
|
||||
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
|
||||
};
|
||||
|
||||
// Return the first value which passes a truth test. Aliased as `detect`.
|
||||
_.find = _.detect = function(obj, iterator, context) {
|
||||
var result;
|
||||
any(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) {
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Return all the elements that pass a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `filter` if available.
|
||||
// Aliased as `select`.
|
||||
_.filter = _.select = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) results[results.length] = value;
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Return all the elements for which a truth test fails.
|
||||
_.reject = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
each(obj, function(value, index, list) {
|
||||
if (!iterator.call(context, value, index, list)) results[results.length] = value;
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Determine whether all of the elements match a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `every` if available.
|
||||
// Aliased as `all`.
|
||||
_.every = _.all = function(obj, iterator, context) {
|
||||
var result = true;
|
||||
if (obj == null) return result;
|
||||
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Determine if at least one element in the object matches a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `some` if available.
|
||||
// Aliased as `any`.
|
||||
var any = _.some = _.any = function(obj, iterator, context) {
|
||||
iterator || (iterator = _.identity);
|
||||
var result = false;
|
||||
if (obj == null) return result;
|
||||
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (result || (result = iterator.call(context, value, index, list))) return breaker;
|
||||
});
|
||||
return !!result;
|
||||
};
|
||||
|
||||
// Determine if a given value is included in the array or object using `===`.
|
||||
// Aliased as `contains`.
|
||||
_.include = _.contains = function(obj, target) {
|
||||
var found = false;
|
||||
if (obj == null) return found;
|
||||
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
||||
found = any(obj, function(value) {
|
||||
return value === target;
|
||||
});
|
||||
return found;
|
||||
};
|
||||
|
||||
// Invoke a method (with arguments) on every item in a collection.
|
||||
_.invoke = function(obj, method) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return _.map(obj, function(value) {
|
||||
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
|
||||
});
|
||||
};
|
||||
|
||||
// Convenience version of a common use case of `map`: fetching a property.
|
||||
_.pluck = function(obj, key) {
|
||||
return _.map(obj, function(value){ return value[key]; });
|
||||
};
|
||||
|
||||
// Return the maximum element or (element-based computation).
|
||||
_.max = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
|
||||
if (!iterator && _.isEmpty(obj)) return -Infinity;
|
||||
var result = {computed : -Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed >= result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Return the minimum element (or element-based computation).
|
||||
_.min = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
|
||||
if (!iterator && _.isEmpty(obj)) return Infinity;
|
||||
var result = {computed : Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed < result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Shuffle an array.
|
||||
_.shuffle = function(obj) {
|
||||
var shuffled = [], rand;
|
||||
each(obj, function(value, index, list) {
|
||||
if (index == 0) {
|
||||
shuffled[0] = value;
|
||||
} else {
|
||||
rand = Math.floor(Math.random() * (index + 1));
|
||||
shuffled[index] = shuffled[rand];
|
||||
shuffled[rand] = value;
|
||||
}
|
||||
});
|
||||
return shuffled;
|
||||
};
|
||||
|
||||
// Sort the object's values by a criterion produced by an iterator.
|
||||
_.sortBy = function(obj, iterator, context) {
|
||||
return _.pluck(_.map(obj, function(value, index, list) {
|
||||
return {
|
||||
value : value,
|
||||
criteria : iterator.call(context, value, index, list)
|
||||
};
|
||||
}).sort(function(left, right) {
|
||||
var a = left.criteria, b = right.criteria;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}), 'value');
|
||||
};
|
||||
|
||||
// Groups the object's values by a criterion. Pass either a string attribute
|
||||
// to group by, or a function that returns the criterion.
|
||||
_.groupBy = function(obj, val) {
|
||||
var result = {};
|
||||
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
|
||||
each(obj, function(value, index) {
|
||||
var key = iterator(value, index);
|
||||
(result[key] || (result[key] = [])).push(value);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Use a comparator function to figure out at what index an object should
|
||||
// be inserted so as to maintain order. Uses binary search.
|
||||
_.sortedIndex = function(array, obj, iterator) {
|
||||
iterator || (iterator = _.identity);
|
||||
var low = 0, high = array.length;
|
||||
while (low < high) {
|
||||
var mid = (low + high) >> 1;
|
||||
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
|
||||
}
|
||||
return low;
|
||||
};
|
||||
|
||||
// Safely convert anything iterable into a real, live array.
|
||||
_.toArray = function(iterable) {
|
||||
if (!iterable) return [];
|
||||
if (iterable.toArray) return iterable.toArray();
|
||||
if (_.isArray(iterable)) return slice.call(iterable);
|
||||
if (_.isArguments(iterable)) return slice.call(iterable);
|
||||
return _.values(iterable);
|
||||
};
|
||||
|
||||
// Return the number of elements in an object.
|
||||
_.size = function(obj) {
|
||||
return _.toArray(obj).length;
|
||||
};
|
||||
|
||||
// Array Functions
|
||||
// ---------------
|
||||
|
||||
// Get the first element of an array. Passing **n** will return the first N
|
||||
// values in the array. Aliased as `head`. The **guard** check allows it to work
|
||||
// with `_.map`.
|
||||
_.first = _.head = function(array, n, guard) {
|
||||
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
|
||||
};
|
||||
|
||||
// Returns everything but the last entry of the array. Especcialy useful on
|
||||
// the arguments object. Passing **n** will return all the values in
|
||||
// the array, excluding the last N. The **guard** check allows it to work with
|
||||
// `_.map`.
|
||||
_.initial = function(array, n, guard) {
|
||||
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
|
||||
};
|
||||
|
||||
// Get the last element of an array. Passing **n** will return the last N
|
||||
// values in the array. The **guard** check allows it to work with `_.map`.
|
||||
_.last = function(array, n, guard) {
|
||||
if ((n != null) && !guard) {
|
||||
return slice.call(array, Math.max(array.length - n, 0));
|
||||
} else {
|
||||
return array[array.length - 1];
|
||||
}
|
||||
};
|
||||
|
||||
// Returns everything but the first entry of the array. Aliased as `tail`.
|
||||
// Especially useful on the arguments object. Passing an **index** will return
|
||||
// the rest of the values in the array from that index onward. The **guard**
|
||||
// check allows it to work with `_.map`.
|
||||
_.rest = _.tail = function(array, index, guard) {
|
||||
return slice.call(array, (index == null) || guard ? 1 : index);
|
||||
};
|
||||
|
||||
// Trim out all falsy values from an array.
|
||||
_.compact = function(array) {
|
||||
return _.filter(array, function(value){ return !!value; });
|
||||
};
|
||||
|
||||
// Return a completely flattened version of an array.
|
||||
_.flatten = function(array, shallow) {
|
||||
return _.reduce(array, function(memo, value) {
|
||||
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
|
||||
memo[memo.length] = value;
|
||||
return memo;
|
||||
}, []);
|
||||
};
|
||||
|
||||
// Return a version of the array that does not contain the specified value(s).
|
||||
_.without = function(array) {
|
||||
return _.difference(array, slice.call(arguments, 1));
|
||||
};
|
||||
|
||||
// Produce a duplicate-free version of the array. If the array has already
|
||||
// been sorted, you have the option of using a faster algorithm.
|
||||
// Aliased as `unique`.
|
||||
_.uniq = _.unique = function(array, isSorted, iterator) {
|
||||
var initial = iterator ? _.map(array, iterator) : array;
|
||||
var result = [];
|
||||
_.reduce(initial, function(memo, el, i) {
|
||||
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
|
||||
memo[memo.length] = el;
|
||||
result[result.length] = array[i];
|
||||
}
|
||||
return memo;
|
||||
}, []);
|
||||
return result;
|
||||
};
|
||||
|
||||
// Produce an array that contains the union: each distinct element from all of
|
||||
// the passed-in arrays.
|
||||
_.union = function() {
|
||||
return _.uniq(_.flatten(arguments, true));
|
||||
};
|
||||
|
||||
// Produce an array that contains every item shared between all the
|
||||
// passed-in arrays. (Aliased as "intersect" for back-compat.)
|
||||
_.intersection = _.intersect = function(array) {
|
||||
var rest = slice.call(arguments, 1);
|
||||
return _.filter(_.uniq(array), function(item) {
|
||||
return _.every(rest, function(other) {
|
||||
return _.indexOf(other, item) >= 0;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Take the difference between one array and a number of other arrays.
|
||||
// Only the elements present in just the first array will remain.
|
||||
_.difference = function(array) {
|
||||
var rest = _.flatten(slice.call(arguments, 1));
|
||||
return _.filter(array, function(value){ return !_.include(rest, value); });
|
||||
};
|
||||
|
||||
// Zip together multiple lists into a single array -- elements that share
|
||||
// an index go together.
|
||||
_.zip = function() {
|
||||
var args = slice.call(arguments);
|
||||
var length = _.max(_.pluck(args, 'length'));
|
||||
var results = new Array(length);
|
||||
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
|
||||
return results;
|
||||
};
|
||||
|
||||
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
|
||||
// we need this function. Return the position of the first occurrence of an
|
||||
// item in an array, or -1 if the item is not included in the array.
|
||||
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
|
||||
// If the array is large and already in sort order, pass `true`
|
||||
// for **isSorted** to use binary search.
|
||||
_.indexOf = function(array, item, isSorted) {
|
||||
if (array == null) return -1;
|
||||
var i, l;
|
||||
if (isSorted) {
|
||||
i = _.sortedIndex(array, item);
|
||||
return array[i] === item ? i : -1;
|
||||
}
|
||||
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
||||
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
||||
_.lastIndexOf = function(array, item) {
|
||||
if (array == null) return -1;
|
||||
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
|
||||
var i = array.length;
|
||||
while (i--) if (i in array && array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Generate an integer Array containing an arithmetic progression. A port of
|
||||
// the native Python `range()` function. See
|
||||
// [the Python documentation](http://docs.python.org/library/functions.html#range).
|
||||
_.range = function(start, stop, step) {
|
||||
if (arguments.length <= 1) {
|
||||
stop = start || 0;
|
||||
start = 0;
|
||||
}
|
||||
step = arguments[2] || 1;
|
||||
|
||||
var len = Math.max(Math.ceil((stop - start) / step), 0);
|
||||
var idx = 0;
|
||||
var range = new Array(len);
|
||||
|
||||
while(idx < len) {
|
||||
range[idx++] = start;
|
||||
start += step;
|
||||
}
|
||||
|
||||
return range;
|
||||
};
|
||||
|
||||
// Function (ahem) Functions
|
||||
// ------------------
|
||||
|
||||
// Reusable constructor function for prototype setting.
|
||||
var ctor = function(){};
|
||||
|
||||
// Create a function bound to a given object (assigning `this`, and arguments,
|
||||
// optionally). Binding with arguments is also known as `curry`.
|
||||
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
|
||||
// We check for `func.bind` first, to fail fast when `func` is undefined.
|
||||
_.bind = function bind(func, context) {
|
||||
var bound, args;
|
||||
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
||||
if (!_.isFunction(func)) throw new TypeError;
|
||||
args = slice.call(arguments, 2);
|
||||
return bound = function() {
|
||||
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
|
||||
ctor.prototype = func.prototype;
|
||||
var self = new ctor;
|
||||
var result = func.apply(self, args.concat(slice.call(arguments)));
|
||||
if (Object(result) === result) return result;
|
||||
return self;
|
||||
};
|
||||
};
|
||||
|
||||
// Bind all of an object's methods to that object. Useful for ensuring that
|
||||
// all callbacks defined on an object belong to it.
|
||||
_.bindAll = function(obj) {
|
||||
var funcs = slice.call(arguments, 1);
|
||||
if (funcs.length == 0) funcs = _.functions(obj);
|
||||
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Memoize an expensive function by storing its results.
|
||||
_.memoize = function(func, hasher) {
|
||||
var memo = {};
|
||||
hasher || (hasher = _.identity);
|
||||
return function() {
|
||||
var key = hasher.apply(this, arguments);
|
||||
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
|
||||
};
|
||||
};
|
||||
|
||||
// Delays a function for the given number of milliseconds, and then calls
|
||||
// it with the arguments supplied.
|
||||
_.delay = function(func, wait) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return setTimeout(function(){ return func.apply(func, args); }, wait);
|
||||
};
|
||||
|
||||
// Defers a function, scheduling it to run after the current call stack has
|
||||
// cleared.
|
||||
_.defer = function(func) {
|
||||
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
||||
};
|
||||
|
||||
// Returns a function, that, when invoked, will only be triggered at most once
|
||||
// during a given window of time.
|
||||
_.throttle = function(func, wait) {
|
||||
var context, args, timeout, throttling, more;
|
||||
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
|
||||
return function() {
|
||||
context = this; args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
if (more) func.apply(context, args);
|
||||
whenDone();
|
||||
};
|
||||
if (!timeout) timeout = setTimeout(later, wait);
|
||||
if (throttling) {
|
||||
more = true;
|
||||
} else {
|
||||
func.apply(context, args);
|
||||
}
|
||||
whenDone();
|
||||
throttling = true;
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds.
|
||||
_.debounce = function(func, wait) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this, args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that will be executed at most one time, no matter how
|
||||
// often you call it. Useful for lazy initialization.
|
||||
_.once = function(func) {
|
||||
var ran = false, memo;
|
||||
return function() {
|
||||
if (ran) return memo;
|
||||
ran = true;
|
||||
return memo = func.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns the first function passed as an argument to the second,
|
||||
// allowing you to adjust arguments, run code before and after, and
|
||||
// conditionally execute the original function.
|
||||
_.wrap = function(func, wrapper) {
|
||||
return function() {
|
||||
var args = [func].concat(slice.call(arguments, 0));
|
||||
return wrapper.apply(this, args);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that is the composition of a list of functions, each
|
||||
// consuming the return value of the function that follows.
|
||||
_.compose = function() {
|
||||
var funcs = arguments;
|
||||
return function() {
|
||||
var args = arguments;
|
||||
for (var i = funcs.length - 1; i >= 0; i--) {
|
||||
args = [funcs[i].apply(this, args)];
|
||||
}
|
||||
return args[0];
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that will only be executed after being called N times.
|
||||
_.after = function(times, func) {
|
||||
if (times <= 0) return func();
|
||||
return function() {
|
||||
if (--times < 1) { return func.apply(this, arguments); }
|
||||
};
|
||||
};
|
||||
|
||||
// Object Functions
|
||||
// ----------------
|
||||
|
||||
// Retrieve the names of an object's properties.
|
||||
// Delegates to **ECMAScript 5**'s native `Object.keys`
|
||||
_.keys = nativeKeys || function(obj) {
|
||||
if (obj !== Object(obj)) throw new TypeError('Invalid object');
|
||||
var keys = [];
|
||||
for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
|
||||
return keys;
|
||||
};
|
||||
|
||||
// Retrieve the values of an object's properties.
|
||||
_.values = function(obj) {
|
||||
return _.map(obj, _.identity);
|
||||
};
|
||||
|
||||
// Return a sorted list of the function names available on the object.
|
||||
// Aliased as `methods`
|
||||
_.functions = _.methods = function(obj) {
|
||||
var names = [];
|
||||
for (var key in obj) {
|
||||
if (_.isFunction(obj[key])) names.push(key);
|
||||
}
|
||||
return names.sort();
|
||||
};
|
||||
|
||||
// Extend a given object with all the properties in passed-in object(s).
|
||||
_.extend = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) {
|
||||
obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Fill in a given object with default properties.
|
||||
_.defaults = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) {
|
||||
if (obj[prop] == null) obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Create a (shallow-cloned) duplicate of an object.
|
||||
_.clone = function(obj) {
|
||||
if (!_.isObject(obj)) return obj;
|
||||
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
||||
};
|
||||
|
||||
// Invokes interceptor with the obj, and then returns obj.
|
||||
// The primary purpose of this method is to "tap into" a method chain, in
|
||||
// order to perform operations on intermediate results within the chain.
|
||||
_.tap = function(obj, interceptor) {
|
||||
interceptor(obj);
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Internal recursive comparison function.
|
||||
function eq(a, b, stack) {
|
||||
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
||||
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
|
||||
if (a === b) return a !== 0 || 1 / a == 1 / b;
|
||||
// A strict comparison is necessary because `null == undefined`.
|
||||
if (a == null || b == null) return a === b;
|
||||
// Unwrap any wrapped objects.
|
||||
if (a._chain) a = a._wrapped;
|
||||
if (b._chain) b = b._wrapped;
|
||||
// Invoke a custom `isEqual` method if one is provided.
|
||||
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
|
||||
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
|
||||
// Compare `[[Class]]` names.
|
||||
var className = toString.call(a);
|
||||
if (className != toString.call(b)) return false;
|
||||
switch (className) {
|
||||
// Strings, numbers, dates, and booleans are compared by value.
|
||||
case '[object String]':
|
||||
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
||||
// equivalent to `new String("5")`.
|
||||
return a == String(b);
|
||||
case '[object Number]':
|
||||
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
|
||||
// other numeric values.
|
||||
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
|
||||
case '[object Date]':
|
||||
case '[object Boolean]':
|
||||
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
||||
// millisecond representations. Note that invalid dates with millisecond representations
|
||||
// of `NaN` are not equivalent.
|
||||
return +a == +b;
|
||||
// RegExps are compared by their source patterns and flags.
|
||||
case '[object RegExp]':
|
||||
return a.source == b.source &&
|
||||
a.global == b.global &&
|
||||
a.multiline == b.multiline &&
|
||||
a.ignoreCase == b.ignoreCase;
|
||||
}
|
||||
if (typeof a != 'object' || typeof b != 'object') return false;
|
||||
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
||||
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
||||
var length = stack.length;
|
||||
while (length--) {
|
||||
// Linear search. Performance is inversely proportional to the number of
|
||||
// unique nested structures.
|
||||
if (stack[length] == a) return true;
|
||||
}
|
||||
// Add the first object to the stack of traversed objects.
|
||||
stack.push(a);
|
||||
var size = 0, result = true;
|
||||
// Recursively compare objects and arrays.
|
||||
if (className == '[object Array]') {
|
||||
// Compare array lengths to determine if a deep comparison is necessary.
|
||||
size = a.length;
|
||||
result = size == b.length;
|
||||
if (result) {
|
||||
// Deep compare the contents, ignoring non-numeric properties.
|
||||
while (size--) {
|
||||
// Ensure commutative equality for sparse arrays.
|
||||
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Objects with different constructors are not equivalent.
|
||||
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
|
||||
// Deep compare objects.
|
||||
for (var key in a) {
|
||||
if (_.has(a, key)) {
|
||||
// Count the expected number of properties.
|
||||
size++;
|
||||
// Deep compare each member.
|
||||
if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
|
||||
}
|
||||
}
|
||||
// Ensure that both objects contain the same number of properties.
|
||||
if (result) {
|
||||
for (key in b) {
|
||||
if (_.has(b, key) && !(size--)) break;
|
||||
}
|
||||
result = !size;
|
||||
}
|
||||
}
|
||||
// Remove the first object from the stack of traversed objects.
|
||||
stack.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Perform a deep comparison to check if two objects are equal.
|
||||
_.isEqual = function(a, b) {
|
||||
return eq(a, b, []);
|
||||
};
|
||||
|
||||
// Is a given array, string, or object empty?
|
||||
// An "empty" object has no enumerable own-properties.
|
||||
_.isEmpty = function(obj) {
|
||||
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
||||
for (var key in obj) if (_.has(obj, key)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Is a given value a DOM element?
|
||||
_.isElement = function(obj) {
|
||||
return !!(obj && obj.nodeType == 1);
|
||||
};
|
||||
|
||||
// Is a given value an array?
|
||||
// Delegates to ECMA5's native Array.isArray
|
||||
_.isArray = nativeIsArray || function(obj) {
|
||||
return toString.call(obj) == '[object Array]';
|
||||
};
|
||||
|
||||
// Is a given variable an object?
|
||||
_.isObject = function(obj) {
|
||||
return obj === Object(obj);
|
||||
};
|
||||
|
||||
// Is a given variable an arguments object?
|
||||
_.isArguments = function(obj) {
|
||||
return toString.call(obj) == '[object Arguments]';
|
||||
};
|
||||
if (!_.isArguments(arguments)) {
|
||||
_.isArguments = function(obj) {
|
||||
return !!(obj && _.has(obj, 'callee'));
|
||||
};
|
||||
}
|
||||
|
||||
// Is a given value a function?
|
||||
_.isFunction = function(obj) {
|
||||
return toString.call(obj) == '[object Function]';
|
||||
};
|
||||
|
||||
// Is a given value a string?
|
||||
_.isString = function(obj) {
|
||||
return toString.call(obj) == '[object String]';
|
||||
};
|
||||
|
||||
// Is a given value a number?
|
||||
_.isNumber = function(obj) {
|
||||
return toString.call(obj) == '[object Number]';
|
||||
};
|
||||
|
||||
// Is the given value `NaN`?
|
||||
_.isNaN = function(obj) {
|
||||
// `NaN` is the only value for which `===` is not reflexive.
|
||||
return obj !== obj;
|
||||
};
|
||||
|
||||
// Is a given value a boolean?
|
||||
_.isBoolean = function(obj) {
|
||||
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
|
||||
};
|
||||
|
||||
// Is a given value a date?
|
||||
_.isDate = function(obj) {
|
||||
return toString.call(obj) == '[object Date]';
|
||||
};
|
||||
|
||||
// Is the given value a regular expression?
|
||||
_.isRegExp = function(obj) {
|
||||
return toString.call(obj) == '[object RegExp]';
|
||||
};
|
||||
|
||||
// Is a given value equal to null?
|
||||
_.isNull = function(obj) {
|
||||
return obj === null;
|
||||
};
|
||||
|
||||
// Is a given variable undefined?
|
||||
_.isUndefined = function(obj) {
|
||||
return obj === void 0;
|
||||
};
|
||||
|
||||
// Has own property?
|
||||
_.has = function(obj, key) {
|
||||
return hasOwnProperty.call(obj, key);
|
||||
};
|
||||
|
||||
// Utility Functions
|
||||
// -----------------
|
||||
|
||||
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
||||
// previous owner. Returns a reference to the Underscore object.
|
||||
_.noConflict = function() {
|
||||
root._ = previousUnderscore;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Keep the identity function around for default iterators.
|
||||
_.identity = function(value) {
|
||||
return value;
|
||||
};
|
||||
|
||||
// Run a function **n** times.
|
||||
_.times = function (n, iterator, context) {
|
||||
for (var i = 0; i < n; i++) iterator.call(context, i);
|
||||
};
|
||||
|
||||
// Escape a string for HTML interpolation.
|
||||
_.escape = function(string) {
|
||||
return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
|
||||
};
|
||||
|
||||
// Add your own custom functions to the Underscore object, ensuring that
|
||||
// they're correctly added to the OOP wrapper as well.
|
||||
_.mixin = function(obj) {
|
||||
each(_.functions(obj), function(name){
|
||||
addToWrapper(name, _[name] = obj[name]);
|
||||
});
|
||||
};
|
||||
|
||||
// Generate a unique integer id (unique within the entire client session).
|
||||
// Useful for temporary DOM ids.
|
||||
var idCounter = 0;
|
||||
_.uniqueId = function(prefix) {
|
||||
var id = idCounter++;
|
||||
return prefix ? prefix + id : id;
|
||||
};
|
||||
|
||||
// By default, Underscore uses ERB-style template delimiters, change the
|
||||
// following template settings to use alternative delimiters.
|
||||
_.templateSettings = {
|
||||
evaluate : /<%([\s\S]+?)%>/g,
|
||||
interpolate : /<%=([\s\S]+?)%>/g,
|
||||
escape : /<%-([\s\S]+?)%>/g
|
||||
};
|
||||
|
||||
// When customizing `templateSettings`, if you don't want to define an
|
||||
// interpolation, evaluation or escaping regex, we need one that is
|
||||
// guaranteed not to match.
|
||||
var noMatch = /.^/;
|
||||
|
||||
// Within an interpolation, evaluation, or escaping, remove HTML escaping
|
||||
// that had been previously added.
|
||||
var unescape = function(code) {
|
||||
return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
|
||||
};
|
||||
|
||||
// JavaScript micro-templating, similar to John Resig's implementation.
|
||||
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
||||
// and correctly escapes quotes within interpolated code.
|
||||
_.template = function(str, data) {
|
||||
var c = _.templateSettings;
|
||||
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
|
||||
'with(obj||{}){__p.push(\'' +
|
||||
str.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(c.escape || noMatch, function(match, code) {
|
||||
return "',_.escape(" + unescape(code) + "),'";
|
||||
})
|
||||
.replace(c.interpolate || noMatch, function(match, code) {
|
||||
return "'," + unescape(code) + ",'";
|
||||
})
|
||||
.replace(c.evaluate || noMatch, function(match, code) {
|
||||
return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
|
||||
})
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\t/g, '\\t')
|
||||
+ "');}return __p.join('');";
|
||||
var func = new Function('obj', '_', tmpl);
|
||||
if (data) return func(data, _);
|
||||
return function(data) {
|
||||
return func.call(this, data, _);
|
||||
};
|
||||
};
|
||||
|
||||
// Add a "chain" function, which will delegate to the wrapper.
|
||||
_.chain = function(obj) {
|
||||
return _(obj).chain();
|
||||
};
|
||||
|
||||
// The OOP Wrapper
|
||||
// ---------------
|
||||
|
||||
// If Underscore is called as a function, it returns a wrapped object that
|
||||
// can be used OO-style. This wrapper holds altered versions of all the
|
||||
// underscore functions. Wrapped objects may be chained.
|
||||
var wrapper = function(obj) { this._wrapped = obj; };
|
||||
|
||||
// Expose `wrapper.prototype` as `_.prototype`
|
||||
_.prototype = wrapper.prototype;
|
||||
|
||||
// Helper function to continue chaining intermediate results.
|
||||
var result = function(obj, chain) {
|
||||
return chain ? _(obj).chain() : obj;
|
||||
};
|
||||
|
||||
// A method to easily add functions to the OOP wrapper.
|
||||
var addToWrapper = function(name, func) {
|
||||
wrapper.prototype[name] = function() {
|
||||
var args = slice.call(arguments);
|
||||
unshift.call(args, this._wrapped);
|
||||
return result(func.apply(_, args), this._chain);
|
||||
};
|
||||
};
|
||||
|
||||
// Add all of the Underscore functions to the wrapper object.
|
||||
_.mixin(_);
|
||||
|
||||
// Add all mutator Array functions to the wrapper.
|
||||
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
var wrapped = this._wrapped;
|
||||
method.apply(wrapped, arguments);
|
||||
var length = wrapped.length;
|
||||
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
|
||||
return result(wrapped, this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Add all accessor Array functions to the wrapper.
|
||||
each(['concat', 'join', 'slice'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
return result(method.apply(this._wrapped, arguments), this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Start chaining a wrapped Underscore object.
|
||||
wrapper.prototype.chain = function() {
|
||||
this._chain = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Extracts the result from a wrapped and chained object.
|
||||
wrapper.prototype.value = function() {
|
||||
return this._wrapped;
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
@@ -0,0 +1,31 @@
|
||||
// Underscore.js 1.3.1
|
||||
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Underscore is freely distributable under the MIT license.
|
||||
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||
// For all details and documentation:
|
||||
// http://documentcloud.github.com/underscore
|
||||
(function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
|
||||
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,
|
||||
h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each=
|
||||
b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===n)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===n)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(x&&a.map===x)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==
|
||||
null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=
|
||||
function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e=
|
||||
e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
|
||||
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})});
|
||||
return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){f==0?b[0]=a:(d=Math.floor(Math.random()*(f+1)),b[f]=b[d],b[d]=a)});return b};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,
|
||||
c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=
|
||||
b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]);
|
||||
return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,
|
||||
d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(p&&a.indexOf===p)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(D&&a.lastIndexOf===D)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
|
||||
var F=function(){};b.bind=function(a,c){var d,e;if(a.bind===s&&s)return s.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));F.prototype=a.prototype;var b=new F,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
|
||||
c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
|
||||
a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};
|
||||
b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments,
|
||||
1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};
|
||||
b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};
|
||||
b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a),
|
||||
function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+
|
||||
u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]=
|
||||
function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=
|
||||
true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
|
||||
|
After Width: | Height: | Size: 345 B |
|
After Width: | Height: | Size: 345 B |
@@ -0,0 +1,808 @@
|
||||
/*
|
||||
* websupport.js
|
||||
* ~~~~~~~~~~~~~
|
||||
*
|
||||
* sphinx.websupport utilties for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
$.fn.autogrow = function() {
|
||||
return this.each(function() {
|
||||
var textarea = this;
|
||||
|
||||
$.fn.autogrow.resize(textarea);
|
||||
|
||||
$(textarea)
|
||||
.focus(function() {
|
||||
textarea.interval = setInterval(function() {
|
||||
$.fn.autogrow.resize(textarea);
|
||||
}, 500);
|
||||
})
|
||||
.blur(function() {
|
||||
clearInterval(textarea.interval);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.autogrow.resize = function(textarea) {
|
||||
var lineHeight = parseInt($(textarea).css('line-height'), 10);
|
||||
var lines = textarea.value.split('\n');
|
||||
var columns = textarea.cols;
|
||||
var lineCount = 0;
|
||||
$.each(lines, function() {
|
||||
lineCount += Math.ceil(this.length / columns) || 1;
|
||||
});
|
||||
var height = lineHeight * (lineCount + 1);
|
||||
$(textarea).css('height', height);
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
(function($) {
|
||||
var comp, by;
|
||||
|
||||
function init() {
|
||||
initEvents();
|
||||
initComparator();
|
||||
}
|
||||
|
||||
function initEvents() {
|
||||
$(document).on("click", 'a.comment-close', function(event) {
|
||||
event.preventDefault();
|
||||
hide($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.vote', function(event) {
|
||||
event.preventDefault();
|
||||
handleVote($(this));
|
||||
});
|
||||
$(document).on("click", 'a.reply', function(event) {
|
||||
event.preventDefault();
|
||||
openReply($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.close-reply', function(event) {
|
||||
event.preventDefault();
|
||||
closeReply($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.sort-option', function(event) {
|
||||
event.preventDefault();
|
||||
handleReSort($(this));
|
||||
});
|
||||
$(document).on("click", 'a.show-proposal', function(event) {
|
||||
event.preventDefault();
|
||||
showProposal($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.hide-proposal', function(event) {
|
||||
event.preventDefault();
|
||||
hideProposal($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.show-propose-change', function(event) {
|
||||
event.preventDefault();
|
||||
showProposeChange($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.hide-propose-change', function(event) {
|
||||
event.preventDefault();
|
||||
hideProposeChange($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.accept-comment', function(event) {
|
||||
event.preventDefault();
|
||||
acceptComment($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.delete-comment', function(event) {
|
||||
event.preventDefault();
|
||||
deleteComment($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.comment-markup', function(event) {
|
||||
event.preventDefault();
|
||||
toggleCommentMarkupBox($(this).attr('id').substring(2));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set comp, which is a comparator function used for sorting and
|
||||
* inserting comments into the list.
|
||||
*/
|
||||
function setComparator() {
|
||||
// If the first three letters are "asc", sort in ascending order
|
||||
// and remove the prefix.
|
||||
if (by.substring(0,3) == 'asc') {
|
||||
var i = by.substring(3);
|
||||
comp = function(a, b) { return a[i] - b[i]; };
|
||||
} else {
|
||||
// Otherwise sort in descending order.
|
||||
comp = function(a, b) { return b[by] - a[by]; };
|
||||
}
|
||||
|
||||
// Reset link styles and format the selected sort option.
|
||||
$('a.sel').attr('href', '#').removeClass('sel');
|
||||
$('a.by' + by).removeAttr('href').addClass('sel');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a comp function. If the user has preferences stored in
|
||||
* the sortBy cookie, use those, otherwise use the default.
|
||||
*/
|
||||
function initComparator() {
|
||||
by = 'rating'; // Default to sort by rating.
|
||||
// If the sortBy cookie is set, use that instead.
|
||||
if (document.cookie.length > 0) {
|
||||
var start = document.cookie.indexOf('sortBy=');
|
||||
if (start != -1) {
|
||||
start = start + 7;
|
||||
var end = document.cookie.indexOf(";", start);
|
||||
if (end == -1) {
|
||||
end = document.cookie.length;
|
||||
by = unescape(document.cookie.substring(start, end));
|
||||
}
|
||||
}
|
||||
}
|
||||
setComparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a comment div.
|
||||
*/
|
||||
function show(id) {
|
||||
$('#ao' + id).hide();
|
||||
$('#ah' + id).show();
|
||||
var context = $.extend({id: id}, opts);
|
||||
var popup = $(renderTemplate(popupTemplate, context)).hide();
|
||||
popup.find('textarea[name="proposal"]').hide();
|
||||
popup.find('a.by' + by).addClass('sel');
|
||||
var form = popup.find('#cf' + id);
|
||||
form.submit(function(event) {
|
||||
event.preventDefault();
|
||||
addComment(form);
|
||||
});
|
||||
$('#s' + id).after(popup);
|
||||
popup.slideDown('fast', function() {
|
||||
getComments(id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide a comment div.
|
||||
*/
|
||||
function hide(id) {
|
||||
$('#ah' + id).hide();
|
||||
$('#ao' + id).show();
|
||||
var div = $('#sc' + id);
|
||||
div.slideUp('fast', function() {
|
||||
div.remove();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an ajax request to get comments for a node
|
||||
* and insert the comments into the comments tree.
|
||||
*/
|
||||
function getComments(id) {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: opts.getCommentsURL,
|
||||
data: {node: id},
|
||||
success: function(data, textStatus, request) {
|
||||
var ul = $('#cl' + id);
|
||||
var speed = 100;
|
||||
$('#cf' + id)
|
||||
.find('textarea[name="proposal"]')
|
||||
.data('source', data.source);
|
||||
|
||||
if (data.comments.length === 0) {
|
||||
ul.html('<li>No comments yet.</li>');
|
||||
ul.data('empty', true);
|
||||
} else {
|
||||
// If there are comments, sort them and put them in the list.
|
||||
var comments = sortComments(data.comments);
|
||||
speed = data.comments.length * 100;
|
||||
appendComments(comments, ul);
|
||||
ul.data('empty', false);
|
||||
}
|
||||
$('#cn' + id).slideUp(speed + 200);
|
||||
ul.slideDown(speed);
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem retrieving the comments.');
|
||||
},
|
||||
dataType: 'json'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a comment via ajax and insert the comment into the comment tree.
|
||||
*/
|
||||
function addComment(form) {
|
||||
var node_id = form.find('input[name="node"]').val();
|
||||
var parent_id = form.find('input[name="parent"]').val();
|
||||
var text = form.find('textarea[name="comment"]').val();
|
||||
var proposal = form.find('textarea[name="proposal"]').val();
|
||||
|
||||
if (text == '') {
|
||||
showError('Please enter a comment.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the form that is being submitted.
|
||||
form.find('textarea,input').attr('disabled', 'disabled');
|
||||
|
||||
// Send the comment to the server.
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: opts.addCommentURL,
|
||||
dataType: 'json',
|
||||
data: {
|
||||
node: node_id,
|
||||
parent: parent_id,
|
||||
text: text,
|
||||
proposal: proposal
|
||||
},
|
||||
success: function(data, textStatus, error) {
|
||||
// Reset the form.
|
||||
if (node_id) {
|
||||
hideProposeChange(node_id);
|
||||
}
|
||||
form.find('textarea')
|
||||
.val('')
|
||||
.add(form.find('input'))
|
||||
.removeAttr('disabled');
|
||||
var ul = $('#cl' + (node_id || parent_id));
|
||||
if (ul.data('empty')) {
|
||||
$(ul).empty();
|
||||
ul.data('empty', false);
|
||||
}
|
||||
insertComment(data.comment);
|
||||
var ao = $('#ao' + node_id);
|
||||
ao.find('img').attr({'src': opts.commentBrightImage});
|
||||
if (node_id) {
|
||||
// if this was a "root" comment, remove the commenting box
|
||||
// (the user can get it back by reopening the comment popup)
|
||||
$('#ca' + node_id).slideUp();
|
||||
}
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
form.find('textarea,input').removeAttr('disabled');
|
||||
showError('Oops, there was a problem adding the comment.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively append comments to the main comment list and children
|
||||
* lists, creating the comment tree.
|
||||
*/
|
||||
function appendComments(comments, ul) {
|
||||
$.each(comments, function() {
|
||||
var div = createCommentDiv(this);
|
||||
ul.append($(document.createElement('li')).html(div));
|
||||
appendComments(this.children, div.find('ul.comment-children'));
|
||||
// To avoid stagnating data, don't store the comments children in data.
|
||||
this.children = null;
|
||||
div.data('comment', this);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* After adding a new comment, it must be inserted in the correct
|
||||
* location in the comment tree.
|
||||
*/
|
||||
function insertComment(comment) {
|
||||
var div = createCommentDiv(comment);
|
||||
|
||||
// To avoid stagnating data, don't store the comments children in data.
|
||||
comment.children = null;
|
||||
div.data('comment', comment);
|
||||
|
||||
var ul = $('#cl' + (comment.node || comment.parent));
|
||||
var siblings = getChildren(ul);
|
||||
|
||||
var li = $(document.createElement('li'));
|
||||
li.hide();
|
||||
|
||||
// Determine where in the parents children list to insert this comment.
|
||||
for(i=0; i < siblings.length; i++) {
|
||||
if (comp(comment, siblings[i]) <= 0) {
|
||||
$('#cd' + siblings[i].id)
|
||||
.parent()
|
||||
.before(li.html(div));
|
||||
li.slideDown('fast');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, this comment rates lower than all the others,
|
||||
// or it is the only comment in the list.
|
||||
ul.append(li.html(div));
|
||||
li.slideDown('fast');
|
||||
}
|
||||
|
||||
function acceptComment(id) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: opts.acceptCommentURL,
|
||||
data: {id: id},
|
||||
success: function(data, textStatus, request) {
|
||||
$('#cm' + id).fadeOut('fast');
|
||||
$('#cd' + id).removeClass('moderate');
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem accepting the comment.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteComment(id) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: opts.deleteCommentURL,
|
||||
data: {id: id},
|
||||
success: function(data, textStatus, request) {
|
||||
var div = $('#cd' + id);
|
||||
if (data == 'delete') {
|
||||
// Moderator mode: remove the comment and all children immediately
|
||||
div.slideUp('fast', function() {
|
||||
div.remove();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// User mode: only mark the comment as deleted
|
||||
div
|
||||
.find('span.user-id:first')
|
||||
.text('[deleted]').end()
|
||||
.find('div.comment-text:first')
|
||||
.text('[deleted]').end()
|
||||
.find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id +
|
||||
', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id)
|
||||
.remove();
|
||||
var comment = div.data('comment');
|
||||
comment.username = '[deleted]';
|
||||
comment.text = '[deleted]';
|
||||
div.data('comment', comment);
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem deleting the comment.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showProposal(id) {
|
||||
$('#sp' + id).hide();
|
||||
$('#hp' + id).show();
|
||||
$('#pr' + id).slideDown('fast');
|
||||
}
|
||||
|
||||
function hideProposal(id) {
|
||||
$('#hp' + id).hide();
|
||||
$('#sp' + id).show();
|
||||
$('#pr' + id).slideUp('fast');
|
||||
}
|
||||
|
||||
function showProposeChange(id) {
|
||||
$('#pc' + id).hide();
|
||||
$('#hc' + id).show();
|
||||
var textarea = $('#pt' + id);
|
||||
textarea.val(textarea.data('source'));
|
||||
$.fn.autogrow.resize(textarea[0]);
|
||||
textarea.slideDown('fast');
|
||||
}
|
||||
|
||||
function hideProposeChange(id) {
|
||||
$('#hc' + id).hide();
|
||||
$('#pc' + id).show();
|
||||
var textarea = $('#pt' + id);
|
||||
textarea.val('').removeAttr('disabled');
|
||||
textarea.slideUp('fast');
|
||||
}
|
||||
|
||||
function toggleCommentMarkupBox(id) {
|
||||
$('#mb' + id).toggle();
|
||||
}
|
||||
|
||||
/** Handle when the user clicks on a sort by link. */
|
||||
function handleReSort(link) {
|
||||
var classes = link.attr('class').split(/\s+/);
|
||||
for (var i=0; i<classes.length; i++) {
|
||||
if (classes[i] != 'sort-option') {
|
||||
by = classes[i].substring(2);
|
||||
}
|
||||
}
|
||||
setComparator();
|
||||
// Save/update the sortBy cookie.
|
||||
var expiration = new Date();
|
||||
expiration.setDate(expiration.getDate() + 365);
|
||||
document.cookie= 'sortBy=' + escape(by) +
|
||||
';expires=' + expiration.toUTCString();
|
||||
$('ul.comment-ul').each(function(index, ul) {
|
||||
var comments = getChildren($(ul), true);
|
||||
comments = sortComments(comments);
|
||||
appendComments(comments, $(ul).empty());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to process a vote when a user clicks an arrow.
|
||||
*/
|
||||
function handleVote(link) {
|
||||
if (!opts.voting) {
|
||||
showError("You'll need to login to vote.");
|
||||
return;
|
||||
}
|
||||
|
||||
var id = link.attr('id');
|
||||
if (!id) {
|
||||
// Didn't click on one of the voting arrows.
|
||||
return;
|
||||
}
|
||||
// If it is an unvote, the new vote value is 0,
|
||||
// Otherwise it's 1 for an upvote, or -1 for a downvote.
|
||||
var value = 0;
|
||||
if (id.charAt(1) != 'u') {
|
||||
value = id.charAt(0) == 'u' ? 1 : -1;
|
||||
}
|
||||
// The data to be sent to the server.
|
||||
var d = {
|
||||
comment_id: id.substring(2),
|
||||
value: value
|
||||
};
|
||||
|
||||
// Swap the vote and unvote links.
|
||||
link.hide();
|
||||
$('#' + id.charAt(0) + (id.charAt(1) == 'u' ? 'v' : 'u') + d.comment_id)
|
||||
.show();
|
||||
|
||||
// The div the comment is displayed in.
|
||||
var div = $('div#cd' + d.comment_id);
|
||||
var data = div.data('comment');
|
||||
|
||||
// If this is not an unvote, and the other vote arrow has
|
||||
// already been pressed, unpress it.
|
||||
if ((d.value !== 0) && (data.vote === d.value * -1)) {
|
||||
$('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide();
|
||||
$('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show();
|
||||
}
|
||||
|
||||
// Update the comments rating in the local data.
|
||||
data.rating += (data.vote === 0) ? d.value : (d.value - data.vote);
|
||||
data.vote = d.value;
|
||||
div.data('comment', data);
|
||||
|
||||
// Change the rating text.
|
||||
div.find('.rating:first')
|
||||
.text(data.rating + ' point' + (data.rating == 1 ? '' : 's'));
|
||||
|
||||
// Send the vote information to the server.
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: opts.processVoteURL,
|
||||
data: d,
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem casting that vote.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a reply form used to reply to an existing comment.
|
||||
*/
|
||||
function openReply(id) {
|
||||
// Swap out the reply link for the hide link
|
||||
$('#rl' + id).hide();
|
||||
$('#cr' + id).show();
|
||||
|
||||
// Add the reply li to the children ul.
|
||||
var div = $(renderTemplate(replyTemplate, {id: id})).hide();
|
||||
$('#cl' + id)
|
||||
.prepend(div)
|
||||
// Setup the submit handler for the reply form.
|
||||
.find('#rf' + id)
|
||||
.submit(function(event) {
|
||||
event.preventDefault();
|
||||
addComment($('#rf' + id));
|
||||
closeReply(id);
|
||||
})
|
||||
.find('input[type=button]')
|
||||
.click(function() {
|
||||
closeReply(id);
|
||||
});
|
||||
div.slideDown('fast', function() {
|
||||
$('#rf' + id).find('textarea').focus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the reply form opened with openReply.
|
||||
*/
|
||||
function closeReply(id) {
|
||||
// Remove the reply div from the DOM.
|
||||
$('#rd' + id).slideUp('fast', function() {
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
// Swap out the hide link for the reply link
|
||||
$('#cr' + id).hide();
|
||||
$('#rl' + id).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively sort a tree of comments using the comp comparator.
|
||||
*/
|
||||
function sortComments(comments) {
|
||||
comments.sort(comp);
|
||||
$.each(comments, function() {
|
||||
this.children = sortComments(this.children);
|
||||
});
|
||||
return comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the children comments from a ul. If recursive is true,
|
||||
* recursively include childrens' children.
|
||||
*/
|
||||
function getChildren(ul, recursive) {
|
||||
var children = [];
|
||||
ul.children().children("[id^='cd']")
|
||||
.each(function() {
|
||||
var comment = $(this).data('comment');
|
||||
if (recursive)
|
||||
comment.children = getChildren($(this).find('#cl' + comment.id), true);
|
||||
children.push(comment);
|
||||
});
|
||||
return children;
|
||||
}
|
||||
|
||||
/** Create a div to display a comment in. */
|
||||
function createCommentDiv(comment) {
|
||||
if (!comment.displayed && !opts.moderator) {
|
||||
return $('<div class="moderate">Thank you! Your comment will show up '
|
||||
+ 'once it is has been approved by a moderator.</div>');
|
||||
}
|
||||
// Prettify the comment rating.
|
||||
comment.pretty_rating = comment.rating + ' point' +
|
||||
(comment.rating == 1 ? '' : 's');
|
||||
// Make a class (for displaying not yet moderated comments differently)
|
||||
comment.css_class = comment.displayed ? '' : ' moderate';
|
||||
// Create a div for this comment.
|
||||
var context = $.extend({}, opts, comment);
|
||||
var div = $(renderTemplate(commentTemplate, context));
|
||||
|
||||
// If the user has voted on this comment, highlight the correct arrow.
|
||||
if (comment.vote) {
|
||||
var direction = (comment.vote == 1) ? 'u' : 'd';
|
||||
div.find('#' + direction + 'v' + comment.id).hide();
|
||||
div.find('#' + direction + 'u' + comment.id).show();
|
||||
}
|
||||
|
||||
if (opts.moderator || comment.text != '[deleted]') {
|
||||
div.find('a.reply').show();
|
||||
if (comment.proposal_diff)
|
||||
div.find('#sp' + comment.id).show();
|
||||
if (opts.moderator && !comment.displayed)
|
||||
div.find('#cm' + comment.id).show();
|
||||
if (opts.moderator || (opts.username == comment.username))
|
||||
div.find('#dc' + comment.id).show();
|
||||
}
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple template renderer. Placeholders such as <%id%> are replaced
|
||||
* by context['id'] with items being escaped. Placeholders such as <#id#>
|
||||
* are not escaped.
|
||||
*/
|
||||
function renderTemplate(template, context) {
|
||||
var esc = $(document.createElement('div'));
|
||||
|
||||
function handle(ph, escape) {
|
||||
var cur = context;
|
||||
$.each(ph.split('.'), function() {
|
||||
cur = cur[this];
|
||||
});
|
||||
return escape ? esc.text(cur || "").html() : cur;
|
||||
}
|
||||
|
||||
return template.replace(/<([%#])([\w\.]*)\1>/g, function() {
|
||||
return handle(arguments[2], arguments[1] == '%' ? true : false);
|
||||
});
|
||||
}
|
||||
|
||||
/** Flash an error message briefly. */
|
||||
function showError(message) {
|
||||
$(document.createElement('div')).attr({'class': 'popup-error'})
|
||||
.append($(document.createElement('div'))
|
||||
.attr({'class': 'error-message'}).text(message))
|
||||
.appendTo('body')
|
||||
.fadeIn("slow")
|
||||
.delay(2000)
|
||||
.fadeOut("slow");
|
||||
}
|
||||
|
||||
/** Add a link the user uses to open the comments popup. */
|
||||
$.fn.comment = function() {
|
||||
return this.each(function() {
|
||||
var id = $(this).attr('id').substring(1);
|
||||
var count = COMMENT_METADATA[id];
|
||||
var title = count + ' comment' + (count == 1 ? '' : 's');
|
||||
var image = count > 0 ? opts.commentBrightImage : opts.commentImage;
|
||||
var addcls = count == 0 ? ' nocomment' : '';
|
||||
$(this)
|
||||
.append(
|
||||
$(document.createElement('a')).attr({
|
||||
href: '#',
|
||||
'class': 'sphinx-comment-open' + addcls,
|
||||
id: 'ao' + id
|
||||
})
|
||||
.append($(document.createElement('img')).attr({
|
||||
src: image,
|
||||
alt: 'comment',
|
||||
title: title
|
||||
}))
|
||||
.click(function(event) {
|
||||
event.preventDefault();
|
||||
show($(this).attr('id').substring(2));
|
||||
})
|
||||
)
|
||||
.append(
|
||||
$(document.createElement('a')).attr({
|
||||
href: '#',
|
||||
'class': 'sphinx-comment-close hidden',
|
||||
id: 'ah' + id
|
||||
})
|
||||
.append($(document.createElement('img')).attr({
|
||||
src: opts.closeCommentImage,
|
||||
alt: 'close',
|
||||
title: 'close'
|
||||
}))
|
||||
.click(function(event) {
|
||||
event.preventDefault();
|
||||
hide($(this).attr('id').substring(2));
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
var opts = {
|
||||
processVoteURL: '/_process_vote',
|
||||
addCommentURL: '/_add_comment',
|
||||
getCommentsURL: '/_get_comments',
|
||||
acceptCommentURL: '/_accept_comment',
|
||||
deleteCommentURL: '/_delete_comment',
|
||||
commentImage: '/static/_static/comment.png',
|
||||
closeCommentImage: '/static/_static/comment-close.png',
|
||||
loadingImage: '/static/_static/ajax-loader.gif',
|
||||
commentBrightImage: '/static/_static/comment-bright.png',
|
||||
upArrow: '/static/_static/up.png',
|
||||
downArrow: '/static/_static/down.png',
|
||||
upArrowPressed: '/static/_static/up-pressed.png',
|
||||
downArrowPressed: '/static/_static/down-pressed.png',
|
||||
voting: false,
|
||||
moderator: false
|
||||
};
|
||||
|
||||
if (typeof COMMENT_OPTIONS != "undefined") {
|
||||
opts = jQuery.extend(opts, COMMENT_OPTIONS);
|
||||
}
|
||||
|
||||
var popupTemplate = '\
|
||||
<div class="sphinx-comments" id="sc<%id%>">\
|
||||
<p class="sort-options">\
|
||||
Sort by:\
|
||||
<a href="#" class="sort-option byrating">best rated</a>\
|
||||
<a href="#" class="sort-option byascage">newest</a>\
|
||||
<a href="#" class="sort-option byage">oldest</a>\
|
||||
</p>\
|
||||
<div class="comment-header">Comments</div>\
|
||||
<div class="comment-loading" id="cn<%id%>">\
|
||||
loading comments... <img src="<%loadingImage%>" alt="" /></div>\
|
||||
<ul id="cl<%id%>" class="comment-ul"></ul>\
|
||||
<div id="ca<%id%>">\
|
||||
<p class="add-a-comment">Add a comment\
|
||||
(<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\
|
||||
<div class="comment-markup-box" id="mb<%id%>">\
|
||||
reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \
|
||||
<code>``code``</code>, \
|
||||
code blocks: <code>::</code> and an indented block after blank line</div>\
|
||||
<form method="post" id="cf<%id%>" class="comment-form" action="">\
|
||||
<textarea name="comment" cols="80"></textarea>\
|
||||
<p class="propose-button">\
|
||||
<a href="#" id="pc<%id%>" class="show-propose-change">\
|
||||
Propose a change ▹\
|
||||
</a>\
|
||||
<a href="#" id="hc<%id%>" class="hide-propose-change">\
|
||||
Propose a change ▿\
|
||||
</a>\
|
||||
</p>\
|
||||
<textarea name="proposal" id="pt<%id%>" cols="80"\
|
||||
spellcheck="false"></textarea>\
|
||||
<input type="submit" value="Add comment" />\
|
||||
<input type="hidden" name="node" value="<%id%>" />\
|
||||
<input type="hidden" name="parent" value="" />\
|
||||
</form>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
var commentTemplate = '\
|
||||
<div id="cd<%id%>" class="sphinx-comment<%css_class%>">\
|
||||
<div class="vote">\
|
||||
<div class="arrow">\
|
||||
<a href="#" id="uv<%id%>" class="vote" title="vote up">\
|
||||
<img src="<%upArrow%>" />\
|
||||
</a>\
|
||||
<a href="#" id="uu<%id%>" class="un vote" title="vote up">\
|
||||
<img src="<%upArrowPressed%>" />\
|
||||
</a>\
|
||||
</div>\
|
||||
<div class="arrow">\
|
||||
<a href="#" id="dv<%id%>" class="vote" title="vote down">\
|
||||
<img src="<%downArrow%>" id="da<%id%>" />\
|
||||
</a>\
|
||||
<a href="#" id="du<%id%>" class="un vote" title="vote down">\
|
||||
<img src="<%downArrowPressed%>" />\
|
||||
</a>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="comment-content">\
|
||||
<p class="tagline comment">\
|
||||
<span class="user-id"><%username%></span>\
|
||||
<span class="rating"><%pretty_rating%></span>\
|
||||
<span class="delta"><%time.delta%></span>\
|
||||
</p>\
|
||||
<div class="comment-text comment"><#text#></div>\
|
||||
<p class="comment-opts comment">\
|
||||
<a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a>\
|
||||
<a href="#" class="close-reply" id="cr<%id%>">reply ▿</a>\
|
||||
<a href="#" id="sp<%id%>" class="show-proposal">proposal ▹</a>\
|
||||
<a href="#" id="hp<%id%>" class="hide-proposal">proposal ▿</a>\
|
||||
<a href="#" id="dc<%id%>" class="delete-comment hidden">delete</a>\
|
||||
<span id="cm<%id%>" class="moderation hidden">\
|
||||
<a href="#" id="ac<%id%>" class="accept-comment">accept</a>\
|
||||
</span>\
|
||||
</p>\
|
||||
<pre class="proposal" id="pr<%id%>">\
|
||||
<#proposal_diff#>\
|
||||
</pre>\
|
||||
<ul class="comment-children" id="cl<%id%>"></ul>\
|
||||
</div>\
|
||||
<div class="clearleft"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
var replyTemplate = '\
|
||||
<li>\
|
||||
<div class="reply-div" id="rd<%id%>">\
|
||||
<form id="rf<%id%>">\
|
||||
<textarea name="comment" cols="80"></textarea>\
|
||||
<input type="submit" value="Add reply" />\
|
||||
<input type="button" value="Cancel" />\
|
||||
<input type="hidden" name="parent" value="<%id%>" />\
|
||||
<input type="hidden" name="node" value="" />\
|
||||
</form>\
|
||||
</div>\
|
||||
</li>';
|
||||
|
||||
$(document).ready(function() {
|
||||
init();
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
$(document).ready(function() {
|
||||
// add comment anchors for all paragraphs that are commentable
|
||||
$('.sphinx-has-comment').comment();
|
||||
|
||||
// highlight search words in search results
|
||||
$("div.context").each(function() {
|
||||
var params = $.getQueryParameters();
|
||||
var terms = (params.q) ? params.q[0].split(/\s+/) : [];
|
||||
var result = $(this);
|
||||
$.each(terms, function() {
|
||||
result.highlightText(this.toLowerCase(), 'highlighted');
|
||||
});
|
||||
});
|
||||
|
||||
// directly open comment window if requested
|
||||
var anchor = document.location.hash;
|
||||
if (anchor.substring(0, 9) == '#comment-') {
|
||||
$('#ao' + anchor.substring(9)).click();
|
||||
document.location.hash = '#s' + anchor.substring(9);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,514 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>API Reference — Catalyst 0.3 documentation</title>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="top" title="Catalyst 0.3 documentation" href="index.html"/>
|
||||
<link rel="next" title="Release Process" href="release-process.html"/>
|
||||
<link rel="prev" title="Development Guidelines" href="development-guidelines.html"/>
|
||||
|
||||
|
||||
<script src="_static/js/modernizr.min.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav" role="document">
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-nav-search">
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home"> Catalyst
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
<ul class="current">
|
||||
<li class="toctree-l1"><a class="reference internal" href="install.html">Install</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="install.html#installing-with-pip">Installing with <code class="docutils literal"><span class="pre">pip</span></code></a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="install.html#gnu-linux">GNU/Linux</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="install.html#osx">OSX</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="install.html#windows">Windows</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="install.html#installing-with-conda">Installing with <code class="docutils literal"><span class="pre">conda</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="beginner-tutorial.html">Zipline Beginner Tutorial</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#basics">Basics</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#my-first-algorithm">My first algorithm</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#running-the-algorithm">Running the algorithm</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="beginner-tutorial.html#ingesting-data">Ingesting Data</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="beginner-tutorial.html#command-line-interface">Command line interface</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#ipython-notebook">IPython Notebook</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#access-to-previous-prices-using-history">Access to previous prices using <code class="docutils literal"><span class="pre">history</span></code></a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="beginner-tutorial.html#working-example-dual-moving-average-cross-over">Working example: Dual Moving Average Cross-Over</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#conclusions">Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="bundles.html">Data Bundles</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="bundles.html#discovering-available-bundles">Discovering Available Bundles</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="bundles.html#ingesting-data">Ingesting Data</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="bundles.html#old-data">Old Data</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="bundles.html#running-backtests-with-data-bundles">Running Backtests with Data Bundles</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="bundles.html#default-data-bundles">Default Data Bundles</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#quandl-wiki-bundle">Quandl WIKI Bundle</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="bundles.html#quantopian-quandl-wiki-mirror">Quantopian Quandl WIKI Mirror</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#yahoo-bundle-factories">Yahoo Bundle Factories</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="bundles.html#writing-a-new-bundle">Writing a New Bundle</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#environ"><code class="docutils literal"><span class="pre">environ</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#asset-db-writer"><code class="docutils literal"><span class="pre">asset_db_writer</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#minute-bar-writer"><code class="docutils literal"><span class="pre">minute_bar_writer</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#daily-bar-writer"><code class="docutils literal"><span class="pre">daily_bar_writer</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#adjustment-writer"><code class="docutils literal"><span class="pre">adjustment_writer</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#calendar"><code class="docutils literal"><span class="pre">calendar</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#start-session"><code class="docutils literal"><span class="pre">start_session</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#end-session"><code class="docutils literal"><span class="pre">end_session</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#cache"><code class="docutils literal"><span class="pre">cache</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#show-progress"><code class="docutils literal"><span class="pre">show_progress</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="bundles.html#output-dir"><code class="docutils literal"><span class="pre">output_dir</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="development-guidelines.html">Development Guidelines</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#creating-a-development-environment">Creating a Development Environment</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#development-with-docker">Development with Docker</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#style-guide-running-tests">Style Guide & Running Tests</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#continuous-integration">Continuous Integration</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#packaging">Packaging</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#contributing-to-the-docs">Contributing to the Docs</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#commit-messages">Commit messages</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#formatting-docstrings">Formatting Docstrings</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="">API Reference</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#running-a-backtest">Running a Backtest</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#algorithm-api">Algorithm API</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#data-object">Data Object</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#scheduling-functions">Scheduling Functions</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#orders">Orders</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="#order-cancellation-policies">Order Cancellation Policies</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#assets">Assets</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#trading-controls">Trading Controls</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#simulation-parameters">Simulation Parameters</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="#commission-models">Commission Models</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="#slippage-models">Slippage Models</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#pipeline">Pipeline</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#miscellaneous">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#pipeline-api">Pipeline API</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#built-in-factors">Built-in Factors</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#pipeline-engine">Pipeline Engine</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#data-loaders">Data Loaders</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#asset-metadata">Asset Metadata</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#trading-calendar-api">Trading Calendar API</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#data-api">Data API</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#writers">Writers</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#readers">Readers</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#bundles">Bundles</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#utilities">Utilities</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#caching">Caching</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#command-line">Command Line</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="release-process.html">Release Process</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#updating-the-release-notes">Updating the Release Notes</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#updating-the-python-stub-files">Updating the Python stub files</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#updating-the-version">Updating the <code class="docutils literal"><span class="pre">__version__</span></code></a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#uploading-pypi-packages">Uploading PyPI packages</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="release-process.html#sdist"><code class="docutils literal"><span class="pre">sdist</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="release-process.html#bdist"><code class="docutils literal"><span class="pre">bdist</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#documentation">Documentation</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#uploading-conda-packages">Uploading conda packages</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#next-commit">Next Commit</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="releases.html">Release Notes</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#development">Development</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#highlights">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#enhancements">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#experimental-features">Experimental Features</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#bug-fixes">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#performance">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#maintenance-and-refactorings">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#build">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#documentation">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#miscellaneous">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-1-1-0">Release 1.1.0</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id1">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id2">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id3">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id4">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id5">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id6">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id7">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-1-0-2">Release 1.0.2</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id8">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#experimental">Experimental</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id9">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id10">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id11">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id12">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#testing">Testing</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#data-format-changes">Data Format Changes</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-1-0-1">Release 1.0.1</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id13">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id14">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id15">Documentation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-1-0-0">Release 1.0.0</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id16">Highlights</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="releases.html#zipline-1-0-rewrite-1105">Zipline 1.0 Rewrite (#1105)</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="releases.html#new-entry-points-1173-and-1178">New Entry Points (#1173 and #1178)</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="releases.html#data-bundles-1173-and-1178">Data Bundles (#1173 and #1178)</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="releases.html#string-support-in-pipeline-1174">String Support in Pipeline (#1174)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id17">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id18">Experimental Features</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id19">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id20">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id21">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id22">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id23">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id24">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-9-0">Release 0.9.0</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id25">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id26">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id27">Experimental Features</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id28">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id29">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id30">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id31">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id32">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id33">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-8-4">Release 0.8.4</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id34">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id35">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id36">Experimental Features</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id37">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id38">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id39">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id40">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id41">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id42">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-8-3">Release 0.8.3</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-8-0">Release 0.8.0</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id43">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id44">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id45">Experimental Features</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id46">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id47">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id48">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id49">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id50">Documentation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-7-0">Release 0.7.0</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id51">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id52">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id53">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id54">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id55">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id56">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#contributors">Contributors</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-6-1">Release 0.6.1</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id57">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id58">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id59">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id60">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id61">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id62">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id63">Contributors</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
<nav class="wy-nav-top" role="navigation" aria-label="top navigation">
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">Catalyst</a>
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
<div class="rst-content">
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
<ul class="wy-breadcrumbs">
|
||||
<li><a href="index.html">Docs</a> »</li>
|
||||
|
||||
<li>API Reference</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
<a href="_sources/appendix.txt" rel="nofollow"> View page source</a>
|
||||
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document">
|
||||
|
||||
<div class="section" id="api-reference">
|
||||
<h1>API Reference<a class="headerlink" href="#api-reference" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="running-a-backtest">
|
||||
<h2>Running a Backtest<a class="headerlink" href="#running-a-backtest" title="Permalink to this headline">¶</a></h2>
|
||||
</div>
|
||||
<div class="section" id="algorithm-api">
|
||||
<h2>Algorithm API<a class="headerlink" href="#algorithm-api" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The following methods are available for use in the <code class="docutils literal"><span class="pre">initialize</span></code>,
|
||||
<code class="docutils literal"><span class="pre">handle_data</span></code>, and <code class="docutils literal"><span class="pre">before_trading_start</span></code> API functions.</p>
|
||||
<p>In all listed functions, the <code class="docutils literal"><span class="pre">self</span></code> argument is implicitly the
|
||||
currently-executing <code class="xref py py-class docutils literal"><span class="pre">TradingAlgorithm</span></code> instance.</p>
|
||||
<div class="section" id="data-object">
|
||||
<h3>Data Object<a class="headerlink" href="#data-object" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
<div class="section" id="scheduling-functions">
|
||||
<h3>Scheduling Functions<a class="headerlink" href="#scheduling-functions" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
<div class="section" id="orders">
|
||||
<h3>Orders<a class="headerlink" href="#orders" title="Permalink to this headline">¶</a></h3>
|
||||
<div class="section" id="order-cancellation-policies">
|
||||
<h4>Order Cancellation Policies<a class="headerlink" href="#order-cancellation-policies" title="Permalink to this headline">¶</a></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="assets">
|
||||
<h3>Assets<a class="headerlink" href="#assets" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
<div class="section" id="trading-controls">
|
||||
<h3>Trading Controls<a class="headerlink" href="#trading-controls" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Zipline provides trading controls to help ensure that the algorithm is
|
||||
performing as expected. The functions help protect the algorithm from certian
|
||||
bugs that could cause undesirable behavior when trading with real money.</p>
|
||||
</div>
|
||||
<div class="section" id="simulation-parameters">
|
||||
<h3>Simulation Parameters<a class="headerlink" href="#simulation-parameters" title="Permalink to this headline">¶</a></h3>
|
||||
<div class="section" id="commission-models">
|
||||
<h4>Commission Models<a class="headerlink" href="#commission-models" title="Permalink to this headline">¶</a></h4>
|
||||
</div>
|
||||
<div class="section" id="slippage-models">
|
||||
<h4>Slippage Models<a class="headerlink" href="#slippage-models" title="Permalink to this headline">¶</a></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="pipeline">
|
||||
<h3>Pipeline<a class="headerlink" href="#pipeline" title="Permalink to this headline">¶</a></h3>
|
||||
<p>For more information, see <a class="reference internal" href="#pipeline-api"><span>Pipeline API</span></a></p>
|
||||
</div>
|
||||
<div class="section" id="miscellaneous">
|
||||
<h3>Miscellaneous<a class="headerlink" href="#miscellaneous" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="pipeline-api">
|
||||
<span id="id1"></span><h2>Pipeline API<a class="headerlink" href="#pipeline-api" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="section" id="built-in-factors">
|
||||
<h3>Built-in Factors<a class="headerlink" href="#built-in-factors" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
<div class="section" id="pipeline-engine">
|
||||
<h3>Pipeline Engine<a class="headerlink" href="#pipeline-engine" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
<div class="section" id="data-loaders">
|
||||
<h3>Data Loaders<a class="headerlink" href="#data-loaders" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="asset-metadata">
|
||||
<h2>Asset Metadata<a class="headerlink" href="#asset-metadata" title="Permalink to this headline">¶</a></h2>
|
||||
</div>
|
||||
<div class="section" id="trading-calendar-api">
|
||||
<h2>Trading Calendar API<a class="headerlink" href="#trading-calendar-api" title="Permalink to this headline">¶</a></h2>
|
||||
</div>
|
||||
<div class="section" id="data-api">
|
||||
<h2>Data API<a class="headerlink" href="#data-api" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="section" id="writers">
|
||||
<h3>Writers<a class="headerlink" href="#writers" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
<div class="section" id="readers">
|
||||
<h3>Readers<a class="headerlink" href="#readers" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
<div class="section" id="bundles">
|
||||
<h3>Bundles<a class="headerlink" href="#bundles" title="Permalink to this headline">¶</a></h3>
|
||||
<dl class="data">
|
||||
<dt id="zipline.data.bundles.bundles">
|
||||
<code class="descclassname">zipline.data.bundles.</code><code class="descname">bundles</code><a class="headerlink" href="#zipline.data.bundles.bundles" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The bundles that have been registered as a mapping from bundle name to bundle
|
||||
data. This mapping is immutable and should only be updated through
|
||||
<code class="xref py py-func docutils literal"><span class="pre">register()</span></code> or
|
||||
<code class="xref py py-func docutils literal"><span class="pre">unregister()</span></code>.</p>
|
||||
</dd></dl>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="utilities">
|
||||
<h2>Utilities<a class="headerlink" href="#utilities" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="section" id="caching">
|
||||
<h3>Caching<a class="headerlink" href="#caching" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
<div class="section" id="command-line">
|
||||
<h3>Command Line<a class="headerlink" href="#command-line" title="Permalink to this headline">¶</a></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
|
||||
<a href="release-process.html" class="btn btn-neutral float-right" title="Release Process" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||
|
||||
|
||||
<a href="development-guidelines.html" class="btn btn-neutral" title="Development Guidelines" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
<div role="contentinfo">
|
||||
<p>
|
||||
© Copyright 2017, Enigma MPC, Inc..
|
||||
</p>
|
||||
</div>
|
||||
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT:'./',
|
||||
VERSION:'0.3',
|
||||
COLLAPSE_INDEX:false,
|
||||
FILE_SUFFIX:'.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
SphinxRtdTheme.StickyNav.enable();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,109 +0,0 @@
|
||||
#matrix:
|
||||
# fast_finish: true
|
||||
|
||||
environment:
|
||||
global:
|
||||
# SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
|
||||
# /E:ON and /V:ON options are not enabled in the batch script intepreter
|
||||
# See: http://stackoverflow.com/a/13751649/163740
|
||||
CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\ci\\appveyor\\run_with_env.cmd"
|
||||
|
||||
# 1. Generated a token for appveyor at https://anaconda.org/quantopian/settings/access with scope api:write.
|
||||
# Can also be done via anaconda CLI with
|
||||
# $ anaconda auth --create --name my_appveyor_token
|
||||
# 2. Generated secure env var below via appveyor's Encrypt data tool at https://ci.appveyor.com/tools/encrypt.
|
||||
# See https://www.appveyor.com/docs/build-configuration/#secure-variables.
|
||||
ANACONDA_TOKEN:
|
||||
secure: "mfGCRgiY6D5BmJkH4HFPDJUH+6W9zH0cxKZ5o/cv9grfRkfu5YRG26No88H1q7Ua"
|
||||
|
||||
CONDA_ROOT_PYTHON_VERSION: "2.7"
|
||||
|
||||
matrix:
|
||||
- PYTHON_VERSION: "2.7"
|
||||
PYTHON_ARCH: "64"
|
||||
PANDAS_VERSION: "0.18.1"
|
||||
NUMPY_VERSION: "1.11.1"
|
||||
SCIPY_VERSION: "0.17.1"
|
||||
|
||||
- PYTHON_VERSION: "3.4"
|
||||
PYTHON_ARCH: "64"
|
||||
PANDAS_VERSION: "0.18.1"
|
||||
NUMPY_VERSION: "1.11.1"
|
||||
SCIPY_VERSION: "0.17.1"
|
||||
|
||||
- PYTHON_VERSION: "3.5"
|
||||
PYTHON_ARCH: "64"
|
||||
PANDAS_VERSION: "0.18.1"
|
||||
NUMPY_VERSION: "1.11.1"
|
||||
SCIPY_VERSION: "0.17.1"
|
||||
|
||||
# We always use a 64-bit machine, but can build x86 distributions
|
||||
# with the PYTHON_ARCH variable (which is used by CMD_IN_ENV).
|
||||
platform:
|
||||
- x64
|
||||
|
||||
cache:
|
||||
- '%LOCALAPPDATA%\pip\Cache'
|
||||
|
||||
# all our python builds have to happen in tests_script...
|
||||
build: false
|
||||
|
||||
init:
|
||||
- "ECHO %PYTHON_VERSION% %PYTHON_ARCH% %PYTHON%"
|
||||
- "ECHO %NUMPY_VERSION%"
|
||||
|
||||
install:
|
||||
# If there is a newer build queued for the same PR, cancel this one.
|
||||
# The AppVeyor 'rollout builds' option is supposed to serve the same
|
||||
# purpose but it is problematic because it tends to cancel builds pushed
|
||||
# directly to master instead of just PR builds (or the converse).
|
||||
# credits: JuliaLang developers.
|
||||
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
|
||||
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
|
||||
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
|
||||
throw "There are newer queued builds for this pull request, failing early." }
|
||||
|
||||
- ps: $NPY_VERSION_ARR=$env:NUMPY_VERSION -split '.', 0, 'simplematch'
|
||||
- ps: $env:CONDA_NPY=$NPY_VERSION_ARR[0..1] -join ""
|
||||
- ps: $PY_VERSION_ARR=$env:PYTHON_VERSION -split '.', 0, 'simplematch'
|
||||
- ps: $env:CONDA_PY=$PY_VERSION_ARR[0..1] -join ""
|
||||
- SET PYTHON=C:\Python%CONDA_PY%_64
|
||||
# Get cygwin's git out of our PATH. See https://github.com/omnia-md/conda-dev-recipes/pull/16/files#diff-180360612c6b8c4ed830919bbb4dd459
|
||||
- "del C:\\cygwin\\bin\\git.exe"
|
||||
# this installs the appropriate Miniconda (Py2/Py3, 32/64 bit),
|
||||
- powershell .\ci\appveyor\install.ps1
|
||||
- SET PATH=%PYTHON%;%PYTHON%\Scripts;%PATH%
|
||||
- sed -i "s/numpy==.*/numpy==%NUMPY_VERSION%/" etc/requirements.txt
|
||||
- sed -i "s/pandas==.*/pandas==%PANDAS_VERSION%/" etc/requirements.txt
|
||||
- sed -i "s/scipy==.*/scipy==%SCIPY_VERSION%/" etc/requirements.txt
|
||||
|
||||
- conda info -a
|
||||
- conda install conda=4.1.11 conda-build=1.21.11 anaconda-client=1.5.1 --yes -q
|
||||
# https://blog.ionelmc.ro/2014/12/21/compiling-python-extensions-on-windows/ for 64bit C compilation
|
||||
- ps: copy .\ci\appveyor\vcvars64.bat "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64"
|
||||
- "%CMD_IN_ENV% python .\\ci\\make_conda_packages.py"
|
||||
|
||||
# test that we can conda install catalyst in a new env
|
||||
- conda create -n installenv --yes -q --use-local python=%PYTHON_VERSION% numpy=%NUMPY_VERSION% catalyst -c quantopian -c https://conda.anaconda.org/quantopian/label/ci
|
||||
|
||||
- ps: $env:BCOLZ_VERSION=(sls "bcolz==(.*)" .\etc\requirements.txt -ca).matches.groups[1].value
|
||||
- ps: $env:NUMEXPR_VERSION=(sls "numexpr==(.*)" .\etc\requirements.txt -ca).matches.groups[1].value
|
||||
- ps: $env:TALIB_VERSION=(sls "TA-Lib==(.*)" .\etc\requirements_talib.txt -ca).matches.groups[1].value
|
||||
- conda create -n testenv --yes -q --use-local pip python=%PYTHON_VERSION% numpy=%NUMPY_VERSION% scipy=%SCIPY_VERSION% ta-lib=%TALIB_VERSION% bcolz=%BCOLZ_VERSION% numexpr=%NUMEXPR_VERSION% -c quantopian -c https://conda.anaconda.org/quantopian/label/ci
|
||||
- activate testenv
|
||||
- SET CACHE_DIR=%LOCALAPPDATA%\pip\Cache\pip_np%CONDA_NPY%py%CONDA_PY%
|
||||
- pip install -r etc/requirements.txt --cache-dir=%CACHE_DIR%
|
||||
- pip install -r etc/requirements_dev.txt --cache-dir=%CACHE_DIR%
|
||||
# this uses git requirements right now
|
||||
- pip install -r etc/requirements_blaze.txt --cache-dir=%CACHE_DIR%
|
||||
- pip install -r etc/requirements_talib.txt --cache-dir=%CACHE_DIR%
|
||||
- pip install -e .[all] --cache-dir=%CACHE_DIR%
|
||||
- pip freeze | sort
|
||||
|
||||
test_script:
|
||||
- nosetests -e catalyst.utils.numpy_utils
|
||||
- flake8 catalyst tests
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
@@ -0,0 +1,715 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Data Bundles — Catalyst 0.3 documentation</title>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="top" title="Catalyst 0.3 documentation" href="index.html"/>
|
||||
<link rel="next" title="Development Guidelines" href="development-guidelines.html"/>
|
||||
<link rel="prev" title="Zipline Beginner Tutorial" href="beginner-tutorial.html"/>
|
||||
|
||||
|
||||
<script src="_static/js/modernizr.min.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav" role="document">
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-nav-search">
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home"> Catalyst
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
<ul class="current">
|
||||
<li class="toctree-l1"><a class="reference internal" href="install.html">Install</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="install.html#installing-with-pip">Installing with <code class="docutils literal"><span class="pre">pip</span></code></a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="install.html#gnu-linux">GNU/Linux</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="install.html#osx">OSX</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="install.html#windows">Windows</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="install.html#installing-with-conda">Installing with <code class="docutils literal"><span class="pre">conda</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="beginner-tutorial.html">Zipline Beginner Tutorial</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#basics">Basics</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#my-first-algorithm">My first algorithm</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#running-the-algorithm">Running the algorithm</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="beginner-tutorial.html#ingesting-data">Ingesting Data</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="beginner-tutorial.html#command-line-interface">Command line interface</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#ipython-notebook">IPython Notebook</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#access-to-previous-prices-using-history">Access to previous prices using <code class="docutils literal"><span class="pre">history</span></code></a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="beginner-tutorial.html#working-example-dual-moving-average-cross-over">Working example: Dual Moving Average Cross-Over</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="beginner-tutorial.html#conclusions">Conclusions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="">Data Bundles</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#discovering-available-bundles">Discovering Available Bundles</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#ingesting-data">Ingesting Data</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#old-data">Old Data</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#running-backtests-with-data-bundles">Running Backtests with Data Bundles</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#default-data-bundles">Default Data Bundles</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#quandl-wiki-bundle">Quandl WIKI Bundle</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="#quantopian-quandl-wiki-mirror">Quantopian Quandl WIKI Mirror</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#yahoo-bundle-factories">Yahoo Bundle Factories</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#writing-a-new-bundle">Writing a New Bundle</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#environ"><code class="docutils literal"><span class="pre">environ</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#asset-db-writer"><code class="docutils literal"><span class="pre">asset_db_writer</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#minute-bar-writer"><code class="docutils literal"><span class="pre">minute_bar_writer</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#daily-bar-writer"><code class="docutils literal"><span class="pre">daily_bar_writer</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#adjustment-writer"><code class="docutils literal"><span class="pre">adjustment_writer</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#calendar"><code class="docutils literal"><span class="pre">calendar</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#start-session"><code class="docutils literal"><span class="pre">start_session</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#end-session"><code class="docutils literal"><span class="pre">end_session</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#cache"><code class="docutils literal"><span class="pre">cache</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#show-progress"><code class="docutils literal"><span class="pre">show_progress</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#output-dir"><code class="docutils literal"><span class="pre">output_dir</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="development-guidelines.html">Development Guidelines</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#creating-a-development-environment">Creating a Development Environment</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#development-with-docker">Development with Docker</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#style-guide-running-tests">Style Guide & Running Tests</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#continuous-integration">Continuous Integration</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#packaging">Packaging</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#contributing-to-the-docs">Contributing to the Docs</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#commit-messages">Commit messages</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="development-guidelines.html#formatting-docstrings">Formatting Docstrings</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="appendix.html">API Reference</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="appendix.html#running-a-backtest">Running a Backtest</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="appendix.html#algorithm-api">Algorithm API</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#data-object">Data Object</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#scheduling-functions">Scheduling Functions</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#orders">Orders</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="appendix.html#order-cancellation-policies">Order Cancellation Policies</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#assets">Assets</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#trading-controls">Trading Controls</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#simulation-parameters">Simulation Parameters</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="appendix.html#commission-models">Commission Models</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="appendix.html#slippage-models">Slippage Models</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#pipeline">Pipeline</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#miscellaneous">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="appendix.html#pipeline-api">Pipeline API</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#built-in-factors">Built-in Factors</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#pipeline-engine">Pipeline Engine</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#data-loaders">Data Loaders</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="appendix.html#asset-metadata">Asset Metadata</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="appendix.html#trading-calendar-api">Trading Calendar API</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="appendix.html#data-api">Data API</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#writers">Writers</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#readers">Readers</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#bundles">Bundles</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="appendix.html#utilities">Utilities</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#caching">Caching</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="appendix.html#command-line">Command Line</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="release-process.html">Release Process</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#updating-the-release-notes">Updating the Release Notes</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#updating-the-python-stub-files">Updating the Python stub files</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#updating-the-version">Updating the <code class="docutils literal"><span class="pre">__version__</span></code></a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#uploading-pypi-packages">Uploading PyPI packages</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="release-process.html#sdist"><code class="docutils literal"><span class="pre">sdist</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="release-process.html#bdist"><code class="docutils literal"><span class="pre">bdist</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#documentation">Documentation</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#uploading-conda-packages">Uploading conda packages</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="release-process.html#next-commit">Next Commit</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="releases.html">Release Notes</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#development">Development</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#highlights">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#enhancements">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#experimental-features">Experimental Features</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#bug-fixes">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#performance">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#maintenance-and-refactorings">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#build">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#documentation">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#miscellaneous">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-1-1-0">Release 1.1.0</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id1">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id2">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id3">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id4">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id5">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id6">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id7">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-1-0-2">Release 1.0.2</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id8">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#experimental">Experimental</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id9">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id10">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id11">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id12">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#testing">Testing</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#data-format-changes">Data Format Changes</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-1-0-1">Release 1.0.1</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id13">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id14">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id15">Documentation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-1-0-0">Release 1.0.0</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id16">Highlights</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="releases.html#zipline-1-0-rewrite-1105">Zipline 1.0 Rewrite (#1105)</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="releases.html#new-entry-points-1173-and-1178">New Entry Points (#1173 and #1178)</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="releases.html#data-bundles-1173-and-1178">Data Bundles (#1173 and #1178)</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="releases.html#string-support-in-pipeline-1174">String Support in Pipeline (#1174)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id17">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id18">Experimental Features</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id19">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id20">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id21">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id22">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id23">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id24">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-9-0">Release 0.9.0</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id25">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id26">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id27">Experimental Features</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id28">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id29">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id30">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id31">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id32">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id33">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-8-4">Release 0.8.4</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id34">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id35">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id36">Experimental Features</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id37">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id38">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id39">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id40">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id41">Documentation</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id42">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-8-3">Release 0.8.3</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-8-0">Release 0.8.0</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id43">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id44">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id45">Experimental Features</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id46">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id47">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id48">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id49">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id50">Documentation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-7-0">Release 0.7.0</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id51">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id52">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id53">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id54">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id55">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id56">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#contributors">Contributors</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="releases.html#release-0-6-1">Release 0.6.1</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id57">Highlights</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id58">Enhancements</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id59">Bug Fixes</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id60">Performance</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id61">Maintenance and Refactorings</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id62">Build</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="releases.html#id63">Contributors</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
<nav class="wy-nav-top" role="navigation" aria-label="top navigation">
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">Catalyst</a>
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
<div class="rst-content">
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
<ul class="wy-breadcrumbs">
|
||||
<li><a href="index.html">Docs</a> »</li>
|
||||
|
||||
<li>Data Bundles</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
<a href="_sources/bundles.txt" rel="nofollow"> View page source</a>
|
||||
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document">
|
||||
|
||||
<div class="section" id="data-bundles">
|
||||
<span id="id1"></span><h1>Data Bundles<a class="headerlink" href="#data-bundles" title="Permalink to this headline">¶</a></h1>
|
||||
<p>A data bundle is a collection of pricing data, adjustment data, and an asset
|
||||
database. Bundles allow us to preload all of the data we will need to run
|
||||
backtests and store the data for future runs.</p>
|
||||
<div class="section" id="discovering-available-bundles">
|
||||
<span id="bundles-command"></span><h2>Discovering Available Bundles<a class="headerlink" href="#discovering-available-bundles" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Zipline comes with a few bundles by default as well as the ability to register
|
||||
new bundles. To see which bundles we have have available, we may run the
|
||||
<code class="docutils literal"><span class="pre">bundles</span></code> command, for example:</p>
|
||||
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>zipline bundles
|
||||
my-custom-bundle 2016-05-05 20:35:19.809398
|
||||
my-custom-bundle 2016-05-05 20:34:53.654082
|
||||
my-custom-bundle 2016-05-05 20:34:48.401767
|
||||
quandl <no ingestions>
|
||||
quantopian-quandl 2016-05-05 20:06:40.894956
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The output here shows that there are 3 bundles available:</p>
|
||||
<ul class="simple">
|
||||
<li><code class="docutils literal"><span class="pre">my-custom-bundle</span></code> (added by the user)</li>
|
||||
<li><code class="docutils literal"><span class="pre">quandl</span></code> (provided by zipline)</li>
|
||||
<li><code class="docutils literal"><span class="pre">quantopian-quandl</span></code> (provided by zipline)</li>
|
||||
</ul>
|
||||
<p>The dates and times next to the name show the times when the data for this
|
||||
bundle was ingested. We have run three different ingestions for
|
||||
<code class="docutils literal"><span class="pre">my-custom-bundle</span></code>. We have never ingested any data for the <code class="docutils literal"><span class="pre">quandl</span></code> bundle
|
||||
so it just shows <code class="docutils literal"><span class="pre"><no</span> <span class="pre">ingestions></span></code> instead. Finally, there is only one
|
||||
ingestion for <code class="docutils literal"><span class="pre">quantopian-quandl</span></code>.</p>
|
||||
</div>
|
||||
<div class="section" id="ingesting-data">
|
||||
<span id="id2"></span><h2>Ingesting Data<a class="headerlink" href="#ingesting-data" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The first step to using a data bundle is to ingest the data. The ingestion
|
||||
process will invoke some custom bundle command and then write the data to a
|
||||
standard location that zipline can find. By default the location where ingested
|
||||
data will be written is <code class="docutils literal"><span class="pre">$ZIPLINE_ROOT/data/<bundle></span></code> where by default
|
||||
<code class="docutils literal"><span class="pre">ZIPLINE_ROOT=~/.zipline</span></code>. The ingestion step may take some time as it could
|
||||
involve downloading and processing a lot of data. This can be run with:</p>
|
||||
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>zipline ingest <span class="o">[</span>-b <bundle><span class="o">]</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>where <code class="docutils literal"><span class="pre"><bundle></span></code> is the name of the bundle to ingest, defaulting to
|
||||
<a class="reference internal" href="#quantopian-quandl-mirror"><span>quantopian-quandl</span></a>.</p>
|
||||
</div>
|
||||
<div class="section" id="old-data">
|
||||
<h2>Old Data<a class="headerlink" href="#old-data" title="Permalink to this headline">¶</a></h2>
|
||||
<p>When the <code class="docutils literal"><span class="pre">ingest</span></code> command is used it will write the new data to a subdirectory
|
||||
of <code class="docutils literal"><span class="pre">$ZIPLINE_ROOT/data/<bundle></span></code> which is named with the current date. This
|
||||
makes it possible to look at older data or even run backtests with the older
|
||||
copies. Running a backtest with an old ingestion makes it easier to reproduce
|
||||
backtest results later.</p>
|
||||
<p>One drawback of saving all of the data by default is that the data directory
|
||||
may grow quite large even if you do not want to use the data. As shown earlier,
|
||||
we can list all of the ingestions with the <a class="reference internal" href="#bundles-command"><span>bundles command</span></a>. To solve the problem of leaking old data there is another
|
||||
command: <code class="docutils literal"><span class="pre">clean</span></code>, which will clear data bundles based on some time
|
||||
constraints.</p>
|
||||
<p>For example:</p>
|
||||
<div class="highlight-bash"><div class="highlight"><pre><span class="c"># clean everything older than <date></span>
|
||||
<span class="nv">$ </span>zipline clean <span class="o">[</span>-b <bundle><span class="o">]</span> --before <date>
|
||||
|
||||
<span class="c"># clean everything newer than <date></span>
|
||||
<span class="nv">$ </span>zipline clean <span class="o">[</span>-b <bundle><span class="o">]</span> --after <date>
|
||||
|
||||
<span class="c"># keep everything in the range of [before, after] and delete the rest</span>
|
||||
<span class="nv">$ </span>zipline clean <span class="o">[</span>-b <bundle><span class="o">]</span> --before <date> --after <after>
|
||||
|
||||
<span class="c"># clean all but the last <int> runs</span>
|
||||
<span class="nv">$ </span>zipline clean <span class="o">[</span>-b <bundle><span class="o">]</span> --keep-last <int>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="running-backtests-with-data-bundles">
|
||||
<h2>Running Backtests with Data Bundles<a class="headerlink" href="#running-backtests-with-data-bundles" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Now that the data has been ingested we can use it to run backtests with the
|
||||
<code class="docutils literal"><span class="pre">run</span></code> command. The bundle to use can be specified with the <code class="docutils literal"><span class="pre">--bundle</span></code> option
|
||||
like:</p>
|
||||
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>zipline run --bundle <bundle> --algofile algo.py ...
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>We may also specify the date to use to look up the bundle data with the
|
||||
<code class="docutils literal"><span class="pre">--bundle-date</span></code> option. Setting the <code class="docutils literal"><span class="pre">--bundle-date</span></code> will cause run to use
|
||||
the most recent bundle ingestion that is less than or equal to the
|
||||
<code class="docutils literal"><span class="pre">bundle-date</span></code>. This is how we can run backtests with older data. The reason
|
||||
that <code class="docutils literal"><span class="pre">-bundle-date</span></code> uses a less than or equal to relationship is that we can
|
||||
specify the date that we ran an old backtest and get the same data that would
|
||||
have been available to us on that date. The <code class="docutils literal"><span class="pre">bundle-date</span></code> defaults to the
|
||||
current day to use the most recent data.</p>
|
||||
</div>
|
||||
<div class="section" id="default-data-bundles">
|
||||
<h2>Default Data Bundles<a class="headerlink" href="#default-data-bundles" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="section" id="quandl-wiki-bundle">
|
||||
<span id="quandl-data-bundle"></span><h3>Quandl WIKI Bundle<a class="headerlink" href="#quandl-wiki-bundle" title="Permalink to this headline">¶</a></h3>
|
||||
<p>By default zipline comes with the <code class="docutils literal"><span class="pre">quandl</span></code> data bundle which uses quandl’s
|
||||
<a class="reference external" href="https://www.quandl.com/data/WIKI">WIKI dataset</a>. The quandl data bundle
|
||||
includes daily pricing data, splits, cash dividends, and asset metadata. To
|
||||
ingest the <code class="docutils literal"><span class="pre">quandl</span></code> data bundle we recommend creating an account on quandl.com
|
||||
to get an API key to be able to make more API requests per day. Once we have an
|
||||
API key we may run:</p>
|
||||
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ QUANDL_API_KEY</span><span class="o">=</span><api-key> zipline ingest -b quandl
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>though we may still run <code class="docutils literal"><span class="pre">ingest</span></code> as an anonymous quandl user (with no API
|
||||
key). We may also set the <code class="docutils literal"><span class="pre">QUANDL_DOWNLOAD_ATTEMPTS</span></code> environment variable to
|
||||
an integer which is the number of attempts that should be made to download data
|
||||
from quandls servers. By default <code class="docutils literal"><span class="pre">QUANDL_DOWNLOAD_ATTEMPTS</span></code> will be 5, meaning
|
||||
that we will retry each attempt 5 times.</p>
|
||||
<div class="admonition note">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last"><code class="docutils literal"><span class="pre">QUANDL_DOWNLOAD_ATTEMPTS</span></code> is not the total number of allowed failures,
|
||||
just the number of allowed failures per request. The quandl loader will make
|
||||
one request per 100 equities for the metadata followed by one request per
|
||||
equity.</p>
|
||||
</div>
|
||||
<div class="section" id="quantopian-quandl-wiki-mirror">
|
||||
<span id="quantopian-quandl-mirror"></span><h4>Quantopian Quandl WIKI Mirror<a class="headerlink" href="#quantopian-quandl-wiki-mirror" title="Permalink to this headline">¶</a></h4>
|
||||
<p>Quantopian provides a mirror of the quandl WIKI dataset with the data in the
|
||||
formats that zipline expects. This is available under the name:
|
||||
<code class="docutils literal"><span class="pre">quantopian-quandl</span></code> and is the default bundle for zipline.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="yahoo-bundle-factories">
|
||||
<h3>Yahoo Bundle Factories<a class="headerlink" href="#yahoo-bundle-factories" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Zipline also ships with a factory function for creating a data bundle out of a
|
||||
set of tickers from yahoo: <code class="xref py py-func docutils literal"><span class="pre">yahoo_equities()</span></code>.
|
||||
<code class="xref py py-func docutils literal"><span class="pre">yahoo_equities()</span></code> makes it easy to pre-download and
|
||||
cache the data for a set of equities from yahoo. The yahoo bundles include daily
|
||||
pricing data along with splits, cash dividends, and inferred asset metadata. To
|
||||
create a bundle from a set of equities, add the following to your
|
||||
<code class="docutils literal"><span class="pre">~/.zipline/extensions.py</span></code> file:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">zipline.data.bundles</span> <span class="kn">import</span> <span class="n">register</span><span class="p">,</span> <span class="n">yahoo_equities</span>
|
||||
|
||||
<span class="c"># these are the tickers you would like data for</span>
|
||||
<span class="n">equities</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s">'AAPL'</span><span class="p">,</span>
|
||||
<span class="s">'MSFT'</span><span class="p">,</span>
|
||||
<span class="s">'GOOG'</span><span class="p">,</span>
|
||||
<span class="p">}</span>
|
||||
<span class="n">register</span><span class="p">(</span>
|
||||
<span class="s">'my-yahoo-equities-bundle'</span><span class="p">,</span> <span class="c"># name this whatever you like</span>
|
||||
<span class="n">yahoo_equities</span><span class="p">(</span><span class="n">equities</span><span class="p">),</span>
|
||||
<span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This may now be used like:</p>
|
||||
<div class="highlight-bash"><div class="highlight"><pre><span class="nv">$ </span>zipline ingest -b my-yahoo-equities-bundle
|
||||
<span class="nv">$ </span>zipline run -f algo.py --bundle my-yahoo-equities-bundle
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>More than one yahoo equities bundle may be registered as long as they use
|
||||
different names.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="writing-a-new-bundle">
|
||||
<h2>Writing a New Bundle<a class="headerlink" href="#writing-a-new-bundle" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Data bundles exist to make it easy to use different data sources with
|
||||
zipline. To add a new bundle, one must implement an <code class="docutils literal"><span class="pre">ingest</span></code> function.</p>
|
||||
<p>The <code class="docutils literal"><span class="pre">ingest</span></code> function is responsible for loading the data into memory and
|
||||
passing it to a set of writer objects provided by zipline to convert the data to
|
||||
zipline’s internal format. The ingest function may work by downloading data from
|
||||
a remote location like the <code class="docutils literal"><span class="pre">quandl</span></code> bundle or yahoo bundles or it may just
|
||||
load files that are already on the machine. The function is provided with
|
||||
writers that will write the data to the correct location transactionally. If an
|
||||
ingestion fails part way through the bundle will not be written in an incomplete
|
||||
state.</p>
|
||||
<p>The signature of the ingest function should be:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="n">ingest</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span>
|
||||
<span class="n">asset_db_writer</span><span class="p">,</span>
|
||||
<span class="n">minute_bar_writer</span><span class="p">,</span>
|
||||
<span class="n">daily_bar_writer</span><span class="p">,</span>
|
||||
<span class="n">adjustment_writer</span><span class="p">,</span>
|
||||
<span class="n">calendar</span><span class="p">,</span>
|
||||
<span class="n">start_session</span><span class="p">,</span>
|
||||
<span class="n">end_session</span><span class="p">,</span>
|
||||
<span class="n">cache</span><span class="p">,</span>
|
||||
<span class="n">show_progress</span><span class="p">,</span>
|
||||
<span class="n">output_dir</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="section" id="environ">
|
||||
<h3><code class="docutils literal"><span class="pre">environ</span></code><a class="headerlink" href="#environ" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">environ</span></code> is a mapping representing the environment variables to use. This is
|
||||
where any custom arguments needed for the ingestion should be passed, for
|
||||
example: the <code class="docutils literal"><span class="pre">quandl</span></code> bundle uses the enviornment to pass the API key and the
|
||||
download retry attempt count.</p>
|
||||
</div>
|
||||
<div class="section" id="asset-db-writer">
|
||||
<h3><code class="docutils literal"><span class="pre">asset_db_writer</span></code><a class="headerlink" href="#asset-db-writer" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">asset_db_writer</span></code> is an instance of <code class="xref py py-class docutils literal"><span class="pre">AssetDBWriter</span></code>.
|
||||
This is the writer for the asset metadata which provides the asset lifetimes and
|
||||
the symbol to asset id (sid) mapping. This may also contain the asset name,
|
||||
exchange and a few other columns. To write data, invoke
|
||||
<code class="xref py py-meth docutils literal"><span class="pre">write()</span></code> with dataframes for the various
|
||||
pieces of metadata. More information about the format of the data exists in the
|
||||
docs for write.</p>
|
||||
</div>
|
||||
<div class="section" id="minute-bar-writer">
|
||||
<h3><code class="docutils literal"><span class="pre">minute_bar_writer</span></code><a class="headerlink" href="#minute-bar-writer" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">minute_bar_writer</span></code> is an instance of
|
||||
<code class="xref py py-class docutils literal"><span class="pre">BcolzMinuteBarWriter</span></code>. This writer is used to
|
||||
convert data to zipline’s internal bcolz format to later be read by a
|
||||
<code class="xref py py-class docutils literal"><span class="pre">BcolzMinuteBarReader</span></code>. If minute data is
|
||||
provided, users should call
|
||||
<code class="xref py py-meth docutils literal"><span class="pre">write()</span></code> with an iterable of
|
||||
(sid, dataframe) tuples. The <code class="docutils literal"><span class="pre">show_progress</span></code> argument should also be forwarded
|
||||
to this method. If the data source does not provide minute level data, then
|
||||
there is no need to call the write method. It is also acceptable to pass an
|
||||
empty iterator to <code class="xref py py-meth docutils literal"><span class="pre">write()</span></code>
|
||||
to signal that there is no minutely data.</p>
|
||||
<div class="admonition note">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">The data passed to
|
||||
<code class="xref py py-meth docutils literal"><span class="pre">write()</span></code> may be a lazy
|
||||
iterator or generator to avoid loading all of the minute data into memory at
|
||||
a single time. A given sid may also appear multiple times in the data as long
|
||||
as the dates are strictly increasing.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="daily-bar-writer">
|
||||
<h3><code class="docutils literal"><span class="pre">daily_bar_writer</span></code><a class="headerlink" href="#daily-bar-writer" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">daily_bar_writer</span></code> is an instance of
|
||||
<code class="xref py py-class docutils literal"><span class="pre">BcolzDailyBarWriter</span></code>. This writer is
|
||||
used to convert data into zipline’s internal bcolz format to later be read by a
|
||||
<code class="xref py py-class docutils literal"><span class="pre">BcolzDailyBarReader</span></code>. If daily data is
|
||||
provided, users should call
|
||||
<code class="xref py py-meth docutils literal"><span class="pre">write()</span></code> with an iterable of
|
||||
(sid dataframe) tuples. The <code class="docutils literal"><span class="pre">show_progress</span></code> argument should also be forwarded
|
||||
to this method. If the data shource does not provide daily data, then there is
|
||||
no need to call the write method. It is also acceptable to pass an empty
|
||||
iterable to <code class="xref py py-meth docutils literal"><span class="pre">write()</span></code> to
|
||||
signal that there is no daily data. If no daily data is provided but minute data
|
||||
is provided, a daily rollup will happen to service daily history requests.</p>
|
||||
<div class="admonition note">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">Like the <code class="docutils literal"><span class="pre">minute_bar_writer</span></code>, the data passed to
|
||||
<code class="xref py py-meth docutils literal"><span class="pre">write()</span></code> may be a lazy
|
||||
iterable or generator to avoid loading all of the data into memory at once.
|
||||
Unlike the <code class="docutils literal"><span class="pre">minute_bar_writer</span></code>, a sid may only appear once in the data
|
||||
iterable.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="adjustment-writer">
|
||||
<h3><code class="docutils literal"><span class="pre">adjustment_writer</span></code><a class="headerlink" href="#adjustment-writer" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">adjustment_writer</span></code> is an instance of
|
||||
<code class="xref py py-class docutils literal"><span class="pre">SQLiteAdjustmentWriter</span></code>. This writer is
|
||||
used to store splits, mergers, dividends, and stock dividends. The data should
|
||||
be provided as dataframes and passed to
|
||||
<code class="xref py py-meth docutils literal"><span class="pre">write()</span></code>. Each of
|
||||
these fields are optional, but the writer can accept as much of the data as you
|
||||
have.</p>
|
||||
</div>
|
||||
<div class="section" id="calendar">
|
||||
<h3><code class="docutils literal"><span class="pre">calendar</span></code><a class="headerlink" href="#calendar" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">calendar</span></code> is an instance of
|
||||
<code class="xref py py-class docutils literal"><span class="pre">zipline.utils.calendars.TradingCalendar</span></code>. The calendar is provided to
|
||||
help some bundles generate queries for the days needed.</p>
|
||||
</div>
|
||||
<div class="section" id="start-session">
|
||||
<h3><code class="docutils literal"><span class="pre">start_session</span></code><a class="headerlink" href="#start-session" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">start_session</span></code> is a <code class="xref py py-class docutils literal"><span class="pre">pandas.Timestamp</span></code> object indicating the first
|
||||
day that the bundle should load data for.</p>
|
||||
</div>
|
||||
<div class="section" id="end-session">
|
||||
<h3><code class="docutils literal"><span class="pre">end_session</span></code><a class="headerlink" href="#end-session" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">end_session</span></code> is a <code class="xref py py-class docutils literal"><span class="pre">pandas.Timestamp</span></code> object indicating the last day
|
||||
that the bundle should load data for.</p>
|
||||
</div>
|
||||
<div class="section" id="cache">
|
||||
<h3><code class="docutils literal"><span class="pre">cache</span></code><a class="headerlink" href="#cache" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">cache</span></code> is an instance of <code class="xref py py-class docutils literal"><span class="pre">dataframe_cache</span></code>. This
|
||||
object is a mapping from strings to dataframes. This object is provided in case
|
||||
an ingestion crashes part way through. The idea is that the ingest function
|
||||
should check the cache for raw data, if it doesn’t exist in the cache, it should
|
||||
acquire it and then store it in the cache. Then it can parse and write the
|
||||
data. The cache will be cleared only after a successful load, this prevents the
|
||||
ingest function from needing to redownload all the data if there is some bug in
|
||||
the parsing. If it is very fast to get the data, for example if it is coming
|
||||
from another local file, then there is no need to use this cache.</p>
|
||||
</div>
|
||||
<div class="section" id="show-progress">
|
||||
<h3><code class="docutils literal"><span class="pre">show_progress</span></code><a class="headerlink" href="#show-progress" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">show_progress</span></code> is a boolean indicating that the user would like to receive
|
||||
feedback about the ingest function’s progress fetching and writing the
|
||||
data. Some examples for where to show how many files you have downloaded out of
|
||||
the total needed, or how far into some data conversion the ingest function
|
||||
is. One tool that may help with implementing <code class="docutils literal"><span class="pre">show_progress</span></code> for a loop is
|
||||
<code class="xref py py-class docutils literal"><span class="pre">maybe_show_progress</span></code>. This argument should always be
|
||||
forwarded to <code class="docutils literal"><span class="pre">minute_bar_writer.write</span></code> and <code class="docutils literal"><span class="pre">daily_bar_writer.write</span></code>.</p>
|
||||
</div>
|
||||
<div class="section" id="output-dir">
|
||||
<h3><code class="docutils literal"><span class="pre">output_dir</span></code><a class="headerlink" href="#output-dir" title="Permalink to this headline">¶</a></h3>
|
||||
<p><code class="docutils literal"><span class="pre">output_dir</span></code> is a string representing the file path where all the data will be
|
||||
written. <code class="docutils literal"><span class="pre">output_dir</span></code> will be some subdirectory of <code class="docutils literal"><span class="pre">$ZIPLINE_ROOT</span></code> and will
|
||||
contain the time of the start of the current ingestion. This can be used to
|
||||
directly move resources here if for some reason your ingest function can produce
|
||||
it’s own outputs without the writers. For example, the <code class="docutils literal"><span class="pre">quantopian:quandl</span></code>
|
||||
bundle uses this to directly untar the bundle into the <code class="docutils literal"><span class="pre">output_dir</span></code>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
|
||||
<a href="development-guidelines.html" class="btn btn-neutral float-right" title="Development Guidelines" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||
|
||||
|
||||
<a href="beginner-tutorial.html" class="btn btn-neutral" title="Zipline Beginner Tutorial" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
<div role="contentinfo">
|
||||
<p>
|
||||
© Copyright 2017, Enigma MPC, Inc..
|
||||
</p>
|
||||
</div>
|
||||
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT:'./',
|
||||
VERSION:'0.3',
|
||||
COLLAPSE_INDEX:false,
|
||||
FILE_SUFFIX:'.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
SphinxRtdTheme.StickyNav.enable();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,86 +0,0 @@
|
||||
#
|
||||
# Copyright 2015 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import os
|
||||
|
||||
|
||||
# This is *not* a place to dump arbitrary classes/modules for convenience,
|
||||
# it is a place to expose the public interfaces.
|
||||
from . import data
|
||||
from . import finance
|
||||
from . import gens
|
||||
from . import utils
|
||||
from .utils.calendars import get_calendar
|
||||
from .utils.run_algo import run_algorithm
|
||||
from ._version import get_versions
|
||||
|
||||
# These need to happen after the other imports.
|
||||
from . algorithm import TradingAlgorithm
|
||||
from . import api
|
||||
|
||||
|
||||
# PERF: Fire a warning if calendars were instantiated during catalyst import.
|
||||
# Having calendars doesn't break anything per-se, but it makes catalyst imports
|
||||
# noticeably slower, which becomes particularly noticeable in the Zipline CLI.
|
||||
from catalyst.utils.calendars.calendar_utils import global_calendar_dispatcher
|
||||
if global_calendar_dispatcher._calendars:
|
||||
import warnings
|
||||
warnings.warn(
|
||||
"Found TradingCalendar instances after catalyst import.\n"
|
||||
"Zipline startup will be much slower until this is fixed!",
|
||||
)
|
||||
del warnings
|
||||
del global_calendar_dispatcher
|
||||
|
||||
|
||||
__version__ = get_versions()['version']
|
||||
del get_versions
|
||||
|
||||
|
||||
def load_ipython_extension(ipython):
|
||||
from .__main__ import catalyst_magic
|
||||
ipython.register_magic_function(catalyst_magic, 'line_cell', 'catalyst')
|
||||
|
||||
|
||||
if os.name == 'nt':
|
||||
# we need to be able to write to our temp directoy on windows so we
|
||||
# create a subdir in %TMP% that has write access and use that as %TMP%
|
||||
def _():
|
||||
import atexit
|
||||
import tempfile
|
||||
|
||||
tempfile.tempdir = tempdir = tempfile.mkdtemp()
|
||||
|
||||
@atexit.register
|
||||
def cleanup_tempdir():
|
||||
import shutil
|
||||
shutil.rmtree(tempdir)
|
||||
_()
|
||||
del _
|
||||
|
||||
|
||||
__all__ = [
|
||||
'TradingAlgorithm',
|
||||
'api',
|
||||
'data',
|
||||
'finance',
|
||||
'get_calendar',
|
||||
'gens',
|
||||
'run_algorithm',
|
||||
'utils',
|
||||
]
|
||||
|
||||
from ._version import get_versions
|
||||
__version__ = get_versions()['version']
|
||||
del get_versions
|
||||
@@ -1,636 +0,0 @@
|
||||
import errno
|
||||
import os
|
||||
from functools import wraps
|
||||
|
||||
import click
|
||||
import logbook
|
||||
import pandas as pd
|
||||
from six import text_type
|
||||
|
||||
from catalyst.data import bundles as bundles_module
|
||||
from catalyst.exchange.exchange_bundle import ExchangeBundle
|
||||
from catalyst.exchange.init_utils import get_exchange
|
||||
from catalyst.utils.cli import Date, Timestamp
|
||||
from catalyst.utils.run_algo import _run, load_extensions
|
||||
|
||||
try:
|
||||
__IPYTHON__
|
||||
except NameError:
|
||||
__IPYTHON__ = False
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.option(
|
||||
'-e',
|
||||
'--extension',
|
||||
multiple=True,
|
||||
help='File or module path to a catalyst extension to load.',
|
||||
)
|
||||
@click.option(
|
||||
'--strict-extensions/--non-strict-extensions',
|
||||
is_flag=True,
|
||||
help='If --strict-extensions is passed then catalyst will not run if it'
|
||||
' cannot load all of the specified extensions. If this is not passed or'
|
||||
' --non-strict-extensions is passed then the failure will be logged but'
|
||||
' execution will continue.',
|
||||
)
|
||||
@click.option(
|
||||
'--default-extension/--no-default-extension',
|
||||
is_flag=True,
|
||||
default=True,
|
||||
help="Don't load the default catalyst extension.py file in $ZIPLINE_HOME.",
|
||||
)
|
||||
@click.version_option()
|
||||
def main(extension, strict_extensions, default_extension):
|
||||
"""Top level catalyst entry point.
|
||||
"""
|
||||
# install a logbook handler before performing any other operations
|
||||
logbook.StderrHandler().push_application()
|
||||
load_extensions(
|
||||
default_extension,
|
||||
extension,
|
||||
strict_extensions,
|
||||
os.environ,
|
||||
)
|
||||
|
||||
|
||||
def extract_option_object(option):
|
||||
"""Convert a click.option call into a click.Option object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
option : decorator
|
||||
A click.option decorator.
|
||||
|
||||
Returns
|
||||
-------
|
||||
option_object : click.Option
|
||||
The option object that this decorator will create.
|
||||
"""
|
||||
|
||||
@option
|
||||
def opt():
|
||||
pass
|
||||
|
||||
return opt.__click_params__[0]
|
||||
|
||||
|
||||
def ipython_only(option):
|
||||
"""Mark that an option should only be exposed in IPython.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
option : decorator
|
||||
A click.option decorator.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ipython_only_dec : decorator
|
||||
A decorator that correctly applies the argument even when not
|
||||
using IPython mode.
|
||||
"""
|
||||
if __IPYTHON__:
|
||||
return option
|
||||
|
||||
argname = extract_option_object(option).name
|
||||
|
||||
def d(f):
|
||||
@wraps(f)
|
||||
def _(*args, **kwargs):
|
||||
kwargs[argname] = None
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return _
|
||||
|
||||
return d
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option(
|
||||
'-f',
|
||||
'--algofile',
|
||||
default=None,
|
||||
type=click.File('r'),
|
||||
help='The file that contains the algorithm to run.',
|
||||
)
|
||||
@click.option(
|
||||
'-t',
|
||||
'--algotext',
|
||||
help='The algorithm script to run.',
|
||||
)
|
||||
@click.option(
|
||||
'-D',
|
||||
'--define',
|
||||
multiple=True,
|
||||
help="Define a name to be bound in the namespace before executing"
|
||||
" the algotext. For example '-Dname=value'. The value may be any python"
|
||||
" expression. These are evaluated in order so they may refer to previously"
|
||||
" defined names.",
|
||||
)
|
||||
@click.option(
|
||||
'--data-frequency',
|
||||
type=click.Choice({'daily', 'minute'}),
|
||||
default='daily',
|
||||
show_default=True,
|
||||
help='The data frequency of the simulation.',
|
||||
)
|
||||
@click.option(
|
||||
'--capital-base',
|
||||
type=float,
|
||||
default=10e6,
|
||||
show_default=True,
|
||||
help='The starting capital for the simulation.',
|
||||
)
|
||||
@click.option(
|
||||
'-b',
|
||||
'--bundle',
|
||||
default='poloniex',
|
||||
metavar='BUNDLE-NAME',
|
||||
show_default=True,
|
||||
help='The data bundle to use for the simulation.',
|
||||
)
|
||||
@click.option(
|
||||
'--bundle-timestamp',
|
||||
type=Timestamp(),
|
||||
default=pd.Timestamp.utcnow(),
|
||||
show_default=False,
|
||||
help='The date to lookup data on or before.\n'
|
||||
'[default: <current-time>]'
|
||||
)
|
||||
@click.option(
|
||||
'-s',
|
||||
'--start',
|
||||
type=Date(tz='utc', as_timestamp=True),
|
||||
help='The start date of the simulation.',
|
||||
)
|
||||
@click.option(
|
||||
'-e',
|
||||
'--end',
|
||||
type=Date(tz='utc', as_timestamp=True),
|
||||
help='The end date of the simulation.',
|
||||
)
|
||||
@click.option(
|
||||
'-o',
|
||||
'--output',
|
||||
default='-',
|
||||
metavar='FILENAME',
|
||||
show_default=True,
|
||||
help="The location to write the perf data. If this is '-' the perf will"
|
||||
" be written to stdout.",
|
||||
)
|
||||
@click.option(
|
||||
'--print-algo/--no-print-algo',
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help='Print the algorithm to stdout.',
|
||||
)
|
||||
@ipython_only(click.option(
|
||||
'--local-namespace/--no-local-namespace',
|
||||
is_flag=True,
|
||||
default=None,
|
||||
help='Should the algorithm methods be resolved in the local namespace.'
|
||||
))
|
||||
@click.option(
|
||||
'-x',
|
||||
'--exchange-name',
|
||||
type=click.Choice({'bitfinex', 'bittrex', 'poloniex'}),
|
||||
help='The name of the targeted exchange (supported: bitfinex, bittrex, poloniex).',
|
||||
)
|
||||
@click.option(
|
||||
'-n',
|
||||
'--algo-namespace',
|
||||
help='A label assigned to the algorithm for data storage purposes.'
|
||||
)
|
||||
@click.option(
|
||||
'-c',
|
||||
'--base-currency',
|
||||
help='The base currency used to calculate statistics '
|
||||
'(e.g. usd, btc, eth).',
|
||||
)
|
||||
@click.pass_context
|
||||
def run(ctx,
|
||||
algofile,
|
||||
algotext,
|
||||
define,
|
||||
data_frequency,
|
||||
capital_base,
|
||||
bundle,
|
||||
bundle_timestamp,
|
||||
start,
|
||||
end,
|
||||
output,
|
||||
print_algo,
|
||||
local_namespace,
|
||||
exchange_name,
|
||||
algo_namespace,
|
||||
base_currency):
|
||||
"""Run a backtest for the given algorithm.
|
||||
"""
|
||||
|
||||
if (algotext is not None) == (algofile is not None):
|
||||
ctx.fail(
|
||||
"must specify exactly one of '-f' / '--algofile' or"
|
||||
" '-t' / '--algotext'",
|
||||
)
|
||||
|
||||
# check that the start and end dates are passed correctly
|
||||
if start is None and end is None:
|
||||
# check both at the same time to avoid the case where a user
|
||||
# does not pass either of these and then passes the first only
|
||||
# to be told they need to pass the second argument also
|
||||
ctx.fail(
|
||||
"must specify dates with '-s' / '--start' and '-e' / '--end'",
|
||||
)
|
||||
if start is None:
|
||||
ctx.fail("must specify a start date with '-s' / '--start'")
|
||||
if end is None:
|
||||
ctx.fail("must specify an end date with '-e' / '--end'")
|
||||
|
||||
if exchange_name is None:
|
||||
ctx.fail("must specify an exchange name '-x'")
|
||||
|
||||
perf = _run(
|
||||
initialize=None,
|
||||
handle_data=None,
|
||||
before_trading_start=None,
|
||||
analyze=None,
|
||||
algofile=algofile,
|
||||
algotext=algotext,
|
||||
defines=define,
|
||||
data_frequency=data_frequency,
|
||||
capital_base=capital_base,
|
||||
data=None,
|
||||
bundle=bundle,
|
||||
bundle_timestamp=bundle_timestamp,
|
||||
start=start,
|
||||
end=end,
|
||||
output=output,
|
||||
print_algo=print_algo,
|
||||
local_namespace=local_namespace,
|
||||
environ=os.environ,
|
||||
live=False,
|
||||
exchange=exchange_name,
|
||||
algo_namespace=algo_namespace,
|
||||
base_currency=base_currency,
|
||||
live_graph=False
|
||||
)
|
||||
|
||||
if output == '-':
|
||||
click.echo(str(perf))
|
||||
elif output != os.devnull: # make the catalyst magic not write any data
|
||||
perf.to_pickle(output)
|
||||
|
||||
return perf
|
||||
|
||||
|
||||
def catalyst_magic(line, cell=None):
|
||||
"""The catalyst IPython cell magic.
|
||||
"""
|
||||
load_extensions(
|
||||
default=True,
|
||||
extensions=[],
|
||||
strict=True,
|
||||
environ=os.environ,
|
||||
)
|
||||
try:
|
||||
return run.main(
|
||||
# put our overrides at the start of the parameter list so that
|
||||
# users may pass values with higher precedence
|
||||
[
|
||||
'--algotext', cell,
|
||||
'--output', os.devnull, # don't write the results by default
|
||||
] + ([
|
||||
# these options are set when running in line magic mode
|
||||
# set a non None algo text to use the ipython user_ns
|
||||
'--algotext', '',
|
||||
'--local-namespace',
|
||||
] if cell is None else []) + line.split(),
|
||||
'%s%%catalyst' % ((cell or '') and '%'),
|
||||
# don't use system exit and propogate errors to the caller
|
||||
standalone_mode=False,
|
||||
)
|
||||
except SystemExit as e:
|
||||
# https://github.com/mitsuhiko/click/pull/533
|
||||
# even in standalone_mode=False `--help` really wants to kill us ;_;
|
||||
if e.code:
|
||||
raise ValueError('main returned non-zero status code: %d' % e.code)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option(
|
||||
'-f',
|
||||
'--algofile',
|
||||
default=None,
|
||||
type=click.File('r'),
|
||||
help='The file that contains the algorithm to run.',
|
||||
)
|
||||
@click.option(
|
||||
'-t',
|
||||
'--algotext',
|
||||
help='The algorithm script to run.',
|
||||
)
|
||||
@click.option(
|
||||
'-D',
|
||||
'--define',
|
||||
multiple=True,
|
||||
help="Define a name to be bound in the namespace before executing"
|
||||
" the algotext. For example '-Dname=value'. The value may be any python"
|
||||
" expression. These are evaluated in order so they may refer to previously"
|
||||
" defined names.",
|
||||
)
|
||||
@click.option(
|
||||
'-o',
|
||||
'--output',
|
||||
default='-',
|
||||
metavar='FILENAME',
|
||||
show_default=True,
|
||||
help="The location to write the perf data. If this is '-' the perf will"
|
||||
" be written to stdout.",
|
||||
)
|
||||
@click.option(
|
||||
'--print-algo/--no-print-algo',
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help='Print the algorithm to stdout.',
|
||||
)
|
||||
@ipython_only(click.option(
|
||||
'--local-namespace/--no-local-namespace',
|
||||
is_flag=True,
|
||||
default=None,
|
||||
help='Should the algorithm methods be resolved in the local namespace.'
|
||||
))
|
||||
@click.option(
|
||||
'-x',
|
||||
'--exchange-name',
|
||||
type=click.Choice({'bitfinex', 'bittrex', 'poloniex'}),
|
||||
help='The name of the targeted exchange (supported: bitfinex, bittrex, poloniex).',
|
||||
)
|
||||
@click.option(
|
||||
'-n',
|
||||
'--algo-namespace',
|
||||
help='A label assigned to the algorithm for data storage purposes.'
|
||||
)
|
||||
@click.option(
|
||||
'-c',
|
||||
'--base-currency',
|
||||
help='The base currency used to calculate statistics '
|
||||
'(e.g. usd, btc, eth).',
|
||||
)
|
||||
@click.option(
|
||||
'--live-graph/--no-live-graph',
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help='Display live graph.',
|
||||
)
|
||||
@click.pass_context
|
||||
def live(ctx,
|
||||
algofile,
|
||||
algotext,
|
||||
define,
|
||||
output,
|
||||
print_algo,
|
||||
local_namespace,
|
||||
exchange_name,
|
||||
algo_namespace,
|
||||
base_currency,
|
||||
live_graph):
|
||||
"""Trade live with the given algorithm.
|
||||
"""
|
||||
if (algotext is not None) == (algofile is not None):
|
||||
ctx.fail(
|
||||
"must specify exactly one of '-f' / '--algofile' or"
|
||||
" '-t' / '--algotext'",
|
||||
)
|
||||
|
||||
if exchange_name is None:
|
||||
ctx.fail("must specify an exchange name '-x'")
|
||||
if algo_namespace is None:
|
||||
ctx.fail("must specify an algorithm name '-n' in live execution mode")
|
||||
if base_currency is None:
|
||||
ctx.fail("must specify a base currency '-c' in live execution mode")
|
||||
|
||||
perf = _run(
|
||||
initialize=None,
|
||||
handle_data=None,
|
||||
before_trading_start=None,
|
||||
analyze=None,
|
||||
algofile=algofile,
|
||||
algotext=algotext,
|
||||
defines=define,
|
||||
data_frequency=None,
|
||||
capital_base=None,
|
||||
data=None,
|
||||
bundle=None,
|
||||
bundle_timestamp=None,
|
||||
start=None,
|
||||
end=None,
|
||||
output=output,
|
||||
print_algo=print_algo,
|
||||
local_namespace=local_namespace,
|
||||
environ=os.environ,
|
||||
live=True,
|
||||
exchange=exchange_name,
|
||||
algo_namespace=algo_namespace,
|
||||
base_currency=base_currency,
|
||||
live_graph=live_graph
|
||||
)
|
||||
|
||||
if output == '-':
|
||||
click.echo(str(perf))
|
||||
elif output != os.devnull: # make the catalyst magic not write any data
|
||||
perf.to_pickle(output)
|
||||
|
||||
return perf
|
||||
|
||||
|
||||
@main.command(name='ingest-exchange')
|
||||
@click.option(
|
||||
'-x',
|
||||
'--exchange-name',
|
||||
type=click.Choice({'bitfinex', 'bittrex', 'poloniex'}),
|
||||
help='The name of the exchange bundle to ingest (supported: bitfinex,'
|
||||
' bittrex, poloniex).',
|
||||
)
|
||||
@click.option(
|
||||
'-f',
|
||||
'--data-frequency',
|
||||
type=click.Choice({'daily', 'minute', 'daily,minute', 'minute,daily'}),
|
||||
default='daily',
|
||||
show_default=True,
|
||||
help='The data frequency of the desired OHLCV bars.',
|
||||
)
|
||||
@click.option(
|
||||
'-s',
|
||||
'--start',
|
||||
default=None,
|
||||
type=Date(tz='utc', as_timestamp=True),
|
||||
help='The start date of the data range. (default: one year from end date)',
|
||||
)
|
||||
@click.option(
|
||||
'-e',
|
||||
'--end',
|
||||
default=None,
|
||||
type=Date(tz='utc', as_timestamp=True),
|
||||
help='The end date of the data range. (default: today)',
|
||||
)
|
||||
@click.option(
|
||||
'-i',
|
||||
'--include-symbols',
|
||||
default=None,
|
||||
help='A list of symbols to ingest (optional comma separated list)',
|
||||
)
|
||||
@click.option(
|
||||
'--exclude-symbols',
|
||||
default=None,
|
||||
help='A list of symbols to exclude from the ingestion '
|
||||
'(optional comma separated list)',
|
||||
)
|
||||
@click.option(
|
||||
'--show-progress/--no-show-progress',
|
||||
default=True,
|
||||
help='Print progress information to the terminal.'
|
||||
)
|
||||
def ingest_exchange(exchange_name, data_frequency, start, end,
|
||||
include_symbols, exclude_symbols, show_progress):
|
||||
"""
|
||||
Ingest data for the given exchange.
|
||||
"""
|
||||
exchange = get_exchange(exchange_name)
|
||||
exchange_bundle = ExchangeBundle(exchange)
|
||||
|
||||
click.echo('ingesting exchange bundle {}'.format(exchange_name))
|
||||
exchange_bundle.ingest(
|
||||
data_frequency=data_frequency,
|
||||
include_symbols=include_symbols,
|
||||
exclude_symbols=exclude_symbols,
|
||||
start=start,
|
||||
end=end,
|
||||
show_progress=show_progress
|
||||
)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option(
|
||||
'-b',
|
||||
'--bundle',
|
||||
metavar='BUNDLE-NAME',
|
||||
default=None,
|
||||
show_default=False,
|
||||
help='The data bundle to ingest.',
|
||||
)
|
||||
@click.option(
|
||||
'-x',
|
||||
'--exchange-name',
|
||||
type=click.Choice({'bitfinex', 'bittrex', 'poloniex'}),
|
||||
help='The name of the exchange bundle to ingest (supported: bitfinex,'
|
||||
' bittrex, poloniex).',
|
||||
)
|
||||
@click.option(
|
||||
'-c',
|
||||
'--compile-locally',
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help='Download dataset from source and compile bundle locally.',
|
||||
)
|
||||
@click.option(
|
||||
'--assets-version',
|
||||
type=int,
|
||||
multiple=True,
|
||||
help='Version of the assets db to which to downgrade.',
|
||||
)
|
||||
@click.option(
|
||||
'--show-progress/--no-show-progress',
|
||||
default=True,
|
||||
help='Print progress information to the terminal.'
|
||||
)
|
||||
@click.pass_context
|
||||
def ingest(ctx, bundle, exchange_name, compile_locally, assets_version,
|
||||
show_progress):
|
||||
"""Ingest the data for the given bundle.
|
||||
"""
|
||||
|
||||
bundles_module.ingest(
|
||||
bundle,
|
||||
os.environ,
|
||||
pd.Timestamp.utcnow(),
|
||||
assets_version,
|
||||
show_progress,
|
||||
compile_locally,
|
||||
)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option(
|
||||
'-b',
|
||||
'--bundle',
|
||||
default='poloniex',
|
||||
metavar='BUNDLE-NAME',
|
||||
show_default=True,
|
||||
help='The data bundle to clean.',
|
||||
)
|
||||
@click.option(
|
||||
'-x',
|
||||
'--exchange_name',
|
||||
metavar='EXCHANGE-NAME',
|
||||
show_default=True,
|
||||
help='The exchange bundle name to clean.',
|
||||
)
|
||||
@click.option(
|
||||
'-e',
|
||||
'--before',
|
||||
type=Timestamp(),
|
||||
help='Clear all data before TIMESTAMP.'
|
||||
' This may not be passed with -k / --keep-last',
|
||||
)
|
||||
@click.option(
|
||||
'-a',
|
||||
'--after',
|
||||
type=Timestamp(),
|
||||
help='Clear all data after TIMESTAMP'
|
||||
' This may not be passed with -k / --keep-last',
|
||||
)
|
||||
@click.option(
|
||||
'-k',
|
||||
'--keep-last',
|
||||
type=int,
|
||||
metavar='N',
|
||||
help='Clear all but the last N downloads.'
|
||||
' This may not be passed with -e / --before or -a / --after',
|
||||
)
|
||||
def clean(bundle, before, after, keep_last):
|
||||
"""Clean up data downloaded with the ingest command.
|
||||
"""
|
||||
bundles_module.clean(
|
||||
bundle,
|
||||
before,
|
||||
after,
|
||||
keep_last,
|
||||
)
|
||||
|
||||
|
||||
@main.command()
|
||||
def bundles():
|
||||
"""List all of the available data bundles.
|
||||
"""
|
||||
for bundle in sorted(bundles_module.bundles.keys()):
|
||||
if bundle.startswith('.'):
|
||||
# hide the test data
|
||||
continue
|
||||
try:
|
||||
ingestions = list(
|
||||
map(text_type, bundles_module.ingestions_for_bundle(bundle))
|
||||
)
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
ingestions = []
|
||||
|
||||
# If we got no ingestions, either because the directory didn't exist or
|
||||
# because there were no entries, print a single message indicating that
|
||||
# no ingestions have yet been made.
|
||||
for timestamp in ingestions or ["<no ingestions>"]:
|
||||
click.echo("%s %s" % (bundle, timestamp))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,936 +0,0 @@
|
||||
#
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import warnings
|
||||
from contextlib import contextmanager
|
||||
from functools import wraps
|
||||
|
||||
from pandas.tslib import normalize_date
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
from six import iteritems, PY2, string_types
|
||||
from cpython cimport bool
|
||||
from collections import Iterable
|
||||
|
||||
from catalyst.assets import (Asset,
|
||||
AssetConvertible,
|
||||
PricingDataAssociable,
|
||||
Future)
|
||||
from catalyst.assets.continuous_futures import ContinuousFuture
|
||||
from catalyst.catalyst_warnings import ZiplineDeprecationWarning
|
||||
|
||||
|
||||
cdef bool _is_iterable(obj):
|
||||
return isinstance(obj, Iterable) and not isinstance(obj, string_types)
|
||||
|
||||
|
||||
# Wraps doesn't work for method objects in python2. Docs should be generated
|
||||
# with python3 so it is not a big deal.
|
||||
if PY2:
|
||||
def no_wraps_py2(f):
|
||||
def dec(g):
|
||||
return g
|
||||
return dec
|
||||
else:
|
||||
no_wraps_py2 = wraps
|
||||
|
||||
|
||||
cdef class check_parameters(object):
|
||||
"""
|
||||
Asserts that the keywords passed into the wrapped function are included
|
||||
in those passed into this decorator. If not, raise a TypeError with a
|
||||
meaningful message, unlike the one Cython returns by default.
|
||||
|
||||
Also asserts that the arguments passed into the wrapped function are
|
||||
consistent with the types passed into this decorator. If not, raise a
|
||||
TypeError with a meaningful message.
|
||||
"""
|
||||
cdef tuple keyword_names
|
||||
cdef tuple types
|
||||
cdef dict keys_to_types
|
||||
|
||||
def __init__(self, keyword_names, types):
|
||||
self.keyword_names = keyword_names
|
||||
self.types = types
|
||||
|
||||
self.keys_to_types = dict(zip(keyword_names, types))
|
||||
|
||||
def __call__(self, func):
|
||||
@no_wraps_py2(func)
|
||||
def assert_keywords_and_call(*args, **kwargs):
|
||||
cdef short i
|
||||
|
||||
# verify all the keyword arguments
|
||||
for field in kwargs:
|
||||
if field not in self.keyword_names:
|
||||
raise TypeError("%s() got an unexpected keyword argument"
|
||||
" '%s'" % (func.__name__, field))
|
||||
|
||||
# verify type of each argument
|
||||
for i, arg in enumerate(args[1:]):
|
||||
expected_type = self.types[i]
|
||||
|
||||
if (i == 0 or i == 1) and _is_iterable(arg):
|
||||
if len(arg) == 0:
|
||||
continue
|
||||
arg = arg[0]
|
||||
|
||||
if not isinstance(arg, expected_type):
|
||||
expected_type_name = expected_type.__name__ \
|
||||
if not _is_iterable(expected_type) \
|
||||
else ', '.join([type_.__name__ for type_ in expected_type])
|
||||
|
||||
raise TypeError("Expected %s argument to be of type %s%s" %
|
||||
(self.keyword_names[i],
|
||||
'or iterable of type ' if i in (0, 1) else '',
|
||||
expected_type_name)
|
||||
)
|
||||
|
||||
# verify type of each kwarg
|
||||
for keyword, arg in iteritems(kwargs):
|
||||
if keyword in ('assets', 'fields') and _is_iterable(arg):
|
||||
if len(arg) == 0:
|
||||
continue
|
||||
arg = arg[0]
|
||||
if not isinstance(arg, self.keys_to_types[keyword]):
|
||||
expected_type = self.keys_to_types[keyword].__name__ \
|
||||
if not _is_iterable(self.keys_to_types[keyword]) \
|
||||
else ', '.join([type_.__name__ for type_ in
|
||||
self.keys_to_types[keyword]])
|
||||
|
||||
raise TypeError("Expected %s argument to be of type %s%s" %
|
||||
(keyword,
|
||||
'or iterable of type ' if keyword in
|
||||
('assets', 'fields') else '',
|
||||
expected_type)
|
||||
)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return assert_keywords_and_call
|
||||
|
||||
|
||||
@contextmanager
|
||||
def handle_non_market_minutes(bar_data):
|
||||
try:
|
||||
bar_data._handle_non_market_minutes = True
|
||||
yield
|
||||
finally:
|
||||
bar_data._handle_non_market_minutes = False
|
||||
|
||||
|
||||
cdef class BarData:
|
||||
"""
|
||||
Provides methods to access spot value or history windows of price data.
|
||||
Also provides some utility methods to determine if an asset is alive,
|
||||
has recent trade data, etc.
|
||||
|
||||
This is what is passed as ``data`` to the ``handle_data`` function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_portal : DataPortal
|
||||
Provider for bar pricing data.
|
||||
simulation_dt_func : callable
|
||||
Function which returns the current simulation time.
|
||||
This is usually bound to a method of TradingSimulation.
|
||||
data_frequency : {'minute', 'daily'}
|
||||
The frequency of the bar data; i.e. whether the data is
|
||||
daily or minute bars
|
||||
restrictions : catalyst.finance.asset_restrictions.Restrictions
|
||||
Object that combines and returns restricted list information from
|
||||
multiple sources
|
||||
universe_func : callable, optional
|
||||
Function which returns the current 'universe'. This is for
|
||||
backwards compatibility with older API concepts.
|
||||
"""
|
||||
cdef object data_portal
|
||||
cdef object simulation_dt_func
|
||||
cdef object data_frequency
|
||||
cdef object restrictions
|
||||
cdef dict _views
|
||||
cdef object _universe_func
|
||||
cdef object _last_calculated_universe
|
||||
cdef object _universe_last_updated_at
|
||||
cdef bool _daily_mode
|
||||
cdef object _trading_calendar
|
||||
cdef object _is_restricted
|
||||
|
||||
cdef bool _adjust_minutes
|
||||
|
||||
def __init__(self, data_portal, simulation_dt_func, data_frequency,
|
||||
trading_calendar, restrictions, universe_func=None):
|
||||
self.data_portal = data_portal
|
||||
self.simulation_dt_func = simulation_dt_func
|
||||
self.data_frequency = data_frequency
|
||||
self._views = {}
|
||||
|
||||
self._daily_mode = (self.data_frequency == "daily")
|
||||
|
||||
self._universe_func = universe_func
|
||||
self._last_calculated_universe = None
|
||||
self._universe_last_updated_at = None
|
||||
|
||||
self._adjust_minutes = False
|
||||
|
||||
self._trading_calendar = trading_calendar
|
||||
self._is_restricted = restrictions.is_restricted
|
||||
|
||||
cdef _get_equity_price_view(self, asset):
|
||||
"""
|
||||
Returns a DataPortalSidView for the given asset. Used to support the
|
||||
data[sid(N)] public API. Not needed if DataPortal is used standalone.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : Asset
|
||||
Asset that is being queried.
|
||||
|
||||
Returns
|
||||
-------
|
||||
SidView : Accessor into the given asset's data.
|
||||
"""
|
||||
try:
|
||||
self._warn_deprecated("`data[sid(N)]` is deprecated. Use "
|
||||
"`data.current`.")
|
||||
view = self._views[asset]
|
||||
except KeyError:
|
||||
try:
|
||||
asset = self.data_portal.asset_finder.retrieve_asset(asset)
|
||||
except ValueError:
|
||||
# assume fetcher
|
||||
pass
|
||||
view = self._views[asset] = self._create_sid_view(asset)
|
||||
|
||||
return view
|
||||
|
||||
cdef _create_sid_view(self, asset):
|
||||
return SidView(
|
||||
asset,
|
||||
self.data_portal,
|
||||
self.simulation_dt_func,
|
||||
self.data_frequency
|
||||
)
|
||||
|
||||
cdef _get_current_minute(self):
|
||||
"""
|
||||
Internal utility method to get the current simulation time.
|
||||
|
||||
Possible answers are:
|
||||
- whatever the algorithm's get_datetime() method returns (this is what
|
||||
`self.simulation_dt_func()` points to)
|
||||
- sometimes we're knowingly not in a market minute, like if we're in
|
||||
before_trading_start. In that case, `self._adjust_minutes` is
|
||||
True, and we get the previous market minute.
|
||||
- if we're in daily mode, get the session label for this minute.
|
||||
"""
|
||||
dt = self.simulation_dt_func()
|
||||
|
||||
if self._adjust_minutes:
|
||||
dt = \
|
||||
self.data_portal.trading_calendar.previous_minute(dt)
|
||||
|
||||
if self._daily_mode:
|
||||
# if we're in daily mode, take the given dt (which is the last
|
||||
# minute of the session) and get the session label for it.
|
||||
dt = self.data_portal.trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
return dt
|
||||
|
||||
@check_parameters(('assets', 'fields'),
|
||||
((Asset, ContinuousFuture) + string_types, string_types))
|
||||
def current(self, assets, fields):
|
||||
"""
|
||||
Returns the current value of the given assets for the given fields
|
||||
at the current simulation time. Current values are the as-traded price
|
||||
and are usually not adjusted for events like splits or dividends (see
|
||||
notes for more information).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
assets : Asset or iterable of Assets
|
||||
fields : str or iterable[str].
|
||||
Valid values are: "price",
|
||||
"last_traded", "open", "high", "low", "close", "volume", or column
|
||||
names in files read by ``fetch_csv``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
current_value : Scalar, pandas Series, or pandas DataFrame.
|
||||
See notes below.
|
||||
|
||||
Notes
|
||||
-----
|
||||
If a single asset and a single field are passed in, a scalar float
|
||||
value is returned.
|
||||
|
||||
If a single asset and a list of fields are passed in, a pandas Series
|
||||
is returned whose indices are the fields, and whose values are scalar
|
||||
values for this asset for each field.
|
||||
|
||||
If a list of assets and a single field are passed in, a pandas Series
|
||||
is returned whose indices are the assets, and whose values are scalar
|
||||
values for each asset for the given field.
|
||||
|
||||
If a list of assets and a list of fields are passed in, a pandas
|
||||
DataFrame is returned, indexed by asset. The columns are the requested
|
||||
fields, filled with the scalar values for each asset for each field.
|
||||
|
||||
If the current simulation time is not a valid market time, we use the
|
||||
last market close instead.
|
||||
|
||||
"price" returns the last known close price of the asset. If there is
|
||||
no last known value (either because the asset has never traded, or
|
||||
because it has delisted) NaN is returned. If a value is found, and we
|
||||
had to cross an adjustment boundary (split, dividend, etc) to get it,
|
||||
the value is adjusted before being returned.
|
||||
|
||||
"last_traded" returns the date of the last trade event of the asset,
|
||||
even if the asset has stopped trading. If there is no last known value,
|
||||
pd.NaT is returned.
|
||||
|
||||
"volume" returns the trade volume for the current simulation time. If
|
||||
there is no trade this minute, 0 is returned.
|
||||
|
||||
"open", "high", "low", and "close" return the relevant information for
|
||||
the current trade bar. If there is no current trade bar, NaN is
|
||||
returned.
|
||||
"""
|
||||
multiple_assets = _is_iterable(assets)
|
||||
multiple_fields = _is_iterable(fields)
|
||||
|
||||
# There's some overly verbose code in here, particularly around
|
||||
# 'do something if self._adjust_minutes is False, otherwise do
|
||||
# something else'. This could be less verbose, but the 99% case is that
|
||||
# `self._adjust_minutes` is False, so it's important to keep that code
|
||||
# path as fast as possible.
|
||||
|
||||
# There's probably a way to make this method (and `history`) less
|
||||
# verbose, but this is OK for now.
|
||||
|
||||
if not multiple_assets:
|
||||
asset = assets
|
||||
|
||||
if not multiple_fields:
|
||||
field = fields
|
||||
|
||||
# return scalar value
|
||||
if not self._adjust_minutes:
|
||||
return self.data_portal.get_spot_value(
|
||||
asset,
|
||||
field,
|
||||
self._get_current_minute(),
|
||||
self.data_frequency
|
||||
)
|
||||
else:
|
||||
return self.data_portal.get_adjusted_value(
|
||||
asset,
|
||||
field,
|
||||
self._get_current_minute(),
|
||||
self.simulation_dt_func(),
|
||||
self.data_frequency
|
||||
)
|
||||
else:
|
||||
# assume fields is iterable
|
||||
# return a Series indexed by field
|
||||
if not self._adjust_minutes:
|
||||
return pd.Series(data={
|
||||
field: self.data_portal.get_spot_value(
|
||||
asset,
|
||||
field,
|
||||
self._get_current_minute(),
|
||||
self.data_frequency
|
||||
)
|
||||
for field in fields
|
||||
}, index=fields, name=assets.symbol)
|
||||
else:
|
||||
return pd.Series(data={
|
||||
field: self.data_portal.get_adjusted_value(
|
||||
asset,
|
||||
field,
|
||||
self._get_current_minute(),
|
||||
self.simulation_dt_func(),
|
||||
self.data_frequency
|
||||
)
|
||||
for field in fields
|
||||
}, index=fields, name=assets.symbol)
|
||||
else:
|
||||
if not multiple_fields:
|
||||
field = fields
|
||||
|
||||
# assume assets is iterable
|
||||
# return a Series indexed by asset
|
||||
if not self._adjust_minutes:
|
||||
return pd.Series(data={
|
||||
asset: self.data_portal.get_spot_value(
|
||||
asset,
|
||||
field,
|
||||
self._get_current_minute(),
|
||||
self.data_frequency
|
||||
)
|
||||
for asset in assets
|
||||
}, index=assets, name=fields)
|
||||
else:
|
||||
return pd.Series(data={
|
||||
asset: self.data_portal.get_adjusted_value(
|
||||
asset,
|
||||
field,
|
||||
self._get_current_minute(),
|
||||
self.simulation_dt_func(),
|
||||
self.data_frequency
|
||||
)
|
||||
for asset in assets
|
||||
}, index=assets, name=fields)
|
||||
|
||||
else:
|
||||
# both assets and fields are iterable
|
||||
data = {}
|
||||
|
||||
if not self._adjust_minutes:
|
||||
for field in fields:
|
||||
series = pd.Series(data={
|
||||
asset: self.data_portal.get_spot_value(
|
||||
asset,
|
||||
field,
|
||||
self._get_current_minute(),
|
||||
self.data_frequency
|
||||
)
|
||||
for asset in assets
|
||||
}, index=assets, name=field)
|
||||
data[field] = series
|
||||
else:
|
||||
for field in fields:
|
||||
series = pd.Series(data={
|
||||
asset: self.data_portal.get_adjusted_value(
|
||||
asset,
|
||||
field,
|
||||
self._get_current_minute(),
|
||||
self.simulation_dt_func(),
|
||||
self.data_frequency
|
||||
)
|
||||
for asset in assets
|
||||
}, index=assets, name=field)
|
||||
data[field] = series
|
||||
|
||||
return pd.DataFrame(data)
|
||||
|
||||
@check_parameters(('continuous_future',),
|
||||
(ContinuousFuture,))
|
||||
def current_chain(self, continuous_future):
|
||||
return self.data_portal.get_current_future_chain(
|
||||
continuous_future,
|
||||
self.simulation_dt_func())
|
||||
|
||||
@check_parameters(('assets',), (Asset,))
|
||||
def can_trade(self, assets):
|
||||
"""
|
||||
For the given asset or iterable of assets, returns true if all of the
|
||||
following are true:
|
||||
1) the asset is alive for the session of the current simulation time
|
||||
(if current simulation time is not a market minute, we use the next
|
||||
session)
|
||||
2) (if we are in minute mode) the asset's exchange is open at the
|
||||
current simulation time or at the simulation calendar's next market
|
||||
minute
|
||||
3) there is a known last price for the asset.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The second condition above warrants some further explanation.
|
||||
- If the asset's exchange calendar is identical to the simulation
|
||||
calendar, then this condition always returns True.
|
||||
- If there are market minutes in the simulation calendar outside of
|
||||
this asset's exchange's trading hours (for example, if the simulation
|
||||
is running on the CME calendar but the asset is MSFT, which trades on
|
||||
the NYSE), during those minutes, this condition will return false
|
||||
(for example, 3:15 am Eastern on a weekday, during which the CME is
|
||||
open but the NYSE is closed).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
assets: Asset or iterable of assets
|
||||
|
||||
Returns
|
||||
-------
|
||||
can_trade : bool or pd.Series[bool] indexed by asset.
|
||||
"""
|
||||
dt = self.simulation_dt_func()
|
||||
|
||||
if self._adjust_minutes:
|
||||
adjusted_dt = self._get_current_minute()
|
||||
else:
|
||||
adjusted_dt = dt
|
||||
|
||||
data_portal = self.data_portal
|
||||
|
||||
if isinstance(assets, Asset):
|
||||
return self._can_trade_for_asset(
|
||||
assets, dt, adjusted_dt, data_portal
|
||||
)
|
||||
else:
|
||||
tradeable = [
|
||||
self._can_trade_for_asset(
|
||||
asset, dt, adjusted_dt, data_portal
|
||||
)
|
||||
for asset in assets
|
||||
]
|
||||
return pd.Series(data=tradeable, index=assets, dtype=bool)
|
||||
|
||||
cdef bool _can_trade_for_asset(self, asset, dt, adjusted_dt, data_portal):
|
||||
cdef object session_label
|
||||
cdef object dt_to_use_for_exchange_check,
|
||||
|
||||
if self._is_restricted(asset, adjusted_dt):
|
||||
return False
|
||||
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
# asset isn't alive
|
||||
return False
|
||||
|
||||
if asset.auto_close_date and session_label >= asset.auto_close_date:
|
||||
return False
|
||||
|
||||
if not self._daily_mode:
|
||||
# Find the next market minute for this calendar, and check if this
|
||||
# asset's exchange is open at that minute.
|
||||
if self._trading_calendar.is_open_on_minute(dt):
|
||||
dt_to_use_for_exchange_check = dt
|
||||
else:
|
||||
dt_to_use_for_exchange_check = \
|
||||
self._trading_calendar.next_open(dt)
|
||||
|
||||
if not asset.is_exchange_open(dt_to_use_for_exchange_check):
|
||||
return False
|
||||
|
||||
# is there a last price?
|
||||
return not np.isnan(
|
||||
data_portal.get_spot_value(
|
||||
asset, "price", adjusted_dt, self.data_frequency
|
||||
)
|
||||
)
|
||||
|
||||
@check_parameters(('assets',), (Asset,))
|
||||
def is_stale(self, assets):
|
||||
"""
|
||||
For the given asset or iterable of assets, returns true if the asset
|
||||
is alive and there is no trade data for the current simulation time.
|
||||
|
||||
If the asset has never traded, returns False.
|
||||
|
||||
If the current simulation time is not a valid market time, we use the
|
||||
current time to check if the asset is alive, but we use the last
|
||||
market minute/day for the trade data check.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
assets: Asset or iterable of assets
|
||||
|
||||
Returns
|
||||
-------
|
||||
boolean or Series of booleans, indexed by asset.
|
||||
"""
|
||||
dt = self.simulation_dt_func()
|
||||
if self._adjust_minutes:
|
||||
adjusted_dt = self._get_current_minute()
|
||||
else:
|
||||
adjusted_dt = dt
|
||||
|
||||
data_portal = self.data_portal
|
||||
|
||||
if isinstance(assets, Asset):
|
||||
return self._is_stale_for_asset(
|
||||
assets, dt, adjusted_dt, data_portal
|
||||
)
|
||||
else:
|
||||
return pd.Series(data={
|
||||
asset: self._is_stale_for_asset(
|
||||
asset, dt, adjusted_dt, data_portal
|
||||
)
|
||||
for asset in assets
|
||||
})
|
||||
|
||||
cdef bool _is_stale_for_asset(self, asset, dt, adjusted_dt, data_portal):
|
||||
session_label = normalize_date(dt) # FIXME
|
||||
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
return False
|
||||
|
||||
current_volume = data_portal.get_spot_value(
|
||||
asset, "volume", adjusted_dt, self.data_frequency
|
||||
)
|
||||
|
||||
if current_volume > 0:
|
||||
# found a current value, so we know this asset is not stale.
|
||||
return False
|
||||
else:
|
||||
# we need to distinguish between if this asset has ever traded
|
||||
# (stale = True) or has never traded (stale = False)
|
||||
last_traded_dt = \
|
||||
data_portal.get_spot_value(asset, "last_traded", adjusted_dt,
|
||||
self.data_frequency)
|
||||
|
||||
return not (last_traded_dt is pd.NaT)
|
||||
|
||||
@check_parameters(('assets', 'fields', 'bar_count',
|
||||
'frequency'),
|
||||
((Asset, ContinuousFuture) + string_types, string_types,
|
||||
int,
|
||||
string_types))
|
||||
def history(self, assets, fields, bar_count, frequency):
|
||||
"""
|
||||
Returns a window of data for the given assets and fields.
|
||||
|
||||
This data is adjusted for splits, dividends, and mergers as of the
|
||||
current algorithm time.
|
||||
|
||||
The semantics of missing data are identical to the ones described in
|
||||
the notes for `get_spot_value`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
assets: Asset or iterable of Asset
|
||||
|
||||
fields: string or iterable of string. Valid values are "open", "high",
|
||||
"low", "close", "volume", "price", and "last_traded".
|
||||
|
||||
bar_count: integer number of bars of trade data
|
||||
|
||||
frequency: string. "1m" for minutely data or "1d" for daily date
|
||||
|
||||
Returns
|
||||
-------
|
||||
history : Series or DataFrame or Panel
|
||||
Return type depends on the dimensionality of the 'assets' and
|
||||
'fields' parameters.
|
||||
|
||||
If single asset and field are passed in, the returned Series is
|
||||
indexed by dt.
|
||||
|
||||
If multiple assets and single field are passed in, the returned
|
||||
DataFrame is indexed by dt, and has assets as columns.
|
||||
|
||||
If a single asset and multiple fields are passed in, the returned
|
||||
DataFrame is indexed by dt, and has fields as columns.
|
||||
|
||||
If multiple assets and multiple fields are passed in, the returned
|
||||
Panel is indexed by field, has dt as the major axis, and assets
|
||||
as the minor axis.
|
||||
|
||||
Notes
|
||||
-----
|
||||
If the current simulation time is not a valid market time, we use the
|
||||
last market close instead.
|
||||
"""
|
||||
if isinstance(fields, string_types):
|
||||
single_asset = isinstance(assets, PricingDataAssociable)
|
||||
|
||||
if single_asset:
|
||||
asset_list = [assets]
|
||||
else:
|
||||
asset_list = assets
|
||||
|
||||
df = self.data_portal.get_history_window(
|
||||
asset_list,
|
||||
self._get_current_minute(),
|
||||
bar_count,
|
||||
frequency,
|
||||
fields,
|
||||
self.data_frequency,
|
||||
)
|
||||
|
||||
if self._adjust_minutes:
|
||||
adjs = self.data_portal.get_adjustments(
|
||||
assets,
|
||||
fields,
|
||||
self._get_current_minute(),
|
||||
self.simulation_dt_func()
|
||||
)
|
||||
|
||||
df = df * adjs
|
||||
|
||||
if single_asset:
|
||||
# single asset, single field, return a series.
|
||||
return df[assets]
|
||||
else:
|
||||
# multiple assets, single field, return a dataframe whose
|
||||
# columns are the assets, indexed by dt.
|
||||
return df
|
||||
else:
|
||||
if isinstance(assets, PricingDataAssociable):
|
||||
# one asset, multiple fields. for now, just make multiple
|
||||
# history calls, one per field, then stitch together the
|
||||
# results. this can definitely be optimized!
|
||||
|
||||
df_dict = {
|
||||
field: self.data_portal.get_history_window(
|
||||
[assets],
|
||||
self._get_current_minute(),
|
||||
bar_count,
|
||||
frequency,
|
||||
field,
|
||||
self.data_frequency,
|
||||
)[assets] for field in fields
|
||||
}
|
||||
|
||||
if self._adjust_minutes:
|
||||
adjs = {
|
||||
field: self.data_portal.get_adjustments(
|
||||
assets,
|
||||
field,
|
||||
self._get_current_minute(),
|
||||
self.simulation_dt_func()
|
||||
)[0] for field in fields
|
||||
}
|
||||
|
||||
df_dict = {field: df * adjs[field]
|
||||
for field, df in iteritems(df_dict)}
|
||||
|
||||
# returned dataframe whose columns are the fields, indexed by
|
||||
# dt.
|
||||
return pd.DataFrame(df_dict)
|
||||
|
||||
else:
|
||||
df_dict = {
|
||||
field: self.data_portal.get_history_window(
|
||||
assets,
|
||||
self._get_current_minute(),
|
||||
bar_count,
|
||||
frequency,
|
||||
field,
|
||||
self.data_frequency,
|
||||
) for field in fields
|
||||
}
|
||||
|
||||
if self._adjust_minutes:
|
||||
adjs = {
|
||||
field: self.data_portal.get_adjustments(
|
||||
assets,
|
||||
field,
|
||||
self._get_current_minute(),
|
||||
self.simulation_dt_func()
|
||||
) for field in fields
|
||||
}
|
||||
|
||||
df_dict = {field: df * adjs[field]
|
||||
for field, df in iteritems(df_dict)}
|
||||
|
||||
# returned panel has:
|
||||
# items: fields
|
||||
# major axis: dt
|
||||
# minor axis: assets
|
||||
return pd.Panel(df_dict)
|
||||
|
||||
property current_dt:
|
||||
def __get__(self):
|
||||
return self.simulation_dt_func()
|
||||
|
||||
@property
|
||||
def fetcher_assets(self):
|
||||
return self.data_portal.get_fetcher_assets(self.simulation_dt_func())
|
||||
|
||||
property _handle_non_market_minutes:
|
||||
def __set__(self, val):
|
||||
self._adjust_minutes = val
|
||||
|
||||
property current_session:
|
||||
def __get__(self):
|
||||
return self._trading_calendar.minute_to_session_label(
|
||||
self.simulation_dt_func(),
|
||||
direction="next"
|
||||
)
|
||||
|
||||
property current_session_minutes:
|
||||
def __get__(self):
|
||||
return self._trading_calendar.minutes_for_session(
|
||||
self.current_session
|
||||
)
|
||||
|
||||
#################
|
||||
# OLD API SUPPORT
|
||||
#################
|
||||
cdef _calculate_universe(self):
|
||||
if self._universe_func is None:
|
||||
return []
|
||||
|
||||
simulation_dt = self.simulation_dt_func()
|
||||
if self._last_calculated_universe is None or \
|
||||
self._universe_last_updated_at != simulation_dt:
|
||||
|
||||
self._last_calculated_universe = self._universe_func()
|
||||
self._universe_last_updated_at = simulation_dt
|
||||
|
||||
return self._last_calculated_universe
|
||||
|
||||
def __iter__(self):
|
||||
self._warn_deprecated("Iterating over the assets in `data` is "
|
||||
"deprecated.")
|
||||
for asset in self._calculate_universe():
|
||||
yield asset
|
||||
|
||||
def __contains__(self, asset):
|
||||
self._warn_deprecated("Checking whether an asset is in data is "
|
||||
"deprecated.")
|
||||
universe = self._calculate_universe()
|
||||
return asset in universe
|
||||
|
||||
def items(self):
|
||||
self._warn_deprecated("Iterating over the assets in `data` is "
|
||||
"deprecated.")
|
||||
return [(asset, self[asset]) for asset in self._calculate_universe()]
|
||||
|
||||
def iteritems(self):
|
||||
self._warn_deprecated("Iterating over the assets in `data` is "
|
||||
"deprecated.")
|
||||
for asset in self._calculate_universe():
|
||||
yield asset, self[asset]
|
||||
|
||||
def __len__(self):
|
||||
self._warn_deprecated("Iterating over the assets in `data` is "
|
||||
"deprecated.")
|
||||
|
||||
return len(self._calculate_universe())
|
||||
|
||||
def keys(self):
|
||||
self._warn_deprecated("Iterating over the assets in `data` is "
|
||||
"deprecated.")
|
||||
|
||||
return list(self._calculate_universe())
|
||||
|
||||
def iterkeys(self):
|
||||
return iter(self.keys())
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self._get_equity_price_view(name)
|
||||
|
||||
cdef _warn_deprecated(self, msg):
|
||||
warnings.warn(
|
||||
msg,
|
||||
category=ZiplineDeprecationWarning,
|
||||
stacklevel=1
|
||||
)
|
||||
|
||||
cdef class SidView:
|
||||
cdef object asset
|
||||
cdef object data_portal
|
||||
cdef object simulation_dt_func
|
||||
cdef object data_frequency
|
||||
|
||||
"""
|
||||
This class exists to temporarily support the deprecated data[sid(N)] API.
|
||||
"""
|
||||
def __init__(self, asset, data_portal, simulation_dt_func, data_frequency):
|
||||
"""
|
||||
Parameters
|
||||
---------
|
||||
asset : Asset
|
||||
The asset for which the instance retrieves data.
|
||||
|
||||
data_portal : DataPortal
|
||||
Provider for bar pricing data.
|
||||
|
||||
simulation_dt_func: function
|
||||
Function which returns the current simulation time.
|
||||
This is usually bound to a method of TradingSimulation.
|
||||
|
||||
data_frequency: string
|
||||
The frequency of the bar data; i.e. whether the data is
|
||||
'daily' or 'minute' bars
|
||||
"""
|
||||
self.asset = asset
|
||||
self.data_portal = data_portal
|
||||
self.simulation_dt_func = simulation_dt_func
|
||||
self.data_frequency = data_frequency
|
||||
|
||||
def __getattr__(self, column):
|
||||
# backwards compatibility code for Q1 API
|
||||
if column == "close_price":
|
||||
column = "close"
|
||||
elif column == "open_price":
|
||||
column = "open"
|
||||
elif column == "dt":
|
||||
return self.dt
|
||||
elif column == "datetime":
|
||||
return self.datetime
|
||||
elif column == "sid":
|
||||
return self.sid
|
||||
|
||||
return self.data_portal.get_spot_value(
|
||||
self.asset,
|
||||
column,
|
||||
self.simulation_dt_func(),
|
||||
self.data_frequency
|
||||
)
|
||||
|
||||
def __contains__(self, column):
|
||||
return self.data_portal.contains(self.asset, column)
|
||||
|
||||
def __getitem__(self, column):
|
||||
return self.__getattr__(column)
|
||||
|
||||
property sid:
|
||||
def __get__(self):
|
||||
return self.asset
|
||||
|
||||
property dt:
|
||||
def __get__(self):
|
||||
return self.datetime
|
||||
|
||||
property datetime:
|
||||
def __get__(self):
|
||||
return self.data_portal.get_last_traded_dt(
|
||||
self.asset,
|
||||
self.simulation_dt_func(),
|
||||
self.data_frequency)
|
||||
|
||||
property current_dt:
|
||||
def __get__(self):
|
||||
return self.simulation_dt_func()
|
||||
|
||||
def mavg(self, num_minutes):
|
||||
self._warn_deprecated("The `mavg` method is deprecated.")
|
||||
return self.data_portal.get_simple_transform(
|
||||
self.asset, "mavg", self.simulation_dt_func(),
|
||||
self.data_frequency, bars=num_minutes
|
||||
)
|
||||
|
||||
def stddev(self, num_minutes):
|
||||
self._warn_deprecated("The `stddev` method is deprecated.")
|
||||
return self.data_portal.get_simple_transform(
|
||||
self.asset, "stddev", self.simulation_dt_func(),
|
||||
self.data_frequency, bars=num_minutes
|
||||
)
|
||||
|
||||
def vwap(self, num_minutes):
|
||||
self._warn_deprecated("The `vwap` method is deprecated.")
|
||||
return self.data_portal.get_simple_transform(
|
||||
self.asset, "vwap", self.simulation_dt_func(),
|
||||
self.data_frequency, bars=num_minutes
|
||||
)
|
||||
|
||||
def returns(self):
|
||||
self._warn_deprecated("The `returns` method is deprecated.")
|
||||
return self.data_portal.get_simple_transform(
|
||||
self.asset, "returns", self.simulation_dt_func(),
|
||||
self.data_frequency
|
||||
)
|
||||
|
||||
cdef _warn_deprecated(self, msg):
|
||||
warnings.warn(
|
||||
msg,
|
||||
category=ZiplineDeprecationWarning,
|
||||
stacklevel=1
|
||||
)
|
||||
@@ -1,460 +0,0 @@
|
||||
|
||||
# This file helps to compute a version number in source trees obtained from
|
||||
# git-archive tarball (such as those provided by githubs download-from-tag
|
||||
# feature). Distribution tarballs (built by setup.py sdist) and build
|
||||
# directories (produced by setup.py build) will contain a much shorter file
|
||||
# that just contains the computed version number.
|
||||
|
||||
# This file is released into the public domain. Generated by
|
||||
# versioneer-0.15 (https://github.com/warner/python-versioneer)
|
||||
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def get_keywords():
|
||||
# these strings will be replaced by git during git-archive.
|
||||
# setup.py/versioneer.py will grep for the variable names, so they must
|
||||
# each be defined on a line of their own. _version.py will just call
|
||||
# get_keywords().
|
||||
git_refnames = "$Format:%d$"
|
||||
git_full = "$Format:%H$"
|
||||
keywords = {"refnames": git_refnames, "full": git_full}
|
||||
return keywords
|
||||
|
||||
|
||||
class VersioneerConfig:
|
||||
pass
|
||||
|
||||
|
||||
def get_config():
|
||||
# these strings are filled in when 'setup.py versioneer' creates
|
||||
# _version.py
|
||||
cfg = VersioneerConfig()
|
||||
cfg.VCS = "git"
|
||||
cfg.style = "pep440"
|
||||
cfg.tag_prefix = ""
|
||||
cfg.parentdir_prefix = "catalyst-"
|
||||
cfg.versionfile_source = "catalyst/_version.py"
|
||||
cfg.verbose = False
|
||||
return cfg
|
||||
|
||||
|
||||
class NotThisMethod(Exception):
|
||||
pass
|
||||
|
||||
|
||||
LONG_VERSION_PY = {}
|
||||
HANDLERS = {}
|
||||
|
||||
|
||||
def register_vcs_handler(vcs, method): # decorator
|
||||
def decorate(f):
|
||||
if vcs not in HANDLERS:
|
||||
HANDLERS[vcs] = {}
|
||||
HANDLERS[vcs][method] = f
|
||||
return f
|
||||
return decorate
|
||||
|
||||
|
||||
def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False):
|
||||
assert isinstance(commands, list)
|
||||
p = None
|
||||
for c in commands:
|
||||
try:
|
||||
dispcmd = str([c] + args)
|
||||
# remember shell=False, so use git.cmd on windows, not just git
|
||||
p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE,
|
||||
stderr=(subprocess.PIPE if hide_stderr
|
||||
else None))
|
||||
break
|
||||
except EnvironmentError:
|
||||
e = sys.exc_info()[1]
|
||||
if e.errno == errno.ENOENT:
|
||||
continue
|
||||
if verbose:
|
||||
print("unable to run %s" % dispcmd)
|
||||
print(e)
|
||||
return None
|
||||
else:
|
||||
if verbose:
|
||||
print("unable to find command, tried %s" % (commands,))
|
||||
return None
|
||||
stdout = p.communicate()[0].strip()
|
||||
if sys.version_info[0] >= 3:
|
||||
stdout = stdout.decode()
|
||||
if p.returncode != 0:
|
||||
if verbose:
|
||||
print("unable to run %s (error)" % dispcmd)
|
||||
return None
|
||||
return stdout
|
||||
|
||||
|
||||
def versions_from_parentdir(parentdir_prefix, root, verbose):
|
||||
# Source tarballs conventionally unpack into a directory that includes
|
||||
# both the project name and a version string.
|
||||
dirname = os.path.basename(root)
|
||||
if not dirname.startswith(parentdir_prefix):
|
||||
if verbose:
|
||||
print("guessing rootdir is '%s', but '%s' doesn't start with "
|
||||
"prefix '%s'" % (root, dirname, parentdir_prefix))
|
||||
raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
|
||||
return {"version": dirname[len(parentdir_prefix):],
|
||||
"full-revisionid": None,
|
||||
"dirty": False, "error": None}
|
||||
|
||||
|
||||
@register_vcs_handler("git", "get_keywords")
|
||||
def git_get_keywords(versionfile_abs):
|
||||
# the code embedded in _version.py can just fetch the value of these
|
||||
# keywords. When used from setup.py, we don't want to import _version.py,
|
||||
# so we do it with a regexp instead. This function is not used from
|
||||
# _version.py.
|
||||
keywords = {}
|
||||
try:
|
||||
f = open(versionfile_abs, "r")
|
||||
for line in f.readlines():
|
||||
if line.strip().startswith("git_refnames ="):
|
||||
mo = re.search(r'=\s*"(.*)"', line)
|
||||
if mo:
|
||||
keywords["refnames"] = mo.group(1)
|
||||
if line.strip().startswith("git_full ="):
|
||||
mo = re.search(r'=\s*"(.*)"', line)
|
||||
if mo:
|
||||
keywords["full"] = mo.group(1)
|
||||
f.close()
|
||||
except EnvironmentError:
|
||||
pass
|
||||
return keywords
|
||||
|
||||
|
||||
@register_vcs_handler("git", "keywords")
|
||||
def git_versions_from_keywords(keywords, tag_prefix, verbose):
|
||||
if not keywords:
|
||||
raise NotThisMethod("no keywords at all, weird")
|
||||
refnames = keywords["refnames"].strip()
|
||||
if refnames.startswith("$Format"):
|
||||
if verbose:
|
||||
print("keywords are unexpanded, not using")
|
||||
raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
|
||||
refs = set([r.strip() for r in refnames.strip("()").split(",")])
|
||||
# starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
|
||||
# just "foo-1.0". If we see a "tag: " prefix, prefer those.
|
||||
TAG = "tag: "
|
||||
tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
|
||||
if not tags:
|
||||
# Either we're using git < 1.8.3, or there really are no tags. We use
|
||||
# a heuristic: assume all version tags have a digit. The old git %d
|
||||
# expansion behaves like git log --decorate=short and strips out the
|
||||
# refs/heads/ and refs/tags/ prefixes that would let us distinguish
|
||||
# between branches and tags. By ignoring refnames without digits, we
|
||||
# filter out many common branch names like "release" and
|
||||
# "stabilization", as well as "HEAD" and "master".
|
||||
tags = set([r for r in refs if re.search(r'\d', r)])
|
||||
if verbose:
|
||||
print("discarding '%s', no digits" % ",".join(refs-tags))
|
||||
if verbose:
|
||||
print("likely tags: %s" % ",".join(sorted(tags)))
|
||||
for ref in sorted(tags):
|
||||
# sorting will prefer e.g. "2.0" over "2.0rc1"
|
||||
if ref.startswith(tag_prefix):
|
||||
r = ref[len(tag_prefix):]
|
||||
if verbose:
|
||||
print("picking %s" % r)
|
||||
return {"version": r,
|
||||
"full-revisionid": keywords["full"].strip(),
|
||||
"dirty": False, "error": None
|
||||
}
|
||||
# no suitable tags, so version is "0+unknown", but full hex is still there
|
||||
if verbose:
|
||||
print("no suitable tags, using unknown + full revision id")
|
||||
return {"version": "0+unknown",
|
||||
"full-revisionid": keywords["full"].strip(),
|
||||
"dirty": False, "error": "no suitable tags"}
|
||||
|
||||
|
||||
@register_vcs_handler("git", "pieces_from_vcs")
|
||||
def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
|
||||
# this runs 'git' from the root of the source tree. This only gets called
|
||||
# if the git-archive 'subst' keywords were *not* expanded, and
|
||||
# _version.py hasn't already been rewritten with a short version string,
|
||||
# meaning we're inside a checked out source tree.
|
||||
|
||||
if not os.path.exists(os.path.join(root, ".git")):
|
||||
if verbose:
|
||||
print("no .git in %s" % root)
|
||||
raise NotThisMethod("no .git directory")
|
||||
|
||||
GITS = ["git"]
|
||||
if sys.platform == "win32":
|
||||
GITS = ["git.cmd", "git.exe"]
|
||||
# if there is a tag, this yields TAG-NUM-gHEX[-dirty]
|
||||
# if there are no tags, this yields HEX[-dirty] (no NUM)
|
||||
describe_out = run_command(GITS, ["describe", "--tags", "--dirty",
|
||||
"--always", "--long"],
|
||||
cwd=root)
|
||||
# --long was added in git-1.5.5
|
||||
if describe_out is None:
|
||||
raise NotThisMethod("'git describe' failed")
|
||||
describe_out = describe_out.strip()
|
||||
full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
|
||||
if full_out is None:
|
||||
raise NotThisMethod("'git rev-parse' failed")
|
||||
full_out = full_out.strip()
|
||||
|
||||
pieces = {}
|
||||
pieces["long"] = full_out
|
||||
pieces["short"] = full_out[:7] # maybe improved later
|
||||
pieces["error"] = None
|
||||
|
||||
# parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
|
||||
# TAG might have hyphens.
|
||||
git_describe = describe_out
|
||||
|
||||
# look for -dirty suffix
|
||||
dirty = git_describe.endswith("-dirty")
|
||||
pieces["dirty"] = dirty
|
||||
if dirty:
|
||||
git_describe = git_describe[:git_describe.rindex("-dirty")]
|
||||
|
||||
# now we have TAG-NUM-gHEX or HEX
|
||||
|
||||
if "-" in git_describe:
|
||||
# TAG-NUM-gHEX
|
||||
mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
|
||||
if not mo:
|
||||
# unparseable. Maybe git-describe is misbehaving?
|
||||
pieces["error"] = ("unable to parse git-describe output: '%s'"
|
||||
% describe_out)
|
||||
return pieces
|
||||
|
||||
# tag
|
||||
full_tag = mo.group(1)
|
||||
if not full_tag.startswith(tag_prefix):
|
||||
if verbose:
|
||||
fmt = "tag '%s' doesn't start with prefix '%s'"
|
||||
print(fmt % (full_tag, tag_prefix))
|
||||
pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
|
||||
% (full_tag, tag_prefix))
|
||||
return pieces
|
||||
pieces["closest-tag"] = full_tag[len(tag_prefix):]
|
||||
|
||||
# distance: number of commits since tag
|
||||
pieces["distance"] = int(mo.group(2))
|
||||
|
||||
# commit: short hex revision ID
|
||||
pieces["short"] = mo.group(3)
|
||||
|
||||
else:
|
||||
# HEX: no tags
|
||||
pieces["closest-tag"] = None
|
||||
count_out = run_command(GITS, ["rev-list", "HEAD", "--count"],
|
||||
cwd=root)
|
||||
pieces["distance"] = int(count_out) # total number of commits
|
||||
|
||||
return pieces
|
||||
|
||||
|
||||
def plus_or_dot(pieces):
|
||||
if "+" in pieces.get("closest-tag", ""):
|
||||
return "."
|
||||
return "+"
|
||||
|
||||
|
||||
def render_pep440(pieces):
|
||||
# now build up version string, with post-release "local version
|
||||
# identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
|
||||
# get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
|
||||
|
||||
# exceptions:
|
||||
# 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
|
||||
|
||||
if pieces["closest-tag"]:
|
||||
rendered = pieces["closest-tag"]
|
||||
if pieces["distance"] or pieces["dirty"]:
|
||||
rendered += plus_or_dot(pieces)
|
||||
rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
|
||||
if pieces["dirty"]:
|
||||
rendered += ".dirty"
|
||||
else:
|
||||
# exception #1
|
||||
rendered = "0+untagged.%d.g%s" % (pieces["distance"],
|
||||
pieces["short"])
|
||||
if pieces["dirty"]:
|
||||
rendered += ".dirty"
|
||||
return rendered
|
||||
|
||||
|
||||
def render_pep440_pre(pieces):
|
||||
# TAG[.post.devDISTANCE] . No -dirty
|
||||
|
||||
# exceptions:
|
||||
# 1: no tags. 0.post.devDISTANCE
|
||||
|
||||
if pieces["closest-tag"]:
|
||||
rendered = pieces["closest-tag"]
|
||||
if pieces["distance"]:
|
||||
rendered += ".post.dev%d" % pieces["distance"]
|
||||
else:
|
||||
# exception #1
|
||||
rendered = "0.post.dev%d" % pieces["distance"]
|
||||
return rendered
|
||||
|
||||
|
||||
def render_pep440_post(pieces):
|
||||
# TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that
|
||||
# .dev0 sorts backwards (a dirty tree will appear "older" than the
|
||||
# corresponding clean one), but you shouldn't be releasing software with
|
||||
# -dirty anyways.
|
||||
|
||||
# exceptions:
|
||||
# 1: no tags. 0.postDISTANCE[.dev0]
|
||||
|
||||
if pieces["closest-tag"]:
|
||||
rendered = pieces["closest-tag"]
|
||||
if pieces["distance"] or pieces["dirty"]:
|
||||
rendered += ".post%d" % pieces["distance"]
|
||||
if pieces["dirty"]:
|
||||
rendered += ".dev0"
|
||||
rendered += plus_or_dot(pieces)
|
||||
rendered += "g%s" % pieces["short"]
|
||||
else:
|
||||
# exception #1
|
||||
rendered = "0.post%d" % pieces["distance"]
|
||||
if pieces["dirty"]:
|
||||
rendered += ".dev0"
|
||||
rendered += "+g%s" % pieces["short"]
|
||||
return rendered
|
||||
|
||||
|
||||
def render_pep440_old(pieces):
|
||||
# TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty.
|
||||
|
||||
# exceptions:
|
||||
# 1: no tags. 0.postDISTANCE[.dev0]
|
||||
|
||||
if pieces["closest-tag"]:
|
||||
rendered = pieces["closest-tag"]
|
||||
if pieces["distance"] or pieces["dirty"]:
|
||||
rendered += ".post%d" % pieces["distance"]
|
||||
if pieces["dirty"]:
|
||||
rendered += ".dev0"
|
||||
else:
|
||||
# exception #1
|
||||
rendered = "0.post%d" % pieces["distance"]
|
||||
if pieces["dirty"]:
|
||||
rendered += ".dev0"
|
||||
return rendered
|
||||
|
||||
|
||||
def render_git_describe(pieces):
|
||||
# TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty
|
||||
# --always'
|
||||
|
||||
# exceptions:
|
||||
# 1: no tags. HEX[-dirty] (note: no 'g' prefix)
|
||||
|
||||
if pieces["closest-tag"]:
|
||||
rendered = pieces["closest-tag"]
|
||||
if pieces["distance"]:
|
||||
rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
|
||||
else:
|
||||
# exception #1
|
||||
rendered = pieces["short"]
|
||||
if pieces["dirty"]:
|
||||
rendered += "-dirty"
|
||||
return rendered
|
||||
|
||||
|
||||
def render_git_describe_long(pieces):
|
||||
# TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty
|
||||
# --always -long'. The distance/hash is unconditional.
|
||||
|
||||
# exceptions:
|
||||
# 1: no tags. HEX[-dirty] (note: no 'g' prefix)
|
||||
|
||||
if pieces["closest-tag"]:
|
||||
rendered = pieces["closest-tag"]
|
||||
rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
|
||||
else:
|
||||
# exception #1
|
||||
rendered = pieces["short"]
|
||||
if pieces["dirty"]:
|
||||
rendered += "-dirty"
|
||||
return rendered
|
||||
|
||||
|
||||
def render(pieces, style):
|
||||
if pieces["error"]:
|
||||
return {"version": "unknown",
|
||||
"full-revisionid": pieces.get("long"),
|
||||
"dirty": None,
|
||||
"error": pieces["error"]}
|
||||
|
||||
if not style or style == "default":
|
||||
style = "pep440" # the default
|
||||
|
||||
if style == "pep440":
|
||||
rendered = render_pep440(pieces)
|
||||
elif style == "pep440-pre":
|
||||
rendered = render_pep440_pre(pieces)
|
||||
elif style == "pep440-post":
|
||||
rendered = render_pep440_post(pieces)
|
||||
elif style == "pep440-old":
|
||||
rendered = render_pep440_old(pieces)
|
||||
elif style == "git-describe":
|
||||
rendered = render_git_describe(pieces)
|
||||
elif style == "git-describe-long":
|
||||
rendered = render_git_describe_long(pieces)
|
||||
else:
|
||||
raise ValueError("unknown style '%s'" % style)
|
||||
|
||||
return {"version": rendered, "full-revisionid": pieces["long"],
|
||||
"dirty": pieces["dirty"], "error": None}
|
||||
|
||||
|
||||
def get_versions():
|
||||
# I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
|
||||
# __file__, we can work backwards from there to the root. Some
|
||||
# py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
|
||||
# case we can only use expanded keywords.
|
||||
|
||||
cfg = get_config()
|
||||
verbose = cfg.verbose
|
||||
|
||||
try:
|
||||
return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
|
||||
verbose)
|
||||
except NotThisMethod:
|
||||
pass
|
||||
|
||||
try:
|
||||
root = os.path.realpath(__file__)
|
||||
# versionfile_source is the relative path from the top of the source
|
||||
# tree (where the .git directory might live) to this file. Invert
|
||||
# this to find the root from __file__.
|
||||
for i in cfg.versionfile_source.split('/'):
|
||||
root = os.path.dirname(root)
|
||||
except NameError:
|
||||
return {"version": "0+unknown", "full-revisionid": None,
|
||||
"dirty": None,
|
||||
"error": "unable to find root of source tree"}
|
||||
|
||||
try:
|
||||
pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
|
||||
return render(pieces, cfg.style)
|
||||
except NotThisMethod:
|
||||
pass
|
||||
|
||||
try:
|
||||
if cfg.parentdir_prefix:
|
||||
return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
|
||||
except NotThisMethod:
|
||||
pass
|
||||
|
||||
return {"version": "0+unknown", "full-revisionid": None,
|
||||
"dirty": None,
|
||||
"error": "unable to compute version"}
|
||||
@@ -1,59 +0,0 @@
|
||||
#
|
||||
# Copyright 2014 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Note that part of the API is implemented in TradingAlgorithm as
|
||||
# methods (e.g. order). These are added to this namespace via the
|
||||
# decorator ``api_method`` inside of algorithm.py.
|
||||
from .finance.asset_restrictions import (
|
||||
Restriction,
|
||||
StaticRestrictions,
|
||||
HistoricalRestrictions,
|
||||
RESTRICTION_STATES,
|
||||
)
|
||||
from .finance import commission, execution, slippage, cancel_policy
|
||||
from .finance.cancel_policy import (
|
||||
NeverCancel,
|
||||
EODCancel
|
||||
)
|
||||
from .finance.slippage import (
|
||||
FixedSlippage,
|
||||
VolumeShareSlippage,
|
||||
)
|
||||
from .utils import math_utils, events
|
||||
from .utils.events import (
|
||||
calendars,
|
||||
date_rules,
|
||||
time_rules
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'EODCancel',
|
||||
'FixedSlippage',
|
||||
'NeverCancel',
|
||||
'VolumeShareSlippage',
|
||||
'Restriction',
|
||||
'StaticRestrictions',
|
||||
'HistoricalRestrictions',
|
||||
'RESTRICTION_STATES',
|
||||
'cancel_policy',
|
||||
'commission',
|
||||
'date_rules',
|
||||
'events',
|
||||
'execution',
|
||||
'math_utils',
|
||||
'slippage',
|
||||
'time_rules',
|
||||
'calendars',
|
||||
]
|
||||
@@ -1,775 +0,0 @@
|
||||
import collections
|
||||
from catalyst.assets import Asset, Equity, Future
|
||||
from catalyst.assets.futures import FutureChain
|
||||
from catalyst.finance.asset_restrictions import Restrictions
|
||||
from catalyst.finance.cancel_policy import CancelPolicy
|
||||
from catalyst.pipeline import Pipeline
|
||||
from catalyst.protocol import Order
|
||||
from catalyst.utils.events import EventRule
|
||||
from catalyst.utils.security_list import SecurityList
|
||||
|
||||
|
||||
def attach_pipeline(pipeline, name, chunks=None):
|
||||
"""Register a pipeline to be computed at the start of each day.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pipeline : Pipeline
|
||||
The pipeline to have computed.
|
||||
name : str
|
||||
The name of the pipeline.
|
||||
chunks : int or iterator, optional
|
||||
The number of days to compute pipeline results for. Increasing
|
||||
this number will make it longer to get the first results but
|
||||
may improve the total runtime of the simulation. If an iterator
|
||||
is passed, we will run in chunks based on values of the itereator.
|
||||
|
||||
Returns
|
||||
-------
|
||||
pipeline : Pipeline
|
||||
Returns the pipeline that was attached unchanged.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:func:`catalyst.api.pipeline_output`
|
||||
"""
|
||||
|
||||
def batch_market_order(share_counts):
|
||||
"""Place a batch market order for multiple assets.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
share_counts : pd.Series[Asset -> int]
|
||||
Map from asset to number of shares to order for that asset.
|
||||
|
||||
Returns
|
||||
-------
|
||||
order_ids : pd.Index[str]
|
||||
Index of ids for newly-created orders.
|
||||
"""
|
||||
|
||||
def cancel_order(order_param):
|
||||
"""Cancel an open order.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order_param : str or Order
|
||||
The order_id or order object to cancel.
|
||||
"""
|
||||
|
||||
def continuous_future(root_symbol_str, offset=0, roll='volume', adjustment='mul'):
|
||||
"""Create a specifier for a continuous contract.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
root_symbol_str : str
|
||||
The root symbol for the future chain.
|
||||
|
||||
offset : int, optional
|
||||
The distance from the primary contract. Default is 0.
|
||||
|
||||
roll_style : str, optional
|
||||
How rolls are determined. Default is 'volume'.
|
||||
|
||||
adjustment : str, optional
|
||||
Method for adjusting lookback prices between rolls. Options are
|
||||
'mul', 'add', and None. Default is 'mul'.
|
||||
|
||||
Returns
|
||||
-------
|
||||
continuous_future : ContinuousFuture
|
||||
The continuous future specifier.
|
||||
"""
|
||||
|
||||
def fetch_csv(url, pre_func=None, post_func=None, date_column='date', date_format=None, timezone='UTC', symbol=None, mask=True, symbol_column=None, special_params_checker=None, **kwargs):
|
||||
"""Fetch a csv from a remote url and register the data so that it is
|
||||
queryable from the ``data`` object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
url : str
|
||||
The url of the csv file to load.
|
||||
pre_func : callable[pd.DataFrame -> pd.DataFrame], optional
|
||||
A callback to allow preprocessing the raw data returned from
|
||||
fetch_csv before dates are paresed or symbols are mapped.
|
||||
post_func : callable[pd.DataFrame -> pd.DataFrame], optional
|
||||
A callback to allow postprocessing of the data after dates and
|
||||
symbols have been mapped.
|
||||
date_column : str, optional
|
||||
The name of the column in the preprocessed dataframe containing
|
||||
datetime information to map the data.
|
||||
date_format : str, optional
|
||||
The format of the dates in the ``date_column``. If not provided
|
||||
``fetch_csv`` will attempt to infer the format. For information
|
||||
about the format of this string, see :func:`pandas.read_csv`.
|
||||
timezone : tzinfo or str, optional
|
||||
The timezone for the datetime in the ``date_column``.
|
||||
symbol : str, optional
|
||||
If the data is about a new asset or index then this string will
|
||||
be the name used to identify the values in ``data``. For example,
|
||||
one may use ``fetch_csv`` to load data for VIX, then this field
|
||||
could be the string ``'VIX'``.
|
||||
mask : bool, optional
|
||||
Drop any rows which cannot be symbol mapped.
|
||||
symbol_column : str
|
||||
If the data is attaching some new attribute to each asset then this
|
||||
argument is the name of the column in the preprocessed dataframe
|
||||
containing the symbols. This will be used along with the date
|
||||
information to map the sids in the asset finder.
|
||||
**kwargs
|
||||
Forwarded to :func:`pandas.read_csv`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
csv_data_source : catalyst.sources.requests_csv.PandasRequestsCSV
|
||||
A requests source that will pull data from the url specified.
|
||||
"""
|
||||
|
||||
def future_symbol(symbol):
|
||||
"""Lookup a futures contract with a given symbol.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : str
|
||||
The symbol of the desired contract.
|
||||
|
||||
Returns
|
||||
-------
|
||||
future : Future
|
||||
The future that trades with the name ``symbol``.
|
||||
|
||||
Raises
|
||||
------
|
||||
SymbolNotFound
|
||||
Raised when no contract named 'symbol' is found.
|
||||
"""
|
||||
|
||||
def get_datetime(tz=None):
|
||||
"""
|
||||
Returns the current simulation datetime.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
tz : tzinfo or str, optional
|
||||
The timezone to return the datetime in. This defaults to utc.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dt : datetime
|
||||
The current simulation datetime converted to ``tz``.
|
||||
"""
|
||||
|
||||
def get_environment(field='platform'):
|
||||
"""Query the execution environment.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
field : {'platform', 'arena', 'data_frequency',
|
||||
'start', 'end', 'capital_base', 'platform', '*'}
|
||||
The field to query. The options have the following meanings:
|
||||
arena : str
|
||||
The arena from the simulation parameters. This will normally
|
||||
be ``'backtest'`` but some systems may use this distinguish
|
||||
live trading from backtesting.
|
||||
data_frequency : {'daily', 'minute'}
|
||||
data_frequency tells the algorithm if it is running with
|
||||
daily data or minute data.
|
||||
start : datetime
|
||||
The start date for the simulation.
|
||||
end : datetime
|
||||
The end date for the simulation.
|
||||
capital_base : float
|
||||
The starting capital for the simulation.
|
||||
platform : str
|
||||
The platform that the code is running on. By default this
|
||||
will be the string 'catalyst'. This can allow algorithms to
|
||||
know if they are running on the Quantopian platform instead.
|
||||
* : dict[str -> any]
|
||||
Returns all of the fields in a dictionary.
|
||||
|
||||
Returns
|
||||
-------
|
||||
val : any
|
||||
The value for the field queried. See above for more information.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
Raised when ``field`` is not a valid option.
|
||||
"""
|
||||
|
||||
def get_order(order_id):
|
||||
"""Lookup an order based on the order id returned from one of the
|
||||
order functions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order_id : str
|
||||
The unique identifier for the order.
|
||||
|
||||
Returns
|
||||
-------
|
||||
order : Order
|
||||
The order object.
|
||||
"""
|
||||
|
||||
def history(bar_count, frequency, field, ffill=True):
|
||||
"""DEPRECATED: use ``data.history`` instead.
|
||||
"""
|
||||
|
||||
def order(asset, amount, limit_price=None, stop_price=None, style=None):
|
||||
"""Place an order.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : Asset
|
||||
The asset that this order is for.
|
||||
amount : int
|
||||
The amount of shares to order. If ``amount`` is positive, this is
|
||||
the number of shares to buy or cover. If ``amount`` is negative,
|
||||
this is the number of shares to sell or short.
|
||||
limit_price : float, optional
|
||||
The limit price for the order.
|
||||
stop_price : float, optional
|
||||
The stop price for the order.
|
||||
style : ExecutionStyle, optional
|
||||
The execution style for the order.
|
||||
|
||||
Returns
|
||||
-------
|
||||
order_id : str or None
|
||||
The unique identifier for this order, or None if no order was
|
||||
placed.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The ``limit_price`` and ``stop_price`` arguments provide shorthands for
|
||||
passing common execution styles. Passing ``limit_price=N`` is
|
||||
equivalent to ``style=LimitOrder(N)``. Similarly, passing
|
||||
``stop_price=M`` is equivalent to ``style=StopOrder(M)``, and passing
|
||||
``limit_price=N`` and ``stop_price=M`` is equivalent to
|
||||
``style=StopLimitOrder(N, M)``. It is an error to pass both a ``style``
|
||||
and ``limit_price`` or ``stop_price``.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`catalyst.finance.execution.ExecutionStyle`
|
||||
:func:`catalyst.api.order_value`
|
||||
:func:`catalyst.api.order_percent`
|
||||
"""
|
||||
|
||||
def order_percent(asset, percent, limit_price=None, stop_price=None, style=None):
|
||||
"""Place an order in the specified asset corresponding to the given
|
||||
percent of the current portfolio value.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : Asset
|
||||
The asset that this order is for.
|
||||
percent : float
|
||||
The percentage of the porfolio value to allocate to ``asset``.
|
||||
This is specified as a decimal, for example: 0.50 means 50%.
|
||||
limit_price : float, optional
|
||||
The limit price for the order.
|
||||
stop_price : float, optional
|
||||
The stop price for the order.
|
||||
style : ExecutionStyle
|
||||
The execution style for the order.
|
||||
|
||||
Returns
|
||||
-------
|
||||
order_id : str
|
||||
The unique identifier for this order.
|
||||
|
||||
Notes
|
||||
-----
|
||||
See :func:`catalyst.api.order` for more information about
|
||||
``limit_price``, ``stop_price``, and ``style``
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`catalyst.finance.execution.ExecutionStyle`
|
||||
:func:`catalyst.api.order`
|
||||
:func:`catalyst.api.order_value`
|
||||
"""
|
||||
|
||||
def order_target(asset, target, limit_price=None, stop_price=None, style=None):
|
||||
"""Place an order to adjust a position to a target number of shares. If
|
||||
the position doesn't already exist, this is equivalent to placing a new
|
||||
order. If the position does exist, this is equivalent to placing an
|
||||
order for the difference between the target number of shares and the
|
||||
current number of shares.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : Asset
|
||||
The asset that this order is for.
|
||||
target : int
|
||||
The desired number of shares of ``asset``.
|
||||
limit_price : float, optional
|
||||
The limit price for the order.
|
||||
stop_price : float, optional
|
||||
The stop price for the order.
|
||||
style : ExecutionStyle
|
||||
The execution style for the order.
|
||||
|
||||
Returns
|
||||
-------
|
||||
order_id : str
|
||||
The unique identifier for this order.
|
||||
|
||||
|
||||
Notes
|
||||
-----
|
||||
``order_target`` does not take into account any open orders. For
|
||||
example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
order_target(sid(0), 10)
|
||||
order_target(sid(0), 10)
|
||||
|
||||
This code will result in 20 shares of ``sid(0)`` because the first
|
||||
call to ``order_target`` will not have been filled when the second
|
||||
``order_target`` call is made.
|
||||
|
||||
See :func:`catalyst.api.order` for more information about
|
||||
``limit_price``, ``stop_price``, and ``style``
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`catalyst.finance.execution.ExecutionStyle`
|
||||
:func:`catalyst.api.order`
|
||||
:func:`catalyst.api.order_target_percent`
|
||||
:func:`catalyst.api.order_target_value`
|
||||
"""
|
||||
|
||||
def order_target_percent(asset, target, limit_price=None, stop_price=None, style=None):
|
||||
"""Place an order to adjust a position to a target percent of the
|
||||
current portfolio value. If the position doesn't already exist, this is
|
||||
equivalent to placing a new order. If the position does exist, this is
|
||||
equivalent to placing an order for the difference between the target
|
||||
percent and the current percent.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : Asset
|
||||
The asset that this order is for.
|
||||
target : float
|
||||
The desired percentage of the porfolio value to allocate to
|
||||
``asset``. This is specified as a decimal, for example:
|
||||
0.50 means 50%.
|
||||
limit_price : float, optional
|
||||
The limit price for the order.
|
||||
stop_price : float, optional
|
||||
The stop price for the order.
|
||||
style : ExecutionStyle
|
||||
The execution style for the order.
|
||||
|
||||
Returns
|
||||
-------
|
||||
order_id : str
|
||||
The unique identifier for this order.
|
||||
|
||||
Notes
|
||||
-----
|
||||
``order_target_value`` does not take into account any open orders. For
|
||||
example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
order_target_percent(sid(0), 10)
|
||||
order_target_percent(sid(0), 10)
|
||||
|
||||
This code will result in 20% of the portfolio being allocated to sid(0)
|
||||
because the first call to ``order_target_percent`` will not have been
|
||||
filled when the second ``order_target_percent`` call is made.
|
||||
|
||||
See :func:`catalyst.api.order` for more information about
|
||||
``limit_price``, ``stop_price``, and ``style``
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`catalyst.finance.execution.ExecutionStyle`
|
||||
:func:`catalyst.api.order`
|
||||
:func:`catalyst.api.order_target`
|
||||
:func:`catalyst.api.order_target_value`
|
||||
"""
|
||||
|
||||
def order_target_value(asset, target, limit_price=None, stop_price=None, style=None):
|
||||
"""Place an order to adjust a position to a target value. If
|
||||
the position doesn't already exist, this is equivalent to placing a new
|
||||
order. If the position does exist, this is equivalent to placing an
|
||||
order for the difference between the target value and the
|
||||
current value.
|
||||
If the Asset being ordered is a Future, the 'target value' calculated
|
||||
is actually the target exposure, as Futures have no 'value'.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : Asset
|
||||
The asset that this order is for.
|
||||
target : float
|
||||
The desired total value of ``asset``.
|
||||
limit_price : float, optional
|
||||
The limit price for the order.
|
||||
stop_price : float, optional
|
||||
The stop price for the order.
|
||||
style : ExecutionStyle
|
||||
The execution style for the order.
|
||||
|
||||
Returns
|
||||
-------
|
||||
order_id : str
|
||||
The unique identifier for this order.
|
||||
|
||||
Notes
|
||||
-----
|
||||
``order_target_value`` does not take into account any open orders. For
|
||||
example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
order_target_value(sid(0), 10)
|
||||
order_target_value(sid(0), 10)
|
||||
|
||||
This code will result in 20 dollars of ``sid(0)`` because the first
|
||||
call to ``order_target_value`` will not have been filled when the
|
||||
second ``order_target_value`` call is made.
|
||||
|
||||
See :func:`catalyst.api.order` for more information about
|
||||
``limit_price``, ``stop_price``, and ``style``
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`catalyst.finance.execution.ExecutionStyle`
|
||||
:func:`catalyst.api.order`
|
||||
:func:`catalyst.api.order_target`
|
||||
:func:`catalyst.api.order_target_percent`
|
||||
"""
|
||||
|
||||
def order_value(asset, value, limit_price=None, stop_price=None, style=None):
|
||||
"""Place an order by desired value rather than desired number of
|
||||
shares.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : Asset
|
||||
The asset that this order is for.
|
||||
value : float
|
||||
If the requested asset exists, the requested value is
|
||||
divided by its price to imply the number of shares to transact.
|
||||
If the Asset being ordered is a Future, the 'value' calculated
|
||||
is actually the exposure, as Futures have no 'value'.
|
||||
|
||||
value > 0 :: Buy/Cover
|
||||
value < 0 :: Sell/Short
|
||||
limit_price : float, optional
|
||||
The limit price for the order.
|
||||
stop_price : float, optional
|
||||
The stop price for the order.
|
||||
style : ExecutionStyle
|
||||
The execution style for the order.
|
||||
|
||||
Returns
|
||||
-------
|
||||
order_id : str
|
||||
The unique identifier for this order.
|
||||
|
||||
Notes
|
||||
-----
|
||||
See :func:`catalyst.api.order` for more information about
|
||||
``limit_price``, ``stop_price``, and ``style``
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`catalyst.finance.execution.ExecutionStyle`
|
||||
:func:`catalyst.api.order`
|
||||
:func:`catalyst.api.order_percent`
|
||||
"""
|
||||
|
||||
def pipeline_output(name):
|
||||
"""Get the results of the pipeline that was attached with the name:
|
||||
``name``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of the pipeline for which results are requested.
|
||||
|
||||
Returns
|
||||
-------
|
||||
results : pd.DataFrame
|
||||
DataFrame containing the results of the requested pipeline for
|
||||
the current simulation date.
|
||||
|
||||
Raises
|
||||
------
|
||||
NoSuchPipeline
|
||||
Raised when no pipeline with the name `name` has been registered.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:func:`catalyst.api.attach_pipeline`
|
||||
:meth:`catalyst.pipeline.engine.PipelineEngine.run_pipeline`
|
||||
"""
|
||||
|
||||
def record(*args, **kwargs):
|
||||
"""Track and record values each day.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
**kwargs
|
||||
The names and values to record.
|
||||
|
||||
Notes
|
||||
-----
|
||||
These values will appear in the performance packets and the performance
|
||||
dataframe passed to ``analyze`` and returned from
|
||||
:func:`~catalyst.run_algorithm`.
|
||||
"""
|
||||
|
||||
def schedule_function(func, date_rule=None, time_rule=None, half_days=True, calendar=None):
|
||||
"""Schedules a function to be called according to some timed rules.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
func : callable[(context, data) -> None]
|
||||
The function to execute when the rule is triggered.
|
||||
date_rule : EventRule, optional
|
||||
The rule for the dates to execute this function.
|
||||
time_rule : EventRule, optional
|
||||
The rule for the times to execute this function.
|
||||
half_days : bool, optional
|
||||
Should this rule fire on half days?
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`catalyst.api.date_rules`
|
||||
:class:`catalyst.api.time_rules`
|
||||
"""
|
||||
|
||||
def set_asset_restrictions(restrictions, on_error='fail'):
|
||||
"""Set a restriction on which assets can be ordered.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
restricted_list : Restrictions
|
||||
An object providing information about restricted assets.
|
||||
|
||||
See Also
|
||||
--------
|
||||
catalyst.finance.asset_restrictions.Restrictions
|
||||
"""
|
||||
|
||||
def set_benchmark(benchmark):
|
||||
"""Set the benchmark asset.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
benchmark : Asset
|
||||
The asset to set as the new benchmark.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Any dividends payed out for that new benchmark asset will be
|
||||
automatically reinvested.
|
||||
"""
|
||||
|
||||
def set_cancel_policy(cancel_policy):
|
||||
"""Sets the order cancellation policy for the simulation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cancel_policy : CancelPolicy
|
||||
The cancellation policy to use.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`catalyst.api.EODCancel`
|
||||
:class:`catalyst.api.NeverCancel`
|
||||
"""
|
||||
|
||||
def set_commission(commission):
|
||||
"""Sets the commission model for the simulation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
commission : CommissionModel
|
||||
The commission model to use.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`catalyst.finance.commission.PerShare`
|
||||
:class:`catalyst.finance.commission.PerTrade`
|
||||
:class:`catalyst.finance.commission.PerDollar`
|
||||
"""
|
||||
|
||||
def set_do_not_order_list(restricted_list, on_error='fail'):
|
||||
"""Set a restriction on which assets can be ordered.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
restricted_list : container[Asset], SecurityList
|
||||
The assets that cannot be ordered.
|
||||
"""
|
||||
|
||||
def set_long_only(on_error='fail'):
|
||||
"""Set a rule specifying that this algorithm cannot take short
|
||||
positions.
|
||||
"""
|
||||
|
||||
def set_max_leverage(max_leverage):
|
||||
"""Set a limit on the maximum leverage of the algorithm.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
max_leverage : float
|
||||
The maximum leverage for the algorithm. If not provided there will
|
||||
be no maximum.
|
||||
"""
|
||||
|
||||
def set_max_order_count(max_count, on_error='fail'):
|
||||
"""Set a limit on the number of orders that can be placed in a single
|
||||
day.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
max_count : int
|
||||
The maximum number of orders that can be placed on any single day.
|
||||
"""
|
||||
|
||||
def set_max_order_size(asset=None, max_shares=None, max_notional=None, on_error='fail'):
|
||||
"""Set a limit on the number of shares and/or dollar value of any single
|
||||
order placed for sid. Limits are treated as absolute values and are
|
||||
enforced at the time that the algo attempts to place an order for sid.
|
||||
|
||||
If an algorithm attempts to place an order that would result in
|
||||
exceeding one of these limits, raise a TradingControlException.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : Asset, optional
|
||||
If provided, this sets the guard only on positions in the given
|
||||
asset.
|
||||
max_shares : int, optional
|
||||
The maximum number of shares that can be ordered at one time.
|
||||
max_notional : float, optional
|
||||
The maximum value that can be ordered at one time.
|
||||
"""
|
||||
|
||||
def set_max_position_size(asset=None, max_shares=None, max_notional=None, on_error='fail'):
|
||||
"""Set a limit on the number of shares and/or dollar value held for the
|
||||
given sid. Limits are treated as absolute values and are enforced at
|
||||
the time that the algo attempts to place an order for sid. This means
|
||||
that it's possible to end up with more than the max number of shares
|
||||
due to splits/dividends, and more than the max notional due to price
|
||||
improvement.
|
||||
|
||||
If an algorithm attempts to place an order that would result in
|
||||
increasing the absolute value of shares/dollar value exceeding one of
|
||||
these limits, raise a TradingControlException.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : Asset, optional
|
||||
If provided, this sets the guard only on positions in the given
|
||||
asset.
|
||||
max_shares : int, optional
|
||||
The maximum number of shares to hold for an asset.
|
||||
max_notional : float, optional
|
||||
The maximum value to hold for an asset.
|
||||
"""
|
||||
|
||||
def set_slippage(slippage):
|
||||
"""Set the slippage model for the simulation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
slippage : SlippageModel
|
||||
The slippage model to use.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`catalyst.finance.slippage.SlippageModel`
|
||||
"""
|
||||
|
||||
def set_symbol_lookup_date(dt):
|
||||
"""Set the date for which symbols will be resolved to their assets
|
||||
(symbols may map to different firms or underlying assets at
|
||||
different times)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dt : datetime
|
||||
The new symbol lookup date.
|
||||
"""
|
||||
|
||||
def sid(sid):
|
||||
"""Lookup an Asset by its unique asset identifier.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sid : int
|
||||
The unique integer that identifies an asset.
|
||||
|
||||
Returns
|
||||
-------
|
||||
asset : Asset
|
||||
The asset with the given ``sid``.
|
||||
|
||||
Raises
|
||||
------
|
||||
SidsNotFound
|
||||
When a requested ``sid`` does not map to any asset.
|
||||
"""
|
||||
|
||||
def symbol(symbol_str):
|
||||
"""Lookup an Equity by its ticker symbol.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol_str : str
|
||||
The ticker symbol for the equity to lookup.
|
||||
|
||||
Returns
|
||||
-------
|
||||
equity : Equity
|
||||
The equity that held the ticker symbol on the current
|
||||
symbol lookup date.
|
||||
|
||||
Raises
|
||||
------
|
||||
SymbolNotFound
|
||||
Raised when the symbols was not held on the current lookup date.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:func:`catalyst.api.set_symbol_lookup_date`
|
||||
"""
|
||||
|
||||
def symbols(*args):
|
||||
"""Lookup multuple Equities as a list.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
*args : iterable[str]
|
||||
The ticker symbols to lookup.
|
||||
|
||||
Returns
|
||||
-------
|
||||
equities : list[Equity]
|
||||
The equities that held the given ticker symbols on the current
|
||||
symbol lookup date.
|
||||
|
||||
Raises
|
||||
------
|
||||
SymbolNotFound
|
||||
Raised when one of the symbols was not held on the current
|
||||
lookup date.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:func:`catalyst.api.set_symbol_lookup_date`
|
||||
"""
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#
|
||||
# Copyright 2015 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from ._assets import (
|
||||
Asset,
|
||||
Equity,
|
||||
Future,
|
||||
make_asset_array,
|
||||
CACHE_FILE_TEMPLATE
|
||||
)
|
||||
from .assets import (
|
||||
AssetFinder,
|
||||
AssetConvertible,
|
||||
PricingDataAssociable,
|
||||
)
|
||||
from .asset_db_schema import ASSET_DB_VERSION
|
||||
from .asset_writer import AssetDBWriter
|
||||
|
||||
__all__ = [
|
||||
'ASSET_DB_VERSION',
|
||||
'Asset',
|
||||
'AssetDBWriter',
|
||||
'Equity',
|
||||
'Future',
|
||||
'AssetFinder',
|
||||
'AssetConvertible',
|
||||
'PricingDataAssociable',
|
||||
'make_asset_array',
|
||||
'CACHE_FILE_TEMPLATE'
|
||||
]
|
||||
@@ -1,578 +0,0 @@
|
||||
# cython: embedsignature=True
|
||||
#
|
||||
# Copyright 2015 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Cythonized Asset object.
|
||||
"""
|
||||
cimport cython
|
||||
from cpython.number cimport PyNumber_Index
|
||||
from cpython.object cimport (
|
||||
Py_EQ,
|
||||
Py_NE,
|
||||
Py_GE,
|
||||
Py_LE,
|
||||
Py_GT,
|
||||
Py_LT,
|
||||
)
|
||||
from cpython cimport bool
|
||||
|
||||
import pandas as pd
|
||||
from datetime import timedelta
|
||||
import numpy as np
|
||||
from numpy cimport int64_t
|
||||
import warnings
|
||||
cimport numpy as np
|
||||
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
from catalyst.exchange.exchange_errors import InvalidSymbolError, SidHashError
|
||||
|
||||
# IMPORTANT NOTE: You must change this template if you change
|
||||
# Asset.__reduce__, or else we'll attempt to unpickle an old version of this
|
||||
# class
|
||||
CACHE_FILE_TEMPLATE = '/tmp/.%s-%s.v7.cache'
|
||||
|
||||
cdef class Asset:
|
||||
cdef readonly int sid
|
||||
# Cached hash of self.sid
|
||||
cdef int sid_hash
|
||||
|
||||
cdef readonly object symbol
|
||||
cdef readonly object asset_name
|
||||
|
||||
cdef readonly object start_date
|
||||
cdef readonly object end_date
|
||||
cdef public object first_traded
|
||||
cdef readonly object auto_close_date
|
||||
|
||||
cdef readonly object exchange
|
||||
cdef readonly object exchange_full
|
||||
cdef readonly object min_trade_size
|
||||
|
||||
_kwargnames = frozenset({
|
||||
'sid',
|
||||
'symbol',
|
||||
'asset_name',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'first_traded',
|
||||
'auto_close_date',
|
||||
'exchange',
|
||||
'exchange_full',
|
||||
'min_trade_size',
|
||||
})
|
||||
|
||||
def __init__(self,
|
||||
int sid, # sid is required
|
||||
object exchange, # exchange is required
|
||||
object symbol="",
|
||||
object asset_name="",
|
||||
object start_date=None,
|
||||
object end_date=None,
|
||||
object first_traded=None,
|
||||
object auto_close_date=None,
|
||||
object exchange_full=None,
|
||||
object min_trade_size=None):
|
||||
|
||||
self.sid = sid
|
||||
self.sid_hash = hash(sid)
|
||||
self.symbol = symbol
|
||||
self.asset_name = asset_name
|
||||
self.exchange = exchange
|
||||
self.exchange_full = (exchange_full if exchange_full is not None
|
||||
else exchange)
|
||||
self.start_date = start_date
|
||||
self.end_date = end_date
|
||||
self.first_traded = first_traded
|
||||
self.auto_close_date = auto_close_date
|
||||
self.min_trade_size = min_trade_size
|
||||
|
||||
def __int__(self):
|
||||
return self.sid
|
||||
|
||||
def __index__(self):
|
||||
return self.sid
|
||||
|
||||
def __hash__(self):
|
||||
return self.sid_hash
|
||||
|
||||
def __richcmp__(x, y, int op):
|
||||
"""
|
||||
Cython rich comparison method. This is used in place of various
|
||||
equality checkers in pure python.
|
||||
"""
|
||||
cdef int x_as_int, y_as_int
|
||||
|
||||
try:
|
||||
x_as_int = PyNumber_Index(x)
|
||||
except (TypeError, OverflowError):
|
||||
return NotImplemented
|
||||
|
||||
try:
|
||||
y_as_int = PyNumber_Index(y)
|
||||
except (TypeError, OverflowError):
|
||||
return NotImplemented
|
||||
|
||||
compared = x_as_int - y_as_int
|
||||
|
||||
# Handle == and != first because they're significantly more common
|
||||
# operations.
|
||||
if op == Py_EQ:
|
||||
return compared == 0
|
||||
elif op == Py_NE:
|
||||
return compared != 0
|
||||
elif op == Py_LT:
|
||||
return compared < 0
|
||||
elif op == Py_LE:
|
||||
return compared <= 0
|
||||
elif op == Py_GT:
|
||||
return compared > 0
|
||||
elif op == Py_GE:
|
||||
return compared >= 0
|
||||
else:
|
||||
raise AssertionError('%d is not an operator' % op)
|
||||
|
||||
def __str__(self):
|
||||
if self.symbol:
|
||||
return '%s(%d [%s])' % (type(self).__name__, self.sid, self.symbol)
|
||||
else:
|
||||
return '%s(%d)' % (type(self).__name__, self.sid)
|
||||
|
||||
def __repr__(self):
|
||||
attrs = ('symbol', 'asset_name', 'exchange',
|
||||
'start_date', 'end_date', 'first_traded', 'auto_close_date',
|
||||
'min_trade_size')
|
||||
tuples = ((attr, repr(getattr(self, attr, None)))
|
||||
for attr in attrs)
|
||||
strings = ('%s=%s' % (t[0], t[1]) for t in tuples)
|
||||
params = ', '.join(strings)
|
||||
return 'Asset(%d, %s)' % (self.sid, params)
|
||||
|
||||
cpdef __reduce__(self):
|
||||
"""
|
||||
Function used by pickle to determine how to serialize/deserialize this
|
||||
class. Should return a tuple whose first element is self.__class__,
|
||||
and whose second element is a tuple of all the attributes that should
|
||||
be serialized/deserialized during pickling.
|
||||
"""
|
||||
return (self.__class__, (self.sid,
|
||||
self.exchange,
|
||||
self.symbol,
|
||||
self.asset_name,
|
||||
self.start_date,
|
||||
self.end_date,
|
||||
self.first_traded,
|
||||
self.auto_close_date,
|
||||
self.exchange_full,
|
||||
self.min_trade_size))
|
||||
|
||||
cpdef to_dict(self):
|
||||
"""
|
||||
Convert to a python dict.
|
||||
"""
|
||||
return {
|
||||
'sid': self.sid,
|
||||
'symbol': self.symbol,
|
||||
'asset_name': self.asset_name,
|
||||
'start_date': self.start_date,
|
||||
'end_date': self.end_date,
|
||||
'first_traded': self.first_traded,
|
||||
'auto_close_date': self.auto_close_date,
|
||||
'exchange': self.exchange,
|
||||
'exchange_full': self.exchange_full,
|
||||
'min_trade_size': self.min_trade_size
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dict_):
|
||||
"""
|
||||
Build an Asset instance from a dict.
|
||||
"""
|
||||
return cls(**dict_)
|
||||
|
||||
def is_alive_for_session(self, session_label):
|
||||
"""
|
||||
Returns whether the asset is alive at the given dt.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
session_label: pd.Timestamp
|
||||
The desired session label to check. (midnight UTC)
|
||||
|
||||
Returns
|
||||
-------
|
||||
boolean: whether the asset is alive at the given dt.
|
||||
"""
|
||||
cdef int64_t ref_start
|
||||
cdef int64_t ref_end
|
||||
|
||||
ref_start = self.start_date.value
|
||||
ref_end = self.end_date.value
|
||||
|
||||
return ref_start <= session_label.value <= ref_end
|
||||
|
||||
def is_exchange_open(self, dt_minute):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
dt_minute: pd.Timestamp (UTC, tz-aware)
|
||||
The minute to check.
|
||||
|
||||
Returns
|
||||
-------
|
||||
boolean: whether the asset's exchange is open at the given minute.
|
||||
"""
|
||||
calendar = get_calendar(self.exchange)
|
||||
return calendar.is_open_on_minute(dt_minute)
|
||||
|
||||
cdef class Equity(Asset):
|
||||
def __repr__(self):
|
||||
attrs = ('symbol', 'asset_name', 'exchange',
|
||||
'start_date', 'end_date', 'first_traded', 'auto_close_date',
|
||||
'exchange_full', 'min_trade_size')
|
||||
tuples = ((attr, repr(getattr(self, attr, None)))
|
||||
for attr in attrs)
|
||||
strings = ('%s=%s' % (t[0], t[1]) for t in tuples)
|
||||
params = ', '.join(strings)
|
||||
return 'Equity(%d, %s)' % (self.sid, params)
|
||||
|
||||
property security_start_date:
|
||||
"""
|
||||
DEPRECATION: This property should be deprecated and is only present for
|
||||
backwards compatibility
|
||||
"""
|
||||
def __get__(self):
|
||||
warnings.warn("The security_start_date property will soon be "
|
||||
"retired. Please use the start_date property instead.",
|
||||
DeprecationWarning)
|
||||
return self.start_date
|
||||
|
||||
property security_end_date:
|
||||
"""
|
||||
DEPRECATION: This property should be deprecated and is only present for
|
||||
backwards compatibility
|
||||
"""
|
||||
def __get__(self):
|
||||
warnings.warn("The security_end_date property will soon be "
|
||||
"retired. Please use the end_date property instead.",
|
||||
DeprecationWarning)
|
||||
return self.end_date
|
||||
|
||||
property security_name:
|
||||
"""
|
||||
DEPRECATION: This property should be deprecated and is only present for
|
||||
backwards compatibility
|
||||
"""
|
||||
def __get__(self):
|
||||
warnings.warn("The security_name property will soon be "
|
||||
"retired. Please use the asset_name property instead.",
|
||||
DeprecationWarning)
|
||||
return self.asset_name
|
||||
|
||||
cdef class Future(Asset):
|
||||
cdef readonly object root_symbol
|
||||
cdef readonly object notice_date
|
||||
cdef readonly object expiration_date
|
||||
cdef readonly object tick_size
|
||||
cdef readonly float multiplier
|
||||
|
||||
_kwargnames = frozenset({
|
||||
'sid',
|
||||
'symbol',
|
||||
'root_symbol',
|
||||
'asset_name',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'notice_date',
|
||||
'expiration_date',
|
||||
'auto_close_date',
|
||||
'first_traded',
|
||||
'exchange',
|
||||
'tick_size',
|
||||
'multiplier',
|
||||
'exchange_full',
|
||||
})
|
||||
|
||||
def __init__(self,
|
||||
int sid, # sid is required
|
||||
object exchange, # exchange is required
|
||||
object symbol="",
|
||||
object root_symbol="",
|
||||
object asset_name="",
|
||||
object start_date=None,
|
||||
object end_date=None,
|
||||
object notice_date=None,
|
||||
object expiration_date=None,
|
||||
object auto_close_date=None,
|
||||
object first_traded=None,
|
||||
object tick_size="",
|
||||
float multiplier=1.0,
|
||||
object exchange_full=None):
|
||||
|
||||
super().__init__(
|
||||
sid,
|
||||
exchange,
|
||||
symbol=symbol,
|
||||
asset_name=asset_name,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
first_traded=first_traded,
|
||||
auto_close_date=auto_close_date,
|
||||
exchange_full=exchange_full,
|
||||
)
|
||||
self.root_symbol = root_symbol
|
||||
self.notice_date = notice_date
|
||||
self.expiration_date = expiration_date
|
||||
self.tick_size = tick_size
|
||||
self.multiplier = multiplier
|
||||
|
||||
if auto_close_date is None:
|
||||
if notice_date is None:
|
||||
self.auto_close_date = expiration_date
|
||||
elif expiration_date is None:
|
||||
self.auto_close_date = notice_date
|
||||
else:
|
||||
self.auto_close_date = min(notice_date, expiration_date)
|
||||
|
||||
def __repr__(self):
|
||||
attrs = ('symbol', 'root_symbol', 'asset_name', 'exchange',
|
||||
'start_date', 'end_date', 'first_traded', 'notice_date',
|
||||
'expiration_date', 'auto_close_date', 'tick_size',
|
||||
'multiplier', 'exchange_full')
|
||||
tuples = ((attr, repr(getattr(self, attr, None)))
|
||||
for attr in attrs)
|
||||
strings = ('%s=%s' % (t[0], t[1]) for t in tuples)
|
||||
params = ', '.join(strings)
|
||||
return 'Future(%d, %s)' % (self.sid, params)
|
||||
|
||||
cpdef __reduce__(self):
|
||||
"""
|
||||
Function used by pickle to determine how to serialize/deserialize this
|
||||
class. Should return a tuple whose first element is self.__class__,
|
||||
and whose second element is a tuple of all the attributes that should
|
||||
be serialized/deserialized during pickling.
|
||||
"""
|
||||
return (self.__class__, (self.sid,
|
||||
self.exchange,
|
||||
self.symbol,
|
||||
self.root_symbol,
|
||||
self.asset_name,
|
||||
self.start_date,
|
||||
self.end_date,
|
||||
self.notice_date,
|
||||
self.expiration_date,
|
||||
self.auto_close_date,
|
||||
self.first_traded,
|
||||
self.tick_size,
|
||||
self.multiplier,
|
||||
self.exchange_full))
|
||||
|
||||
cpdef to_dict(self):
|
||||
"""
|
||||
Convert to a python dict.
|
||||
"""
|
||||
super_dict = super(Future, self).to_dict()
|
||||
super_dict['root_symbol'] = self.root_symbol
|
||||
super_dict['notice_date'] = self.notice_date
|
||||
super_dict['expiration_date'] = self.expiration_date
|
||||
super_dict['tick_size'] = self.tick_size
|
||||
super_dict['multiplier'] = self.multiplier
|
||||
return super_dict
|
||||
|
||||
cdef class TradingPair(Asset):
|
||||
cdef readonly float leverage
|
||||
cdef readonly object market_currency
|
||||
cdef readonly object base_currency
|
||||
cdef readonly object end_daily
|
||||
cdef readonly object end_minute
|
||||
cdef readonly object exchange_symbol
|
||||
|
||||
_kwargnames = frozenset({
|
||||
'sid',
|
||||
'symbol',
|
||||
'asset_name',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'first_traded',
|
||||
'auto_close_date',
|
||||
'exchange',
|
||||
'exchange_full',
|
||||
'leverage',
|
||||
'market_currency',
|
||||
'base_currency',
|
||||
'end_daily',
|
||||
'end_minute',
|
||||
'exchange_symbol',
|
||||
'min_trade_size'
|
||||
})
|
||||
def __init__(self,
|
||||
object symbol,
|
||||
object exchange,
|
||||
object start_date=None,
|
||||
object asset_name=None,
|
||||
int sid=0,
|
||||
float leverage=1.0,
|
||||
object end_daily=None,
|
||||
object end_minute=None,
|
||||
object end_date=None,
|
||||
object exchange_symbol=None,
|
||||
object first_traded=None,
|
||||
object auto_close_date=None,
|
||||
object exchange_full=None,
|
||||
object min_trade_size=None):
|
||||
"""
|
||||
Replicates the Asset constructor with some built-in conventions
|
||||
and a new 'leverage' attribute.
|
||||
|
||||
Symbol
|
||||
------
|
||||
Catalyst defines its own set of "universal" symbols to reference
|
||||
trading pairs across exchanges. This is required because exchanges
|
||||
are not adhering to a universal symbolism. For example, Bitfinex
|
||||
uses the BTC symbol for Bitcon while Kraken uses XBT. In addition,
|
||||
pairs are sometimes presented differently. For example, Bitfinex
|
||||
puts the market currency before the base currency without a
|
||||
separator, Bittrex puts the base currency first and uses a dash
|
||||
seperator.
|
||||
|
||||
Here is the Catalyst convention: [Market Currency]_[Base Currency]
|
||||
For example: btc_usd, eth_btc, neo_eth, ltc_eur.
|
||||
|
||||
The symbol for each currency (e.g. btc, eth, ltc) is generally
|
||||
aligned with the Bittrex exchange.
|
||||
|
||||
Sid
|
||||
---
|
||||
The sid of each asset is calculated based on a numeric hash of the
|
||||
universal symbol. This simple approach avoids maintaining a mapping
|
||||
of sids.
|
||||
|
||||
Leverage
|
||||
--------
|
||||
In contrast with equities, crypto exchanges generally assign
|
||||
leverage values to specific trading pairs. Pairs with the
|
||||
highest volume and market cap generally benefit from high leverage.
|
||||
New currencies from ICO generally cannot be leveraged.
|
||||
|
||||
The leverage value is either None or and integer.
|
||||
|
||||
Leverage allows you to open a larger position with a smaller amount
|
||||
of funds. For example, if you open a $5,000 position in BTC/USD
|
||||
with 5:1 leverage, only one-fifth of this amount, or $1000, will be
|
||||
tied to the position from your balance. Your remaining balance will
|
||||
be available for opening more positions. If you open this same
|
||||
position with 2:1 leverage, $2,500 of your balance will be tied to
|
||||
the position. If you open with 1:1 leverage, $5,000 of your balance
|
||||
will be tied to the position.
|
||||
|
||||
:param symbol:
|
||||
:param exchange:
|
||||
:param start_date:
|
||||
:param asset_name:
|
||||
:param sid:
|
||||
:param leverage:
|
||||
:param end_daily
|
||||
:param end_minute
|
||||
:param end_date:
|
||||
:param exchange_symbol:
|
||||
:param first_traded:
|
||||
:param auto_close_date:
|
||||
:param exchange_full:
|
||||
:param min_trade_size:
|
||||
"""
|
||||
|
||||
symbol = symbol.lower()
|
||||
try:
|
||||
self.market_currency, self.base_currency = symbol.split('_')
|
||||
except Exception as e:
|
||||
raise InvalidSymbolError(symbol=symbol, error=e)
|
||||
|
||||
if sid == 0 or sid is None:
|
||||
try:
|
||||
sid = abs(hash(symbol)) % (10 ** 4)
|
||||
except Exception as e:
|
||||
raise SidHashError(symbol=symbol)
|
||||
|
||||
if asset_name is None:
|
||||
asset_name = ' / '.join(symbol.split('_')).upper()
|
||||
|
||||
if start_date is None:
|
||||
start_date = pd.Timestamp.utcnow()
|
||||
|
||||
if end_date is None:
|
||||
end_date = pd.Timestamp.utcnow() + timedelta(days=365)
|
||||
|
||||
super().__init__(
|
||||
sid,
|
||||
exchange,
|
||||
symbol=symbol,
|
||||
asset_name=asset_name,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
first_traded=first_traded,
|
||||
auto_close_date=auto_close_date,
|
||||
exchange_full=exchange_full,
|
||||
min_trade_size=min_trade_size
|
||||
)
|
||||
|
||||
self.leverage = leverage
|
||||
self.end_daily = end_daily
|
||||
self.end_minute = end_minute
|
||||
self.exchange_symbol = exchange_symbol
|
||||
|
||||
def __repr__(self):
|
||||
return 'Trading Pair {symbol}({sid}) Exchange: {exchange}, ' \
|
||||
'Introduced On: {start_date}, ' \
|
||||
'Market Currency: {market_currency}, ' \
|
||||
'Base Currency: {base_currency}, ' \
|
||||
'Exchange Leverage: {leverage}, ' \
|
||||
'Minimum Trade Size: {min_trade_size} ' \
|
||||
'Last daily ingestion: {end_daily} ' \
|
||||
'Last minutely ingestion: {end_minute}'.format(
|
||||
symbol=self.symbol,
|
||||
sid=self.sid,
|
||||
exchange=self.exchange,
|
||||
start_date=self.start_date,
|
||||
market_currency=self.market_currency,
|
||||
base_currency=self.base_currency,
|
||||
leverage=self.leverage,
|
||||
min_trade_size=self.min_trade_size,
|
||||
end_daily=self.end_daily,
|
||||
end_minute=self.end_minute
|
||||
)
|
||||
|
||||
cpdef __reduce__(self):
|
||||
"""
|
||||
Function used by pickle to determine how to serialize/deserialize this
|
||||
class. Should return a tuple whose first element is self.__class__,
|
||||
and whose second element is a tuple of all the attributes that should
|
||||
be serialized/deserialized during pickling.
|
||||
"""
|
||||
return (self.__class__, (self.symbol,
|
||||
self.exchange,
|
||||
self.start_date,
|
||||
self.asset_name,
|
||||
self.sid,
|
||||
self.leverage,
|
||||
self.end_date,
|
||||
self.first_traded,
|
||||
self.auto_close_date,
|
||||
self.exchange_full,
|
||||
self.min_trade_size))
|
||||
|
||||
def make_asset_array(int size, Asset asset):
|
||||
cdef np.ndarray out = np.empty([size], dtype=object)
|
||||
out.fill(asset)
|
||||
return out
|
||||
@@ -1,316 +0,0 @@
|
||||
from functools import wraps
|
||||
|
||||
from alembic.migration import MigrationContext
|
||||
from alembic.operations import Operations
|
||||
import sqlalchemy as sa
|
||||
from toolz.curried import do, operator as op
|
||||
|
||||
from catalyst.assets.asset_writer import write_version_info
|
||||
from catalyst.errors import AssetDBImpossibleDowngrade
|
||||
from catalyst.utils.preprocess import preprocess
|
||||
from catalyst.utils.sqlite_utils import coerce_string_to_eng
|
||||
|
||||
|
||||
@preprocess(engine=coerce_string_to_eng)
|
||||
def downgrade(engine, desired_version):
|
||||
"""Downgrades the assets db at the given engine to the desired version.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
engine : Engine
|
||||
An SQLAlchemy engine to the assets database.
|
||||
desired_version : int
|
||||
The desired resulting version for the assets database.
|
||||
"""
|
||||
|
||||
# Check the version of the db at the engine
|
||||
with engine.begin() as conn:
|
||||
metadata = sa.MetaData(conn)
|
||||
metadata.reflect()
|
||||
version_info_table = metadata.tables['version_info']
|
||||
starting_version = sa.select((version_info_table.c.version,)).scalar()
|
||||
|
||||
# Check for accidental upgrade
|
||||
if starting_version < desired_version:
|
||||
raise AssetDBImpossibleDowngrade(db_version=starting_version,
|
||||
desired_version=desired_version)
|
||||
|
||||
# Check if the desired version is already the db version
|
||||
if starting_version == desired_version:
|
||||
# No downgrade needed
|
||||
return
|
||||
|
||||
# Create alembic context
|
||||
ctx = MigrationContext.configure(conn)
|
||||
op = Operations(ctx)
|
||||
|
||||
# Integer keys of downgrades to run
|
||||
# E.g.: [5, 4, 3, 2] would downgrade v6 to v2
|
||||
downgrade_keys = range(desired_version, starting_version)[::-1]
|
||||
|
||||
# Disable foreign keys until all downgrades are complete
|
||||
_pragma_foreign_keys(conn, False)
|
||||
|
||||
# Execute the downgrades in order
|
||||
for downgrade_key in downgrade_keys:
|
||||
_downgrade_methods[downgrade_key](op, conn, version_info_table)
|
||||
|
||||
# Re-enable foreign keys
|
||||
_pragma_foreign_keys(conn, True)
|
||||
|
||||
|
||||
def _pragma_foreign_keys(connection, on):
|
||||
"""Sets the PRAGMA foreign_keys state of the SQLite database. Disabling
|
||||
the pragma allows for batch modification of tables with foreign keys.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
connection : Connection
|
||||
A SQLAlchemy connection to the db
|
||||
on : bool
|
||||
If true, PRAGMA foreign_keys will be set to ON. Otherwise, the PRAGMA
|
||||
foreign_keys will be set to OFF.
|
||||
"""
|
||||
connection.execute("PRAGMA foreign_keys=%s" % ("ON" if on else "OFF"))
|
||||
|
||||
|
||||
# This dict contains references to downgrade methods that can be applied to an
|
||||
# assets db. The resulting db's version is the key.
|
||||
# e.g. The method at key '0' is the downgrade method from v1 to v0
|
||||
_downgrade_methods = {}
|
||||
|
||||
|
||||
def downgrades(src):
|
||||
"""Decorator for marking that a method is a downgrade to a version to the
|
||||
previous version.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
src : int
|
||||
The version this downgrades from.
|
||||
|
||||
Returns
|
||||
-------
|
||||
decorator : callable[(callable) -> callable]
|
||||
The decorator to apply.
|
||||
"""
|
||||
def _(f):
|
||||
destination = src - 1
|
||||
|
||||
@do(op.setitem(_downgrade_methods, destination))
|
||||
@wraps(f)
|
||||
def wrapper(op, conn, version_info_table):
|
||||
conn.execute(version_info_table.delete()) # clear the version
|
||||
f(op)
|
||||
write_version_info(conn, version_info_table, destination)
|
||||
|
||||
return wrapper
|
||||
return _
|
||||
|
||||
|
||||
@downgrades(1)
|
||||
def _downgrade_v1(op):
|
||||
"""
|
||||
Downgrade assets db by removing the 'tick_size' column and renaming the
|
||||
'multiplier' column.
|
||||
"""
|
||||
# Drop indices before batch
|
||||
# This is to prevent index collision when creating the temp table
|
||||
op.drop_index('ix_futures_contracts_root_symbol')
|
||||
op.drop_index('ix_futures_contracts_symbol')
|
||||
|
||||
# Execute batch op to allow column modification in SQLite
|
||||
with op.batch_alter_table('futures_contracts') as batch_op:
|
||||
|
||||
# Rename 'multiplier'
|
||||
batch_op.alter_column(column_name='multiplier',
|
||||
new_column_name='contract_multiplier')
|
||||
|
||||
# Delete 'tick_size'
|
||||
batch_op.drop_column('tick_size')
|
||||
|
||||
# Recreate indices after batch
|
||||
op.create_index('ix_futures_contracts_root_symbol',
|
||||
table_name='futures_contracts',
|
||||
columns=['root_symbol'])
|
||||
op.create_index('ix_futures_contracts_symbol',
|
||||
table_name='futures_contracts',
|
||||
columns=['symbol'],
|
||||
unique=True)
|
||||
|
||||
|
||||
@downgrades(2)
|
||||
def _downgrade_v2(op):
|
||||
"""
|
||||
Downgrade assets db by removing the 'auto_close_date' column.
|
||||
"""
|
||||
# Drop indices before batch
|
||||
# This is to prevent index collision when creating the temp table
|
||||
op.drop_index('ix_equities_fuzzy_symbol')
|
||||
op.drop_index('ix_equities_company_symbol')
|
||||
|
||||
# Execute batch op to allow column modification in SQLite
|
||||
with op.batch_alter_table('equities') as batch_op:
|
||||
batch_op.drop_column('auto_close_date')
|
||||
|
||||
# Recreate indices after batch
|
||||
op.create_index('ix_equities_fuzzy_symbol',
|
||||
table_name='equities',
|
||||
columns=['fuzzy_symbol'])
|
||||
op.create_index('ix_equities_company_symbol',
|
||||
table_name='equities',
|
||||
columns=['company_symbol'])
|
||||
|
||||
|
||||
@downgrades(3)
|
||||
def _downgrade_v3(op):
|
||||
"""
|
||||
Downgrade assets db by adding a not null constraint on
|
||||
``equities.first_traded``
|
||||
"""
|
||||
op.create_table(
|
||||
'_new_equities',
|
||||
sa.Column(
|
||||
'sid',
|
||||
sa.Integer,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
),
|
||||
sa.Column('symbol', sa.Text),
|
||||
sa.Column('company_symbol', sa.Text),
|
||||
sa.Column('share_class_symbol', sa.Text),
|
||||
sa.Column('fuzzy_symbol', sa.Text),
|
||||
sa.Column('asset_name', sa.Text),
|
||||
sa.Column('start_date', sa.Integer, default=0, nullable=False),
|
||||
sa.Column('end_date', sa.Integer, nullable=False),
|
||||
sa.Column('first_traded', sa.Integer, nullable=False),
|
||||
sa.Column('auto_close_date', sa.Integer),
|
||||
sa.Column('exchange', sa.Text),
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
insert into _new_equities
|
||||
select * from equities
|
||||
where equities.first_traded is not null
|
||||
""",
|
||||
)
|
||||
op.drop_table('equities')
|
||||
op.rename_table('_new_equities', 'equities')
|
||||
# we need to make sure the indices have the proper names after the rename
|
||||
op.create_index(
|
||||
'ix_equities_company_symbol',
|
||||
'equities',
|
||||
['company_symbol'],
|
||||
)
|
||||
op.create_index(
|
||||
'ix_equities_fuzzy_symbol',
|
||||
'equities',
|
||||
['fuzzy_symbol'],
|
||||
)
|
||||
|
||||
|
||||
@downgrades(4)
|
||||
def _downgrade_v4(op):
|
||||
"""
|
||||
Downgrades assets db by copying the `exchange_full` column to `exchange`,
|
||||
then dropping the `exchange_full` column.
|
||||
"""
|
||||
op.drop_index('ix_equities_fuzzy_symbol')
|
||||
op.drop_index('ix_equities_company_symbol')
|
||||
|
||||
op.execute("UPDATE equities SET exchange = exchange_full")
|
||||
|
||||
with op.batch_alter_table('equities') as batch_op:
|
||||
batch_op.drop_column('exchange_full')
|
||||
|
||||
op.create_index('ix_equities_fuzzy_symbol',
|
||||
table_name='equities',
|
||||
columns=['fuzzy_symbol'])
|
||||
op.create_index('ix_equities_company_symbol',
|
||||
table_name='equities',
|
||||
columns=['company_symbol'])
|
||||
|
||||
|
||||
@downgrades(5)
|
||||
def _downgrade_v5(op):
|
||||
op.create_table(
|
||||
'_new_equities',
|
||||
sa.Column(
|
||||
'sid',
|
||||
sa.Integer,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
),
|
||||
sa.Column('symbol', sa.Text),
|
||||
sa.Column('company_symbol', sa.Text),
|
||||
sa.Column('share_class_symbol', sa.Text),
|
||||
sa.Column('fuzzy_symbol', sa.Text),
|
||||
sa.Column('asset_name', sa.Text),
|
||||
sa.Column('start_date', sa.Integer, default=0, nullable=False),
|
||||
sa.Column('end_date', sa.Integer, nullable=False),
|
||||
sa.Column('first_traded', sa.Integer),
|
||||
sa.Column('auto_close_date', sa.Integer),
|
||||
sa.Column('exchange', sa.Text),
|
||||
sa.Column('exchange_full', sa.Text)
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
insert into _new_equities
|
||||
select
|
||||
equities.sid as sid,
|
||||
sym.symbol as symbol,
|
||||
sym.company_symbol as company_symbol,
|
||||
sym.share_class_symbol as share_class_symbol,
|
||||
sym.company_symbol || sym.share_class_symbol as fuzzy_symbol,
|
||||
equities.asset_name as asset_name,
|
||||
equities.start_date as start_date,
|
||||
equities.end_date as end_date,
|
||||
equities.first_traded as first_traded,
|
||||
equities.auto_close_date as auto_close_date,
|
||||
equities.exchange as exchange,
|
||||
equities.exchange_full as exchange_full
|
||||
from
|
||||
equities
|
||||
inner join
|
||||
-- Nested select here to take the most recently held ticker
|
||||
-- for each sid. The group by with no aggregation function will
|
||||
-- take the last element in the group, so we first order by
|
||||
-- the end date ascending to ensure that the groupby takes
|
||||
-- the last ticker.
|
||||
(select
|
||||
*
|
||||
from
|
||||
(select
|
||||
*
|
||||
from
|
||||
equity_symbol_mappings
|
||||
order by
|
||||
equity_symbol_mappings.end_date asc)
|
||||
group by
|
||||
sid) sym
|
||||
on
|
||||
equities.sid == sym.sid
|
||||
""",
|
||||
)
|
||||
op.drop_table('equity_symbol_mappings')
|
||||
op.drop_table('equities')
|
||||
op.rename_table('_new_equities', 'equities')
|
||||
# we need to make sure the indicies have the proper names after the rename
|
||||
op.create_index(
|
||||
'ix_equities_company_symbol',
|
||||
'equities',
|
||||
['company_symbol'],
|
||||
)
|
||||
op.create_index(
|
||||
'ix_equities_fuzzy_symbol',
|
||||
'equities',
|
||||
['fuzzy_symbol'],
|
||||
)
|
||||
|
||||
|
||||
@downgrades(6)
|
||||
def _downgrade_v6(op):
|
||||
op.drop_table('equity_supplementary_mappings')
|
||||
@@ -1,201 +0,0 @@
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# Define a version number for the database generated by these writers
|
||||
# Increment this version number any time a change is made to the schema of the
|
||||
# assets database
|
||||
# NOTE: When upgrading this remember to add a downgrade in:
|
||||
# .asset_db_migrations
|
||||
ASSET_DB_VERSION = 6
|
||||
|
||||
# A frozenset of the names of all tables in the assets db
|
||||
# NOTE: When modifying this schema, update the ASSET_DB_VERSION value
|
||||
asset_db_table_names = frozenset({
|
||||
'asset_router',
|
||||
'equities',
|
||||
'equity_symbol_mappings',
|
||||
'equity_supplementary_mappings',
|
||||
'futures_contracts',
|
||||
'futures_exchanges',
|
||||
'futures_root_symbols',
|
||||
'version_info',
|
||||
})
|
||||
|
||||
metadata = sa.MetaData()
|
||||
|
||||
equities = sa.Table(
|
||||
'equities',
|
||||
metadata,
|
||||
sa.Column(
|
||||
'sid',
|
||||
sa.Integer,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
),
|
||||
sa.Column('asset_name', sa.Text),
|
||||
sa.Column('start_date', sa.Integer, default=0, nullable=False),
|
||||
sa.Column('end_date', sa.Integer, nullable=False),
|
||||
sa.Column('first_traded', sa.Integer),
|
||||
sa.Column('auto_close_date', sa.Integer),
|
||||
sa.Column('exchange', sa.Text),
|
||||
sa.Column('exchange_full', sa.Text),
|
||||
sa.Column('min_trade_size', sa.Float)
|
||||
)
|
||||
|
||||
equity_symbol_mappings = sa.Table(
|
||||
'equity_symbol_mappings',
|
||||
metadata,
|
||||
sa.Column(
|
||||
'id',
|
||||
sa.Integer,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
),
|
||||
sa.Column(
|
||||
'sid',
|
||||
sa.Integer,
|
||||
sa.ForeignKey(equities.c.sid),
|
||||
nullable=False,
|
||||
index=True,
|
||||
),
|
||||
sa.Column(
|
||||
'symbol',
|
||||
sa.Text,
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
'company_symbol',
|
||||
sa.Text,
|
||||
index=True,
|
||||
),
|
||||
sa.Column(
|
||||
'share_class_symbol',
|
||||
sa.Text,
|
||||
),
|
||||
sa.Column(
|
||||
'start_date',
|
||||
sa.Integer,
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
'end_date',
|
||||
sa.Integer,
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
|
||||
equity_supplementary_mappings = sa.Table(
|
||||
'equity_supplementary_mappings',
|
||||
metadata,
|
||||
sa.Column(
|
||||
'sid',
|
||||
sa.Integer,
|
||||
sa.ForeignKey(equities.c.sid),
|
||||
nullable=False,
|
||||
primary_key=True
|
||||
),
|
||||
sa.Column('field', sa.Text, nullable=False, primary_key=True),
|
||||
sa.Column('start_date', sa.Integer, nullable=False, primary_key=True),
|
||||
sa.Column('end_date', sa.Integer, nullable=False),
|
||||
sa.Column('value', sa.Text, nullable=False),
|
||||
)
|
||||
|
||||
futures_exchanges = sa.Table(
|
||||
'futures_exchanges',
|
||||
metadata,
|
||||
sa.Column(
|
||||
'exchange',
|
||||
sa.Text,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
),
|
||||
sa.Column('timezone', sa.Text),
|
||||
)
|
||||
|
||||
futures_root_symbols = sa.Table(
|
||||
'futures_root_symbols',
|
||||
metadata,
|
||||
sa.Column(
|
||||
'root_symbol',
|
||||
sa.Text,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
),
|
||||
sa.Column('root_symbol_id', sa.Integer),
|
||||
sa.Column('sector', sa.Text),
|
||||
sa.Column('description', sa.Text),
|
||||
sa.Column(
|
||||
'exchange',
|
||||
sa.Text,
|
||||
sa.ForeignKey('futures_exchanges.exchange'),
|
||||
),
|
||||
)
|
||||
|
||||
futures_contracts = sa.Table(
|
||||
'futures_contracts',
|
||||
metadata,
|
||||
sa.Column(
|
||||
'sid',
|
||||
sa.Integer,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
),
|
||||
sa.Column('symbol', sa.Text, unique=True, index=True),
|
||||
sa.Column(
|
||||
'root_symbol',
|
||||
sa.Text,
|
||||
sa.ForeignKey('futures_root_symbols.root_symbol'),
|
||||
index=True
|
||||
),
|
||||
sa.Column('asset_name', sa.Text),
|
||||
sa.Column('start_date', sa.Integer, default=0, nullable=False),
|
||||
sa.Column('end_date', sa.Integer, nullable=False),
|
||||
sa.Column('first_traded', sa.Integer),
|
||||
sa.Column(
|
||||
'exchange',
|
||||
sa.Text,
|
||||
sa.ForeignKey('futures_exchanges.exchange'),
|
||||
),
|
||||
sa.Column('notice_date', sa.Integer, nullable=False),
|
||||
sa.Column('expiration_date', sa.Integer, nullable=False),
|
||||
sa.Column('auto_close_date', sa.Integer, nullable=False),
|
||||
sa.Column('multiplier', sa.Float),
|
||||
sa.Column('tick_size', sa.Float),
|
||||
)
|
||||
|
||||
asset_router = sa.Table(
|
||||
'asset_router',
|
||||
metadata,
|
||||
sa.Column(
|
||||
'sid',
|
||||
sa.Integer,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
primary_key=True),
|
||||
sa.Column('asset_type', sa.Text),
|
||||
)
|
||||
|
||||
version_info = sa.Table(
|
||||
'version_info',
|
||||
metadata,
|
||||
sa.Column(
|
||||
'id',
|
||||
sa.Integer,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
),
|
||||
sa.Column(
|
||||
'version',
|
||||
sa.Integer,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
),
|
||||
# This constraint ensures a single entry in this table
|
||||
sa.CheckConstraint('id <= 1'),
|
||||
)
|
||||
@@ -1,740 +0,0 @@
|
||||
#
|
||||
# Copyright 2015 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from collections import namedtuple
|
||||
import re
|
||||
|
||||
from contextlib2 import ExitStack
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import sqlalchemy as sa
|
||||
from toolz import first
|
||||
|
||||
from catalyst.errors import AssetDBVersionError
|
||||
from catalyst.assets.asset_db_schema import (
|
||||
ASSET_DB_VERSION,
|
||||
asset_db_table_names,
|
||||
asset_router,
|
||||
equities as equities_table,
|
||||
equity_symbol_mappings,
|
||||
equity_supplementary_mappings as equity_supplementary_mappings_table,
|
||||
futures_contracts as futures_contracts_table,
|
||||
futures_exchanges,
|
||||
futures_root_symbols,
|
||||
metadata,
|
||||
version_info,
|
||||
)
|
||||
|
||||
from catalyst.utils.preprocess import preprocess
|
||||
from catalyst.utils.range import from_tuple, intersecting_ranges
|
||||
from catalyst.utils.sqlite_utils import coerce_string_to_eng
|
||||
|
||||
# Define a namedtuple for use with the load_data and _load_data methods
|
||||
AssetData = namedtuple(
|
||||
'AssetData', (
|
||||
'equities',
|
||||
'equities_mappings',
|
||||
'futures',
|
||||
'exchanges',
|
||||
'root_symbols',
|
||||
'equity_supplementary_mappings',
|
||||
),
|
||||
)
|
||||
|
||||
SQLITE_MAX_VARIABLE_NUMBER = 999
|
||||
|
||||
symbol_columns = frozenset({
|
||||
'symbol',
|
||||
'company_symbol',
|
||||
'share_class_symbol',
|
||||
})
|
||||
mapping_columns = symbol_columns | {'start_date', 'end_date'}
|
||||
|
||||
# Default values for the equities DataFrame
|
||||
_equities_defaults = {
|
||||
'symbol': None,
|
||||
'asset_name': None,
|
||||
'start_date': 0,
|
||||
'end_date': np.iinfo(np.int64).max,
|
||||
'first_traded': None,
|
||||
'auto_close_date': None,
|
||||
# the canonical exchange name, like "NYSE"
|
||||
'exchange': None,
|
||||
# optional, something like "New York Stock Exchange"
|
||||
'exchange_full': None,
|
||||
'min_trade_size': 1
|
||||
}
|
||||
|
||||
# Default values for the futures DataFrame
|
||||
_futures_defaults = {
|
||||
'symbol': None,
|
||||
'root_symbol': None,
|
||||
'asset_name': None,
|
||||
'start_date': 0,
|
||||
'end_date': np.iinfo(np.int64).max,
|
||||
'first_traded': None,
|
||||
'exchange': None,
|
||||
'notice_date': None,
|
||||
'expiration_date': None,
|
||||
'auto_close_date': None,
|
||||
'tick_size': None,
|
||||
'multiplier': 1,
|
||||
}
|
||||
|
||||
# Default values for the exchanges DataFrame
|
||||
_exchanges_defaults = {
|
||||
'timezone': None,
|
||||
}
|
||||
|
||||
# Default values for the root_symbols DataFrame
|
||||
_root_symbols_defaults = {
|
||||
'root_symbol_id': None,
|
||||
'sector': None,
|
||||
'description': None,
|
||||
'exchange': None,
|
||||
}
|
||||
|
||||
# Default values for the equity_supplementary_mappings DataFrame
|
||||
_equity_supplementary_mappings_defaults = {
|
||||
'sid': None,
|
||||
'value': None,
|
||||
'field': None,
|
||||
'start_date': 0,
|
||||
'end_date': np.iinfo(np.int64).max,
|
||||
}
|
||||
|
||||
|
||||
# Fuzzy symbol delimiters that may break up a company symbol and share class
|
||||
_delimited_symbol_delimiters_regex = re.compile(r'[./\-_]')
|
||||
_delimited_symbol_default_triggers = frozenset({np.nan, None, ''})
|
||||
|
||||
|
||||
def split_delimited_symbol(symbol):
|
||||
"""
|
||||
Takes in a symbol that may be delimited and splits it in to a company
|
||||
symbol and share class symbol. Also returns the fuzzy symbol, which is the
|
||||
symbol without any fuzzy characters at all.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : str
|
||||
The possibly-delimited symbol to be split
|
||||
|
||||
Returns
|
||||
-------
|
||||
company_symbol : str
|
||||
The company part of the symbol.
|
||||
share_class_symbol : str
|
||||
The share class part of a symbol.
|
||||
"""
|
||||
# return blank strings for any bad fuzzy symbols, like NaN or None
|
||||
if symbol in _delimited_symbol_default_triggers:
|
||||
return '', ''
|
||||
|
||||
symbol = symbol.upper()
|
||||
|
||||
split_list = re.split(
|
||||
pattern=_delimited_symbol_delimiters_regex,
|
||||
string=symbol,
|
||||
maxsplit=1,
|
||||
)
|
||||
|
||||
# Break the list up in to its two components, the company symbol and the
|
||||
# share class symbol
|
||||
company_symbol = split_list[0]
|
||||
if len(split_list) > 1:
|
||||
share_class_symbol = split_list[1]
|
||||
else:
|
||||
share_class_symbol = ''
|
||||
|
||||
return company_symbol, share_class_symbol
|
||||
|
||||
|
||||
def _generate_output_dataframe(data_subset, defaults):
|
||||
"""
|
||||
Generates an output dataframe from the given subset of user-provided
|
||||
data, the given column names, and the given default values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_subset : DataFrame
|
||||
A DataFrame, usually from an AssetData object,
|
||||
that contains the user's input metadata for the asset type being
|
||||
processed
|
||||
defaults : dict
|
||||
A dict where the keys are the names of the columns of the desired
|
||||
output DataFrame and the values are the default values to insert in the
|
||||
DataFrame if no user data is provided
|
||||
|
||||
Returns
|
||||
-------
|
||||
DataFrame
|
||||
A DataFrame containing all user-provided metadata, and default values
|
||||
wherever user-provided metadata was missing
|
||||
"""
|
||||
# The columns provided.
|
||||
cols = set(data_subset.columns)
|
||||
desired_cols = set(defaults)
|
||||
|
||||
# Drop columns with unrecognised headers.
|
||||
data_subset.drop(cols - desired_cols,
|
||||
axis=1,
|
||||
inplace=True)
|
||||
|
||||
# Get those columns which we need but
|
||||
# for which no data has been supplied.
|
||||
for col in desired_cols - cols:
|
||||
# write the default value for any missing columns
|
||||
data_subset[col] = defaults[col]
|
||||
|
||||
return data_subset
|
||||
|
||||
|
||||
def _check_asset_group(group):
|
||||
row = group.sort_values('end_date').iloc[-1]
|
||||
row.start_date = group.start_date.min()
|
||||
row.end_date = group.end_date.max()
|
||||
row.drop(list(symbol_columns), inplace=True)
|
||||
return row
|
||||
|
||||
|
||||
def _format_range(r):
|
||||
return (
|
||||
str(pd.Timestamp(r.start, unit='ns')),
|
||||
str(pd.Timestamp(r.stop, unit='ns')),
|
||||
)
|
||||
|
||||
|
||||
def _split_symbol_mappings(df):
|
||||
"""Split out the symbol: sid mappings from the raw data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
df : pd.DataFrame
|
||||
The dataframe with multiple rows for each symbol: sid pair.
|
||||
|
||||
Returns
|
||||
-------
|
||||
asset_info : pd.DataFrame
|
||||
The asset info with one row per asset.
|
||||
symbol_mappings : pd.DataFrame
|
||||
The dataframe of just symbol: sid mappings. The index will be
|
||||
the sid, then there will be three columns: symbol, start_date, and
|
||||
end_date.
|
||||
"""
|
||||
mappings = df[list(mapping_columns)]
|
||||
ambigious = {}
|
||||
for symbol in mappings.symbol.unique():
|
||||
persymbol = mappings[mappings.symbol == symbol]
|
||||
intersections = list(intersecting_ranges(map(
|
||||
from_tuple,
|
||||
zip(persymbol.start_date, persymbol.end_date),
|
||||
)))
|
||||
if intersections:
|
||||
ambigious[symbol] = (
|
||||
intersections,
|
||||
persymbol[['start_date', 'end_date']].astype('datetime64[ns]'),
|
||||
)
|
||||
|
||||
if ambigious:
|
||||
raise ValueError(
|
||||
'Ambiguous ownership for %d symbol%s, multiple assets held the'
|
||||
' following symbols:\n%s' % (
|
||||
len(ambigious),
|
||||
'' if len(ambigious) == 1 else 's',
|
||||
'\n'.join(
|
||||
'%s:\n intersections: %s\n %s' % (
|
||||
symbol,
|
||||
tuple(map(_format_range, intersections)),
|
||||
# indent the dataframe string
|
||||
'\n '.join(str(df).splitlines()),
|
||||
)
|
||||
for symbol, (intersections, df) in sorted(
|
||||
ambigious.items(),
|
||||
key=first,
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
return (
|
||||
df.groupby(level=0).apply(_check_asset_group),
|
||||
df[list(mapping_columns)],
|
||||
)
|
||||
|
||||
|
||||
def _dt_to_epoch_ns(dt_series):
|
||||
"""Convert a timeseries into an Int64Index of nanoseconds since the epoch.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dt_series : pd.Series
|
||||
The timeseries to convert.
|
||||
|
||||
Returns
|
||||
-------
|
||||
idx : pd.Int64Index
|
||||
The index converted to nanoseconds since the epoch.
|
||||
"""
|
||||
index = pd.to_datetime(dt_series.values)
|
||||
if index.tzinfo is None:
|
||||
index = index.tz_localize('UTC')
|
||||
else:
|
||||
index = index.tz_convert('UTC')
|
||||
return index.view(np.int64)
|
||||
|
||||
|
||||
def check_version_info(conn, version_table, expected_version):
|
||||
"""
|
||||
Checks for a version value in the version table.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
conn : sa.Connection
|
||||
The connection to use to perform the check.
|
||||
version_table : sa.Table
|
||||
The version table of the asset database
|
||||
expected_version : int
|
||||
The expected version of the asset database
|
||||
|
||||
Raises
|
||||
------
|
||||
AssetDBVersionError
|
||||
If the version is in the table and not equal to ASSET_DB_VERSION.
|
||||
"""
|
||||
|
||||
# Read the version out of the table
|
||||
version_from_table = conn.execute(
|
||||
sa.select((version_table.c.version,)),
|
||||
).scalar()
|
||||
|
||||
# A db without a version is considered v0
|
||||
if version_from_table is None:
|
||||
version_from_table = 0
|
||||
|
||||
# Raise an error if the versions do not match
|
||||
if (version_from_table != expected_version):
|
||||
raise AssetDBVersionError(db_version=version_from_table,
|
||||
expected_version=expected_version)
|
||||
|
||||
|
||||
def write_version_info(conn, version_table, version_value):
|
||||
"""
|
||||
Inserts the version value in to the version table.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
conn : sa.Connection
|
||||
The connection to use to execute the insert.
|
||||
version_table : sa.Table
|
||||
The version table of the asset database
|
||||
version_value : int
|
||||
The version to write in to the database
|
||||
|
||||
"""
|
||||
conn.execute(sa.insert(version_table, values={'version': version_value}))
|
||||
|
||||
|
||||
class _empty(object):
|
||||
columns = ()
|
||||
|
||||
|
||||
class AssetDBWriter(object):
|
||||
"""Class used to write data to an assets db.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
engine : Engine or str
|
||||
An SQLAlchemy engine or path to a SQL database.
|
||||
"""
|
||||
DEFAULT_CHUNK_SIZE = SQLITE_MAX_VARIABLE_NUMBER
|
||||
|
||||
@preprocess(engine=coerce_string_to_eng)
|
||||
def __init__(self, engine):
|
||||
self.engine = engine
|
||||
|
||||
def write(self,
|
||||
equities=None,
|
||||
futures=None,
|
||||
exchanges=None,
|
||||
root_symbols=None,
|
||||
equity_supplementary_mappings=None,
|
||||
chunk_size=DEFAULT_CHUNK_SIZE):
|
||||
"""Write asset metadata to a sqlite database.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
equities : pd.DataFrame, optional
|
||||
The equity metadata. The columns for this dataframe are:
|
||||
|
||||
symbol : str
|
||||
The ticker symbol for this equity.
|
||||
asset_name : str
|
||||
The full name for this asset.
|
||||
start_date : datetime
|
||||
The date when this asset was created.
|
||||
end_date : datetime, optional
|
||||
The last date we have trade data for this asset.
|
||||
first_traded : datetime, optional
|
||||
The first date we have trade data for this asset.
|
||||
auto_close_date : datetime, optional
|
||||
The date on which to close any positions in this asset.
|
||||
exchange : str
|
||||
The exchange where this asset is traded.
|
||||
min_trade_size: float, optional
|
||||
The minimum denomination this asset can be traded.
|
||||
|
||||
The index of this dataframe should contain the sids.
|
||||
futures : pd.DataFrame, optional
|
||||
The future contract metadata. The columns for this dataframe are:
|
||||
|
||||
symbol : str
|
||||
The ticker symbol for this futures contract.
|
||||
root_symbol : str
|
||||
The root symbol, or the symbol with the expiration stripped
|
||||
out.
|
||||
asset_name : str
|
||||
The full name for this asset.
|
||||
start_date : datetime, optional
|
||||
The date when this asset was created.
|
||||
end_date : datetime, optional
|
||||
The last date we have trade data for this asset.
|
||||
first_traded : datetime, optional
|
||||
The first date we have trade data for this asset.
|
||||
exchange : str
|
||||
The exchange where this asset is traded.
|
||||
notice_date : datetime
|
||||
The date when the owner of the contract may be forced
|
||||
to take physical delivery of the contract's asset.
|
||||
expiration_date : datetime
|
||||
The date when the contract expires.
|
||||
auto_close_date : datetime
|
||||
The date when the broker will automatically close any
|
||||
positions in this contract.
|
||||
tick_size : float
|
||||
The minimum price movement of the contract.
|
||||
multiplier: float
|
||||
The amount of the underlying asset represented by this
|
||||
contract.
|
||||
exchanges : pd.DataFrame, optional
|
||||
The exchanges where assets can be traded. The columns of this
|
||||
dataframe are:
|
||||
|
||||
exchange : str
|
||||
The name of the exchange.
|
||||
timezone : str
|
||||
The timezone of the exchange.
|
||||
root_symbols : pd.DataFrame, optional
|
||||
The root symbols for the futures contracts. The columns for this
|
||||
dataframe are:
|
||||
|
||||
root_symbol : str
|
||||
The root symbol name.
|
||||
root_symbol_id : int
|
||||
The unique id for this root symbol.
|
||||
sector : string, optional
|
||||
The sector of this root symbol.
|
||||
description : string, optional
|
||||
A short description of this root symbol.
|
||||
exchange : str
|
||||
The exchange where this root symbol is traded.
|
||||
equity_supplementary_mappings : pd.DataFrame, optional
|
||||
Additional mappings from values of abitrary type to assets.
|
||||
chunk_size : int, optional
|
||||
The amount of rows to write to the SQLite table at once.
|
||||
This defaults to the default number of bind params in sqlite.
|
||||
If you have compiled sqlite3 with more bind or less params you may
|
||||
want to pass that value here.
|
||||
|
||||
See Also
|
||||
--------
|
||||
catalyst.assets.asset_finder
|
||||
"""
|
||||
with self.engine.begin() as conn:
|
||||
# Create SQL tables if they do not exist.
|
||||
self.init_db(conn)
|
||||
|
||||
# Get the data to add to SQL.
|
||||
data = self._load_data(
|
||||
equities if equities is not None else pd.DataFrame(),
|
||||
futures if futures is not None else pd.DataFrame(),
|
||||
exchanges if exchanges is not None else pd.DataFrame(),
|
||||
root_symbols if root_symbols is not None else pd.DataFrame(),
|
||||
(
|
||||
equity_supplementary_mappings
|
||||
if equity_supplementary_mappings is not None
|
||||
else pd.DataFrame()
|
||||
),
|
||||
)
|
||||
# Write the data to SQL.
|
||||
self._write_df_to_table(
|
||||
futures_exchanges,
|
||||
data.exchanges,
|
||||
conn,
|
||||
chunk_size,
|
||||
)
|
||||
self._write_df_to_table(
|
||||
futures_root_symbols,
|
||||
data.root_symbols,
|
||||
conn,
|
||||
chunk_size,
|
||||
)
|
||||
self._write_df_to_table(
|
||||
equity_supplementary_mappings_table,
|
||||
data.equity_supplementary_mappings,
|
||||
conn,
|
||||
chunk_size,
|
||||
idx=False,
|
||||
)
|
||||
self._write_assets(
|
||||
'future',
|
||||
data.futures,
|
||||
conn,
|
||||
chunk_size,
|
||||
)
|
||||
self._write_assets(
|
||||
'equity',
|
||||
data.equities,
|
||||
conn,
|
||||
chunk_size,
|
||||
mapping_data=data.equities_mappings,
|
||||
)
|
||||
|
||||
def _write_df_to_table(
|
||||
self,
|
||||
tbl,
|
||||
df,
|
||||
txn,
|
||||
chunk_size,
|
||||
idx=True,
|
||||
idx_label=None,
|
||||
):
|
||||
df.to_sql(
|
||||
tbl.name,
|
||||
txn.connection,
|
||||
index=idx,
|
||||
index_label=(
|
||||
idx_label
|
||||
if idx_label is not None else
|
||||
first(tbl.primary_key.columns).name
|
||||
),
|
||||
if_exists='append',
|
||||
chunksize=chunk_size,
|
||||
)
|
||||
|
||||
def _write_assets(self,
|
||||
asset_type,
|
||||
assets,
|
||||
txn,
|
||||
chunk_size,
|
||||
mapping_data=None):
|
||||
if asset_type == 'future':
|
||||
tbl = futures_contracts_table
|
||||
if mapping_data is not None:
|
||||
raise TypeError('no mapping data expected for futures')
|
||||
|
||||
elif asset_type == 'equity':
|
||||
tbl = equities_table
|
||||
if mapping_data is None:
|
||||
raise TypeError('mapping data required for equities')
|
||||
# write the symbol mapping data.
|
||||
self._write_df_to_table(
|
||||
equity_symbol_mappings,
|
||||
mapping_data,
|
||||
txn,
|
||||
chunk_size,
|
||||
idx_label='sid',
|
||||
)
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
"asset_type must be in {'future', 'equity'}, got: %s" %
|
||||
asset_type,
|
||||
)
|
||||
|
||||
self._write_df_to_table(tbl, assets, txn, chunk_size)
|
||||
|
||||
pd.DataFrame({
|
||||
asset_router.c.sid.name: assets.index.values,
|
||||
asset_router.c.asset_type.name: asset_type,
|
||||
}).to_sql(
|
||||
asset_router.name,
|
||||
txn.connection,
|
||||
if_exists='append',
|
||||
index=False,
|
||||
chunksize=chunk_size
|
||||
)
|
||||
|
||||
def _all_tables_present(self, txn):
|
||||
"""
|
||||
Checks if any tables are present in the current assets database.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
txn : Transaction
|
||||
The open transaction to check in.
|
||||
|
||||
Returns
|
||||
-------
|
||||
has_tables : bool
|
||||
True if any tables are present, otherwise False.
|
||||
"""
|
||||
conn = txn.connect()
|
||||
for table_name in asset_db_table_names:
|
||||
if txn.dialect.has_table(conn, table_name):
|
||||
return True
|
||||
return False
|
||||
|
||||
def init_db(self, txn=None):
|
||||
"""Connect to database and create tables.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
txn : sa.engine.Connection, optional
|
||||
The transaction to execute in. If this is not provided, a new
|
||||
transaction will be started with the engine provided.
|
||||
|
||||
Returns
|
||||
-------
|
||||
metadata : sa.MetaData
|
||||
The metadata that describes the new assets db.
|
||||
"""
|
||||
with ExitStack() as stack:
|
||||
if txn is None:
|
||||
txn = stack.enter_context(self.engine.begin())
|
||||
|
||||
tables_already_exist = self._all_tables_present(txn)
|
||||
|
||||
# Create the SQL tables if they do not already exist.
|
||||
metadata.create_all(txn, checkfirst=True)
|
||||
|
||||
if tables_already_exist:
|
||||
check_version_info(txn, version_info, ASSET_DB_VERSION)
|
||||
else:
|
||||
write_version_info(txn, version_info, ASSET_DB_VERSION)
|
||||
|
||||
def _normalize_equities(self, equities):
|
||||
# HACK: If 'company_name' is provided, map it to asset_name
|
||||
if ('company_name' in equities.columns and
|
||||
'asset_name' not in equities.columns):
|
||||
equities['asset_name'] = equities['company_name']
|
||||
|
||||
# remap 'file_name' to 'symbol' if provided
|
||||
if 'file_name' in equities.columns:
|
||||
equities['symbol'] = equities['file_name']
|
||||
|
||||
equities_output = _generate_output_dataframe(
|
||||
data_subset=equities,
|
||||
defaults=_equities_defaults,
|
||||
)
|
||||
|
||||
# Split symbols to company_symbols and share_class_symbols
|
||||
tuple_series = equities_output['symbol'].apply(split_delimited_symbol)
|
||||
split_symbols = pd.DataFrame(
|
||||
tuple_series.tolist(),
|
||||
columns=['company_symbol', 'share_class_symbol'],
|
||||
index=tuple_series.index
|
||||
)
|
||||
equities_output = pd.concat((equities_output, split_symbols), axis=1)
|
||||
|
||||
# Upper-case all symbol data
|
||||
for col in symbol_columns:
|
||||
equities_output[col] = equities_output[col].str.upper()
|
||||
|
||||
# Convert date columns to UNIX Epoch integers (nanoseconds)
|
||||
for col in ('start_date',
|
||||
'end_date',
|
||||
'first_traded',
|
||||
'auto_close_date'):
|
||||
equities_output[col] = _dt_to_epoch_ns(equities_output[col])
|
||||
|
||||
return _split_symbol_mappings(equities_output)
|
||||
|
||||
def _normalize_futures(self, futures):
|
||||
futures_output = _generate_output_dataframe(
|
||||
data_subset=futures,
|
||||
defaults=_futures_defaults,
|
||||
)
|
||||
for col in ('symbol', 'root_symbol'):
|
||||
futures_output[col] = futures_output[col].str.upper()
|
||||
|
||||
for col in ('start_date',
|
||||
'end_date',
|
||||
'first_traded',
|
||||
'notice_date',
|
||||
'expiration_date',
|
||||
'auto_close_date'):
|
||||
futures_output[col] = _dt_to_epoch_ns(futures_output[col])
|
||||
|
||||
return futures_output
|
||||
|
||||
def _normalize_equity_supplementary_mappings(self, mappings):
|
||||
mappings_output = _generate_output_dataframe(
|
||||
data_subset=mappings,
|
||||
defaults=_equity_supplementary_mappings_defaults,
|
||||
)
|
||||
|
||||
for col in ('start_date', 'end_date'):
|
||||
mappings_output[col] = _dt_to_epoch_ns(mappings_output[col])
|
||||
|
||||
return mappings_output
|
||||
|
||||
def _load_data(
|
||||
self,
|
||||
equities,
|
||||
futures,
|
||||
exchanges,
|
||||
root_symbols,
|
||||
equity_supplementary_mappings,
|
||||
):
|
||||
"""
|
||||
Returns a standard set of pandas.DataFrames:
|
||||
equities, futures, exchanges, root_symbols
|
||||
"""
|
||||
# Check whether identifier columns have been provided.
|
||||
# If they have, set the index to this column.
|
||||
# If not, assume the index already cotains the identifier information.
|
||||
for df, id_col in [(equities, 'sid'),
|
||||
(futures, 'sid'),
|
||||
(exchanges, 'exchange'),
|
||||
(root_symbols, 'root_symbol')]:
|
||||
if id_col in df.columns:
|
||||
df.set_index(id_col, inplace=True)
|
||||
|
||||
equities_output, equities_mappings = self._normalize_equities(equities)
|
||||
futures_output = self._normalize_futures(futures)
|
||||
|
||||
equity_supplementary_mappings_output = (
|
||||
self._normalize_equity_supplementary_mappings(
|
||||
equity_supplementary_mappings,
|
||||
)
|
||||
)
|
||||
|
||||
exchanges_output = _generate_output_dataframe(
|
||||
data_subset=exchanges,
|
||||
defaults=_exchanges_defaults,
|
||||
)
|
||||
|
||||
root_symbols_output = _generate_output_dataframe(
|
||||
data_subset=root_symbols,
|
||||
defaults=_root_symbols_defaults,
|
||||
)
|
||||
|
||||
return AssetData(
|
||||
equities=equities_output,
|
||||
equities_mappings=equities_mappings,
|
||||
futures=futures_output,
|
||||
exchanges=exchanges_output,
|
||||
root_symbols=root_symbols_output,
|
||||
equity_supplementary_mappings=equity_supplementary_mappings_output,
|
||||
)
|
||||
@@ -1,423 +0,0 @@
|
||||
# cython: embedsignature=True
|
||||
#
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Cythonized ContinuousFutures object.
|
||||
"""
|
||||
cimport cython
|
||||
from cpython.number cimport PyNumber_Index
|
||||
from cpython.object cimport (
|
||||
Py_EQ,
|
||||
Py_NE,
|
||||
Py_GE,
|
||||
Py_LE,
|
||||
Py_GT,
|
||||
Py_LT,
|
||||
)
|
||||
from cpython cimport bool
|
||||
|
||||
from functools import partial
|
||||
|
||||
from numpy import array, empty, iinfo
|
||||
from numpy cimport long_t, int64_t
|
||||
from pandas import Timestamp
|
||||
import warnings
|
||||
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
|
||||
|
||||
def delivery_predicate(codes, contract):
|
||||
# This relies on symbols that are construct following a pattern of
|
||||
# root symbol + delivery code + year, e.g. PLF16
|
||||
# This check would be more robust if the future contract class had
|
||||
# a 'delivery_month' member.
|
||||
delivery_code = contract.symbol[-3]
|
||||
return delivery_code in codes
|
||||
|
||||
march_cycle_delivery_predicate = partial(delivery_predicate,
|
||||
set(['H', 'M', 'U', 'Z']))
|
||||
|
||||
CHAIN_PREDICATES = {
|
||||
'ME': march_cycle_delivery_predicate,
|
||||
'PL': partial(delivery_predicate, set(['F', 'J', 'N', 'V'])),
|
||||
'PA': march_cycle_delivery_predicate,
|
||||
|
||||
# The majority of trading in these currency futures is done on a
|
||||
# March quarterly cycle (Mar, Jun, Sep, Dec) but contracts are
|
||||
# listed for the first 3 consecutive months from the present day. We
|
||||
# want the continuous futures to be composed of just the quarterly
|
||||
# contracts.
|
||||
'JY': march_cycle_delivery_predicate,
|
||||
'CD': march_cycle_delivery_predicate,
|
||||
'AD': march_cycle_delivery_predicate,
|
||||
'BP': march_cycle_delivery_predicate,
|
||||
|
||||
# Gold and silver contracts trade on an unusual specific set of months.
|
||||
'GC': partial(delivery_predicate, set(['G', 'J', 'M', 'Q', 'V', 'Z'])),
|
||||
'XG': partial(delivery_predicate, set(['G', 'J', 'M', 'Q', 'V', 'Z'])),
|
||||
'SV': partial(delivery_predicate, set(['H', 'K', 'N', 'U', 'Z'])),
|
||||
'YS': partial(delivery_predicate, set(['H', 'K', 'N', 'U', 'Z'])),
|
||||
}
|
||||
|
||||
ADJUSTMENT_STYLES = {'add', 'mul', None}
|
||||
|
||||
|
||||
cdef class ContinuousFuture:
|
||||
"""
|
||||
Represents a specifier for a chain of future contracts, where the
|
||||
coordinates for the chain are:
|
||||
root_symbol : str
|
||||
The root symbol of the contracts.
|
||||
offset : int
|
||||
The distance from the primary chain.
|
||||
e.g. 0 specifies the primary chain, 1 the secondary, etc.
|
||||
roll_style : str
|
||||
How rolls from contract to contract should be calculated.
|
||||
Currently supports 'calendar'.
|
||||
|
||||
Instances of this class are exposed to the algorithm.
|
||||
"""
|
||||
|
||||
cdef readonly long_t sid
|
||||
# Cached hash of self.sid
|
||||
cdef long_t sid_hash
|
||||
|
||||
cdef readonly object root_symbol
|
||||
cdef readonly int offset
|
||||
cdef readonly object roll_style
|
||||
|
||||
cdef readonly object start_date
|
||||
cdef readonly object end_date
|
||||
|
||||
cdef readonly object exchange
|
||||
|
||||
cdef readonly object adjustment
|
||||
|
||||
_kwargnames = frozenset({
|
||||
'sid',
|
||||
'root_symbol',
|
||||
'offset',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'exchange',
|
||||
})
|
||||
|
||||
def __init__(self,
|
||||
long_t sid, # sid is required
|
||||
object root_symbol,
|
||||
int offset,
|
||||
object roll_style,
|
||||
object start_date,
|
||||
object end_date,
|
||||
object exchange,
|
||||
object adjustment=None):
|
||||
|
||||
self.sid = sid
|
||||
self.sid_hash = hash(sid)
|
||||
self.root_symbol = root_symbol
|
||||
self.roll_style = roll_style
|
||||
self.offset = offset
|
||||
self.exchange = exchange
|
||||
self.start_date = start_date
|
||||
self.end_date = end_date
|
||||
self.adjustment = adjustment
|
||||
|
||||
|
||||
def __int__(self):
|
||||
return self.sid
|
||||
|
||||
def __index__(self):
|
||||
return self.sid
|
||||
|
||||
def __hash__(self):
|
||||
return self.sid_hash
|
||||
|
||||
def __richcmp__(x, y, int op):
|
||||
"""
|
||||
Cython rich comparison method. This is used in place of various
|
||||
equality checkers in pure python.
|
||||
"""
|
||||
cdef long_t x_as_int, y_as_int
|
||||
|
||||
try:
|
||||
x_as_int = PyNumber_Index(x)
|
||||
except (TypeError, OverflowError):
|
||||
return NotImplemented
|
||||
|
||||
try:
|
||||
y_as_int = PyNumber_Index(y)
|
||||
except (TypeError, OverflowError):
|
||||
return NotImplemented
|
||||
|
||||
compared = x_as_int - y_as_int
|
||||
|
||||
# Handle == and != first because they're significantly more common
|
||||
# operations.
|
||||
if op == Py_EQ:
|
||||
return compared == 0
|
||||
elif op == Py_NE:
|
||||
return compared != 0
|
||||
elif op == Py_LT:
|
||||
return compared < 0
|
||||
elif op == Py_LE:
|
||||
return compared <= 0
|
||||
elif op == Py_GT:
|
||||
return compared > 0
|
||||
elif op == Py_GE:
|
||||
return compared >= 0
|
||||
else:
|
||||
raise AssertionError('%d is not an operator' % op)
|
||||
|
||||
def __str__(self):
|
||||
return '%s(%d [%s, %s, %s, %s])' % (
|
||||
type(self).__name__,
|
||||
self.sid,
|
||||
self.root_symbol,
|
||||
self.offset,
|
||||
self.roll_style,
|
||||
self.adjustment,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
attrs = ('root_symbol', 'offset', 'roll_style', 'adjustment')
|
||||
tuples = ((attr, repr(getattr(self, attr, None)))
|
||||
for attr in attrs)
|
||||
strings = ('%s=%s' % (t[0], t[1]) for t in tuples)
|
||||
params = ', '.join(strings)
|
||||
return 'ContinuousFuture(%d, %s)' % (self.sid, params)
|
||||
|
||||
cpdef __reduce__(self):
|
||||
"""
|
||||
Function used by pickle to determine how to serialize/deserialize this
|
||||
class. Should return a tuple whose first element is self.__class__,
|
||||
and whose second element is a tuple of all the attributes that should
|
||||
be serialized/deserialized during pickling.
|
||||
"""
|
||||
return (self.__class__, (self.sid,
|
||||
self.root_symbol,
|
||||
self.start_date,
|
||||
self.end_date,
|
||||
self.offset,
|
||||
self.roll_style,
|
||||
self.exchange))
|
||||
|
||||
cpdef to_dict(self):
|
||||
"""
|
||||
Convert to a python dict.
|
||||
"""
|
||||
return {
|
||||
'sid': self.sid,
|
||||
'root_symbol': self.root_symbol,
|
||||
'start_date': self.start_date,
|
||||
'end_date': self.end_date,
|
||||
'offset': self.offset,
|
||||
'roll_style': self.roll_style,
|
||||
'exchange': self.exchange,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dict_):
|
||||
"""
|
||||
Build an ContinuousFuture instance from a dict.
|
||||
"""
|
||||
return cls(**dict_)
|
||||
|
||||
def is_alive_for_session(self, session_label):
|
||||
"""
|
||||
Returns whether the continuous future is alive at the given dt.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
session_label: pd.Timestamp
|
||||
The desired session label to check. (midnight UTC)
|
||||
|
||||
Returns
|
||||
-------
|
||||
boolean: whether the continuous is alive at the given dt.
|
||||
"""
|
||||
cdef int64_t ref_start
|
||||
cdef int64_t ref_end
|
||||
|
||||
ref_start = self.start_date.value
|
||||
ref_end = self.end_date.value
|
||||
|
||||
return ref_start <= session_label.value <= ref_end
|
||||
|
||||
def is_exchange_open(self, dt_minute):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
dt_minute: pd.Timestamp (UTC, tz-aware)
|
||||
The minute to check.
|
||||
|
||||
Returns
|
||||
-------
|
||||
boolean: whether the continuous futures's exchange is open at the
|
||||
given minute.
|
||||
"""
|
||||
calendar = get_calendar(self.exchange)
|
||||
return calendar.is_open_on_minute(dt_minute)
|
||||
|
||||
|
||||
cdef class ContractNode(object):
|
||||
|
||||
cdef readonly object contract
|
||||
cdef public object prev
|
||||
cdef public object next
|
||||
|
||||
def __init__(self, contract):
|
||||
self.contract = contract
|
||||
self.prev = None
|
||||
self.next = None
|
||||
|
||||
def __rshift__(self, offset):
|
||||
i = 0
|
||||
curr = self
|
||||
while i < offset and curr is not None:
|
||||
curr = curr.next
|
||||
i += 1
|
||||
return curr
|
||||
|
||||
def __lshift__(self, offset):
|
||||
i = 0
|
||||
curr = self
|
||||
while i < offset and curr is not None:
|
||||
curr = curr.prev
|
||||
i += 1
|
||||
return curr
|
||||
|
||||
|
||||
cdef class OrderedContracts(object):
|
||||
"""
|
||||
A container for aligned values of a future contract chain, in sorted order
|
||||
of their occurrence.
|
||||
Used to get answers about contracts in relation to their auto close
|
||||
dates and start dates.
|
||||
|
||||
Members
|
||||
-------
|
||||
root_symbol : str
|
||||
The root symbol of the future contract chain.
|
||||
contracts : deque
|
||||
The contracts in the chain in order of occurrence.
|
||||
start_dates : long[:]
|
||||
The start dates of the contracts in the chain.
|
||||
Corresponds by index with contract_sids.
|
||||
auto_close_dates : long[:]
|
||||
The auto close dates of the contracts in the chain.
|
||||
Corresponds by index with contract_sids.
|
||||
future_chain_predicates : dict
|
||||
A dict mapping root symbol to a predicate function which accepts a contract
|
||||
as a parameter and returns whether or not the contract should be included in the
|
||||
chain.
|
||||
|
||||
Instances of this class are used by the simulation engine, but not
|
||||
exposed to the algorithm.
|
||||
"""
|
||||
|
||||
cdef readonly object root_symbol
|
||||
cdef readonly object _head_contract
|
||||
cdef readonly dict sid_to_contract
|
||||
cdef readonly int64_t _start_date
|
||||
cdef readonly int64_t _end_date
|
||||
cdef readonly object chain_predicate
|
||||
|
||||
def __init__(self, object root_symbol, object contracts, object chain_predicate=None):
|
||||
|
||||
self.root_symbol = root_symbol
|
||||
|
||||
self.sid_to_contract = {}
|
||||
|
||||
self._start_date = iinfo('int64').max
|
||||
self._end_date = 0
|
||||
|
||||
if chain_predicate is None:
|
||||
chain_predicate = lambda x: True
|
||||
|
||||
self._head_contract = None
|
||||
prev = None
|
||||
while contracts:
|
||||
contract = contracts.popleft()
|
||||
|
||||
# It is possible that the first contract in our list has a start
|
||||
# date on or after its auto close date. In that case the contract
|
||||
# is not tradable, so do not include it in the chain.
|
||||
if prev is None and contract.start_date >= contract.auto_close_date:
|
||||
continue
|
||||
|
||||
if not chain_predicate(contract):
|
||||
continue
|
||||
|
||||
self._start_date = min(contract.start_date.value, self._start_date)
|
||||
self._end_date = max(contract.end_date.value, self._end_date)
|
||||
|
||||
curr = ContractNode(contract)
|
||||
self.sid_to_contract[contract.sid] = curr
|
||||
if self._head_contract is None:
|
||||
self._head_contract = curr
|
||||
prev = curr
|
||||
continue
|
||||
curr.prev = prev
|
||||
prev.next = curr
|
||||
prev = curr
|
||||
|
||||
cpdef long_t contract_before_auto_close(self, long_t dt_value):
|
||||
"""
|
||||
Get the contract with next upcoming auto close date.
|
||||
"""
|
||||
curr = self._head_contract
|
||||
while curr.next is not None:
|
||||
if curr.contract.auto_close_date.value > dt_value:
|
||||
break
|
||||
curr = curr.next
|
||||
return curr.contract.sid
|
||||
|
||||
cpdef contract_at_offset(self, long_t sid, Py_ssize_t offset, int64_t start_cap):
|
||||
"""
|
||||
Get the sid which is the given sid plus the offset distance.
|
||||
An offset of 0 should be reflexive.
|
||||
"""
|
||||
cdef Py_ssize_t i
|
||||
curr = self.sid_to_contract[sid]
|
||||
i = 0
|
||||
while i < offset:
|
||||
if curr.next is None:
|
||||
return None
|
||||
curr = curr.next
|
||||
i += 1
|
||||
if curr.contract.start_date.value <= start_cap:
|
||||
return curr.contract.sid
|
||||
else:
|
||||
return None
|
||||
|
||||
cpdef long_t[:] active_chain(self, long_t starting_sid, long_t dt_value):
|
||||
curr = self.sid_to_contract[starting_sid]
|
||||
cdef list contracts = []
|
||||
|
||||
while curr is not None:
|
||||
if curr.contract.start_date.value <= dt_value:
|
||||
contracts.append(curr.contract.sid)
|
||||
curr = curr.next
|
||||
|
||||
return array(contracts, dtype='int64')
|
||||
|
||||
property start_date:
|
||||
def __get__(self):
|
||||
return Timestamp(self._start_date, tz='UTC')
|
||||
|
||||
property end_date:
|
||||
def __get__(self):
|
||||
return Timestamp(self._end_date, tz='UTC')
|
||||
@@ -1,18 +0,0 @@
|
||||
#
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# http://www.cmegroup.com/product-codes-listing/month-codes.html
|
||||
CME_CODE_TO_MONTH = dict(zip('FGHJKMNQUVXZ', range(1, 13)))
|
||||
MONTH_TO_CME_CODE = dict(zip(range(1, 13), 'FGHJKMNQUVXZ'))
|
||||
@@ -1,201 +0,0 @@
|
||||
#
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from six import with_metaclass
|
||||
|
||||
|
||||
class RollFinder(with_metaclass(ABCMeta, object)):
|
||||
"""
|
||||
Abstract base class for calculating when futures contracts are the active
|
||||
contract.
|
||||
"""
|
||||
@abstractmethod
|
||||
def _active_contract(self, oc, front, back, dt):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_contract_center(self, root_symbol, dt, offset):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
root_symbol : str
|
||||
The root symbol for the contract chain.
|
||||
dt : Timestamp
|
||||
The datetime for which to retrieve the current contract.
|
||||
offset : int
|
||||
The offset from the primary contract.
|
||||
0 is the primary, 1 is the secondary, etc.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Future
|
||||
The active future contract at the given dt.
|
||||
"""
|
||||
oc = self.asset_finder.get_ordered_contracts(root_symbol)
|
||||
session = self.trading_calendar.minute_to_session_label(dt)
|
||||
front = oc.contract_before_auto_close(session.value)
|
||||
back = oc.contract_at_offset(front, 1, dt.value)
|
||||
if back is None:
|
||||
return front
|
||||
primary = self._active_contract(oc, front, back, session)
|
||||
return oc.contract_at_offset(primary, offset, session.value)
|
||||
|
||||
def get_rolls(self, root_symbol, start, end, offset):
|
||||
"""
|
||||
Get the rolls, i.e. the session at which to hop from contract to
|
||||
contract in the chain.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
root_symbol : str
|
||||
The root symbol for which to calculate rolls.
|
||||
start : Timestamp
|
||||
Start of the date range.
|
||||
end : Timestamp
|
||||
End of the date range.
|
||||
offset : int
|
||||
Offset from the primary.
|
||||
|
||||
Returns
|
||||
-------
|
||||
rolls - list[tuple(sid, roll_date)]
|
||||
A list of rolls, where first value is the first active `sid`,
|
||||
and the `roll_date` on which to hop to the next contract.
|
||||
The last pair in the chain has a value of `None` since the roll
|
||||
is after the range.
|
||||
"""
|
||||
oc = self.asset_finder.get_ordered_contracts(root_symbol)
|
||||
front = self.get_contract_center(root_symbol, end, 0)
|
||||
back = oc.contract_at_offset(front, 1, end.value)
|
||||
if back is not None:
|
||||
end_session = self.trading_calendar.minute_to_session_label(end)
|
||||
first = self._active_contract(oc, front, back, end_session)
|
||||
else:
|
||||
first = front
|
||||
first_contract = oc.sid_to_contract[first]
|
||||
rolls = [((first_contract >> offset).contract.sid, None)]
|
||||
tc = self.trading_calendar
|
||||
sessions = tc.sessions_in_range(tc.minute_to_session_label(start),
|
||||
tc.minute_to_session_label(end))
|
||||
freq = sessions.freq
|
||||
if first == front:
|
||||
curr = first_contract << 1
|
||||
else:
|
||||
curr = first_contract << 2
|
||||
session = sessions[-1]
|
||||
|
||||
while session > start and curr is not None:
|
||||
front = curr.contract.sid
|
||||
back = rolls[0][0]
|
||||
prev_c = curr.prev
|
||||
while session > start:
|
||||
prev = session - freq
|
||||
if prev_c is not None:
|
||||
if prev < prev_c.contract.auto_close_date:
|
||||
break
|
||||
if back != self._active_contract(oc, front, back, prev):
|
||||
# TODO: Instead of listing each contract with its roll date
|
||||
# as tuples, create a series which maps every day to the
|
||||
# active contract on that day.
|
||||
rolls.insert(0, ((curr >> offset).contract.sid, session))
|
||||
break
|
||||
session = prev
|
||||
curr = curr.prev
|
||||
if curr is not None:
|
||||
session = curr.contract.auto_close_date
|
||||
return rolls
|
||||
|
||||
|
||||
class CalendarRollFinder(RollFinder):
|
||||
"""
|
||||
The CalendarRollFinder calculates contract rolls based purely on the
|
||||
contract's auto close date.
|
||||
"""
|
||||
|
||||
def __init__(self, trading_calendar, asset_finder):
|
||||
self.trading_calendar = trading_calendar
|
||||
self.asset_finder = asset_finder
|
||||
|
||||
def _active_contract(self, oc, front, back, dt):
|
||||
contract = oc.sid_to_contract[front].contract
|
||||
auto_close_date = contract.auto_close_date
|
||||
auto_closed = dt >= auto_close_date
|
||||
return back if auto_closed else front
|
||||
|
||||
|
||||
class VolumeRollFinder(RollFinder):
|
||||
"""
|
||||
The CalendarRollFinder calculates contract rolls based on when
|
||||
volume activity transfers from one contract to another.
|
||||
"""
|
||||
GRACE_DAYS = 7
|
||||
THRESHOLD = 0.10
|
||||
|
||||
def __init__(self, trading_calendar, asset_finder, session_reader):
|
||||
self.trading_calendar = trading_calendar
|
||||
self.asset_finder = asset_finder
|
||||
self.session_reader = session_reader
|
||||
|
||||
def _active_contract(self, oc, front, back, dt):
|
||||
"""
|
||||
Return the active contract based on the previous trading day's volume.
|
||||
|
||||
In the rare case that a double volume switch occurs we treat the first
|
||||
switch as the roll. Take the following case for example:
|
||||
|
||||
| +++++ _____
|
||||
| + __ / <--- 'G'
|
||||
| ++/++\++++/++
|
||||
| _/ \__/ +
|
||||
| / +
|
||||
| ____/ + <--- 'F'
|
||||
|_________|__|___|________
|
||||
a b c <--- Switches
|
||||
|
||||
We should treat 'a' as the roll date rather than 'c' because from the
|
||||
perspective of 'a', if a switch happens and we are pretty close to the
|
||||
auto-close date, we would probably assume it is time to roll. This
|
||||
means that for every date after 'a', `data.current(cf, 'contract')`
|
||||
should return the 'G' contract.
|
||||
"""
|
||||
tc = self.trading_calendar
|
||||
trading_day = tc.day
|
||||
prev = dt - trading_day
|
||||
get_value = self.session_reader.get_value
|
||||
front_vol = get_value(front, prev, 'volume')
|
||||
back_vol = get_value(back, prev, 'volume')
|
||||
front_contract = oc.sid_to_contract[front].contract
|
||||
|
||||
if dt >= front_contract.auto_close_date or back_vol > front_vol:
|
||||
return back
|
||||
|
||||
gap_start = \
|
||||
front_contract.auto_close_date - (trading_day * self.GRACE_DAYS)
|
||||
gap_end = prev - trading_day
|
||||
if dt < gap_start:
|
||||
return front
|
||||
|
||||
# If we are within `self.GRACE_DAYS` of the front contract's auto close
|
||||
# date, and a volume flip happened during that period, return the back
|
||||
# contract as the active one.
|
||||
sessions = tc.sessions_in_range(
|
||||
tc.minute_to_session_label(gap_start),
|
||||
tc.minute_to_session_label(gap_end),
|
||||
)
|
||||
for session in sessions:
|
||||
front_vol = get_value(front, session, 'volume')
|
||||
back_vol = get_value(back, session, 'volume')
|
||||
if back_vol > front_vol:
|
||||
return back
|
||||
return front
|
||||
@@ -1,263 +0,0 @@
|
||||
from itertools import product
|
||||
from string import ascii_uppercase
|
||||
|
||||
import pandas as pd
|
||||
from pandas.tseries.offsets import MonthBegin
|
||||
from six import iteritems
|
||||
|
||||
from .futures import CME_CODE_TO_MONTH
|
||||
|
||||
|
||||
def make_rotating_equity_info(num_assets,
|
||||
first_start,
|
||||
frequency,
|
||||
periods_between_starts,
|
||||
asset_lifetime):
|
||||
"""
|
||||
Create a DataFrame representing lifetimes of assets that are constantly
|
||||
rotating in and out of existence.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
num_assets : int
|
||||
How many assets to create.
|
||||
first_start : pd.Timestamp
|
||||
The start date for the first asset.
|
||||
frequency : str or pd.tseries.offsets.Offset (e.g. trading_day)
|
||||
Frequency used to interpret next two arguments.
|
||||
periods_between_starts : int
|
||||
Create a new asset every `frequency` * `periods_between_new`
|
||||
asset_lifetime : int
|
||||
Each asset exists for `frequency` * `asset_lifetime` days.
|
||||
|
||||
Returns
|
||||
-------
|
||||
info : pd.DataFrame
|
||||
DataFrame representing newly-created assets.
|
||||
"""
|
||||
return pd.DataFrame(
|
||||
{
|
||||
'symbol': [chr(ord('A') + i) for i in range(num_assets)],
|
||||
# Start a new asset every `periods_between_starts` days.
|
||||
'start_date': pd.date_range(
|
||||
first_start,
|
||||
freq=(periods_between_starts * frequency),
|
||||
periods=num_assets,
|
||||
),
|
||||
# Each asset lasts for `asset_lifetime` days.
|
||||
'end_date': pd.date_range(
|
||||
first_start + (asset_lifetime * frequency),
|
||||
freq=(periods_between_starts * frequency),
|
||||
periods=num_assets,
|
||||
),
|
||||
'exchange': 'TEST',
|
||||
'exchange_full': 'TEST FULL',
|
||||
},
|
||||
index=range(num_assets),
|
||||
)
|
||||
|
||||
|
||||
def make_simple_equity_info(sids,
|
||||
start_date,
|
||||
end_date,
|
||||
symbols=None):
|
||||
"""
|
||||
Create a DataFrame representing assets that exist for the full duration
|
||||
between `start_date` and `end_date`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sids : array-like of int
|
||||
start_date : pd.Timestamp, optional
|
||||
end_date : pd.Timestamp, optional
|
||||
symbols : list, optional
|
||||
Symbols to use for the assets.
|
||||
If not provided, symbols are generated from the sequence 'A', 'B', ...
|
||||
|
||||
Returns
|
||||
-------
|
||||
info : pd.DataFrame
|
||||
DataFrame representing newly-created assets.
|
||||
"""
|
||||
num_assets = len(sids)
|
||||
if symbols is None:
|
||||
symbols = list(ascii_uppercase[:num_assets])
|
||||
return pd.DataFrame(
|
||||
{
|
||||
'symbol': list(symbols),
|
||||
'start_date': pd.to_datetime([start_date] * num_assets),
|
||||
'end_date': pd.to_datetime([end_date] * num_assets),
|
||||
'exchange': 'TEST',
|
||||
'exchange_full': 'TEST FULL',
|
||||
},
|
||||
index=sids,
|
||||
columns=(
|
||||
'start_date',
|
||||
'end_date',
|
||||
'symbol',
|
||||
'exchange',
|
||||
'exchange_full',
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def make_jagged_equity_info(num_assets,
|
||||
start_date,
|
||||
first_end,
|
||||
frequency,
|
||||
periods_between_ends,
|
||||
auto_close_delta):
|
||||
"""
|
||||
Create a DataFrame representing assets that all begin at the same start
|
||||
date, but have cascading end dates.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
num_assets : int
|
||||
How many assets to create.
|
||||
start_date : pd.Timestamp
|
||||
The start date for all the assets.
|
||||
first_end : pd.Timestamp
|
||||
The date at which the first equity will end.
|
||||
frequency : str or pd.tseries.offsets.Offset (e.g. trading_day)
|
||||
Frequency used to interpret the next argument.
|
||||
periods_between_ends : int
|
||||
Starting after the first end date, end each asset every
|
||||
`frequency` * `periods_between_ends`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
info : pd.DataFrame
|
||||
DataFrame representing newly-created assets.
|
||||
"""
|
||||
frame = pd.DataFrame(
|
||||
{
|
||||
'symbol': [chr(ord('A') + i) for i in range(num_assets)],
|
||||
'start_date': start_date,
|
||||
'end_date': pd.date_range(
|
||||
first_end,
|
||||
freq=(periods_between_ends * frequency),
|
||||
periods=num_assets,
|
||||
),
|
||||
'exchange': 'TEST',
|
||||
'exchange_full': 'TEST FULL',
|
||||
},
|
||||
index=range(num_assets),
|
||||
)
|
||||
|
||||
# Explicitly pass None to disable setting the auto_close_date column.
|
||||
if auto_close_delta is not None:
|
||||
frame['auto_close_date'] = frame['end_date'] + auto_close_delta
|
||||
|
||||
return frame
|
||||
|
||||
|
||||
def make_future_info(first_sid,
|
||||
root_symbols,
|
||||
years,
|
||||
notice_date_func,
|
||||
expiration_date_func,
|
||||
start_date_func,
|
||||
month_codes=None):
|
||||
"""
|
||||
Create a DataFrame representing futures for `root_symbols` during `year`.
|
||||
|
||||
Generates a contract per triple of (symbol, year, month) supplied to
|
||||
`root_symbols`, `years`, and `month_codes`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
first_sid : int
|
||||
The first sid to use for assigning sids to the created contracts.
|
||||
root_symbols : list[str]
|
||||
A list of root symbols for which to create futures.
|
||||
years : list[int or str]
|
||||
Years (e.g. 2014), for which to produce individual contracts.
|
||||
notice_date_func : (Timestamp) -> Timestamp
|
||||
Function to generate notice dates from first of the month associated
|
||||
with asset month code. Return NaT to simulate futures with no notice
|
||||
date.
|
||||
expiration_date_func : (Timestamp) -> Timestamp
|
||||
Function to generate expiration dates from first of the month
|
||||
associated with asset month code.
|
||||
start_date_func : (Timestamp) -> Timestamp, optional
|
||||
Function to generate start dates from first of the month associated
|
||||
with each asset month code. Defaults to a start_date one year prior
|
||||
to the month_code date.
|
||||
month_codes : dict[str -> [1..12]], optional
|
||||
Dictionary of month codes for which to create contracts. Entries
|
||||
should be strings mapped to values from 1 (January) to 12 (December).
|
||||
Default is catalyst.futures.CME_CODE_TO_MONTH
|
||||
|
||||
Returns
|
||||
-------
|
||||
futures_info : pd.DataFrame
|
||||
DataFrame of futures data suitable for passing to an AssetDBWriter.
|
||||
"""
|
||||
if month_codes is None:
|
||||
month_codes = CME_CODE_TO_MONTH
|
||||
|
||||
year_strs = list(map(str, years))
|
||||
years = [pd.Timestamp(s, tz='UTC') for s in year_strs]
|
||||
|
||||
# Pairs of string/date like ('K06', 2006-05-01)
|
||||
contract_suffix_to_beginning_of_month = tuple(
|
||||
(month_code + year_str[-2:], year + MonthBegin(month_num))
|
||||
for ((year, year_str), (month_code, month_num))
|
||||
in product(
|
||||
zip(years, year_strs),
|
||||
iteritems(month_codes),
|
||||
)
|
||||
)
|
||||
|
||||
contracts = []
|
||||
parts = product(root_symbols, contract_suffix_to_beginning_of_month)
|
||||
for sid, (root_sym, (suffix, month_begin)) in enumerate(parts, first_sid):
|
||||
contracts.append({
|
||||
'sid': sid,
|
||||
'root_symbol': root_sym,
|
||||
'symbol': root_sym + suffix,
|
||||
'start_date': start_date_func(month_begin),
|
||||
'notice_date': notice_date_func(month_begin),
|
||||
'expiration_date': notice_date_func(month_begin),
|
||||
'multiplier': 500,
|
||||
'exchange': "TEST",
|
||||
'exchange_full': 'TEST FULL',
|
||||
})
|
||||
return pd.DataFrame.from_records(contracts, index='sid')
|
||||
|
||||
|
||||
def make_commodity_future_info(first_sid,
|
||||
root_symbols,
|
||||
years,
|
||||
month_codes=None):
|
||||
"""
|
||||
Make futures testing data that simulates the notice/expiration date
|
||||
behavior of physical commodities like oil.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
first_sid : int
|
||||
root_symbols : list[str]
|
||||
years : list[int]
|
||||
month_codes : dict[str -> int]
|
||||
|
||||
Expiration dates are on the 20th of the month prior to the month code.
|
||||
Notice dates are are on the 20th two months prior to the month code.
|
||||
Start dates are one year before the contract month.
|
||||
|
||||
See Also
|
||||
--------
|
||||
make_future_info
|
||||
"""
|
||||
nineteen_days = pd.Timedelta(days=19)
|
||||
one_year = pd.Timedelta(days=365)
|
||||
return make_future_info(
|
||||
first_sid=first_sid,
|
||||
root_symbols=root_symbols,
|
||||
years=years,
|
||||
notice_date_func=lambda dt: dt - MonthBegin(2) + nineteen_days,
|
||||
expiration_date_func=lambda dt: dt - MonthBegin(1) + nineteen_days,
|
||||
start_date_func=lambda dt: dt - one_year,
|
||||
month_codes=month_codes,
|
||||
)
|
||||
@@ -1,18 +0,0 @@
|
||||
#
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
class ZiplineDeprecationWarning(DeprecationWarning):
|
||||
pass
|
||||
@@ -1,294 +0,0 @@
|
||||
import json, time, csv
|
||||
from datetime import datetime
|
||||
import pandas as pd
|
||||
import os, time, shutil, requests, logbook
|
||||
from catalyst.exchange.exchange_utils import get_exchange_symbols_filename
|
||||
|
||||
|
||||
DT_START = int(time.mktime(datetime(2010, 1, 1, 0, 0).timetuple()))
|
||||
DT_END = int(time.time())
|
||||
CSV_OUT_FOLDER = '/var/tmp/catalyst/data/poloniex/'
|
||||
CSV_OUT_FOLDER = '/Volumes/enigma/data/poloniex/'
|
||||
CONN_RETRIES = 2
|
||||
|
||||
logbook.StderrHandler().push_application()
|
||||
log = logbook.Logger(__name__)
|
||||
|
||||
class PoloniexCurator(object):
|
||||
'''
|
||||
OHLCV data feed generator for crypto data. Based on Poloniex market data
|
||||
'''
|
||||
|
||||
_api_path = 'https://poloniex.com/public?'
|
||||
currency_pairs = []
|
||||
|
||||
def __init__(self):
|
||||
if not os.path.exists(CSV_OUT_FOLDER):
|
||||
try:
|
||||
os.makedirs(CSV_OUT_FOLDER)
|
||||
except Exception as e:
|
||||
log.error('Failed to create data folder: %s' % CSV_OUT_FOLDER)
|
||||
log.exception(e)
|
||||
|
||||
'''
|
||||
Retrieves and returns all currency pairs from the exchange
|
||||
'''
|
||||
def get_currency_pairs(self):
|
||||
url = self._api_path + 'command=returnTicker'
|
||||
|
||||
try:
|
||||
response = requests.get(url)
|
||||
except Exception as e:
|
||||
log.error('Failed to retrieve list of currency pairs')
|
||||
log.exception(e)
|
||||
return None
|
||||
|
||||
data = response.json()
|
||||
self.currency_pairs = []
|
||||
for ticker in data:
|
||||
self.currency_pairs.append(ticker)
|
||||
self.currency_pairs.sort()
|
||||
|
||||
log.debug('Currency pairs retrieved successfully: %d' % (len(self.currency_pairs)))
|
||||
|
||||
|
||||
'''
|
||||
Helper function that reads tradeID and date fields from CSV readline
|
||||
'''
|
||||
def _retrieve_tradeID_date(self, row):
|
||||
tId = int(row.split(',')[0])
|
||||
d = pd.to_datetime( row.split(',')[1], infer_datetime_format=True).value // 10 ** 9
|
||||
return tId, d
|
||||
|
||||
'''
|
||||
Retrieves TradeHistory from exchange for a given currencyPair between start and end dates.
|
||||
If no start date is provided, uses a system-wide one (beginning of time for cryptotrading)
|
||||
If no end date is provided, 'now' is used
|
||||
Stores results in CSV file on disk.
|
||||
This function is called recursively to work around the limitations imposed by the provider API.
|
||||
'''
|
||||
def retrieve_trade_history(self, currencyPair, start=DT_START, end=DT_END, temp=None):
|
||||
csv_fn = CSV_OUT_FOLDER + 'crypto_trades-' + currencyPair + '.csv'
|
||||
|
||||
'''
|
||||
Check what data we already have on disk, reading first and last lines from file.
|
||||
Data is stored on file from NEWEST to OLDEST.
|
||||
'''
|
||||
try:
|
||||
with open(csv_fn, 'ab+') as f:
|
||||
f.seek(0, os.SEEK_END)
|
||||
if(f.tell() > 2): # First check file is not zero size
|
||||
f.seek(0) # Go to the beginning to read first line
|
||||
last_tradeID, end_file = self._retrieve_tradeID_date(f.readline())
|
||||
f.seek(-2, os.SEEK_END) # Jump to the second last byte.
|
||||
while f.read(1) != b"\n": # Until EOL is found...
|
||||
f.seek(-2, os.SEEK_CUR) # ...jump back the read byte plus one more.
|
||||
first_tradeID, start_file = self._retrieve_tradeID_date(f.readline())
|
||||
|
||||
if( first_tradeID == 1 and end_file + 3600 > DT_END ):
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
log.error('Error opening file: %s' % csv_fn)
|
||||
log.exception(e)
|
||||
|
||||
'''
|
||||
Poloniex API limits querying TradeHistory to intervals smaller than 1 month,
|
||||
so we make sure that start date is never more than 1 month apart from end date
|
||||
'''
|
||||
if( end - start > 2419200 ): # 60 s/min * 60 min/hr * 24 hr/day * 28 days
|
||||
newstart = end - 2419200
|
||||
else:
|
||||
newstart = start
|
||||
|
||||
log.debug(currencyPair+': Retrieving from '+str(newstart)+' to '+str(end) +'\t '
|
||||
+ time.ctime(newstart) + ' - '+ time.ctime(end))
|
||||
|
||||
url = self._api_path + 'command=returnTradeHistory¤cyPair=' + currencyPair + '&start=' + str(newstart) + '&end=' + str(end)
|
||||
|
||||
try:
|
||||
response = requests.get(url)
|
||||
except Exception as e:
|
||||
log.error('Failed to retrieve trade history data for %s' % currencyPair)
|
||||
log.exception(e)
|
||||
return None
|
||||
else:
|
||||
if isinstance(response.json(), dict) and response.json()['error']:
|
||||
log.error('Failed to to retrieve trade history data for %s: %s' % (currencyPair,response.json()['error']))
|
||||
exit(1)
|
||||
|
||||
'''
|
||||
If we get to transactionId == 1, and we already have that on disk,
|
||||
we got to the end of TradeHistory for this coin.
|
||||
'''
|
||||
if('first_tradeID' in locals() and response.json()[-1]['tradeID'] == first_tradeID):
|
||||
return
|
||||
|
||||
'''
|
||||
There are primarily two scenarios:
|
||||
a) There is newer data available that we need to add at the beginning
|
||||
of the file. We'll retrieve all what we need until we get to what
|
||||
we already have, writing it to a temporary file; and we will write
|
||||
that at the beginning of our existing file.
|
||||
b) We are going back in time, appending at the end of our existing
|
||||
TradeHistory until the first transaction for this currencyPair
|
||||
'''
|
||||
try:
|
||||
if( 'end_file' in locals() and end_file + 3600 < end):
|
||||
if (temp is None):
|
||||
temp = os.tmpfile()
|
||||
tempcsv = csv.writer(temp)
|
||||
for item in response.json():
|
||||
if( item['tradeID'] <= last_tradeID ):
|
||||
continue
|
||||
tempcsv.writerow([
|
||||
item['tradeID'],
|
||||
item['date'],
|
||||
item['type'],
|
||||
item['rate'],
|
||||
item['amount'],
|
||||
item['total'],
|
||||
item['globalTradeID']
|
||||
])
|
||||
if( response.json()[-1]['tradeID'] > last_tradeID ):
|
||||
end = pd.to_datetime( response.json()[-1]['date'], infer_datetime_format=True).value // 10 ** 9
|
||||
self.retrieve_trade_history(currencyPair, start, end, temp=temp)
|
||||
else:
|
||||
with open(csv_fn,'rb+') as f:
|
||||
shutil.copyfileobj(f,temp)
|
||||
f.seek(0)
|
||||
temp.seek(0)
|
||||
shutil.copyfileobj(temp,f)
|
||||
temp.close()
|
||||
end = start_file
|
||||
else:
|
||||
with open(csv_fn, 'ab') as csvfile:
|
||||
csvwriter = csv.writer(csvfile)
|
||||
for item in response.json():
|
||||
if( 'first_tradeID' in locals() and item['tradeID'] >= first_tradeID ):
|
||||
continue
|
||||
csvwriter.writerow([
|
||||
item['tradeID'],
|
||||
item['date'],
|
||||
item['type'],
|
||||
item['rate'],
|
||||
item['amount'],
|
||||
item['total'],
|
||||
item['globalTradeID']
|
||||
])
|
||||
end = pd.to_datetime( response.json()[-1]['date'], infer_datetime_format=True).value // 10 ** 9
|
||||
|
||||
except Exception as e:
|
||||
log.error('Error opening %s' % csv_fn)
|
||||
log.exception(e)
|
||||
|
||||
'''
|
||||
If we got here, we aren't done yet. Call recursively with 'end' times
|
||||
that go sequentially back in time.
|
||||
'''
|
||||
self.retrieve_trade_history(currencyPair, start, end)
|
||||
|
||||
|
||||
'''
|
||||
Generates OHLCV dataframe from a dataframe containing all TradeHistory
|
||||
by resampling with 1-minute period
|
||||
'''
|
||||
def generate_ohlcv(self, df):
|
||||
df.set_index('date', inplace=True) # Index by date
|
||||
vol = df['total'].to_frame('volume') # Will deal with vol separately, as ohlc() messes it up
|
||||
df.drop('total', axis=1, inplace=True) # Drop volume data from dataframe
|
||||
ohlc = df.resample('T').ohlc() # Resample OHLC in 1min bins
|
||||
ohlc.columns = ohlc.columns.map(lambda t: t[1]) # Raname columns by dropping 'rate'
|
||||
closes = ohlc['close'].fillna(method='pad') # Pad forward missing 'close'
|
||||
ohlc = ohlc.apply(lambda x: x.fillna(closes)) # Fill N/A with last close
|
||||
vol = vol.resample('T').sum().fillna(0) # Add volumes by bin
|
||||
ohlcv = pd.concat([ohlc,vol], axis=1) # Concatenate OHLC + Volume
|
||||
return ohlcv
|
||||
|
||||
|
||||
'''
|
||||
Generates OHLCV data file with 1minute bars from TradeHistory on disk
|
||||
'''
|
||||
def write_ohlcv_file(self, currencyPair):
|
||||
csv_trades = CSV_OUT_FOLDER + 'crypto_trades-' + currencyPair + '.csv'
|
||||
csv_1min = CSV_OUT_FOLDER + 'crypto_1min-' + currencyPair + '.csv'
|
||||
if( os.path.isfile(csv_1min) ):
|
||||
log.debug(currencyPair+': 1min data already present. Delete the file if you want to rebuild it.')
|
||||
else:
|
||||
df = pd.read_csv(csv_trades, names=['tradeID','date','type','rate','amount','total','globalTradeID'],
|
||||
dtype = {'tradeID': int, 'date': str, 'type': str, 'rate': float, 'amount': float, 'total': float, 'globalTradeID': int } )
|
||||
df.drop(['tradeID','type','amount','globalTradeID'], axis=1, inplace=True)
|
||||
df['date'] = pd.to_datetime(df['date'], infer_datetime_format=True)
|
||||
ohlcv = self.generate_ohlcv(df)
|
||||
try:
|
||||
with open(csv_1min, 'ab') as csvfile:
|
||||
csvwriter = csv.writer(csvfile)
|
||||
for item in ohlcv.itertuples():
|
||||
if item.Index == 0:
|
||||
continue
|
||||
csvwriter.writerow([
|
||||
item.Index.value // 10 ** 9,
|
||||
item.open,
|
||||
item.high,
|
||||
item.low,
|
||||
item.close,
|
||||
item.volume,
|
||||
])
|
||||
except Exception as e:
|
||||
log.error('Error opening %s' % csv_fn)
|
||||
log.exception(e)
|
||||
log.debug(currencyPair+': Generated 1min OHLCV data.')
|
||||
|
||||
|
||||
'''
|
||||
Returns a data frame for a given currencyPair from data on disk
|
||||
'''
|
||||
def onemin_to_dataframe(self, currencyPair, start, end):
|
||||
csv_fn = CSV_OUT_FOLDER + 'crypto_1min-' + currencyPair + '.csv'
|
||||
df = pd.read_csv(csv_fn, names=['date', 'open', 'high', 'low', 'close', 'volume'])
|
||||
df['date'] = pd.to_datetime(df['date'],unit='s')
|
||||
df.set_index('date', inplace=True)
|
||||
return df[start : end]
|
||||
|
||||
'''
|
||||
Generates a symbols.json file with corresponding start_date for each currencyPair
|
||||
'''
|
||||
def generate_symbols_json(self, filename=None):
|
||||
symbol_map = {}
|
||||
|
||||
if(filename is None):
|
||||
filename = get_exchange_symbols_filename('poloniex')
|
||||
|
||||
with open(filename, 'w') as symbols:
|
||||
for currencyPair in self.currency_pairs:
|
||||
start = None
|
||||
csv_fn = CSV_OUT_FOLDER + 'crypto_trades-' + currencyPair + '.csv'
|
||||
with open(csv_fn, 'r') as f:
|
||||
f.seek(0, os.SEEK_END)
|
||||
if(f.tell() > 2): # First check file is not zero size
|
||||
f.seek(-2, os.SEEK_END) # Jump to the second last byte.
|
||||
while f.read(1) != b"\n": # Until EOL is found...
|
||||
f.seek(-2, os.SEEK_CUR) # ...jump back the read byte plus one more.
|
||||
start = pd.to_datetime( f.readline().split(',')[1], infer_datetime_format=True)
|
||||
|
||||
if(start is None):
|
||||
start = time.gmtime()
|
||||
base, market = currencyPair.lower().split('_')
|
||||
symbol = '{market}_{base}'.format( market=market, base=base )
|
||||
symbol_map[currencyPair] = dict(
|
||||
symbol = symbol,
|
||||
start_date = start.strftime("%Y-%m-%d")
|
||||
)
|
||||
json.dump(symbol_map, symbols, sort_keys=True, indent=2, separators=(',',':'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pc = PoloniexCurator()
|
||||
pc.get_currency_pairs()
|
||||
#pc.generate_symbols_json()
|
||||
|
||||
for currencyPair in pc.currency_pairs:
|
||||
pc.retrieve_trade_history(currencyPair)
|
||||
pc.write_ohlcv_file(currencyPair)
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
from . import loader
|
||||
from .loader import (
|
||||
load_from_yahoo,
|
||||
load_bars_from_yahoo,
|
||||
load_prices_from_csv,
|
||||
load_prices_from_csv_folder,
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
'load_bars_from_yahoo',
|
||||
'load_from_yahoo',
|
||||
'load_prices_from_csv',
|
||||
'load_prices_from_csv_folder',
|
||||
'loader',
|
||||
]
|
||||
@@ -1,303 +0,0 @@
|
||||
#
|
||||
# Copyright 2015 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from cpython cimport (
|
||||
PyDict_Contains,
|
||||
PySet_Add,
|
||||
)
|
||||
|
||||
from numpy import (
|
||||
int64,
|
||||
uint32,
|
||||
zeros,
|
||||
)
|
||||
from numpy cimport int64_t, ndarray
|
||||
from pandas import Timestamp
|
||||
|
||||
ctypedef object Timestamp_t
|
||||
ctypedef object DatetimeIndex_t
|
||||
ctypedef object Int64Index_t
|
||||
|
||||
from catalyst.lib.adjustment import Float64Multiply
|
||||
from catalyst.assets.asset_writer import (
|
||||
SQLITE_MAX_VARIABLE_NUMBER as SQLITE_MAX_IN_STATEMENT,
|
||||
)
|
||||
from catalyst.utils.pandas_utils import timedelta_to_integral_seconds
|
||||
|
||||
|
||||
_SID_QUERY_TEMPLATE = """
|
||||
SELECT DISTINCT sid FROM {0}
|
||||
WHERE effective_date >= ? AND effective_date <= ?
|
||||
"""
|
||||
cdef dict SID_QUERIES = {
|
||||
tablename: _SID_QUERY_TEMPLATE.format(tablename)
|
||||
for tablename in ('splits', 'dividends', 'mergers')
|
||||
}
|
||||
|
||||
ADJ_QUERY_TEMPLATE = """
|
||||
SELECT sid, ratio, effective_date
|
||||
FROM {0}
|
||||
WHERE sid IN ({1}) AND effective_date >= {2} AND effective_date <= {3}
|
||||
"""
|
||||
|
||||
EPOCH = Timestamp(0, tz='UTC')
|
||||
|
||||
cdef set _get_sids_from_table(object db,
|
||||
str tablename,
|
||||
int start_date,
|
||||
int end_date):
|
||||
"""
|
||||
Get the unique sids for all adjustments between start_date and end_date
|
||||
from table `tablename`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
db : sqlite3.connection
|
||||
tablename : str
|
||||
start_date : int (seconds since epoch)
|
||||
end_date : int (seconds since epoch)
|
||||
|
||||
Returns
|
||||
-------
|
||||
sids : set
|
||||
Set of sets
|
||||
"""
|
||||
|
||||
cdef object cursor = db.execute(
|
||||
SID_QUERIES[tablename],
|
||||
(start_date, end_date),
|
||||
)
|
||||
cdef set out = set()
|
||||
cdef tuple result
|
||||
for result in cursor.fetchall():
|
||||
PySet_Add(out, result[0])
|
||||
return out
|
||||
|
||||
|
||||
cdef set _get_split_sids(object db, int start_date, int end_date):
|
||||
return _get_sids_from_table(db, 'splits', start_date, end_date)
|
||||
|
||||
|
||||
cdef set _get_merger_sids(object db, int start_date, int end_date):
|
||||
return _get_sids_from_table(db, 'mergers', start_date, end_date)
|
||||
|
||||
|
||||
cdef set _get_dividend_sids(object db, int start_date, int end_date):
|
||||
return _get_sids_from_table(db, 'dividends', start_date, end_date)
|
||||
|
||||
|
||||
cdef _adjustments(object adjustments_db,
|
||||
set split_sids,
|
||||
set merger_sids,
|
||||
set dividends_sids,
|
||||
int start_date,
|
||||
int end_date,
|
||||
Int64Index_t assets):
|
||||
|
||||
c = adjustments_db.cursor()
|
||||
|
||||
splits_to_query = [str(a) for a in assets if a in split_sids]
|
||||
splits_results = []
|
||||
while splits_to_query:
|
||||
query_len = min(len(splits_to_query), SQLITE_MAX_IN_STATEMENT)
|
||||
query_assets = splits_to_query[:query_len]
|
||||
t= [str(a) for a in query_assets]
|
||||
statement = ADJ_QUERY_TEMPLATE.format('splits',
|
||||
",".join(['?' for _ in query_assets]), start_date, end_date)
|
||||
c.execute(statement, t)
|
||||
splits_to_query = splits_to_query[query_len:]
|
||||
splits_results.extend(c.fetchall())
|
||||
|
||||
mergers_to_query = [str(a) for a in assets if a in merger_sids]
|
||||
mergers_results = []
|
||||
while mergers_to_query:
|
||||
query_len = min(len(mergers_to_query), SQLITE_MAX_IN_STATEMENT)
|
||||
query_assets = mergers_to_query[:query_len]
|
||||
t= [str(a) for a in query_assets]
|
||||
statement = ADJ_QUERY_TEMPLATE.format('mergers',
|
||||
",".join(['?' for _ in query_assets]), start_date, end_date)
|
||||
c.execute(statement, t)
|
||||
mergers_to_query = mergers_to_query[query_len:]
|
||||
mergers_results.extend(c.fetchall())
|
||||
|
||||
dividends_to_query = [str(a) for a in assets if a in dividends_sids]
|
||||
dividends_results = []
|
||||
while dividends_to_query:
|
||||
query_len = min(len(dividends_to_query), SQLITE_MAX_IN_STATEMENT)
|
||||
query_assets = dividends_to_query[:query_len]
|
||||
t= [str(a) for a in query_assets]
|
||||
statement = ADJ_QUERY_TEMPLATE.format('dividends',
|
||||
",".join(['?' for _ in query_assets]), start_date, end_date)
|
||||
c.execute(statement, t)
|
||||
dividends_to_query = dividends_to_query[query_len:]
|
||||
dividends_results.extend(c.fetchall())
|
||||
|
||||
return splits_results, mergers_results, dividends_results
|
||||
|
||||
|
||||
cpdef load_adjustments_from_sqlite(object adjustments_db, # sqlite3.Connection
|
||||
list columns,
|
||||
DatetimeIndex_t dates,
|
||||
Int64Index_t assets):
|
||||
"""
|
||||
Load a dictionary of Adjustment objects from adjustments_db
|
||||
|
||||
Parameters
|
||||
----------
|
||||
adjustments_db : sqlite3.Connection
|
||||
Connection to a sqlite3 table in the format written by
|
||||
SQLiteAdjustmentWriter.
|
||||
columns : list[str]
|
||||
List of column names for which adjustments are needed.
|
||||
dates : pd.DatetimeIndex
|
||||
Dates for which adjustments are needed
|
||||
assets : pd.Int64Index
|
||||
Assets for which adjustments are needed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
adjustments : list[dict[int -> Adjustment]]
|
||||
A list of mappings from index to adjustment objects to apply at that
|
||||
index.
|
||||
"""
|
||||
|
||||
cdef int start_date = timedelta_to_integral_seconds(dates[0] - EPOCH)
|
||||
cdef int end_date = timedelta_to_integral_seconds(dates[-1] - EPOCH)
|
||||
|
||||
cdef set split_sids = _get_split_sids(
|
||||
adjustments_db,
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
cdef set merger_sids = _get_merger_sids(
|
||||
adjustments_db,
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
cdef set dividend_sids = _get_dividend_sids(
|
||||
adjustments_db,
|
||||
start_date,
|
||||
end_date,
|
||||
)
|
||||
|
||||
cdef:
|
||||
list splits, mergers, dividends
|
||||
splits, mergers, dividends = _adjustments(
|
||||
adjustments_db,
|
||||
split_sids,
|
||||
merger_sids,
|
||||
dividend_sids,
|
||||
start_date,
|
||||
end_date,
|
||||
assets,
|
||||
)
|
||||
|
||||
cdef list results = [{} for column in columns]
|
||||
cdef dict asset_ixs = {} # Cache sid lookups here.
|
||||
cdef dict date_ixs = {}
|
||||
cdef:
|
||||
int i
|
||||
int dt
|
||||
int sid
|
||||
double ratio
|
||||
int eff_date
|
||||
int date_loc
|
||||
Py_ssize_t asset_ix
|
||||
dict col_adjustments
|
||||
|
||||
cdef ndarray[int64_t, ndim=1] _dates_seconds = \
|
||||
dates.values.astype('datetime64[s]').view(int64)
|
||||
|
||||
# Pre-populate date index cache.
|
||||
for i, dt in enumerate(_dates_seconds):
|
||||
date_ixs[dt] = i
|
||||
|
||||
# splits affect prices and volumes, volumes is the inverse
|
||||
for sid, ratio, eff_date in splits:
|
||||
if eff_date < start_date:
|
||||
continue
|
||||
|
||||
date_loc = _lookup_dt(date_ixs, eff_date, _dates_seconds)
|
||||
|
||||
if not PyDict_Contains(asset_ixs, sid):
|
||||
asset_ixs[sid] = assets.get_loc(sid)
|
||||
asset_ix = asset_ixs[sid]
|
||||
|
||||
price_adj = Float64Multiply(0, date_loc, asset_ix, asset_ix, ratio)
|
||||
for i, column in enumerate(columns):
|
||||
col_adjustments = results[i]
|
||||
if column != 'volume':
|
||||
try:
|
||||
col_adjustments[date_loc].append(price_adj)
|
||||
except KeyError:
|
||||
col_adjustments[date_loc] = [price_adj]
|
||||
else:
|
||||
volume_adj = Float64Multiply(
|
||||
0, date_loc, asset_ix, asset_ix, 1.0 / ratio
|
||||
)
|
||||
try:
|
||||
col_adjustments[date_loc].append(volume_adj)
|
||||
except KeyError:
|
||||
col_adjustments[date_loc] = [volume_adj]
|
||||
|
||||
# mergers affect prices only
|
||||
for sid, ratio, eff_date in mergers:
|
||||
if eff_date < start_date:
|
||||
continue
|
||||
|
||||
date_loc = _lookup_dt(date_ixs, eff_date, _dates_seconds)
|
||||
|
||||
if not PyDict_Contains(asset_ixs, sid):
|
||||
asset_ixs[sid] = assets.get_loc(sid)
|
||||
asset_ix = asset_ixs[sid]
|
||||
|
||||
adj = Float64Multiply(0, date_loc, asset_ix, asset_ix, ratio)
|
||||
for i, column in enumerate(columns):
|
||||
col_adjustments = results[i]
|
||||
if column != 'volume':
|
||||
try:
|
||||
col_adjustments[date_loc].append(adj)
|
||||
except KeyError:
|
||||
col_adjustments[date_loc] = [adj]
|
||||
|
||||
# dividends affect prices only
|
||||
for sid, ratio, eff_date in dividends:
|
||||
if eff_date < start_date:
|
||||
continue
|
||||
|
||||
date_loc = _lookup_dt(date_ixs, eff_date, _dates_seconds)
|
||||
|
||||
if not PyDict_Contains(asset_ixs, sid):
|
||||
asset_ixs[sid] = assets.get_loc(sid)
|
||||
asset_ix = asset_ixs[sid]
|
||||
|
||||
adj = Float64Multiply(0, date_loc, asset_ix, asset_ix, ratio)
|
||||
for i, column in enumerate(columns):
|
||||
col_adjustments = results[i]
|
||||
if column != 'volume':
|
||||
try:
|
||||
col_adjustments[date_loc].append(adj)
|
||||
except KeyError:
|
||||
col_adjustments[date_loc] = [adj]
|
||||
|
||||
return results
|
||||
|
||||
|
||||
cdef _lookup_dt(dict dt_cache,
|
||||
int dt,
|
||||
ndarray[int64_t, ndim=1] fallback):
|
||||
|
||||
if not PyDict_Contains(dt_cache, dt):
|
||||
dt_cache[dt] = fallback.searchsorted(dt, side='right')
|
||||
return dt_cache[dt]
|
||||
@@ -1,227 +0,0 @@
|
||||
#
|
||||
# Copyright 2015 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import bcolz
|
||||
cimport cython
|
||||
from cpython cimport bool
|
||||
|
||||
from numpy import (
|
||||
array,
|
||||
float64,
|
||||
intp,
|
||||
uint32,
|
||||
uint64,
|
||||
zeros,
|
||||
)
|
||||
from numpy cimport (
|
||||
float64_t,
|
||||
intp_t,
|
||||
ndarray,
|
||||
uint32_t,
|
||||
uint64_t,
|
||||
uint8_t,
|
||||
)
|
||||
from numpy.math cimport NAN
|
||||
|
||||
ctypedef object carray_t
|
||||
ctypedef object ctable_t
|
||||
ctypedef object Timestamp_t
|
||||
ctypedef object DatetimeIndex_t
|
||||
ctypedef object Int64Index_t
|
||||
|
||||
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cpdef _compute_row_slices(dict asset_starts_absolute,
|
||||
dict asset_ends_absolute,
|
||||
dict asset_starts_calendar,
|
||||
intp_t query_start,
|
||||
intp_t query_end,
|
||||
Int64Index_t requested_assets):
|
||||
"""
|
||||
Core indexing functionality for loading raw data from bcolz.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset_starts_absolute : dict
|
||||
Dictionary containing the index of the first row of each asset in the
|
||||
bcolz file from which we will query.
|
||||
|
||||
asset_ends_absolute : dict
|
||||
Dictionary containing the index of the last row of each asset in the
|
||||
bcolz file from which we will query.
|
||||
|
||||
asset_starts_calendar : dict
|
||||
Dictionary containing the index of in our calendar corresponding to the
|
||||
start date of each asset
|
||||
|
||||
query_start : intp
|
||||
query_end : intp
|
||||
Start and end indices in our calendar of the dates for which we're
|
||||
querying.
|
||||
|
||||
requested_assets : pandas.Int64Index
|
||||
The assets for which we want to load data.
|
||||
|
||||
For each asset in requested assets, computes three values:
|
||||
1.) The index in the raw bcolz data of first row to load.
|
||||
2.) The index in the raw bcolz data of the last row to load.
|
||||
3.) The index in the dates of our query corresponding to the first row for
|
||||
each asset. This is non-zero iff the asset's lifetime begins partway
|
||||
through the requested query dates.
|
||||
|
||||
Returns
|
||||
-------
|
||||
first_rows, last_rows, offsets : 3-tuple of ndarrays
|
||||
"""
|
||||
cdef:
|
||||
intp_t nassets = len(requested_assets)
|
||||
|
||||
# For each sid, we need to compute the following:
|
||||
ndarray[dtype=intp_t, ndim=1] first_row_a = zeros(nassets, dtype=intp)
|
||||
ndarray[dtype=intp_t, ndim=1] last_row_a = zeros(nassets, dtype=intp)
|
||||
ndarray[dtype=intp_t, ndim=1] offset_a = zeros(nassets, dtype=intp)
|
||||
|
||||
# Loop variables.
|
||||
intp_t i
|
||||
intp_t asset
|
||||
intp_t asset_start_data
|
||||
intp_t asset_end_data
|
||||
intp_t asset_start_calendar
|
||||
intp_t asset_end_calendar
|
||||
|
||||
for i, asset in enumerate(requested_assets):
|
||||
asset_start_data = asset_starts_absolute[asset]
|
||||
asset_end_data = asset_ends_absolute[asset]
|
||||
asset_start_calendar = asset_starts_calendar[asset]
|
||||
asset_end_calendar = (
|
||||
asset_start_calendar + (asset_end_data - asset_start_data)
|
||||
)
|
||||
|
||||
# If the asset started during the query, then start with the asset's
|
||||
# first row.
|
||||
# Otherwise start with the asset's first row + the number of rows
|
||||
# before the query on which the asset existed.
|
||||
first_row_a[i] = (
|
||||
asset_start_data + max(0, (query_start - asset_start_calendar))
|
||||
)
|
||||
# If the asset ended during the query, the end with the asset's last
|
||||
# row.
|
||||
# Otherwise, end with the asset's last row minus the number of rows
|
||||
# after the query for which the asset
|
||||
last_row_a[i] = (
|
||||
asset_end_data - max(0, asset_end_calendar - query_end)
|
||||
)
|
||||
# If the asset existed on or before the query, no offset.
|
||||
# Otherwise, offset by the number of rows in the query in which the
|
||||
# asset did not yet exist.
|
||||
offset_a[i] = max(0, asset_start_calendar - query_start)
|
||||
|
||||
return first_row_a, last_row_a, offset_a
|
||||
|
||||
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cpdef _read_bcolz_data(ctable_t table,
|
||||
tuple shape,
|
||||
list columns,
|
||||
intp_t[:] first_rows,
|
||||
intp_t[:] last_rows,
|
||||
intp_t[:] offsets,
|
||||
bool read_all):
|
||||
"""
|
||||
Load raw bcolz data for the given columns and indices.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
table : bcolz.ctable
|
||||
The table from which to read.
|
||||
shape : tuple (length 2)
|
||||
The shape of the expected output arrays.
|
||||
columns : list[str]
|
||||
List of column names to read.
|
||||
|
||||
first_rows : ndarray[intp]
|
||||
last_rows : ndarray[intp]
|
||||
offsets : ndarray[intp
|
||||
Arrays in the format returned by _compute_row_slices.
|
||||
read_all : bool
|
||||
Whether to read_all sid data at once, or to read a silce from the
|
||||
carray for each sid.
|
||||
|
||||
Returns
|
||||
-------
|
||||
results : list of ndarray
|
||||
A 2D array of shape `shape` for each column in `columns`.
|
||||
"""
|
||||
cdef:
|
||||
int nassets
|
||||
str column_name
|
||||
carray_t carray
|
||||
ndarray[dtype=uint64_t, ndim=1] raw_data
|
||||
ndarray[dtype=uint64_t, ndim=2] outbuf
|
||||
ndarray[dtype=uint8_t, ndim=2, cast=True] where_nan
|
||||
ndarray[dtype=float64_t, ndim=2] outbuf_as_float
|
||||
intp_t asset
|
||||
intp_t out_idx
|
||||
intp_t raw_idx
|
||||
intp_t first_row
|
||||
intp_t last_row
|
||||
intp_t offset
|
||||
list results = []
|
||||
|
||||
ndays = shape[0]
|
||||
nassets = shape[1]
|
||||
if not nassets== len(first_rows) == len(last_rows) == len(offsets):
|
||||
raise ValueError("Incompatible index arrays.")
|
||||
|
||||
for column_name in columns:
|
||||
outbuf = zeros(shape=shape, dtype=uint64)
|
||||
if read_all:
|
||||
raw_data = table[column_name][:]
|
||||
|
||||
for asset in range(nassets):
|
||||
first_row = first_rows[asset]
|
||||
last_row = last_rows[asset]
|
||||
offset = offsets[asset]
|
||||
if first_row <= last_row:
|
||||
outbuf[offset:offset + (last_row + 1 - first_row), asset] =\
|
||||
raw_data[first_row:last_row + 1]
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
carray = table[column_name]
|
||||
|
||||
for asset in range(nassets):
|
||||
first_row = first_rows[asset]
|
||||
last_row = last_rows[asset]
|
||||
offset = offsets[asset]
|
||||
out_start = offset
|
||||
out_end = (last_row - first_row) + offset + 1
|
||||
if first_row <= last_row:
|
||||
outbuf[offset:offset + (last_row + 1 - first_row), asset] =\
|
||||
carray[first_row:last_row + 1]
|
||||
else:
|
||||
continue
|
||||
|
||||
if column_name in ['open', 'high', 'low', 'close']:
|
||||
where_nan = (outbuf == 0)
|
||||
outbuf_as_float = outbuf.astype(float64) * .000000001
|
||||
outbuf_as_float[where_nan] = NAN
|
||||
results.append(outbuf_as_float)
|
||||
elif column_name in ['volume']:
|
||||
results.append(outbuf.astype(float64) * .000000001)
|
||||
else:
|
||||
results.append(outbuf)
|
||||
return results
|
||||
@@ -1,160 +0,0 @@
|
||||
from numpy cimport ndarray, long_t
|
||||
from numpy import searchsorted
|
||||
from cpython cimport bool
|
||||
cimport cython
|
||||
|
||||
cdef inline int int_min(int a, int b): return a if a <= b else b
|
||||
|
||||
@cython.cdivision(True)
|
||||
def minute_value(ndarray[long_t, ndim=1] market_opens,
|
||||
Py_ssize_t pos,
|
||||
short minutes_per_day):
|
||||
"""
|
||||
Finds the value of the minute represented by `pos` in the given array of
|
||||
market opens.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
market_opens: numpy array of ints
|
||||
Market opens, in minute epoch values.
|
||||
|
||||
pos: int
|
||||
The index of the desired minute.
|
||||
|
||||
minutes_per_day: int
|
||||
The number of minutes per day (e.g. 390 for NYSE).
|
||||
|
||||
Returns
|
||||
-------
|
||||
int: The minute epoch value of the desired minute.
|
||||
"""
|
||||
cdef short q, r
|
||||
|
||||
q = cython.cdiv(pos, minutes_per_day)
|
||||
r = cython.cmod(pos, minutes_per_day)
|
||||
|
||||
return market_opens[q] + r
|
||||
|
||||
def find_position_of_minute(ndarray[long_t, ndim=1] market_opens,
|
||||
ndarray[long_t, ndim=1] market_closes,
|
||||
long_t minute_val,
|
||||
short minutes_per_day,
|
||||
bool forward_fill):
|
||||
"""
|
||||
Finds the position of a given minute in the given array of market opens.
|
||||
If not a market minute, adjusts to the last market minute.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
market_opens: numpy array of ints
|
||||
Market opens, in minute epoch values.
|
||||
|
||||
market_closes: numpy array of ints
|
||||
Market closes, in minute epoch values.
|
||||
|
||||
minute_val: int
|
||||
The desired minute, as a minute epoch.
|
||||
|
||||
minutes_per_day: int
|
||||
The number of minutes per day (e.g. 390 for NYSE).
|
||||
|
||||
forward_fill: bool
|
||||
Whether to use the previous market minute if the given minute does
|
||||
not fall within an open/close pair.
|
||||
|
||||
Returns
|
||||
-------
|
||||
int: The position of the given minute in the market opens array.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If the given minute is not between a single open/close pair AND
|
||||
forward_fill is False. For example, if minute_val is 17:00 Eastern
|
||||
for a given day whose normal hours are 9:30 to 16:00, and we are not
|
||||
forward filling, ValueError is raised.
|
||||
"""
|
||||
cdef Py_ssize_t market_open_loc, market_open, delta
|
||||
|
||||
market_open_loc = \
|
||||
searchsorted(market_opens, minute_val, side='right') - 1
|
||||
market_open = market_opens[market_open_loc]
|
||||
market_close = market_closes[market_open_loc]
|
||||
|
||||
if not forward_fill and ((minute_val - market_open) >= minutes_per_day):
|
||||
raise ValueError("Given minute is not between an open and a close")
|
||||
|
||||
delta = int_min(minute_val - market_open, market_close - market_open)
|
||||
|
||||
return (market_open_loc * minutes_per_day) + delta
|
||||
|
||||
def find_last_traded_position_internal(
|
||||
ndarray[long_t, ndim=1] market_opens,
|
||||
ndarray[long_t, ndim=1] market_closes,
|
||||
long_t end_minute,
|
||||
long_t start_minute,
|
||||
volumes,
|
||||
short minutes_per_day):
|
||||
|
||||
"""
|
||||
Finds the position of the last traded minute for the given volumes array.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
market_opens: numpy array of ints
|
||||
Market opens, in minute epoch values.
|
||||
|
||||
market_closes: numpy array of ints
|
||||
Market closes, in minute epoch values.
|
||||
|
||||
end_minute: int
|
||||
The minute from which to start looking backwards, as a minute epoch.
|
||||
|
||||
start_minute: int
|
||||
The asset's start date, as a minute epoch. Acts as the bottom limit of
|
||||
how far we can look backwards.
|
||||
|
||||
volumes: bcolz carray
|
||||
The volume history for the given asset.
|
||||
|
||||
minutes_per_day: int
|
||||
The number of minutes per day (e.g. 390 for NYSE).
|
||||
|
||||
Returns
|
||||
-------
|
||||
int: The position of the last traded minute, starting from `minute_val`
|
||||
"""
|
||||
cdef Py_ssize_t minute_pos, current_minute, q
|
||||
|
||||
minute_pos = int_min(
|
||||
find_position_of_minute(market_opens, market_closes, end_minute,
|
||||
minutes_per_day, True),
|
||||
len(volumes) - 1
|
||||
)
|
||||
|
||||
while minute_pos >= 0:
|
||||
current_minute = minute_value(
|
||||
market_opens, minute_pos, minutes_per_day
|
||||
)
|
||||
|
||||
q = cython.cdiv(minute_pos, minutes_per_day)
|
||||
if current_minute > market_closes[q]:
|
||||
minute_pos = find_position_of_minute(market_opens,
|
||||
market_closes,
|
||||
market_closes[q],
|
||||
minutes_per_day,
|
||||
False)
|
||||
continue
|
||||
|
||||
if current_minute < start_minute:
|
||||
return -1
|
||||
|
||||
if volumes[minute_pos] != 0:
|
||||
return minute_pos
|
||||
|
||||
minute_pos -= 1
|
||||
|
||||
# we've gone to the beginning of this asset's range, and still haven't
|
||||
# found a trade event
|
||||
return -1
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from cython cimport boundscheck, wraparound
|
||||
from numpy import finfo, float64, nan, isnan
|
||||
from numpy cimport intp_t, float64_t, uint32_t
|
||||
|
||||
@boundscheck(False)
|
||||
@wraparound(False)
|
||||
cpdef void _minute_to_session_open(intp_t[:] close_locs,
|
||||
float64_t[:] data,
|
||||
float64_t[:] out):
|
||||
cdef intp_t i, close_loc, loc = 0
|
||||
cdef float64_t val
|
||||
for i, close_loc in enumerate(close_locs):
|
||||
val = nan
|
||||
# Start by getting the price value at the opening minute of each day.
|
||||
# If the value is NaN, continue looking forward until we either find a
|
||||
# valid value or reach the closing minute, at which point the value is
|
||||
# just kept as a NaN. We increment 'loc' after obtaining the value to
|
||||
# ensure we do not reach an out of bounds index.
|
||||
while isnan(val) and loc <= close_loc:
|
||||
val = data[loc]
|
||||
loc += 1
|
||||
out[i] = val
|
||||
loc = close_loc + 1
|
||||
|
||||
|
||||
@boundscheck(False)
|
||||
@wraparound(False)
|
||||
cpdef void _minute_to_session_high(intp_t[:] close_locs,
|
||||
float64_t[:] data,
|
||||
float64_t[:] out):
|
||||
cdef intp_t i, close_loc, loc = 0
|
||||
cdef float64_t val
|
||||
for i, close_loc in enumerate(close_locs):
|
||||
val = -1
|
||||
while loc <= close_loc:
|
||||
val = max(val, data[loc])
|
||||
loc += 1
|
||||
if val == -1:
|
||||
val = nan
|
||||
out[i] = val
|
||||
loc = close_loc + 1
|
||||
|
||||
|
||||
@boundscheck(False)
|
||||
@wraparound(False)
|
||||
cpdef void _minute_to_session_low(intp_t[:] close_locs,
|
||||
float64_t[:] data,
|
||||
float64_t[:] out):
|
||||
cdef intp_t i, close_loc, loc = 0
|
||||
cdef float64_t val
|
||||
cdef float64_t max_float = finfo(float64).max
|
||||
for i, close_loc in enumerate(close_locs):
|
||||
val = max_float
|
||||
while loc <= close_loc:
|
||||
val = min(val, data[loc])
|
||||
loc += 1
|
||||
if val == max_float:
|
||||
val = nan
|
||||
out[i] = val
|
||||
loc = close_loc + 1
|
||||
|
||||
|
||||
@boundscheck(False)
|
||||
@wraparound(False)
|
||||
cpdef void _minute_to_session_close(intp_t[:] close_locs,
|
||||
float64_t[:] data,
|
||||
float64_t[:] out):
|
||||
cdef intp_t i, prev_close_loc, loc = 0
|
||||
cdef float64_t val
|
||||
num_out = len(out)
|
||||
for i in range(num_out - 1, -1, -1):
|
||||
if i > 0:
|
||||
prev_close_loc = close_locs[i - 1]
|
||||
else:
|
||||
prev_close_loc = -1
|
||||
loc = close_locs[i]
|
||||
val = nan
|
||||
# Start by getting the price value at the closing minute of each day.
|
||||
# If the value is NaN, continue looking back until we either find a
|
||||
# valid value or reach the closing minute of the previous day, at which
|
||||
# point the value is just kept as a NaN. We decrement 'loc' after
|
||||
# obtaining the value to ensure we do not reach a negative index.
|
||||
while isnan(val) and loc > prev_close_loc:
|
||||
val = data[loc]
|
||||
loc -= 1
|
||||
out[i] = val
|
||||
|
||||
|
||||
@boundscheck(False)
|
||||
@wraparound(False)
|
||||
cpdef void _minute_to_session_volume(intp_t[:] close_locs,
|
||||
uint32_t[:] data,
|
||||
uint32_t[:] out):
|
||||
cdef intp_t i, close_loc, loc = 0
|
||||
cdef uint32_t val
|
||||
loc = 0
|
||||
for i, close_loc in enumerate(close_locs):
|
||||
val = 0
|
||||
while loc <= close_loc:
|
||||
val += data[loc]
|
||||
loc += 1
|
||||
out[i] = val
|
||||
loc = close_loc + 1
|
||||
@@ -1,138 +0,0 @@
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from abc import ABCMeta, abstractmethod, abstractproperty
|
||||
from six import with_metaclass
|
||||
|
||||
|
||||
class NoDataOnDate(Exception):
|
||||
"""
|
||||
Raised when a spot price cannot be found for the sid and date.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class NoDataBeforeDate(NoDataOnDate):
|
||||
pass
|
||||
|
||||
|
||||
class NoDataAfterDate(NoDataOnDate):
|
||||
pass
|
||||
|
||||
|
||||
class BarReader(with_metaclass(ABCMeta, object)):
|
||||
@abstractproperty
|
||||
def data_frequency(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def load_raw_arrays(self, columns, start_date, end_date, assets):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
fields : list of str
|
||||
'open', 'high', 'low', 'close', or 'volume'
|
||||
start_dt: Timestamp
|
||||
Beginning of the window range.
|
||||
end_dt: Timestamp
|
||||
End of the window range.
|
||||
sids : list of int
|
||||
The asset identifiers in the window.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of np.ndarray
|
||||
A list with an entry per field of ndarrays with shape
|
||||
(minutes in range, sids) with a dtype of float64, containing the
|
||||
values for the respective field over start and end dt range.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def last_available_dt(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
dt : pd.Timestamp
|
||||
The last session for which the reader can provide data.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def trading_calendar(self):
|
||||
"""
|
||||
Returns the catalyst.utils.calendar.trading_calendar used to read
|
||||
the data. Can be None (if the writer didn't specify it).
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def first_trading_day(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
dt : pd.Timestamp
|
||||
The first trading day (session) for which the reader can provide
|
||||
data.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_value(self, sid, dt, field):
|
||||
"""
|
||||
Retrieve the value at the given coordinates.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sid : int
|
||||
The asset identifier.
|
||||
dt : pd.Timestamp
|
||||
The timestamp for the desired data point.
|
||||
field : string
|
||||
The OHLVC name for the desired data point.
|
||||
|
||||
Returns
|
||||
-------
|
||||
value : float|int
|
||||
The value at the given coordinates, ``float`` for OHLC, ``int``
|
||||
for 'volume'.
|
||||
|
||||
Raises
|
||||
------
|
||||
NoDataOnDate
|
||||
If the given dt is not a valid market minute (in minute mode) or
|
||||
session (in daily mode) according to this reader's tradingcalendar.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_last_traded_dt(self, asset, dt):
|
||||
"""
|
||||
Get the latest minute on or before ``dt`` in which ``asset`` traded.
|
||||
|
||||
If there are no trades on or before ``dt``, returns ``pd.NaT``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : catalyst.asset.Asset
|
||||
The asset for which to get the last traded minute.
|
||||
dt : pd.Timestamp
|
||||
The minute at which to start searching for the last traded minute.
|
||||
|
||||
Returns
|
||||
-------
|
||||
last_traded : pd.Timestamp
|
||||
The dt of the last trade for the given asset, using the input
|
||||
dt as a vantage point.
|
||||
"""
|
||||
pass
|
||||
@@ -1,64 +0,0 @@
|
||||
#
|
||||
# Copyright 2013 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import pandas_datareader.data as pd_reader
|
||||
|
||||
|
||||
def get_benchmark_returns(symbol, first_date, last_date):
|
||||
"""
|
||||
Get a Series of benchmark returns from Google associated with `symbol`.
|
||||
Default is `SPY`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : str
|
||||
Benchmark symbol for which we're getting the returns.
|
||||
first_date : pd.Timestamp
|
||||
First date for which we want to get data.
|
||||
last_date : pd.Timestamp
|
||||
Last date for which we want to get data.
|
||||
|
||||
The furthest date that Google goes back to is 1993-02-01. It has missing
|
||||
data for 2008-12-15, 2009-08-11, and 2012-02-02, so we add data for the
|
||||
dates for which Google is missing data.
|
||||
|
||||
We're also limited to 4000 days worth of data per request. If we make a
|
||||
request for data that extends past 4000 trading days, we'll still only
|
||||
receive 4000 days of data.
|
||||
|
||||
first_date is **not** included because we need the close from day N - 1 to
|
||||
compute the returns for day N.
|
||||
"""
|
||||
if symbol == '^GSPC':
|
||||
symbol = 'spy'
|
||||
|
||||
data = pd_reader.DataReader(
|
||||
symbol,
|
||||
'google',
|
||||
first_date,
|
||||
last_date
|
||||
)
|
||||
|
||||
data = data['Close']
|
||||
|
||||
data[pd.Timestamp('2008-12-15')] = np.nan
|
||||
data[pd.Timestamp('2009-08-11')] = np.nan
|
||||
data[pd.Timestamp('2012-02-02')] = np.nan
|
||||
|
||||
data = data.fillna(method='ffill')
|
||||
|
||||
return data.sort_index().tz_localize('UTC').pct_change(1).iloc[1:]
|
||||
@@ -1,31 +0,0 @@
|
||||
# These imports are necessary to force module-scope register calls to happen.
|
||||
from . import quandl # noqa
|
||||
from . import poloniex
|
||||
from .core import (
|
||||
UnknownBundle,
|
||||
bundles,
|
||||
clean,
|
||||
from_bundle_ingest_dirname,
|
||||
ingest,
|
||||
ingestions_for_bundle,
|
||||
load,
|
||||
register,
|
||||
to_bundle_ingest_dirname,
|
||||
unregister,
|
||||
)
|
||||
from .yahoo import yahoo_equities
|
||||
|
||||
__all__ = [
|
||||
'UnknownBundle',
|
||||
'bundles',
|
||||
'clean',
|
||||
'from_bundle_ingest_dirname',
|
||||
'ingest',
|
||||
'ingestions_for_bundle',
|
||||
'load',
|
||||
'register',
|
||||
'to_bundle_ingest_dirname',
|
||||
'unregister',
|
||||
'yahoo_equities',
|
||||
'poloniex_cryptoassets',
|
||||
]
|
||||
@@ -1,499 +0,0 @@
|
||||
#
|
||||
# Copyright 2017 Enigma MPC, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from itertools import count
|
||||
import tarfile
|
||||
from time import time, sleep
|
||||
|
||||
from abc import abstractmethod, abstractproperty
|
||||
import logbook
|
||||
import pandas as pd
|
||||
|
||||
from . import core as bundles
|
||||
|
||||
from catalyst.utils.cli import (
|
||||
item_show_count,
|
||||
maybe_show_progress
|
||||
)
|
||||
from catalyst.utils.memoize import lazyval
|
||||
|
||||
logbook.StderrHandler().push_application()
|
||||
log = logbook.Logger(__name__)
|
||||
|
||||
DEFAULT_RETRIES = 5
|
||||
|
||||
class BaseBundle(object):
|
||||
def __init__(self, asset_filter=[]):
|
||||
self._asset_filter = asset_filter
|
||||
self._reset()
|
||||
|
||||
def _reset(self):
|
||||
self._splits = []
|
||||
self._dividends = []
|
||||
|
||||
@lazyval
|
||||
def name(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@lazyval
|
||||
def exchange(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@lazyval
|
||||
def calendar_name(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@lazyval
|
||||
def minutes_per_day(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@lazyval
|
||||
def frequencies(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@lazyval
|
||||
def md_column_names(self):
|
||||
return _dtypes_to_cols(self.md_dtypes)
|
||||
|
||||
@lazyval
|
||||
def md_dtypes(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@lazyval
|
||||
def column_names(self):
|
||||
return _dtypes_to_cols(self.dtypes)
|
||||
|
||||
@lazyval
|
||||
def dtypes(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@lazyval
|
||||
def tar_url(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@lazyval
|
||||
def wait_time(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractproperty
|
||||
def splits(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractproperty
|
||||
def dividends(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractmethod
|
||||
def fetch_raw_metadata_frame(self, api_key, page_number):
|
||||
raise NotImplementedError()
|
||||
|
||||
def post_process_symbol_metadata(self, metadata, data):
|
||||
return metadata
|
||||
|
||||
@abstractmethod
|
||||
def fetch_raw_symbol_frame(self, api_key, symbol, start_date, end_date):
|
||||
raise NotImplementedError()
|
||||
|
||||
def ingest(self,
|
||||
environ,
|
||||
asset_db_writer,
|
||||
minute_bar_writer,
|
||||
daily_bar_writer,
|
||||
adjustment_writer,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
cache,
|
||||
show_progress,
|
||||
is_compile,
|
||||
output_dir):
|
||||
|
||||
try:
|
||||
api_key = environ.get('CATALYST_API_KEY')
|
||||
retries = environ.get('CATALYST_DOWNLOAD_ATTEMPTS', 5)
|
||||
|
||||
if is_compile:
|
||||
# User has instructed local compilation and ingestion of bundle.
|
||||
# Fetch raw metadata for all symbols.
|
||||
raw_metadata = self._fetch_metadata_frame(
|
||||
api_key,
|
||||
cache=cache,
|
||||
retries=retries,
|
||||
environ=environ,
|
||||
show_progress=show_progress,
|
||||
)
|
||||
|
||||
# Compile daily symbol data if bundle supports daily mode and
|
||||
# persist the dataset to disk.
|
||||
symbol_map = raw_metadata.symbol
|
||||
if 'daily' in self.frequencies:
|
||||
daily_bar_writer.write(
|
||||
self._fetch_symbol_iter(
|
||||
api_key,
|
||||
cache,
|
||||
symbol_map,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
'daily',
|
||||
retries,
|
||||
),
|
||||
assets=raw_metadata.index,
|
||||
show_progress=show_progress,
|
||||
)
|
||||
|
||||
# Post-process metadata using cached symbol frames, and write to
|
||||
# disk. This metadata must be written before any attempt to write
|
||||
# minute data.
|
||||
metadata = self._post_process_metadata(
|
||||
raw_metadata,
|
||||
cache,
|
||||
show_progress=show_progress,
|
||||
)
|
||||
asset_db_writer.write(metadata)
|
||||
|
||||
# Compile minute symbol data if bundle supports minute mode and
|
||||
# persist the dataset to disk.
|
||||
if 'minute' in self.frequencies:
|
||||
minute_bar_writer.write(
|
||||
self._fetch_symbol_iter(
|
||||
api_key,
|
||||
cache,
|
||||
symbol_map,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
'minute',
|
||||
retries,
|
||||
),
|
||||
show_progress=show_progress,
|
||||
)
|
||||
|
||||
# For legacy purposes, this call is required to ensure the database
|
||||
# contains an appropriately initialized file structure. We don't
|
||||
# forsee a usecase for adjustments at this time, but may later
|
||||
# choose to expose this functionality in the future.
|
||||
adjustment_writer.write(
|
||||
splits=(
|
||||
pd.concat(self.splits, ignore_index=True)
|
||||
if len(self.splits) > 0 else
|
||||
None
|
||||
),
|
||||
dividends=(
|
||||
pd.concat(self.dividends, ignore_index=True)
|
||||
if len(self.dividends) > 0 else
|
||||
None
|
||||
),
|
||||
)
|
||||
else:
|
||||
# Otherwise, user has instructed to download and untar bundle
|
||||
# directly from the bundles `tar_url`.
|
||||
self._download_and_untar(show_progress, output_dir)
|
||||
except Exception as e:
|
||||
log.exception(
|
||||
' Failed to ingest {name}:\n{msg}'.format(
|
||||
name=self.name,
|
||||
msg=str(e),
|
||||
)
|
||||
)
|
||||
else:
|
||||
self._reset()
|
||||
|
||||
def _download_and_untar(self, show_progress, output_dir):
|
||||
# Download bundle conditioned on whether the user would like progress
|
||||
# information to be displayed in the CLI.
|
||||
if show_progress:
|
||||
data = bundles.download_with_progress(
|
||||
self.tar_url,
|
||||
chunk_size=bundles.ONE_MEGABYTE,
|
||||
label='Downloading {name} bundle'.format(name=self.name),
|
||||
)
|
||||
else:
|
||||
data = bundles.download_without_progress(self.tar_url)
|
||||
|
||||
# File transfer has completed, untar the bundle to the appropriate
|
||||
# data directory.
|
||||
with tarfile.open('r', fileobj=data) as tar:
|
||||
tar.extractall(output_dir)
|
||||
|
||||
def _fetch_metadata_frame(self,
|
||||
api_key,
|
||||
cache,
|
||||
retries=DEFAULT_RETRIES,
|
||||
environ=None,
|
||||
show_progress=False):
|
||||
|
||||
# Setup raw metadata iterator to fetch pages if necessary.
|
||||
raw_iter = self._fetch_metadata_iter(api_key, cache, retries, environ)
|
||||
|
||||
# Concatenate all frame in iterator to compute a single metadata frame.
|
||||
with maybe_show_progress(
|
||||
raw_iter,
|
||||
show_progress,
|
||||
label='Fetching symbol metadata',
|
||||
item_show_func=item_show_count(),
|
||||
length=3,
|
||||
show_percent=False,
|
||||
) as blocks:
|
||||
metadata = pd.concat(blocks, ignore_index=True)
|
||||
|
||||
return metadata
|
||||
|
||||
def _fetch_metadata_iter(self, api_key, cache, retries, environ):
|
||||
for page_number in count(1):
|
||||
# Attempt to load metadata page from cache. If it does not exist,
|
||||
# poll the API upto `retries` times in order to get raw DataFrame.
|
||||
key = 'metadata-page-{pn}.frame'.format(pn=page_number)
|
||||
try:
|
||||
raw = cache[key]
|
||||
except KeyError:
|
||||
for _ in range(retries):
|
||||
try:
|
||||
raw = self.fetch_raw_metadata_frame(
|
||||
api_key,
|
||||
page_number,
|
||||
)
|
||||
break
|
||||
except ValueError as e:
|
||||
raw = pd.DataFrame([])
|
||||
break
|
||||
except Exception as e:
|
||||
log.exception(
|
||||
'Failed to load metadata from {}. '
|
||||
'Retrying.'.format(self.name)
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
'Failed to download metadata page {} after {} '
|
||||
'attempts.'.format(page_number, retries)
|
||||
)
|
||||
|
||||
|
||||
if raw.empty:
|
||||
# Empty DataFrame signals completion.
|
||||
break
|
||||
|
||||
# Apply selective asset filtering, useful for benchmark
|
||||
# ingestion.
|
||||
if self._asset_filter:
|
||||
raw = raw[raw.symbol.isin(self._asset_filter)]
|
||||
|
||||
# Update cached value for key.
|
||||
cache[key] = raw
|
||||
|
||||
# Return metadata frame to application.
|
||||
yield raw
|
||||
|
||||
def _post_process_metadata(self, metadata, cache, show_progress=False):
|
||||
# Create empty data frame using target metadata column names and dtypes
|
||||
final_metadata = pd.DataFrame(
|
||||
columns=self.md_column_names,
|
||||
index=metadata.index,
|
||||
)
|
||||
|
||||
# Iterate over the available symbols, loading the asset's raw symbol
|
||||
# data from the cache. The final metadata is computed and recorded in
|
||||
# the appropriate row depending on the asset's id.
|
||||
with maybe_show_progress(
|
||||
metadata.symbol.iteritems(),
|
||||
show_progress,
|
||||
label='Post-processing symbol metadata',
|
||||
item_show_func=item_show_count(len(metadata)),
|
||||
length=len(metadata),
|
||||
show_percent=False,
|
||||
) as symbols_map:
|
||||
for asset_id, symbol in symbols_map:
|
||||
# Attempt to load data from disk, the cache should have an entry
|
||||
# for each symbol at this point of the execution. If one does
|
||||
# not exist, we should fail.
|
||||
key = '{sym}.daily.frame'.format(sym=symbol)
|
||||
try:
|
||||
raw_data = cache[key]
|
||||
except KeyError:
|
||||
raise ValueError(
|
||||
'Unable to find cached data for symbol: {0}'.format(symbol)
|
||||
)
|
||||
|
||||
# Perform and require post-processing of metadata.
|
||||
final_symbol_metadata = self.post_process_symbol_metadata(
|
||||
asset_id,
|
||||
metadata.iloc[asset_id],
|
||||
raw_data,
|
||||
)
|
||||
|
||||
# Record symbol's final metadata.
|
||||
final_metadata.iloc[asset_id] = final_symbol_metadata
|
||||
|
||||
# Register all assets with the bundle's default exchange.
|
||||
final_metadata['exchange'] = self.exchange
|
||||
|
||||
return final_metadata
|
||||
|
||||
def _fetch_symbol_iter(self,
|
||||
api_key,
|
||||
cache,
|
||||
symbol_map,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
data_frequency,
|
||||
retries):
|
||||
|
||||
for asset_id, symbol in symbol_map.iteritems():
|
||||
# Record start time of iteration, compare at end of iteration to
|
||||
# adhere to the datas source's rate limit policy.
|
||||
start_time = pd.Timestamp.utcnow()
|
||||
|
||||
# Fetch new data if cached data is absent or stale, otherwise
|
||||
# returns the cached data unaltered. The `should_sleep` flag
|
||||
# indicates that an API call was attempted, and that we should be
|
||||
# ensure aren't exceeding our rate limit before proceeding to the
|
||||
# next symbol. If the raw_data is updated, it is cached before being
|
||||
# returned.
|
||||
raw_data, should_sleep = self._maybe_update_symbol_frame(
|
||||
start_time,
|
||||
api_key,
|
||||
cache,
|
||||
symbol,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
data_frequency,
|
||||
retries,
|
||||
)
|
||||
|
||||
# TODO(cfromknecht) further data validation?
|
||||
|
||||
# Pass asset_id and symbol data to writer.
|
||||
yield asset_id, raw_data
|
||||
|
||||
# If an API call was made during this iteration and the time to
|
||||
# reach this point was less than the inter-request `wait_time`,
|
||||
# sleep until after enough time has elapsed to prevent getting rate
|
||||
# limited.
|
||||
if should_sleep:
|
||||
remaining = pd.Timestamp.utcnow() - start_time + self.wait_time
|
||||
if remaining.value > 0:
|
||||
sleep(remaining.value / 10**9)
|
||||
|
||||
def _maybe_update_symbol_frame(self,
|
||||
start_time,
|
||||
api_key,
|
||||
cache,
|
||||
symbol,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
data_frequency,
|
||||
retries):
|
||||
|
||||
# Attempt to load pre-existing symbol data from cache.
|
||||
key = '{sym}.{freq}.frame'.format(sym=symbol, freq=data_frequency)
|
||||
try:
|
||||
raw_data = cache[key]
|
||||
except KeyError:
|
||||
raw_data = None
|
||||
|
||||
# Select the most recent date in cached dataset if it exists,
|
||||
# otherwise use the provided `start_session`.
|
||||
last = start_session
|
||||
if raw_data is not None and len(raw_data) > 0:
|
||||
last = raw_data.index[-1].tz_localize('UTC')
|
||||
|
||||
should_sleep = False
|
||||
|
||||
# Determine time at which cached data will be considered stale.
|
||||
cache_expiration = last + pd.Timedelta(days=2)
|
||||
if start_time <= cache_expiration and raw_data is not None:
|
||||
# Data is fresh enough to reuse, no need to update. Iterator can
|
||||
# proceed to next symbol directly since no API call was required.
|
||||
return raw_data, should_sleep
|
||||
|
||||
# If we arrive here, we must have attempted an API call.
|
||||
# Setting this flag tells the iterator to pause before starting
|
||||
# the next asset, that we don't exceed the data source's rate
|
||||
# limit.
|
||||
should_sleep = True
|
||||
|
||||
raw_data = self._fetch_symbol_frame(
|
||||
api_key,
|
||||
symbol,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
data_frequency,
|
||||
retries=retries,
|
||||
)
|
||||
|
||||
# Cache latest symbol data.
|
||||
cache[key] = raw_data
|
||||
|
||||
return raw_data, should_sleep
|
||||
|
||||
def _fetch_symbol_frame(self,
|
||||
api_key,
|
||||
symbol,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
data_frequency,
|
||||
retries=DEFAULT_RETRIES):
|
||||
|
||||
# Data for symbol is old enough to attempt an update or is not
|
||||
# present in the cache. Fetch raw data for a single symbol
|
||||
# with requested intervals and frequency. Retry as necessary.
|
||||
for _ in range(retries):
|
||||
try:
|
||||
raw_data = self.fetch_raw_symbol_frame(
|
||||
api_key,
|
||||
symbol,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
data_frequency,
|
||||
)
|
||||
raw_data.index = pd.to_datetime(raw_data.index, utc=True)
|
||||
#raw_data.index = raw_data.index.tz_localize('UTC')
|
||||
|
||||
# Filter incoming data to fit start and end sessions.
|
||||
raw_data = raw_data[
|
||||
(raw_data.index >= start_session) &
|
||||
(raw_data.index <= end_session)
|
||||
]
|
||||
|
||||
# Filter out any duplicates entries, keep last one, since
|
||||
# previous frame is probably an incomplete.
|
||||
raw_data = raw_data[~raw_data.index.duplicated(keep='last')]
|
||||
|
||||
return raw_data
|
||||
|
||||
except Exception as e:
|
||||
log.exception(
|
||||
'Exception raised fetching {name} data. Retrying.'
|
||||
.format(name=self.name)
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
'Failed to download data for symbol {sym} '
|
||||
'after {n} attempts.'.format(
|
||||
sym=symbol,
|
||||
n=retries,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _dtypes_to_cols(dtypes):
|
||||
return [name for name, _ in dtypes]
|
||||
@@ -1,73 +0,0 @@
|
||||
#
|
||||
# Copyright 2017 Enigma MPC, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from catalyst.data.bundles.base import BaseBundle
|
||||
from catalyst.utils.memoize import lazyval
|
||||
|
||||
class BasePricingBundle(BaseBundle):
|
||||
@lazyval
|
||||
def md_dtypes(self):
|
||||
return [
|
||||
('symbol', 'object'),
|
||||
('start_date', 'datetime64[ns]'),
|
||||
('end_date', 'datetime64[ns]'),
|
||||
('ac_date', 'datetime64[ns]'),
|
||||
('min_trade_size', 'float'),
|
||||
]
|
||||
|
||||
@lazyval
|
||||
def dtypes(self):
|
||||
return [
|
||||
('date', 'datetime64[ns]'),
|
||||
('open', 'float64'),
|
||||
('high', 'float64'),
|
||||
('low', 'float64'),
|
||||
('close', 'float64'),
|
||||
('volume', 'float64'),
|
||||
]
|
||||
|
||||
class BaseCryptoPricingBundle(BasePricingBundle):
|
||||
@lazyval
|
||||
def calendar_name(self):
|
||||
return 'OPEN'
|
||||
|
||||
@lazyval
|
||||
def minutes_per_day(self):
|
||||
return 1440
|
||||
|
||||
@property
|
||||
def splits(self):
|
||||
return []
|
||||
|
||||
@property
|
||||
def dividends(self):
|
||||
return []
|
||||
|
||||
class BaseEquityPricingBundle(BasePricingBundle):
|
||||
@lazyval
|
||||
def calendar_name(self):
|
||||
return 'NYSE'
|
||||
|
||||
@lazyval
|
||||
def minutes_per_day(self):
|
||||
return 390
|
||||
|
||||
@property
|
||||
def splits(self):
|
||||
return self._splits
|
||||
|
||||
@property
|
||||
def dividends(self):
|
||||
return self._dividends
|
||||
@@ -1,708 +0,0 @@
|
||||
from collections import namedtuple
|
||||
import errno
|
||||
from io import BytesIO
|
||||
import os
|
||||
import requests
|
||||
import shutil
|
||||
import warnings
|
||||
|
||||
from contextlib2 import ExitStack
|
||||
import click
|
||||
import pandas as pd
|
||||
from toolz import curry, complement, take
|
||||
|
||||
from ..us_equity_pricing import (
|
||||
BcolzDailyBarReader,
|
||||
BcolzDailyBarWriter,
|
||||
SQLiteAdjustmentReader,
|
||||
SQLiteAdjustmentWriter,
|
||||
)
|
||||
from ..minute_bars import (
|
||||
BcolzMinuteBarReader,
|
||||
BcolzMinuteBarWriter,
|
||||
)
|
||||
from catalyst.assets import AssetDBWriter, AssetFinder, ASSET_DB_VERSION
|
||||
from catalyst.assets.asset_db_migrations import downgrade
|
||||
from catalyst.utils.cache import (
|
||||
dataframe_cache,
|
||||
working_dir,
|
||||
working_file,
|
||||
)
|
||||
from catalyst.utils.compat import mappingproxy
|
||||
from catalyst.utils.input_validation import ensure_timestamp, optionally
|
||||
import catalyst.utils.paths as pth
|
||||
from catalyst.utils.preprocess import preprocess
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
from catalyst.utils.cli import maybe_show_progress
|
||||
|
||||
ONE_MEGABYTE = 1024 * 1024
|
||||
|
||||
def asset_db_path(bundle_name, timestr, environ=None, db_version=None):
|
||||
return pth.data_path(
|
||||
asset_db_relative(bundle_name, timestr, environ, db_version),
|
||||
environ=environ,
|
||||
)
|
||||
|
||||
|
||||
def minute_path(bundle_name, timestr, environ=None):
|
||||
return pth.data_path(
|
||||
minute_relative(bundle_name, timestr, environ),
|
||||
environ=environ,
|
||||
)
|
||||
|
||||
|
||||
def daily_path(bundle_name, timestr, environ=None):
|
||||
return pth.data_path(
|
||||
daily_relative(bundle_name, timestr, environ),
|
||||
environ=environ,
|
||||
)
|
||||
|
||||
|
||||
def adjustment_db_path(bundle_name, timestr, environ=None):
|
||||
return pth.data_path(
|
||||
adjustment_db_relative(bundle_name, timestr, environ),
|
||||
environ=environ,
|
||||
)
|
||||
|
||||
|
||||
def cache_path(bundle_name, environ=None):
|
||||
return pth.data_path(
|
||||
cache_relative(bundle_name, environ),
|
||||
environ=environ,
|
||||
)
|
||||
|
||||
|
||||
def adjustment_db_relative(bundle_name, timestr, environ=None):
|
||||
return bundle_name, timestr, 'adjustments.sqlite'
|
||||
|
||||
|
||||
def cache_relative(bundle_name, timestr, environ=None):
|
||||
return bundle_name, '.cache'
|
||||
|
||||
|
||||
def daily_relative(bundle_name, timestr, environ=None):
|
||||
return bundle_name, timestr, 'daily_equities.bcolz'
|
||||
|
||||
|
||||
def minute_relative(bundle_name, timestr, environ=None):
|
||||
return bundle_name, timestr, 'minute_equities.bcolz'
|
||||
|
||||
|
||||
def asset_db_relative(bundle_name, timestr, environ=None, db_version=None):
|
||||
db_version = ASSET_DB_VERSION if db_version is None else db_version
|
||||
|
||||
return bundle_name, timestr, 'assets-%d.sqlite' % db_version
|
||||
|
||||
|
||||
def to_bundle_ingest_dirname(ts):
|
||||
"""Convert a pandas Timestamp into the name of the directory for the
|
||||
ingestion.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ts : pandas.Timestamp
|
||||
The time of the ingestions
|
||||
|
||||
Returns
|
||||
-------
|
||||
name : str
|
||||
The name of the directory for this ingestion.
|
||||
"""
|
||||
return ts.isoformat().replace(':', ';')
|
||||
|
||||
|
||||
def from_bundle_ingest_dirname(cs):
|
||||
"""Read a bundle ingestion directory name into a pandas Timestamp.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cs : str
|
||||
The name of the directory.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ts : pandas.Timestamp
|
||||
The time when this ingestion happened.
|
||||
"""
|
||||
return pd.Timestamp(cs.replace(';', ':'))
|
||||
|
||||
|
||||
def ingestions_for_bundle(bundle, environ=None):
|
||||
return sorted(
|
||||
(from_bundle_ingest_dirname(ing)
|
||||
for ing in os.listdir(pth.data_path([bundle], environ))
|
||||
if not pth.hidden(ing)),
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
def download_with_progress(url, chunk_size, **progress_kwargs):
|
||||
"""
|
||||
Download streaming data from a URL, printing progress information to the
|
||||
terminal.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
url : str
|
||||
A URL that can be understood by ``requests.get``.
|
||||
chunk_size : int
|
||||
Number of bytes to read at a time from requests.
|
||||
**progress_kwargs
|
||||
Forwarded to click.progressbar.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data : BytesIO
|
||||
A BytesIO containing the downloaded data.
|
||||
"""
|
||||
resp = requests.get(url, stream=True)
|
||||
resp.raise_for_status()
|
||||
|
||||
total_size = int(resp.headers['content-length'])
|
||||
data = BytesIO()
|
||||
|
||||
progress_kwargs['length'] = total_size
|
||||
with maybe_show_progress(None, True, **progress_kwargs) as pbar:
|
||||
for chunk in resp.iter_content(chunk_size=chunk_size):
|
||||
data.write(chunk)
|
||||
pbar.update(len(chunk))
|
||||
|
||||
data.seek(0)
|
||||
return data
|
||||
|
||||
|
||||
def download_without_progress(url):
|
||||
"""
|
||||
Download data from a URL, returning a BytesIO containing the loaded data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
url : str
|
||||
A URL that can be understood by ``requests.get``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data : BytesIO
|
||||
A BytesIO containing the downloaded data.
|
||||
"""
|
||||
resp = requests.get(url)
|
||||
resp.raise_for_status()
|
||||
return BytesIO(resp.content)
|
||||
|
||||
|
||||
RegisteredBundle = namedtuple(
|
||||
'RegisteredBundle',
|
||||
['calendar_name',
|
||||
'start_session',
|
||||
'end_session',
|
||||
'minutes_per_day',
|
||||
'ingest',
|
||||
'create_writers']
|
||||
)
|
||||
|
||||
BundleData = namedtuple(
|
||||
'BundleData',
|
||||
'asset_finder minute_bar_reader daily_bar_reader '
|
||||
'adjustment_reader',
|
||||
)
|
||||
|
||||
BundleCore = namedtuple(
|
||||
'BundleCore',
|
||||
'bundles register_bundle register unregister ingest load clean',
|
||||
)
|
||||
|
||||
|
||||
class UnknownBundle(click.ClickException, LookupError):
|
||||
"""Raised if no bundle with the given name was registered.
|
||||
"""
|
||||
exit_code = 1
|
||||
|
||||
def __init__(self, name):
|
||||
super(UnknownBundle, self).__init__(
|
||||
'No bundle registered with the name %r' % name,
|
||||
)
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
|
||||
class BadClean(click.ClickException, ValueError):
|
||||
"""Exception indicating that an invalid argument set was passed to
|
||||
``clean``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
before, after, keep_last : any
|
||||
The bad arguments to ``clean``.
|
||||
|
||||
See Also
|
||||
--------
|
||||
clean
|
||||
"""
|
||||
def __init__(self, before, after, keep_last):
|
||||
super(BadClean, self).__init__(
|
||||
'Cannot pass a combination of `before` and `after` with'
|
||||
'`keep_last`. Got: before=%r, after=%r, keep_n=%r\n' % (
|
||||
before,
|
||||
after,
|
||||
keep_last,
|
||||
),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
|
||||
def _make_bundle_core():
|
||||
"""Create a family of data bundle functions that read from the same
|
||||
bundle mapping.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bundles : mappingproxy
|
||||
The mapping of bundles to bundle payloads.
|
||||
register_bundle : Bundle
|
||||
A bundle instance to add to the ``bundles`` mapping.
|
||||
register : callable
|
||||
The function which registers new bundles in the ``bundles`` mapping.
|
||||
unregister : callable
|
||||
The function which deregisters bundles from the ``bundles`` mapping.
|
||||
ingest : callable
|
||||
The function which downloads and write data for a given data bundle.
|
||||
load : callable
|
||||
The function which loads the ingested bundles back into memory.
|
||||
clean : callable
|
||||
The function which cleans up data written with ``ingest``.
|
||||
"""
|
||||
_bundles = {} # the registered bundles
|
||||
# Expose _bundles through a proxy so that users cannot mutate this
|
||||
# accidentally. Users may go through `register` to update this which will
|
||||
# warn when trampling another bundle.
|
||||
bundles = mappingproxy(_bundles)
|
||||
|
||||
def register_bundle(bundle_cls,
|
||||
asset_filter=None,
|
||||
start_session=None,
|
||||
end_session=None,
|
||||
create_writers=True):
|
||||
bundle = bundle_cls(asset_filter=asset_filter)
|
||||
return register(
|
||||
bundle.name,
|
||||
bundle.ingest,
|
||||
calendar_name=bundle.calendar_name,
|
||||
minutes_per_day=bundle.minutes_per_day,
|
||||
start_session=start_session,
|
||||
end_session=end_session,
|
||||
create_writers=create_writers,
|
||||
)
|
||||
|
||||
@curry
|
||||
def register(name,
|
||||
f,
|
||||
calendar_name='OPEN',
|
||||
start_session=None,
|
||||
end_session=None,
|
||||
minutes_per_day=1440,
|
||||
create_writers=True):
|
||||
"""Register a data bundle ingest function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name of the bundle.
|
||||
f : callable
|
||||
The ingest function. This function will be passed:
|
||||
|
||||
environ : mapping
|
||||
The environment this is being run with.
|
||||
asset_db_writer : AssetDBWriter
|
||||
The asset db writer to write into.
|
||||
minute_bar_writer : BcolzMinuteBarWriter
|
||||
The minute bar writer to write into.
|
||||
daily_bar_writer : BcolzDailyBarWriter
|
||||
The daily bar writer to write into.
|
||||
adjustment_writer : SQLiteAdjustmentWriter
|
||||
The adjustment db writer to write into.
|
||||
calendar : catalyst.utils.calendars.TradingCalendar
|
||||
The trading calendar to ingest for.
|
||||
start_session : pd.Timestamp
|
||||
The first session of data to ingest.
|
||||
end_session : pd.Timestamp
|
||||
The last session of data to ingest.
|
||||
cache : DataFrameCache
|
||||
A mapping object to temporarily store dataframes.
|
||||
This should be used to cache intermediates in case the load
|
||||
fails. This will be automatically cleaned up after a
|
||||
successful load.
|
||||
show_progress : bool
|
||||
Show the progress for the current load where possible.
|
||||
calendar_name : str, optional
|
||||
The name of a calendar used to align bundle data.
|
||||
Default is 'NYSE'.
|
||||
start_session : pd.Timestamp, optional
|
||||
The first session for which we want data. If not provided,
|
||||
or if the date lies outside the range supported by the
|
||||
calendar, the first_session of the calendar is used.
|
||||
end_session : pd.Timestamp, optional
|
||||
The last session for which we want data. If not provided,
|
||||
or if the date lies outside the range supported by the
|
||||
calendar, the last_session of the calendar is used.
|
||||
minutes_per_day : int, optional
|
||||
The number of minutes in each normal trading day.
|
||||
create_writers : bool, optional
|
||||
Should the ingest machinery create the writers for the ingest
|
||||
function. This can be disabled as an optimization for cases where
|
||||
they are not needed, like the ``quantopian-quandl`` bundle.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This function my be used as a decorator, for example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@register('quandl')
|
||||
def quandl_ingest_function(...):
|
||||
...
|
||||
|
||||
See Also
|
||||
--------
|
||||
catalyst.data.bundles.bundles
|
||||
"""
|
||||
if name in bundles:
|
||||
warnings.warn(
|
||||
'Overwriting bundle with name %r' % name,
|
||||
stacklevel=3,
|
||||
)
|
||||
|
||||
# NOTE: We don't eagerly compute calendar values here because
|
||||
# `register` is called at module scope in catalyst, and creating a
|
||||
# calendar currently takes between 0.5 and 1 seconds, which causes a
|
||||
# noticeable delay on the catalyst CLI.
|
||||
_bundles[name] = RegisteredBundle(
|
||||
calendar_name=calendar_name,
|
||||
start_session=start_session,
|
||||
end_session=end_session,
|
||||
minutes_per_day=minutes_per_day,
|
||||
ingest=f,
|
||||
create_writers=create_writers,
|
||||
)
|
||||
return f
|
||||
|
||||
def unregister(name):
|
||||
"""Unregister a bundle.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name of the bundle to unregister.
|
||||
|
||||
Raises
|
||||
------
|
||||
UnknownBundle
|
||||
Raised when no bundle has been registered with the given name.
|
||||
|
||||
See Also
|
||||
--------
|
||||
catalyst.data.bundles.bundles
|
||||
"""
|
||||
try:
|
||||
del _bundles[name]
|
||||
except KeyError:
|
||||
raise UnknownBundle(name)
|
||||
|
||||
def ingest(name,
|
||||
environ=os.environ,
|
||||
timestamp=None,
|
||||
assets_versions=(),
|
||||
show_progress=False,
|
||||
is_compile=False):
|
||||
"""Ingest data for a given bundle.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name of the bundle.
|
||||
environ : mapping, optional
|
||||
The environment variables. By default this is os.environ.
|
||||
timestamp : datetime, optional
|
||||
The timestamp to use for the load.
|
||||
By default this is the current time.
|
||||
assets_versions : Iterable[int], optional
|
||||
Versions of the assets db to which to downgrade.
|
||||
show_progress : bool, optional
|
||||
Tell the ingest function to display the progress where possible.
|
||||
"""
|
||||
try:
|
||||
bundle = bundles[name]
|
||||
except KeyError:
|
||||
raise UnknownBundle(name)
|
||||
|
||||
calendar = get_calendar(bundle.calendar_name)
|
||||
|
||||
start_session = bundle.start_session
|
||||
end_session = bundle.end_session
|
||||
|
||||
if start_session is None or start_session < calendar.first_session:
|
||||
start_session = calendar.first_session
|
||||
|
||||
if end_session is None or end_session > calendar.last_session:
|
||||
end_session = calendar.last_session
|
||||
|
||||
if timestamp is None:
|
||||
timestamp = pd.Timestamp.utcnow()
|
||||
timestamp = timestamp.tz_convert('utc').tz_localize(None)
|
||||
|
||||
timestr = to_bundle_ingest_dirname(timestamp)
|
||||
cachepath = cache_path(name, environ=environ)
|
||||
pth.ensure_directory(pth.data_path([name, timestr], environ=environ))
|
||||
pth.ensure_directory(cachepath)
|
||||
with dataframe_cache(cachepath, clean_on_failure=False) as cache, \
|
||||
ExitStack() as stack:
|
||||
# we use `cleanup_on_failure=False` so that we don't purge the
|
||||
# cache directory if the load fails in the middle
|
||||
if bundle.create_writers:
|
||||
wd = stack.enter_context(working_dir(
|
||||
pth.data_path([], environ=environ))
|
||||
)
|
||||
daily_bars_path = wd.ensure_dir(
|
||||
*daily_relative(
|
||||
name, timestr, environ=environ,
|
||||
)
|
||||
)
|
||||
daily_bar_writer = BcolzDailyBarWriter(
|
||||
daily_bars_path,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
)
|
||||
# Do an empty write to ensure that the daily ctables exist
|
||||
# when we create the SQLiteAdjustmentWriter below. The
|
||||
# SQLiteAdjustmentWriter needs to open the daily ctables so
|
||||
# that it can compute the adjustment ratios for the dividends.
|
||||
daily_bar_writer.write(())
|
||||
|
||||
minute_bar_writer = BcolzMinuteBarWriter(
|
||||
wd.ensure_dir(*minute_relative(
|
||||
name, timestr, environ=environ)
|
||||
),
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
minutes_per_day=bundle.minutes_per_day,
|
||||
)
|
||||
|
||||
assets_db_path = wd.getpath(*asset_db_relative(
|
||||
name, timestr, environ=environ,
|
||||
))
|
||||
asset_db_writer = AssetDBWriter(assets_db_path)
|
||||
|
||||
adjustment_db_writer = stack.enter_context(
|
||||
SQLiteAdjustmentWriter(
|
||||
wd.getpath(*adjustment_db_relative(
|
||||
name, timestr, environ=environ)),
|
||||
BcolzDailyBarReader(daily_bars_path),
|
||||
calendar.all_sessions,
|
||||
overwrite=True,
|
||||
)
|
||||
)
|
||||
else:
|
||||
daily_bar_writer = None
|
||||
minute_bar_writer = None
|
||||
asset_db_writer = None
|
||||
adjustment_db_writer = None
|
||||
if assets_versions:
|
||||
raise ValueError('Need to ingest a bundle that creates '
|
||||
'writers in order to downgrade the assets'
|
||||
' db.')
|
||||
bundle.ingest(
|
||||
environ,
|
||||
asset_db_writer,
|
||||
minute_bar_writer,
|
||||
daily_bar_writer,
|
||||
adjustment_db_writer,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
cache,
|
||||
show_progress,
|
||||
is_compile,
|
||||
pth.data_path([name, timestr], environ=environ),
|
||||
)
|
||||
|
||||
for version in sorted(set(assets_versions), reverse=True):
|
||||
version_path = wd.getpath(*asset_db_relative(
|
||||
name, timestr, environ=environ, db_version=version,
|
||||
))
|
||||
with working_file(version_path) as wf:
|
||||
shutil.copy2(assets_db_path, wf.path)
|
||||
downgrade(wf.path, version)
|
||||
|
||||
def most_recent_data(bundle_name, timestamp, environ=None):
|
||||
"""Get the path to the most recent data after ``date``for the
|
||||
given bundle.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bundle_name : str
|
||||
The name of the bundle to lookup.
|
||||
timestamp : datetime
|
||||
The timestamp to begin searching on or before.
|
||||
environ : dict, optional
|
||||
An environment dict to forward to catalyst_root.
|
||||
"""
|
||||
if bundle_name not in bundles:
|
||||
raise UnknownBundle(bundle_name)
|
||||
|
||||
try:
|
||||
candidates = os.listdir(
|
||||
pth.data_path([bundle_name], environ=environ),
|
||||
)
|
||||
return pth.data_path(
|
||||
[bundle_name,
|
||||
max(
|
||||
filter(complement(pth.hidden), candidates),
|
||||
key=from_bundle_ingest_dirname,
|
||||
)],
|
||||
environ=environ,
|
||||
)
|
||||
except (ValueError, OSError) as e:
|
||||
if getattr(e, 'errno', errno.ENOENT) != errno.ENOENT:
|
||||
raise
|
||||
raise ValueError(
|
||||
'no data for bundle {bundle!r} on or before {timestamp}\n'
|
||||
'maybe you need to run: $ catalyst ingest -b {bundle}'.format(
|
||||
bundle=bundle_name,
|
||||
timestamp=timestamp,
|
||||
),
|
||||
)
|
||||
|
||||
def load(name, environ=os.environ, timestamp=None):
|
||||
"""Loads a previously ingested bundle.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name of the bundle.
|
||||
environ : mapping, optional
|
||||
The environment variables. Defaults of os.environ.
|
||||
timestamp : datetime, optional
|
||||
The timestamp of the data to lookup.
|
||||
Defaults to the current time.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bundle_data : BundleData
|
||||
The raw data readers for this bundle.
|
||||
"""
|
||||
if timestamp is None:
|
||||
timestamp = pd.Timestamp.utcnow()
|
||||
timestr = most_recent_data(name, timestamp, environ=environ)
|
||||
return BundleData(
|
||||
asset_finder=AssetFinder(
|
||||
asset_db_path(name, timestr, environ=environ),
|
||||
),
|
||||
minute_bar_reader=BcolzMinuteBarReader(
|
||||
minute_path(name, timestr, environ=environ),
|
||||
),
|
||||
daily_bar_reader=BcolzDailyBarReader(
|
||||
daily_path(name, timestr, environ=environ),
|
||||
),
|
||||
adjustment_reader=SQLiteAdjustmentReader(
|
||||
adjustment_db_path(name, timestr, environ=environ),
|
||||
),
|
||||
)
|
||||
|
||||
@preprocess(
|
||||
before=optionally(ensure_timestamp),
|
||||
after=optionally(ensure_timestamp),
|
||||
)
|
||||
def clean(name,
|
||||
before=None,
|
||||
after=None,
|
||||
keep_last=None,
|
||||
environ=os.environ):
|
||||
"""Clean up data that was created with ``ingest`` or
|
||||
``$ python -m catalyst ingest``
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name of the bundle to remove data for.
|
||||
before : datetime, optional
|
||||
Remove data ingested before this date.
|
||||
This argument is mutually exclusive with: keep_last
|
||||
after : datetime, optional
|
||||
Remove data ingested after this date.
|
||||
This argument is mutually exclusive with: keep_last
|
||||
keep_last : int, optional
|
||||
Remove all but the last ``keep_last`` ingestions.
|
||||
This argument is mutually exclusive with:
|
||||
before
|
||||
after
|
||||
environ : mapping, optional
|
||||
The environment variables. Defaults of os.environ.
|
||||
|
||||
Returns
|
||||
-------
|
||||
cleaned : set[str]
|
||||
The names of the runs that were removed.
|
||||
|
||||
Raises
|
||||
------
|
||||
BadClean
|
||||
Raised when ``before`` and or ``after`` are passed with
|
||||
``keep_last``. This is a subclass of ``ValueError``.
|
||||
"""
|
||||
try:
|
||||
all_runs = sorted(
|
||||
filter(
|
||||
complement(pth.hidden),
|
||||
os.listdir(pth.data_path([name], environ=environ)),
|
||||
),
|
||||
key=from_bundle_ingest_dirname,
|
||||
)
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
raise UnknownBundle(name)
|
||||
if ((before is not None or after is not None) and
|
||||
keep_last is not None):
|
||||
raise BadClean(before, after, keep_last)
|
||||
|
||||
if keep_last is None:
|
||||
def should_clean(name):
|
||||
dt = from_bundle_ingest_dirname(name)
|
||||
return (
|
||||
(before is not None and dt < before) or
|
||||
(after is not None and dt > after)
|
||||
)
|
||||
|
||||
elif keep_last >= 0:
|
||||
last_n_dts = set(take(keep_last, reversed(all_runs)))
|
||||
|
||||
def should_clean(name):
|
||||
return name not in last_n_dts
|
||||
else:
|
||||
raise BadClean(before, after, keep_last)
|
||||
|
||||
cleaned = set()
|
||||
for run in all_runs:
|
||||
if should_clean(run):
|
||||
path = pth.data_path([name, run], environ=environ)
|
||||
shutil.rmtree(path)
|
||||
cleaned.add(path)
|
||||
|
||||
return cleaned
|
||||
|
||||
return BundleCore(
|
||||
bundles,
|
||||
register_bundle,
|
||||
register,
|
||||
unregister,
|
||||
ingest,
|
||||
load,
|
||||
clean,
|
||||
)
|
||||
|
||||
|
||||
bundles, register_bundle, register, unregister, ingest, load, clean = _make_bundle_core()
|
||||
@@ -1,188 +0,0 @@
|
||||
#
|
||||
# Copyright 2017 Enigma MPC, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
||||
from catalyst.data.bundles.core import register_bundle
|
||||
from catalyst.data.bundles.base_pricing import BaseCryptoPricingBundle
|
||||
from catalyst.utils.memoize import lazyval
|
||||
|
||||
from catalyst.curate.poloniex import PoloniexCurator
|
||||
|
||||
class PoloniexBundle(BaseCryptoPricingBundle):
|
||||
@lazyval
|
||||
def name(self):
|
||||
return 'poloniex'
|
||||
|
||||
@lazyval
|
||||
def exchange(self):
|
||||
return 'POLO'
|
||||
|
||||
@lazyval
|
||||
def frequencies(self):
|
||||
return set((
|
||||
'daily',
|
||||
'minute',
|
||||
))
|
||||
|
||||
@lazyval
|
||||
def tar_url(self):
|
||||
return (
|
||||
'https://s3.amazonaws.com/enigmaco/catalyst-bundles/poloniex/poloniex-bundle.tar.gz'
|
||||
)
|
||||
|
||||
@lazyval
|
||||
def wait_time(self):
|
||||
return pd.Timedelta(milliseconds=170)
|
||||
|
||||
def fetch_raw_metadata_frame(self, api_key, page_number):
|
||||
if page_number > 1:
|
||||
return pd.DataFrame([])
|
||||
|
||||
raw = pd.read_json(
|
||||
self._format_metadata_url(
|
||||
api_key,
|
||||
page_number,
|
||||
),
|
||||
orient='index',
|
||||
)
|
||||
|
||||
raw = raw.sort_index().reset_index()
|
||||
raw.rename(
|
||||
columns={'index':'symbol'},
|
||||
inplace=True,
|
||||
)
|
||||
|
||||
raw = raw[raw['isFrozen'] == 0]
|
||||
|
||||
return raw
|
||||
|
||||
def post_process_symbol_metadata(self, asset_id, sym_md, sym_data):
|
||||
start_date = sym_data.index[0]
|
||||
end_date = sym_data.index[-1]
|
||||
ac_date = end_date + pd.Timedelta(days=1)
|
||||
min_trade_size = 0.00000001
|
||||
|
||||
return (
|
||||
sym_md.symbol,
|
||||
start_date,
|
||||
end_date,
|
||||
ac_date,
|
||||
min_trade_size,
|
||||
)
|
||||
|
||||
def fetch_raw_symbol_frame(self,
|
||||
api_key,
|
||||
symbol,
|
||||
calendar,
|
||||
start_date,
|
||||
end_date,
|
||||
frequency):
|
||||
|
||||
# TODO: replace this with direct exchange call
|
||||
# The end date and frequency should be used to calculate the number of bars
|
||||
if(frequency == 'minute'):
|
||||
pc = PoloniexCurator()
|
||||
raw = pc.onemin_to_dataframe(symbol, start_date, end_date)
|
||||
|
||||
else:
|
||||
raw = pd.read_json(
|
||||
self._format_data_url(
|
||||
api_key,
|
||||
symbol,
|
||||
start_date,
|
||||
end_date,
|
||||
frequency,
|
||||
),
|
||||
orient='records',
|
||||
)
|
||||
raw.set_index('date', inplace=True)
|
||||
|
||||
# BcolzDailyBarReader introduces a 1/1000 factor in the way pricing is stored
|
||||
# on disk, which we compensate here to get the right pricing amounts
|
||||
# ref: data/us_equity_pricing.py
|
||||
scale = 1
|
||||
raw.loc[:, 'open'] /= scale
|
||||
raw.loc[:, 'high'] /= scale
|
||||
raw.loc[:, 'low'] /= scale
|
||||
raw.loc[:, 'close'] /= scale
|
||||
raw.loc[:, 'volume'] *= scale
|
||||
|
||||
return raw
|
||||
|
||||
'''
|
||||
HELPER METHODS
|
||||
'''
|
||||
|
||||
def _format_metadata_url(self, api_key, page_number):
|
||||
query_params = [
|
||||
('command', 'returnTicker'),
|
||||
]
|
||||
|
||||
return self._format_polo_query(query_params)
|
||||
|
||||
|
||||
def _format_data_url(self,
|
||||
api_key,
|
||||
symbol,
|
||||
start_date,
|
||||
end_date,
|
||||
data_frequency):
|
||||
period_map = {
|
||||
'daily': 86400,
|
||||
}
|
||||
|
||||
try:
|
||||
period = period_map[data_frequency]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
query_params = [
|
||||
('command', 'returnChartData'),
|
||||
('currencyPair', symbol),
|
||||
('start', start_date.value / 10**9),
|
||||
('end', end_date.value / 10**9),
|
||||
('period', period),
|
||||
]
|
||||
|
||||
return self._format_polo_query(query_params)
|
||||
|
||||
def _format_polo_query(self, query_params):
|
||||
# TODO: got against the exchange object
|
||||
return 'https://poloniex.com/public?{query}'.format(
|
||||
query=urlencode(query_params),
|
||||
)
|
||||
|
||||
'''
|
||||
As a second parameter, you can pass an array of currency pairs
|
||||
that will be processed as an asset_filter to only process that
|
||||
subset of assets in the bundle, such as:
|
||||
register_bundle(PoloniexBundle, ['USDT_BTC',])
|
||||
|
||||
For a production environment make sure to use (to bundle all pairs):
|
||||
register_bundle(PoloniexBundle)
|
||||
'''
|
||||
|
||||
if 'ingest' in sys.argv and '-c' in sys.argv:
|
||||
register_bundle(PoloniexBundle)
|
||||
else:
|
||||
register_bundle(PoloniexBundle, create_writers=False)
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
#
|
||||
# Copyright 2017 Enigma MPC, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
||||
from catalyst.data.bundles.core import register_bundle
|
||||
from catalyst.data.bundles.base_pricing import BaseEquityPricingBundle
|
||||
from catalyst.utils.memoize import lazyval
|
||||
|
||||
"""
|
||||
Module for building a complete daily dataset from Quandl's WIKI dataset.
|
||||
"""
|
||||
from itertools import count
|
||||
import tarfile
|
||||
from time import time, sleep
|
||||
from datetime import datetime
|
||||
|
||||
from logbook import Logger
|
||||
import pandas as pd
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
||||
from catalyst.utils.calendars import register_calendar_alias
|
||||
from catalyst.utils.cli import maybe_show_progress
|
||||
|
||||
from . import core as bundles
|
||||
|
||||
log = Logger(__name__)
|
||||
seconds_per_call = (pd.Timedelta('10 minutes') / 2000).total_seconds()
|
||||
|
||||
class QuandlBundle(BaseEquityPricingBundle):
|
||||
@lazyval
|
||||
def name(self):
|
||||
return 'quandl'
|
||||
|
||||
@lazyval
|
||||
def exchange(self):
|
||||
return 'QUANDL'
|
||||
|
||||
@lazyval
|
||||
def frequencies(self):
|
||||
return set(('daily',))
|
||||
|
||||
@lazyval
|
||||
def tar_url(self):
|
||||
return 'https://s3.amazonaws.com/quantopian-public-zipline-data/quandl'
|
||||
|
||||
@lazyval
|
||||
def wait_time(self):
|
||||
return pd.Timedelta(milliseconds=300)
|
||||
|
||||
@lazyval
|
||||
def _excluded_symbols(self):
|
||||
"""
|
||||
Invalid symbols that quandl has had in its metadata:
|
||||
"""
|
||||
return frozenset({'TEST123456789'})
|
||||
|
||||
def fetch_raw_metadata_frame(self, api_key, page_number):
|
||||
raw = pd.read_csv(
|
||||
self._format_metadata_url(api_key, page_number),
|
||||
date_parser=pd.tseries.tools.to_datetime,
|
||||
parse_dates=[
|
||||
'oldest_available_date',
|
||||
'newest_available_date',
|
||||
],
|
||||
dtype={
|
||||
'dataset_code': 'str',
|
||||
'name': 'str',
|
||||
'oldest_available_date': 'str',
|
||||
'newest_available_date': 'str',
|
||||
},
|
||||
usecols=[
|
||||
'dataset_code',
|
||||
'name',
|
||||
'oldest_available_date',
|
||||
'newest_available_date',
|
||||
],
|
||||
).rename(
|
||||
columns={
|
||||
'dataset_code': 'symbol',
|
||||
'name': 'asset_name',
|
||||
'oldest_available_date': 'start_date',
|
||||
'newest_available_date': 'end_date',
|
||||
},
|
||||
)
|
||||
|
||||
raw['start_date'] = raw['start_date'].astype(datetime)
|
||||
raw['end_date'] = raw['end_date'].astype(datetime)
|
||||
raw['ac_date'] = raw['end_date'] + pd.Timedelta(days=1)
|
||||
|
||||
# Filter out invalid symbols
|
||||
raw = raw[~raw.symbol.isin(self._excluded_symbols)]
|
||||
|
||||
# cut out all the other stuff in the name column
|
||||
# we need to escape the paren because it is actually splitting on a regex
|
||||
raw.asset_name = raw.asset_name.str.split(r' \(', 1).str.get(0)
|
||||
|
||||
return raw
|
||||
|
||||
def fetch_raw_symbol_frame(self,
|
||||
api_key,
|
||||
symbol,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
data_frequency):
|
||||
raw_data = pd.read_csv(
|
||||
self._format_wiki_url(
|
||||
api_key,
|
||||
symbol,
|
||||
start_session,
|
||||
end_session,
|
||||
data_frequency,
|
||||
),
|
||||
parse_dates=['Date'],
|
||||
index_col='Date',
|
||||
usecols=[
|
||||
'Open',
|
||||
'High',
|
||||
'Low',
|
||||
'Close',
|
||||
'Volume',
|
||||
'Date',
|
||||
'Ex-Dividend',
|
||||
'Split Ratio',
|
||||
],
|
||||
na_values=['NA'],
|
||||
).rename(columns={
|
||||
'Open': 'open',
|
||||
'High': 'high',
|
||||
'Low': 'low',
|
||||
'Close': 'close',
|
||||
'Volume': 'volume',
|
||||
'Date': 'date',
|
||||
'Ex-Dividend': 'ex_dividend',
|
||||
'Split Ratio': 'split_ratio',
|
||||
})
|
||||
|
||||
sessions = calendar.sessions_in_range(start_session, end_session)
|
||||
|
||||
return raw_data.reindex(
|
||||
sessions.tz_localize(None),
|
||||
copy=False,
|
||||
).fillna(0.0)
|
||||
|
||||
def post_process_symbol_metadata(self, asset_id, sym_md, sym_data):
|
||||
self._update_splits(asset_id, sym_data)
|
||||
self._update_dividends(asset_id, sym_data)
|
||||
|
||||
return sym_md
|
||||
|
||||
def _update_splits(self, asset_id, raw_data):
|
||||
split_ratios = raw_data.split_ratio
|
||||
df = pd.DataFrame({'ratio': 1 / split_ratios[split_ratios != 1]})
|
||||
df.index.name = 'effective_date'
|
||||
df.reset_index(inplace=True)
|
||||
df['sid'] = asset_id
|
||||
self.splits.append(df)
|
||||
|
||||
|
||||
def _update_dividends(self, asset_id, raw_data):
|
||||
divs = raw_data.ex_dividend
|
||||
df = pd.DataFrame({'amount': divs[divs != 0]})
|
||||
df.index.name = 'ex_date'
|
||||
df.reset_index(inplace=True)
|
||||
df['sid'] = asset_id
|
||||
# we do not have this data in the WIKI dataset
|
||||
df['record_date'] = df['declared_date'] = df['pay_date'] = pd.NaT
|
||||
self.dividends.append(df)
|
||||
|
||||
|
||||
def _format_metadata_url(self, api_key, page_number):
|
||||
"""Build the query RL for the quandl WIKI metadata.
|
||||
"""
|
||||
query_params = [
|
||||
('per_page', '100'),
|
||||
('sort_by', 'id'),
|
||||
('page', str(page_number)),
|
||||
('database_code', 'WIKI'),
|
||||
]
|
||||
if api_key is not None:
|
||||
query_params = [('api_key', api_key)] + query_params
|
||||
|
||||
return (
|
||||
'https://www.quandl.com/api/v3/datasets.csv?' + urlencode(query_params)
|
||||
)
|
||||
|
||||
|
||||
def _format_wiki_url(self,
|
||||
api_key,
|
||||
symbol,
|
||||
start_date,
|
||||
end_date,
|
||||
data_frequency):
|
||||
"""
|
||||
Build a query URL for a quandl WIKI dataset.
|
||||
"""
|
||||
query_params = [
|
||||
('start_date', start_date.strftime('%Y-%m-%d')),
|
||||
('end_date', end_date.strftime('%Y-%m-%d')),
|
||||
('order', 'asc'),
|
||||
]
|
||||
if api_key is not None:
|
||||
query_params = [('api_key', api_key)] + query_params
|
||||
|
||||
return (
|
||||
"https://www.quandl.com/api/v3/datasets/WIKI/"
|
||||
"{symbol}.csv?{query}".format(
|
||||
symbol=symbol,
|
||||
query=urlencode(query_params),
|
||||
)
|
||||
)
|
||||
|
||||
register_calendar_alias('QUANDL', 'NYSE')
|
||||
register_bundle(QuandlBundle)
|
||||
@@ -1,203 +0,0 @@
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from pandas_datareader.data import DataReader
|
||||
import requests
|
||||
|
||||
from catalyst.utils.calendars import register_calendar_alias
|
||||
from catalyst.utils.cli import maybe_show_progress
|
||||
from .core import register
|
||||
|
||||
|
||||
def _cachpath(symbol, type_):
|
||||
return '-'.join((symbol.replace(os.path.sep, '_'), type_))
|
||||
|
||||
|
||||
def yahoo_equities(symbols, start=None, end=None):
|
||||
"""Create a data bundle ingest function from a set of symbols loaded from
|
||||
yahoo.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbols : iterable[str]
|
||||
The ticker symbols to load data for.
|
||||
start : datetime, optional
|
||||
The start date to query for. By default this pulls the full history
|
||||
for the calendar.
|
||||
end : datetime, optional
|
||||
The end date to query for. By default this pulls the full history
|
||||
for the calendar.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ingest : callable
|
||||
The bundle ingest function for the given set of symbols.
|
||||
|
||||
Examples
|
||||
--------
|
||||
This code should be added to ~/.catalyst/extension.py
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from catalyst.data.bundles import yahoo_equities, register
|
||||
|
||||
symbols = (
|
||||
'AAPL',
|
||||
'IBM',
|
||||
'MSFT',
|
||||
)
|
||||
register('my_bundle', yahoo_equities(symbols))
|
||||
|
||||
Notes
|
||||
-----
|
||||
The sids for each symbol will be the index into the symbols sequence.
|
||||
"""
|
||||
# strict this in memory so that we can reiterate over it
|
||||
symbols = tuple(symbols)
|
||||
|
||||
def ingest(environ,
|
||||
asset_db_writer,
|
||||
minute_bar_writer, # unused
|
||||
daily_bar_writer,
|
||||
adjustment_writer,
|
||||
calendar,
|
||||
start_session,
|
||||
end_session,
|
||||
cache,
|
||||
show_progress,
|
||||
output_dir,
|
||||
# pass these as defaults to make them 'nonlocal' in py2
|
||||
start=start,
|
||||
end=end):
|
||||
if start is None:
|
||||
start = start_session
|
||||
if end is None:
|
||||
end = None
|
||||
|
||||
metadata = pd.DataFrame(np.empty(len(symbols), dtype=[
|
||||
('start_date', 'datetime64[ns]'),
|
||||
('end_date', 'datetime64[ns]'),
|
||||
('auto_close_date', 'datetime64[ns]'),
|
||||
('symbol', 'object'),
|
||||
]))
|
||||
|
||||
def _pricing_iter():
|
||||
sid = 0
|
||||
with maybe_show_progress(
|
||||
symbols,
|
||||
show_progress,
|
||||
label='Downloading Yahoo pricing data: ') as it, \
|
||||
requests.Session() as session:
|
||||
for symbol in it:
|
||||
path = _cachpath(symbol, 'ohlcv')
|
||||
try:
|
||||
df = cache[path]
|
||||
except KeyError:
|
||||
df = cache[path] = DataReader(
|
||||
symbol,
|
||||
'yahoo',
|
||||
start,
|
||||
end,
|
||||
session=session,
|
||||
).sort_index()
|
||||
|
||||
# the start date is the date of the first trade and
|
||||
# the end date is the date of the last trade
|
||||
start_date = df.index[0]
|
||||
end_date = df.index[-1]
|
||||
# The auto_close date is the day after the last trade.
|
||||
ac_date = end_date + pd.Timedelta(days=1)
|
||||
metadata.iloc[sid] = start_date, end_date, ac_date, symbol
|
||||
|
||||
df.rename(
|
||||
columns={
|
||||
'Open': 'open',
|
||||
'High': 'high',
|
||||
'Low': 'low',
|
||||
'Close': 'close',
|
||||
'Volume': 'volume',
|
||||
},
|
||||
inplace=True,
|
||||
)
|
||||
yield sid, df
|
||||
sid += 1
|
||||
|
||||
daily_bar_writer.write(_pricing_iter(), show_progress=show_progress)
|
||||
|
||||
symbol_map = pd.Series(metadata.symbol.index, metadata.symbol)
|
||||
|
||||
# Hardcode the exchange to "YAHOO" for all assets and (elsewhere)
|
||||
# register "YAHOO" to resolve to the NYSE calendar, because these are
|
||||
# all equities and thus can use the NYSE calendar.
|
||||
metadata['exchange'] = "YAHOO"
|
||||
asset_db_writer.write(equities=metadata)
|
||||
|
||||
adjustments = []
|
||||
with maybe_show_progress(
|
||||
symbols,
|
||||
show_progress,
|
||||
label='Downloading Yahoo adjustment data: ') as it, \
|
||||
requests.Session() as session:
|
||||
for symbol in it:
|
||||
path = _cachpath(symbol, 'adjustment')
|
||||
try:
|
||||
df = cache[path]
|
||||
except KeyError:
|
||||
df = cache[path] = DataReader(
|
||||
symbol,
|
||||
'yahoo-actions',
|
||||
start,
|
||||
end,
|
||||
session=session,
|
||||
).sort_index()
|
||||
|
||||
df['sid'] = symbol_map[symbol]
|
||||
adjustments.append(df)
|
||||
|
||||
adj_df = pd.concat(adjustments)
|
||||
adj_df.index.name = 'date'
|
||||
adj_df.reset_index(inplace=True)
|
||||
|
||||
splits = adj_df[adj_df.action == 'SPLIT']
|
||||
splits = splits.rename(
|
||||
columns={'value': 'ratio', 'date': 'effective_date'},
|
||||
)
|
||||
splits.drop('action', axis=1, inplace=True)
|
||||
|
||||
dividends = adj_df[adj_df.action == 'DIVIDEND']
|
||||
dividends = dividends.rename(
|
||||
columns={'value': 'amount', 'date': 'ex_date'},
|
||||
)
|
||||
dividends.drop('action', axis=1, inplace=True)
|
||||
# we do not have this data in the yahoo dataset
|
||||
dividends['record_date'] = pd.NaT
|
||||
dividends['declared_date'] = pd.NaT
|
||||
dividends['pay_date'] = pd.NaT
|
||||
|
||||
adjustment_writer.write(splits=splits, dividends=dividends)
|
||||
|
||||
return ingest
|
||||
|
||||
|
||||
# bundle used when creating test data
|
||||
register(
|
||||
'.test',
|
||||
yahoo_equities(
|
||||
(
|
||||
'AMD',
|
||||
'CERN',
|
||||
'COST',
|
||||
'DELL',
|
||||
'GPS',
|
||||
'INTC',
|
||||
'MMM',
|
||||
'AAPL',
|
||||
'MSFT',
|
||||
),
|
||||
pd.Timestamp('2004-01-02', tz='utc'),
|
||||
pd.Timestamp('2015-01-01', tz='utc'),
|
||||
),
|
||||
)
|
||||
|
||||
register_calendar_alias("YAHOO", "NYSE")
|
||||
@@ -1,359 +0,0 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from catalyst.data.session_bars import SessionBarReader
|
||||
|
||||
|
||||
class ContinuousFutureSessionBarReader(SessionBarReader):
|
||||
|
||||
def __init__(self, bar_reader, roll_finders):
|
||||
self._bar_reader = bar_reader
|
||||
self._roll_finders = roll_finders
|
||||
|
||||
def load_raw_arrays(self, columns, start_date, end_date, assets):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
fields : list of str
|
||||
'sid'
|
||||
start_dt: Timestamp
|
||||
Beginning of the window range.
|
||||
end_dt: Timestamp
|
||||
End of the window range.
|
||||
sids : list of int
|
||||
The asset identifiers in the window.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of np.ndarray
|
||||
A list with an entry per field of ndarrays with shape
|
||||
(minutes in range, sids) with a dtype of float64, containing the
|
||||
values for the respective field over start and end dt range.
|
||||
"""
|
||||
rolls_by_asset = {}
|
||||
for asset in assets:
|
||||
rf = self._roll_finders[asset.roll_style]
|
||||
rolls_by_asset[asset] = rf.get_rolls(
|
||||
asset.root_symbol, start_date, end_date, asset.offset)
|
||||
num_sessions = len(
|
||||
self.trading_calendar.sessions_in_range(start_date, end_date))
|
||||
shape = num_sessions, len(assets)
|
||||
|
||||
results = []
|
||||
|
||||
tc = self._bar_reader.trading_calendar
|
||||
sessions = tc.sessions_in_range(start_date, end_date)
|
||||
|
||||
# Get partitions
|
||||
partitions_by_asset = {}
|
||||
for asset in assets:
|
||||
partitions = []
|
||||
partitions_by_asset[asset] = partitions
|
||||
rolls = rolls_by_asset[asset]
|
||||
start = start_date
|
||||
for roll in rolls:
|
||||
sid, roll_date = roll
|
||||
start_loc = sessions.get_loc(start)
|
||||
if roll_date is not None:
|
||||
end = roll_date - sessions.freq
|
||||
end_loc = sessions.get_loc(end)
|
||||
else:
|
||||
end = end_date
|
||||
end_loc = len(sessions) - 1
|
||||
partitions.append((sid, start, end, start_loc, end_loc))
|
||||
if roll[-1] is not None:
|
||||
start = sessions[end_loc + 1]
|
||||
|
||||
for column in columns:
|
||||
if column != 'volume' and column != 'sid':
|
||||
out = np.full(shape, np.nan)
|
||||
else:
|
||||
out = np.zeros(shape, dtype=np.int64)
|
||||
for i, asset in enumerate(assets):
|
||||
partitions = partitions_by_asset[asset]
|
||||
for sid, start, end, start_loc, end_loc in partitions:
|
||||
if column != 'sid':
|
||||
result = self._bar_reader.load_raw_arrays(
|
||||
[column], start, end, [sid])[0][:, 0]
|
||||
else:
|
||||
result = int(sid)
|
||||
out[start_loc:end_loc + 1, i] = result
|
||||
results.append(out)
|
||||
return results
|
||||
|
||||
@property
|
||||
def last_available_dt(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
dt : pd.Timestamp
|
||||
The last session for which the reader can provide data.
|
||||
"""
|
||||
return self._bar_reader.last_available_dt
|
||||
|
||||
@property
|
||||
def trading_calendar(self):
|
||||
"""
|
||||
Returns the catalyst.utils.calendar.trading_calendar used to read
|
||||
the data. Can be None (if the writer didn't specify it).
|
||||
"""
|
||||
return self._bar_reader.trading_calendar
|
||||
|
||||
@property
|
||||
def first_trading_day(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
dt : pd.Timestamp
|
||||
The first trading day (session) for which the reader can provide
|
||||
data.
|
||||
"""
|
||||
return self._bar_reader.first_trading_day
|
||||
|
||||
def get_value(self, continuous_future, dt, field):
|
||||
"""
|
||||
Retrieve the value at the given coordinates.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sid : int
|
||||
The asset identifier.
|
||||
dt : pd.Timestamp
|
||||
The timestamp for the desired data point.
|
||||
field : string
|
||||
The OHLVC name for the desired data point.
|
||||
|
||||
Returns
|
||||
-------
|
||||
value : float|int
|
||||
The value at the given coordinates, ``float`` for OHLC, ``int``
|
||||
for 'volume'.
|
||||
|
||||
Raises
|
||||
------
|
||||
NoDataOnDate
|
||||
If the given dt is not a valid market minute (in minute mode) or
|
||||
session (in daily mode) according to this reader's tradingcalendar.
|
||||
"""
|
||||
rf = self._roll_finders[continuous_future.roll_style]
|
||||
sid = (rf.get_contract_center(continuous_future.root_symbol,
|
||||
dt,
|
||||
continuous_future.offset))
|
||||
return self._bar_reader.get_value(sid, dt, field)
|
||||
|
||||
def get_last_traded_dt(self, asset, dt):
|
||||
"""
|
||||
Get the latest minute on or before ``dt`` in which ``asset`` traded.
|
||||
|
||||
If there are no trades on or before ``dt``, returns ``pd.NaT``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : catalyst.asset.Asset
|
||||
The asset for which to get the last traded minute.
|
||||
dt : pd.Timestamp
|
||||
The minute at which to start searching for the last traded minute.
|
||||
|
||||
Returns
|
||||
-------
|
||||
last_traded : pd.Timestamp
|
||||
The dt of the last trade for the given asset, using the input
|
||||
dt as a vantage point.
|
||||
"""
|
||||
rf = self._roll_finders[asset.roll_style]
|
||||
sid = (rf.get_contract_center(asset.root_symbol,
|
||||
dt,
|
||||
asset.offset))
|
||||
if sid is None:
|
||||
return pd.NaT
|
||||
contract = rf.asset_finder.retrieve_asset(sid)
|
||||
return self._bar_reader.get_last_traded_dt(contract, dt)
|
||||
|
||||
@property
|
||||
def sessions(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
sessions : DatetimeIndex
|
||||
All session labels (unionining the range for all assets) which the
|
||||
reader can provide.
|
||||
"""
|
||||
return self._bar_reader.sessions
|
||||
|
||||
|
||||
class ContinuousFutureMinuteBarReader(SessionBarReader):
|
||||
|
||||
def __init__(self, bar_reader, roll_finders):
|
||||
self._bar_reader = bar_reader
|
||||
self._roll_finders = roll_finders
|
||||
|
||||
def load_raw_arrays(self, columns, start_date, end_date, assets):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
fields : list of str
|
||||
'open', 'high', 'low', 'close', or 'volume'
|
||||
start_dt: Timestamp
|
||||
Beginning of the window range.
|
||||
end_dt: Timestamp
|
||||
End of the window range.
|
||||
sids : list of int
|
||||
The asset identifiers in the window.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of np.ndarray
|
||||
A list with an entry per field of ndarrays with shape
|
||||
(minutes in range, sids) with a dtype of float64, containing the
|
||||
values for the respective field over start and end dt range.
|
||||
"""
|
||||
rolls_by_asset = {}
|
||||
|
||||
tc = self.trading_calendar
|
||||
start_session = tc.minute_to_session_label(start_date)
|
||||
end_session = tc.minute_to_session_label(end_date)
|
||||
|
||||
for asset in assets:
|
||||
rf = self._roll_finders[asset.roll_style]
|
||||
rolls_by_asset[asset] = rf.get_rolls(
|
||||
asset.root_symbol,
|
||||
start_session,
|
||||
end_session, asset.offset)
|
||||
|
||||
sessions = tc.sessions_in_range(start_date, end_date)
|
||||
|
||||
minutes = tc.minutes_in_range(start_date, end_date)
|
||||
num_minutes = len(minutes)
|
||||
shape = num_minutes, len(assets)
|
||||
|
||||
results = []
|
||||
|
||||
# Get partitions
|
||||
partitions_by_asset = {}
|
||||
for asset in assets:
|
||||
partitions = []
|
||||
partitions_by_asset[asset] = partitions
|
||||
rolls = rolls_by_asset[asset]
|
||||
start = start_date
|
||||
for roll in rolls:
|
||||
sid, roll_date = roll
|
||||
start_loc = minutes.searchsorted(start)
|
||||
if roll_date is not None:
|
||||
_, end = tc.open_and_close_for_session(
|
||||
roll_date - sessions.freq)
|
||||
end_loc = minutes.searchsorted(end)
|
||||
else:
|
||||
end = end_date
|
||||
end_loc = len(minutes) - 1
|
||||
partitions.append((sid, start, end, start_loc, end_loc))
|
||||
if roll[-1] is not None:
|
||||
start, _ = tc.open_and_close_for_session(
|
||||
tc.minute_to_session_label(minutes[end_loc + 1]))
|
||||
|
||||
for column in columns:
|
||||
if column != 'volume':
|
||||
out = np.full(shape, np.nan)
|
||||
else:
|
||||
out = np.zeros(shape, dtype=np.uint32)
|
||||
for i, asset in enumerate(assets):
|
||||
partitions = partitions_by_asset[asset]
|
||||
for sid, start, end, start_loc, end_loc in partitions:
|
||||
if column != 'sid':
|
||||
result = self._bar_reader.load_raw_arrays(
|
||||
[column], start, end, [sid])[0][:, 0]
|
||||
else:
|
||||
result = int(sid)
|
||||
out[start_loc:end_loc + 1, i] = result
|
||||
results.append(out)
|
||||
return results
|
||||
|
||||
@property
|
||||
def last_available_dt(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
dt : pd.Timestamp
|
||||
The last session for which the reader can provide data.
|
||||
"""
|
||||
return self._bar_reader.last_available_dt
|
||||
|
||||
@property
|
||||
def trading_calendar(self):
|
||||
"""
|
||||
Returns the catalyst.utils.calendar.trading_calendar used to read
|
||||
the data. Can be None (if the writer didn't specify it).
|
||||
"""
|
||||
return self._bar_reader.trading_calendar
|
||||
|
||||
@property
|
||||
def first_trading_day(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
dt : pd.Timestamp
|
||||
The first trading day (session) for which the reader can provide
|
||||
data.
|
||||
"""
|
||||
return self._bar_reader.first_trading_day
|
||||
|
||||
def get_value(self, continuous_future, dt, field):
|
||||
"""
|
||||
Retrieve the value at the given coordinates.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sid : int
|
||||
The asset identifier.
|
||||
dt : pd.Timestamp
|
||||
The timestamp for the desired data point.
|
||||
field : string
|
||||
The OHLVC name for the desired data point.
|
||||
|
||||
Returns
|
||||
-------
|
||||
value : float|int
|
||||
The value at the given coordinates, ``float`` for OHLC, ``int``
|
||||
for 'volume'.
|
||||
|
||||
Raises
|
||||
------
|
||||
NoDataOnDate
|
||||
If the given dt is not a valid market minute (in minute mode) or
|
||||
session (in daily mode) according to this reader's tradingcalendar.
|
||||
"""
|
||||
rf = self._roll_finders[continuous_future.roll_style]
|
||||
sid = (rf.get_contract_center(continuous_future.root_symbol,
|
||||
dt,
|
||||
continuous_future.offset))
|
||||
return self._bar_reader.get_value(sid, dt, field)
|
||||
|
||||
def get_last_traded_dt(self, asset, dt):
|
||||
"""
|
||||
Get the latest minute on or before ``dt`` in which ``asset`` traded.
|
||||
|
||||
If there are no trades on or before ``dt``, returns ``pd.NaT``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : catalyst.asset.Asset
|
||||
The asset for which to get the last traded minute.
|
||||
dt : pd.Timestamp
|
||||
The minute at which to start searching for the last traded minute.
|
||||
|
||||
Returns
|
||||
-------
|
||||
last_traded : pd.Timestamp
|
||||
The dt of the last trade for the given asset, using the input
|
||||
dt as a vantage point.
|
||||
"""
|
||||
rf = self._roll_finders[asset.roll_style]
|
||||
sid = (rf.get_contract_center(asset.root_symbol,
|
||||
dt,
|
||||
asset.offset))
|
||||
if sid is None:
|
||||
return pd.NaT
|
||||
contract = rf.asset_finder.retrieve_asset(sid)
|
||||
return self._bar_reader.get_last_traded_dt(contract, dt)
|
||||
|
||||
@property
|
||||
def sessions(self):
|
||||
return self._bar_reader.sessions
|
||||
@@ -1,150 +0,0 @@
|
||||
#
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
from numpy import (
|
||||
full,
|
||||
nan,
|
||||
int64,
|
||||
float64,
|
||||
zeros
|
||||
)
|
||||
from six import iteritems, with_metaclass
|
||||
|
||||
from catalyst.utils.memoize import lazyval
|
||||
|
||||
|
||||
class AssetDispatchBarReader(with_metaclass(ABCMeta)):
|
||||
"""
|
||||
|
||||
Parameters
|
||||
----------
|
||||
- trading_calendar : catalyst.utils.trading_calendar.TradingCalendar
|
||||
- asset_finder : catalyst.assets.AssetFinder
|
||||
- readers : dict
|
||||
A dict mapping Asset type to the corresponding
|
||||
[Minute|Session]BarReader
|
||||
- last_available_dt : pd.Timestamp or None, optional
|
||||
If not provided, infers it by using the min of the
|
||||
last_available_dt values of the underlying readers.
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
trading_calendar,
|
||||
asset_finder,
|
||||
readers,
|
||||
last_available_dt=None,
|
||||
):
|
||||
self._trading_calendar = trading_calendar
|
||||
self._asset_finder = asset_finder
|
||||
self._readers = readers
|
||||
self._last_available_dt = last_available_dt
|
||||
|
||||
for t, r in iteritems(self._readers):
|
||||
assert trading_calendar == r.trading_calendar, \
|
||||
"All readers must share target trading_calendar. " \
|
||||
"Reader={0} for type={1} uses calendar={2} which does not " \
|
||||
"match the desired shared calendar={3} ".format(
|
||||
r, t, r.trading_calendar, trading_calendar)
|
||||
|
||||
@abstractmethod
|
||||
def _dt_window_size(self, start_dt, end_dt):
|
||||
pass
|
||||
|
||||
@property
|
||||
def _asset_types(self):
|
||||
return self._readers.keys()
|
||||
|
||||
def _make_raw_array_shape(self, start_dt, end_dt, num_sids):
|
||||
return self._dt_window_size(start_dt, end_dt), num_sids
|
||||
|
||||
def _make_raw_array_out(self, field, shape):
|
||||
if field == 'volume':
|
||||
out = zeros(shape, dtype=float64)
|
||||
elif field != 'sid':
|
||||
out = full(shape, nan)
|
||||
else:
|
||||
out = zeros(shape, dtype=int64)
|
||||
return out
|
||||
|
||||
@property
|
||||
def trading_calendar(self):
|
||||
return self._trading_calendar
|
||||
|
||||
@lazyval
|
||||
def last_available_dt(self):
|
||||
if self._last_available_dt is not None:
|
||||
return self._last_available_dt
|
||||
else:
|
||||
return min(r.last_available_dt for r in self._readers.values())
|
||||
|
||||
@lazyval
|
||||
def first_trading_day(self):
|
||||
return max(r.first_trading_day for r in self._readers.values())
|
||||
|
||||
def get_value(self, sid, dt, field):
|
||||
asset = self._asset_finder.retrieve_asset(sid)
|
||||
r = self._readers[type(asset)]
|
||||
return r.get_value(asset, dt, field)
|
||||
|
||||
def get_last_traded_dt(self, asset, dt):
|
||||
r = self._readers[type(asset)]
|
||||
return r.get_last_traded_dt(asset, dt)
|
||||
|
||||
def load_raw_arrays(self, fields, start_dt, end_dt, sids):
|
||||
asset_types = self._asset_types
|
||||
sid_groups = {t: [] for t in asset_types}
|
||||
out_pos = {t: [] for t in asset_types}
|
||||
|
||||
assets = self._asset_finder.retrieve_all(sids)
|
||||
|
||||
for i, asset in enumerate(assets):
|
||||
t = type(asset)
|
||||
sid_groups[t].append(asset)
|
||||
out_pos[t].append(i)
|
||||
|
||||
batched_arrays = {
|
||||
t: self._readers[t].load_raw_arrays(fields,
|
||||
start_dt,
|
||||
end_dt,
|
||||
sid_groups[t])
|
||||
for t in asset_types if sid_groups[t]}
|
||||
|
||||
results = []
|
||||
shape = self._make_raw_array_shape(start_dt, end_dt, len(sids))
|
||||
|
||||
for i, field in enumerate(fields):
|
||||
out = self._make_raw_array_out(field, shape)
|
||||
for t, arrays in iteritems(batched_arrays):
|
||||
out[:, out_pos[t]] = arrays[i]
|
||||
results.append(out)
|
||||
|
||||
return results
|
||||
|
||||
class AssetDispatchMinuteBarReader(AssetDispatchBarReader):
|
||||
|
||||
def _dt_window_size(self, start_dt, end_dt):
|
||||
return len(self.trading_calendar.minutes_in_range(start_dt, end_dt))
|
||||
|
||||
class AssetDispatchSessionBarReader(AssetDispatchBarReader):
|
||||
|
||||
def _dt_window_size(self, start_dt, end_dt):
|
||||
return len(self.trading_calendar.sessions_in_range(start_dt, end_dt))
|
||||
|
||||
@lazyval
|
||||
def sessions(self):
|
||||
return self.trading_calendar.sessions_in_range(
|
||||
self.first_trading_day,
|
||||
self.last_available_dt)
|
||||
@@ -1,597 +0,0 @@
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from abc import (
|
||||
ABCMeta,
|
||||
abstractmethod,
|
||||
abstractproperty,
|
||||
)
|
||||
|
||||
from numpy import concatenate
|
||||
from lru import LRU
|
||||
from pandas import isnull
|
||||
from pandas.tslib import normalize_date
|
||||
from toolz import sliding_window
|
||||
|
||||
from six import with_metaclass
|
||||
|
||||
from catalyst.assets import Equity, Future
|
||||
from catalyst.assets.continuous_futures import ContinuousFuture
|
||||
from catalyst.lib._int64window import AdjustedArrayWindow as Int64Window
|
||||
from catalyst.lib._float64window import AdjustedArrayWindow as Float64Window
|
||||
from catalyst.lib.adjustment import Float64Multiply, Float64Add
|
||||
from catalyst.utils.cache import ExpiringCache
|
||||
from catalyst.utils.math_utils import number_of_decimal_places
|
||||
from catalyst.utils.memoize import lazyval
|
||||
from catalyst.utils.numpy_utils import float64_dtype
|
||||
from catalyst.utils.pandas_utils import find_in_sorted_index
|
||||
|
||||
# Default number of decimal places used for rounding asset prices.
|
||||
DEFAULT_ASSET_PRICE_DECIMALS = 9
|
||||
|
||||
|
||||
class HistoryCompatibleUSEquityAdjustmentReader(object):
|
||||
|
||||
def __init__(self, adjustment_reader):
|
||||
self._adjustments_reader = adjustment_reader
|
||||
|
||||
def load_adjustments(self, columns, dts, assets):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
adjustments : list[dict[int -> Adjustment]]
|
||||
A list, where each element corresponds to the `columns`, of
|
||||
mappings from index to adjustment objects to apply at that index.
|
||||
"""
|
||||
out = [None] * len(columns)
|
||||
for i, column in enumerate(columns):
|
||||
adjs = {}
|
||||
for asset in assets:
|
||||
adjs.update(self._get_adjustments_in_range(
|
||||
asset, dts, column))
|
||||
out[i] = adjs
|
||||
return out
|
||||
|
||||
def _get_adjustments_in_range(self, asset, dts, field):
|
||||
"""
|
||||
Get the Float64Multiply objects to pass to an AdjustedArrayWindow.
|
||||
|
||||
For the use of AdjustedArrayWindow in the loader, which looks back
|
||||
from current simulation time back to a window of data the dictionary is
|
||||
structured with:
|
||||
- the key into the dictionary for adjustments is the location of the
|
||||
day from which the window is being viewed.
|
||||
- the start of all multiply objects is always 0 (in each window all
|
||||
adjustments are overlapping)
|
||||
- the end of the multiply object is the location before the calendar
|
||||
location of the adjustment action, making all days before the event
|
||||
adjusted.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
asset : Asset
|
||||
The assets for which to get adjustments.
|
||||
dts : iterable of datetime64-like
|
||||
The dts for which adjustment data is needed.
|
||||
field : str
|
||||
OHLCV field for which to get the adjustments.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : dict[loc -> Float64Multiply]
|
||||
The adjustments as a dict of loc -> Float64Multiply
|
||||
"""
|
||||
sid = int(asset)
|
||||
start = normalize_date(dts[0])
|
||||
end = normalize_date(dts[-1])
|
||||
adjs = {}
|
||||
if field != 'volume':
|
||||
mergers = self._adjustments_reader.get_adjustments_for_sid(
|
||||
'mergers', sid)
|
||||
for m in mergers:
|
||||
dt = m[0]
|
||||
if start < dt <= end:
|
||||
end_loc = dts.searchsorted(dt)
|
||||
adj_loc = end_loc
|
||||
mult = Float64Multiply(0,
|
||||
end_loc - 1,
|
||||
0,
|
||||
0,
|
||||
m[1])
|
||||
try:
|
||||
adjs[adj_loc].append(mult)
|
||||
except KeyError:
|
||||
adjs[adj_loc] = [mult]
|
||||
divs = self._adjustments_reader.get_adjustments_for_sid(
|
||||
'dividends', sid)
|
||||
for d in divs:
|
||||
dt = d[0]
|
||||
if start < dt <= end:
|
||||
end_loc = dts.searchsorted(dt)
|
||||
adj_loc = end_loc
|
||||
mult = Float64Multiply(0,
|
||||
end_loc - 1,
|
||||
0,
|
||||
0,
|
||||
d[1])
|
||||
try:
|
||||
adjs[adj_loc].append(mult)
|
||||
except KeyError:
|
||||
adjs[adj_loc] = [mult]
|
||||
splits = self._adjustments_reader.get_adjustments_for_sid(
|
||||
'splits', sid)
|
||||
for s in splits:
|
||||
dt = s[0]
|
||||
if start < dt <= end:
|
||||
if field == 'volume':
|
||||
ratio = 1.0 / s[1]
|
||||
else:
|
||||
ratio = s[1]
|
||||
end_loc = dts.searchsorted(dt)
|
||||
adj_loc = end_loc
|
||||
mult = Float64Multiply(0,
|
||||
end_loc - 1,
|
||||
0,
|
||||
0,
|
||||
ratio)
|
||||
try:
|
||||
adjs[adj_loc].append(mult)
|
||||
except KeyError:
|
||||
adjs[adj_loc] = [mult]
|
||||
return adjs
|
||||
|
||||
|
||||
class ContinuousFutureAdjustmentReader(object):
|
||||
"""
|
||||
Calculates adjustments for continuous futures, based on the
|
||||
close and open of the contracts on the either side of each roll.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
trading_calendar,
|
||||
asset_finder,
|
||||
bar_reader,
|
||||
roll_finders,
|
||||
frequency):
|
||||
self._trading_calendar = trading_calendar
|
||||
self._asset_finder = asset_finder
|
||||
self._bar_reader = bar_reader
|
||||
self._roll_finders = roll_finders
|
||||
self._frequency = frequency
|
||||
|
||||
def load_adjustments(self, columns, dts, assets):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
adjustments : list[dict[int -> Adjustment]]
|
||||
A list, where each element corresponds to the `columns`, of
|
||||
mappings from index to adjustment objects to apply at that index.
|
||||
"""
|
||||
out = [None] * len(columns)
|
||||
for i, column in enumerate(columns):
|
||||
adjs = {}
|
||||
for asset in assets:
|
||||
adjs.update(self._get_adjustments_in_range(
|
||||
asset, dts, column))
|
||||
out[i] = adjs
|
||||
return out
|
||||
|
||||
def _make_adjustment(self,
|
||||
adjustment_type,
|
||||
front_close,
|
||||
back_close,
|
||||
end_loc):
|
||||
adj_base = back_close - front_close
|
||||
if adjustment_type == 'mul':
|
||||
adj_value = 1.0 + adj_base / front_close
|
||||
adj_class = Float64Multiply
|
||||
elif adjustment_type == 'add':
|
||||
adj_value = adj_base
|
||||
adj_class = Float64Add
|
||||
return adj_class(0,
|
||||
end_loc,
|
||||
0,
|
||||
0,
|
||||
adj_value)
|
||||
|
||||
def _get_adjustments_in_range(self, cf, dts, field):
|
||||
if field == 'volume' or field == 'sid':
|
||||
return {}
|
||||
if cf.adjustment is None:
|
||||
return {}
|
||||
rf = self._roll_finders[cf.roll_style]
|
||||
partitions = []
|
||||
|
||||
rolls = rf.get_rolls(cf.root_symbol, dts[0], dts[-1],
|
||||
cf.offset)
|
||||
|
||||
tc = self._trading_calendar
|
||||
|
||||
adjs = {}
|
||||
|
||||
for front, back in sliding_window(2, rolls):
|
||||
front_sid, roll_dt = front
|
||||
back_sid = back[0]
|
||||
dt = tc.previous_session_label(roll_dt)
|
||||
if self._frequency == 'minute':
|
||||
dt = tc.open_and_close_for_session(dt)[1]
|
||||
roll_dt = tc.open_and_close_for_session(roll_dt)[0]
|
||||
partitions.append((front_sid,
|
||||
back_sid,
|
||||
dt,
|
||||
roll_dt))
|
||||
for partition in partitions:
|
||||
front_sid, back_sid, dt, roll_dt = partition
|
||||
last_front_dt = self._bar_reader.get_last_traded_dt(
|
||||
self._asset_finder.retrieve_asset(front_sid), dt)
|
||||
last_back_dt = self._bar_reader.get_last_traded_dt(
|
||||
self._asset_finder.retrieve_asset(back_sid), dt)
|
||||
if isnull(last_front_dt) or isnull(last_back_dt):
|
||||
continue
|
||||
front_close = self._bar_reader.get_value(
|
||||
front_sid, last_front_dt, 'close')
|
||||
back_close = self._bar_reader.get_value(
|
||||
back_sid, last_back_dt, 'close')
|
||||
adj_loc = dts.searchsorted(roll_dt)
|
||||
end_loc = adj_loc - 1
|
||||
adj = self._make_adjustment(cf.adjustment,
|
||||
front_close,
|
||||
back_close,
|
||||
end_loc)
|
||||
try:
|
||||
adjs[adj_loc].append(adj)
|
||||
except KeyError:
|
||||
adjs[adj_loc] = [adj]
|
||||
return adjs
|
||||
|
||||
|
||||
class SlidingWindow(object):
|
||||
"""
|
||||
Wrapper around an AdjustedArrayWindow which supports monotonically
|
||||
increasing (by datetime) requests for a sized window of data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
window : AdjustedArrayWindow
|
||||
Window of pricing data with prefetched values beyond the current
|
||||
simulation dt.
|
||||
cal_start : int
|
||||
Index in the overall calendar at which the window starts.
|
||||
"""
|
||||
|
||||
def __init__(self, window, size, cal_start, offset):
|
||||
self.window = window
|
||||
self.cal_start = cal_start
|
||||
self.current = next(window)
|
||||
self.offset = offset
|
||||
self.most_recent_ix = self.cal_start + size
|
||||
|
||||
def get(self, end_ix):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
out : A np.ndarray of the equity pricing up to end_ix after adjustments
|
||||
and rounding have been applied.
|
||||
"""
|
||||
if self.most_recent_ix == end_ix:
|
||||
return self.current
|
||||
|
||||
target = end_ix - self.cal_start - self.offset + 1
|
||||
self.current = self.window.seek(target)
|
||||
|
||||
self.most_recent_ix = end_ix
|
||||
return self.current
|
||||
|
||||
|
||||
class HistoryLoader(with_metaclass(ABCMeta)):
|
||||
"""
|
||||
Loader for sliding history windows, with support for adjustments.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
trading_calendar: TradingCalendar
|
||||
Contains the grouping logic needed to assign minutes to periods.
|
||||
reader : DailyBarReader, MinuteBarReader
|
||||
Reader for pricing bars.
|
||||
adjustment_reader : SQLiteAdjustmentReader
|
||||
Reader for adjustment data.
|
||||
"""
|
||||
FIELDS = ('open', 'high', 'low', 'close', 'volume', 'sid')
|
||||
|
||||
def __init__(self, trading_calendar, reader, equity_adjustment_reader,
|
||||
asset_finder,
|
||||
roll_finders=None,
|
||||
sid_cache_size=1000,
|
||||
prefetch_length=0):
|
||||
self.trading_calendar = trading_calendar
|
||||
self._asset_finder = asset_finder
|
||||
self._reader = reader
|
||||
self._adjustment_readers = {}
|
||||
if equity_adjustment_reader is not None:
|
||||
self._adjustment_readers[Equity] = \
|
||||
HistoryCompatibleUSEquityAdjustmentReader(
|
||||
equity_adjustment_reader)
|
||||
if roll_finders:
|
||||
self._adjustment_readers[ContinuousFuture] =\
|
||||
ContinuousFutureAdjustmentReader(trading_calendar,
|
||||
asset_finder,
|
||||
reader,
|
||||
roll_finders,
|
||||
self._frequency)
|
||||
self._window_blocks = {
|
||||
field: ExpiringCache(LRU(sid_cache_size))
|
||||
for field in self.FIELDS
|
||||
}
|
||||
self._prefetch_length = prefetch_length
|
||||
|
||||
@abstractproperty
|
||||
def _frequency(self):
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def _calendar(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def _array(self, start, end, assets, field):
|
||||
pass
|
||||
|
||||
def _decimal_places_for_asset(self, asset, reference_date):
|
||||
if isinstance(asset, Future) and asset.tick_size:
|
||||
return number_of_decimal_places(asset.tick_size)
|
||||
elif isinstance(asset, ContinuousFuture):
|
||||
# Tick size should be the same for all contracts of a continuous
|
||||
# future, so arbitrarily get the contract with next upcoming auto
|
||||
# close date.
|
||||
oc = self._asset_finder.get_ordered_contracts(asset.root_symbol)
|
||||
contract_sid = oc.contract_before_auto_close(reference_date.value)
|
||||
if contract_sid is not None:
|
||||
contract = self._asset_finder.retrieve_asset(contract_sid)
|
||||
if contract.tick_size:
|
||||
return number_of_decimal_places(contract.tick_size)
|
||||
return DEFAULT_ASSET_PRICE_DECIMALS
|
||||
|
||||
def _ensure_sliding_windows(self, assets, dts, field,
|
||||
is_perspective_after):
|
||||
"""
|
||||
Ensure that there is a Float64Multiply window for each asset that can
|
||||
provide data for the given parameters.
|
||||
If the corresponding window for the (assets, len(dts), field) does not
|
||||
exist, then create a new one.
|
||||
If a corresponding window does exist for (assets, len(dts), field), but
|
||||
can not provide data for the current dts range, then create a new
|
||||
one and replace the expired window.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
assets : iterable of Assets
|
||||
The assets in the window
|
||||
dts : iterable of datetime64-like
|
||||
The datetimes for which to fetch data.
|
||||
Makes an assumption that all dts are present and contiguous,
|
||||
in the calendar.
|
||||
field : str
|
||||
The OHLCV field for which to retrieve data.
|
||||
is_perspective_after : bool
|
||||
see: `PricingHistoryLoader.history`
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : list of Float64Window with sufficient data so that each asset's
|
||||
window can provide `get` for the index corresponding with the last
|
||||
value in `dts`
|
||||
"""
|
||||
end = dts[-1]
|
||||
size = len(dts)
|
||||
asset_windows = {}
|
||||
needed_assets = []
|
||||
cal = self._calendar
|
||||
|
||||
assets = self._asset_finder.retrieve_all(assets)
|
||||
end_ix = find_in_sorted_index(cal, end)
|
||||
|
||||
for asset in assets:
|
||||
try:
|
||||
window = self._window_blocks[field].get(
|
||||
(asset, size, is_perspective_after), end)
|
||||
except KeyError:
|
||||
needed_assets.append(asset)
|
||||
else:
|
||||
if end_ix < window.most_recent_ix:
|
||||
# Window needs reset. Requested end index occurs before the
|
||||
# end index from the previous history call for this window.
|
||||
# Grab new window instead of rewinding adjustments.
|
||||
needed_assets.append(asset)
|
||||
else:
|
||||
asset_windows[asset] = window
|
||||
|
||||
if needed_assets:
|
||||
offset = 0
|
||||
start_ix = find_in_sorted_index(cal, dts[0])
|
||||
|
||||
prefetch_end_ix = min(end_ix + self._prefetch_length, len(cal) - 1)
|
||||
prefetch_end = cal[prefetch_end_ix]
|
||||
prefetch_dts = cal[start_ix:prefetch_end_ix + 1]
|
||||
if is_perspective_after:
|
||||
adj_end_ix = min(prefetch_end_ix + 1, len(cal) - 1)
|
||||
adj_dts = cal[start_ix:adj_end_ix + 1]
|
||||
else:
|
||||
adj_dts = prefetch_dts
|
||||
prefetch_len = len(prefetch_dts)
|
||||
array = self._array(prefetch_dts, needed_assets, field)
|
||||
|
||||
if field == 'sid':
|
||||
window_type = Int64Window
|
||||
else:
|
||||
window_type = Float64Window
|
||||
|
||||
view_kwargs = {}
|
||||
if field == 'volume':
|
||||
array = array.astype(float64_dtype)
|
||||
|
||||
for i, asset in enumerate(needed_assets):
|
||||
adj_reader = None
|
||||
try:
|
||||
adj_reader = self._adjustment_readers[type(asset)]
|
||||
except KeyError:
|
||||
adj_reader = None
|
||||
if adj_reader is not None:
|
||||
adjs = adj_reader.load_adjustments(
|
||||
[field], adj_dts, [asset])[0]
|
||||
else:
|
||||
adjs = {}
|
||||
window = window_type(
|
||||
array[:, i].reshape(prefetch_len, 1),
|
||||
view_kwargs,
|
||||
adjs,
|
||||
offset,
|
||||
size,
|
||||
int(is_perspective_after),
|
||||
self._decimal_places_for_asset(asset, dts[-1]),
|
||||
)
|
||||
sliding_window = SlidingWindow(window, size, start_ix, offset)
|
||||
asset_windows[asset] = sliding_window
|
||||
self._window_blocks[field].set(
|
||||
(asset, size, is_perspective_after),
|
||||
sliding_window,
|
||||
prefetch_end)
|
||||
|
||||
return [asset_windows[asset] for asset in assets]
|
||||
|
||||
def history(self, assets, dts, field, is_perspective_after):
|
||||
"""
|
||||
A window of pricing data with adjustments applied assuming that the
|
||||
end of the window is the day before the current simulation time.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
assets : iterable of Assets
|
||||
The assets in the window.
|
||||
dts : iterable of datetime64-like
|
||||
The datetimes for which to fetch data.
|
||||
Makes an assumption that all dts are present and contiguous,
|
||||
in the calendar.
|
||||
field : str
|
||||
The OHLCV field for which to retrieve data.
|
||||
is_perspective_after : bool
|
||||
True, if the window is being viewed immediately after the last dt
|
||||
in the sliding window.
|
||||
False, if the window is viewed on the last dt.
|
||||
|
||||
This flag is used for handling the case where the last dt in the
|
||||
requested window immediately precedes a corporate action, e.g.:
|
||||
|
||||
- is_perspective_after is True
|
||||
|
||||
When the viewpoint is after the last dt in the window, as when a
|
||||
daily history window is accessed from a simulation that uses a
|
||||
minute data frequency, the history call to this loader will not
|
||||
include the current simulation dt. At that point in time, the raw
|
||||
data for the last day in the window will require adjustment, so the
|
||||
most recent adjustment with respect to the simulation time is
|
||||
applied to the last dt in the requested window.
|
||||
|
||||
An example equity which has a 0.5 split ratio dated for 05-27,
|
||||
with the dts for a history call of 5 bars with a '1d' frequency at
|
||||
05-27 9:31. Simulation frequency is 'minute'.
|
||||
|
||||
(In this case this function is called with 4 daily dts, and the
|
||||
calling function is responsible for stitching back on the
|
||||
'current' dt)
|
||||
|
||||
| | | | | last dt | <-- viewer is here |
|
||||
| | 05-23 | 05-24 | 05-25 | 05-26 | 05-27 9:31 |
|
||||
| raw | 10.10 | 10.20 | 10.30 | 10.40 | |
|
||||
| adj | 5.05 | 5.10 | 5.15 | 5.25 | |
|
||||
|
||||
The adjustment is applied to the last dt, 05-26, and all previous
|
||||
dts.
|
||||
|
||||
- is_perspective_after is False, daily
|
||||
|
||||
When the viewpoint is the same point in time as the last dt in the
|
||||
window, as when a daily history window is accessed from a
|
||||
simulation that uses a daily data frequency, the history call will
|
||||
include the current dt. At that point in time, the raw data for the
|
||||
last day in the window will be post-adjustment, so no adjustment
|
||||
is applied to the last dt.
|
||||
|
||||
An example equity which has a 0.5 split ratio dated for 05-27,
|
||||
with the dts for a history call of 5 bars with a '1d' frequency at
|
||||
05-27 0:00. Simulation frequency is 'daily'.
|
||||
|
||||
| | | | | | <-- viewer is here |
|
||||
| | | | | | last dt |
|
||||
| | 05-23 | 05-24 | 05-25 | 05-26 | 05-27 |
|
||||
| raw | 10.10 | 10.20 | 10.30 | 10.40 | 5.25 |
|
||||
| adj | 5.05 | 5.10 | 5.15 | 5.20 | 5.25 |
|
||||
|
||||
Adjustments are applied 05-23 through 05-26 but not to the last dt,
|
||||
05-27
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : np.ndarray with shape(len(days between start, end), len(assets))
|
||||
"""
|
||||
block = self._ensure_sliding_windows(assets,
|
||||
dts,
|
||||
field,
|
||||
is_perspective_after)
|
||||
end_ix = self._calendar.searchsorted(dts[-1])
|
||||
|
||||
return concatenate(
|
||||
[window.get(end_ix) for window in block],
|
||||
axis=1,
|
||||
)
|
||||
|
||||
|
||||
class DailyHistoryLoader(HistoryLoader):
|
||||
|
||||
@property
|
||||
def _frequency(self):
|
||||
return 'daily'
|
||||
|
||||
@property
|
||||
def _calendar(self):
|
||||
return self._reader.sessions
|
||||
|
||||
def _array(self, dts, assets, field):
|
||||
return self._reader.load_raw_arrays(
|
||||
[field],
|
||||
dts[0],
|
||||
dts[-1],
|
||||
assets,
|
||||
)[0]
|
||||
|
||||
|
||||
class MinuteHistoryLoader(HistoryLoader):
|
||||
|
||||
@property
|
||||
def _frequency(self):
|
||||
return 'minute'
|
||||
|
||||
@lazyval
|
||||
def _calendar(self):
|
||||
mm = self.trading_calendar.all_minutes
|
||||
start = mm.searchsorted(self._reader.first_trading_day)
|
||||
end = mm.searchsorted(self._reader.last_available_dt, side='right')
|
||||
return mm[start:end]
|
||||
|
||||
def _array(self, dts, assets, field):
|
||||
return self._reader.load_raw_arrays(
|
||||
[field],
|
||||
dts[0],
|
||||
dts[-1],
|
||||
assets,
|
||||
)[0]
|
||||
@@ -1,742 +0,0 @@
|
||||
#
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import datetime
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
|
||||
import logbook
|
||||
import pandas as pd
|
||||
import pytz
|
||||
from pandas_datareader.data import DataReader
|
||||
from six import iteritems
|
||||
from six.moves.urllib_error import HTTPError
|
||||
|
||||
from catalyst.utils.calendars import get_calendar
|
||||
from . import treasuries, treasuries_can
|
||||
from .benchmarks import get_benchmark_returns
|
||||
from ..utils.deprecate import deprecated
|
||||
from ..utils.paths import (
|
||||
cache_root,
|
||||
data_root,
|
||||
)
|
||||
|
||||
logger = logbook.Logger('Loader')
|
||||
|
||||
# Mapping from index symbol to appropriate bond data
|
||||
INDEX_MAPPING = {
|
||||
'SPY':
|
||||
(treasuries, 'treasury_curves.csv', 'www.federalreserve.gov'),
|
||||
'^GSPTSE':
|
||||
(treasuries_can, 'treasury_curves_can.csv', 'bankofcanada.ca'),
|
||||
'^FTSE': # use US treasuries until UK bonds implemented
|
||||
(treasuries, 'treasury_curves.csv', 'www.federalreserve.gov'),
|
||||
}
|
||||
|
||||
ONE_HOUR = pd.Timedelta(hours=1)
|
||||
|
||||
|
||||
def last_modified_time(path):
|
||||
"""
|
||||
Get the last modified time of path as a Timestamp.
|
||||
"""
|
||||
return pd.Timestamp(os.path.getmtime(path), unit='s', tz='UTC')
|
||||
|
||||
|
||||
def get_data_filepath(name, environ=None):
|
||||
"""
|
||||
Returns a handle to data file.
|
||||
|
||||
Creates containing directory, if needed.
|
||||
"""
|
||||
dr = data_root(environ)
|
||||
|
||||
if not os.path.exists(dr):
|
||||
os.makedirs(dr)
|
||||
|
||||
return os.path.join(dr, name)
|
||||
|
||||
|
||||
def get_cache_filepath(name):
|
||||
cr = cache_root()
|
||||
if not os.path.exists(cr):
|
||||
os.makedirs(cr)
|
||||
|
||||
return os.path.join(cr, name)
|
||||
|
||||
|
||||
def get_benchmark_filename(symbol):
|
||||
return "%s_benchmark.csv" % symbol
|
||||
|
||||
|
||||
def has_data_for_dates(series_or_df, first_date, last_date):
|
||||
"""
|
||||
Does `series_or_df` have data on or before first_date and on or after
|
||||
last_date?
|
||||
"""
|
||||
dts = series_or_df.index
|
||||
if not isinstance(dts, pd.DatetimeIndex):
|
||||
raise TypeError("Expected a DatetimeIndex, but got %s." % type(dts))
|
||||
first, last = dts[[0, -1]].tz_localize(None)
|
||||
return (first <= first_date.tz_localize(None)) and (
|
||||
last >= last_date.tz_localize(None))
|
||||
|
||||
|
||||
def load_crypto_market_data(trading_day=None, trading_days=None,
|
||||
bm_symbol=None, bundle=None, bundle_data=None,
|
||||
environ=None, exchange=None):
|
||||
if trading_day is None:
|
||||
trading_day = get_calendar('OPEN').trading_day
|
||||
|
||||
# TODO: consider making configurable
|
||||
bm_symbol = 'btc_usdt'
|
||||
# if trading_days is None:
|
||||
# trading_days = get_calendar('OPEN').schedule
|
||||
|
||||
first_date = get_calendar('OPEN').first_trading_session
|
||||
now = pd.Timestamp.utcnow()
|
||||
|
||||
# We expect to have benchmark and treasury data that's current up until
|
||||
# **two** full trading days prior to the most recently completed trading
|
||||
# day.
|
||||
# Example:
|
||||
# On Thu Oct 22 2015, the previous completed trading day is Wed Oct 21.
|
||||
# However, data for Oct 21 doesn't become available until the early morning
|
||||
# hours of Oct 22. This means that there are times on the 22nd at which we
|
||||
# cannot reasonably expect to have data for the 21st available. To be
|
||||
# conservative, we instead expect that at any time on the 22nd, we can
|
||||
# download data for Tuesday the 20th, which is two full trading days prior
|
||||
# to the date on which we're running a test.
|
||||
|
||||
# We'll attempt to download new data if the latest entry in our cache is
|
||||
# before this date.
|
||||
'''
|
||||
if(bundle_data):
|
||||
# If we are using the bundle to retrieve the cryptobenchmark, find the last
|
||||
# date for which there is trading data in the bundle
|
||||
asset = bundle_data.asset_finder.lookup_symbol(symbol=bm_symbol,as_of_date=None)
|
||||
ix = bundle_data.daily_bar_reader._last_rows[asset.sid]
|
||||
last_date = pd.to_datetime(bundle_data.daily_bar_reader._spot_col('day')[ix],unit='s')
|
||||
else:
|
||||
last_date = trading_days[trading_days.get_loc(now, method='ffill') - 2]
|
||||
'''
|
||||
last_date = trading_days[trading_days.get_loc(now, method='ffill') - 1]
|
||||
|
||||
if exchange is None:
|
||||
# This is exceptional, since placing the import at the module scope
|
||||
# breaks things and it's only needed here
|
||||
from catalyst.exchange.poloniex.poloniex import Poloniex
|
||||
exchange = Poloniex('', '', '')
|
||||
|
||||
benchmark_asset = exchange.get_asset(bm_symbol)
|
||||
|
||||
# exchange.get_history_window() already ensures that we have the right data
|
||||
# for the right dates
|
||||
br = exchange.get_history_window(
|
||||
assets=[benchmark_asset],
|
||||
end_dt=last_date,
|
||||
bar_count=pd.Timedelta(last_date - first_date).days,
|
||||
frequency='1d',
|
||||
field='close',
|
||||
data_frequency='daily')
|
||||
br.columns = ['close']
|
||||
br = br.pct_change(1).iloc[1:]
|
||||
br.loc[first_date]=0
|
||||
br=br.sort_index()
|
||||
|
||||
# Override first_date for treasury data since we have it for many more years
|
||||
# and is independent of crypto data
|
||||
first_date_treasury = pd.Timestamp('1990-01-02', tz='UTC')
|
||||
tc = ensure_treasury_data(
|
||||
bm_symbol,
|
||||
first_date_treasury,
|
||||
last_date,
|
||||
now,
|
||||
environ,
|
||||
)
|
||||
benchmark_returns = br[br.index.slice_indexer(first_date, last_date)]
|
||||
treasury_curves = tc[
|
||||
tc.index.slice_indexer(first_date_treasury, last_date)]
|
||||
return benchmark_returns, treasury_curves
|
||||
|
||||
|
||||
def load_market_data(trading_day=None, trading_days=None, bm_symbol='SPY',
|
||||
environ=None):
|
||||
"""
|
||||
Load benchmark returns and treasury yield curves for the given calendar and
|
||||
benchmark symbol.
|
||||
|
||||
Benchmarks are downloaded as a Series from Google Finance. Treasury curves
|
||||
are US Treasury Bond rates and are downloaded from 'www.federalreserve.gov'
|
||||
by default. For Canadian exchanges, a loader for Canadian bonds from the
|
||||
Bank of Canada is also available.
|
||||
|
||||
Results downloaded from the internet are cached in
|
||||
~/.catalyst/data. Subsequent loads will attempt to read from the cached
|
||||
files before falling back to redownload.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
trading_day : pandas.CustomBusinessDay, optional
|
||||
A trading_day used to determine the latest day for which we
|
||||
expect to have data. Defaults to an NYSE trading day.
|
||||
trading_days : pd.DatetimeIndex, optional
|
||||
A calendar of trading days. Also used for determining what cached
|
||||
dates we should expect to have cached. Defaults to the NYSE calendar.
|
||||
bm_symbol : str, optional
|
||||
Symbol for the benchmark index to load. Defaults to 'SPY', the Google
|
||||
ticker for the S&P 500.
|
||||
|
||||
Returns
|
||||
-------
|
||||
(benchmark_returns, treasury_curves) : (pd.Series, pd.DataFrame)
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Both return values are DatetimeIndexed with values dated to midnight in UTC
|
||||
of each stored date. The columns of `treasury_curves` are:
|
||||
|
||||
'1month', '3month', '6month',
|
||||
'1year','2year','3year','5year','7year','10year','20year','30year'
|
||||
"""
|
||||
if trading_day is None:
|
||||
trading_day = get_calendar('NYSE').trading_day
|
||||
if trading_days is None:
|
||||
trading_days = get_calendar('NYSE').all_sessions
|
||||
|
||||
first_date = trading_days[0]
|
||||
now = pd.Timestamp.utcnow()
|
||||
|
||||
# We expect to have benchmark and treasury data that's current up until
|
||||
# **two** full trading days prior to the most recently completed trading
|
||||
# day.
|
||||
# Example:
|
||||
# On Thu Oct 22 2015, the previous completed trading day is Wed Oct 21.
|
||||
# However, data for Oct 21 doesn't become available until the early morning
|
||||
# hours of Oct 22. This means that there are times on the 22nd at which we
|
||||
# cannot reasonably expect to have data for the 21st available. To be
|
||||
# conservative, we instead expect that at any time on the 22nd, we can
|
||||
# download data for Tuesday the 20th, which is two full trading days prior
|
||||
# to the date on which we're running a test.
|
||||
|
||||
# We'll attempt to download new data if the latest entry in our cache is
|
||||
# before this date.
|
||||
last_date = trading_days[trading_days.get_loc(now, method='ffill') - 2]
|
||||
|
||||
br = ensure_benchmark_data(
|
||||
bm_symbol,
|
||||
first_date,
|
||||
last_date,
|
||||
now,
|
||||
# We need the trading_day to figure out the close prior to the first
|
||||
# date so that we can compute returns for the first date.
|
||||
trading_day,
|
||||
environ,
|
||||
)
|
||||
tc = ensure_treasury_data(
|
||||
bm_symbol,
|
||||
first_date,
|
||||
last_date,
|
||||
now,
|
||||
environ,
|
||||
)
|
||||
benchmark_returns = br[br.index.slice_indexer(first_date, last_date)]
|
||||
treasury_curves = tc[tc.index.slice_indexer(first_date, last_date)]
|
||||
return benchmark_returns, treasury_curves
|
||||
|
||||
|
||||
def ensure_crypto_benchmark_data(symbol,
|
||||
first_date,
|
||||
last_date,
|
||||
now,
|
||||
trading_day,
|
||||
bundle,
|
||||
bundle_data,
|
||||
environ=None):
|
||||
filename = get_benchmark_filename(symbol)
|
||||
|
||||
logger.info(
|
||||
('Loading benchmark data for {symbol!r} '
|
||||
'from {first_date} to {last_date}'),
|
||||
symbol=symbol,
|
||||
first_date=first_date,
|
||||
last_date=last_date
|
||||
)
|
||||
|
||||
data = _load_cached_data(
|
||||
filename,
|
||||
first_date,
|
||||
last_date,
|
||||
now,
|
||||
'benchmark',
|
||||
environ,
|
||||
)
|
||||
|
||||
if data is not None:
|
||||
return data
|
||||
|
||||
# If no cached data was found or it was missing any dates then download the
|
||||
# necessary data.
|
||||
|
||||
if (bundle == 'poloniex'):
|
||||
'''
|
||||
If we're using the Poloniex bundle, we'll get the benchmark from the bundle
|
||||
instead of downloading it from Poloniex every time we need it.
|
||||
Poloniex has a captcha for API queries originating from outside the US that
|
||||
prevents users abroad from getting Catalyst to work
|
||||
'''
|
||||
logger.info(
|
||||
(
|
||||
'Retrieving benchmark data from bundle for {symbol!r} from {first_date} to {last_date}'),
|
||||
symbol=symbol, first_date=first_date, last_date=last_date)
|
||||
|
||||
asset = bundle_data.asset_finder.lookup_symbol(symbol=symbol,
|
||||
as_of_date=None)
|
||||
fields = ['day', 'close']
|
||||
raw = bundle_data.daily_bar_reader.load_raw_arrays(
|
||||
columns=fields,
|
||||
start_date=first_date - trading_day,
|
||||
end_date=last_date,
|
||||
assets=[asset, ])
|
||||
bench_raw = pd.concat([pd.DataFrame(raw[0], columns=['date']),
|
||||
pd.DataFrame(raw[1], columns=['close'])],
|
||||
axis=1)
|
||||
bench_raw['date'] = pd.to_datetime(bench_raw['date'], unit='s')
|
||||
bench_raw.set_index('date', inplace=True)
|
||||
bench_raw.sort_index(inplace=True)
|
||||
bench_raw = bench_raw[
|
||||
pd.to_datetime(first_date - trading_day):pd.to_datetime(
|
||||
last_date)]
|
||||
|
||||
else:
|
||||
# This is how it used to be: downloading the benchmark everytime.
|
||||
# Leaving this code here to be repurposed in the future for other bundles.
|
||||
logger.info(
|
||||
(
|
||||
'Downloading benchmark data for {symbol!r} from {first_date} to {last_date}'),
|
||||
symbol=symbol, first_date=first_date, last_date=last_date)
|
||||
|
||||
raise DeprecationWarning('poloniex bundle deprecated')
|
||||
# Load benchmark symbol from Poloniex API
|
||||
# try:
|
||||
# bundle = PoloniexBundle()
|
||||
# bench_raw = bundle._fetch_symbol_frame(
|
||||
# None,
|
||||
# symbol,
|
||||
# get_calendar(bundle.calendar_name),
|
||||
# first_date - trading_day,
|
||||
# last_date,
|
||||
# 'daily',
|
||||
# )
|
||||
# except (OSError, IOError, HTTPError):
|
||||
# logger.exception('Failed to fetch new crypto benchmark returns')
|
||||
# raise
|
||||
|
||||
# select close column and compute percent change between days
|
||||
daily_close = bench_raw[['close']]
|
||||
daily_close = daily_close.pct_change(1).iloc[1:]
|
||||
|
||||
try:
|
||||
# write to benchmark csv cache
|
||||
daily_close.to_csv(get_data_filepath(filename, environ))
|
||||
except (OSError, IOError, HTTPError):
|
||||
logger.exception('Failed to cache the new benchmark returns')
|
||||
raise
|
||||
|
||||
if not has_data_for_dates(daily_close, first_date, last_date):
|
||||
logger.warn("Still don't have expected data after redownload!")
|
||||
|
||||
return daily_close
|
||||
|
||||
|
||||
def ensure_benchmark_data(symbol, first_date, last_date, now, trading_day,
|
||||
environ=None):
|
||||
"""
|
||||
Ensure we have benchmark data for `symbol` from `first_date` to `last_date`
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : str
|
||||
The symbol for the benchmark to load.
|
||||
first_date : pd.Timestamp
|
||||
First required date for the cache.
|
||||
last_date : pd.Timestamp
|
||||
Last required date for the cache.
|
||||
now : pd.Timestamp
|
||||
The current time. This is used to prevent repeated attempts to
|
||||
re-download data that isn't available due to scheduling quirks or other
|
||||
failures.
|
||||
trading_day : pd.CustomBusinessDay
|
||||
A trading day delta. Used to find the day before first_date so we can
|
||||
get the close of the day prior to first_date.
|
||||
|
||||
We attempt to download data unless we already have data stored at the data
|
||||
cache for `symbol` whose first entry is before or on `first_date` and whose
|
||||
last entry is on or after `last_date`.
|
||||
|
||||
If we perform a download and the cache criteria are not satisfied, we wait
|
||||
at least one hour before attempting a redownload. This is determined by
|
||||
comparing the current time to the result of os.path.getmtime on the cache
|
||||
path.
|
||||
"""
|
||||
filename = get_benchmark_filename(symbol)
|
||||
data = _load_cached_data(filename, first_date, last_date, now, 'benchmark',
|
||||
environ)
|
||||
if data is not None:
|
||||
return data
|
||||
|
||||
# If no cached data was found or it was missing any dates then download the
|
||||
# necessary data.
|
||||
logger.info(
|
||||
('Downloading benchmark data for {symbol!r} '
|
||||
'from {first_date} to {last_date}'),
|
||||
symbol=symbol,
|
||||
first_date=first_date - trading_day,
|
||||
last_date=last_date
|
||||
)
|
||||
|
||||
try:
|
||||
data = get_benchmark_returns(
|
||||
symbol,
|
||||
first_date - trading_day,
|
||||
last_date,
|
||||
)
|
||||
data.to_csv(get_data_filepath(filename, environ))
|
||||
except (OSError, IOError, HTTPError):
|
||||
logger.exception('Failed to cache the new benchmark returns')
|
||||
raise
|
||||
if not has_data_for_dates(data, first_date, last_date):
|
||||
logger.warn("Still don't have expected data after redownload!")
|
||||
return data
|
||||
|
||||
|
||||
def ensure_benchmark_data(symbol, first_date, last_date, now, trading_day,
|
||||
environ=None):
|
||||
"""
|
||||
Ensure we have benchmark data for `symbol` from `first_date` to `last_date`
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : str
|
||||
The symbol for the benchmark to load.
|
||||
first_date : pd.Timestamp
|
||||
First required date for the cache.
|
||||
last_date : pd.Timestamp
|
||||
Last required date for the cache.
|
||||
now : pd.Timestamp
|
||||
The current time. This is used to prevent repeated attempts to
|
||||
re-download data that isn't available due to scheduling quirks or other
|
||||
failures.
|
||||
trading_day : pd.CustomBusinessDay
|
||||
A trading day delta. Used to find the day before first_date so we can
|
||||
get the close of the day prior to first_date.
|
||||
|
||||
We attempt to download data unless we already have data stored at the data
|
||||
cache for `symbol` whose first entry is before or on `first_date` and whose
|
||||
last entry is on or after `last_date`.
|
||||
|
||||
If we perform a download and the cache criteria are not satisfied, we wait
|
||||
at least one hour before attempting a redownload. This is determined by
|
||||
comparing the current time to the result of os.path.getmtime on the cache
|
||||
path.
|
||||
"""
|
||||
filename = get_benchmark_filename(symbol)
|
||||
data = _load_cached_data(filename, first_date, last_date, now, 'benchmark',
|
||||
environ)
|
||||
if data is not None:
|
||||
return data
|
||||
|
||||
# If no cached data was found or it was missing any dates then download the
|
||||
# necessary data.
|
||||
logger.info(
|
||||
('Downloading benchmark data for {symbol!r} '
|
||||
'from {first_date} to {last_date}'),
|
||||
symbol=symbol,
|
||||
first_date=first_date - trading_day,
|
||||
last_date=last_date
|
||||
)
|
||||
|
||||
try:
|
||||
data = get_benchmark_returns(
|
||||
symbol,
|
||||
first_date - trading_day,
|
||||
last_date,
|
||||
)
|
||||
data.to_csv(get_data_filepath(filename, environ))
|
||||
except (OSError, IOError, HTTPError):
|
||||
logger.exception('Failed to cache the new benchmark returns')
|
||||
raise
|
||||
if not has_data_for_dates(data, first_date, last_date):
|
||||
logger.warn("Still don't have expected data after redownload!")
|
||||
return data
|
||||
|
||||
|
||||
def ensure_treasury_data(symbol, first_date, last_date, now, environ=None):
|
||||
"""
|
||||
Ensure we have treasury data from treasury module associated with
|
||||
`symbol`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : str
|
||||
Benchmark symbol for which we're loading associated treasury curves.
|
||||
first_date : pd.Timestamp
|
||||
First date required to be in the cache.
|
||||
last_date : pd.Timestamp
|
||||
Last date required to be in the cache.
|
||||
now : pd.Timestamp
|
||||
The current time. This is used to prevent repeated attempts to
|
||||
re-download data that isn't available due to scheduling quirks or other
|
||||
failures.
|
||||
|
||||
We attempt to download data unless we already have data stored in the cache
|
||||
for `module_name` whose first entry is before or on `first_date` and whose
|
||||
last entry is on or after `last_date`.
|
||||
|
||||
If we perform a download and the cache criteria are not satisfied, we wait
|
||||
at least one hour before attempting a redownload. This is determined by
|
||||
comparing the current time to the result of os.path.getmtime on the cache
|
||||
path.
|
||||
"""
|
||||
loader_module, filename, source = INDEX_MAPPING.get(
|
||||
symbol, INDEX_MAPPING['SPY'],
|
||||
)
|
||||
first_date = max(first_date, loader_module.earliest_possible_date())
|
||||
|
||||
data = _load_cached_data(filename, first_date, last_date, now, 'treasury',
|
||||
environ)
|
||||
if data is not None:
|
||||
return data
|
||||
|
||||
# If no cached data was found or it was missing any dates then download the
|
||||
# necessary data.
|
||||
logger.info('Downloading treasury data for {symbol!r}.', symbol=symbol)
|
||||
|
||||
try:
|
||||
data = loader_module.get_treasury_data(first_date, last_date)
|
||||
data.to_csv(get_data_filepath(filename, environ))
|
||||
except (OSError, IOError, HTTPError):
|
||||
logger.exception('failed to cache treasury data')
|
||||
if not has_data_for_dates(data, first_date, last_date):
|
||||
logger.warn("Still don't have expected data after redownload!")
|
||||
return data
|
||||
|
||||
|
||||
def _load_cached_data(filename, first_date, last_date, now, resource_name,
|
||||
environ=None):
|
||||
# Path for the cache.
|
||||
path = get_data_filepath(filename, environ)
|
||||
|
||||
# If the path does not exist, it means the first download has not happened
|
||||
# yet, so don't try to read from 'path'.
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
data = pd.DataFrame.from_csv(path)
|
||||
if data.empty:
|
||||
raise ValueError("File is empty.")
|
||||
data.index = pd.to_datetime(data.index, infer_datetime_format=True,
|
||||
errors='coerce').tz_localize('UTC')
|
||||
if has_data_for_dates(data, first_date, last_date):
|
||||
return data
|
||||
|
||||
# Don't re-download if we've successfully downloaded and written a
|
||||
# file in the last hour.
|
||||
last_download_time = last_modified_time(path)
|
||||
if (now - last_download_time) <= ONE_HOUR:
|
||||
logger.warn(
|
||||
"Refusing to download new {resource} data because a "
|
||||
"download succeeded at {time}.",
|
||||
resource=resource_name,
|
||||
time=last_download_time,
|
||||
)
|
||||
return data
|
||||
|
||||
except (OSError, IOError, ValueError) as e:
|
||||
# These can all be raised by various versions of pandas on various
|
||||
# classes of malformed input. Treat them all as cache misses.
|
||||
logger.info(
|
||||
"Loading data for {path} failed with error [{error}].",
|
||||
path=path,
|
||||
error=e,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"Cache at {path} does not have data from {start} to {end}.",
|
||||
start=first_date,
|
||||
end=last_date,
|
||||
path=path,
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def _load_raw_yahoo_data(indexes=None, stocks=None, start=None, end=None):
|
||||
"""Load closing prices from yahoo finance.
|
||||
|
||||
:Optional:
|
||||
indexes : dict (Default: {'SPX': '^SPY'})
|
||||
Financial indexes to load.
|
||||
stocks : list (Default: ['AAPL', 'GE', 'IBM', 'MSFT',
|
||||
'XOM', 'AA', 'JNJ', 'PEP', 'KO'])
|
||||
Stock closing prices to load.
|
||||
start : datetime (Default: datetime(1993, 1, 1, 0, 0, 0, 0, pytz.utc))
|
||||
Retrieve prices from start date on.
|
||||
end : datetime (Default: datetime(2002, 1, 1, 0, 0, 0, 0, pytz.utc))
|
||||
Retrieve prices until end date.
|
||||
|
||||
:Note:
|
||||
This is based on code presented in a talk by Wes McKinney:
|
||||
http://wesmckinney.com/files/20111017/notebook_output.pdf
|
||||
"""
|
||||
assert indexes is not None or stocks is not None, """
|
||||
must specify stocks or indexes"""
|
||||
|
||||
if start is None:
|
||||
start = pd.datetime(1990, 1, 1, 0, 0, 0, 0, pytz.utc)
|
||||
|
||||
if start is not None and end is not None:
|
||||
assert start < end, "start date is later than end date."
|
||||
|
||||
data = OrderedDict()
|
||||
|
||||
if stocks is not None:
|
||||
for stock in stocks:
|
||||
logger.info('Loading stock: {}'.format(stock))
|
||||
stock_pathsafe = stock.replace(os.path.sep, '--')
|
||||
cache_filename = "{stock}-{start}-{end}.csv".format(
|
||||
stock=stock_pathsafe,
|
||||
start=start,
|
||||
end=end).replace(':', '-')
|
||||
cache_filepath = get_cache_filepath(cache_filename)
|
||||
if os.path.exists(cache_filepath):
|
||||
stkd = pd.DataFrame.from_csv(cache_filepath)
|
||||
else:
|
||||
stkd = DataReader(stock, 'yahoo', start, end).sort_index()
|
||||
stkd.to_csv(cache_filepath)
|
||||
data[stock] = stkd
|
||||
|
||||
if indexes is not None:
|
||||
for name, ticker in iteritems(indexes):
|
||||
logger.info('Loading index: {} ({})'.format(name, ticker))
|
||||
stkd = DataReader(ticker, 'yahoo', start, end).sort_index()
|
||||
data[name] = stkd
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def load_from_yahoo(indexes=None,
|
||||
stocks=None,
|
||||
start=None,
|
||||
end=None,
|
||||
adjusted=True):
|
||||
"""
|
||||
Loads price data from Yahoo into a dataframe for each of the indicated
|
||||
assets. By default, 'price' is taken from Yahoo's 'Adjusted Close',
|
||||
which removes the impact of splits and dividends. If the argument
|
||||
'adjusted' is False, then the non-adjusted 'close' field is used instead.
|
||||
|
||||
:param indexes: Financial indexes to load.
|
||||
:type indexes: dict
|
||||
:param stocks: Stock closing prices to load.
|
||||
:type stocks: list
|
||||
:param start: Retrieve prices from start date on.
|
||||
:type start: datetime
|
||||
:param end: Retrieve prices until end date.
|
||||
:type end: datetime
|
||||
:param adjusted: Adjust the price for splits and dividends.
|
||||
:type adjusted: bool
|
||||
|
||||
"""
|
||||
data = _load_raw_yahoo_data(indexes, stocks, start, end)
|
||||
if adjusted:
|
||||
close_key = 'Adj Close'
|
||||
else:
|
||||
close_key = 'Close'
|
||||
df = pd.DataFrame({key: d[close_key] for key, d in iteritems(data)})
|
||||
df.index = df.index.tz_localize(pytz.utc)
|
||||
return df
|
||||
|
||||
|
||||
@deprecated(
|
||||
'load_bars_from_yahoo is deprecated, please register a'
|
||||
' yahoo_equities data bundle instead',
|
||||
)
|
||||
def load_bars_from_yahoo(indexes=None,
|
||||
stocks=None,
|
||||
start=None,
|
||||
end=None,
|
||||
adjusted=True):
|
||||
"""
|
||||
Loads data from Yahoo into a panel with the following
|
||||
column names for each indicated security:
|
||||
|
||||
- open
|
||||
- high
|
||||
- low
|
||||
- close
|
||||
- volume
|
||||
- price
|
||||
|
||||
Note that 'price' is Yahoo's 'Adjusted Close', which removes the
|
||||
impact of splits and dividends. If the argument 'adjusted' is True, then
|
||||
the open, high, low, and close values are adjusted as well.
|
||||
|
||||
:param indexes: Financial indexes to load.
|
||||
:type indexes: dict
|
||||
:param stocks: Stock closing prices to load.
|
||||
:type stocks: list
|
||||
:param start: Retrieve prices from start date on.
|
||||
:type start: datetime
|
||||
:param end: Retrieve prices until end date.
|
||||
:type end: datetime
|
||||
:param adjusted: Adjust open/high/low/close for splits and dividends.
|
||||
The 'price' field is always adjusted.
|
||||
:type adjusted: bool
|
||||
|
||||
"""
|
||||
data = _load_raw_yahoo_data(indexes, stocks, start, end)
|
||||
panel = pd.Panel(data)
|
||||
# Rename columns
|
||||
panel.minor_axis = ['open', 'high', 'low', 'close', 'volume', 'price']
|
||||
panel.major_axis = panel.major_axis.tz_localize(pytz.utc)
|
||||
# Adjust data
|
||||
if adjusted:
|
||||
adj_cols = ['open', 'high', 'low', 'close']
|
||||
for ticker in panel.items:
|
||||
ratio = (panel[ticker]['price'] / panel[ticker]['close'])
|
||||
ratio_filtered = ratio.fillna(0).values
|
||||
for col in adj_cols:
|
||||
panel[ticker][col] *= ratio_filtered
|
||||
return panel
|
||||
|
||||
|
||||
def load_prices_from_csv(filepath, identifier_col, tz='UTC'):
|
||||
data = pd.read_csv(filepath, index_col=identifier_col)
|
||||
data.index = pd.DatetimeIndex(data.index, tz=tz)
|
||||
data.sort_index(inplace=True)
|
||||
return data
|
||||
|
||||
|
||||
def load_prices_from_csv_folder(folderpath, identifier_col, tz='UTC'):
|
||||
data = None
|
||||
for file in os.listdir(folderpath):
|
||||
if '.csv' not in file:
|
||||
continue
|
||||
raw = load_prices_from_csv(os.path.join(folderpath, file),
|
||||
identifier_col, tz)
|
||||
if data is None:
|
||||
data = raw
|
||||
else:
|
||||
data = pd.concat([data, raw], axis=1)
|
||||
return data
|
||||
@@ -1,735 +0,0 @@
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from collections import OrderedDict
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from six import with_metaclass
|
||||
|
||||
from catalyst.data._resample import (
|
||||
_minute_to_session_open,
|
||||
_minute_to_session_high,
|
||||
_minute_to_session_low,
|
||||
_minute_to_session_close,
|
||||
_minute_to_session_volume,
|
||||
)
|
||||
from catalyst.data.bar_reader import NoDataOnDate
|
||||
from catalyst.data.minute_bars import MinuteBarReader
|
||||
from catalyst.data.session_bars import SessionBarReader
|
||||
from catalyst.utils.memoize import lazyval
|
||||
|
||||
_MINUTE_TO_SESSION_OHCLV_HOW = OrderedDict((
|
||||
('open', 'first'),
|
||||
('high', 'max'),
|
||||
('low', 'min'),
|
||||
('close', 'last'),
|
||||
('volume', 'sum'),
|
||||
))
|
||||
|
||||
|
||||
def minute_frame_to_session_frame(minute_frame, calendar):
|
||||
|
||||
"""
|
||||
Resample a DataFrame with minute data into the frame expected by a
|
||||
BcolzDailyBarWriter.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
minute_frame : pd.DataFrame
|
||||
A DataFrame with the columns `open`, `high`, `low`, `close`, `volume`,
|
||||
and `dt` (minute dts)
|
||||
calendar : catalyst.utils.calendars.trading_calendar.TradingCalendar
|
||||
A TradingCalendar on which session labels to resample from minute
|
||||
to session.
|
||||
|
||||
Return
|
||||
------
|
||||
session_frame : pd.DataFrame
|
||||
A DataFrame with the columns `open`, `high`, `low`, `close`, `volume`,
|
||||
and `day` (datetime-like).
|
||||
"""
|
||||
how = OrderedDict((c, _MINUTE_TO_SESSION_OHCLV_HOW[c])
|
||||
for c in minute_frame.columns)
|
||||
return minute_frame.groupby(calendar.minute_to_session_label).agg(how)
|
||||
|
||||
|
||||
def minute_to_session(column, close_locs, data, out):
|
||||
"""
|
||||
Resample an array with minute data into an array with session data.
|
||||
|
||||
This function assumes that the minute data is the exact length of all
|
||||
minutes in the sessions in the output.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
column : str
|
||||
The `open`, `high`, `low`, `close`, or `volume` column.
|
||||
close_locs : array[intp]
|
||||
The locations in `data` which are the market close minutes.
|
||||
data : array[float64|uint32]
|
||||
The minute data to be sampled into session data.
|
||||
The first value should align with the market open of the first session,
|
||||
containing values for all minutes for all sessions. With the last value
|
||||
being the market close of the last session.
|
||||
out : array[float64|uint32]
|
||||
The output array into which to write the sampled sessions.
|
||||
"""
|
||||
if column == 'open':
|
||||
_minute_to_session_open(close_locs, data, out)
|
||||
elif column == 'high':
|
||||
_minute_to_session_high(close_locs, data, out)
|
||||
elif column == 'low':
|
||||
_minute_to_session_low(close_locs, data, out)
|
||||
elif column == 'close':
|
||||
_minute_to_session_close(close_locs, data, out)
|
||||
elif column == 'volume':
|
||||
_minute_to_session_volume(close_locs, data, out)
|
||||
return out
|
||||
|
||||
|
||||
class DailyHistoryAggregator(object):
|
||||
"""
|
||||
Converts minute pricing data into a daily summary, to be used for the
|
||||
last slot in a call to history with a frequency of `1d`.
|
||||
|
||||
This summary is the same as a daily bar rollup of minute data, with the
|
||||
distinction that the summary is truncated to the `dt` requested.
|
||||
i.e. the aggregation slides forward during a the course of simulation day.
|
||||
|
||||
Provides aggregation for `open`, `high`, `low`, `close`, and `volume`.
|
||||
The aggregation rules for each price type is documented in their respective
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, market_opens, minute_reader, trading_calendar):
|
||||
self._market_opens = market_opens
|
||||
self._minute_reader = minute_reader
|
||||
self._trading_calendar = trading_calendar
|
||||
|
||||
# The caches are structured as (date, market_open, entries), where
|
||||
# entries is a dict of asset -> (last_visited_dt, value)
|
||||
#
|
||||
# Whenever an aggregation method determines the current value,
|
||||
# the entry for the respective asset should be overwritten with a new
|
||||
# entry for the current dt.value (int) and aggregation value.
|
||||
#
|
||||
# When the requested dt's date is different from date the cache is
|
||||
# flushed, so that the cache entries do not grow unbounded.
|
||||
#
|
||||
# Example cache:
|
||||
# cache = (date(2016, 3, 17),
|
||||
# pd.Timestamp('2016-03-17 13:31', tz='UTC'),
|
||||
# {
|
||||
# 1: (1458221460000000000, np.nan),
|
||||
# 2: (1458221460000000000, 42.0),
|
||||
# })
|
||||
self._caches = {
|
||||
'open': None,
|
||||
'high': None,
|
||||
'low': None,
|
||||
'close': None,
|
||||
'volume': None
|
||||
}
|
||||
|
||||
# The int value is used for deltas to avoid extra computation from
|
||||
# creating new Timestamps.
|
||||
self._one_min = pd.Timedelta('1 min').value
|
||||
|
||||
def _prelude(self, dt, field):
|
||||
session = self._trading_calendar.minute_to_session_label(dt)
|
||||
dt_value = dt.value
|
||||
cache = self._caches[field]
|
||||
if cache is None or cache[0] != session:
|
||||
market_open = self._market_opens.loc[session]
|
||||
cache = self._caches[field] = (session, market_open, {})
|
||||
|
||||
_, market_open, entries = cache
|
||||
try:
|
||||
market_open = market_open.tz_localize('UTC')
|
||||
except TypeError:
|
||||
market_open = market_open.tz_convert('UTC')
|
||||
if dt != market_open:
|
||||
prev_dt = dt_value - self._one_min
|
||||
else:
|
||||
prev_dt = None
|
||||
return market_open, prev_dt, dt_value, entries
|
||||
|
||||
def opens(self, assets, dt):
|
||||
"""
|
||||
The open field's aggregation returns the first value that occurs
|
||||
for the day, if there has been no data on or before the `dt` the open
|
||||
is `nan`.
|
||||
|
||||
Once the first non-nan open is seen, that value remains constant per
|
||||
asset for the remainder of the day.
|
||||
|
||||
Returns
|
||||
-------
|
||||
np.array with dtype=float64, in order of assets parameter.
|
||||
"""
|
||||
market_open, prev_dt, dt_value, entries = self._prelude(dt, 'open')
|
||||
|
||||
opens = []
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
for asset in assets:
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
opens.append(np.NaN)
|
||||
continue
|
||||
|
||||
if prev_dt is None:
|
||||
val = self._minute_reader.get_value(asset, dt, 'open')
|
||||
entries[asset] = (dt_value, val)
|
||||
opens.append(val)
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
last_visited_dt, first_open = entries[asset]
|
||||
if last_visited_dt == dt_value:
|
||||
opens.append(first_open)
|
||||
continue
|
||||
elif not pd.isnull(first_open):
|
||||
opens.append(first_open)
|
||||
entries[asset] = (dt_value, first_open)
|
||||
continue
|
||||
else:
|
||||
after_last = pd.Timestamp(
|
||||
last_visited_dt + self._one_min, tz='UTC')
|
||||
window = self._minute_reader.load_raw_arrays(
|
||||
['open'],
|
||||
after_last,
|
||||
dt,
|
||||
[asset],
|
||||
)[0]
|
||||
nonnan = window[~pd.isnull(window)]
|
||||
if len(nonnan):
|
||||
val = nonnan[0]
|
||||
else:
|
||||
val = np.nan
|
||||
entries[asset] = (dt_value, val)
|
||||
opens.append(val)
|
||||
continue
|
||||
except KeyError:
|
||||
window = self._minute_reader.load_raw_arrays(
|
||||
['open'],
|
||||
market_open,
|
||||
dt,
|
||||
[asset],
|
||||
)[0]
|
||||
nonnan = window[~pd.isnull(window)]
|
||||
if len(nonnan):
|
||||
val = nonnan[0]
|
||||
else:
|
||||
val = np.nan
|
||||
entries[asset] = (dt_value, val)
|
||||
opens.append(val)
|
||||
continue
|
||||
return np.array(opens)
|
||||
|
||||
def highs(self, assets, dt):
|
||||
"""
|
||||
The high field's aggregation returns the largest high seen between
|
||||
the market open and the current dt.
|
||||
If there has been no data on or before the `dt` the high is `nan`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
np.array with dtype=float64, in order of assets parameter.
|
||||
"""
|
||||
market_open, prev_dt, dt_value, entries = self._prelude(dt, 'high')
|
||||
|
||||
highs = []
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
for asset in assets:
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
highs.append(np.NaN)
|
||||
continue
|
||||
|
||||
if prev_dt is None:
|
||||
val = self._minute_reader.get_value(asset, dt, 'high')
|
||||
entries[asset] = (dt_value, val)
|
||||
highs.append(val)
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
last_visited_dt, last_max = entries[asset]
|
||||
if last_visited_dt == dt_value:
|
||||
highs.append(last_max)
|
||||
continue
|
||||
elif last_visited_dt == prev_dt:
|
||||
curr_val = self._minute_reader.get_value(
|
||||
asset, dt, 'high')
|
||||
if pd.isnull(curr_val):
|
||||
val = last_max
|
||||
elif pd.isnull(last_max):
|
||||
val = curr_val
|
||||
else:
|
||||
val = max(last_max, curr_val)
|
||||
entries[asset] = (dt_value, val)
|
||||
highs.append(val)
|
||||
continue
|
||||
else:
|
||||
after_last = pd.Timestamp(
|
||||
last_visited_dt + self._one_min, tz='UTC')
|
||||
window = self._minute_reader.load_raw_arrays(
|
||||
['high'],
|
||||
after_last,
|
||||
dt,
|
||||
[asset],
|
||||
)[0].T
|
||||
val = np.nanmax(np.append(window, last_max))
|
||||
entries[asset] = (dt_value, val)
|
||||
highs.append(val)
|
||||
continue
|
||||
except KeyError:
|
||||
window = self._minute_reader.load_raw_arrays(
|
||||
['high'],
|
||||
market_open,
|
||||
dt,
|
||||
[asset],
|
||||
)[0].T
|
||||
val = np.nanmax(window)
|
||||
entries[asset] = (dt_value, val)
|
||||
highs.append(val)
|
||||
continue
|
||||
return np.array(highs)
|
||||
|
||||
def lows(self, assets, dt):
|
||||
"""
|
||||
The low field's aggregation returns the smallest low seen between
|
||||
the market open and the current dt.
|
||||
If there has been no data on or before the `dt` the low is `nan`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
np.array with dtype=float64, in order of assets parameter.
|
||||
"""
|
||||
market_open, prev_dt, dt_value, entries = self._prelude(dt, 'low')
|
||||
|
||||
lows = []
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
for asset in assets:
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
lows.append(np.NaN)
|
||||
continue
|
||||
|
||||
if prev_dt is None:
|
||||
val = self._minute_reader.get_value(asset, dt, 'low')
|
||||
entries[asset] = (dt_value, val)
|
||||
lows.append(val)
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
last_visited_dt, last_min = entries[asset]
|
||||
if last_visited_dt == dt_value:
|
||||
lows.append(last_min)
|
||||
continue
|
||||
elif last_visited_dt == prev_dt:
|
||||
curr_val = self._minute_reader.get_value(
|
||||
asset, dt, 'low')
|
||||
val = np.nanmin([last_min, curr_val])
|
||||
entries[asset] = (dt_value, val)
|
||||
lows.append(val)
|
||||
continue
|
||||
else:
|
||||
after_last = pd.Timestamp(
|
||||
last_visited_dt + self._one_min, tz='UTC')
|
||||
window = self._minute_reader.load_raw_arrays(
|
||||
['low'],
|
||||
after_last,
|
||||
dt,
|
||||
[asset],
|
||||
)[0].T
|
||||
val = np.nanmin(np.append(window, last_min))
|
||||
entries[asset] = (dt_value, val)
|
||||
lows.append(val)
|
||||
continue
|
||||
except KeyError:
|
||||
window = self._minute_reader.load_raw_arrays(
|
||||
['low'],
|
||||
market_open,
|
||||
dt,
|
||||
[asset],
|
||||
)[0].T
|
||||
val = np.nanmin(window)
|
||||
entries[asset] = (dt_value, val)
|
||||
lows.append(val)
|
||||
continue
|
||||
return np.array(lows)
|
||||
|
||||
def closes(self, assets, dt):
|
||||
"""
|
||||
The close field's aggregation returns the latest close at the given
|
||||
dt.
|
||||
If the close for the given dt is `nan`, the most recent non-nan
|
||||
`close` is used.
|
||||
If there has been no data on or before the `dt` the close is `nan`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
np.array with dtype=float64, in order of assets parameter.
|
||||
"""
|
||||
market_open, prev_dt, dt_value, entries = self._prelude(dt, 'close')
|
||||
|
||||
closes = []
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
def _get_filled_close(asset):
|
||||
"""
|
||||
Returns the most recent non-nan close for the asset in this
|
||||
session. If there has been no data in this session on or before the
|
||||
`dt`, returns `nan`
|
||||
"""
|
||||
window = self._minute_reader.load_raw_arrays(
|
||||
['close'],
|
||||
market_open,
|
||||
dt,
|
||||
[asset],
|
||||
)[0]
|
||||
try:
|
||||
return window[~np.isnan(window)][-1]
|
||||
except IndexError:
|
||||
return np.NaN
|
||||
|
||||
for asset in assets:
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
closes.append(np.NaN)
|
||||
continue
|
||||
|
||||
if prev_dt is None:
|
||||
val = self._minute_reader.get_value(asset, dt, 'close')
|
||||
entries[asset] = (dt_value, val)
|
||||
closes.append(val)
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
last_visited_dt, last_close = entries[asset]
|
||||
if last_visited_dt == dt_value:
|
||||
closes.append(last_close)
|
||||
continue
|
||||
elif last_visited_dt == prev_dt:
|
||||
val = self._minute_reader.get_value(
|
||||
asset, dt, 'close')
|
||||
if pd.isnull(val):
|
||||
val = last_close
|
||||
entries[asset] = (dt_value, val)
|
||||
closes.append(val)
|
||||
continue
|
||||
else:
|
||||
val = self._minute_reader.get_value(
|
||||
asset, dt, 'close')
|
||||
if pd.isnull(val):
|
||||
val = _get_filled_close(asset)
|
||||
entries[asset] = (dt_value, val)
|
||||
closes.append(val)
|
||||
continue
|
||||
except KeyError:
|
||||
val = self._minute_reader.get_value(
|
||||
asset, dt, 'close')
|
||||
if pd.isnull(val):
|
||||
val = _get_filled_close(asset)
|
||||
entries[asset] = (dt_value, val)
|
||||
closes.append(val)
|
||||
continue
|
||||
return np.array(closes)
|
||||
|
||||
def volumes(self, assets, dt):
|
||||
"""
|
||||
The volume field's aggregation returns the sum of all volumes
|
||||
between the market open and the `dt`
|
||||
If there has been no data on or before the `dt` the volume is 0.
|
||||
|
||||
Returns
|
||||
-------
|
||||
np.array with dtype=int64, in order of assets parameter.
|
||||
"""
|
||||
market_open, prev_dt, dt_value, entries = self._prelude(dt, 'volume')
|
||||
|
||||
volumes = []
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
for asset in assets:
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
volumes.append(0)
|
||||
continue
|
||||
|
||||
if prev_dt is None:
|
||||
val = self._minute_reader.get_value(asset, dt, 'volume')
|
||||
entries[asset] = (dt_value, val)
|
||||
volumes.append(val)
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
last_visited_dt, last_total = entries[asset]
|
||||
if last_visited_dt == dt_value:
|
||||
volumes.append(last_total)
|
||||
continue
|
||||
elif last_visited_dt == prev_dt:
|
||||
val = self._minute_reader.get_value(
|
||||
asset, dt, 'volume')
|
||||
val += last_total
|
||||
entries[asset] = (dt_value, val)
|
||||
volumes.append(val)
|
||||
continue
|
||||
else:
|
||||
after_last = pd.Timestamp(
|
||||
last_visited_dt + self._one_min, tz='UTC')
|
||||
window = self._minute_reader.load_raw_arrays(
|
||||
['volume'],
|
||||
after_last,
|
||||
dt,
|
||||
[asset],
|
||||
)[0]
|
||||
val = np.nansum(window) + last_total
|
||||
entries[asset] = (dt_value, val)
|
||||
volumes.append(val)
|
||||
continue
|
||||
except KeyError:
|
||||
window = self._minute_reader.load_raw_arrays(
|
||||
['volume'],
|
||||
market_open,
|
||||
dt,
|
||||
[asset],
|
||||
)[0]
|
||||
val = np.nansum(window)
|
||||
entries[asset] = (dt_value, val)
|
||||
volumes.append(val)
|
||||
continue
|
||||
return np.array(volumes)
|
||||
|
||||
|
||||
class MinuteResampleSessionBarReader(SessionBarReader):
|
||||
|
||||
def __init__(self, calendar, minute_bar_reader):
|
||||
self._calendar = calendar
|
||||
self._minute_bar_reader = minute_bar_reader
|
||||
|
||||
def _get_resampled(self, columns, start_session, end_session, assets):
|
||||
range_open = self._calendar.session_open(start_session)
|
||||
range_close = self._calendar.session_close(end_session)
|
||||
|
||||
minute_data = self._minute_bar_reader.load_raw_arrays(
|
||||
columns,
|
||||
range_open,
|
||||
range_close,
|
||||
assets,
|
||||
)
|
||||
|
||||
# Get the index of the close minute for each session in the range.
|
||||
# If the range contains only one session, the only close in the range
|
||||
# is the last minute in the data. Otherwise, we need to get all the
|
||||
# session closes and find their indices in the range of minutes.
|
||||
if start_session == end_session:
|
||||
close_ilocs = np.array([len(minute_data[0]) - 1], dtype=np.int64)
|
||||
else:
|
||||
minutes = self._calendar.minutes_in_range(
|
||||
range_open,
|
||||
range_close,
|
||||
)
|
||||
session_closes = self._calendar.session_closes_in_range(
|
||||
start_session,
|
||||
end_session,
|
||||
)
|
||||
close_ilocs = minutes.searchsorted(session_closes.values)
|
||||
|
||||
results = []
|
||||
shape = (len(close_ilocs), len(assets))
|
||||
|
||||
for col in columns:
|
||||
if col != 'volume':
|
||||
out = np.full(shape, np.nan)
|
||||
else:
|
||||
out = np.zeros(shape, dtype=np.uint32)
|
||||
results.append(out)
|
||||
|
||||
for i in range(len(assets)):
|
||||
for j, column in enumerate(columns):
|
||||
data = minute_data[j][:, i]
|
||||
minute_to_session(column, close_ilocs, data, results[j][:, i])
|
||||
|
||||
return results
|
||||
|
||||
@property
|
||||
def trading_calendar(self):
|
||||
return self._calendar
|
||||
|
||||
def load_raw_arrays(self, columns, start_dt, end_dt, sids):
|
||||
return self._get_resampled(columns, start_dt, end_dt, sids)
|
||||
|
||||
def get_value(self, sid, session, colname):
|
||||
# WARNING: This will need caching or other optimization if used in a
|
||||
# tight loop.
|
||||
# This was developed to complete interface, but has not been tuned
|
||||
# for real world use.
|
||||
return self._get_resampled([colname], session, session, [sid])[0][0][0]
|
||||
|
||||
@lazyval
|
||||
def sessions(self):
|
||||
cal = self._calendar
|
||||
first = self._minute_bar_reader.first_trading_day
|
||||
last = cal.minute_to_session_label(
|
||||
self._minute_bar_reader.last_available_dt)
|
||||
return cal.sessions_in_range(first, last)
|
||||
|
||||
@lazyval
|
||||
def last_available_dt(self):
|
||||
return self.trading_calendar.minute_to_session_label(
|
||||
self._minute_bar_reader.last_available_dt
|
||||
)
|
||||
|
||||
@property
|
||||
def first_trading_day(self):
|
||||
return self._minute_bar_reader.first_trading_day
|
||||
|
||||
def get_last_traded_dt(self, asset, dt):
|
||||
return self.trading_calendar.minute_to_session_label(
|
||||
self._minute_bar_reader.get_last_traded_dt(asset, dt))
|
||||
|
||||
|
||||
class ReindexBarReader(with_metaclass(ABCMeta)):
|
||||
"""
|
||||
A base class for readers which reindexes results, filling in the additional
|
||||
indices with empty data.
|
||||
|
||||
Used to align the reading assets which trade on different calendars.
|
||||
|
||||
Currently only supports a ``trading_calendar`` which is a superset of the
|
||||
``reader``'s calendar.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
- trading_calendar : catalyst.utils.trading_calendar.TradingCalendar
|
||||
The calendar to use when indexing results from the reader.
|
||||
- reader : MinuteBarReader|SessionBarReader
|
||||
The reader which has a calendar that is a subset of the desired
|
||||
``trading_calendar``.
|
||||
- first_trading_session : pd.Timestamp
|
||||
The first trading session the reader should provide. Must be specified,
|
||||
since the ``reader``'s first session may not exactly align with the
|
||||
desired calendar. Specifically, in the case where the first session
|
||||
on the target calendar is a holiday on the ``reader``'s calendar.
|
||||
- last_trading_session : pd.Timestamp
|
||||
The last trading session the reader should provide. Must be specified,
|
||||
since the ``reader``'s last session may not exactly align with the
|
||||
desired calendar. Specifically, in the case where the last session
|
||||
on the target calendar is a holiday on the ``reader``'s calendar.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
trading_calendar,
|
||||
reader,
|
||||
first_trading_session,
|
||||
last_trading_session):
|
||||
self._trading_calendar = trading_calendar
|
||||
self._reader = reader
|
||||
self._first_trading_session = first_trading_session
|
||||
self._last_trading_session = last_trading_session
|
||||
|
||||
@property
|
||||
def last_available_dt(self):
|
||||
return self._reader.last_available_dt
|
||||
|
||||
def get_last_traded_dt(self, sid, dt):
|
||||
return self._reader.get_last_traded_dt(sid, dt)
|
||||
|
||||
@property
|
||||
def first_trading_day(self):
|
||||
return self._reader.first_trading_day
|
||||
|
||||
def get_value(self, sid, dt, field):
|
||||
# Give an empty result if no data is present.
|
||||
try:
|
||||
return self._reader.get_value(sid, dt, field)
|
||||
except NoDataOnDate:
|
||||
if field == 'volume':
|
||||
return 0
|
||||
else:
|
||||
return np.nan
|
||||
|
||||
@abstractmethod
|
||||
def _outer_dts(self, start_dt, end_dt):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def _inner_dts(self, start_dt, end_dt):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def trading_calendar(self):
|
||||
return self._trading_calendar
|
||||
|
||||
@lazyval
|
||||
def sessions(self):
|
||||
return self.trading_calendar.sessions_in_range(
|
||||
self._first_trading_session,
|
||||
self._last_trading_session
|
||||
)
|
||||
|
||||
def load_raw_arrays(self, fields, start_dt, end_dt, sids):
|
||||
outer_dts = self._outer_dts(start_dt, end_dt)
|
||||
inner_dts = self._inner_dts(start_dt, end_dt)
|
||||
|
||||
indices = outer_dts.searchsorted(inner_dts)
|
||||
|
||||
shape = len(outer_dts), len(sids)
|
||||
|
||||
outer_results = []
|
||||
|
||||
if len(inner_dts) > 0:
|
||||
inner_results = self._reader.load_raw_arrays(
|
||||
fields, inner_dts[0], inner_dts[-1], sids)
|
||||
else:
|
||||
inner_results = None
|
||||
|
||||
for i, field in enumerate(fields):
|
||||
if field != 'volume':
|
||||
out = np.full(shape, np.nan)
|
||||
else:
|
||||
out = np.zeros(shape, dtype=np.uint32)
|
||||
|
||||
if inner_results is not None:
|
||||
out[indices] = inner_results[i]
|
||||
|
||||
outer_results.append(out)
|
||||
|
||||
return outer_results
|
||||
|
||||
|
||||
class ReindexMinuteBarReader(ReindexBarReader, MinuteBarReader):
|
||||
"""
|
||||
See: ``ReindexBarReader``
|
||||
"""
|
||||
|
||||
def _outer_dts(self, start_dt, end_dt):
|
||||
return self._trading_calendar.minutes_in_range(start_dt, end_dt)
|
||||
|
||||
def _inner_dts(self, start_dt, end_dt):
|
||||
return self._reader.calendar.minutes_in_range(start_dt, end_dt)
|
||||
|
||||
|
||||
class ReindexSessionBarReader(ReindexBarReader, SessionBarReader):
|
||||
"""
|
||||
See: ``ReindexBarReader``
|
||||
"""
|
||||
|
||||
def _outer_dts(self, start_dt, end_dt):
|
||||
return self.trading_calendar.sessions_in_range(start_dt, end_dt)
|
||||
|
||||
def _inner_dts(self, start_dt, end_dt):
|
||||
return self._reader.trading_calendar.sessions_in_range(
|
||||
start_dt, end_dt)
|
||||
@@ -1,36 +0,0 @@
|
||||
# Copyright 2016 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from abc import abstractproperty
|
||||
|
||||
from catalyst.data.bar_reader import BarReader
|
||||
|
||||
|
||||
class SessionBarReader(BarReader):
|
||||
"""
|
||||
Reader for OHCLV pricing data at a session frequency.
|
||||
"""
|
||||
@property
|
||||
def data_frequency(self):
|
||||
return 'session'
|
||||
|
||||
@abstractproperty
|
||||
def sessions(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
sessions : DatetimeIndex
|
||||
All session labels (unionining the range for all assets) which the
|
||||
reader can provide.
|
||||
"""
|
||||
pass
|
||||
@@ -1,101 +0,0 @@
|
||||
#
|
||||
# Copyright 2013 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from operator import itemgetter
|
||||
import re
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
get_unit_and_periods = itemgetter('unit', 'periods')
|
||||
|
||||
|
||||
def parse_treasury_csv_column(column):
|
||||
"""
|
||||
Parse a treasury CSV column into a more human-readable format.
|
||||
|
||||
Columns start with 'RIFLGFC', followed by Y or M (year or month), followed
|
||||
by a two-digit number signifying number of years/months, followed by _N.B.
|
||||
We only care about the middle two entries, which we turn into a string like
|
||||
3month or 30year.
|
||||
"""
|
||||
column_re = re.compile(
|
||||
r"^(?P<prefix>RIFLGFC)"
|
||||
"(?P<unit>[YM])"
|
||||
"(?P<periods>[0-9]{2})"
|
||||
"(?P<suffix>_N.B)$"
|
||||
)
|
||||
|
||||
match = column_re.match(column)
|
||||
if match is None:
|
||||
raise ValueError("Couldn't parse CSV column %r." % column)
|
||||
unit, periods = get_unit_and_periods(match.groupdict())
|
||||
|
||||
# Roundtrip through int to coerce '06' into '6'.
|
||||
return str(int(periods)) + ('year' if unit == 'Y' else 'month')
|
||||
|
||||
|
||||
def earliest_possible_date():
|
||||
"""
|
||||
The earliest date for which we can load data from this module.
|
||||
"""
|
||||
# The US Treasury actually has data going back further than this, but it's
|
||||
# pretty rare to find pricing data going back that far, and there's no
|
||||
# reason to make people download benchmarks back to 1950 that they'll never
|
||||
# be able to use.
|
||||
return pd.Timestamp('1980', tz='UTC')
|
||||
|
||||
|
||||
def get_treasury_data(start_date, end_date):
|
||||
return pd.read_csv(
|
||||
"https://www.federalreserve.gov/datadownload/Output.aspx"
|
||||
"?rel=H15"
|
||||
"&series=bf17364827e38702b42a58cf8eaa3f78"
|
||||
"&lastObs="
|
||||
"&from=" # An unbounded query is ~2x faster than specifying dates.
|
||||
"&to="
|
||||
"&filetype=csv"
|
||||
"&label=include"
|
||||
"&layout=seriescolumn"
|
||||
"&type=package",
|
||||
skiprows=5, # First 5 rows are useless headers.
|
||||
parse_dates=['Time Period'],
|
||||
na_values=['ND'], # Presumably this stands for "No Data".
|
||||
index_col=0,
|
||||
).loc[
|
||||
start_date:end_date
|
||||
].dropna(
|
||||
how='all'
|
||||
).rename(
|
||||
columns=parse_treasury_csv_column
|
||||
).tz_localize('UTC') * 0.01 # Convert from 2.57% to 0.0257.
|
||||
|
||||
|
||||
def dataconverter(s):
|
||||
try:
|
||||
return float(s) / 100
|
||||
except:
|
||||
return np.nan
|
||||
|
||||
|
||||
def get_daily_10yr_treasury_data():
|
||||
"""Download daily 10 year treasury rates from the Federal Reserve and
|
||||
return a pandas.Series."""
|
||||
url = "https://www.federalreserve.gov/datadownload/Output.aspx?rel=H15" \
|
||||
"&series=bcb44e57fb57efbe90002369321bfb3f&lastObs=&from=&to=" \
|
||||
"&filetype=csv&label=include&layout=seriescolumn"
|
||||
return pd.read_csv(url, header=5, index_col=0, names=['DATE', 'BC_10YEAR'],
|
||||
parse_dates=True, converters={1: dataconverter},
|
||||
squeeze=True)
|
||||
@@ -1,150 +0,0 @@
|
||||
#
|
||||
# Copyright 2013 Quantopian, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import pandas as pd
|
||||
import six
|
||||
from toolz import curry
|
||||
from toolz.curried.operator import add as prepend
|
||||
|
||||
COLUMN_NAMES = {
|
||||
"V39063": '1month',
|
||||
"V39065": '3month',
|
||||
"V39066": '6month',
|
||||
"V39067": '1year',
|
||||
"V39051": '2year',
|
||||
"V39052": '3year',
|
||||
"V39053": '5year',
|
||||
"V39054": '7year',
|
||||
"V39055": '10year',
|
||||
# Bank of Canada refers to this as 'Long' Rate, approximately 30 years.
|
||||
"V39056": '30year',
|
||||
}
|
||||
BILL_IDS = ['V39063', 'V39065', 'V39066', 'V39067']
|
||||
BOND_IDS = ['V39051', 'V39052', 'V39053', 'V39054', 'V39055', 'V39056']
|
||||
|
||||
|
||||
@curry
|
||||
def _format_url(instrument_type,
|
||||
instrument_ids,
|
||||
start_date,
|
||||
end_date,
|
||||
earliest_allowed_date):
|
||||
"""
|
||||
Format a URL for loading data from Bank of Canada.
|
||||
"""
|
||||
return (
|
||||
"http://www.bankofcanada.ca/stats/results/csv"
|
||||
"?lP=lookup_{instrument_type}_yields.php"
|
||||
"&sR={restrict}"
|
||||
"&se={instrument_ids}"
|
||||
"&dF={start}"
|
||||
"&dT={end}".format(
|
||||
instrument_type=instrument_type,
|
||||
instrument_ids='-'.join(map(prepend("L_"), instrument_ids)),
|
||||
restrict=earliest_allowed_date.strftime("%Y-%m-%d"),
|
||||
start=start_date.strftime("%Y-%m-%d"),
|
||||
end=end_date.strftime("%Y-%m-%d"),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
format_bill_url = _format_url('tbill', BILL_IDS)
|
||||
format_bond_url = _format_url('bond', BOND_IDS)
|
||||
|
||||
|
||||
def load_frame(url, skiprows):
|
||||
"""
|
||||
Load a DataFrame of data from a Bank of Canada site.
|
||||
"""
|
||||
return pd.read_csv(
|
||||
url,
|
||||
skiprows=skiprows,
|
||||
skipinitialspace=True,
|
||||
na_values=["Bank holiday", "Not available"],
|
||||
parse_dates=["Date"],
|
||||
index_col="Date",
|
||||
).dropna(how='all') \
|
||||
.tz_localize('UTC') \
|
||||
.rename(columns=COLUMN_NAMES)
|
||||
|
||||
|
||||
def check_known_inconsistencies(bill_data, bond_data):
|
||||
"""
|
||||
There are a couple quirks in the data provided by Bank of Canada.
|
||||
Check that no new quirks have been introduced in the latest download.
|
||||
"""
|
||||
inconsistent_dates = bill_data.index.sym_diff(bond_data.index)
|
||||
known_inconsistencies = [
|
||||
# bill_data has an entry for 2010-02-15, which bond_data doesn't.
|
||||
# bond_data has an entry for 2006-09-04, which bill_data doesn't.
|
||||
# Both of these dates are bank holidays (Flag Day and Labor Day,
|
||||
# respectively).
|
||||
pd.Timestamp('2006-09-04', tz='UTC'),
|
||||
pd.Timestamp('2010-02-15', tz='UTC'),
|
||||
# 2013-07-25 comes back as "Not available" from the bills endpoint.
|
||||
# This date doesn't seem to be a bank holiday, but the previous
|
||||
# calendar implementation dropped this entry, so we drop it as well.
|
||||
# If someone cares deeply about the integrity of the Canadian trading
|
||||
# calendar, they may want to consider forward-filling here rather than
|
||||
# dropping the row.
|
||||
pd.Timestamp('2013-07-25', tz='UTC'),
|
||||
]
|
||||
unexpected_inconsistences = inconsistent_dates.drop(known_inconsistencies)
|
||||
if len(unexpected_inconsistences):
|
||||
in_bills = bill_data.index.difference(bond_data.index).difference(
|
||||
known_inconsistencies
|
||||
)
|
||||
in_bonds = bond_data.index.difference(bill_data.index).difference(
|
||||
known_inconsistencies
|
||||
)
|
||||
raise ValueError(
|
||||
"Inconsistent dates for Canadian treasury bills vs bonds. \n"
|
||||
"Dates with bills but not bonds: {in_bills}.\n"
|
||||
"Dates with bonds but not bills: {in_bonds}.".format(
|
||||
in_bills=in_bills,
|
||||
in_bonds=in_bonds,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def earliest_possible_date():
|
||||
"""
|
||||
The earliest date for which we can load data from this module.
|
||||
"""
|
||||
today = pd.Timestamp('now', tz='UTC').normalize()
|
||||
# Bank of Canada only has the last 10 years of data at any given time.
|
||||
return today.replace(year=today.year - 10)
|
||||
|
||||
|
||||
def get_treasury_data(start_date, end_date):
|
||||
bill_data = load_frame(
|
||||
format_bill_url(start_date, end_date, start_date),
|
||||
# We skip fewer rows here because we query for fewer bill fields,
|
||||
# which makes the header smaller.
|
||||
skiprows=18,
|
||||
)
|
||||
bond_data = load_frame(
|
||||
format_bond_url(start_date, end_date, start_date),
|
||||
skiprows=22,
|
||||
)
|
||||
check_known_inconsistencies(bill_data, bond_data)
|
||||
|
||||
# dropna('any') removes the rows for which we only had data for one of
|
||||
# bills/bonds.
|
||||
out = pd.concat([bond_data, bill_data], axis=1).dropna(how='any')
|
||||
assert set(out.columns) == set(six.itervalues(COLUMN_NAMES))
|
||||
|
||||
# Multiply by 0.01 to convert from percentages to expected output format.
|
||||
return out * 0.01
|
||||