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 status_recovered attribute.

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

>>> from vectorbtpro import *

>>> price = vbt.YFData.pull(
...     "BTC-USD",
...     start="2019-10 UTC",
...     end="2020-01 UTC"
... ).get('Close')

100%

>>> price = price.rename(None)

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

>>> drawdowns.readable
   Drawdown Id  Column               Start Index              Valley Index  \
0            0       0 2019-10-02 00:00:00+00:00 2019-10-06 00:00:00+00:00
1            1       0 2019-10-09 00:00:00+00:00 2019-10-24 00:00:00+00:00
2            2       0 2019-10-27 00:00:00+00:00 2019-12-17 00:00:00+00:00

                  End Index   Peak Value  Valley Value    End Value     Status
0 2019-10-09 00:00:00+00:00  8393.041992   7988.155762  8595.740234  Recovered
1 2019-10-25 00:00:00+00:00  8595.740234   7493.488770  8660.700195  Recovered
2 2019-12-31 00:00:00+00:00  9551.714844   6640.515137  7193.599121     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:

>>> # vectorbtpro.generic.accessors.GenericAccessor.drawdowns.coverage
>>> price.vbt.drawdowns.coverage
0.967391304347826

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 [%]                              80.0
Total Records                                2
Total Recovered Drawdowns                    1
Total Active Drawdowns                       1
Active Drawdown [%]                  33.333333
Active Duration                2 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          2 days 00:00:00
Avg Drawdown Duration          2 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 [%]                              80.0
Total Records                                2
Total Recovered Drawdowns                    1
Total Active Drawdowns                       1
Active Drawdown [%]                  33.333333
Active Duration                2 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          2 days 00:00:00
Avg Drawdown Duration          2 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 [%]                              80.0
Total Records                                2
Total Recovered Drawdowns                    1
Total Active Drawdowns                       1
Max Drawdown [%]                          50.0
Avg Drawdown [%]                          50.0
Max Drawdown Duration          2 days 00:00:00
Avg Drawdown Duration          2 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().show()


dd_attach_field_config ReadonlyConfig

Config of fields to be attached to Drawdowns.

ReadonlyConfig(
    status=dict(
        attach_filters=True
    )
)

dd_field_config ReadonlyConfig

Field config for Drawdowns.

ReadonlyConfig(
    dtype=np.dtype([
        ('id', 'int64'),
        ('col', 'int64'),
        ('start_idx', 'int64'),
        ('valley_idx', 'int64'),
        ('end_idx', 'int64'),
        ('start_val', 'float64'),
        ('valley_val', 'float64'),
        ('end_val', 'float64'),
        ('status', 'int64')
    ]),
    settings=dict(
        id=dict(
            title='Drawdown Id'
        ),
        valley_idx=dict(
            title='Valley Index',
            mapping='index'
        ),
        start_val=dict(
            title='Start Value'
        ),
        valley_val=dict(
            title='Valley Value'
        ),
        end_val=dict(
            title='End Value'
        ),
        status=dict(
            mapping=DrawdownStatusT(
                Active=0,
                Recovered=1
            )
        )
    )
)

dd_shortcut_config ReadonlyConfig

Config of shortcut properties to be attached to Drawdowns.

ReadonlyConfig(
    ranges=dict(),
    decline_ranges=dict(),
    recovery_ranges=dict(),
    drawdown=dict(
        obj_type='mapped_array'
    ),
    avg_drawdown=dict(
        obj_type='red_array'
    ),
    max_drawdown=dict(
        obj_type='red_array'
    ),
    recovery_return=dict(
        obj_type='mapped_array'
    ),
    avg_recovery_return=dict(
        obj_type='red_array'
    ),
    max_recovery_return=dict(
        obj_type='red_array'
    ),
    decline_duration=dict(
        obj_type='mapped_array'
    ),
    recovery_duration=dict(
        obj_type='mapped_array'
    ),
    recovery_duration_ratio=dict(
        obj_type='mapped_array'
    ),
    active_drawdown=dict(
        obj_type='red_array'
    ),
    active_duration=dict(
        obj_type='red_array'
    ),
    active_recovery=dict(
        obj_type='red_array'
    ),
    active_recovery_return=dict(
        obj_type='red_array'
    ),
    active_recovery_duration=dict(
        obj_type='red_array'
    )
)

Drawdowns class

Drawdowns(
    wrapper,
    records_arr,
    open=None,
    high=None,
    low=None,
    close=None,
    **kwargs
)

Extends Ranges for working with drawdown records.

Requires records_arr to have all fields defined in drawdown_dt.

Superclasses

Inherited members


active_drawdown property

Drawdowns.get_active_drawdown() with default arguments.


