Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
polakowo
GitHub Repository: polakowo/vectorbt
Path: blob/master/tests/test_returns.py
1071 views
1
from datetime import datetime
2
3
import numpy as np
4
import pandas as pd
5
import pytest
6
7
import vectorbt as vbt
8
from tests.utils import isclose
9
10
qs_available = True
11
try:
12
import quantstats as qs
13
except:
14
qs_available = False
15
16
day_dt = np.timedelta64(86400000000000)
17
18
ts = pd.DataFrame({
19
'a': [1, 2, 3, 4, 5],
20
'b': [5, 4, 3, 2, 1],
21
'c': [1, 2, 3, 2, 1]
22
}, index=pd.DatetimeIndex([
23
datetime(2018, 1, 1),
24
datetime(2018, 1, 2),
25
datetime(2018, 1, 3),
26
datetime(2018, 1, 4),
27
datetime(2018, 1, 5)
28
]))
29
rets = ts.pct_change()
30
31
seed = 42
32
33
np.random.seed(seed)
34
benchmark_rets = pd.DataFrame({
35
'a': rets['a'] * np.random.uniform(0.8, 1.2, rets.shape[0]),
36
'b': rets['b'] * np.random.uniform(0.8, 1.2, rets.shape[0]) * 2,
37
'c': rets['c'] * np.random.uniform(0.8, 1.2, rets.shape[0]) * 3
38
})
39
40
41
# ############# Global ############# #
42
43
def setup_module():
44
vbt.settings.numba['check_func_suffix'] = True
45
vbt.settings.caching.enabled = False
46
vbt.settings.caching.whitelist = []
47
vbt.settings.caching.blacklist = []
48
vbt.settings.returns.defaults = dict(
49
start_value=0.,
50
window=rets.shape[0],
51
minp=1,
52
ddof=1,
53
risk_free=0.01,
54
levy_alpha=2.,
55
required_return=0.1,
56
cutoff=0.05
57
)
58
59
60
def teardown_module():
61
vbt.settings.reset()
62
63
64
# ############# accessors.py ############# #
65
66
67
class TestAccessors:
68
def test_indexing(self):
69
assert rets.vbt.returns['a'].total() == rets['a'].vbt.returns.total()
70
71
def test_benchmark_rets(self):
72
ret_acc = rets.vbt.returns(benchmark_rets=benchmark_rets)
73
pd.testing.assert_frame_equal(ret_acc.benchmark_rets, benchmark_rets)
74
pd.testing.assert_series_equal(ret_acc['a'].benchmark_rets, benchmark_rets['a'])
75
76
def test_freq(self):
77
assert rets.vbt.returns.wrapper.freq == day_dt
78
assert rets['a'].vbt.returns.wrapper.freq == day_dt
79
assert rets.vbt.returns(freq='2D').wrapper.freq == day_dt * 2
80
assert rets['a'].vbt.returns(freq='2D').wrapper.freq == day_dt * 2
81
assert pd.Series([1, 2, 3]).vbt.returns.wrapper.freq is None
82
assert pd.Series([1, 2, 3]).vbt.returns(freq='3D').wrapper.freq == day_dt * 3
83
assert pd.Series([1, 2, 3]).vbt.returns(freq=np.timedelta64(4, 'D')).wrapper.freq == day_dt * 4
84
85
def test_ann_factor(self):
86
assert rets['a'].vbt.returns(year_freq='365 days').ann_factor == 365
87
assert rets.vbt.returns(year_freq='365 days').ann_factor == 365
88
with pytest.raises(Exception):
89
assert pd.Series([1, 2, 3]).vbt.returns(freq=None).ann_factor
90
91
def test_from_value(self):
92
pd.testing.assert_series_equal(pd.Series.vbt.returns.from_value(ts['a']).obj, ts['a'].pct_change())
93
pd.testing.assert_frame_equal(pd.DataFrame.vbt.returns.from_value(ts).obj, ts.pct_change())
94
assert pd.Series.vbt.returns.from_value(ts['a'], year_freq='365 days').year_freq == pd.to_timedelta('365 days')
95
assert pd.DataFrame.vbt.returns.from_value(ts, year_freq='365 days').year_freq == pd.to_timedelta('365 days')
96
97
def test_daily(self):
98
ret_12h = pd.DataFrame({
99
'a': [0.1, 0.1, 0.1, 0.1, 0.1],
100
'b': [-0.1, -0.1, -0.1, -0.1, -0.1],
101
'c': [0.1, -0.1, 0.1, -0.1, 0.1]
102
}, index=pd.DatetimeIndex([
103
datetime(2018, 1, 1, 0),
104
datetime(2018, 1, 1, 12),
105
datetime(2018, 1, 2, 0),
106
datetime(2018, 1, 2, 12),
107
datetime(2018, 1, 3, 0)
108
]))
109
pd.testing.assert_series_equal(
110
ret_12h['a'].vbt.returns.daily(),
111
pd.Series(
112
np.array([0.21, 0.21, 0.1]),
113
index=pd.DatetimeIndex([
114
'2018-01-01',
115
'2018-01-02',
116
'2018-01-03'
117
], dtype='datetime64[ns]', freq='D'),
118
name=ret_12h['a'].name
119
)
120
)
121
pd.testing.assert_frame_equal(
122
ret_12h.vbt.returns.daily(),
123
pd.DataFrame(
124
np.array([
125
[0.21, -0.19, -0.01],
126
[0.21, -0.19, -0.01],
127
[0.1, -0.1, 0.1]
128
]),
129
index=pd.DatetimeIndex([
130
'2018-01-01',
131
'2018-01-02',
132
'2018-01-03'
133
], dtype='datetime64[ns]', freq='D'),
134
columns=ret_12h.columns
135
)
136
)
137
138
def test_annual(self):
139
pd.testing.assert_series_equal(
140
rets['a'].vbt.returns.annual(),
141
pd.Series(
142
np.array([4.]),
143
index=pd.DatetimeIndex(['2018-01-01'], dtype='datetime64[ns]', freq='365D'),
144
name=rets['a'].name
145
)
146
)
147
pd.testing.assert_frame_equal(
148
rets.vbt.returns.annual(),
149
pd.DataFrame(
150
np.array([[4., -0.8, 0.]]),
151
index=pd.DatetimeIndex(['2018-01-01'], dtype='datetime64[ns]', freq='365D'),
152
columns=rets.columns
153
)
154
)
155
156
def test_cumulative(self):
157
pd.testing.assert_series_equal(
158
rets['a'].vbt.returns.cumulative(),
159
pd.Series(
160
[0.0, 1.0, 2.0, 3.0, 4.0],
161
index=rets.index,
162
name='a'
163
)
164
)
165
pd.testing.assert_frame_equal(
166
rets.vbt.returns.cumulative(),
167
pd.DataFrame(
168
[
169
[0.0, 0.0, 0.0],
170
[1.0, -0.19999999999999996, 1.0],
171
[2.0, -0.3999999999999999, 2.0],
172
[3.0, -0.6, 1.0],
173
[4.0, -0.8, 0.0]
174
],
175
index=rets.index,
176
columns=rets.columns
177
)
178
)
179
180
def test_total_return(self):
181
assert isclose(rets['a'].vbt.returns.total(), 4.0)
182
pd.testing.assert_series_equal(
183
rets.vbt.returns.total(),
184
pd.Series(
185
[4.0, -0.8, 0.0],
186
index=rets.columns,
187
name='total_return'
188
)
189
)
190
pd.testing.assert_frame_equal(
191
rets.vbt.returns.rolling_total(),
192
pd.DataFrame(
193
[
194
[np.nan, np.nan, np.nan],
195
[1.0, -0.19999999999999996, 1.0],
196
[2.0, -0.3999999999999999, 2.0],
197
[3.0, -0.6, 1.0],
198
[4.0, -0.8, 0.0]
199
],
200
index=rets.index,
201
columns=rets.columns
202
)
203
)
204
205
def test_annualized_return(self):
206
assert isclose(rets['a'].vbt.returns.annualized(), 1.0587911840678754e+51)
207
pd.testing.assert_series_equal(
208
rets.vbt.returns.annualized(),
209
pd.Series(
210
[1.0587911840678754e+51, -1.0, 0.0],
211
index=rets.columns,
212
name='annualized_return'
213
)
214
)
215
pd.testing.assert_frame_equal(
216
rets.vbt.returns.rolling_annualized(),
217
pd.DataFrame(
218
[
219
[np.nan, np.nan, np.nan],
220
[8.669103912675328e+54, -1.0, 8.669103912675328e+54],
221
[1.1213796164129035e+58, -1.0, 1.1213796164129035e+58],
222
[8.669103912675328e+54, -1.0, 2.9443342053298444e+27],
223
[1.0587911840678754e+51, -1.0, 0.0]
224
],
225
index=rets.index,
226
columns=rets.columns
227
)
228
)
229
230
def test_annualized_volatility(self):
231
assert isclose(rets['a'].vbt.returns.annualized_volatility(), 6.417884083645567)
232
pd.testing.assert_series_equal(
233
rets.vbt.returns.annualized_volatility(),
234
pd.Series(
235
[6.417884083645567, 2.5122615973129334, 13.509256086106296],
236
index=rets.columns,
237
name='annualized_volatility'
238
)
239
)
240
pd.testing.assert_frame_equal(
241
rets.vbt.returns.rolling_annualized_volatility(),
242
pd.DataFrame(
243
[
244
[np.nan, np.nan, np.nan],
245
[np.nan, np.nan, np.nan],
246
[6.754628043053148, 0.6754628043053155, 6.754628043053148],
247
[6.62836217969305, 1.2868638306046682, 12.868638306046675],
248
[6.417884083645567, 2.5122615973129334, 13.509256086106296]
249
],
250
index=rets.index,
251
columns=rets.columns
252
)
253
)
254
255
def test_calmar_ratio(self):
256
assert isclose(rets['a'].vbt.returns.calmar_ratio(), np.nan)
257
pd.testing.assert_series_equal(
258
rets.vbt.returns.calmar_ratio(),
259
pd.Series(
260
[np.nan, -1.25, 0.0],
261
index=rets.columns,
262
name='calmar_ratio'
263
)
264
)
265
pd.testing.assert_frame_equal(
266
rets.vbt.returns.rolling_calmar_ratio(),
267
pd.DataFrame(
268
[
269
[np.nan, np.nan, np.nan],
270
[np.nan, -5.000000000000001, np.nan],
271
[np.nan, -2.5000000000000004, np.nan],
272
[np.nan, -1.6666666666666667, 8.833002615989533e+27],
273
[np.nan, -1.25, 0.0]
274
],
275
index=rets.index,
276
columns=rets.columns
277
)
278
)
279
280
def test_omega_ratio(self):
281
assert isclose(rets['a'].vbt.returns.omega_ratio(risk_free=0.01, required_return=0.1), np.inf)
282
pd.testing.assert_series_equal(
283
rets.vbt.returns.omega_ratio(risk_free=0.01, required_return=0.1),
284
pd.Series(
285
[np.inf, 0.0, 1.7327023435781848],
286
index=rets.columns,
287
name='omega_ratio'
288
)
289
)
290
pd.testing.assert_frame_equal(
291
rets.vbt.returns.rolling_omega_ratio(),
292
pd.DataFrame(
293
[
294
[np.nan, np.nan, np.nan],
295
[np.inf, 0.0, np.inf],
296
[np.inf, 0.0, np.inf],
297
[np.inf, 0.0, 4.305883016460259],
298
[np.inf, 0.0, 1.7327023435781848]
299
],
300
index=rets.index,
301
columns=rets.columns
302
)
303
)
304
305
def test_sharpe_ratio(self):
306
assert isclose(rets['a'].vbt.returns.sharpe_ratio(risk_free=0.01), 29.052280196490333)
307
pd.testing.assert_series_equal(
308
rets.vbt.returns.sharpe_ratio(risk_free=0.01),
309
pd.Series(
310
[29.052280196490333, -48.06592068111974, 4.232900240313306],
311
index=rets.columns,
312
name='sharpe_ratio'
313
)
314
)
315
pd.testing.assert_frame_equal(
316
rets.vbt.returns.rolling_sharpe_ratio(),
317
pd.DataFrame(
318
[
319
[np.nan, np.nan, np.nan],
320
[np.nan, np.nan, np.nan],
321
[39.98739801487463, -126.98700720939904, 39.98739801487463],
322
[33.101020977359426, -76.89667951041766, 10.746626111906732],
323
[29.052280196490333, -48.06592068111974, 4.232900240313306]
324
],
325
index=rets.index,
326
columns=rets.columns
327
)
328
)
329
330
def test_deflated_sharpe_ratio(self):
331
pd.testing.assert_series_equal(
332
rets.vbt.returns.deflated_sharpe_ratio(risk_free=0.01),
333
pd.Series([np.nan, np.nan, 0.0005355605507117676], index=rets.columns, name='deflated_sharpe_ratio')
334
)
335
pd.testing.assert_series_equal(
336
rets.vbt.returns.deflated_sharpe_ratio(risk_free=0.03),
337
pd.Series([np.nan, np.nan, 0.0003423112350834066], index=rets.columns, name='deflated_sharpe_ratio')
338
)
339
340
def test_downside_risk(self):
341
assert isclose(rets['a'].vbt.returns.downside_risk(required_return=0.1), 0.0)
342
pd.testing.assert_series_equal(
343
rets.vbt.returns.downside_risk(required_return=0.1),
344
pd.Series(
345
[0.0, 8.329186468210578, 7.069987427302981],
346
index=rets.columns,
347
name='downside_risk'
348
)
349
)
350
pd.testing.assert_frame_equal(
351
rets.vbt.returns.rolling_downside_risk(),
352
pd.DataFrame(
353
[
354
[np.nan, np.nan, np.nan],
355
[0.0, 5.7314919523628385, 0.0],
356
[0.0, 6.227459353540574, 0.0],
357
[0.0, 6.978571699349585, 4.779779942245908],
358
[0.0, 8.329186468210578, 7.069987427302981]
359
],
360
index=rets.index,
361
columns=rets.columns
362
)
363
)
364
365
def test_sortino_ratio(self):
366
assert isclose(rets['a'].vbt.returns.sortino_ratio(required_return=0.1), np.inf)
367
pd.testing.assert_series_equal(
368
rets.vbt.returns.sortino_ratio(required_return=0.1),
369
pd.Series(
370
[np.inf, -18.441677017667562, 3.4417788692752858],
371
index=rets.columns,
372
name='sortino_ratio'
373
)
374
)
375
pd.testing.assert_frame_equal(
376
rets.vbt.returns.rolling_sortino_ratio(),
377
pd.DataFrame(
378
[
379
[np.nan, np.nan, np.nan],
380
[np.inf, -19.1049731745428, np.inf],
381
[np.inf, -19.04869919906529, np.inf],
382
[np.inf, -18.887182253617894, 22.06052281036573],
383
[np.inf, -18.441677017667562, 3.4417788692752858]
384
],
385
index=rets.index,
386
columns=rets.columns
387
)
388
)
389
390
def test_information_ratio(self):
391
assert isclose(rets['a'].vbt.returns.information_ratio(benchmark_rets['a']), -0.5575108215121097)
392
pd.testing.assert_series_equal(
393
rets.vbt.returns.information_ratio(benchmark_rets),
394
pd.Series(
395
[-0.5575108215121097, 1.8751745305884349, -0.3791876496995291],
396
index=rets.columns,
397
name='information_ratio'
398
)
399
)
400
pd.testing.assert_frame_equal(
401
rets.vbt.returns.rolling_information_ratio(benchmark_rets),
402
pd.DataFrame(
403
[
404
[np.nan, np.nan, np.nan],
405
[np.nan, np.nan, np.nan],
406
[-1.1972053570548309, 1.6499071488151926, -1.9503403469059444],
407
[-0.9036343476254122, 2.183905200180643, -0.6855076064440647],
408
[-0.5575108215121097, 1.8751745305884349, -0.3791876496995291]
409
],
410
index=rets.index,
411
columns=rets.columns
412
)
413
)
414
415
def test_beta(self):
416
assert isclose(rets['a'].vbt.returns.beta(benchmark_rets['a']), 0.7853755858374825)
417
pd.testing.assert_series_equal(
418
rets.vbt.returns.beta(benchmark_rets),
419
pd.Series(
420
[0.7853755858374825, 0.4123019930790345, 0.30840682076341036],
421
index=rets.columns,
422
name='beta'
423
)
424
)
425
pd.testing.assert_frame_equal(
426
rets.vbt.returns.rolling_beta(benchmark_rets),
427
pd.DataFrame(
428
[
429
[np.nan, np.nan, np.nan],
430
[np.nan, np.nan, np.nan],
431
[0.7887842027059571, 0.2049668794115673, 0.2681790192492397],
432
[0.7969484728140032, 0.34249231546013587, 0.30111751528469777],
433
[0.7853755858374825, 0.4123019930790345, 0.30840682076341036]
434
],
435
index=rets.index,
436
columns=rets.columns
437
)
438
)
439
440
def test_alpha(self):
441
assert isclose(rets['a'].vbt.returns.alpha(benchmark_rets['a'], risk_free=0.01), 41819510790.213036)
442
pd.testing.assert_series_equal(
443
rets.vbt.returns.alpha(benchmark_rets, risk_free=0.01),
444
pd.Series(
445
[41819510790.213036, -0.9999999939676926, -0.999999999999793],
446
index=rets.columns,
447
name='alpha'
448
)
449
)
450
pd.testing.assert_frame_equal(
451
rets.vbt.returns.rolling_alpha(benchmark_rets),
452
pd.DataFrame(
453
[
454
[np.nan, np.nan, np.nan],
455
[np.nan, np.nan, np.nan],
456
[18396133022.071487, -1.0, 558643.320341666],
457
[974350522.6315696, -0.9999999999999931, -0.9999999996015246],
458
[41819510790.213036, -0.9999999939676926, -0.999999999999793]
459
],
460
index=rets.index,
461
columns=rets.columns
462
)
463
)
464
465
def test_tail_ratio(self):
466
assert isclose(rets['a'].vbt.returns.tail_ratio(), 3.5238095238095237)
467
pd.testing.assert_series_equal(
468
rets.vbt.returns.tail_ratio(),
469
pd.Series(
470
[3.5238095238095237, 0.43684210526315786, 1.947368421052631],
471
index=rets.columns,
472
name='tail_ratio'
473
)
474
)
475
pd.testing.assert_frame_equal(
476
rets.vbt.returns.rolling_tail_ratio(),
477
pd.DataFrame(
478
[
479
[np.nan, np.nan, np.nan],
480
[1.0, 1.0, 1.0],
481
[1.857142857142857, 0.818181818181818, 1.857142857142857],
482
[2.714285714285715, 0.6307692307692306, 3.8000000000000007],
483
[3.5238095238095237, 0.43684210526315786, 1.947368421052631]
484
],
485
index=rets.index,
486
columns=rets.columns
487
)
488
)
489
490
def test_value_at_risk(self):
491
assert isclose(rets['a'].vbt.returns.value_at_risk(cutoff=0.05), 0.26249999999999996)
492
pd.testing.assert_series_equal(
493
rets.vbt.returns.value_at_risk(cutoff=0.05),
494
pd.Series(
495
[0.26249999999999996, -0.47500000000000003, -0.47500000000000003],
496
index=rets.columns,
497
name='value_at_risk'
498
)
499
)
500
pd.testing.assert_frame_equal(
501
rets.vbt.returns.rolling_value_at_risk(),
502
pd.DataFrame(
503
[
504
[np.nan, np.nan, np.nan],
505
[1.0, -0.19999999999999996, 1.0],
506
[0.525, -0.2475, 0.525],
507
[0.3499999999999999, -0.325, -0.24999999999999994],
508
[0.26249999999999996, -0.47500000000000003, -0.47500000000000003]
509
],
510
index=rets.index,
511
columns=rets.columns
512
)
513
)
514
515
def test_cond_value_at_risk(self):
516
assert isclose(rets['a'].vbt.returns.cond_value_at_risk(cutoff=0.05), 0.25)
517
pd.testing.assert_series_equal(
518
rets.vbt.returns.cond_value_at_risk(cutoff=0.05),
519
pd.Series(
520
[0.25, -0.5, -0.5],
521
index=rets.columns,
522
name='cond_value_at_risk'
523
)
524
)
525
pd.testing.assert_frame_equal(
526
rets.vbt.returns.rolling_cond_value_at_risk(),
527
pd.DataFrame(
528
[
529
[np.nan, np.nan, np.nan],
530
[1.0, -0.19999999999999996, 1.0],
531
[0.5, -0.25, 0.5],
532
[0.33333333333333326, -0.33333333333333337, -0.33333333333333337],
533
[0.25, -0.5, -0.5]
534
],
535
index=rets.index,
536
columns=rets.columns
537
)
538
)
539
540
def test_capture(self):
541
assert isclose(rets['a'].vbt.returns.capture(benchmark_rets['a']), 0.0007435597416888084)
542
pd.testing.assert_series_equal(
543
rets.vbt.returns.capture(benchmark_rets),
544
pd.Series(
545
[0.0007435597416888084, 1.0, -0.0],
546
index=rets.columns,
547
name='capture'
548
)
549
)
550
pd.testing.assert_frame_equal(
551
rets.vbt.returns.rolling_capture(benchmark_rets),
552
pd.DataFrame(
553
[
554
[np.nan, np.nan, np.nan],
555
[1.443034545422564e-07, 1.0, 4.0670014163453153e-66],
556
[6.758314743559073e-07, 1.0, 2.2829041301869233e-75],
557
[9.623155594782632e-06, 1.0, 43620380068493.234],
558
[0.0007435597416888084, 1.0, -0.0]
559
],
560
index=rets.index,
561
columns=rets.columns
562
)
563
)
564
565
def test_up_capture(self):
566
assert isclose(rets['a'].vbt.returns.up_capture(benchmark_rets['a']), 0.0001227848643711666)
567
pd.testing.assert_series_equal(
568
rets.vbt.returns.up_capture(benchmark_rets),
569
pd.Series(
570
[0.0001227848643711666, np.nan, 1.0907657953912082e-112],
571
index=rets.columns,
572
name='up_capture'
573
)
574
)
575
pd.testing.assert_frame_equal(
576
rets.vbt.returns.rolling_up_capture(benchmark_rets),
577
pd.DataFrame(
578
[
579
[np.nan, np.nan, np.nan],
580
[2.0823486992829062e-14, np.nan, 1.6540500520554797e-131],
581
[5.555940938023189e-10, np.nan, 1.0907657953912082e-112],
582
[2.0468688202710215e-07, np.nan, 1.0907657953912082e-112],
583
[0.0001227848643711666, np.nan, 1.0907657953912082e-112]
584
],
585
index=rets.index,
586
columns=rets.columns
587
)
588
)
589
590
def test_down_capture(self):
591
assert isclose(rets['a'].vbt.returns.down_capture(benchmark_rets['a']), np.nan)
592
pd.testing.assert_series_equal(
593
rets.vbt.returns.down_capture(benchmark_rets),
594
pd.Series(
595
[np.nan, np.nan, np.nan],
596
index=rets.columns,
597
name='down_capture'
598
)
599
)
600
pd.testing.assert_frame_equal(
601
rets.vbt.returns.rolling_down_capture(benchmark_rets),
602
pd.DataFrame(
603
[
604
[np.nan, np.nan, np.nan],
605
[np.nan, 1.0, np.nan],
606
[np.nan, 1.0, np.nan],
607
[np.nan, 1.0, 1.0],
608
[np.nan, np.nan, np.nan]
609
],
610
index=rets.index,
611
columns=rets.columns
612
)
613
)
614
615
def test_drawdown(self):
616
pd.testing.assert_series_equal(
617
rets['a'].vbt.returns.drawdown(),
618
pd.Series(
619
np.array([0., 0., 0., 0., 0.]),
620
index=rets['a'].index,
621
name=rets['a'].name
622
)
623
)
624
pd.testing.assert_frame_equal(
625
rets.vbt.returns.drawdown(),
626
pd.DataFrame(
627
np.array([
628
[0., 0., 0.],
629
[0., -0.2, 0.],
630
[0., -0.4, 0.],
631
[0., -0.6, -0.33333333],
632
[0., -0.8, -0.66666667]
633
]),
634
index=pd.DatetimeIndex([
635
'2018-01-01',
636
'2018-01-02',
637
'2018-01-03',
638
'2018-01-04',
639
'2018-01-05'
640
], dtype='datetime64[ns]', freq=None),
641
columns=rets.columns
642
)
643
)
644
645
def test_max_drawdown(self):
646
assert isclose(
647
rets['a'].vbt.returns.max_drawdown(),
648
rets['a'].vbt.returns.drawdowns.max_drawdown(fill_value=0.)
649
)
650
pd.testing.assert_series_equal(
651
rets.vbt.returns.max_drawdown(),
652
rets.vbt.returns.drawdowns.max_drawdown(fill_value=0.)
653
)
654
pd.testing.assert_frame_equal(
655
rets.vbt.returns.rolling_max_drawdown(),
656
pd.DataFrame(
657
[
658
[np.nan, np.nan, np.nan],
659
[0.0, -0.19999999999999996, 0.0],
660
[0.0, -0.3999999999999999, 0.0],
661
[0.0, -0.6, -0.33333333333333337],
662
[0.0, -0.8, -0.6666666666666667]
663
],
664
index=rets.index,
665
columns=rets.columns
666
)
667
)
668
669
def test_drawdowns(self):
670
assert type(rets['a'].vbt.returns.drawdowns) is vbt.Drawdowns
671
assert rets['a'].vbt.returns.drawdowns.wrapper.freq == rets['a'].vbt.wrapper.freq
672
assert rets['a'].vbt.returns.drawdowns.wrapper.ndim == rets['a'].ndim
673
assert rets.vbt.returns.drawdowns.wrapper.ndim == rets.ndim
674
assert isclose(rets['a'].vbt.returns.drawdowns.max_drawdown(), rets['a'].vbt.returns.max_drawdown())
675
pd.testing.assert_series_equal(
676
rets.vbt.returns.drawdowns.max_drawdown(fill_value=0.),
677
rets.vbt.returns.max_drawdown()
678
)
679
680
def test_stats(self):
681
stats_index = pd.Index([
682
'Start',
683
'End',
684
'Period',
685
'Total Return [%]',
686
'Annualized Return [%]',
687
'Annualized Volatility [%]',
688
'Max Drawdown [%]',
689
'Max Drawdown Duration',
690
'Sharpe Ratio',
691
'Calmar Ratio',
692
'Omega Ratio',
693
'Sortino Ratio',
694
'Skew',
695
'Kurtosis',
696
'Tail Ratio',
697
'Common Sense Ratio',
698
'Value at Risk'
699
], dtype='object')
700
pd.testing.assert_series_equal(
701
rets.vbt.returns.stats(),
702
pd.Series([
703
pd.Timestamp('2018-01-01 00:00:00'), pd.Timestamp('2018-01-05 00:00:00'),
704
pd.Timedelta('5 days 00:00:00'), 106.66666666666667, 3.529303946892918e+52,
705
747.9800589021598, 73.33333333333333, pd.Timedelta('3 days 00:00:00'),
706
-4.926913414772034, -0.625, np.inf, np.inf, 0.25687104876585726, -0.25409565813913854,
707
1.9693400167084374, 1.2436594860479807e+51, -0.2291666666666667
708
],
709
index=stats_index,
710
name='agg_func_mean'
711
)
712
)
713
pd.testing.assert_series_equal(
714
rets.vbt.returns.stats(column='a'),
715
pd.Series([
716
pd.Timestamp('2018-01-01 00:00:00'), pd.Timestamp('2018-01-05 00:00:00'),
717
pd.Timedelta('5 days 00:00:00'), 400.0, 1.0587911840678753e+53, 641.7884083645566,
718
np.nan, pd.NaT, 29.052280196490333, np.nan, np.inf, np.inf, 1.4693345482106241,
719
2.030769230769236, 3.5238095238095237, 3.730978458143942e+51, 0.26249999999999996
720
],
721
index=stats_index,
722
name='a'
723
)
724
)
725
pd.testing.assert_series_equal(
726
rets.vbt.returns.stats(column='a', settings=dict(freq='10 days', year_freq='200 days')),
727
pd.Series([
728
pd.Timestamp('2018-01-01 00:00:00'), pd.Timestamp('2018-01-05 00:00:00'),
729
pd.Timedelta('50 days 00:00:00'), 400.0, 62400.0, 150.23130314433288, np.nan, pd.NaT,
730
6.800624405721308, np.nan, np.inf, np.inf, 1.4693345482106241, 2.030769230769236,
731
3.5238095238095237, 2202.3809523809523, 0.26249999999999996
732
],
733
index=stats_index,
734
name='a'
735
)
736
)
737
pd.testing.assert_series_equal(
738
rets.vbt.returns.stats(column='a', settings=dict(benchmark_rets=benchmark_rets)),
739
pd.Series([
740
pd.Timestamp('2018-01-01 00:00:00'), pd.Timestamp('2018-01-05 00:00:00'),
741
pd.Timedelta('5 days 00:00:00'), 400.0, 451.8597134178033, 1.0587911840678753e+53,
742
641.7884083645566, np.nan, pd.NaT, 29.052280196490333, np.nan, np.inf, np.inf,
743
1.4693345482106241, 2.030769230769236, 3.5238095238095237, 3.730978458143942e+51,
744
0.26249999999999996, 41819510790.213036, 0.7853755858374825
745
],
746
index=pd.Index([
747
'Start',
748
'End',
749
'Period',
750
'Total Return [%]',
751
'Benchmark Return [%]',
752
'Annualized Return [%]',
753
'Annualized Volatility [%]',
754
'Max Drawdown [%]',
755
'Max Drawdown Duration',
756
'Sharpe Ratio',
757
'Calmar Ratio',
758
'Omega Ratio',
759
'Sortino Ratio',
760
'Skew',
761
'Kurtosis',
762
'Tail Ratio',
763
'Common Sense Ratio',
764
'Value at Risk',
765
'Alpha',
766
'Beta'
767
], dtype='object'),
768
name='a'
769
)
770
)
771
pd.testing.assert_series_equal(
772
rets.vbt.returns.stats(column='a', settings=dict(benchmark_rets=benchmark_rets)),
773
rets.vbt.returns(benchmark_rets=benchmark_rets).stats(column='a'),
774
)
775
pd.testing.assert_series_equal(
776
rets['c'].vbt.returns.stats(),
777
rets.vbt.returns.stats(column='c')
778
)
779
pd.testing.assert_series_equal(
780
rets['c'].vbt.returns.stats(),
781
rets.vbt.returns.stats(column='c', group_by=False)
782
)
783
pd.testing.assert_series_equal(
784
rets.vbt.returns(freq='10d').stats(),
785
rets.vbt.returns.stats(settings=dict(freq='10d'))
786
)
787
pd.testing.assert_series_equal(
788
rets.vbt.returns(freq='d', year_freq='400d').stats(),
789
rets.vbt.returns.stats(settings=dict(freq='d', year_freq='400d'))
790
)
791
stats_df = rets.vbt.returns.stats(agg_func=None)
792
assert stats_df.shape == (3, 17)
793
pd.testing.assert_index_equal(stats_df.index, rets.vbt.returns.wrapper.columns)
794
pd.testing.assert_index_equal(stats_df.columns, stats_index)
795
796
def test_qs(self):
797
if qs_available:
798
pd.testing.assert_series_equal(
799
rets.vbt.returns.qs.sharpe(),
800
qs.stats.sharpe(rets.dropna(), periods=365, rf=0.01)
801
)
802
pd.testing.assert_series_equal(
803
rets.vbt.returns(freq='h', year_freq='252d').qs.sharpe(),
804
qs.stats.sharpe(rets.dropna(), periods=252 * 24, rf=0.01)
805
)
806
pd.testing.assert_series_equal(
807
rets.vbt.returns(freq='h', year_freq='252d').qs.sharpe(periods=252, periods_per_year=252, rf=0),
808
qs.stats.sharpe(rets.dropna())
809
)
810
assert rets['a'].vbt.returns(benchmark_rets=benchmark_rets['a']).qs.r_squared() == 0.6321016849785153
811
812