Skip to content

drawdowns module

Base class for working with drawdown records.

Drawdown records capture information on drawdowns. Since drawdowns are ranges, they subclass Ranges.

Warning

Drawdowns return both recovered AND active drawdowns, which may skew your performance results. To only consider recovered drawdowns, you should explicitly query recovered attribute.

Using Drawdowns.from_ts(), you can generate drawdown records for any time series and analyze them right away.

>>> import vectorbt as vbt
>>> import numpy as np
>>> import pandas as pd

>>> start = '2019-10-01 UTC'  # crypto is in UTC
>>> end = '2020-01-01 UTC'
>>> price = vbt.YFData.download('BTC-USD', start=start, end=end).get('Close')
>>> price = price.rename(None)

>>> drawdowns = vbt.Drawdowns.from_ts(price, wrapper_kwargs=dict(freq='d'))

>>> drawdowns.records_readable
   Drawdown Id  Column            Peak Timestamp           Start Timestamp  \
0            0       0 2019-10-02 00:00:00+00:00 2019-10-03 00:00:00+00:00
1            1       0 2019-10-09 00:00:00+00:00 2019-10-10 00:00:00+00:00
2            2       0 2019-10-27 00:00:00+00:00 2019-10-28 00:00:00+00:00

           Valley Timestamp             End Timestamp   Peak Value  \
0 2019-10-06 00:00:00+00:00 2019-10-09 00:00:00+00:00  8393.041992
1 2019-10-24 00:00:00+00:00 2019-10-25 00:00:00+00:00  8595.740234
2 2019-12-17 00:00:00+00:00 2020-01-01 00:00:00+00:00  9551.714844

   Valley Value    End Value     Status
0   7988.155762  8595.740234  Recovered
1   7493.488770  8660.700195  Recovered
2   6640.515137  7200.174316     Active

>>> drawdowns.duration.max(wrap_kwargs=dict(to_timedelta=True))
Timedelta('66 days 00:00:00')

From accessors

Moreover, all generic accessors have a property drawdowns and a method get_drawdowns:

>>> # vectorbt.generic.accessors.GenericAccessor.drawdowns.coverage
>>> price.vbt.drawdowns.coverage()
0.9354838709677419

Stats

>>> df = pd.DataFrame({
...     'a': [1, 2, 1, 3, 2],
...     'b': [2, 3, 1, 2, 1]
... })

>>> drawdowns = df.vbt(freq='d').drawdowns

>>> drawdowns['a'].stats()
Start                                        0
End                                          4
Period                         5 days 00:00:00
Coverage [%]                              40.0
Total Records                                2
Total Recovered Drawdowns                    1
Total Active Drawdowns                       1
Active Drawdown [%]                  33.333333
Active Duration                1 days 00:00:00
Active Recovery [%]                        0.0
Active Recovery Return [%]                 0.0
Active Recovery Duration       0 days 00:00:00
Max Drawdown [%]                          50.0
Avg Drawdown [%]                          50.0
Max Drawdown Duration          1 days 00:00:00
Avg Drawdown Duration          1 days 00:00:00
Max Recovery Return [%]                  200.0
Avg Recovery Return [%]                  200.0
Max Recovery Duration          1 days 00:00:00
Avg Recovery Duration          1 days 00:00:00
Avg Recovery Duration Ratio                1.0
Name: a, dtype: object

By default, the metrics max_dd, avg_dd, max_dd_duration, and avg_dd_duration do not include active drawdowns. To change that, pass incl_active=True:

>>> drawdowns['a'].stats(settings=dict(incl_active=True))
Start                                        0
End                                          4
Period                         5 days 00:00:00
Coverage [%]                              40.0
Total Records                                2
Total Recovered Drawdowns                    1
Total Active Drawdowns                       1
Active Drawdown [%]                  33.333333
Active Duration                1 days 00:00:00
Active Recovery [%]                        0.0
Active Recovery Return [%]                 0.0
Active Recovery Duration       0 days 00:00:00
Max Drawdown [%]                          50.0
Avg Drawdown [%]                     41.666667
Max Drawdown Duration          1 days 00:00:00
Avg Drawdown Duration          1 days 00:00:00
Max Recovery Return [%]                  200.0
Avg Recovery Return [%]                  200.0
Max Recovery Duration          1 days 00:00:00
Avg Recovery Duration          1 days 00:00:00
Avg Recovery Duration Ratio                1.0
Name: a, dtype: object

