from scipy.stats import skew, kurtosis from vectorbt import _typing as tp from vectorbt.root_accessors import register_dataframe_accessor, register_series_accessor from vectorbt.utils import checks from vectorbt.utils.config import merge_dicts from vectorbt.utils.figure import make_figure from vectorbt.utils.decorators import cached_property, cached_method from vectorbt.base.reshape_fns import to_1d, to_2d, broadcast_to from vectorbt.generic.drawdowns import Drawdowns from vectorbt.generic.accessors import (GenericAccessor, GenericSRAccessor, GenericDFAccessor) from vectorbt.utils.datetime import freq_to_timedelta, DatetimeIndexes from vectorbt.returns import nb, metrics ReturnsAccessorT = tp.TypeVar("ReturnsAccessorT", bound="ReturnsAccessor") class ReturnsAccessor(GenericAccessor): """Accessor on top of return series. For both, Series and DataFrames. Accessible through `pd.Series.vbt.returns` and `pd.DataFrame.vbt.returns`.""" def __init__(self, obj: tp.SeriesFrame, year_freq: tp.Optional[tp.FrequencyLike] = None, **kwargs) -> None: if not checks.is_pandas(obj): # parent accessor obj = obj._obj GenericAccessor.__init__(self, obj, **kwargs)
""" import numpy as np import pandas as pd from vectorbt import _typing as tp from vectorbt.utils import checks from vectorbt.utils.decorators import cached_method from vectorbt.utils.config import merge_dicts from vectorbt.base.reshape_fns import to_1d from vectorbt.base.array_wrapper import ArrayWrapper, Wrapping from vectorbt.records import nb from vectorbt.records.mapped_array import MappedArray from vectorbt.records.col_mapper import ColumnMapper RecordsT = tp.TypeVar("RecordsT", bound="Records") IndexingMetaT = tp.Tuple[ArrayWrapper, tp.RecordArray, tp.MaybeArray, tp.Array1d] class Records(Wrapping): """Wraps the actual records array (such as trades) and exposes methods for mapping it to some array of values (such as P&L of each trade). Args: wrapper (ArrayWrapper): Array wrapper. See `vectorbt.base.array_wrapper.ArrayWrapper`. records_arr (array_like): A structured NumPy array of records. Must have the fields `id` (record index) and `col` (column index).
Accessors do not utilize caching. Grouping is only supported by the methods that accept the `group_by` argument.""" import numpy as np import pandas as pd from vectorbt import _typing as tp from vectorbt.base import combine_fns, index_fns, reshape_fns from vectorbt.base.array_wrapper import ArrayWrapper, Wrapping from vectorbt.base.column_grouper import ColumnGrouper from vectorbt.utils import checks from vectorbt.utils.config import merge_dicts, get_func_arg_names from vectorbt.utils.decorators import class_or_instancemethod, attach_binary_magic_methods, attach_unary_magic_methods BaseAccessorT = tp.TypeVar("BaseAccessorT", bound="BaseAccessor") @attach_binary_magic_methods(lambda self, other, np_func: self.combine( other, allow_multiple=False, combine_func=np_func)) @attach_unary_magic_methods( lambda self, np_func: self.apply(apply_func=np_func)) class BaseAccessor(Wrapping): """Accessor on top of Series and DataFrames. Accessible through `pd.Series.vbt` and `pd.DataFrame.vbt`, and all child accessors. Series is just a DataFrame with one column, hence to avoid defining methods exclusively for 1-dim data, we will convert any Series to a DataFrame and perform matrix computation on it. Afterwards, by using `BaseAccessor.wrapper`, we will convert the 2-dim output back to a Series.
""" ranges_attach_field_config = Config(dict(status=dict(attach_filters=True)), readonly=True, as_attrs=False) """_""" __pdoc__[ 'ranges_attach_field_config'] = f"""Config of fields to be attached to `Ranges`. ```json {ranges_attach_field_config.to_doc()} ``` """ RangesT = tp.TypeVar("RangesT", bound="Ranges") @attach_fields(ranges_attach_field_config) @override_field_config(ranges_field_config) class Ranges(Records): """Extends `Records` for working with range records. Requires `records_arr` to have all fields defined in `vectorbt.generic.enums.range_dt`.""" @property def field_config(self) -> Config: return self._field_config def __init__(self, wrapper: ArrayWrapper, records_arr: tp.RecordArray,
to each structured object and constructing the new user-defined class using them. This way, one can manipulate complex classes with dozens of pandas objects using a single command.""" import numpy as np import pandas as pd from vectorbt import _typing as tp from vectorbt.utils import checks from vectorbt.base import index_fns, reshape_fns class IndexingError(Exception): """Exception raised when an indexing error has occurred.""" IndexingBaseT = tp.TypeVar("IndexingBaseT", bound="IndexingBase") class IndexingBase: """Class that supports indexing through `IndexingBase.indexing_func`.""" def indexing_func(self: IndexingBaseT, pd_indexing_func: tp.Callable, **kwargs) -> IndexingBaseT: """Apply `pd_indexing_func` on all pandas objects in question and return a new instance of the class. Should be overridden.""" raise NotImplementedError class LocBase: """Class that implements location-based indexing.""" def __init__(self, indexing_func: tp.Callable, **kwargs) -> None:
if p.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD) else p for p in list(source_sig.parameters.values())[1:] ] source_sig = source_sig.replace(parameters=(self_arg, ) + tuple(other_args)) new_method.__signature__ = source_sig new_method.__doc__ = f"See `quantstats.{module_name}.{qs_func_name}`." new_method.__qualname__ = f"{cls.__name__}.{new_method_name}" new_method.__name__ = new_method_name setattr(cls, new_method_name, new_method) return cls QSAdapterT = tp.TypeVar("QSAdapterT", bound="QSAdapter") @attach_qs_methods class QSAdapter(Configured): """Adapter class for quantstats.""" def __init__(self, returns_accessor: ReturnsAccessor, defaults: tp.KwargsLike = None, **kwargs) -> None: checks.assert_instance_of(returns_accessor, ReturnsAccessor) Configured.__init__(self, returns_accessor=returns_accessor, defaults=defaults, **kwargs)
from vectorbt.utils.datetime_ import is_tz_aware, to_timezone from vectorbt.utils.decorators import cached_method __pdoc__ = {} class symbol_dict(dict): """Dict that contains symbols as keys.""" pass class MetaData(type(StatsBuilderMixin), type(PlotsBuilderMixin)): pass DataT = tp.TypeVar("DataT", bound="Data") class Data(Wrapping, StatsBuilderMixin, PlotsBuilderMixin, metaclass=MetaData): """Class that downloads, updates, and manages data coming from a data source.""" def __init__(self, wrapper: ArrayWrapper, data: tp.Data, tz_localize: tp.Optional[tp.TimezoneLike], tz_convert: tp.Optional[tp.TimezoneLike], missing_index: str, missing_columns: str, download_kwargs: dict, **kwargs) -> None: Wrapping.__init__(
import pandas as pd from vectorbt import _typing as tp from vectorbt.base.array_wrapper import ArrayWrapper, Wrapping from vectorbt.base.reshape_fns import to_1d_array, to_dict from vectorbt.generic import nb as generic_nb from vectorbt.generic.plots_builder import PlotsBuilderMixin from vectorbt.generic.stats_builder import StatsBuilderMixin from vectorbt.records import nb from vectorbt.records.col_mapper import ColumnMapper from vectorbt.utils import checks from vectorbt.utils.config import merge_dicts, Config, Configured from vectorbt.utils.decorators import cached_method, attach_binary_magic_methods, attach_unary_magic_methods from vectorbt.utils.mapping import to_mapping, apply_mapping MappedArrayT = tp.TypeVar("MappedArrayT", bound="MappedArray") IndexingMetaT = tp.Tuple[ArrayWrapper, tp.Array1d, tp.Array1d, tp.Array1d, tp.Optional[tp.Array1d], tp.MaybeArray, tp.Array1d] def combine_mapped_with_other( self: MappedArrayT, other: tp.Union["MappedArray", tp.ArrayLike], np_func: tp.Callable[[tp.ArrayLike, tp.ArrayLike], tp.Array1d] ) -> MappedArrayT: """Combine `MappedArray` with other compatible object. If other object is also `MappedArray`, their `id_arr` and `col_arr` must match.""" if isinstance(other, MappedArray): checks.assert_array_equal(self.id_arr, other.id_arr) checks.assert_array_equal(self.col_arr, other.col_arr) other = other.values
attach_filters=True ) ), readonly=True, as_attrs=False ) """_""" __pdoc__['orders_attach_field_config'] = f"""Config of fields to be attached to `Orders`. ```json {orders_attach_field_config.to_doc()} ``` """ OrdersT = tp.TypeVar("OrdersT", bound="Orders") @attach_fields(orders_attach_field_config) @override_field_config(orders_field_config) class Orders(Records): """Extends `Records` for working with order records.""" @property def field_config(self) -> Config: return self._field_config def __init__(self, wrapper: ArrayWrapper, records_arr: tp.RecordArray, close: tp.Optional[tp.ArrayLike] = None,
res_side=dict(attach_filters=True), res_status=dict(attach_filters=True), res_status_info=dict(attach_filters=True)), readonly=True, as_attrs=False) """_""" __pdoc__[ 'logs_attach_field_config'] = f"""Config of fields to be attached to `Logs`. ```json {logs_attach_field_config.to_doc()} ``` """ LogsT = tp.TypeVar("LogsT", bound="Logs") @attach_fields(logs_attach_field_config) @override_field_config(logs_field_config) class Logs(Records): """Extends `Records` for working with log records.""" @property def field_config(self) -> Config: return self._field_config # ############# Stats ############# # @property def stats_defaults(self) -> tp.Kwargs: """Defaults for `Logs.stats`.
if dct is None: return {} dct_copy = type(dct)() for k, v in dct.items(): if isinstance(v, dict): dct_copy[k] = copy_dict(v) else: dct_copy[k] = copy(v) return dct_copy _RaiseKeyError = object() DumpTuple = namedtuple('DumpTuple', ('cls', 'dumps')) PickleableT = tp.TypeVar("PickleableT", bound="Pickleable") class Pickleable: """Superclass that defines abstract properties and methods for pickle-able classes.""" def dumps(self, **kwargs) -> bytes: """Pickle to bytes.""" raise NotImplementedError @classmethod def loads(cls: tp.Type[PickleableT], dumps: bytes, **kwargs) -> PickleableT: """Unpickle from bytes.""" raise NotImplementedError def save(self, fname: tp.Union[str, Path], **kwargs) -> None:
if end is not None: end = to_tzaware_datetime(end, tz=get_local_tz()) return yf.Ticker(symbol).history(period=period, start=start, end=end, **kwargs) def update_symbol(self, symbol: tp.Label, **kwargs) -> tp.Frame: """Update the symbol. `**kwargs` will override keyword arguments passed to `YFData.download_symbol`.""" download_kwargs = self.select_symbol_kwargs(symbol, self.download_kwargs) download_kwargs['start'] = self.data[symbol].index[-1] kwargs = merge_dicts(download_kwargs, kwargs) return self.download_symbol(symbol, **kwargs) BinanceDataT = tp.TypeVar("BinanceDataT", bound="BinanceData") class BinanceData(Data): """`Data` for data coming from `python-binance`. Usage: * Fetch the 1-minute data of the last 2 hours, wait 1 minute, and update: ```pycon >>> import vectorbt as vbt >>> binance_data = vbt.BinanceData.download( ... "BTCUSDT", ... start='2 hours ago UTC', ... end='now UTC',
from vectorbt.utils.config import merge_dicts from vectorbt.utils.datetime import DatetimeIndexes from vectorbt.utils.enum import enum_to_value_map from vectorbt.utils.figure import make_figure, get_domain from vectorbt.utils.array import min_rel_rescale, max_rel_rescale from vectorbt.base.reshape_fns import to_1d, to_2d, broadcast_to from vectorbt.base.array_wrapper import ArrayWrapper from vectorbt.records.base import Records from vectorbt.records.mapped_array import MappedArray from vectorbt.portfolio.enums import TradeDirection, TradeStatus, trade_dt, position_dt, TradeType from vectorbt.portfolio import nb from vectorbt.portfolio.orders import Orders # ############# Trades ############# # TradesT = tp.TypeVar("TradesT", bound="Trades") class Trades(Records): """Extends `Records` for working with trade records. In vectorbt, a trade is a partial closing operation; it's is a more fine-grained representation of a position. One position can incorporate multiple trades. Performance for this operation is calculated based on the size-weighted average of previous opening operations within the same position. The PnL of all trades combined always equals to the PnL of the entire position. For example, if you have a single large buy operation and 100 small sell operations, you will see 100 trades, each opening with a fraction of the buy operation's size and fees. On the other hand, having 100 buy operations and just a single sell operation will generate a single trade with buy price being a size-weighted average over all purchase prices, and opening size and fees being the sum over all sizes and fees.
result, attr, getattr_func=getattr_func, call_last_attr=True ) else: result = deep_getattr( result, attr, getattr_func=getattr_func, call_last_attr=call_last_attr ) return result AttrResolverT = tp.TypeVar("AttrResolverT", bound="AttrResolver") class AttrResolver: """Class that implements resolution of self and its attributes. Resolution is `getattr` that works for self, properties, and methods. It also utilizes built-in caching.""" @property def self_aliases(self) -> tp.Set[str]: """Names to associate with this object.""" return {'self'} def resolve_self(self: AttrResolverT, cond_kwargs: tp.KwargsLike = None, custom_arg_names: tp.Optional[tp.Set[str]] = None,
def __init__(self, func: tp.Callable) -> None: self.func = func self.__doc__ = getattr(func, '__doc__') def __get__(self, instance: object, owner: tp.Optional[tp.Type] = None) -> tp.Any: if instance is None: return self.func(owner) return self.func(instance) def __set__(self, instance: object, value: tp.Any) -> None: raise AttributeError("can't set attribute") custom_propertyT = tp.TypeVar("custom_propertyT", bound="custom_property") class custom_property: """Custom property that stores function and flags as attributes. Can be called both as ```pycon >>> @custom_property ... def user_function(self): pass ``` and ```plaintext >>> @custom_property(a=0, b=0) # flags ... def user_function(self): pass ```
import numpy as np import pandas as pd from vectorbt import _typing as tp from vectorbt.base import index_fns, reshape_fns from vectorbt.base.column_grouper import ColumnGrouper from vectorbt.base.indexing import IndexingError, PandasIndexer from vectorbt.base.reshape_fns import to_pd_array from vectorbt.utils import checks from vectorbt.utils.array_ import get_ranges_arr from vectorbt.utils.attr_ import AttrResolver, AttrResolverT from vectorbt.utils.config import Configured, merge_dicts from vectorbt.utils.datetime_ import freq_to_timedelta, DatetimeIndexes from vectorbt.utils.decorators import cached_method ArrayWrapperT = tp.TypeVar("ArrayWrapperT", bound="ArrayWrapper") IndexingMetaT = tp.Tuple[ArrayWrapperT, tp.MaybeArray, tp.MaybeArray, tp.Array1d] class ArrayWrapper(Configured, PandasIndexer): """Class that stores index, columns and shape metadata for wrapping NumPy arrays. Tightly integrated with `vectorbt.base.column_grouper.ColumnGrouper`. If the underlying object is a Series, pass `[sr.name]` as `columns`. `**kwargs` are passed to `vectorbt.base.column_grouper.ColumnGrouper`. !!! note This class is meant to be immutable. To change any attribute, use `ArrayWrapper.replace`.
""" dd_attach_field_config = Config(dict(status=dict(attach_filters=True)), readonly=True, as_attrs=False) """_""" __pdoc__[ 'dd_attach_field_config'] = f"""Config of fields to be attached to `Drawdowns`. ```json {dd_attach_field_config.to_doc()} ``` """ DrawdownsT = tp.TypeVar("DrawdownsT", bound="Drawdowns") @attach_fields(dd_attach_field_config) @override_field_config(dd_field_config) class Drawdowns(Ranges): """Extends `vectorbt.generic.ranges.Ranges` for working with drawdown records. Requires `records_arr` to have all fields defined in `vectorbt.generic.enums.drawdown_dt`.""" @property def field_config(self) -> Config: return self._field_config def __init__(self, wrapper: ArrayWrapper, records_arr: tp.RecordArray,
So, for example, the method `pd.Series.vbt.to_2d_array` is also available as `pd.Series.vbt.returns.to_2d_array`. !!! note Accessors in vectorbt are not cached, so querying `df.vbt` twice will also call `Vbt_DFAccessor` twice.""" import warnings import pandas as pd from pandas.core.accessor import DirNamesMixin from vectorbt import _typing as tp from vectorbt.generic.accessors import GenericSRAccessor, GenericDFAccessor from vectorbt.utils.config import Configured ParentAccessorT = tp.TypeVar("ParentAccessorT", bound=object) AccessorT = tp.TypeVar("AccessorT", bound=object) class Accessor: """Custom property-like object. !!! note In contrast to other pandas accessors, this accessor is not cached! This prevents from using old data if the object has been changed in-place.""" def __init__(self, name: str, accessor: tp.Type[AccessorT]) -> None: self._name = name self._accessor = accessor