active_duration property

Drawdowns.get_active_duration() with default arguments.


active_recovery property

Drawdowns.get_active_recovery() with default arguments.


active_recovery_duration property

Drawdowns.get_active_recovery_duration() with default arguments.


active_recovery_return property

Drawdowns.get_active_recovery_return() with default arguments.


avg_drawdown property

Drawdowns.get_avg_drawdown() with default arguments.


avg_recovery_return property

Drawdowns.get_avg_recovery_return() with default arguments.


decline_duration property

Drawdowns.get_decline_duration() with default arguments.


decline_ranges property

Drawdowns.get_decline_ranges() with default arguments.


drawdown property

Drawdowns.get_drawdown() with default arguments.


end_val property

Mapped array of the field end_val.


field_config class variable

Field config of Drawdowns.

HybridConfig(
    dtype=np.dtype([
        ('id', 'int64'),
        ('col', 'int64'),
        ('start_idx', 'int64'),
        ('valley_idx', 'int64'),
        ('end_idx', 'int64'),
        ('start_val', 'float64'),
        ('valley_val', 'float64'),
        ('end_val', 'float64'),
        ('status', 'int64')
    ]),
    settings=dict(
        id=dict(
            name='id',
            title='Drawdown Id',
            mapping='ids'
        ),
        col=dict(
            name='col',
            title='Column',
            mapping='columns',
            as_customdata=False
        ),
        idx=dict(
            name='end_idx',
            title='Index',
            mapping='index'
        ),
        start_idx=dict(
            title='Start Index',
            mapping='index'
        ),
        end_idx=dict(
            title='End Index',
            mapping='index'
        ),
        status=dict(
            title='Status',
            mapping=DrawdownStatusT(
                Active=0,
                Recovered=1
            )
        ),
        valley_idx=dict(
            title='Valley Index',
            mapping='index'
        ),
        start_val=dict(
            title='Start Value'
        ),
        valley_val=dict(
            title='Valley Value'
        ),
        end_val=dict(
            title='End Value'
        )
    )
)

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

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


from_price class method

Drawdowns.from_price(
    close,
    *,
    open=None,
    high=None,
    low=None,
    sim_start=None,
    sim_end=None,
    attach_data=True,
    jitted=None,
    chunked=None,
    wrapper=None,
    wrapper_kwargs=None,
    **kwargs
)

Build Drawdowns from price.

**kwargs will be passed to Drawdowns.


get_active_drawdown method

Drawdowns.get_active_drawdown(
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None
)

Get drawdown of the last active drawdown only.

Does not support grouping.


get_active_duration method

