Skip to content

trading.analyzer.graphing.underwater_drawdown

trading.analyzer.graphing.underwater_drawdown

build_underwater_plot

build_underwater_plot(df: DataFrame, experiment_ids: list[str] | None = None, top_n: int = 8, rank_by: str = 'calmar_ratio', max_traces: int = 40) -> go.Figure

Builds an underwater plot (drawdown-from-peak over time) for a shortlist of experiments.

Parameters:

Name Type Description Default
df DataFrame

Experiment DataFrame.

required
experiment_ids list[str] | None

List of experiment_id values to plot. If given, all of them are plotted and no slider is added (the set is fixed).

None
top_n int

How many traces are visible when the chart first loads (auto-rank mode only).

8
rank_by str

Metric column to rank by when auto-selecting the shortlist.

'calmar_ratio'
max_traces int

How many experiments get a trace at all, i.e. the slider's max (auto-rank mode only; ignored when experiment_ids is given).

40

Returns:

Type Description
Figure

A plotly Figure.

Source code in src\contango\trading\analyzer\graphing\underwater_drawdown.py
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def build_underwater_plot(
    df: pd.DataFrame,
    experiment_ids: list[str] | None = None,
    top_n: int = 8,
    rank_by: str = "calmar_ratio",
    max_traces: int = 40,
) -> go.Figure:
    """
    Builds an underwater plot (drawdown-from-peak over time) for a shortlist of experiments.

    Args:
        df: Experiment DataFrame.
        experiment_ids: List of experiment_id values to plot. If given, all of
                        them are plotted and no slider is added (the set is fixed).
        top_n: How many traces are visible when the chart first loads (auto-rank mode only).
        rank_by: Metric column to rank by when auto-selecting the shortlist.
        max_traces: How many experiments get a trace at all, i.e. the slider's max
                    (auto-rank mode only; ignored when experiment_ids is given).

    Returns:
        A plotly Figure.
    """
    plot_df = df.dropna(subset=["equity_curve"]).copy()
    use_slider = experiment_ids is None

    if not use_slider:
        plot_df = plot_df[plot_df["experiment_id"].isin(experiment_ids)]
    else:
        plot_df = (
            plot_df.sort_values(rank_by, ascending=False)
            .head(max_traces)
            .reset_index(drop=True)
        )

    n_traces = len(plot_df)
    default_top_n = min(top_n, n_traces)

    fig = go.Figure()
    for i, (_, row) in enumerate(plot_df.iterrows()):
        timestamps, drawdowns = _compute_underwater_series(row["equity_curve"])
        fig.add_trace(  # type: ignore[unknownMemberType]
            go.Scatter(
                x=timestamps,
                y=drawdowns,
                mode="lines",
                name=row["experiment_id"],
                fill="tozeroy",
                visible=(i < default_top_n) if use_slider else True,
            )
        )

    layout_kwargs: dict[str, object] = dict(
        title="Underwater Plot — Drawdown From Peak Over Time",
        xaxis_title="Time",
        yaxis_title="Drawdown From Peak",
        yaxis_tickformat=".1%"
    )

    if use_slider:
        steps: list[dict[str, str | list[dict[str, list[bool]]]]] = []
        for n in range(1, n_traces + 1):
            steps.append(
                dict(
                    method="restyle",
                    args=[{"visible": [i < n for i in range(n_traces)]}],
                    label=str(n),
                )
            )

        layout_kwargs["sliders"] = [
            dict(
                active=default_top_n - 1,
                currentvalue={"prefix": "Showing top "},
                pad={"t": 60},
                steps=steps,
            )
        ]

    fig.update_layout(**layout_kwargs)  # type: ignore[unknownMemberType]
    return fig