mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-27 18:58:48 +08:00
Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
+173
-179
@@ -4,7 +4,7 @@ API Reference
|
||||
Running a Backtest
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autofunction:: zipline.run_algorithm(...)
|
||||
.. autofunction:: catalyst.run_algorithm(...)
|
||||
|
||||
Algorithm API
|
||||
~~~~~~~~~~~~~
|
||||
@@ -18,341 +18,335 @@ currently-executing :class:`~zipline.algorithm.TradingAlgorithm` instance.
|
||||
Data Object
|
||||
```````````
|
||||
|
||||
.. autoclass:: zipline.protocol.BarData
|
||||
.. autoclass:: catalyst.protocol.BarData
|
||||
:members:
|
||||
|
||||
Scheduling Functions
|
||||
````````````````````
|
||||
|
||||
.. autofunction:: zipline.api.schedule_function
|
||||
.. autofunction:: catalyst.api.schedule_function
|
||||
|
||||
.. autoclass:: zipline.api.date_rules
|
||||
.. autoclass:: catalyst.api.date_rules
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. autoclass:: zipline.api.time_rules
|
||||
.. autoclass:: catalyst.api.time_rules
|
||||
:members:
|
||||
|
||||
Orders
|
||||
``````
|
||||
|
||||
.. autofunction:: zipline.api.order
|
||||
.. autofunction:: catalyst.api.order
|
||||
|
||||
.. autofunction:: zipline.api.order_value
|
||||
.. autofunction:: catalyst.api.order_value
|
||||
|
||||
.. autofunction:: zipline.api.order_percent
|
||||
.. autofunction:: catalyst.api.order_percent
|
||||
|
||||
.. autofunction:: zipline.api.order_target
|
||||
.. autofunction:: catalyst.api.order_target
|
||||
|
||||
.. autofunction:: zipline.api.order_target_value
|
||||
.. autofunction:: catalyst.api.order_target_value
|
||||
|
||||
.. autofunction:: zipline.api.order_target_percent
|
||||
.. autofunction:: catalyst.api.order_target_percent
|
||||
|
||||
.. autoclass:: zipline.finance.execution.ExecutionStyle
|
||||
.. autoclass:: catalyst.finance.execution.ExecutionStyle
|
||||
:members:
|
||||
|
||||
.. autoclass:: zipline.finance.execution.MarketOrder
|
||||
.. autoclass:: catalyst.finance.execution.MarketOrder
|
||||
|
||||
.. autoclass:: zipline.finance.execution.LimitOrder
|
||||
.. autoclass:: catalyst.finance.execution.LimitOrder
|
||||
|
||||
.. autoclass:: zipline.finance.execution.StopOrder
|
||||
.. autoclass:: catalyst.finance.execution.StopOrder
|
||||
|
||||
.. autoclass:: zipline.finance.execution.StopLimitOrder
|
||||
.. autoclass:: catalyst.finance.execution.StopLimitOrder
|
||||
|
||||
.. autofunction:: zipline.api.get_order
|
||||
.. autofunction:: catalyst.api.get_order
|
||||
|
||||
.. autofunction:: zipline.api.get_open_orders
|
||||
.. autofunction:: catalyst.api.get_open_orders
|
||||
|
||||
.. autofunction:: zipline.api.cancel_order
|
||||
.. autofunction:: catalyst.api.cancel_order
|
||||
|
||||
Order Cancellation Policies
|
||||
'''''''''''''''''''''''''''
|
||||
|
||||
.. autofunction:: zipline.api.set_cancel_policy
|
||||
.. autofunction:: catalyst.api.set_cancel_policy
|
||||
|
||||
.. autoclass:: zipline.finance.cancel_policy.CancelPolicy
|
||||
.. autoclass:: catalyst.finance.cancel_policy.CancelPolicy
|
||||
:members:
|
||||
|
||||
.. autofunction:: zipline.api.EODCancel
|
||||
.. autofunction:: catalyst.api.EODCancel
|
||||
|
||||
.. autofunction:: zipline.api.NeverCancel
|
||||
.. autofunction:: catalyst.api.NeverCancel
|
||||
|
||||
|
||||
Assets
|
||||
``````
|
||||
|
||||
.. autofunction:: zipline.api.symbol
|
||||
.. autofunction:: catalyst.api.symbol
|
||||
|
||||
.. autofunction:: zipline.api.symbols
|
||||
.. autofunction:: catalyst.api.symbols
|
||||
|
||||
.. autofunction:: zipline.api.future_symbol
|
||||
.. autofunction:: catalyst.api.set_symbol_lookup_date
|
||||
|
||||
.. autofunction:: zipline.api.set_symbol_lookup_date
|
||||
|
||||
.. autofunction:: zipline.api.sid
|
||||
.. autofunction:: catalyst.api.sid
|
||||
|
||||
|
||||
Trading Controls
|
||||
````````````````
|
||||
|
||||
Zipline provides trading controls to help ensure that the algorithm is
|
||||
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.
|
||||
|
||||
.. autofunction:: zipline.api.set_do_not_order_list
|
||||
.. autofunction:: catalyst.api.set_do_not_order_list
|
||||
|
||||
.. autofunction:: zipline.api.set_long_only
|
||||
.. autofunction:: catalyst.api.set_long_only
|
||||
|
||||
.. autofunction:: zipline.api.set_max_leverage
|
||||
.. autofunction:: catalyst.api.set_max_leverage
|
||||
|
||||
.. autofunction:: zipline.api.set_max_order_count
|
||||
.. autofunction:: catalyst.api.set_max_order_count
|
||||
|
||||
.. autofunction:: zipline.api.set_max_order_size
|
||||
.. autofunction:: catalyst.api.set_max_order_size
|
||||
|
||||
.. autofunction:: zipline.api.set_max_position_size
|
||||
.. autofunction:: catalyst.api.set_max_position_size
|
||||
|
||||
|
||||
Simulation Parameters
|
||||
`````````````````````
|
||||
|
||||
.. autofunction:: zipline.api.set_benchmark
|
||||
.. autofunction:: catalyst.api.set_benchmark
|
||||
|
||||
Commission Models
|
||||
'''''''''''''''''
|
||||
|
||||
.. autofunction:: zipline.api.set_commission
|
||||
.. autofunction:: catalyst.api.set_commission
|
||||
|
||||
.. autoclass:: zipline.finance.commission.CommissionModel
|
||||
.. autoclass:: catalyst.finance.commission.CommissionModel
|
||||
:members:
|
||||
|
||||
.. autoclass:: zipline.finance.commission.PerShare
|
||||
.. autoclass:: catalyst.finance.commission.PerShare
|
||||
|
||||
.. autoclass:: zipline.finance.commission.PerTrade
|
||||
.. autoclass:: catalyst.finance.commission.PerTrade
|
||||
|
||||
.. autoclass:: zipline.finance.commission.PerDollar
|
||||
.. autoclass:: catalyst.finance.commission.PerDollar
|
||||
|
||||
Slippage Models
|
||||
'''''''''''''''
|
||||
|
||||
.. autofunction:: zipline.api.set_slippage
|
||||
.. autofunction:: catalyst.api.set_slippage
|
||||
|
||||
.. autoclass:: zipline.finance.slippage.SlippageModel
|
||||
.. autoclass:: catalyst.finance.slippage.SlippageModel
|
||||
:members:
|
||||
|
||||
.. autoclass:: zipline.finance.slippage.FixedSlippage
|
||||
.. autoclass:: catalyst.finance.slippage.FixedSlippage
|
||||
|
||||
.. autoclass:: zipline.finance.slippage.VolumeShareSlippage
|
||||
.. autoclass:: catalyst.finance.slippage.VolumeShareSlippage
|
||||
|
||||
Pipeline
|
||||
````````
|
||||
|
||||
For more information, see :ref:`pipeline-api`
|
||||
Not supported yet.
|
||||
|
||||
.. autofunction:: zipline.api.attach_pipeline
|
||||
.. For more information, see :ref:`pipeline-api`
|
||||
|
||||
.. autofunction:: zipline.api.pipeline_output
|
||||
.. .. autofunction:: catalyst.api.attach_pipeline
|
||||
|
||||
.. .. autofunction:: catalyst.api.pipeline_output
|
||||
|
||||
|
||||
Miscellaneous
|
||||
`````````````
|
||||
|
||||
.. autofunction:: zipline.api.record
|
||||
.. autofunction:: catalyst.api.record
|
||||
|
||||
.. autofunction:: zipline.api.get_environment
|
||||
.. autofunction:: catalyst.api.get_environment
|
||||
|
||||
.. autofunction:: zipline.api.fetch_csv
|
||||
.. autofunction:: catalyst.api.fetch_csv
|
||||
|
||||
|
||||
.. _pipeline-api:
|
||||
|
||||
Pipeline API
|
||||
~~~~~~~~~~~~
|
||||
.. Pipeline API
|
||||
.. ~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: zipline.pipeline.Pipeline
|
||||
:members:
|
||||
:member-order: groupwise
|
||||
.. .. autoclass:: zipline.pipeline.Pipeline
|
||||
.. :members:
|
||||
.. :member-order: groupwise
|
||||
|
||||
.. autoclass:: zipline.pipeline.CustomFactor
|
||||
:members:
|
||||
:member-order: groupwise
|
||||
.. .. autoclass:: zipline.pipeline.CustomFactor
|
||||
.. :members:
|
||||
.. :member-order: groupwise
|
||||
|
||||
.. autoclass:: zipline.pipeline.filters.Filter
|
||||
:members: __and__, __or__
|
||||
:exclude-members: dtype
|
||||
.. .. autoclass:: zipline.pipeline.filters.Filter
|
||||
.. :members: __and__, __or__
|
||||
.. :exclude-members: dtype
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.Factor
|
||||
:members: bottom, deciles, demean, linear_regression, pearsonr,
|
||||
percentile_between, quantiles, quartiles, quintiles, rank,
|
||||
spearmanr, top, winsorize, zscore, isnan, notnan, isfinite, eq,
|
||||
__add__, __sub__, __mul__, __div__, __mod__, __pow__, __lt__,
|
||||
__le__, __ne__, __ge__, __gt__
|
||||
:exclude-members: dtype
|
||||
:member-order: bysource
|
||||
.. .. autoclass:: zipline.pipeline.factors.Factor
|
||||
.. :members: bottom, deciles, demean, linear_regression, pearsonr,
|
||||
.. percentile_between, quantiles, quartiles, quintiles, rank,
|
||||
.. spearmanr, top, winsorize, zscore, isnan, notnan, isfinite, eq,
|
||||
.. \__add__, \__sub__, \__mul__, \__div__, \__mod__, \__pow__,
|
||||
.. \__lt__, \__le__, \__ne__, \__ge__, \__gt__
|
||||
.. :exclude-members: dtype
|
||||
.. :member-order: bysource
|
||||
|
||||
.. autoclass:: zipline.pipeline.term.Term
|
||||
:members:
|
||||
:exclude-members: compute_extra_rows, dependencies, inputs, mask, windowed
|
||||
.. .. autoclass:: zipline.pipeline.term.Term
|
||||
.. :members:
|
||||
.. :exclude-members: compute_extra_rows, dependencies, inputs, mask, windowed
|
||||
|
||||
.. autoclass:: zipline.pipeline.data.USEquityPricing
|
||||
:members: open, high, low, close, volume
|
||||
:undoc-members:
|
||||
.. .. autoclass:: zipline.pipeline.data.USEquityPricing
|
||||
.. :members: open, high, low, close, volume
|
||||
.. :undoc-members:
|
||||
|
||||
Built-in Factors
|
||||
````````````````
|
||||
.. Built-in Factors
|
||||
.. ````````````````
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.AverageDollarVolume
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.AverageDollarVolume
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.BollingerBands
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.BollingerBands
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.BusinessDaysSincePreviousEvent
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.BusinessDaysSincePreviousEvent
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.BusinessDaysUntilNextEvent
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.BusinessDaysUntilNextEvent
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.ExponentialWeightedMovingAverage
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.ExponentialWeightedMovingAverage
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.ExponentialWeightedMovingStdDev
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.ExponentialWeightedMovingStdDev
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.Latest
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.Latest
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.MaxDrawdown
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.MaxDrawdown
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.Returns
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.Returns
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.RollingLinearRegressionOfReturns
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.RollingLinearRegressionOfReturns
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.RollingPearsonOfReturns
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.RollingPearsonOfReturns
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.RollingSpearmanOfReturns
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.RollingSpearmanOfReturns
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.RSI
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.RSI
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.SimpleMovingAverage
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.SimpleMovingAverage
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.VWAP
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.VWAP
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.pipeline.factors.WeightedAverageValue
|
||||
:members:
|
||||
.. .. autoclass:: zipline.pipeline.factors.WeightedAverageValue
|
||||
.. :members:
|
||||
|
||||
Pipeline Engine
|
||||
```````````````
|
||||
.. Pipeline Engine
|
||||
.. ```````````````
|
||||
|
||||
.. autoclass:: zipline.pipeline.engine.PipelineEngine
|
||||
:members: run_pipeline, run_chunked_pipeline
|
||||
:member-order: bysource
|
||||
.. .. autoclass:: zipline.pipeline.engine.PipelineEngine
|
||||
.. :members: run_pipeline, run_chunked_pipeline
|
||||
.. :member-order: bysource
|
||||
|
||||
.. autoclass:: zipline.pipeline.engine.SimplePipelineEngine
|
||||
:members: __init__, run_pipeline, run_chunked_pipeline
|
||||
:member-order: bysource
|
||||
.. .. autoclass:: zipline.pipeline.engine.SimplePipelineEngine
|
||||
.. :members: __init__, run_pipeline, run_chunked_pipeline
|
||||
.. :member-order: bysource
|
||||
|
||||
.. autofunction:: zipline.pipeline.engine.default_populate_initial_workspace
|
||||
.. .. autofunction:: zipline.pipeline.engine.default_populate_initial_workspace
|
||||
|
||||
Data Loaders
|
||||
````````````
|
||||
.. Data Loaders
|
||||
.. ````````````
|
||||
|
||||
.. autoclass:: zipline.pipeline.loaders.equity_pricing_loader.USEquityPricingLoader
|
||||
:members: __init__, from_files, load_adjusted_array
|
||||
:member-order: bysource
|
||||
.. .. autoclass:: zipline.pipeline.loaders.equity_pricing_loader.USEquityPricingLoader
|
||||
.. :members: __init__, from_files, load_adjusted_array
|
||||
.. :member-order: bysource
|
||||
|
||||
Asset Metadata
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: zipline.assets.Asset
|
||||
.. autoclass:: catalyst.assets.Asset
|
||||
:members:
|
||||
|
||||
.. autoclass:: zipline.assets.Equity
|
||||
:members:
|
||||
|
||||
.. autoclass:: zipline.assets.Future
|
||||
:members:
|
||||
|
||||
.. autoclass:: zipline.assets.AssetConvertible
|
||||
.. autoclass:: catalyst.assets.AssetConvertible
|
||||
:members:
|
||||
|
||||
|
||||
Trading Calendar API
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autofunction:: zipline.utils.calendars.get_calendar
|
||||
.. autofunction:: catalyst.utils.calendars.get_calendar
|
||||
|
||||
.. autoclass:: zipline.utils.calendars.TradingCalendar
|
||||
.. autoclass:: catalyst.utils.calendars.TradingCalendar
|
||||
:members:
|
||||
|
||||
.. autofunction:: zipline.utils.calendars.register_calendar
|
||||
.. autofunction:: catalyst.utils.calendars.register_calendar
|
||||
|
||||
.. autofunction:: zipline.utils.calendars.register_calendar_type
|
||||
.. autofunction:: catalyst.utils.calendars.register_calendar_type
|
||||
|
||||
.. autofunction:: zipline.utils.calendars.deregister_calendar
|
||||
.. autofunction:: catalyst.utils.calendars.deregister_calendar
|
||||
|
||||
.. autofunction:: zipline.utils.calendars.clear_calendars
|
||||
.. autofunction:: catalyst.utils.calendars.clear_calendars
|
||||
|
||||
|
||||
Data API
|
||||
~~~~~~~~
|
||||
|
||||
Writers
|
||||
```````
|
||||
.. autoclass:: zipline.data.minute_bars.BcolzMinuteBarWriter
|
||||
:members:
|
||||
.. Writers
|
||||
.. ```````
|
||||
.. .. autoclass:: zipline.data.minute_bars.BcolzMinuteBarWriter
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.data.us_equity_pricing.BcolzDailyBarWriter
|
||||
:members:
|
||||
.. .. autoclass:: zipline.data.us_equity_pricing.BcolzDailyBarWriter
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.data.us_equity_pricing.SQLiteAdjustmentWriter
|
||||
:members:
|
||||
.. .. autoclass:: zipline.data.us_equity_pricing.SQLiteAdjustmentWriter
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.assets.AssetDBWriter
|
||||
:members:
|
||||
.. .. autoclass:: zipline.assets.AssetDBWriter
|
||||
.. :members:
|
||||
|
||||
Readers
|
||||
```````
|
||||
.. autoclass:: zipline.data.minute_bars.BcolzMinuteBarReader
|
||||
:members:
|
||||
.. Readers
|
||||
.. ```````
|
||||
.. .. autoclass:: zipline.data.minute_bars.BcolzMinuteBarReader
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.data.us_equity_pricing.BcolzDailyBarReader
|
||||
:members:
|
||||
.. .. autoclass:: zipline.data.us_equity_pricing.BcolzDailyBarReader
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.data.us_equity_pricing.SQLiteAdjustmentReader
|
||||
:members:
|
||||
.. .. autoclass:: zipline.data.us_equity_pricing.SQLiteAdjustmentReader
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.assets.AssetFinder
|
||||
:members:
|
||||
.. .. autoclass:: zipline.assets.AssetFinder
|
||||
.. :members:
|
||||
|
||||
.. autoclass:: zipline.data.data_portal.DataPortal
|
||||
:members:
|
||||
.. .. autoclass:: zipline.data.data_portal.DataPortal
|
||||
.. :members:
|
||||
|
||||
Bundles
|
||||
```````
|
||||
.. autofunction:: zipline.data.bundles.register
|
||||
.. Bundles
|
||||
.. ```````
|
||||
.. .. autofunction:: zipline.data.bundles.register
|
||||
|
||||
.. autofunction:: zipline.data.bundles.ingest(name, environ=os.environ, date=None, show_progress=True)
|
||||
.. .. autofunction:: zipline.data.bundles.ingest(name, environ=os.environ, date=None, show_progress=True)
|
||||
|
||||
.. autofunction:: zipline.data.bundles.load(name, environ=os.environ, date=None)
|
||||
.. .. autofunction:: zipline.data.bundles.load(name, environ=os.environ, date=None)
|
||||
|
||||
.. autofunction:: zipline.data.bundles.unregister
|
||||
.. .. autofunction:: zipline.data.bundles.unregister
|
||||
|
||||
.. data:: zipline.data.bundles.bundles
|
||||
.. .. data:: zipline.data.bundles.bundles
|
||||
|
||||
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
|
||||
:func:`~zipline.data.bundles.register` or
|
||||
:func:`~zipline.data.bundles.unregister`.
|
||||
.. 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
|
||||
.. :func:`~zipline.data.bundles.register` or
|
||||
.. :func:`~zipline.data.bundles.unregister`.
|
||||
|
||||
.. autofunction:: zipline.data.bundles.yahoo_equities
|
||||
.. .. autofunction:: zipline.data.bundles.yahoo_equities
|
||||
|
||||
|
||||
|
||||
@@ -362,16 +356,16 @@ Utilities
|
||||
Caching
|
||||
```````
|
||||
|
||||
.. autoclass:: zipline.utils.cache.CachedObject
|
||||
.. autoclass:: catalyst.utils.cache.CachedObject
|
||||
|
||||
.. autoclass:: zipline.utils.cache.ExpiringCache
|
||||
.. autoclass:: catalyst.utils.cache.ExpiringCache
|
||||
|
||||
.. autoclass:: zipline.utils.cache.dataframe_cache
|
||||
.. autoclass:: catalyst.utils.cache.dataframe_cache
|
||||
|
||||
.. autoclass:: zipline.utils.cache.working_file
|
||||
.. autoclass:: catalyst.utils.cache.working_file
|
||||
|
||||
.. autoclass:: zipline.utils.cache.working_dir
|
||||
.. autoclass:: catalyst.utils.cache.working_dir
|
||||
|
||||
Command Line
|
||||
````````````
|
||||
.. autofunction:: zipline.utils.cli.maybe_show_progress
|
||||
.. autofunction:: catalyst.utils.cli.maybe_show_progress
|
||||
|
||||
@@ -168,7 +168,7 @@ We'll start with the CLI, and introduce the ``run_algorithm()`` in the last
|
||||
example of this tutorial. Some of the :doc:`example algorithms <example-algos>`
|
||||
provide instructions on how to run them both from the CLI, and using the
|
||||
:func:`~catalyst.run_algorithm` function. For the third method, refer to the
|
||||
corresponding section on :doc:`Catalyst & Jupyter Notebook <jupyter>` after you
|
||||
corresponding section on :ref:`Catalyst & Jupyter Notebook <jupyter>` after you
|
||||
have assimilated the contents of this tutorial.
|
||||
|
||||
Command line interface
|
||||
@@ -473,6 +473,7 @@ Which we execute by running:
|
||||
</div>
|
||||
|
||||
|
|
||||
|
||||
There is a row for each trading day, starting on the first day of our
|
||||
simulation Jan 1st, 2016. In the columns you can find various
|
||||
information about the state of your algorithm. The column
|
||||
@@ -518,7 +519,7 @@ alongside enigma-catalyst (with the exception of the ``Conda`` install, where it
|
||||
was included by default inside the conda environment we created). If for any
|
||||
reason you don't have it installed, you can add it by running:
|
||||
|
||||
.. code-block:: python
|
||||
.. code-block:: bash
|
||||
|
||||
(catalyst)$ pip install matplotlib
|
||||
|
||||
@@ -579,162 +580,8 @@ which you can skim through for now. A copy of this algorithm is available in
|
||||
the ``examples`` directory:
|
||||
`dual_moving_average.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/dual_moving_average.py>`_.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from logbook import Logger
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from catalyst import run_algorithm
|
||||
from catalyst.api import (order, record, symbol, order_target_percent,
|
||||
get_open_orders)
|
||||
from catalyst.exchange.utils.stats_utils import extract_transactions
|
||||
|
||||
NAMESPACE = 'dual_moving_average'
|
||||
log = Logger(NAMESPACE)
|
||||
|
||||
def initialize(context):
|
||||
context.i = 0
|
||||
context.asset = symbol('ltc_usd')
|
||||
context.base_price = None
|
||||
|
||||
|
||||
def handle_data(context, data):
|
||||
# define the windows for the moving averages
|
||||
short_window = 50
|
||||
long_window = 200
|
||||
|
||||
# Skip as many bars as long_window to properly compute the average
|
||||
context.i += 1
|
||||
if context.i < long_window:
|
||||
return
|
||||
|
||||
# Compute moving averages calling data.history() for each
|
||||
# moving average with the appropriate parameters. We choose to use
|
||||
# minute bars for this simulation -> freq="1m"
|
||||
# Returns a pandas dataframe.
|
||||
short_mavg = data.history(context.asset, 'price',
|
||||
bar_count=short_window, frequency="1m").mean()
|
||||
long_mavg = data.history(context.asset, 'price',
|
||||
bar_count=long_window, frequency="1m").mean()
|
||||
|
||||
# Let's keep the price of our asset in a more handy variable
|
||||
price = data.current(context.asset, 'price')
|
||||
|
||||
# If base_price is not set, we use the current value. This is the
|
||||
# price at the first bar which we reference to calculate price_change.
|
||||
if context.base_price is None:
|
||||
context.base_price = price
|
||||
price_change = (price - context.base_price) / context.base_price
|
||||
|
||||
# Save values for later inspection
|
||||
record(price=price,
|
||||
cash=context.portfolio.cash,
|
||||
price_change=price_change,
|
||||
short_mavg=short_mavg,
|
||||
long_mavg=long_mavg)
|
||||
|
||||
# Since we are using limit orders, some orders may not execute immediately
|
||||
# we wait until all orders are executed before considering more trades.
|
||||
orders = get_open_orders(context.asset)
|
||||
if len(orders) > 0:
|
||||
return
|
||||
|
||||
# Exit if we cannot trade
|
||||
if not data.can_trade(context.asset):
|
||||
return
|
||||
|
||||
# We check what's our position on our portfolio and trade accordingly
|
||||
pos_amount = context.portfolio.positions[context.asset].amount
|
||||
|
||||
# Trading logic
|
||||
if short_mavg > long_mavg and pos_amount == 0:
|
||||
# we buy 100% of our portfolio for this asset
|
||||
order_target_percent(context.asset, 1)
|
||||
elif short_mavg < long_mavg and pos_amount > 0:
|
||||
# we sell all our positions for this asset
|
||||
order_target_percent(context.asset, 0)
|
||||
|
||||
|
||||
def analyze(context, perf):
|
||||
|
||||
# Get the base_currency that was passed as a parameter to the simulation
|
||||
exchange = list(context.exchanges.values())[0]
|
||||
base_currency = exchange.base_currency.upper()
|
||||
|
||||
# First chart: Plot portfolio value using base_currency
|
||||
ax1 = plt.subplot(411)
|
||||
perf.loc[:, ['portfolio_value']].plot(ax=ax1)
|
||||
ax1.legend_.remove()
|
||||
ax1.set_ylabel('Portfolio Value\n({})'.format(base_currency))
|
||||
start, end = ax1.get_ylim()
|
||||
ax1.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
# Second chart: Plot asset price, moving averages and buys/sells
|
||||
ax2 = plt.subplot(412, sharex=ax1)
|
||||
perf.loc[:, ['price','short_mavg','long_mavg']].plot(ax=ax2, label='Price')
|
||||
ax2.legend_.remove()
|
||||
ax2.set_ylabel('{asset}\n({base})'.format(
|
||||
asset = context.asset.symbol,
|
||||
base = base_currency
|
||||
))
|
||||
start, end = ax2.get_ylim()
|
||||
ax2.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
transaction_df = extract_transactions(perf)
|
||||
if not transaction_df.empty:
|
||||
buy_df = transaction_df[transaction_df['amount'] > 0]
|
||||
sell_df = transaction_df[transaction_df['amount'] < 0]
|
||||
ax2.scatter(
|
||||
buy_df.index.to_pydatetime(),
|
||||
perf.loc[buy_df.index, 'price'],
|
||||
marker='^',
|
||||
s=100,
|
||||
c='green',
|
||||
label=''
|
||||
)
|
||||
ax2.scatter(
|
||||
sell_df.index.to_pydatetime(),
|
||||
perf.loc[sell_df.index, 'price'],
|
||||
marker='v',
|
||||
s=100,
|
||||
c='red',
|
||||
label=''
|
||||
)
|
||||
|
||||
# Third chart: Compare percentage change between our portfolio
|
||||
# and the price of the asset
|
||||
ax3 = plt.subplot(413, sharex=ax1)
|
||||
perf.loc[:, ['algorithm_period_return', 'price_change']].plot(ax=ax3)
|
||||
ax3.legend_.remove()
|
||||
ax3.set_ylabel('Percent Change')
|
||||
start, end = ax3.get_ylim()
|
||||
ax3.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
# Fourth chart: Plot our cash
|
||||
ax4 = plt.subplot(414, sharex=ax1)
|
||||
perf.cash.plot(ax=ax4)
|
||||
ax4.set_ylabel('Cash\n({})'.format(base_currency))
|
||||
start, end = ax4.get_ylim()
|
||||
ax4.yaxis.set_ticks(np.arange(0, end, end/5))
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_algorithm(
|
||||
capital_base=1000,
|
||||
data_frequency='minute',
|
||||
initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
exchange_name='bitfinex',
|
||||
algo_namespace=NAMESPACE,
|
||||
base_currency='usd',
|
||||
start=pd.to_datetime('2017-9-22', utc=True),
|
||||
end=pd.to_datetime('2017-9-23', utc=True),
|
||||
)
|
||||
.. literalinclude:: ../../catalyst/examples/dual_moving_average.py
|
||||
:language: python
|
||||
|
||||
In order to run the code above, you have to ingest the needed data first:
|
||||
|
||||
@@ -806,6 +653,7 @@ the ``scikit-learn`` functions require ``numpy.ndarray``\ s rather than
|
||||
``pandas.DataFrame``\ s, so you can simply pass the underlying
|
||||
``ndarray`` of a ``DataFrame`` via ``.values``).
|
||||
|
||||
.. _jupyter:
|
||||
|
||||
Jupyter Notebook
|
||||
~~~~~~~~~~~~~~~~
|
||||
@@ -826,13 +674,13 @@ In order to use Jupyter Notebook, you first have to install it inside your
|
||||
environment. It's available as ``pip`` package, so regardless of how you
|
||||
installed Catalyst, go inside your catalyst environemnt and run:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
(catalyst)$ pip install jupyter
|
||||
|
||||
Once you have Jupyter Notebook installed, every time you want to use it run:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
(catalyst)$ jupyter notebook
|
||||
|
||||
@@ -846,7 +694,7 @@ Before running your algorithms inside the Jupyter Notebook, remember to ingest
|
||||
the data from the command line interface (CLI). In the example below, you would
|
||||
need to run first:
|
||||
|
||||
.. code:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
catalyst ingest-exchange -x bitfinex -i btc_usd
|
||||
|
||||
@@ -16607,7 +16455,49 @@ NaN
|
||||
|
||||
</div>
|
||||
|
||||
PyCharm IDE
|
||||
~~~~~~~~~~~
|
||||
|
||||
PyCharm is an Integrated Development Environment (IDE) used in computer
|
||||
programming, specifically for the Python language. It streamlines the continuos
|
||||
development of Python code, and among other things includes a debugger that
|
||||
comes in handy to see the inner workings of Catalyst, and your trading
|
||||
algorithms.
|
||||
|
||||
Install
|
||||
^^^^^^^
|
||||
Install PyCharm from their `Website <https://www.jetbrains.com/pycharm/download/>`__.
|
||||
There is a free and open-source **Community** version.
|
||||
|
||||
Setup
|
||||
^^^^^
|
||||
|
||||
1. When creating a new project in PyCharm, right under you specify the Location,
|
||||
click on **Project Interpreter** to display a drop down menu
|
||||
|
||||
2. Select **Existing interpreter**, click the gear box right next to it and
|
||||
select 'add local'. Depending on your installation, select either
|
||||
"*Virtual Environemnt*" or "*Conda Environment" and click the '...' button to
|
||||
navigate to your catalyst env and select the Python binary file:
|
||||
``bin/python`` for Linux/MacOS installations or 'python.exe' for Windows
|
||||
installs (for example: 'C:\\Users\\user\\Anaconda2\\envs\\catalyst\\python.exe').
|
||||
Select OK. You may want to click on *Make available to all projects* for your
|
||||
future reference. Click OK again, and create your new environment using the
|
||||
set up of your virtual environment.
|
||||
|
||||
Alternatively, if you already have your project created, in Windows do:
|
||||
|
||||
1. File -> Default Settings -> Project Interpreter. Click the gear box next to
|
||||
the project interpreter and select ‘add local’, and follow the steps from the
|
||||
second step above.
|
||||
|
||||
On MacOS:
|
||||
|
||||
1. PyCharm -> Preferences -> Settings -> Project:’NAME_OF_PROJECT’ ->
|
||||
Project Interpreter. Click the gear box next to the project interpreter
|
||||
and select ‘add local’, and follow the steps from the second step above.
|
||||
|
||||
You should now be able to run your project/scripts in PyCharm.
|
||||
|
||||
Next steps
|
||||
~~~~~~~~~~
|
||||
|
||||
+5
-2
@@ -27,8 +27,8 @@ extlinks = {
|
||||
|
||||
# -- Docstrings ---------------------------------------------------------------
|
||||
|
||||
#extensions += ['numpydoc']
|
||||
#numpydoc_show_class_members = False
|
||||
extensions += ['numpydoc']
|
||||
numpydoc_show_class_members = False
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['.templates']
|
||||
@@ -97,3 +97,6 @@ intersphinx_mapping = {
|
||||
doctest_global_setup = "import catalyst"
|
||||
|
||||
todo_include_todos = True
|
||||
|
||||
suppress_warnings = ['image.nonlocal_uri']
|
||||
|
||||
|
||||
@@ -36,25 +36,15 @@ Finally, you can build the C extensions by running:
|
||||
|
||||
$ python setup.py build_ext --inplace
|
||||
|
||||
.. To finish, make sure `tests`__ pass.
|
||||
Development with Docker
|
||||
-----------------------
|
||||
|
||||
.. __ #style-guide-running-tests
|
||||
If you want to work with zipline using a `Docker`__ container, you'll need to
|
||||
build the ``Dockerfile`` in the Zipline root directory, and then build
|
||||
``Dockerfile-dev``. Instructions for building both containers can be found in
|
||||
``Dockerfile`` and ``Dockerfile-dev``, respectively.
|
||||
|
||||
.. If you get an error running nosetests after setting up a fresh virtualenv, please try running
|
||||
|
||||
.. code-block
|
||||
|
||||
.. # where zipline is the name of your virtualenv
|
||||
.. $ deactivate zipline
|
||||
.. $ workon zipline
|
||||
|
||||
|
||||
.. Development with Docker
|
||||
.. -----------------------
|
||||
|
||||
..If you want to work with zipline using a `Docker`__ container, you'll need to build the ``Dockerfile`` in the Zipline root directory, and then build ``Dockerfile-dev``. Instructions for building both containers can be found in ``Dockerfile`` and ``Dockerfile-dev``, respectively.
|
||||
|
||||
.. __ https://docs.docker.com/get-started/
|
||||
__ https://docs.docker.com/get-started/
|
||||
|
||||
Git Branching Structure
|
||||
-----------------------
|
||||
|
||||
+18
-881
@@ -1,4 +1,5 @@
|
||||
|
|
||||
|
||||
Example Algorithms
|
||||
==================
|
||||
|
||||
@@ -51,35 +52,8 @@ Buy BTC Simple Algorithm
|
||||
|
||||
Source code: `examples/buy_btc_simple.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/buy_btc_simple.py>`_
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
'''
|
||||
Run this example, by executing the following from your terminal:
|
||||
catalyst ingest-exchange -x bitfinex -f daily -i btc_usdt
|
||||
catalyst run -f buy_btc_simple.py -x bitfinex --start 2016-1-1 --end 2017-9-30 -o buy_btc_simple_out.pickle
|
||||
|
||||
If you want to run this code using another exchange, make sure that
|
||||
the asset is available on that exchange. For example, if you were to run
|
||||
it for exchange Poloniex, you would need to edit the following line:
|
||||
|
||||
context.asset = symbol('btc_usdt') # note 'usdt' instead of 'usd'
|
||||
|
||||
and specify exchange poloniex as follows:
|
||||
catalyst ingest-exchange -x poloniex -f daily -i btc_usdt
|
||||
catalyst run -f buy_btc_simple.py -x poloniex --start 2016-1-1 --end 2017-9-30 -o buy_btc_simple_out.pickle
|
||||
|
||||
To see which assets are available on each exchange, visit:
|
||||
https://www.enigma.co/catalyst/status
|
||||
'''
|
||||
|
||||
from catalyst.api import order, record, symbol
|
||||
|
||||
def initialize(context):
|
||||
context.asset = symbol('btc_usd')
|
||||
|
||||
def handle_data(context, data):
|
||||
order(context.asset, 1)
|
||||
record(btc = data.current(context.asset, 'price'))
|
||||
.. literalinclude:: ../../catalyst/examples/buy_btc_simple.py
|
||||
:language: python
|
||||
|
||||
This simple algorithm does not produce any output nor displays any chart.
|
||||
|
||||
@@ -89,8 +63,6 @@ This simple algorithm does not produce any output nor displays any chart.
|
||||
Buy and Hodl Algorithm
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Source code: `examples/buy_and_hodl.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/buy_and_hodl.py>`_
|
||||
|
||||
First ingest the historical pricing data needed to run this algorithm:
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -118,157 +90,10 @@ that 2015-3-1 is the earliest date that Catalyst supports (if you choose an
|
||||
earlier date, you'll get an error), and the most recent date you can choose is
|
||||
one day prior to the current date.
|
||||
|
||||
Source code: `examples/buy_and_hodl.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/buy_and_hodl.py>`_
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2017 Enigma MPC, Inc.
|
||||
# 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 pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from catalyst import run_algorithm
|
||||
from catalyst.api import (order_target_value, symbol, record,
|
||||
cancel_order, get_open_orders, )
|
||||
|
||||
|
||||
def initialize(context):
|
||||
context.ASSET_NAME = 'btc_usd'
|
||||
context.TARGET_HODL_RATIO = 0.8
|
||||
context.RESERVE_RATIO = 1.0 - context.TARGET_HODL_RATIO
|
||||
|
||||
context.is_buying = True
|
||||
context.asset = symbol(context.ASSET_NAME)
|
||||
|
||||
context.i = 0
|
||||
|
||||
|
||||
def handle_data(context, data):
|
||||
context.i += 1
|
||||
|
||||
starting_cash = context.portfolio.starting_cash
|
||||
target_hodl_value = context.TARGET_HODL_RATIO * starting_cash
|
||||
reserve_value = context.RESERVE_RATIO * starting_cash
|
||||
|
||||
# Cancel any outstanding orders
|
||||
orders = get_open_orders(context.asset) or []
|
||||
for order in orders:
|
||||
cancel_order(order)
|
||||
|
||||
# Stop buying after passing the reserve threshold
|
||||
cash = context.portfolio.cash
|
||||
if cash <= reserve_value:
|
||||
context.is_buying = False
|
||||
|
||||
# Retrieve current asset price from pricing data
|
||||
price = data.current(context.asset, 'price')
|
||||
|
||||
# Check if still buying and could (approximately) afford another purchase
|
||||
if context.is_buying and cash > price:
|
||||
print('buying')
|
||||
# Place order to make position in asset equal to target_hodl_value
|
||||
order_target_value(
|
||||
context.asset,
|
||||
target_hodl_value,
|
||||
limit_price=price * 1.1,
|
||||
)
|
||||
|
||||
record(
|
||||
price=price,
|
||||
volume=data.current(context.asset, 'volume'),
|
||||
cash=cash,
|
||||
starting_cash=context.portfolio.starting_cash,
|
||||
leverage=context.account.leverage,
|
||||
)
|
||||
|
||||
|
||||
def analyze(context=None, results=None):
|
||||
|
||||
# Plot the portfolio and asset data.
|
||||
ax1 = plt.subplot(611)
|
||||
results[['portfolio_value']].plot(ax=ax1)
|
||||
ax1.set_ylabel('Portfolio Value (USD)')
|
||||
|
||||
ax2 = plt.subplot(612, sharex=ax1)
|
||||
ax2.set_ylabel('{asset} (USD)'.format(asset=context.ASSET_NAME))
|
||||
results[['price']].plot(ax=ax2)
|
||||
|
||||
trans = results.ix[[t != [] for t in results.transactions]]
|
||||
buys = trans.ix[
|
||||
[t[0]['amount'] > 0 for t in trans.transactions]
|
||||
]
|
||||
ax2.scatter(
|
||||
buys.index.to_pydatetime(),
|
||||
results.price[buys.index],
|
||||
marker='^',
|
||||
s=100,
|
||||
c='g',
|
||||
label=''
|
||||
)
|
||||
|
||||
ax3 = plt.subplot(613, sharex=ax1)
|
||||
results[['leverage', 'alpha', 'beta']].plot(ax=ax3)
|
||||
ax3.set_ylabel('Leverage ')
|
||||
|
||||
ax4 = plt.subplot(614, sharex=ax1)
|
||||
results[['starting_cash', 'cash']].plot(ax=ax4)
|
||||
ax4.set_ylabel('Cash (USD)')
|
||||
|
||||
results[[
|
||||
'treasury',
|
||||
'algorithm',
|
||||
'benchmark',
|
||||
]] = results[[
|
||||
'treasury_period_return',
|
||||
'algorithm_period_return',
|
||||
'benchmark_period_return',
|
||||
]]
|
||||
|
||||
ax5 = plt.subplot(615, sharex=ax1)
|
||||
results[[
|
||||
'treasury',
|
||||
'algorithm',
|
||||
'benchmark',
|
||||
]].plot(ax=ax5)
|
||||
ax5.set_ylabel('Percent Change')
|
||||
|
||||
ax6 = plt.subplot(616, sharex=ax1)
|
||||
results[['volume']].plot(ax=ax6)
|
||||
ax6.set_ylabel('Volume (mCoins/5min)')
|
||||
|
||||
plt.legend(loc=3)
|
||||
|
||||
# Show the plot.
|
||||
plt.gcf().set_size_inches(18, 8)
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_algorithm(
|
||||
capital_base=10000,
|
||||
data_frequency='daily',
|
||||
initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
exchange_name='bitfinex',
|
||||
algo_namespace='buy_and_hodl',
|
||||
base_currency='usd',
|
||||
start=pd.to_datetime('2015-03-01', utc=True),
|
||||
end=pd.to_datetime('2017-10-31', utc=True),
|
||||
)
|
||||
.. literalinclude:: ../../catalyst/examples/buy_and_hodl.py
|
||||
:language: python
|
||||
|
||||
.. image:: https://s3.amazonaws.com/enigmaco-docs/github.io/example_buy_and_hodl.png
|
||||
|
||||
@@ -277,166 +102,13 @@ one day prior to the current date.
|
||||
Dual Moving Average Crossover
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Source Code: `examples/dual_moving_average.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/dual_moving_average.py>`_
|
||||
|
||||
This strategy is covered in detail in the last part of
|
||||
`this tutorial <beginner-tutorial.html#history>`_.
|
||||
|
||||
.. code-block:: python
|
||||
Source Code: `examples/dual_moving_average.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/dual_moving_average.py>`_
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from logbook import Logger
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from catalyst import run_algorithm
|
||||
from catalyst.api import (order, record, symbol, order_target_percent,
|
||||
get_open_orders)
|
||||
from catalyst.exchange.stats_utils import extract_transactions
|
||||
|
||||
NAMESPACE = 'dual_moving_average'
|
||||
log = Logger(NAMESPACE)
|
||||
|
||||
def initialize(context):
|
||||
context.i = 0
|
||||
context.asset = symbol('ltc_usd')
|
||||
context.base_price = None
|
||||
|
||||
|
||||
def handle_data(context, data):
|
||||
# define the windows for the moving averages
|
||||
short_window = 50
|
||||
long_window = 200
|
||||
|
||||
# Skip as many bars as long_window to properly compute the average
|
||||
context.i += 1
|
||||
if context.i < long_window:
|
||||
return
|
||||
|
||||
# Compute moving averages calling data.history() for each
|
||||
# moving average with the appropriate parameters. We choose to use
|
||||
# minute bars for this simulation -> freq="1m"
|
||||
# Returns a pandas dataframe.
|
||||
short_mavg = data.history(context.asset, 'price',
|
||||
bar_count=short_window, frequency="1m").mean()
|
||||
long_mavg = data.history(context.asset, 'price',
|
||||
bar_count=long_window, frequency="1m").mean()
|
||||
|
||||
# Let's keep the price of our asset in a more handy variable
|
||||
price = data.current(context.asset, 'price')
|
||||
|
||||
# If base_price is not set, we use the current value. This is the
|
||||
# price at the first bar which we reference to calculate price_change.
|
||||
if context.base_price is None:
|
||||
context.base_price = price
|
||||
price_change = (price - context.base_price) / context.base_price
|
||||
|
||||
# Save values for later inspection
|
||||
record(price=price,
|
||||
cash=context.portfolio.cash,
|
||||
price_change=price_change,
|
||||
short_mavg=short_mavg,
|
||||
long_mavg=long_mavg)
|
||||
|
||||
# Since we are using limit orders, some orders may not execute immediately
|
||||
# we wait until all orders are executed before considering more trades.
|
||||
orders = get_open_orders(context.asset)
|
||||
if len(orders) > 0:
|
||||
return
|
||||
|
||||
# Exit if we cannot trade
|
||||
if not data.can_trade(context.asset):
|
||||
return
|
||||
|
||||
# We check what's our position on our portfolio and trade accordingly
|
||||
pos_amount = context.portfolio.positions[context.asset].amount
|
||||
|
||||
# Trading logic
|
||||
if short_mavg > long_mavg and pos_amount == 0:
|
||||
# we buy 100% of our portfolio for this asset
|
||||
order_target_percent(context.asset, 1)
|
||||
elif short_mavg < long_mavg and pos_amount > 0:
|
||||
# we sell all our positions for this asset
|
||||
order_target_percent(context.asset, 0)
|
||||
|
||||
|
||||
def analyze(context, perf):
|
||||
|
||||
# Get the base_currency that was passed as a parameter to the simulation
|
||||
base_currency = context.exchanges.values()[0].base_currency.upper()
|
||||
|
||||
# First chart: Plot portfolio value using base_currency
|
||||
ax1 = plt.subplot(411)
|
||||
perf.loc[:, ['portfolio_value']].plot(ax=ax1)
|
||||
ax1.legend_.remove()
|
||||
ax1.set_ylabel('Portfolio Value\n({})'.format(base_currency))
|
||||
start, end = ax1.get_ylim()
|
||||
ax1.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
# Second chart: Plot asset price, moving averages and buys/sells
|
||||
ax2 = plt.subplot(412, sharex=ax1)
|
||||
perf.loc[:, ['price','short_mavg','long_mavg']].plot(ax=ax2, label='Price')
|
||||
ax2.legend_.remove()
|
||||
ax2.set_ylabel('{asset}\n({base})'.format(
|
||||
asset = context.asset.symbol,
|
||||
base = base_currency
|
||||
))
|
||||
start, end = ax2.get_ylim()
|
||||
ax2.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
transaction_df = extract_transactions(perf)
|
||||
if not transaction_df.empty:
|
||||
buy_df = transaction_df[transaction_df['amount'] > 0]
|
||||
sell_df = transaction_df[transaction_df['amount'] < 0]
|
||||
ax2.scatter(
|
||||
buy_df.index.to_pydatetime(),
|
||||
perf.loc[buy_df.index, 'price'],
|
||||
marker='^',
|
||||
s=100,
|
||||
c='green',
|
||||
label=''
|
||||
)
|
||||
ax2.scatter(
|
||||
sell_df.index.to_pydatetime(),
|
||||
perf.loc[sell_df.index, 'price'],
|
||||
marker='v',
|
||||
s=100,
|
||||
c='red',
|
||||
label=''
|
||||
)
|
||||
|
||||
# Third chart: Compare percentage change between our portfolio
|
||||
# and the price of the asset
|
||||
ax3 = plt.subplot(413, sharex=ax1)
|
||||
perf.loc[:, ['algorithm_period_return', 'price_change']].plot(ax=ax3)
|
||||
ax3.legend_.remove()
|
||||
ax3.set_ylabel('Percent Change')
|
||||
start, end = ax3.get_ylim()
|
||||
ax3.yaxis.set_ticks(np.arange(start, end, (end-start)/5))
|
||||
|
||||
# Fourth chart: Plot our cash
|
||||
ax4 = plt.subplot(414, sharex=ax1)
|
||||
perf.cash.plot(ax=ax4)
|
||||
ax4.set_ylabel('Cash\n({})'.format(base_currency))
|
||||
start, end = ax4.get_ylim()
|
||||
ax4.yaxis.set_ticks(np.arange(0, end, end/5))
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_algorithm(
|
||||
capital_base=1000,
|
||||
data_frequency='minute',
|
||||
initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
exchange_name='bitfinex',
|
||||
algo_namespace=NAMESPACE,
|
||||
base_currency='usd',
|
||||
start=pd.to_datetime('2017-9-22', utc=True),
|
||||
end=pd.to_datetime('2017-9-23', utc=True),
|
||||
)
|
||||
.. literalinclude:: ../../catalyst/examples/dual_moving_average.py
|
||||
:language: python
|
||||
|
||||
.. image:: https://s3.amazonaws.com/enigmaco-docs/github.io/tutorial_dual_moving_average.png
|
||||
|
||||
@@ -446,8 +118,6 @@ This strategy is covered in detail in the last part of
|
||||
Mean Reversion Algorithm
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Source code: `examples/mean_reversion_simple.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/mean_reversion_simple.py>`_
|
||||
|
||||
This algorithm is based on a simple momentum strategy. When the cryptoasset goes
|
||||
up quickly, we're going to buy; when it goes down quickly, we're going to sell.
|
||||
Hopefully, we'll ride the waves.
|
||||
@@ -468,284 +138,10 @@ lines 218-245, so in order to run the algorithm we just type:
|
||||
|
||||
python mean_reversion_simple.py
|
||||
|
||||
.. code-block:: python
|
||||
Source code: `examples/mean_reversion_simple.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/mean_reversion_simple.py>`_
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import talib
|
||||
from logbook import Logger
|
||||
|
||||
from catalyst import run_algorithm
|
||||
from catalyst.api import symbol, record, order_target_percent, get_open_orders
|
||||
from catalyst.exchange.stats_utils import extract_transactions
|
||||
# We give a name to the algorithm which Catalyst will use to persist its state.
|
||||
# In this example, Catalyst will create the `.catalyst/data/live_algos`
|
||||
# directory. If we stop and start the algorithm, Catalyst will resume its
|
||||
# state using the files included in the folder.
|
||||
from catalyst.utils.paths import ensure_directory
|
||||
|
||||
NAMESPACE = 'mean_reversion_simple'
|
||||
log = Logger(NAMESPACE)
|
||||
|
||||
|
||||
# To run an algorithm in Catalyst, you need two functions: initialize and
|
||||
# handle_data.
|
||||
|
||||
def initialize(context):
|
||||
# This initialize function sets any data or variables that you'll use in
|
||||
# your algorithm. For instance, you'll want to define the trading pair (or
|
||||
# trading pairs) you want to backtest. You'll also want to define any
|
||||
# parameters or values you're going to use.
|
||||
|
||||
# In our example, we're looking at Neo in USD.
|
||||
context.neo_eth = symbol('neo_usd')
|
||||
context.base_price = None
|
||||
context.current_day = None
|
||||
|
||||
context.RSI_OVERSOLD = 30
|
||||
context.RSI_OVERBOUGHT = 80
|
||||
context.CANDLE_SIZE = '15T'
|
||||
|
||||
context.start_time = time.time()
|
||||
|
||||
|
||||
def handle_data(context, data):
|
||||
# This handle_data function is where the real work is done. Our data is
|
||||
# minute-level tick data, and each minute is called a frame. This function
|
||||
# runs on each frame of the data.
|
||||
|
||||
# We flag the first period of each day.
|
||||
# Since cryptocurrencies trade 24/7 the `before_trading_starts` handle
|
||||
# would only execute once. This method works with minute and daily
|
||||
# frequencies.
|
||||
today = data.current_dt.floor('1D')
|
||||
if today != context.current_day:
|
||||
context.traded_today = False
|
||||
context.current_day = today
|
||||
|
||||
# We're computing the volume-weighted-average-price of the security
|
||||
# defined above, in the context.neo_eth variable. For this example, we're
|
||||
# using three bars on the 15 min bars.
|
||||
|
||||
# The frequency attribute determine the bar size. We use this convention
|
||||
# for the frequency alias:
|
||||
# http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases
|
||||
prices = data.history(
|
||||
context.neo_eth,
|
||||
fields='close',
|
||||
bar_count=50,
|
||||
frequency=context.CANDLE_SIZE
|
||||
)
|
||||
|
||||
# Ta-lib calculates various technical indicator based on price and
|
||||
# volume arrays.
|
||||
|
||||
# In this example, we are comp
|
||||
rsi = talib.RSI(prices.values, timeperiod=14)
|
||||
|
||||
# We need a variable for the current price of the security to compare to
|
||||
# the average. Since we are requesting two fields, data.current()
|
||||
# returns a DataFrame with
|
||||
current = data.current(context.neo_eth, fields=['close', 'volume'])
|
||||
price = current['close']
|
||||
|
||||
# If base_price is not set, we use the current value. This is the
|
||||
# price at the first bar which we reference to calculate price_change.
|
||||
if context.base_price is None:
|
||||
context.base_price = price
|
||||
|
||||
price_change = (price - context.base_price) / context.base_price
|
||||
cash = context.portfolio.cash
|
||||
|
||||
# Now that we've collected all current data for this frame, we use
|
||||
# the record() method to save it. This data will be available as
|
||||
# a parameter of the analyze() function for further analysis.
|
||||
record(
|
||||
price=price,
|
||||
volume=current['volume'],
|
||||
price_change=price_change,
|
||||
rsi=rsi[-1],
|
||||
cash=cash
|
||||
)
|
||||
|
||||
# We are trying to avoid over-trading by limiting our trades to
|
||||
# one per day.
|
||||
if context.traded_today:
|
||||
return
|
||||
|
||||
# Since we are using limit orders, some orders may not execute immediately
|
||||
# we wait until all orders are executed before considering more trades.
|
||||
orders = get_open_orders(context.neo_eth)
|
||||
if len(orders) > 0:
|
||||
return
|
||||
|
||||
# Exit if we cannot trade
|
||||
if not data.can_trade(context.neo_eth):
|
||||
return
|
||||
|
||||
# Another powerful built-in feature of the Catalyst backtester is the
|
||||
# portfolio object. The portfolio object tracks your positions, cash,
|
||||
# cost basis of specific holdings, and more. In this line, we calculate
|
||||
# how long or short our position is at this minute.
|
||||
pos_amount = context.portfolio.positions[context.neo_eth].amount
|
||||
|
||||
if rsi[-1] <= context.RSI_OVERSOLD and pos_amount == 0:
|
||||
log.info(
|
||||
'{}: buying - price: {}, rsi: {}'.format(
|
||||
data.current_dt, price, rsi[-1]
|
||||
)
|
||||
)
|
||||
# Set a style for limit orders,
|
||||
limit_price = price * 1.005
|
||||
order_target_percent(
|
||||
context.neo_eth, 1, limit_price=limit_price
|
||||
)
|
||||
context.traded_today = True
|
||||
|
||||
elif rsi[-1] >= context.RSI_OVERBOUGHT and pos_amount > 0:
|
||||
log.info(
|
||||
'{}: selling - price: {}, rsi: {}'.format(
|
||||
data.current_dt, price, rsi[-1]
|
||||
)
|
||||
)
|
||||
limit_price = price * 0.995
|
||||
order_target_percent(
|
||||
context.neo_eth, 0, limit_price=limit_price
|
||||
)
|
||||
context.traded_today = True
|
||||
|
||||
|
||||
def analyze(context=None, perf=None):
|
||||
end = time.time()
|
||||
log.info('elapsed time: {}'.format(end - context.start_time))
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
# The base currency of the algo exchange
|
||||
base_currency = context.exchanges.values()[0].base_currency.upper()
|
||||
|
||||
# Plot the portfolio value over time.
|
||||
ax1 = plt.subplot(611)
|
||||
perf.loc[:, 'portfolio_value'].plot(ax=ax1)
|
||||
ax1.set_ylabel('Portfolio\nValue\n({})'.format(base_currency))
|
||||
|
||||
# Plot the price increase or decrease over time.
|
||||
ax2 = plt.subplot(612, sharex=ax1)
|
||||
perf.loc[:, 'price'].plot(ax=ax2, label='Price')
|
||||
|
||||
ax2.set_ylabel('{asset}\n({base})'.format(
|
||||
asset=context.neo_eth.symbol, base=base_currency
|
||||
))
|
||||
|
||||
transaction_df = extract_transactions(perf)
|
||||
if not transaction_df.empty:
|
||||
buy_df = transaction_df[transaction_df['amount'] > 0]
|
||||
sell_df = transaction_df[transaction_df['amount'] < 0]
|
||||
ax2.scatter(
|
||||
buy_df.index.to_pydatetime(),
|
||||
perf.loc[buy_df.index.floor('1 min'), 'price'],
|
||||
marker='^',
|
||||
s=100,
|
||||
c='green',
|
||||
label=''
|
||||
)
|
||||
ax2.scatter(
|
||||
sell_df.index.to_pydatetime(),
|
||||
perf.loc[sell_df.index.floor('1 min'), 'price'],
|
||||
marker='v',
|
||||
s=100,
|
||||
c='red',
|
||||
label=''
|
||||
)
|
||||
|
||||
ax4 = plt.subplot(613, sharex=ax1)
|
||||
perf.loc[:, 'cash'].plot(
|
||||
ax=ax4, label='Base Currency ({})'.format(base_currency)
|
||||
)
|
||||
ax4.set_ylabel('Cash\n({})'.format(base_currency))
|
||||
|
||||
perf['algorithm'] = perf.loc[:, 'algorithm_period_return']
|
||||
|
||||
ax5 = plt.subplot(614, sharex=ax1)
|
||||
perf.loc[:, ['algorithm', 'price_change']].plot(ax=ax5)
|
||||
ax5.set_ylabel('Percent\nChange')
|
||||
|
||||
ax6 = plt.subplot(615, sharex=ax1)
|
||||
perf.loc[:, 'rsi'].plot(ax=ax6, label='RSI')
|
||||
ax6.set_ylabel('RSI')
|
||||
ax6.axhline(context.RSI_OVERBOUGHT, color='darkgoldenrod')
|
||||
ax6.axhline(context.RSI_OVERSOLD, color='darkgoldenrod')
|
||||
|
||||
if not transaction_df.empty:
|
||||
ax6.scatter(
|
||||
buy_df.index.to_pydatetime(),
|
||||
perf.loc[buy_df.index.floor('1 min'), 'rsi'],
|
||||
marker='^',
|
||||
s=100,
|
||||
c='green',
|
||||
label=''
|
||||
)
|
||||
ax6.scatter(
|
||||
sell_df.index.to_pydatetime(),
|
||||
perf.loc[sell_df.index.floor('1 min'), 'rsi'],
|
||||
marker='v',
|
||||
s=100,
|
||||
c='red',
|
||||
label=''
|
||||
)
|
||||
plt.legend(loc=3)
|
||||
start, end = ax6.get_ylim()
|
||||
ax6.yaxis.set_ticks(np.arange(0, end, end/5))
|
||||
|
||||
# Show the plot.
|
||||
plt.gcf().set_size_inches(18, 8)
|
||||
plt.show()
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# The execution mode: backtest or live
|
||||
MODE = 'backtest'
|
||||
|
||||
if MODE == 'backtest':
|
||||
folder = os.path.join(
|
||||
tempfile.gettempdir(), 'catalyst', NAMESPACE
|
||||
)
|
||||
ensure_directory(folder)
|
||||
|
||||
timestr = time.strftime('%Y%m%d-%H%M%S')
|
||||
out = os.path.join(folder, '{}.p'.format(timestr))
|
||||
# catalyst run -f catalyst/examples/mean_reversion_simple.py -x bitfinex -s 2017-10-1 -e 2017-11-10 -c usdt -n mean-reversion --data-frequency minute --capital-base 10000
|
||||
run_algorithm(
|
||||
capital_base=10000,
|
||||
data_frequency='minute',
|
||||
initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
exchange_name='bitfinex',
|
||||
algo_namespace=NAMESPACE,
|
||||
base_currency='usd',
|
||||
start=pd.to_datetime('2017-10-01', utc=True),
|
||||
end=pd.to_datetime('2017-11-10', utc=True),
|
||||
output=out
|
||||
)
|
||||
log.info('saved perf stats: {}'.format(out))
|
||||
|
||||
elif MODE == 'live':
|
||||
run_algorithm(
|
||||
capital_base=0.5,
|
||||
initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
exchange_name='bittrex',
|
||||
live=True,
|
||||
algo_namespace=NAMESPACE,
|
||||
base_currency='usd',
|
||||
live_graph=False
|
||||
)
|
||||
.. literalinclude:: ../../catalyst/examples/mean_reversion_simple.py
|
||||
:language: python
|
||||
|
||||
.. image:: https://s3.amazonaws.com/enigmaco-docs/github.io/example_mean_reversion_simple.png
|
||||
|
||||
@@ -762,8 +158,6 @@ strategy.
|
||||
Simple Universe
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Source code: `examples/simple_universe.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/simple_universe.py>`_
|
||||
|
||||
This example aims to provide an easy way for users to learn how to
|
||||
collect data from any given exchange and select a subset of the available
|
||||
currency pairs for trading. You simply need to specify the exchange and
|
||||
@@ -790,142 +184,10 @@ of the file:
|
||||
|
||||
catalyst ingest-exchange -x bitfinex -f minute
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python simple_universe.py
|
||||
|
||||
Credits: This code was originally submitted by `Abner Ayala-Acevedo
|
||||
<https://github.com/abnera>`_. Thank you!
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from catalyst import run_algorithm
|
||||
from catalyst.exchange.utils.exchange_utils import get_exchange_symbols
|
||||
from catalyst.api import (symbols, )
|
||||
|
||||
|
||||
def initialize(context):
|
||||
context.i = -1 # minute counter
|
||||
context.exchange = context.exchanges.values()[0].name.lower()
|
||||
context.base_currency = context.exchanges.values()[0].base_currency.lower()
|
||||
|
||||
|
||||
def handle_data(context, data):
|
||||
context.i += 1
|
||||
lookback_days = 7 # 7 days
|
||||
|
||||
# current date & time in each iteration formatted into a string
|
||||
now = data.current_dt
|
||||
date, time = now.strftime('%Y-%m-%d %H:%M:%S').split(' ')
|
||||
lookback_date = now - timedelta(days=lookback_days)
|
||||
# keep only the date as a string, discard the time
|
||||
lookback_date = lookback_date.strftime('%Y-%m-%d %H:%M:%S').split(' ')[0]
|
||||
|
||||
one_day_in_minutes = 1440 # 60 * 24 assumes data_frequency='minute'
|
||||
# update universe everyday at midnight
|
||||
if not context.i % one_day_in_minutes:
|
||||
context.universe = universe(context, lookback_date, date)
|
||||
|
||||
# get data every 30 minutes
|
||||
minutes = 30
|
||||
# get lookback_days of history data: that is 'lookback' number of bins
|
||||
lookback = one_day_in_minutes / minutes * lookback_days
|
||||
if not context.i % minutes and context.universe:
|
||||
# we iterate for every pair in the current universe
|
||||
for coin in context.coins:
|
||||
pair = str(coin.symbol)
|
||||
|
||||
# Get 30 minute interval OHLCV data. This is the standard data
|
||||
# required for candlestick or indicators/signals. Return Pandas
|
||||
# DataFrames. 30T means 30-minute re-sampling of one minute data.
|
||||
# Adjust it to your desired time interval as needed.
|
||||
opened = fill(data.history(coin, 'open',
|
||||
bar_count=lookback, frequency='30T')).values
|
||||
high = fill(data.history(coin, 'high',
|
||||
bar_count=lookback, frequency='30T')).values
|
||||
low = fill(data.history(coin, 'low',
|
||||
bar_count=lookback, frequency='30T')).values
|
||||
close = fill(data.history(coin, 'price',
|
||||
bar_count=lookback, frequency='30T')).values
|
||||
volume = fill(data.history(coin, 'volume',
|
||||
bar_count=lookback, frequency='30T')).values
|
||||
|
||||
# close[-1] is the last value in the set, which is the equivalent
|
||||
# to current price (as in the most recent value)
|
||||
# displays the minute price for each pair every 30 minutes
|
||||
print('{now}: {pair} -\tO:{o},\tH:{h},\tL:{c},\tC{c},\tV:{v}'.format(
|
||||
now=now,
|
||||
pair=pair,
|
||||
o=opened[-1],
|
||||
h=high[-1],
|
||||
l=low[-1],
|
||||
c=close[-1],
|
||||
v=volume[-1],
|
||||
))
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# --------------- Insert Your Strategy Here -------------------
|
||||
# -------------------------------------------------------------
|
||||
|
||||
|
||||
def analyze(context=None, results=None):
|
||||
pass
|
||||
|
||||
|
||||
# Get the universe for a given exchange and a given base_currency market
|
||||
# Example: Poloniex BTC Market
|
||||
def universe(context, lookback_date, current_date):
|
||||
# get all the pairs for the given exchange
|
||||
json_symbols = get_exchange_symbols(context.exchange)
|
||||
# convert into a DataFrame for easier processing
|
||||
df = pd.DataFrame.from_dict(json_symbols).transpose().astype(str)
|
||||
df['base_currency'] = df.apply(lambda row: row.symbol.split('_')[1],axis=1)
|
||||
df['market_currency'] = df.apply(lambda row: row.symbol.split('_')[0],axis=1)
|
||||
|
||||
# Filter all the pairs to get only the ones for a given base_currency
|
||||
df = df[df['base_currency'] == context.base_currency]
|
||||
|
||||
# Filter all the pairs to ensure that pair existed in the current date range
|
||||
df = df[df.start_date < lookback_date]
|
||||
df = df[df.end_daily >= current_date]
|
||||
context.coins = symbols(*df.symbol) # convert all the pairs to symbols
|
||||
|
||||
return df.symbol.tolist()
|
||||
|
||||
|
||||
# Replace all NA, NAN or infinite values with its nearest value
|
||||
def fill(series):
|
||||
if isinstance(series, pd.Series):
|
||||
return series.replace([np.inf, -np.inf], np.nan).ffill().bfill()
|
||||
elif isinstance(series, np.ndarray):
|
||||
return pd.Series(series).replace(
|
||||
[np.inf, -np.inf], np.nan
|
||||
).ffill().bfill().values
|
||||
else:
|
||||
return series
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
start_date = pd.to_datetime('2017-11-10', utc=True)
|
||||
end_date = pd.to_datetime('2017-11-13', utc=True)
|
||||
|
||||
performance = run_algorithm(start=start_date, end=end_date,
|
||||
capital_base=100.0, # amount of base_currency
|
||||
initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
exchange_name='bitfinex',
|
||||
data_frequency='minute',
|
||||
base_currency='btc',
|
||||
live=False,
|
||||
live_graph=False,
|
||||
algo_namespace='simple_universe')
|
||||
Source code: `examples/simple_universe.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/simple_universe.py>`_
|
||||
|
||||
.. literalinclude:: ../../catalyst/examples/simple_universe.py
|
||||
:language: python
|
||||
|
||||
|
||||
.. _portfolio_optimization:
|
||||
@@ -939,135 +201,10 @@ use 180 days of historical data and rebalance every 30 days. This code was used
|
||||
in writting the following article:
|
||||
`Markowitz Portfolio Optimization for Cryptocurrencies <https://blog.enigma.co/markowitz-portfolio-optimization-for-cryptocurrencies-in-catalyst-b23c38652556>`_.
|
||||
|
||||
.. code-block:: python
|
||||
Source code: `examples/simple_universe.py <https://github.com/enigmampc/catalyst/blob/master/catalyst/examples/portfolio_optimization.py>`_
|
||||
|
||||
'''
|
||||
You can run this code using the Python interpreter:
|
||||
|
||||
$ python portfolio_optimization.py
|
||||
'''
|
||||
|
||||
from __future__ import division
|
||||
import os
|
||||
import pytz
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from scipy.optimize import minimize
|
||||
import matplotlib.pyplot as plt
|
||||
from datetime import datetime
|
||||
|
||||
from catalyst.api import record, symbol, symbols, order_target_percent
|
||||
from catalyst.utils.run_algo import run_algorithm
|
||||
|
||||
np.set_printoptions(threshold='nan', suppress=True)
|
||||
|
||||
|
||||
def initialize(context):
|
||||
# Portfolio assets list
|
||||
context.assets = symbols('btc_usdt', 'eth_usdt', 'ltc_usdt', 'dash_usdt',
|
||||
'xmr_usdt')
|
||||
context.nassets = len(context.assets)
|
||||
# Set the time window that will be used to compute expected return
|
||||
# and asset correlations
|
||||
context.window = 180
|
||||
# Set the number of days between each portfolio rebalancing
|
||||
context.rebalance_period = 30
|
||||
context.i = 0
|
||||
|
||||
|
||||
def handle_data(context, data):
|
||||
# Only rebalance at the beggining of the algorithm execution and
|
||||
# every multiple of the rebalance period
|
||||
if context.i == 0 or context.i%context.rebalance_period == 0:
|
||||
n = context.window
|
||||
prices = data.history(context.assets, fields='price',
|
||||
bar_count=n+1, frequency='1d')
|
||||
pr = np.asmatrix(prices)
|
||||
t_prices = prices.iloc[1:n+1]
|
||||
t_val = t_prices.values
|
||||
tminus_prices = prices.iloc[0:n]
|
||||
tminus_val = tminus_prices.values
|
||||
# Compute daily returns (r)
|
||||
r = np.asmatrix(t_val/tminus_val-1)
|
||||
# Compute the expected returns of each asset with the average
|
||||
# daily return for the selected time window
|
||||
m = np.asmatrix(np.mean(r, axis=0))
|
||||
# ###
|
||||
stds = np.std(r, axis=0)
|
||||
# Compute excess returns matrix (xr)
|
||||
xr = r - m
|
||||
# Matrix algebra to get variance-covariance matrix
|
||||
cov_m = np.dot(np.transpose(xr),xr)/n
|
||||
# Compute asset correlation matrix (informative only)
|
||||
corr_m = cov_m/np.dot(np.transpose(stds),stds)
|
||||
|
||||
# Define portfolio optimization parameters
|
||||
n_portfolios = 50000
|
||||
results_array = np.zeros((3+context.nassets,n_portfolios))
|
||||
for p in xrange(n_portfolios):
|
||||
weights = np.random.random(context.nassets)
|
||||
weights /= np.sum(weights)
|
||||
w = np.asmatrix(weights)
|
||||
p_r = np.sum(np.dot(w,np.transpose(m)))*365
|
||||
p_std = np.sqrt(np.dot(np.dot(w,cov_m),np.transpose(w)))*np.sqrt(365)
|
||||
|
||||
#store results in results array
|
||||
results_array[0,p] = p_r
|
||||
results_array[1,p] = p_std
|
||||
#store Sharpe Ratio (return / volatility) - risk free rate element
|
||||
#excluded for simplicity
|
||||
results_array[2,p] = results_array[0,p] / results_array[1,p]
|
||||
i = 0
|
||||
for iw in weights:
|
||||
results_array[3+i,p] = weights[i]
|
||||
i += 1
|
||||
|
||||
#convert results array to Pandas DataFrame
|
||||
results_frame = pd.DataFrame(np.transpose(results_array),
|
||||
columns=['r','stdev','sharpe']+context.assets)
|
||||
#locate position of portfolio with highest Sharpe Ratio
|
||||
max_sharpe_port = results_frame.iloc[results_frame['sharpe'].idxmax()]
|
||||
#locate positon of portfolio with minimum standard deviation
|
||||
min_vol_port = results_frame.iloc[results_frame['stdev'].idxmin()]
|
||||
|
||||
#order optimal weights for each asset
|
||||
for asset in context.assets:
|
||||
if data.can_trade(asset):
|
||||
order_target_percent(asset, max_sharpe_port[asset])
|
||||
|
||||
#create scatter plot coloured by Sharpe Ratio
|
||||
plt.scatter(results_frame.stdev,results_frame.r,c=results_frame.sharpe,cmap='RdYlGn')
|
||||
plt.xlabel('Volatility')
|
||||
plt.ylabel('Returns')
|
||||
plt.colorbar()
|
||||
#plot red star to highlight position of portfolio with highest Sharpe Ratio
|
||||
plt.scatter(max_sharpe_port[1],max_sharpe_port[0],marker='o',color='b',s=200)
|
||||
#plot green star to highlight position of minimum variance portfolio
|
||||
plt.show()
|
||||
print(max_sharpe_port)
|
||||
record(pr=pr,r=r, m=m, stds=stds ,max_sharpe_port=max_sharpe_port, corr_m=corr_m)
|
||||
context.i += 1
|
||||
|
||||
|
||||
def analyze(context=None, results=None):
|
||||
# Form DataFrame with selected data
|
||||
data = results[['pr','r','m','stds','max_sharpe_port','corr_m','portfolio_value']]
|
||||
|
||||
# Save results in CSV file
|
||||
filename = os.path.splitext(os.path.basename(__file__))[0]
|
||||
data.to_csv(filename + '.csv')
|
||||
|
||||
|
||||
# Bitcoin data is available from 2015-3-2. Dates vary for other tokens.
|
||||
start = datetime(2017, 1, 1, 0, 0, 0, 0, pytz.utc)
|
||||
end = datetime(2017, 8, 16, 0, 0, 0, 0, pytz.utc)
|
||||
results = run_algorithm(initialize=initialize,
|
||||
handle_data=handle_data,
|
||||
analyze=analyze,
|
||||
start=start,
|
||||
end=end,
|
||||
exchange_name='poloniex',
|
||||
capital_base=100000, )
|
||||
.. literalinclude:: ../../catalyst/examples/portfolio_optimization.py
|
||||
:language: python
|
||||
|
||||
.. image:: https://cdn-images-1.medium.com/max/1600/0*EjjiKZHlYF3sn7yQ.
|
||||
:align: center
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
.. include:: ../../README.rst
|
||||
|
||||
|
|
||||
|
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
|
||||
|
||||
+35
-13
@@ -47,8 +47,10 @@ you can install MiniConda, which is a smaller footprint (fewer packages and
|
||||
smaller size) than its big brother Anaconda, but it still contains all the
|
||||
main packages needed. To install MiniConda, you can follow these steps:
|
||||
|
||||
1. Download `MiniConda <https://conda.io/miniconda.html>`_. Select Python 2.7
|
||||
for your Operating System.
|
||||
1. Download `MiniConda <https://conda.io/miniconda.html>`_. Select either
|
||||
Python 3.6 (recommended) or Python 2.7 for your Operating System. The
|
||||
`Enigma Data Marketplace <https://enigmampc.github.io/marketplace/>`_ will
|
||||
require Python3, that's why we are recommending to opt for the newer version.
|
||||
2. Install MiniConda. See the `Installation Instructions
|
||||
<https://conda.io/docs/user-guide/install/index.html>`_ if you need help.
|
||||
3. Ensure the correct installation by running ``conda list`` in a Terminal
|
||||
@@ -64,21 +66,30 @@ main packages needed. To install MiniConda, you can follow these steps:
|
||||
|
||||
Once either Conda or MiniConda has been set up you can install Catalyst:
|
||||
|
||||
1. Download the file `python2.7-environment.yml
|
||||
<https://github.com/enigmampc/catalyst/blob/master/etc/python2.7-environment.yml>`_.
|
||||
1. Download the file `python3.6-environment.yml
|
||||
<https://github.com/enigmampc/catalyst/blob/master/etc/python3.6-environment.yml>`_
|
||||
(recommended) or `python2.7-environment.yml
|
||||
<https://github.com/enigmampc/catalyst/blob/master/etc/python2.7-environment.yml>`_
|
||||
matching your Conda installation from step #1 above.
|
||||
|
||||
To download, simply click on the 'Raw' button and save the file locally
|
||||
to a folder you can remember. Make sure that the file gets saved with the
|
||||
``.yml`` extension, and nothing like a ``.txt`` file or anything else.
|
||||
|
||||
2. Open a Terminal window and enter [``cd/dir``] into the directory where you
|
||||
saved the above ``python2.7-environment.yml`` file.
|
||||
saved the above ``.yml`` file.
|
||||
|
||||
3. Install using this file. This step can take about 5-10 minutes to install.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
conda env create -f python2.7-environment.yml
|
||||
conda env create -f python3.6-environment.yml
|
||||
|
||||
or
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
conda env create -f python2.7-environment.yml
|
||||
|
||||
4. Activate the environment (which you need to do every time you start a new
|
||||
session to run Catalyst):
|
||||
@@ -298,7 +309,7 @@ Troubleshooting ``pip`` Install
|
||||
.. _pipenv:
|
||||
|
||||
Installing with ``pipenv``
|
||||
-------------------------
|
||||
--------------------------
|
||||
|
||||
Installing Catalyst via ``pipenv`` is perhaps easier that installing it via
|
||||
``pip`` itself but you need to install ``pipenv`` first via ``pip``.
|
||||
@@ -443,12 +454,22 @@ about matplotlib backends, please refer to the
|
||||
Windows Requirements
|
||||
--------------------
|
||||
|
||||
In Windows, you will first need to install the `Microsoft Visual C++ Compiler
|
||||
for Python 2.7
|
||||
<https://www.microsoft.com/en-us/download/details.aspx?id=44266>`_. This
|
||||
package contains the compiler and the set of system headers necessary for
|
||||
producing binary wheels for Python 2.7 packages. If it's not already in your
|
||||
system, download it and install it before proceeding to the next step.
|
||||
In Windows, you will first need to install the Microsoft Visual C++ Compiler,
|
||||
which is different depending on the version of Python that you plan to use:
|
||||
|
||||
* Python 3.5, 3.6: `Visual C++ 2015 Build Tools
|
||||
<http://landinghub.visualstudio.com/visual-cpp-build-tools>`_,
|
||||
which installs Visual C++ version 14.0. **This is the recommended version**
|
||||
|
||||
* Python 2.7: `Microsoft Visual C++ Compiler for Python 2.7
|
||||
<https://www.microsoft.com/en-us/download/details.aspx?id=44266>`_, which
|
||||
installs version Visual C++ version 9.0
|
||||
|
||||
This package contains the compiler and the set of system headers necessary for
|
||||
producing binary wheels for Python packages. If it's not already in your
|
||||
system, download it and install it before proceeding to the next step. If you
|
||||
need additional help, or are looking for other versions of Visual C++ for
|
||||
Windows (only advanced users), follow `this link <https://wiki.python.org/moin/WindowsCompilers>`_.
|
||||
|
||||
Once you have the above compiler installed, the easiest and best supported way
|
||||
to install Catalyst in Windows is to use :ref:`Conda <conda>`. If you didn't
|
||||
@@ -476,6 +497,7 @@ mentioned above are as follows:
|
||||
default you get 0 as the Value Data)
|
||||
|
||||
|
|
||||
|
||||
- **The installer has encountered an unexpected error installing this package.
|
||||
This may indicate a problem with this package. The error code is 2503.**
|
||||
|
||||
|
||||
@@ -30,22 +30,24 @@ Paper Trading vs Live Trading modes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Catalyst currently supports three different modes in which you can execute your
|
||||
trading algorithm. The first is backtesting, which is covered extensively in the
|
||||
tutorial, and uses historical data to run your algorithm. There is no
|
||||
trading algorithm. The first is **backtesting**, which is covered extensively in
|
||||
the tutorial, and uses historical data to run your algorithm. There is no
|
||||
interaction with the exchange in backtesting mode, and this is the first mode
|
||||
that you should test any new algorithm.
|
||||
|
||||
Once you are confident with the simulations that you have obtained with your
|
||||
algorithm in backtesting, you may switch to live trading, where you have two
|
||||
different modes:
|
||||
* *Paper Trading*: The simulated algorithm runs in real time, and fetches
|
||||
pricing data in real time from the exchange, but the orders never reach the
|
||||
exchange, and are instead kept within Catalyst and simulated. No real currency
|
||||
is bought or sold. Think of it as a `backtesting happening in real time`.
|
||||
* *Live Trading*: This is the proper live trading mode in which an algorithm
|
||||
runs in real time, fetching pricing data from live exchanges and placing orders
|
||||
against the exchange. Real currency is transacted on the exchange driven by the
|
||||
algorithm.
|
||||
|
||||
* **Paper Trading**: The simulated algorithm runs in real time, and fetches
|
||||
pricing data in real time from the exchange, but the orders never reach the
|
||||
exchange, and are instead kept within Catalyst and simulated. No real currency
|
||||
is bought or sold. Think of it as a `backtesting happening in real time`.
|
||||
|
||||
* **Live Trading**: This is the proper live trading mode in which an algorithm
|
||||
runs in real time, fetching pricing data from live exchanges and placing
|
||||
orders against the exchange. Real currency is transacted on the exchange
|
||||
driven by the algorithm.
|
||||
|
||||
These three modes are controlled by the following variables:
|
||||
|
||||
@@ -113,7 +115,7 @@ Currency symbols (e.g. btc, eth, ltc) follow the Bittrex convention.
|
||||
|
||||
Here are some examples:
|
||||
|
||||
.. code-block:: json
|
||||
.. code:: python
|
||||
|
||||
# With Bitfinex
|
||||
bitcoin_usd_asset = symbol('btc_usd')
|
||||
@@ -174,6 +176,13 @@ Here is the breakdown of the new arguments:
|
||||
- ``simulate_orders``: Enables the paper trading mode, in which orders are
|
||||
simulated in Catalyst instead of processed on the exchange. It defaults to
|
||||
``True``.
|
||||
- ``end_date``: When setting the end_date to a time in the **future**,
|
||||
it will schedule the live algo to finish gracefully at the specified date.
|
||||
- ``start_date``: (**Will be implemented in the future**)
|
||||
The live algo starts by default in the present, as mentioned above.
|
||||
by setting the start_date to a time in the future, the algorithm would
|
||||
essentially sleep and when the predefined time comes, it would start executing.
|
||||
|
||||
|
||||
|
||||
The `catalyst live` command offers additional parameters.
|
||||
|
||||
@@ -2,6 +2,38 @@
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
Version 0.5.3
|
||||
^^^^^^^^^^^^^
|
||||
**Release Date**: 2018-02-09
|
||||
|
||||
Bug Fixes
|
||||
~~~~~~~~~
|
||||
- Fixed an issue with last candle in backtesting :issue:`219`
|
||||
|
||||
Version 0.5.2
|
||||
^^^^^^^^^^^^^
|
||||
**Release Date**: 2018-02-08
|
||||
|
||||
Bug Fixes
|
||||
~~~~~~~~~
|
||||
- Fixed an issue with live candle values :issue:`216` and :issue:`199`
|
||||
|
||||
Version 0.5.1
|
||||
^^^^^^^^^^^^^
|
||||
**Release Date**: 2018-02-07
|
||||
|
||||
Bug Fixes
|
||||
~~~~~~~~~
|
||||
- Fixed an issue with orders that stay open :issue:`211`
|
||||
- Fixed Jupyter issues :issue:`179`
|
||||
- Fetching multiple tickers in one call to minimize rate limit risks :issue:`174`
|
||||
- Improved live state presentation :issue:`171`
|
||||
|
||||
|
||||
Build
|
||||
~~~~~
|
||||
- Introducing the Enigma Marketplace
|
||||
|
||||
Version 0.4.7
|
||||
^^^^^^^^^^^^^
|
||||
**Release Date**: 2018-01-19
|
||||
|
||||
@@ -11,6 +11,7 @@ Installation: MacOS
|
||||
|
||||
|
|
||||
|
|
||||
|
||||
Installation: Windows
|
||||
---------------------
|
||||
|
||||
@@ -21,6 +22,7 @@ Where things go smoothly:
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/H8HqcEbZmkk" frameborder="0" allowfullscreen></iframe>
|
||||
|
||||
|
|
||||
|
||||
Where things don't:
|
||||
|
||||
.. raw:: html
|
||||
@@ -29,6 +31,7 @@ Where things don't:
|
||||
|
||||
|
|
||||
|
|
||||
|
||||
Backtesting a Strategy
|
||||
----------------------
|
||||
|
||||
@@ -44,6 +47,7 @@ sell. Hopefully, we’ll ride the waves.
|
||||
|
||||
|
|
||||
|
|
||||
|
||||
Live Trading a Strategy
|
||||
-----------------------
|
||||
|
||||
@@ -54,5 +58,6 @@ in the previous video, we now take it to trade live against the Bittrex exchange
|
||||
.. raw:: html
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/NupiE-Xuglw" frameborder="0" allowfullscreen></iframe>
|
||||
|
||||
|
|
||||
|
|
||||
|
|
||||
Reference in New Issue
Block a user