Skip to content

Commit

Permalink
QSTrader update version 0.2.4 (#386)
Browse files Browse the repository at this point in the history
* updated performance.py for future warnings and fixed get_loc deprecation

* updated tearsheet.py for future warnings

* Updated package requirements

* updated tests

* Updated version number
  • Loading branch information
juliettejames authored Feb 7, 2024
1 parent 7d1df11 commit 9cf06a7
Show file tree
Hide file tree
Showing 9 changed files with 38 additions and 27 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 0.2.4

* Fixed bug involving NaN at Timestamp in sixty_forty example.
* Removed support for python 3.7 and 3.8
* Updated the python package requirements to work with matplotlib 3.8, numpy 1.26 and pandas 2.2.0

# 0.2.3

* Updated the python package requirements to work with matplotlib 3.4, numpy 1.21 and pandas 1.3
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,19 @@ Any issues with installation should be reported to the development team as issue
The following command will create a brand new environment called `backtest`.

```
conda create -n backtest
conda create -n backtest python
```
This will use the conda default Python version. At time of writing this was Python 3.12. QSTrader currently supports Python 3.9, 3.10, 3.11 and 3.12. Optionally you can specify a python version by substituting python==3.9 into the command as follows:

```
conda create -n backtest python==3.9
```

In order to start using QSTrader, you need to activate this new environment and install QSTrader using pip.

```
conda activate backtest
pip install qstrader
pip3 install qstrader
```

## pip
Expand All @@ -51,7 +56,7 @@ Alternatively, you can use [venv](https://docs.python.org/3/tutorial/venv.html#c
```
python -m venv backtest
source backtest/bin/activate # Need to activate environment before installing package
pip install qstrader
pip3 install qstrader
```

# Full Documentation
Expand Down
8 changes: 5 additions & 3 deletions qstrader/data/daily_bar_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def _convert_bar_frame_into_bid_ask_df(self, bar_df):
dp_df = seq_oc_df[['Date', 'Price']]
dp_df['Bid'] = dp_df['Price']
dp_df['Ask'] = dp_df['Price']
dp_df = dp_df.loc[:, ['Date', 'Bid', 'Ask']].fillna(method='ffill').set_index('Date').sort_index()
dp_df = dp_df.loc[:, ['Date', 'Bid', 'Ask']].ffill().set_index('Date').sort_index()
return dp_df

def _convert_bars_into_bid_ask_dfs(self):
Expand Down Expand Up @@ -215,8 +215,9 @@ def get_bid(self, dt, asset):
The bid price.
"""
bid_ask_df = self.asset_bid_ask_frames[asset]
bid_series = bid_ask_df.iloc[bid_ask_df.index.get_indexer([dt], method='pad')]['Bid']
try:
bid = bid_ask_df.iloc[bid_ask_df.index.get_loc(dt, method='pad')]['Bid']
bid = bid_series.iloc[0]
except KeyError: # Before start date
return np.NaN
return bid
Expand All @@ -239,8 +240,9 @@ def get_ask(self, dt, asset):
The ask price.
"""
bid_ask_df = self.asset_bid_ask_frames[asset]
ask_series = bid_ask_df.iloc[bid_ask_df.index.get_indexer([dt], method='pad')]['Ask']
try:
ask = bid_ask_df.iloc[bid_ask_df.index.get_loc(dt, method='pad')]['Ask']
ask = ask_series.iloc[0]
except KeyError: # Before start date
return np.NaN
return ask
Expand Down
6 changes: 3 additions & 3 deletions qstrader/statistics/performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def aggregate_returns(returns, convert_to):
Aggregates returns by day, week, month, or year.
"""
def cumulate_returns(x):
return np.exp(np.log(1 + x).cumsum())[-1] - 1
return np.exp(np.log(1 + x).cumsum()).iloc[-1] - 1

if convert_to == 'weekly':
return returns.groupby(
Expand Down Expand Up @@ -38,7 +38,7 @@ def create_cagr(equity, periods=252):
periods - Daily (252), Hourly (252*6.5), Minutely(252*6.5*60) etc.
"""
years = len(equity) / float(periods)
return (equity[-1] ** (1.0 / years)) - 1.0
return (equity.iloc[-1] ** (1.0 / years)) - 1.0


def create_sharpe_ratio(returns, periods=252):
Expand Down Expand Up @@ -89,7 +89,7 @@ def create_drawdowns(returns):
# Calculate the drawdown and duration statistics
perf = pd.DataFrame(index=idx)
perf["Drawdown"] = (hwm - returns) / hwm
perf["Drawdown"].iloc[0] = 0.0
perf.loc[perf.index[0], 'Drawdown'] = 0.0
perf["DurationCheck"] = np.where(perf["Drawdown"] == 0, 0, 1)
duration = max(
sum(1 for i in g if i == 1)
Expand Down
4 changes: 2 additions & 2 deletions qstrader/statistics/tearsheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def format_perc(x, pos):
# Strategy statistics
returns = stats["returns"]
cum_returns = stats['cum_returns']
tot_ret = cum_returns[-1] - 1.0
tot_ret = cum_returns.iloc[-1] - 1.0
cagr = perf.create_cagr(cum_returns, self.periods)
sharpe = perf.create_sharpe_ratio(returns, self.periods)
sortino = perf.create_sortino_ratio(returns, self.periods)
Expand All @@ -205,7 +205,7 @@ def format_perc(x, pos):
if bench_stats is not None:
bench_returns = bench_stats["returns"]
bench_cum_returns = bench_stats['cum_returns']
bench_tot_ret = bench_cum_returns[-1] - 1.0
bench_tot_ret = bench_cum_returns.iloc[-1] - 1.0
bench_cagr = perf.create_cagr(bench_cum_returns, self.periods)
bench_sharpe = perf.create_sharpe_ratio(bench_returns, self.periods)
bench_sortino = perf.create_sortino_ratio(bench_returns, self.periods)
Expand Down
2 changes: 1 addition & 1 deletion qstrader/system/rebalance/end_of_month.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def _generate_rebalances(self):
rebalance_dates = pd.date_range(
start=self.start_dt,
end=self.end_dt,
freq='BM'
freq='BME'
)

rebalance_times = [
Expand Down
8 changes: 4 additions & 4 deletions requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Click>=7.0
matplotlib==3.4.3
numpy==1.21.2
pandas==1.3.3
seaborn==0.11.2
matplotlib==3.8.2
numpy==1.26.4
pandas==2.2.0
seaborn==0.13.2
15 changes: 8 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name="qstrader",
version="0.2.3",
version="0.2.4",
description="QSTrader backtesting simulation engine",
long_description=long_description,
long_description_content_type="text/markdown",
Expand All @@ -17,17 +17,18 @@
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
],
packages=find_packages(exclude=("tests",)),
include_package_data=True,
install_requires=[
"Click==7.1.2",
"matplotlib>=3.3.4",
"numpy>=1.18.4",
"pandas>=1.3.3",
"seaborn>=0.10.1"
"matplotlib>=3.8.2",
"numpy>=1.26.4",
"pandas>=2.2.0",
"seaborn>=0.13.2"
]
)
5 changes: 1 addition & 4 deletions tests/integration/trading/test_backtest_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ def test_backtest_sixty_forty(etf_filepath):
# Pandas 1.1.5 and 1.2.0 very slightly
for symbol in expected_dict.keys():
for metric in expected_dict[symbol].keys():
assert pytest.approx(
portfolio_dict[symbol][metric],
expected_dict[symbol][metric]
)
assert portfolio_dict[symbol][metric] == pytest.approx(expected_dict[symbol][metric])


def test_backtest_long_short_leveraged(etf_filepath):
Expand Down

0 comments on commit 9cf06a7

Please sign in to comment.