# Configuration and persistence (/features/tooling/configuration-and-persistence)

## Reference graphs \[#reference-graphs]

New in v2025.12.31

✅ When analyzing complex codebases like VBT, it can be challenging to keep track of all the
interdependencies between modules, classes, and functions. To address this, VBT now includes
a functionality that can index the source code of any codebase for object references and generate
reference graphs from any specified entry point. These graphs provide a visual representation of how different
components relate to each other, making it easier to understand the overall structure and flow of the code.

```python title="Generate a reference graph for Pandas and display it in a new browser tab"
>>> ref_index = vbt.RefIndex(container_kinds=["module", "class"], incl_modules="pandas")
>>> ref_graph = ref_index.build_graph("pandas")
>>> ref_graph.plot(
...     interactive="dash",
...     to_dash_kwargs=dict(fit_to_window=True),
...     dash_run_kwargs=dict(jupyter_mode="tab")
... )
```

Reference graph of Pandas modules, classes, callables, and data dependencies. [Figure data (JSON)](/assets/figures/features/productivity/reference-graph.0345cbe42aad.json)

!!! info "Online explorer"
    Explore the VBT's reference graph in the [`API`](/api/) → Reference graph.

## Annotations \[#annotations]

New in v2023.12.23

✅ When writing a function, you can specify the meaning of each argument using an annotation
immediately next to the argument. VBT now provides a rich set of in-house annotations tailored to specific
tasks. For example, whether an argument is a parameter can be specified directly in the function instead of
in the [parameterized decorator](/features/optimization/strategy-optimization/#parameterized-decorator).

```python title="Test a cross-validation function with annotations"
>>> @vbt.cv_split(
...     splitter="from_rolling",
...     splitter_kwargs=dict(length=365, split=0.5, set_labels=["train", "test"]),
...     parameterized_kwargs=dict(random_subset=100, seed=42),
... )
... def sma_crossover_cv(
...     data: vbt.Takeable,  # (1)
...     fast_period: vbt.Param(condition="x < slow_period"),  # (2)
...     slow_period: vbt.Param,  # (3)
...     metric
... ) -> vbt.MergeFunc("concat"):
...     fast_sma = data.run("sma", fast_period, hide_params=True)
...     slow_sma = data.run("sma", slow_period, hide_params=True)
...     entries = fast_sma.real_crossed_above(slow_sma)
...     exits = fast_sma.real_crossed_below(slow_sma)
...     pf = vbt.PF.from_signals(data, entries, exits, direction="both")
...     return pf.deep_getattr(metric)

>>> sma_crossover_cv(
...     vbt.YFData.pull("BTC-USD", start="4 years ago"),
...     np.arange(20, 50),
...     np.arange(20, 50),
...     "trades.expectancy"
... )
split  set    fast_period  slow_period
0      train  25           26               2.822879
       test   25           26               4.740533
1      train  22           37              16.967189
       test   22           37             -16.101936
2      train  30           41             308.879034
       test   30           41              14.445511
3      train  29           49              32.328702
       test   29           49              13.546335
4      train  27           48              20.695292
       test   27           48               6.539824
5      train  22           46              19.143686
       test   22           46              -4.664139
6      train  34           45               5.235739
       test   34           45              -2.697966
dtype: float64
```

1.  The passed argument must be takeable (for selecting subsets by split).
2.  Parameter with a condition requiring it to be less than the slow period.
3.  Parameter for the slow period.

## Compression \[#compression]

New in 1.10.0

✅ Serialized VBT objects can sometimes use a lot of disk space. With this update,
VBT now supports a variety of compression algorithms to make files as light as possible! 🪶

```python title="Save data without and with compression"
>>> data = vbt.RandomOHLCData.pull(
...     "RAND",
...     start="2022",
...     end="2023",
...     timeframe="1 minute",
...     seed=42
... )

>>> file_path = data.save()
>>> print(vbt.file_size(file_path))
21.0 MB

>>> file_path = data.save(compression="blosc")
>>> print(vbt.file_size(file_path))
13.3 MB
```

## Faster loading \[#faster-loading]

New in 1.10.0

✅ If your pipeline does not need accessors, Plotly graphs, or most other optional
features, you can disable the auto-import feature entirely to reduce VBT's loading
time to under a second ⏳

=== "INI"
    ```ini title="Define importing settings in vbt.cfg"
    [importing]
    auto_import = False
    ```

=== "YAML"
    ```yaml title="Define importing settings in vbt.yml"
    importing:
      auto_import: false
    ```

=== "TOML"
    ```toml title="Define importing settings in vbt.toml"
    [importing]
    auto_import = false
    ```

```python title="Measure the loading time"
>>> start = utc_time()
>>> from vectorbtpro import *
>>> end = utc_time()
>>> end - start
0.580937910079956
```

## Serialization \[#serialization]

New in 1.9.0

✅ Just like machine learning models, every native VBT object can be serialized and saved to
a binary file. It has never been easier to share data and insights! Another benefit is that only the
actual content of each object is serialized, not its class definition, so the loaded object
always uses the most up-to-date class definition. There is also special logic implemented
to help you "reconstruct" objects if VBT introduces any breaking API changes 🏗️

```python title="Backtest each month of data and save the results for later"
>>> data = vbt.YFData.pull("BTC-USD", start="2022-01-01", end="2022-06-01")

>>> def backtest_month(close):
...     return vbt.PF.from_random_signals(close, n=10, seed=42)

>>> month_pfs = data.close.resample(vbt.offset("M")).apply(backtest_month)
>>> month_pfs
Date
2022-01-01 00:00:00+00:00    Portfolio(\n    wrapper=ArrayWrapper(\n       ...
2022-02-01 00:00:00+00:00    Portfolio(\n    wrapper=ArrayWrapper(\n       ...
2022-03-01 00:00:00+00:00    Portfolio(\n    wrapper=ArrayWrapper(\n       ...
2022-04-01 00:00:00+00:00    Portfolio(\n    wrapper=ArrayWrapper(\n       ...
2022-05-01 00:00:00+00:00    Portfolio(\n    wrapper=ArrayWrapper(\n       ...
Freq: MS, Name: Close, dtype: object

>>> vbt.save(month_pfs, "month_pfs")  # (1)

>>> month_pfs = vbt.load("month_pfs")  # (2)
>>> month_pfs.apply(lambda pf: pf.total_return)
Date
2022-01-01 00:00:00+00:00   -0.083672
2022-02-01 00:00:00+00:00    0.173909
2022-03-01 00:00:00+00:00   -0.006249
2022-04-01 00:00:00+00:00   -0.057868
2022-05-01 00:00:00+00:00    0.019209
Freq: MS, Name: Close, dtype: float64
```

1.  Save to disk.
2.  Load from disk later.

## Formatting engine \[#formatting-engine]

New in 1.0.2

✅ VBT is a comprehensive library that defines thousands of classes, functions, and objects.
When working with these, you may want to "look inside" an object to better understand
its attributes and contents. Fortunately, there is a formatting engine that can accurately format
any in-house object as a human-readable string. Did you know the API documentation is partly
powered by this engine? 😉

```python title="Introspect a data instance"
>>> data = vbt.YFData.pull("BTC-USD", start="2020", end="2021")

>>> vbt.pprint(data)  # (1)
YFData(
    wrapper=ArrayWrapper(...),
    data=symbol_dict({
        'BTC-USD': <pandas.core.frame.DataFrame object at 0x7f7f1fbc6cd0 with shape (366, 7)>
    }),
    single_key=True,
    classes=symbol_dict(),
    fetch_kwargs=symbol_dict({
        'BTC-USD': dict(
            start='2020',
            end='2021'
        )
    }),
    returned_kwargs=symbol_dict({
        'BTC-USD': dict()
    }),
    last_index=symbol_dict({
        'BTC-USD': Timestamp('2020-12-31 00:00:00+0000', tz='UTC')
    }),
    tz_localize=datetime.timezone.utc,
    tz_convert='UTC',
    missing_index='nan',
    missing_columns='raise'
)

>>> vbt.pdir(data)  # (2)
                                            type                                             path
attr
align_columns                        classmethod                       vectorbtpro.data.base.Data
align_index                          classmethod                       vectorbtpro.data.base.Data
build_feature_config_doc             classmethod                       vectorbtpro.data.base.Data
...                                          ...                                              ...
vwap                                    property                       vectorbtpro.data.base.Data
wrapper                                 property               vectorbtpro.base.wrapping.Wrapping
xs                                      function          vectorbtpro.base.indexing.PandasIndexer

>>> vbt.phelp(data.get)  # (3)
YFData.get(
    columns=None,
    symbols=None,
    **kwargs
):
    Get one or more columns of one or more symbols of data.
```

1.  Similar to Python's `print` command, pretty-prints the contents of any VBT object.
2.  Similar to Python's `dir` command, pretty-prints the attributes of a class, object, or module.
3.  Similar to Python's `help` command, pretty-prints the signature and docstring of a function.

## Templates \[#templates]

New in 1.0.0

✅ It is easy to extend classes, but since VBT revolves around functions, how do we enhance them or
change their workflow? The easiest way is to introduce a small function (i.e., callback) that the user
can provide and that the main function calls at some point. However, this would require the main function
to know what arguments to pass to the callback and how to handle its outputs. Here is a better idea:
allow most arguments of the main function to become callbacks, then execute those to obtain their actual
values. These arguments are called "templates" and this process is known as "substitution".
Templates are especially useful when some arguments (such as arrays) should be built only
once all required information is available, for example, when other arrays have already been broadcast.
Each substitution opportunity has its own identifier so you can control when a template
should be substituted. In VBT, templates are first-class citizens and are integrated into most
functions for unmatched flexibility! 🧞

```python title="Design a template-enhanced resampling functionality"
>>> def resample_apply(index, by, apply_func, *args, template_context={}, **kwargs):
...     grouper = index.vbt.get_grouper(by)  # (1)
...     results = {}
...     with vbt.ProgressBar() as pbar:
...         for group, group_idxs in grouper:  # (2)
...             group_index = index[group_idxs]
...             context = {"group": group, "group_index": group_index, **template_context}  # (3)
...             final_apply_func = vbt.substitute_templates(apply_func, context, eval_id="apply_func")  # (4)
...             final_args = vbt.substitute_templates(args, context, eval_id="args")
...             final_kwargs = vbt.substitute_templates(kwargs, context, eval_id="kwargs")
...             results[group] = final_apply_func(*final_args, **final_kwargs)
...             pbar.update()
...     return pd.Series(results)

>>> data = vbt.YFData.pull(["BTC-USD", "ETH-USD"], missing_index="drop")
>>> resample_apply(
...     data.index, "Y",
...     lambda x, y: x.corr(y),  # (5)
...     vbt.RepEval("btc_close[group_index]"),  # (6)
...     vbt.RepEval("eth_close[group_index]"),
...     template_context=dict(
...         btc_close=data.get("Close", "BTC-USD"),  # (7)
...         eth_close=data.get("Close", "ETH-USD")
...     )
... )
```

1.  Builds a grouper. Accepts both group-by and resample instructions.
2.  Iterates over groups in the grouper. Each group contains a label (such as `2017-01-01 00:00:00+00:00`)
    and the row indices corresponding to this label.
3.  Creates a new context with information about the current group and any external information provided
    by the user.
4.  Substitutes the function and arguments using the newly populated context.
5.  Simple function to compute the correlation coefficient between two arrays.
6.  Defines both arguments as expression templates where data is selected for each group.
    All variables in these expressions will be automatically recognized and replaced by the current context.
    After evaluation, the templates will be replaced by their outputs.
7.  Specifies any additional information your templates depend on.

Group 7/7: 100%

```text title="Output"
2017    0.808930
2018    0.897112
2019    0.753659
2020    0.940741
2021    0.553255
2022    0.975911
2023    0.974914
Freq: A-DEC, dtype: float64
```
