Skip to content

Commit 9cf06a7

Browse files
QSTrader update version 0.2.4 (#386)
* 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
1 parent 7d1df11 commit 9cf06a7

File tree

9 files changed

+38
-27
lines changed

9 files changed

+38
-27
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 0.2.4
2+
3+
* Fixed bug involving NaN at Timestamp in sixty_forty example.
4+
* Removed support for python 3.7 and 3.8
5+
* Updated the python package requirements to work with matplotlib 3.8, numpy 1.26 and pandas 2.2.0
6+
17
# 0.2.3
28

39
* Updated the python package requirements to work with matplotlib 3.4, numpy 1.21 and pandas 1.3

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,19 @@ Any issues with installation should be reported to the development team as issue
3434
The following command will create a brand new environment called `backtest`.
3535

3636
```
37-
conda create -n backtest
37+
conda create -n backtest python
38+
```
39+
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:
40+
41+
```
42+
conda create -n backtest python==3.9
3843
```
3944

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

4247
```
4348
conda activate backtest
44-
pip install qstrader
49+
pip3 install qstrader
4550
```
4651

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

5762
# Full Documentation

qstrader/data/daily_bar_csv.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def _convert_bar_frame_into_bid_ask_df(self, bar_df):
174174
dp_df = seq_oc_df[['Date', 'Price']]
175175
dp_df['Bid'] = dp_df['Price']
176176
dp_df['Ask'] = dp_df['Price']
177-
dp_df = dp_df.loc[:, ['Date', 'Bid', 'Ask']].fillna(method='ffill').set_index('Date').sort_index()
177+
dp_df = dp_df.loc[:, ['Date', 'Bid', 'Ask']].ffill().set_index('Date').sort_index()
178178
return dp_df
179179

180180
def _convert_bars_into_bid_ask_dfs(self):
@@ -215,8 +215,9 @@ def get_bid(self, dt, asset):
215215
The bid price.
216216
"""
217217
bid_ask_df = self.asset_bid_ask_frames[asset]
218+
bid_series = bid_ask_df.iloc[bid_ask_df.index.get_indexer([dt], method='pad')]['Bid']
218219
try:
219-
bid = bid_ask_df.iloc[bid_ask_df.index.get_loc(dt, method='pad')]['Bid']
220+
bid = bid_series.iloc[0]
220221
except KeyError: # Before start date
221222
return np.NaN
222223
return bid
@@ -239,8 +240,9 @@ def get_ask(self, dt, asset):
239240
The ask price.
240241
"""
241242
bid_ask_df = self.asset_bid_ask_frames[asset]
243+
ask_series = bid_ask_df.iloc[bid_ask_df.index.get_indexer([dt], method='pad')]['Ask']
242244
try:
243-
ask = bid_ask_df.iloc[bid_ask_df.index.get_loc(dt, method='pad')]['Ask']
245+
ask = ask_series.iloc[0]
244246
except KeyError: # Before start date
245247
return np.NaN
246248
return ask

qstrader/statistics/performance.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def aggregate_returns(returns, convert_to):
99
Aggregates returns by day, week, month, or year.
1010
"""
1111
def cumulate_returns(x):
12-
return np.exp(np.log(1 + x).cumsum())[-1] - 1
12+
return np.exp(np.log(1 + x).cumsum()).iloc[-1] - 1
1313

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

4343

4444
def create_sharpe_ratio(returns, periods=252):
@@ -89,7 +89,7 @@ def create_drawdowns(returns):
8989
# Calculate the drawdown and duration statistics
9090
perf = pd.DataFrame(index=idx)
9191
perf["Drawdown"] = (hwm - returns) / hwm
92-
perf["Drawdown"].iloc[0] = 0.0
92+
perf.loc[perf.index[0], 'Drawdown'] = 0.0
9393
perf["DurationCheck"] = np.where(perf["Drawdown"] == 0, 0, 1)
9494
duration = max(
9595
sum(1 for i in g if i == 1)

qstrader/statistics/tearsheet.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def format_perc(x, pos):
195195
# Strategy statistics
196196
returns = stats["returns"]
197197
cum_returns = stats['cum_returns']
198-
tot_ret = cum_returns[-1] - 1.0
198+
tot_ret = cum_returns.iloc[-1] - 1.0
199199
cagr = perf.create_cagr(cum_returns, self.periods)
200200
sharpe = perf.create_sharpe_ratio(returns, self.periods)
201201
sortino = perf.create_sortino_ratio(returns, self.periods)
@@ -205,7 +205,7 @@ def format_perc(x, pos):
205205
if bench_stats is not None:
206206
bench_returns = bench_stats["returns"]
207207
bench_cum_returns = bench_stats['cum_returns']
208-
bench_tot_ret = bench_cum_returns[-1] - 1.0
208+
bench_tot_ret = bench_cum_returns.iloc[-1] - 1.0
209209
bench_cagr = perf.create_cagr(bench_cum_returns, self.periods)
210210
bench_sharpe = perf.create_sharpe_ratio(bench_returns, self.periods)
211211
bench_sortino = perf.create_sortino_ratio(bench_returns, self.periods)

qstrader/system/rebalance/end_of_month.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def _generate_rebalances(self):
6565
rebalance_dates = pd.date_range(
6666
start=self.start_dt,
6767
end=self.end_dt,
68-
freq='BM'
68+
freq='BME'
6969
)
7070

7171
rebalance_times = [

requirements/base.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Click>=7.0
2-
matplotlib==3.4.3
3-
numpy==1.21.2
4-
pandas==1.3.3
5-
seaborn==0.11.2
2+
matplotlib==3.8.2
3+
numpy==1.26.4
4+
pandas==2.2.0
5+
seaborn==0.13.2

setup.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setup(
77
name="qstrader",
8-
version="0.2.3",
8+
version="0.2.4",
99
description="QSTrader backtesting simulation engine",
1010
long_description=long_description,
1111
long_description_content_type="text/markdown",
@@ -17,17 +17,18 @@
1717
"License :: OSI Approved :: MIT License",
1818
"Programming Language :: Python",
1919
"Programming Language :: Python :: 3",
20-
"Programming Language :: Python :: 3.7",
21-
"Programming Language :: Python :: 3.8",
2220
"Programming Language :: Python :: 3.9",
21+
"Programming Language :: Python :: 3.10",
22+
"Programming Language :: Python :: 3.11",
23+
"Programming Language :: Python :: 3.12",
2324
],
2425
packages=find_packages(exclude=("tests",)),
2526
include_package_data=True,
2627
install_requires=[
2728
"Click==7.1.2",
28-
"matplotlib>=3.3.4",
29-
"numpy>=1.18.4",
30-
"pandas>=1.3.3",
31-
"seaborn>=0.10.1"
29+
"matplotlib>=3.8.2",
30+
"numpy>=1.26.4",
31+
"pandas>=2.2.0",
32+
"seaborn>=0.13.2"
3233
]
3334
)

tests/integration/trading/test_backtest_e2e.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,7 @@ def test_backtest_sixty_forty(etf_filepath):
6969
# Pandas 1.1.5 and 1.2.0 very slightly
7070
for symbol in expected_dict.keys():
7171
for metric in expected_dict[symbol].keys():
72-
assert pytest.approx(
73-
portfolio_dict[symbol][metric],
74-
expected_dict[symbol][metric]
75-
)
72+
assert portfolio_dict[symbol][metric] == pytest.approx(expected_dict[symbol][metric])
7673

7774

7875
def test_backtest_long_short_leveraged(etf_filepath):

0 commit comments

Comments
 (0)