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¶
Hint
>>> 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¶
Hint
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
- AttrResolver
- Configured
- Documented
- IndexingBase
- PandasIndexer
- Pickleable
- PlotsBuilderMixin
- Ranges
- Records
- RecordsWithFields
- StatsBuilderMixin
- Wrapping
Inherited members
- AttrResolver.deep_getattr()
- AttrResolver.post_resolve_attr()
- AttrResolver.pre_resolve_attr()
- AttrResolver.resolve_attr()
- Configured.copy()
- Configured.dumps()
- Configured.loads()
- Configured.to_doc()
- Configured.update_config()
- PandasIndexer.xs()
- Pickleable.load()
- Pickleable.save()
- PlotsBuilderMixin.build_subplots_doc()
- PlotsBuilderMixin.override_subplots_doc()
- PlotsBuilderMixin.plots()
- Ranges.avg_duration()
- Ranges.closed
- Ranges.col
- Ranges.col_arr
- Ranges.col_mapper
- Ranges.config
- Ranges.coverage()
- Ranges.duration
- Ranges.end_idx
- Ranges.id
- Ranges.id_arr
- Ranges.idx_arr
- Ranges.iloc
- Ranges.indexing_kwargs
- Ranges.loc
- Ranges.max_duration()
- Ranges.open
- Ranges.records
- Ranges.records_arr
- Ranges.records_readable
- Ranges.self_aliases
- Ranges.start_idx
- Ranges.status
- Ranges.to_mask()
- Ranges.ts
- Ranges.values
- Ranges.wrapper
- Ranges.writeable_attrs
- Records.apply()
- Records.apply_mask()
- Records.build_field_config_doc()
- Records.count()
- Records.get_apply_mapping_arr()
- Records.get_by_col_idxs()
- Records.get_field_arr()
- Records.get_field_mapping()
- Records.get_field_name()
- Records.get_field_setting()
- Records.get_field_title()
- Records.get_map_field()
- Records.get_map_field_to_index()
- Records.indexing_func_meta()
- Records.is_sorted()
- Records.map()
- Records.map_array()
- Records.map_field()
- Records.override_field_config_doc()
- Records.replace()
- Records.sort()
- StatsBuilderMixin.build_metrics_doc()
- StatsBuilderMixin.override_metrics_doc()
- StatsBuilderMixin.stats()
- Wrapping.regroup()
- Wrapping.resolve_self()
- Wrapping.select_one()
- Wrapping.select_one_from_obj()
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¶
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 0x11996ad40>",
"agg_func": null,
"tags": "wrapper"
},
"end": {
"title": "End",
"calc_func": "<function Drawdowns.<lambda> at 0x11996ade0>",
"agg_func": null,
"tags": "wrapper"
},
"period": {
"title": "Period",
"calc_func": "<function Drawdowns.<lambda> at 0x11996ae80>",
"apply_to_timedelta": true,
"agg_func": null,
"tags": "wrapper"
},
"coverage": {
"title": "Coverage [%]",
"calc_func": "coverage",
"post_calc_func": "<function Drawdowns.<lambda> at 0x11996af20>",
"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 0x11996afc0>",
"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 0x11996b060>",
"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 0x11996b100>",
"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 0x11996b1a0>",
"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 0x11996b240>",
"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 0x11996b2e0>",
"tags": [
"drawdowns",
"recovered"
]
},
"avg_return": {
"title": "Avg Recovery Return [%]",
"calc_func": "recovered.recovery_return.mean",
"post_calc_func": "<function Drawdowns.<lambda> at 0x11996b380>",
"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
orFigureWidget
- 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¶
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
.