Drawdowns.get_active_duration(
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

Get duration of the last active drawdown only.

Does not support grouping.


get_active_recovery method

Drawdowns.get_active_recovery(
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None
)

Get recovery of the last active drawdown only.

Does not support grouping.


get_active_recovery_duration method

Drawdowns.get_active_recovery_duration(
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

Get recovery duration of the last active drawdown only.

Does not support grouping.


get_active_recovery_return method

Drawdowns.get_active_recovery_return(
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

Get recovery return of the last active drawdown only.

Does not support grouping.


get_avg_drawdown method

Drawdowns.get_avg_drawdown(
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

Get average drawdown (ADD).

Based on Drawdowns.drawdown.


get_avg_recovery_return method

Drawdowns.get_avg_recovery_return(
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

Get average recovery return.

Based on Drawdowns.recovery_return.


get_decline_duration method

Drawdowns.get_decline_duration(
    jitted=None,
    chunked=None,
    **kwargs
)

See dd_decline_duration_nb().

Takes into account both recovered and active drawdowns.


get_decline_ranges method

Drawdowns.get_decline_ranges(
    **kwargs
)

Get records of type Ranges for peak-to-valley ranges.


get_drawdown method

Drawdowns.get_drawdown(
    jitted=None,
    chunked=None,
    **kwargs
)

See dd_drawdown_nb().

Takes into account both recovered and active drawdowns.


get_max_drawdown method

Drawdowns.get_max_drawdown(
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

Get maximum drawdown (MDD).

Based on Drawdowns.drawdown.


get_max_recovery_return method

Drawdowns.get_max_recovery_return(
    group_by=None,
    jitted=None,
    chunked=None,
    wrap_kwargs=None,
    **kwargs
)

Get maximum recovery return.

Based on Drawdowns.recovery_return.


get_ranges method

Drawdowns.get_ranges(
    **kwargs
)

Get records of type Ranges for peak-to-end ranges.


get_recovery_duration method

Drawdowns.get_recovery_duration(
    jitted=None,
    chunked=None,
    **kwargs
)

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.


get_recovery_duration_ratio method

Drawdowns.get_recovery_duration_ratio(
    jitted=None,
    chunked=None,
    **kwargs
)

See dd_recovery_duration_ratio_nb().

Takes into account both recovered and active drawdowns.


get_recovery_ranges method

Drawdowns.get_recovery_ranges(
    **kwargs
)

Get records of type Ranges for valley-to-end ranges.


get_recovery_return method

Drawdowns.get_recovery_return(
    jitted=None,
    chunked=None,
    **kwargs
)

See dd_recovery_return_nb().

Takes into account both recovered and active drawdowns.


max_drawdown property

Drawdowns.get_max_drawdown() with default arguments.


max_recovery_return property

Drawdowns.get_max_recovery_return() with default arguments.


metrics class variable

Metrics supported by Drawdowns.

HybridConfig(
    start_index=dict(
        title='Start Index',
        calc_func=<function Drawdowns.<lambda> at 0x1325a96c0>,
        agg_func=None,
        tags='wrapper'
    ),
    end_index=dict(
        title='End Index',
        calc_func=<function Drawdowns.<lambda> at 0x1325a9760>,
        agg_func=None,
        tags='wrapper'
    ),
    total_duration=dict(
        title='Total Duration',
        calc_func=<function Drawdowns.<lambda> at 0x1325a9800>,
        apply_to_timedelta=True,
        agg_func=None,
        tags='wrapper'
    ),
    coverage=dict(
        title='Coverage [%]',
        calc_func='coverage',
        post_calc_func=<function Drawdowns.<lambda> at 0x1325a98a0>,
        tags=[
            'ranges',
            'duration'
        ]
    ),
    total_records=dict(
        title='Total Records',
        calc_func='count',
        tags='records'
    ),
    total_recovered=dict(
        title='Total Recovered Drawdowns',
        calc_func='status_recovered.count',
        tags='drawdowns'
    ),
    total_active=dict(
        title='Total Active Drawdowns',
        calc_func='status_active.count',
        tags='drawdowns'
    ),
    active_dd=dict(
        title='Active Drawdown [%]',
        calc_func='active_drawdown',
        post_calc_func=<function Drawdowns.<lambda> at 0x1325a9940>,
        check_is_not_grouped=True,
        tags=[
            'drawdowns',
            'active'
        ]
    ),
    active_duration=dict(
        title='Active Duration',
        calc_func='active_duration',
        fill_wrap_kwargs=True,
        check_is_not_grouped=True,
        tags=[
            'drawdowns',
            'active',
            'duration'
        ]
    ),
    active_recovery=dict(
        title='Active Recovery [%]',
        calc_func='active_recovery',
        post_calc_func=<function Drawdowns.<lambda> at 0x1325a99e0>,
        check_is_not_grouped=True,
        tags=[
            'drawdowns',
            'active'
        ]
    ),
    active_recovery_return=dict(
        title='Active Recovery Return [%]',
        calc_func='active_recovery_return',
        post_calc_func=<function Drawdowns.<lambda> at 0x1325a9a80>,
        check_is_not_grouped=True,
        tags=[
            'drawdowns',
            'active'
        ]
    ),
    active_recovery_duration=dict(
        title='Active Recovery Duration',
        calc_func='active_recovery_duration',
        fill_wrap_kwargs=True,
        check_is_not_grouped=True,
        tags=[
            'drawdowns',
            'active',
            'duration'
        ]
    ),
    max_dd=dict(
        title='Max Drawdown [%]',
        calc_func=RepEval(
            template="'max_drawdown' if incl_active else 'status_recovered.get_max_drawdown'",
            context=None,
            strict=None,
            context_merge_kwargs=None,
            eval_id=None
        ),
        post_calc_func=<function Drawdowns.<lambda> at 0x1325a9b20>,
        tags=RepEval(
            template="['drawdowns'] if incl_active else ['drawdowns', 'recovered']",
            context=None,
            strict=None,
            context_merge_kwargs=None,
            eval_id=None
        )
    ),
    avg_dd=dict(
        title='Avg Drawdown [%]',
        calc_func=RepEval(
            template="'avg_drawdown' if incl_active else 'status_recovered.get_avg_drawdown'",
            context=None,
            strict=None,
            context_merge_kwargs=None,
            eval_id=None
        ),
        post_calc_func=<function Drawdowns.<lambda> at 0x1325a9bc0>,
        tags=RepEval(
            template="['drawdowns'] if incl_active else ['drawdowns', 'recovered']",
            context=None,
            strict=None,
            context_merge_kwargs=None,
            eval_id=None
        )
    ),
    max_dd_duration=dict(
        title='Max Drawdown Duration',
        calc_func=RepEval(
            template="'max_duration' if incl_active else 'status_recovered.get_max_duration'",
            context=None,
            strict=None,
            context_merge_kwargs=None,
            eval_id=None
        ),
        fill_wrap_kwargs=True,
        tags=RepEval(
            template="['drawdowns', 'duration'] if incl_active else ['drawdowns', 'recovered', 'duration']",
            context=None,
            strict=None,
            context_merge_kwargs=None,
            eval_id=None
        )
    ),
    avg_dd_duration=dict(
        title='Avg Drawdown Duration',
        calc_func=RepEval(
            template="'avg_duration' if incl_active else 'status_recovered.get_avg_duration'",
            context=None,
            strict=None,
            context_merge_kwargs=None,
            eval_id=None
        ),
        fill_wrap_kwargs=True,
        tags=RepEval(
            template="['drawdowns', 'duration'] if incl_active else ['drawdowns', 'recovered', 'duration']",
            context=None,
            strict=None,
            context_merge_kwargs=None,
            eval_id=None
        )
    ),
    max_return=dict(
        title='Max Recovery Return [%]',
        calc_func='status_recovered.recovery_return.max',
        post_calc_func=<function Drawdowns.<lambda> at 0x1325a9c60>,
        tags=[
            'drawdowns',
            'recovered'
        ]
    ),
    avg_return=dict(
        title='Avg Recovery Return [%]',
        calc_func='status_recovered.recovery_return.mean',
        post_calc_func=<function Drawdowns.<lambda> at 0x1325a9d00>,
        tags=[
            'drawdowns',
            'recovered'
        ]
    ),
    max_recovery_duration=dict(
        title='Max Recovery Duration',
        calc_func='status_recovered.recovery_duration.max',
        apply_to_timedelta=True,
        tags=[
            'drawdowns',
            'recovered',
            'duration'
        ]
    ),
    avg_recovery_duration=dict(
        title='Avg Recovery Duration',
        calc_func='status_recovered.recovery_duration.mean',
        apply_to_timedelta=True,
        tags=[
            'drawdowns',
            'recovered',
            'duration'
        ]
    ),
    recovery_duration_ratio=dict(
        title='Avg Recovery Duration Ratio',
        calc_func='status_recovered.recovery_duration_ratio.mean',
        tags=[
            'drawdowns',
            'recovered'
        ]
    )
)

Returns Drawdowns._metrics, which gets (hybrid-) 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.


plot method

Drawdowns.plot(
    column=None,
    top_n=5,
    plot_ohlc=True,
    plot_close=True,
    plot_markers=True,
    plot_zones=True,
    ohlc_type=None,
    ohlc_trace_kwargs=None,
    close_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_ohlc : bool
Whether to plot OHLC.
plot_close : bool
Whether to plot close.
plot_markers : bool
Whether to plot markers.
plot_zones : bool
Whether to plot zones.
ohlc_type

Either 'OHLC', 'Candlestick' or Plotly trace.

Pass None to use the default.

ohlc_trace_kwargs : dict
Keyword arguments passed to ohlc_type.
close_trace_kwargs : dict
Keyword arguments passed to plotly.graph_objects.Scatter for Drawdowns.close.
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

>>> index = pd.date_range("2020", periods=8)
>>> price = pd.Series([1, 2, 1, 2, 3, 2, 1, 2], index=index)
>>> vbt.Drawdowns.from_price(price, wrapper_kwargs=dict(freq='1 day')).plot().show()


plots_defaults property

Defaults for PlotsBuilderMixin.plots().

Merges Ranges.plots_defaults and plots from drawdowns.


ranges property

Drawdowns.get_ranges() with default arguments.


recovery_duration property

Drawdowns.get_recovery_duration() with default arguments.


recovery_duration_ratio property

Drawdowns.get_recovery_duration_ratio() with default arguments.


recovery_ranges property

Drawdowns.get_recovery_ranges() with default arguments.


recovery_return property

Drawdowns.get_recovery_return() with default arguments.


start_val property

Mapped array of the field start_val.


stats_defaults property

Defaults for StatsBuilderMixin.stats().

Merges Ranges.stats_defaults and stats from drawdowns.


status_active property

Records filtered by status == 0.


status_recovered property

Records filtered by status == 1.


subplots class variable

Subplots supported by Drawdowns.

HybridConfig(
    plot=dict(
        title='Drawdowns',
        check_is_not_grouped=True,
        plot_func='plot',
        tags='drawdowns'
    )
)

Returns Drawdowns._subplots, which gets (hybrid-) 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 property

Mapped array of the field valley_idx.


valley_val property

Mapped array of the field valley_val.