# Patterns and event studies (/features/analytics/patterns-and-event-studies)

## Simulation ranges \[#simulation-ranges]

New in v2024.5.15

✅ Each simulation starts and ends at specific points, usually matching the first and last rows of your
data. You can set a different simulation range beforehand, and now, you can also adjust this range during
the simulation. This flexibility lets you stop the simulation when further processing is unnecessary.
Additionally, the date range is saved in the portfolio object, so all metrics and subplots recognize it.
Processing only the relevant dates speeds up execution and adds a new dimension to your analysis:
isolated time windows 🔬

=== "Example 1: Liquidation"
    ```python title="Simulate a quick liquidation scenario"
    >>> @njit
    ... def post_segment_func_nb(ctx):
    ...     value = vbt.pf_nb.get_group_value_nb(ctx, ctx.group)
    ...     if value <= 0:
    ...         vbt.pf_nb.stop_group_sim_nb(ctx, ctx.group)  # (1)

    >>> pf = vbt.PF.from_random_signals(
    ...     "BTC-USD",
    ...     n=10,
    ...     seed=42,
    ...     sim_start="auto",  # (2)
    ...     post_segment_func_nb=post_segment_func_nb,
    ...     leverage=10,
    ... )
    >>> pf.plot_value()  # (3)
    ```

    1.  Stop the simulation of the current group if its value turns negative.
    2.  Start the simulation at the first signal.
    3.  Make sure all metrics and subplots use only data up to the liquidation point, even if the portfolio
        contains the full original data set (2014 → today).

    Seeded leveraged BTC-USD portfolio value through liquidation. [Figure data (JSON)](/assets/figures/features/analysis/liquidation.abff6a63de05.json)

=== "Example 2: Date range analysis"
    ```python title="Analyze a date range of an already simulated portfolio"
    >>> pf = vbt.PF.from_random_signals("BTC-USD", n=10, seed=42)

    >>> pf.get_sharpe_ratio(sim_start="2023", sim_end="2024")  # (1)
    1.7846214408154346

    >>> pf.get_sharpe_ratio(sim_start="2023", sim_end="2024", rec_sim_range=True)  # (2)
    1.8377982089422782

    >>> pf.returns_stats(settings=dict(sim_start="2023", sim_end="2024"))  # (3)
    Start Index                  2023-01-01 00:00:00+00:00
    End Index                    2023-12-31 00:00:00+00:00
    Total Duration                       365 days 00:00:00
    Total Return [%]                             84.715081
    Benchmark Return [%]                        155.417419
    Annualized Return [%]                        84.715081
    Annualized Volatility [%]                     38.49976
    Max Drawdown [%]                             20.057773
    Max Drawdown Duration                102 days 00:00:00
    Sharpe Ratio                                  1.784621
    Calmar Ratio                                  4.223554
    Omega Ratio                                   1.378076
    Sortino Ratio                                 3.059933
    Skew                                          -0.39136
    Kurtosis                                     13.607937
    Tail Ratio                                    1.323376
    Common Sense Ratio                            1.823713
    Value at Risk                                -0.028314
    Alpha                                        -0.103145
    Beta                                          0.770428
    dtype: object
    ```

    1.  Consider only the returns within the specified date range when calculating the Sharpe ratio.
        Note that returns may still be affected by data outside the date range (such as open positions).
    2.  Recursively apply the date range to all metrics that the Sharpe ratio depends on, such as equity,
        cash, and orders, treating data outside the range as if it does not exist.
    3.  Make sure the date range is used consistently for all statistics.

## Patterns \[#patterns]

New in 1.5.0

✅ Patterns are distinctive formations created by price movements on a chart and are central to
technical analysis. There are now new dedicated functions and classes for detecting patterns of any
complexity in any type of time series data. The idea is simple: fit a pattern to align with the
scale and period of your selected data window, then compute the element-wise distance between
them to get a single similarity score. You can adjust the threshold for this score to decide above
which value a data window should be marked as "matched." Thanks to Numba, this operation can be
performed hundreds of thousands of times per second! 🔎

```python title="Find and plot a descending triangle pattern"
>>> data = vbt.YFData.pull("BTC-USD")
>>> data.hlc3.vbt.find_pattern(
...     pattern=[5, 1, 3, 1, 2, 1],
...     window=100,
...     max_window=700,
... ).loc["2017":"2019"].plot().show()
```

Descending triangle pattern matches in BTC-USD from 2017 through 2019. [Figure data (JSON)](/assets/figures/features/analysis/patterns.0fd1d09b4584.json)

!!! info "Tutorial"
    Learn more in the [Patterns and projections](/tutorials/patterns-and-projections) tutorial.

## Projections \[#projections]

New in 1.5.0

✅ There are cleaner ways to analyze events and their impact on price than conventional backtesting.
Meet projections! 👋 Not only can they help you assess event performance visually and
quantitatively, but they can also project events into the future to support trading. This is done
by extracting the price range after each event, collecting all these price ranges into a multidimensional
array, and then deriving confidence intervals and other useful statistics from that array. When
combined with patterns, these tools are a quantitative analyst's dream! 🌟

```python title="Find occurrences of the price moving similarly to the last week and project them"
>>> data = vbt.YFData.pull("ETH-USD")
>>> pattern_ranges = data.hlc3.vbt.find_pattern(
...     pattern=data.close.iloc[-7:],
...     rescale_mode="rebase"
... )
>>> delta_ranges = pattern_ranges.with_delta(7)
>>> fig = data.iloc[-7:].plot(plot_volume=False)
>>> delta_ranges.plot_projections(fig=fig)
>>> fig.show()
```

ETH-USD last-week pattern matches with projected confidence bands. [Figure data (JSON)](/assets/figures/features/analysis/projections.0b45eca04db2.json)

!!! info "Tutorial"
    Learn more in the [Patterns and projections](/tutorials/patterns-and-projections) tutorial.

## OHLC-native classes \[#ohlc-native-classes]

New in 1.3.0

✅ Previously, OHLC data was used for simulation, but only the close price was analyzed.
Now, most classes let you track all OHLC data for more accurate quantitative and qualitative analysis.

```python title="Plot trades of a random portfolio"
>>> data = vbt.YFData.pull("BTC-USD", start="2020-01", end="2020-03")
>>> pf = vbt.PF.from_random_signals(
...     open=data.open,
...     high=data.high,
...     low=data.low,
...     close=data.close,
...     n=10,
...     seed=42
... )
>>> pf.trades.plot().show()
```

BTC-USD OHLC with random portfolio trade entries and exits. [Figure data (JSON)](/assets/figures/features/analysis/ohlc-native-classes.4ffa9efd631b.json)
