Skip to content

Commit 01dda23

Browse files
committed
Add metrics for backtests
1 parent 16f607c commit 01dda23

File tree

64 files changed

+6568
-4825
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+6568
-4825
lines changed

.flake8

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
exclude =
33
investing_algorithm_framework/domain/utils/backtesting.py
44
investing_algorithm_framework/infrastructure/database/sql_alchemy.py
5-
examples
5+
examples
6+
investing_algorithm_framework/metrics

examples/resources/backtest_report/report.json

Lines changed: 4375 additions & 3055 deletions
Large diffs are not rendered by default.

examples/test.ipynb

Lines changed: 0 additions & 1406 deletions
This file was deleted.

examples/test.py

Lines changed: 0 additions & 51 deletions
This file was deleted.

investing_algorithm_framework/__init__.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,25 @@
1313
DateRange, get_backtest_report, DEFAULT_LOGGING_CONFIG, \
1414
BacktestReport, TradeStatus, MarketDataType, TradeRiskType, \
1515
APPLICATION_DIRECTORY, pretty_print_orders, pretty_print_trades, \
16-
pretty_print_positions, DataSource, OrderExecutor, PortfolioProvider
16+
pretty_print_positions, DataSource, OrderExecutor, PortfolioProvider, \
17+
SnapshotInterval
1718
from investing_algorithm_framework.infrastructure import \
1819
CCXTOrderBookMarketDataSource, CCXTOHLCVMarketDataSource, \
1920
CCXTTickerMarketDataSource, CSVOHLCVMarketDataSource, \
20-
CSVTickerMarketDataSource, AzureBlobStorageStateHandler
21+
CSVTickerMarketDataSource, AzureBlobStorageStateHandler, \
22+
PandasOHLCVBacktestMarketDataSource, PandasOHLCVMarketDataSource
2123
from .create_app import create_app
2224
from .download_data import download
23-
from .app.metrics import get_profit_factor, \
25+
from .overfitting import create_ohlcv_shuffle_permutation, \
26+
create_ohlcv_shuffle_returns_and_reconstruct_permutation, \
27+
create_ohlcv_shuffle_block_permutation
28+
from .metrics import get_volatility, get_sortino_ratio, get_profit_factor, \
2429
get_cumulative_profit_factor_series, get_rolling_profit_factor_series, \
25-
get_sharpe_ratio, get_price_efficiency_ratio, get_equity_curve
30+
get_sharpe_ratio, get_price_efficiency_ratio, get_equity_curve, \
31+
get_drawdown_series, get_max_drawdown, get_cagr, \
32+
get_standard_deviation_returns, get_standard_deviation_downside_returns, \
33+
get_max_drawdown_absolute, get_exposure_time, get_average_trade_duration, \
34+
get_net_profit
2635

2736
__all__ = [
2837
"Algorithm",
@@ -94,4 +103,21 @@
94103
"get_sharpe_ratio",
95104
"get_price_efficiency_ratio",
96105
"get_equity_curve",
106+
"get_drawdown_series",
107+
"get_max_drawdown",
108+
"create_ohlcv_shuffle_permutation",
109+
"create_ohlcv_shuffle_returns_and_reconstruct_permutation",
110+
"create_ohlcv_shuffle_block_permutation",
111+
"PandasOHLCVBacktestMarketDataSource",
112+
"PandasOHLCVMarketDataSource",
113+
"get_volatility",
114+
"get_sortino_ratio",
115+
"get_cagr",
116+
"get_standard_deviation_returns",
117+
"get_standard_deviation_downside_returns",
118+
"SnapshotInterval",
119+
"get_max_drawdown_absolute",
120+
"get_exposure_time",
121+
"get_average_trade_duration",
122+
"get_net_profit",
97123
]

investing_algorithm_framework/app/__init__.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
from investing_algorithm_framework.app.web import create_flask_app
66
from .algorithm import Algorithm
77
from .context import Context
8-
from .metrics import get_cumulative_profit_factor_series, \
9-
get_rolling_profit_factor_series, get_price_efficiency_ratio
8+
109

1110
__all__ = [
1211
"Algorithm",
@@ -17,7 +16,4 @@
1716
"Task",
1817
"AppHook",
1918
"Context",
20-
"get_cumulative_profit_factor_series",
21-
"get_rolling_profit_factor_series",
22-
"get_price_efficiency_ratio"
2319
]

