Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PyPI #22

Merged
merged 4 commits into from
Jan 18, 2024
Merged

PyPI #22

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: PyPI Publish

on:
release:
types: [published]

permissions:
contents: read

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"

- name: Install tools
run: |
python -m pip install --upgrade pip
pip install --upgrade setuptools wheel

- name: Build Package
run: make build

- name: Publish Package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
verbose: true
print-hash: true
1 change: 0 additions & 1 deletion .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# .github/workflows/app.yaml
name: PyTest
on: push

Expand Down
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ init:
install: init
$(PIP) install -e .

uninstall:
$(PIP) uninstall bktest

test:
$(PYTHON) -m pytest -v

example:
PYTHONPATH=. find example/ -name "*.py" -exec $(PYTHON) {} \;
build:
rm -rf build *.egg-info dist
python setup.py sdist bdist_wheel

.PHONY: init install test example
.PHONY: init install uninstall test build
73 changes: 35 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,39 @@
# crunchdao-backtest

A small backtesting utility.

![image](https://user-images.githubusercontent.com/7386242/189368488-b0c30d48-9a1f-4362-9f78-10a451535682.png)

[![PyTest](https://github.com/crunchdao/backtest/actions/workflows/pytest.yml/badge.svg)](https://github.com/crunchdao/backtest/actions/workflows/pytest.yml)

- [crunchdao-backtest](#crunchdao-backtest)
- [Install](#install)
- [Usage](#usage)
- [Options](#options)
- [Exporters](#exporters)
- [Console](#console)
- [Dump](#dump)
- [Influx](#influx)
- [QuantStats](#quantstats)
- [PDF](#pdf)
- [Specific Return](#specific-return)
- [Data Sources](#data-sources)
- [Yahoo](#yahoo)
- [CoinMarketCap](#coinmarketcap)
- [FactSet](#factset)
- [File](#file)
- [.parquet](#parquet)

## Install
- [Install](#install)
- [Usage](#usage)
- [Options](#options)
- [Exporters](#exporters)
- [Console](#console)
- [Dump](#dump)
- [Influx](#influx)
- [QuantStats](#quantstats)
- [PDF](#pdf)
- [Specific Return](#specific-return)
- [Data Sources](#data-sources)
- [Yahoo](#yahoo)
- [CoinMarketCap](#coinmarketcap)
- [FactSet](#factset)
- [File](#file)
- [.parquet](#parquet)

# Install

```
python3 -m pip install --upgrade git+https://github.com/crunchdao/backtest
pip install --upgrade bktest
```

## Usage
# Usage

```bash
python3 -m backtest [OPTIONS]
bktest [OPTIONS]
```

### Options
## Options

| Option | Value | Default | Format | Description |
| --- | --- | --- | --- | --- |
Expand All @@ -61,11 +58,11 @@ python3 -m backtest [OPTIONS]
| `--rfr-file` | `<directory>` | | `path` | The directory of rfr file to use. The file must contain a column with date information and a column with the rfr information in %. |
| `--rfr-file-column-date` | `<column>` | `date` | `string` | Change the date column name to use. |

#### Exporters
### Exporters

Multiple exporters can be enabled at one time.

##### Console
#### Console

The console exporter allows a quick look at the backtest.

Expand All @@ -77,7 +74,7 @@ The console exporter allows a quick look at the backtest.
| `--console-hide-skips` | | `false` | | Do not the skipped days. |
| `--console-text-no-color` | | `false` | | Disable colors in the output. (only if the format is `text`) |

##### Dump
#### Dump

The dump exporter generate a dump of the portfolio at each day.

Expand All @@ -87,7 +84,7 @@ The dump exporter generate a dump of the portfolio at each day.
| `--dump-output-file` | `<file>` | `dump.csv` | `path` | Specify the output file. |
| `--dump-auto-delete` | | `false` | | Automatically delete the previous dump file if it is present. |

##### Influx
#### Influx

Export the generated data to an Influx database. <br />
Making it easier to plot the values using software like Grafana.
Expand All @@ -101,7 +98,7 @@ Making it easier to plot the values using software like Grafana.
| `--influx-measurement` | `<measurement>` | `snapshots` |`string` | Specify the table name to use. |
| `--influx-key` | `<key>` | `test` | `string` | Specify the unique key to use. **Previous data with the same key will be deleted!** |

##### QuantStats
#### QuantStats

Generate a tearsheet from the backtest data.

Expand All @@ -113,7 +110,7 @@ Generate a tearsheet from the backtest data.
| `--quantstats-benchmark-ticker` | `<ticker>` | `SPY` | `symbol` | Specify the ticker to use as a benchmark in the tearsheet. |
| `--quantstats-auto-delete` | | `false` | | Automatically delete the previous report files if they are present. |

##### PDF
#### PDF

Generate a tearsheet from a custom template.

Expand All @@ -127,7 +124,7 @@ Generate a tearsheet from a custom template.
| `--pdf-variable` | `[<key> <value>]` | `[]` | `string` `string` | Add a custom variable. |
| `--pdf-user-script` | `[<file>]` | `[]` | `path` | Add a user script. |

##### Specific Return
#### Specific Return

Generate a tearsheet from the specific return backtest data.

Expand All @@ -141,39 +138,39 @@ Generate a tearsheet from the specific return backtest data.
| `--specific-return-output-file-csv` | `<file>` | `sr-report.csv` | `path` | Specify the output file containing raw returns. |
| `--specific-return-auto-delete` | | `false` | | Automatically delete the previous report files if they are present. |

#### Data Sources
### Data Sources

Only one data source can be used at once.

##### Yahoo
#### Yahoo

| Option | Value | Default | Format | Description |
| --- | --- | --- | --- | --- |
| `--yahoo` | | `false` | | Enable yahoo as the data source. |

##### CoinMarketCap
#### CoinMarketCap

| Option | Value | Default | Format | Description |
| --- | --- | --- | --- | --- |
| `--coinmarketcap` | | `false` | | Enable coinmarketcap as the data source. |
| `--coinmarketcap-force-mapping-refresh` | | `false` | | Force a mapping refresh. This is usually only done automatically the first time of using this data source. |
| `--coinmarketcap-page-size` | `<size>` | `10_000` | `number` | Specify the page size while building the mapping. |

##### FactSet
#### FactSet

| Option | Value | Default | Format | Description |
| --- | --- | --- | --- | --- |
| `--factset` | | `false` | | Enable factset as the data source. |
| `--factset-username-serial` | | `$FACTSET_USERNAME_SERIAL` | | Specify the factset's username serial to use. |
| `--factset-api-key` | | `$FACTSET_API_KEY` | | Specify the factset's api key to use. |

##### File
#### File

Use a static file as a price data source.

If another data source is specified, files sources will be used first in a delegated data source. Meaning that if the data are not available in the file, the next data source will be used.

###### .parquet
##### .parquet

| Option | Value | Default | Format | Description |
| --- | --- | --- | --- | --- |
Expand Down
4 changes: 0 additions & 4 deletions backtest/__init__.py

This file was deleted.

11 changes: 11 additions & 0 deletions bktest/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""
bktest
~~~~~~
The backtest package - a Python cli used simply
backtest your portfolio!
"""

from .backtest import Backtester
from .account import *
from .holding import *
from .order import *
File renamed without changes.
6 changes: 6 additions & 0 deletions bktest/__version__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
__title__ = 'bktest'
__description__ = 'bktest - A simple backtester by CrunchDAO'
__version__ = '1.0.0'
__author__ = 'Enzo CACERES'
__author_email__ = '[email protected]'
__url__ = 'https://github.com/crunchdao/backtest'
9 changes: 4 additions & 5 deletions backtest/account.py → bktest/account.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import sys
import typing

from backtest.utils import is_blank

from .fee import ConstantFeeModel, FeeModel
from .holding import Holding
from .order import Order, OrderResult, CloseResult
from .fee import FeeModel, ConstantFeeModel
from .order import CloseResult, Order, OrderResult
from .utils import is_blank


class Account:
Expand Down Expand Up @@ -122,4 +121,4 @@ def _handle_cash(self, order: Order, fee: float):

def interest_on_cash(self, rfr):
'''Interest income on cash from short sale - amount borrowed on the long position'''
self.cash += self.cash * ((((max((rfr - 0.17), 0) / 100) + 1) ** (1/365)) - 1)
self.cash += self.cash * ((((max((rfr - 0.17), 0) / 100) + 1) ** (1/365)) - 1)
File renamed without changes.
0 backtest/cli.py → bktest/cli.py
100755 → 100644
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
18 changes: 9 additions & 9 deletions backtest/export/dump.py → bktest/export/dump.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import abc
import datetime
import math
import os
import sys
import math
import typing
import datetime

from ..utils import signum

import pandas
import readwrite

from ..utils import signum
from .base import BaseExporter
from .model import Snapshot

Expand All @@ -24,7 +24,7 @@ def __init__(
self.output_file = output_file
self.auto_delete = auto_delete
self.auto_override = auto_override

self.all_dates = set()
self.rows = []

Expand Down Expand Up @@ -53,7 +53,7 @@ def on_snapshot(self, snapshot: Snapshot) -> None:

if snapshot.postponned is not None:
date = snapshot.postponned

self.rows.extend([
(
date,
Expand All @@ -78,13 +78,13 @@ def finalize(self) -> None:
self.rows,
columns=["date", "symbol", "quantity", "price", "market_price", "equity"]
)

if not len(self.dataframe):
print(
"[warning] cannot create dump: dataframe is empty",
file=sys.stderr
)

return

def inverse_sign_if_shorting(row: pandas.Series):
Expand Down Expand Up @@ -117,7 +117,7 @@ def compute_profit(group: pandas.DataFrame):

if self.output_file is not None:
if self.auto_override or not os.path.exists(self.output_file):
self.dataframe.to_csv(self.output_file)
readwrite.write(self.dataframe, self.output_file)
else:
print(
f"[warning] {self.output_file} already exists",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 2 additions & 1 deletion backtest/fee.py → bktest/fee.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import abc

from .order import Order
import py_expression_eval

from .order import Order


class FeeModel(metaclass=abc.ABCMeta):

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
15 changes: 8 additions & 7 deletions example/yahoo.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import datetime

import backtest
import pandas

import bktest

end = datetime.date.today()
start = end - datetime.timedelta(days=38)
initial_cash = 1_000_000
quantity_in_decimal = False

data_source = backtest.data.source.YahooDataSource()
data_source = bktest.data.source.YahooDataSource()

order_provider = backtest.order.provider.DataFrameOrderProvider(pandas.DataFrame([
order_provider = bktest.order.provider.DataFrameOrderProvider(pandas.DataFrame([
{"symbol": "AAPL", "quantity": +50, "date": (start + datetime.timedelta(days=10)).isoformat()},
{"symbol": "TSLA", "quantity": -25, "date": (start + datetime.timedelta(days=10)).isoformat()},
{"symbol": "AAPL", "quantity": -40, "date": (start + datetime.timedelta(days=20)).isoformat()},
{"symbol": "TSLA", "quantity": +50, "date": (start + datetime.timedelta(days=30)).isoformat()},
{"symbol": "AAPL", "quantity": +50, "date": (start + datetime.timedelta(days=30)).isoformat()},
]))

fee_model = backtest.fee.ExpressionFeeModel(
fee_model = bktest.fee.ExpressionFeeModel(
"abs(price * quantity) * 0.1"
)

backtest.Backtester(
bktest.Backtester(
start=start,
end=end,
order_provider=order_provider,
Expand All @@ -31,8 +32,8 @@
data_source=data_source,
fee_model=fee_model,
exporters=[
backtest.export.ConsoleExporter(),
backtest.export.DumpExporter(
bktest.export.ConsoleExporter(),
bktest.export.DumpExporter(
auto_override=True
),
# backtest.export.QuantStatsExporter(
Expand Down
Loading
Loading