StatsBuilderMixin.stats() also supports (re-)grouping:

>>> drawdowns['a'].stats(group_by=True)
UserWarning: Metric 'active_dd' does not support grouped data
UserWarning: Metric 'active_duration' does not support grouped data
UserWarning: Metric 'active_recovery' does not support grouped data
UserWarning: Metric 'active_recovery_return' does not support grouped data
UserWarning: Metric 'active_recovery_duration' does not support grouped data

Start                                        0
End                                          4
Period                         5 days 00:00:00
Coverage [%]                              40.0
Total Records                                2
Total Recovered Drawdowns                    1
Total Active Drawdowns                       1
Max Drawdown [%]                          50.0
Avg Drawdown [%]                          50.0
Max Drawdown Duration          1 days 00:00:00
Avg Drawdown Duration          1 days 00:00:00
Max Recovery Return [%]                  200.0
Avg Recovery Return [%]                  200.0
Max Recovery Duration          1 days 00:00:00
Avg Recovery Duration          1 days 00:00:00
Avg Recovery Duration Ratio                1.0
Name: group, dtype: object

Plots

Drawdowns class has a single subplot based on Drawdowns.plot():

>>> drawdowns['a'].plots()


dd_attach_field_config Config

Config of fields to be attached to Drawdowns.

Config({
    "status": {
        "attach_filters": true
    }
})

dd_field_config Config

Field config for Drawdowns.

Config({
    "dtype": {
        "id": "int64",
        "col": "int64",
        "peak_idx": "int64",
        "start_idx": "int64",
        "valley_idx": "int64",
        "end_idx": "int64",
        "peak_val": "float64",
        "valley_val": "float64",
        "end_val": "float64",
        "status": "int64"
    },
    "settings": {
        "id": {
            "title": "Drawdown Id"
        },
        "peak_idx": {
            "title": "Peak Timestamp",
            "mapping": "index"
        },
        "valley_idx": {
            "title": "Valley Timestamp",
            "mapping": "index"
        },
        "peak_val": {
            "title": "Peak Value"
        },
        "valley_val": {
            "title": "Valley Value"
        },
        "end_val": {
            "title": "End Value"
        },
        "status": {
            "mapping": {
                "Active": 0,
                "Recovered": 1
            }
        }
    }
})

Drawdowns class

Drawdowns(
    wrapper,
    records_arr,
    ts=None,
    **kwargs
)

Extends Ranges for working with drawdown records.

Requires records_arr to have all fields defined in drawdown_dt.

Superclasses

Inherited members


active method

Records filtered by status == 0.


active_drawdown method

Drawdowns.active_drawdown(
    group_by=None,
    wrap_kwargs=None
)

Drawdown of the last active drawdown only.

Does not support grouping.


active_duration method

Drawdowns.active_duration(
    group_by=None,
    wrap_kwargs=None,
    **kwargs
)

Duration of the last active drawdown only.

Does not support grouping.


active_recovery method

Drawdowns.active_recovery(
    group_by=None,
    wrap_kwargs=None
)

Recovery of the last active drawdown only.

Does not support grouping.


active_recovery_duration method

Drawdowns.active_recovery_duration(
    group_by=None,
    wrap_kwargs=None,
    **kwargs
)

Recovery duration of the last active drawdown only.

Does not support grouping.


active_recovery_return method

Drawdowns.active_recovery_return(
    group_by=None,
    wrap_kwargs=None,
    **kwargs
)

Recovery return of the last active drawdown only.

Does not support grouping.


avg_drawdown method