investing_algorithm_framework/app/app.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
SQLALCHEMY_DATABASE_URI, OperationalException, StateHandler, \
1919
BACKTESTING_START_DATE, BACKTESTING_END_DATE, BacktestReport, \
2020
APP_MODE, MarketCredential, AppMode, BacktestDateRange, \
21-
DATABASE_DIRECTORY_NAME, BACKTESTING_INITIAL_AMOUNT, \
22-
MarketDataSource, PortfolioConfiguration, \
21+
DATABASE_DIRECTORY_NAME, BACKTESTING_INITIAL_AMOUNT, SNAPSHOT_INTERVAL, \
22+
MarketDataSource, PortfolioConfiguration, SnapshotInterval, \
2323
PortfolioProvider, OrderExecutor, ImproperlyConfigured
2424
from investing_algorithm_framework.infrastructure import setup_sqlalchemy, \
2525
create_all_tables, CCXTOrderExecutor, CCXTPortfolioProvider
@@ -703,19 +703,23 @@ def get_market_credentials(self) -> List[MarketCredential]:
703703
def run_backtest(
704704
self,
705705
backtest_date_range: BacktestDateRange,
706+
name: str = None,
706707
initial_amount=None,
707708
output_directory=None,
708709
algorithm=None,
709710
strategy=None,
710711
strategies: List = None,
711712
save_strategy=False,
713+
snapshot_interval: SnapshotInterval = SnapshotInterval.TRADE_CLOSE
712714
) -> BacktestReport:
713715
"""
714716
Run a backtest for an algorithm.
715717
716718
Args:
717719
backtest_date_range: The date range to run the backtest for
718720
(instance of BacktestDateRange)
721+
name: The name of the backtest. This is used to identify the
722+
backtest report in the output directory.
719723
initial_amount: The initial amount to start the backtest with.
720724
This will be the amount of trading currency that the backtest
721725
portfolio will start with.
@@ -729,6 +733,11 @@ def run_backtest(
729733
save_strategy: bool - Whether to save the strategy
730734
as part of the backtest report. You can only save in-memory
731735
strategies when running multiple backtests. This is because
736+
snapshot_interval (SnapshotInterval): The snapshot
737+
interval to use for the backtest. This is used to determine
738+
how often the portfolio snapshot should be taken during the
739+
backtest. The default is TRADE_CLOSE, which means that the
740+
portfolio snapshot will be taken at the end of each trade.
732741
733742
Returns:
734743
Instance of BacktestReport
@@ -741,7 +750,8 @@ def run_backtest(
741750
BACKTESTING_END_DATE: backtest_date_range.end_date,
742751
DATABASE_NAME: "backtest-database.sqlite3",
743752
DATABASE_DIRECTORY_NAME: "backtest_databases",
744-
BACKTESTING_INITIAL_AMOUNT: initial_amount
753+
BACKTESTING_INITIAL_AMOUNT: initial_amount,
754+
SNAPSHOT_INTERVAL: snapshot_interval.value,
745755
})
746756

747757
self.initialize_config()
@@ -759,7 +769,7 @@ def run_backtest(
759769

760770
algorithm_factory = self.container.algorithm_factory()
761771
algorithm = algorithm_factory.create_algorithm(
762-
name=self.name if self.name is not None else "Backtest Algorithm",
772+
name=name if name else self._name,
763773
strategies=(
764774
self._strategies if strategies is None else strategies
765775
),
@@ -775,7 +785,19 @@ def run_backtest(
775785
self.container.strategy_orchestrator_service()
776786
strategy_orchestrator_service.initialize(algorithm)
777787
backtest_service = self.container.backtest_service()
788+
789+
# Setup snapshot service as observer
790+
backtest_service.clear_observers()
791+
portfolio_snapshot_service = \
792+
self.container.portfolio_snapshot_service()
793+
backtest_service.add_observer(portfolio_snapshot_service)
778794
context = self.container.context()
795+
order_service = self.container.order_service()
796+
order_service.clear_observers()
797+
order_service.add_observer(portfolio_snapshot_service)
798+
portfolio_service = self.container.portfolio_service()
799+
portfolio_service.clear_observers()
800+
portfolio_service.add_observer(portfolio_snapshot_service)
779801

780802
# Run the backtest with the backtest_service and collect and
781803
# save the report

investing_algorithm_framework/app/context.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1334,7 +1334,6 @@ def close_trade(self, trade, precision=None) -> None:
13341334
ticker = self.market_data_source_service.get_ticker(
13351335
symbol=trade.symbol, market=portfolio.market
13361336
)
1337-
13381337
logger.info(f"Closing trade {trade.id} {trade.symbol}")
13391338
self.order_service.create(
13401339
{

investing_algorithm_framework/app/metrics/__init__.py

Lines changed: 0 additions & 14 deletions
This file was deleted.

investing_algorithm_framework/app/metrics/sharp_ratio.py

Lines changed: 0 additions & 81 deletions
This file was deleted.

0 commit comments

Comments
 (0)