| 307 | np.testing.assert_array_equal(peaks, pd.Series([7, 4], index=[3, 5]).reindex(dd.index)) |
| 308 | |
| 309 | def test_compute_stats(self): |
| 310 | stats = Backtest(GOOG, SmaCross, finalize_trades=True).run() |
| 311 | expected = pd.Series({ |
| 312 | # NOTE: These values are also used on the website! # noqa: E126 |
| 313 | '# Trades': 66, |
| 314 | 'Avg. Drawdown Duration': pd.Timedelta('41 days 00:00:00'), |
| 315 | 'Avg. Drawdown [%]': -5.925851581948801, |
| 316 | 'Avg. Trade Duration': pd.Timedelta('46 days 00:00:00'), |
| 317 | 'Avg. Trade [%]': 2.531715975158555, |
| 318 | 'Best Trade [%]': 53.59595229490424, |
| 319 | 'Buy & Hold Return [%]': 522.0601851851852, |
| 320 | 'Calmar Ratio': 0.4414380935608377, |
| 321 | 'Duration': pd.Timedelta('3116 days 00:00:00'), |
| 322 | 'End': pd.Timestamp('2013-03-01 00:00:00'), |
| 323 | 'Equity Final [$]': 51422.98999999996, |
| 324 | 'Equity Peak [$]': 75787.44, |
| 325 | 'Expectancy [%]': 3.2748078066748834, |
| 326 | 'Exposure Time [%]': 96.74115456238361, |
| 327 | 'Max. Drawdown Duration': pd.Timedelta('584 days 00:00:00'), |
| 328 | 'Max. Drawdown [%]': -47.98012705007589, |
| 329 | 'Max. Trade Duration': pd.Timedelta('183 days 00:00:00'), |
| 330 | 'Profit Factor': 2.167945974262033, |
| 331 | 'Return (Ann.) [%]': 21.180255813792282, |
| 332 | 'Return [%]': 414.2298999999996, |
| 333 | 'Volatility (Ann.) [%]': 36.49390889140787, |
| 334 | 'CAGR [%]': 14.159843619607383, |
| 335 | 'SQN': 1.0766187356697705, |
| 336 | 'Kelly Criterion': 0.1518705127029717, |
| 337 | 'Sharpe Ratio': 0.5803778344714113, |
| 338 | 'Sortino Ratio': 1.0847880675854096, |
| 339 | 'Start': pd.Timestamp('2004-08-19 00:00:00'), |
| 340 | 'Win Rate [%]': 46.96969696969697, |
| 341 | 'Worst Trade [%]': -18.39887353835481, |
| 342 | 'Alpha [%]': 394.37391142027462, |
| 343 | 'Beta': 0.03803390709192, |
| 344 | }) |
| 345 | |
| 346 | def almost_equal(a, b): |
| 347 | try: |
| 348 | return np.isclose(a, b, rtol=1.e-8) |
| 349 | except TypeError: |
| 350 | return a == b |
| 351 | |
| 352 | diff = {key: print(key) or value # noqa: T201 |
| 353 | for key, value in stats.filter(regex='^[^_]').items() |
| 354 | if not almost_equal(value, expected[key])} |
| 355 | self.assertDictEqual(diff, {}) |
| 356 | |
| 357 | self.assertSequenceEqual( |
| 358 | sorted(stats['_equity_curve'].columns), |
| 359 | sorted(['Equity', 'DrawdownPct', 'DrawdownDuration'])) |
| 360 | |
| 361 | self.assertEqual(len(stats['_trades']), 66) |
| 362 | |
| 363 | indicator_columns = [ |
| 364 | f'{entry}_SMA(C,{n})' |
| 365 | for entry in ('Entry', 'Exit') |
| 366 | for n in (SmaCross.fast, SmaCross.slow)] |