Drawdowns.avg_drawdown(
    group_by=None,
    wrap_kwargs=None,
    **kwargs
)

Average drawdown (ADD).

Based on Drawdowns.drawdown.


avg_recovery_return method

Drawdowns.avg_recovery_return(
    group_by=None,
    wrap_kwargs=None,
    **kwargs
)

Average recovery return.

Based on Drawdowns.recovery_return.


decline_duration method

See dd_decline_duration_nb().

Takes into account both recovered and active drawdowns.


drawdown method

See dd_drawdown_nb().

Takes into account both recovered and active drawdowns.


end_val method

Mapped array of the field end_val.


field_config class variable

Field config of Drawdowns.

Config({
    "dtype": {
        "id": "int64",
        "col": "int64",
        "peak_idx": "int64",
        "start_idx": "int64",
        "valley_idx": "int64",
        "end_idx": "int64",
        "peak_val": "float64",
        "valley_val": "float64",
        "end_val": "float64",
        "status": "int64"
    },
    "settings": {
        "id": {
            "name": "id",
            "title": "Drawdown Id"
        },
        "col": {
            "name": "col",
            "title": "Column",
            "mapping": "columns"
        },
        "idx": {
            "name": "end_idx",
            "title": "Timestamp",
            "mapping": "index"
        },
        "start_idx": {
            "title": "Start Timestamp",
            "mapping": "index"
        },
        "end_idx": {
            "title": "End Timestamp",
            "mapping": "index"
        },
        "status": {
            "title": "Status",
            "mapping": {
                "Active": 0,
                "Recovered": 1
            }
        },
        "peak_idx": {
            "title": "Peak Timestamp",
            "mapping": "index"
        },
        "valley_idx": {
            "title": "Valley Timestamp",
            "mapping": "index"
        },
        "peak_val": {
            "title": "Peak Value"
        },
        "valley_val": {
            "title": "Valley Value"
        },
        "end_val": {
            "title": "End Value"
        }
    }
})

from_ts class method

Drawdowns.from_ts(
    ts,
    attach_ts=True,
    wrapper_kwargs=None,
    **kwargs
)

Build Drawdowns from time series ts.

**kwargs will be passed to Drawdowns.


indexing_func method

Drawdowns.indexing_func(
    pd_indexing_func,
    **kwargs
)

Perform indexing on Drawdowns.


max_drawdown method

Drawdowns.max_drawdown(
    group_by=None,
    wrap_kwargs=None,
    **kwargs
)

Maximum drawdown (MDD).

Based on Drawdowns.drawdown.


max_recovery_return method

Drawdowns.max_recovery_return(
    group_by=None,
    wrap_kwargs=None,
    **kwargs
)

Maximum recovery return.

Based on Drawdowns.recovery_return.


metrics class variable

Metrics supported by Drawdowns.

