Skip to content

Commit

Permalink
clean up and added timestamp compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmeier committed Jan 5, 2021
1 parent aeb6c32 commit e6ce509
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 26 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

[![License: LGPL v3](https://img.shields.io/github/license/jrmeier/fast-trade)](LICENSE)
[![PyPI](https://img.shields.io/pypi/v/fast-trade.svg?style=flat-square)](https://pypi.org/project/fast-trade/)
[![](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/download/releases/3.7.0/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
[![Python 3.7+](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/download/releases/3.7.0/)
[![Python application](https://github.com/jrmeier/fast-trade/workflows/Python%20application/badge.svg)](https://github.com/jrmeier/fast-trade/actions)

A library built with strategy portability and performance in mind for back-test trading strategies.

## Motivations

Strategies are cheap. This is the main motivation behind fast-trade. Since a strategy is just a JSON object, strategies can be created, stored, modified, versioned, and re-run easily. Ideally, a strategy could be generated and tested quickly; fast-trade is just the library to handle that.
Fast Trade is also useful for quickly analyzing chart (`ohlc`) data.

## Indicators

Expand Down Expand Up @@ -41,8 +41,6 @@ date,close,open,high,low,volume

A dataframe can also be passed to `run_backtest(...)` function such as `run_backtest(strategy, df=<your data frame>)`.

I've been using this project to collect and store tick data [crypto-data](https://github.com/jrmeier/crypto-data).

## Install

```bash
Expand Down Expand Up @@ -209,17 +207,20 @@ Strategies include all the instructions needed to run the backtest minus the dat

- optional,
- default: `""`
- description: The time string of when to start the backtest with `%Y-%m-%d %H:%M:%S` date format.
- description: The time string of when to start the backtest with `%Y-%m-%d %H:%M:%S` date format or a timestamp. It will be tested
- Ex.
- `"2018-05-01 00:00:00"` May 1st, 2018 at midnight

- stop: string

- optional
- default: `""`
- description: The time string of when to stop the backtest with `%Y-%m-%d %H:%M:%S` date format.
- description: The time string of when to stop the backtest with `%Y-%m-%d %H:%M:%S` date format or a timestamp
- Ex.
- `"2020-12-28 00:08:00"` December 28th, 2020 at 8am.
- `"2020-06-01"`June 6th, 2020
- `1590969600` (seconds) June 6th, 2020
- `1590969600000` (milliseconds) June 6th, 2020

- base_balance: float

Expand Down Expand Up @@ -253,7 +254,7 @@ Strategies include all the instructions needed to run the backtest minus the dat
```python
{
"name": "example",
"chart_period": "4m",
"chart_period": "4T",
"start": "2018-05-01 00:00:00",
"stop": "2018-05-04 00:00:00",
"enter": [
Expand Down
15 changes: 14 additions & 1 deletion fast_trade/build_data_frame.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
import pandas as pd
from finta import TA
import os
Expand Down Expand Up @@ -78,7 +79,7 @@ def apply_charting_to_df(
Parameters
----------
df: dataframe with data loaded
chart_period: string, describes how often to sample data, default is '1M' (1 minute)
chart_period: string, describes how often to sample data, default is '1Min' (1 minute)
see https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#dateoffset-objects
start_time: datestring in YYYY-MM-DD HH:MM (ex. 2020-08-31 04:00) of when to begin the backtest
stop_time: datestring of YYYY-MM-DD HH:MM when to stop the backtest
Expand All @@ -92,6 +93,18 @@ def apply_charting_to_df(
else:
df.index = pd.to_datetime(df.index)

if start_time:
if isinstance(start_time, datetime) or type(start_time) is int:
time_unit = detect_time_unit(start_time)
start_time = pd.to_datetime(start_time, unit=time_unit)
start_time = start_time.strftime("%Y-%m-%d %H:%M:%S")

if stop_time:
if isinstance(stop_time, datetime) or type(stop_time) is int:
time_unit = detect_time_unit(stop_time)
stop_time = pd.to_datetime(stop_time, unit=time_unit)
stop_time = stop_time.strftime("%Y-%m-%d %H:%M:%S")

df = df.resample(chart_period).first()

if start_time and stop_time:
Expand Down
4 changes: 2 additions & 2 deletions fast_trade/build_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ def build_summary(df, perf_start_time, strategy):
"min_trade_len": min_trade_time_held.total_seconds(),
"total_num_winning_trades": total_num_winning_trades,
"total_num_losing_trades": total_num_losing_trades,
"avg_win_per": round(float(avg_win_per)),
"avg_loss_per": round(float(avg_loss_per)),
"avg_win_per": round(float(avg_win_per), 3),
"avg_loss_per": round(float(avg_loss_per), 3),
"best_trade_perc": round(max_trade_perc, 3),
"min_trade_perc": round(min_trade_perc, 3),
"median_trade_perc": round(median_trade_perc, 3),
Expand Down
8 changes: 3 additions & 5 deletions fast_trade/run_backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,12 @@ def run_backtest(
trade_log, dataframe of all the rows where transactions happened
"""


perf_start_time = datetime.datetime.utcnow()

if ohlcv_path:
df = build_data_frame(strategy, ohlcv_path)
flagged_enter, flagged_exit, strategy = get_flagged_logiz(strategy)
flagged_enter, flagged_exit = get_flagged_logiz(strategy)

# just so there can be some defaults
new_strategy = strategy.copy()
new_strategy["base_balance"] = strategy.get("base_balance", 1000)
new_strategy["exit_on_end"] = strategy.get("exit_on_end", True)
Expand All @@ -47,8 +45,8 @@ def run_backtest(
else:
perf_stop_time = datetime.datetime.utcnow()
summary = {"test_duration": (perf_stop_time - perf_start_time).total_seconds()}
trade_log = None

trade_log = None
return {
"summary": summary,
"df": df,
Expand Down Expand Up @@ -91,7 +89,7 @@ def get_flagged_logiz(strategy: dict):
for e_en in flagged_enter:
flagged_enter.append(strategy["enter"].pop(e_en))

return flagged_enter, flagged_exit, strategy
return flagged_enter, flagged_exit


def apply_strategy_to_dataframe(df: pd.DataFrame, strategy: dict):
Expand Down
28 changes: 17 additions & 11 deletions run_example.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
# flake8: noqa
from fast_trade import run_backtest
from fast_trade.cli_helpers import save
import datetime
import json

ds1 = "2020-06-01"
s1 = 1590969600
ms1 = 1590969600000

ds2 = "2020-07-10"
s2 = 1594339200
ms2 = 1594339200000

strategy = {
"chart_period": "3T",
"enter": [["price_action_1", ">", 3]],
"chart_period": "1T",
"enter": [["price_action_1", ">", 2]],
"exit": [["price_action_2", "<", 1]],
"exit_on_end": False,
"commission": 0.001,
"indicators": [
{"args": [80], "df": "close", "func": "ta.roc", "name": "price_action_1"},
{"args": [60], "df": "close", "func": "ta.roc", "name": "price_action_1"},
{"args": [10], "df": "close", "func": "ta.roc", "name": "price_action_2"},
],
"start": "2020-02-01",
"stop": "2020-02-10",
"start": "",
"stop": s2,
"name": "generated",
}

Expand All @@ -25,8 +32,7 @@
"/Users/jedmeier/Projects/fast-trade-candlesticks/gi_zip_toss/BTCUSDT_2020.csv"
)
tmp_start = datetime.datetime.utcnow()
test = run_backtest(strategy, ohlcv_path=datafile, summary=False)
tmp_stop = datetime.datetime.utcnow()
# print(test["summary"])
print(test["df"])
# print(test)
test = run_backtest(strategy, ohlcv_path=datafile, summary=True)
# print(test["df"])
# print(test["trade_df"])
print(json.dumps(test["summary"], indent=2))
4 changes: 4 additions & 0 deletions test/test_build_data_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,15 @@ def test_apply_charting_to_df_3():
mock_df, mock_chart_period, mock_start_time, mock_stop_time
)

print("past_stop_time: ",past_stop_time)

past_stop_time = datetime.datetime.strptime(
"2018-04-17 04:11:00", "%Y-%m-%d %H:%M:%S"
)
result_df = apply_charting_to_df(
mock_df, mock_chart_period, mock_start_time, mock_stop_time
)

print("result_df.index[0]: ",result_df.index[0])

assert result_df.index[0] < past_stop_time

0 comments on commit e6ce509

Please sign in to comment.