Skip to content

Commit

Permalink
Merge pull request #1 from bsdz/basket_orders
Browse files Browse the repository at this point in the history
Basket orders & Documentation & Restructuring
  • Loading branch information
bsdz authored Feb 8, 2023
2 parents 0a566fc + dee0368 commit d21dff1
Show file tree
Hide file tree
Showing 42 changed files with 10,534 additions and 457 deletions.
82 changes: 60 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ There are some basic tests but use at your own peril. It's not production level

The core module uses pandas.

## Other dependencies

The portfolio optimisation module uses scipy.

## Installation

```bash
Expand All @@ -23,43 +27,52 @@ pip install yatbe
Below is an example usage (the performance of the example strategy won't be good).

```python
import inspect
from pathlib import Path
import pandas as pd

import yabte
from yabte import Strategy, StrategyRunner, Order
from yabte.utils import crossover
import pandas as pd

data_dir = Path(yabte.__file__).parents[1] / "tests/data"
from yabte.backtest import Strategy, StrategyRunner, Order, Book
from yabte.utilities.strategy_helpers import crossover

data_dir = Path(inspect.getfile(Strategy)).parents[2] / "tests/data/nasdaq"

class SmokeStrat1(Strategy):
class SMAXO(Strategy):
def init(self):
# enhance data with simple moving averages
csma10 = (

p = self.params
days_short = p.get("days_short", 10)
days_long = p.get("days_long", 20)

close_sma_short = (
self.data.loc[:, (slice(None), "Close")]
.rolling(10)
.rolling(days_short)
.mean()
.rename({"Close": "CloseSMA10"}, axis=1, level=1)
.rename({"Close": "CloseSMAShort"}, axis=1, level=1)
)
csma20 = (
close_sma_long = (
self.data.loc[:, (slice(None), "Close")]
.rolling(20)
.rolling(days_long)
.mean()
.rename({"Close": "CloseSMA20"}, axis=1, level=1)
.rename({"Close": "CloseSMALong"}, axis=1, level=1)
)
self.data = pd.concat([self.data, csma10, csma20], axis=1).sort_index(axis=1)
self.data = pd.concat(
[self.data, close_sma_short, close_sma_long], axis=1
).sort_index(axis=1)

def on_close(self):
# create some orders
data_2d = self.data.iloc[-2:]
for sym in ["GOOG", "MSFT"]:
data = data_2d[sym].loc[:, ("CloseSMA10", "CloseSMA20")].dropna()

for symbol in ["GOOG", "MSFT"]:
df = self.data[symbol]
ix_2d = df.index[-2:]
data = df.loc[ix_2d, ("CloseSMAShort", "CloseSMALong")].dropna()
if len(data) == 2:
if crossover(data.CloseSMA10, data.CloseSMA20):
self.orders.append(Order(asset_name=sym, size=100))
elif crossover(data.CloseSMA20, data.CloseSMA10):
self.orders.append(Order(asset_name=sym, size=-100))
if crossover(data.CloseSMAShort, data.CloseSMALong):
self.orders.append(Order(asset_name=symbol, size=-100))
elif crossover(data.CloseSMALong, data.CloseSMAShort):
self.orders.append(Order(asset_name=symbol, size=100))


# load some data
Expand All @@ -71,15 +84,40 @@ df_goog.columns = pd.MultiIndex.from_tuples([("GOOG", f) for f in df_goog.column
df_msft = pd.read_csv(data_dir / "MSFT.csv", index_col=0, parse_dates=[0])
df_msft.columns = pd.MultiIndex.from_tuples([("MSFT", f) for f in df_msft.columns])

# create a book with 100000 cash
book = Book(name="PrimaryBook", cash="100000")

# run our strategy
sr = StrategyRunner(
data=pd.concat([df_goog, df_msft], axis=1),
asset_meta=asset_meta,
strats=[SmokeStrat1],
strats=[SMAXO],
books=[book],
)
sr.run()

# see the trades or book history
th = sr.trade_history
bch = sr.book_history.loc[:, (slice(None), "cash")]
```

# plot the trades against book value
bvh = sr.book_history.loc[:, (slice(None), "value")].droplevel(axis=1, level=1)
ax = bvh.plot(title="Book Value History")

for symbol, scol, lcol in [("GOOG", "red", "green"), ("MSFT", "blue", "yellow")]:
long_ix = th.query(f"asset_name == '{symbol}' and quantity > 0").ts
short_ix = th.query(f"asset_name == '{symbol}' and quantity < 0").ts
bvh.loc[long_ix].rename(columns={"PrimaryBook": f"{symbol} Short"}).plot(color=scol, marker="v", markersize=5, linestyle="None", ax=ax)
bvh.loc[short_ix].rename(columns={"PrimaryBook": f"{symbol} Long"}).plot(color=lcol, marker="^", markersize=5, linestyle="None", ax=ax)

```

![Output from code](./readme_image.png)

## Examples

Jupyter notebook examples can be found under the [notebooks folder](https://github.com/bsdz/yabte/tree/main/notebooks).

## Documentation

Documentation can be found on [Read the Docs](https://yabte.readthedocs.io/en/latest/).
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
34 changes: 34 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

import sys
import os

# ensure project path avaliable to sphinx
sys.path.insert(0, os.path.abspath(".."))


# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = 'yabte'
copyright = '2023, Blair Azzopardi'
author = 'Blair Azzopardi'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = ['sphinx.ext.autodoc', 'sphinx.ext.mathjax', 'sphinx.ext.viewcode']

templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']



# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
22 changes: 22 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.. yabte documentation master file, created by
sphinx-quickstart on Tue Feb 7 15:11:49 2023.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to yabte's documentation!
=================================


.. toctree::
:maxdepth: 2
:caption: Contents:

yabte.backtest
yabte.portopt

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
35 changes: 35 additions & 0 deletions docs/make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)

if "%1" == "" goto help

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
34 changes: 34 additions & 0 deletions docs/yabte.backtest.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--------------
yabte.backtest
--------------

Strategy
--------

.. automodule:: yabte.backtest.strategy
:members:

Order
-----

.. automodule:: yabte.backtest.order
:members:
:member-order: bysource

Trade
-----

.. automodule:: yabte.backtest.trade
:members:

Book
----

.. automodule:: yabte.backtest.book
:members:

Asset
-----

.. automodule:: yabte.backtest.asset
:members:
21 changes: 21 additions & 0 deletions docs/yabte.portopt.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-------------
yabte.portopt
-------------

Minimum Variance
----------------

.. automodule:: yabte.portopt.minimum_variance
:members:

Inverse Variance
----------------

.. automodule:: yabte.portopt.inverse_volatility
:members:

Hierarchical Risk Parity
------------------------

.. automodule:: yabte.portopt.hierarchical_risk_parity
:members:
Loading

0 comments on commit d21dff1

Please sign in to comment.