Config({
    "start": {
        "title": "Start",
        "calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc160>",
        "agg_func": null,
        "tags": "wrapper"
    },
    "end": {
        "title": "End",
        "calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc1f0>",
        "agg_func": null,
        "tags": "wrapper"
    },
    "period": {
        "title": "Period",
        "calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc040>",
        "apply_to_timedelta": true,
        "agg_func": null,
        "tags": "wrapper"
    },
    "coverage": {
        "title": "Coverage [%]",
        "calc_func": "coverage",
        "post_calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc4c0>",
        "tags": [
            "ranges",
            "duration"
        ]
    },
    "total_records": {
        "title": "Total Records",
        "calc_func": "count",
        "tags": "records"
    },
    "total_recovered": {
        "title": "Total Recovered Drawdowns",
        "calc_func": "recovered.count",
        "tags": "drawdowns"
    },
    "total_active": {
        "title": "Total Active Drawdowns",
        "calc_func": "active.count",
        "tags": "drawdowns"
    },
    "active_dd": {
        "title": "Active Drawdown [%]",
        "calc_func": "active_drawdown",
        "post_calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc550>",
        "check_is_not_grouped": true,
        "tags": [
            "drawdowns",
            "active"
        ]
    },
    "active_duration": {
        "title": "Active Duration",
        "calc_func": "active_duration",
        "fill_wrap_kwargs": true,
        "check_is_not_grouped": true,
        "tags": [
            "drawdowns",
            "active",
            "duration"
        ]
    },
    "active_recovery": {
        "title": "Active Recovery [%]",
        "calc_func": "active_recovery",
        "post_calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc5e0>",
        "check_is_not_grouped": true,
        "tags": [
            "drawdowns",
            "active"
        ]
    },
    "active_recovery_return": {
        "title": "Active Recovery Return [%]",
        "calc_func": "active_recovery_return",
        "post_calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc670>",
        "check_is_not_grouped": true,
        "tags": [
            "drawdowns",
            "active"
        ]
    },
    "active_recovery_duration": {
        "title": "Active Recovery Duration",
        "calc_func": "active_recovery_duration",
        "fill_wrap_kwargs": true,
        "check_is_not_grouped": true,
        "tags": [
            "drawdowns",
            "active",
            "duration"
        ]
    },
    "max_dd": {
        "title": "Max Drawdown [%]",
        "calc_func": "RepEval(expression=\"'max_drawdown' if incl_active else 'recovered.max_drawdown'\", mapping={})",
        "post_calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc700>",
        "tags": "RepEval(expression=\"['drawdowns'] if incl_active else ['drawdowns', 'recovered']\", mapping={})"
    },
    "avg_dd": {
        "title": "Avg Drawdown [%]",
        "calc_func": "RepEval(expression=\"'avg_drawdown' if incl_active else 'recovered.avg_drawdown'\", mapping={})",
        "post_calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc790>",
        "tags": "RepEval(expression=\"['drawdowns'] if incl_active else ['drawdowns', 'recovered']\", mapping={})"
    },
    "max_dd_duration": {
        "title": "Max Drawdown Duration",
        "calc_func": "RepEval(expression=\"'max_duration' if incl_active else 'recovered.max_duration'\", mapping={})",
        "fill_wrap_kwargs": true,
        "tags": "RepEval(expression=\"['drawdowns', 'duration'] if incl_active else ['drawdowns', 'recovered', 'duration']\", mapping={})"
    },
    "avg_dd_duration": {
        "title": "Avg Drawdown Duration",
        "calc_func": "RepEval(expression=\"'avg_duration' if incl_active else 'recovered.avg_duration'\", mapping={})",
        "fill_wrap_kwargs": true,
        "tags": "RepEval(expression=\"['drawdowns', 'duration'] if incl_active else ['drawdowns', 'recovered', 'duration']\", mapping={})"
    },
    "max_return": {
        "title": "Max Recovery Return [%]",
        "calc_func": "recovered.recovery_return.max",
        "post_calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc820>",
        "tags": [
            "drawdowns",
            "recovered"
        ]
    },
    "avg_return": {
        "title": "Avg Recovery Return [%]",
        "calc_func": "recovered.recovery_return.mean",
        "post_calc_func": "<function Drawdowns.<lambda> at 0x7f95498fc8b0>",
        "tags": [
            "drawdowns",
            "recovered"
        ]
    },
    "max_recovery_duration": {
        "title": "Max Recovery Duration",
        "calc_func": "recovered.recovery_duration.max",
        "apply_to_timedelta": true,
        "tags": [
            "drawdowns",
            "recovered",
            "duration"
        ]
    },
    "avg_recovery_duration": {
        "title": "Avg Recovery Duration",
        "calc_func": "recovered.recovery_duration.mean",
        "apply_to_timedelta": true,
        "tags": [
            "drawdowns",
            "recovered",
            "duration"
        ]
    },
    "recovery_duration_ratio": {
        "title": "Avg Recovery Duration Ratio",
        "calc_func": "recovered.recovery_duration_ratio.mean",
        "tags": [
            "drawdowns",
            "recovered"
        ]
    }
})

Returns Drawdowns._metrics, which gets (deep) copied upon creation of each instance. Thus, changing this config won't affect the class.

To change metrics, you can either change the config in-place, override this property, or overwrite the instance variable Drawdowns._metrics.


peak_idx method

Mapped array of the field peak_idx.


peak_val method

Mapped array of the field peak_val.


plot method

Drawdowns.plot(
    column=None,
    top_n=5,
    plot_zones=True,
    ts_trace_kwargs=None,
    peak_trace_kwargs=None,
    valley_trace_kwargs=None,
    recovery_trace_kwargs=None,
    active_trace_kwargs=None,
    decline_shape_kwargs=None,
    recovery_shape_kwargs=None,
    active_shape_kwargs=None,
    add_trace_kwargs=None,
    xref='x',
    yref='y',
    fig=None,
    **layout_kwargs
)

Plot drawdowns.

Args

column : str
Name of the column to plot.
top_n : int
Filter top N drawdown records by maximum drawdown.
plot_zones : bool
Whether to plot zones.
ts_trace_kwargs : dict
Keyword arguments passed to plotly.graph_objects.Scatter for Drawdowns.ts.
peak_trace_kwargs : dict
Keyword arguments passed to plotly.graph_objects.Scatter for peak values.
valley_trace_kwargs : dict
Keyword arguments passed to plotly.graph_objects.Scatter for valley values.
recovery_trace_kwargs : dict
Keyword arguments passed to plotly.graph_objects.Scatter for recovery values.
active_trace_kwargs : dict
Keyword arguments passed to plotly.graph_objects.Scatter for active recovery values.
decline_shape_kwargs : dict
Keyword arguments passed to plotly.graph_objects.Figure.add_shape for decline zones.
recovery_shape_kwargs : dict
Keyword arguments passed to plotly.graph_objects.Figure.add_shape for recovery zones.
active_shape_kwargs : dict
Keyword arguments passed to plotly.graph_objects.Figure.add_shape for active recovery zones.
add_trace_kwargs : dict
Keyword arguments passed to add_trace.
xref : str
X coordinate axis.
yref : str
Y coordinate axis.
fig : Figure or FigureWidget
Figure to add traces to.
**layout_kwargs
Keyword arguments for layout.

Usage

>>> import vectorbt as vbt
>>> from datetime import datetime, timedelta
>>> import pandas as pd

>>> price = pd.Series([1, 2, 1, 2, 3, 2, 1, 2], name='Price')
>>> price.index = [datetime(2020, 1, 1) + timedelta(days=i) for i in range(len(price))]
>>> vbt.Drawdowns.from_ts(price, wrapper_kwargs=dict(freq='1 day')).plot()


plots_defaults property

Defaults for PlotsBuilderMixin.plots().

Merges Ranges.plots_defaults and drawdowns.plots from settings.


recovered method

Records filtered by status == 1.


recovery_duration method

See dd_recovery_duration_nb().

A value higher than 1 means the recovery was slower than the decline.

Takes into account both recovered and active drawdowns.


recovery_duration_ratio method

See dd_recovery_duration_ratio_nb().

Takes into account both recovered and active drawdowns.


recovery_return method

See dd_recovery_return_nb().

Takes into account both recovered and active drawdowns.


stats_defaults property

Defaults for StatsBuilderMixin.stats().

Merges Ranges.stats_defaults and drawdowns.stats from settings.


subplots class variable

Subplots supported by Drawdowns.

Config({
    "plot": {
        "title": "Drawdowns",
        "check_is_not_grouped": true,
        "plot_func": "plot",
        "tags": "drawdowns"
    }
})

Returns Drawdowns._subplots, which gets (deep) copied upon creation of each instance. Thus, changing this config won't affect the class.

To change subplots, you can either change the config in-place, override this property, or overwrite the instance variable Drawdowns._subplots.


valley_idx method

Mapped array of the field valley_idx.


valley_val method

Mapped array of the field valley_val.