def copy_dict(dct: InConfigLikeT, copy_mode: str = 'shallow', nested: bool = True) -> OutConfigLikeT: """Copy dict based on a copy mode. The following modes are supported: * 'shallow': Copies keys only. * 'hybrid': Copies keys and values using `copy.copy`. * 'deep': Copies the whole thing using `copy.deepcopy`. Set `nested` to True to copy all child dicts in recursive manner.""" if dct is None: dct = {} checks.assert_type(copy_mode, str) copy_mode = copy_mode.lower() if copy_mode not in ['shallow', 'hybrid', 'deep']: raise ValueError(f"Copy mode '{copy_mode}' not supported") if copy_mode == 'deep': return deepcopy(dct) if isinstance(dct, Config): return dct.copy(copy_mode=copy_mode, nested=nested) dct_copy = copy(dct) # copy structure using shallow copy for k, v in dct_copy.items(): if nested and isinstance(v, dict): _v = copy_dict(v, copy_mode=copy_mode, nested=nested) else: if copy_mode == 'hybrid': _v = copy(v) # copy values using shallow copy else: _v = v set_dict_item(dct_copy, k, _v, force=True) return dct_copy
def update_dict(x: InConfigLikeT, y: InConfigLikeT, nested: bool = True, force: bool = False) -> None: """Update dict with keys and values from other dict. Set `nested` to True to update all child dicts in recursive manner. For `force`, see `set_dict_item`. If you want to treat any dict as a single value, wrap it with `atomic_dict`. !!! note If the child dict is not atomic, it will copy only its values, not its meta.""" if x is None: return if y is None: return checks.assert_type(x, dict) checks.assert_type(y, dict) for k, v in y.items(): if nested \ and k in x \ and isinstance(x[k], dict) \ and isinstance(v, dict) \ and not isinstance(v, atomic_dict): update_dict(x[k], v, force=force) else: set_dict_item(x, k, v, force=force)
def plot(self, tr_trace_kwargs={}, atr_trace_kwargs={}, fig=None, **layout_kwargs): """Plot `ATR.tr` and `ATR.atr`. Args: tr_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `ATR.tr`. atr_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `ATR.atr`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Returns: vectorbt.widgets.common.DefaultFigureWidget Example: ```py atr[(10, False)].plot() ``` ![](/vectorbt/docs/img/ATR.png)""" checks.assert_type(self.tr, pd.Series) checks.assert_type(self.atr, pd.Series) tr_trace_kwargs = {**dict( name=f'TR ({self.name})' ), **tr_trace_kwargs} atr_trace_kwargs = {**dict( name=f'ATR ({self.name})' ), **atr_trace_kwargs} fig = self.tr.vbt.timeseries.plot(trace_kwargs=tr_trace_kwargs, fig=fig, **layout_kwargs) fig = self.atr.vbt.timeseries.plot(trace_kwargs=atr_trace_kwargs, fig=fig, **layout_kwargs) return fig
def create_param_combs(op_tree: tp.Tuple, depth: int = 0) -> tp.List[tp.List]: """Create arbitrary parameter combinations from the operation tree `op_tree`. `op_tree` is a tuple with nested instructions to generate parameters. The first element of the tuple should be a callable that takes remaining elements as arguments. If one of the elements is a tuple itself and its first argument is a callable, it will be unfolded in the same way as above. ## Example ```python-repl >>> import numpy as np >>> from itertools import combinations, product >>> create_param_combs((product, (combinations, [0, 1, 2, 3], 2), [4, 5])) [[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2], [1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 3, 3], [4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5]] ``` """ checks.assert_type(op_tree, tuple) checks.assert_type(op_tree[0], Callable) new_op_tree: tp.Tuple = (op_tree[0],) for elem in op_tree[1:]: if isinstance(elem, tuple) and isinstance(elem[0], Callable): new_op_tree += (create_param_combs(elem, depth=depth + 1),) else: new_op_tree += (elem,) out = list(new_op_tree[0](*new_op_tree[1:])) if depth == 0: # do something return flatten_param_tuples(out) return out
def create_param_combs(op_tree, depth=0): """Create arbitrary parameter combinations from the operation tree `op_tree`. `op_tree` must be a tuple of tuples, each being an instruction to generate parameters. The first element of each tuple should a function that takes remaining elements as arguments. If one of the elements is a tuple, it will be unfolded in the same way. ## Example ```python-repl >>> import numpy as np >>> from itertools import combinations, product >>> create_param_combs((product, (combinations, [0, 1, 2, 3], 2), [4, 5])) [[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2], [1, 1, 2, 2, 3, 3, 2, 2, 3, 3, 3, 3], [4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5]] ``` """ checks.assert_type(op_tree, tuple) new_op_tree = (op_tree[0], ) for elem in op_tree[1:]: if isinstance(elem, tuple): new_op_tree += (create_param_combs(elem, depth=depth + 1), ) else: new_op_tree += (elem, ) out = list(new_op_tree[0](*new_op_tree[1:])) if depth == 0: # do something return flatten_param_tuples(out) return out
def plot(self, mstd_trace_kwargs={}, fig=None, **layout_kwargs): """Plot `MSTD.mstd`. Args: mstd_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `MSTD.mstd`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Returns: vectorbt.widgets.common.DefaultFigureWidget Example: ```py mstd[(10, False)].plot() ``` ![](/vectorbt/docs/img/MSTD.png)""" checks.assert_type(self.mstd, pd.Series) mstd_trace_kwargs = {**dict( name=f'MSTD ({self.name})' ), **mstd_trace_kwargs} fig = self.mstd.vbt.timeseries.plot(trace_kwargs=mstd_trace_kwargs, fig=fig, **layout_kwargs) return fig
def align_to(self, other): """Align to `other` by their indexes and columns. Example: ```python-repl >>> import pandas as pd >>> df1 = pd.DataFrame([[1, 2], [3, 4]], index=['x', 'y'], columns=['a', 'b']) >>> df2 = pd.DataFrame([[5, 6, 7, 8], [9, 10, 11, 12]], index=['x', 'y'], ... columns=pd.MultiIndex.from_arrays([[1, 1, 2, 2], ['a', 'b', 'a', 'b']])) >>> print(df1.vbt.align_to(df2)) 1 2 a b a b x 1 2 1 2 y 3 4 3 4 ```""" checks.assert_type(other, (pd.Series, pd.DataFrame)) obj = reshape_fns.to_2d(self._obj) other = reshape_fns.to_2d(other) aligned_index = index_fns.align_index_to(obj.index, other.index) aligned_columns = index_fns.align_index_to(obj.columns, other.columns) obj = obj.iloc[aligned_index, aligned_columns] return self.wrap_array(obj.values, index=other.index, columns=other.columns)
def plot(self, obv_trace_kwargs={}, fig=None, **layout_kwargs): """Plot `OBV.obv`. Args: obv_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `OBV.obv`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Returns: vectorbt.widgets.common.DefaultFigureWidget Example: ```py obv.plot() ``` ![](/vectorbt/docs/img/OBV.png)""" checks.assert_type(self.obv, pd.Series) obv_trace_kwargs = {**dict( name=f'OBV ({self.name})' ), **obv_trace_kwargs} fig = self.obv.vbt.timeseries.plot(trace_kwargs=obv_trace_kwargs, fig=fig, **layout_kwargs) return fig
def annual(self): """Annual returns.""" checks.assert_type(self._obj.index, DatetimeTypes) if self.freq == self.year_freq: return self._obj return self.resample_apply(self.year_freq, nb.total_return_apply_nb)
def daily(self, **kwargs) -> tp.SeriesFrame: """Daily returns.""" checks.assert_type(self.wrapper.index, DatetimeIndexes) if self.wrapper.freq == pd.Timedelta('1D'): return self._obj return self.resample_apply('1D', nb.total_return_apply_nb, **kwargs)
def make_symmetric(arg): """Make object symmetric along the diagonal.""" checks.assert_type(arg, (pd.Series, pd.DataFrame)) arg = to_2d(arg) checks.assert_same_type(arg.index, arg.columns) if isinstance(arg.index, pd.MultiIndex): checks.assert_same_len(arg.index.names, arg.columns.names) names1, names2 = tuple(arg.index.names), tuple(arg.columns.names) else: names1, names2 = arg.index.name, arg.columns.name if names1 == names2: new_name = names1 else: if isinstance(arg.index, pd.MultiIndex): new_name = tuple(zip(*[names1, names2])) else: new_name = (names1, names2) idx_vals = np.unique(np.concatenate((arg.index, arg.columns))) arg = arg.copy() if isinstance(arg.index, pd.MultiIndex): unique_index = pd.MultiIndex.from_tuples(idx_vals, names=new_name) arg.index.names = new_name arg.columns.names = new_name else: unique_index = pd.Index(idx_vals, name=new_name) arg.index.name = new_name arg.columns.name = new_name df_out = pd.DataFrame(index=unique_index, columns=unique_index) df_out.loc[:, :] = arg df_out[df_out.isnull()] = arg.transpose() return df_out
def __init__(self, scheduler: tp.Optional[AsyncScheduler] = None) -> None: if scheduler is None: scheduler = AsyncScheduler() checks.assert_type(scheduler, AsyncScheduler) self._scheduler = scheduler self._async_task = None
def merge_dicts(*dicts: tp.DictLike) -> dict: """Merge dicts.""" x, y = dicts[0], dicts[1] if x is None: x = {} if y is None: y = {} checks.assert_type(x, dict) checks.assert_type(y, dict) if len(x) == 0: z = y.copy() elif len(y) == 0: z = x.copy() else: z = {} overlapping_keys = [k for k in x if k in y] # order matters for k in overlapping_keys: if isinstance(x[k], dict) and isinstance( y[k], dict) and not isinstance(y[k], atomic_dict): z[k] = merge_dicts(x[k], y[k]) else: z[k] = y[k] for k in [k for k in x if k not in y]: z[k] = x[k] for k in [k for k in y if k not in x]: z[k] = y[k] if len(dicts) > 2: return merge_dicts(z, *dicts[2:]) return z
def __init__(self, wrapper, data, tz_localize=None, tz_convert=None, missing_index=None, missing_columns=None, download_kwargs=None, **kwargs): Wrapping.__init__(self, wrapper, data=data, tz_localize=tz_localize, tz_convert=tz_convert, missing_index=missing_index, missing_columns=missing_columns, download_kwargs=download_kwargs, **kwargs) checks.assert_type(data, dict) for k, v in data.items(): checks.assert_meta_equal(v, data[list(data.keys())[0]]) self._data = data self._tz_localize = tz_localize self._tz_convert = tz_convert self._missing_index = missing_index self._missing_columns = missing_columns self._download_kwargs = download_kwargs
def plot_pnl(self, profit_trace_kwargs={}, loss_trace_kwargs={}, fig=None, **layout_kwargs): """Plot position P/L as markers. Args: profit_trace_kwargs (dict): Keyword arguments passed to [`plotly.graph_objects.Scatter`](https://plotly.com/python-api-reference/generated/plotly.graph_objects.Scatter.html) for "Profit" markers. loss_trace_kwargs (dict): Keyword arguments passed to [`plotly.graph_objects.Scatter`](https://plotly.com/python-api-reference/generated/plotly.graph_objects.Scatter.html) for "Loss" markers. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Example: ```py portfolio = vbt.Portfolio.from_orders(price, price.diff(), init_capital=100) portfolio.positions.plot_pnl() ``` ![](/vectorbt/docs/img/positions_plot_pnl.png)""" checks.assert_type(self.pnl, pd.Series) above_trace_kwargs = {**dict(name='Profit'), **profit_trace_kwargs} below_trace_kwargs = {**dict(name='Loss'), **loss_trace_kwargs} return self.pnl.vbt.timeseries.plot_against( 0, above_trace_kwargs=above_trace_kwargs, below_trace_kwargs=below_trace_kwargs)
def from_order_func(cls, ts, order_func_np, *args, investment=None, slippage=None, commission=None): """Build portfolio based on order function.""" if investment is None: investment = defaults.portfolio['investment'] if slippage is None: slippage = defaults.portfolio['slippage'] if commission is None: commission = defaults.portfolio['commission'] checks.assert_type(ts, (pd.Series, pd.DataFrame)) ts.vbt.timeseries.validate() investment = float(investment) slippage = float(slippage) commission = float(commission) cash, shares = nb.portfolio_np(ts.vbt.to_2d_array(), investment, slippage, commission, order_func_np, *args) cash = ts.vbt.wrap_array(cash) shares = ts.vbt.wrap_array(shares) return cls(ts, cash, shares, investment, slippage, commission)
def __init__(self, wrapper, mapped_arr, col_arr, idx_arr=None): Configured.__init__( self, wrapper=wrapper, mapped_arr=mapped_arr, col_arr=col_arr, idx_arr=idx_arr ) checks.assert_type(wrapper, ArrayWrapper) if not isinstance(mapped_arr, np.ndarray): mapped_arr = np.asarray(mapped_arr) if not isinstance(col_arr, np.ndarray): col_arr = np.asarray(col_arr) checks.assert_shape_equal(mapped_arr, col_arr, axis=0) if idx_arr is not None: if not isinstance(idx_arr, np.ndarray): idx_arr = np.asarray(idx_arr) checks.assert_shape_equal(mapped_arr, idx_arr, axis=0) self._wrapper = wrapper self._mapped_arr = mapped_arr self._col_arr = col_arr self._idx_arr = idx_arr PandasIndexer.__init__(self, _mapped_array_indexing_func)
def traverse_attr_kwargs(cls, key=None, value=None): """Traverse `cls` and its children for properties/methods with `kwargs`, and optionally a specific `key` and `value`. Class attributes acting as children should have a key `child_cls`. Returns a nested dict of attributes.""" checks.assert_type(cls, type) if value is not None and not isinstance(value, tuple): value = (value, ) attrs = {} for attr in dir(cls): prop = getattr(cls, attr) if hasattr(prop, 'kwargs'): kwargs = getattr(prop, 'kwargs') if key is None: attrs[attr] = kwargs else: if key in kwargs: if value is None: attrs[attr] = kwargs else: _value = kwargs[key] if _value in value: attrs[attr] = kwargs if 'child_cls' in kwargs: child_cls = kwargs['child_cls'] checks.assert_type(child_cls, type) attrs[attr] = kwargs attrs[attr]['child_attrs'] = traverse_attr_kwargs( child_cls, key, value) return attrs
def daily(self): """Daily returns.""" checks.assert_type(self.index, DatetimeTypes) if self.freq == pd.Timedelta('1D'): return self._obj return self.resample_apply('1D', nb.total_return_apply_nb)
def update_data(self, value): """Update the data of the plot efficiently. Args: value (int or float): The value to be displayed. """ # NOTE: If called by Plotly event handler and in case of error, this won't visible in a notebook cell, but in logs! checks.assert_type(value, (int, float)) # Update value range if self._value_range is None: self._value_range = value, value else: self._value_range = min(self._value_range[0], value), max(self._value_range[1], value) # Update traces with self.batch_update(): indicator = self.data[0] if self._value_range is not None: indicator.gauge.axis.range = self._value_range if self._cmap_name is not None: indicator.gauge.bar.color = rgb_from_cmap( self._cmap_name, value, self._value_range) indicator.delta.reference = indicator.value indicator.value = value
def plot(self, ts_trace_kwargs={}, ma_trace_kwargs={}, fig=None, **layout_kwargs): """Plot `MA.ma` against `MA.ts`. Args: ts_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `MA.ts`. ma_trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Scatter` for `MA.ma`. fig (plotly.graph_objects.Figure): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. Returns: vectorbt.widgets.common.DefaultFigureWidget Example: ```py ma[(10, False)].plot() ``` ![](/vectorbt/docs/img/MA.png)""" checks.assert_type(self.ts, pd.Series) checks.assert_type(self.ma, pd.Series) ts_trace_kwargs = {**dict( name=f'Price ({self.name})' ), **ts_trace_kwargs} ma_trace_kwargs = {**dict( name=f'MA ({self.name})' ), **ma_trace_kwargs} fig = self.ts.vbt.timeseries.plot(trace_kwargs=ts_trace_kwargs, fig=fig, **layout_kwargs) fig = self.ma.vbt.timeseries.plot(trace_kwargs=ma_trace_kwargs, fig=fig, **layout_kwargs) return fig
def unstack_to_df(arg, index_levels=None, column_levels=None, symmetric=False, sort=True): """Reshape `arg` based on its multi-index into a DataFrame. Use `index_levels` to specify what index levels will form new index, and `column_levels` for new columns. Set `symmetric` to True to make DataFrame symmetric. ## Example ```python-repl >>> import pandas as pd >>> from vectorbt.base.reshape_fns import unstack_to_df >>> index = pd.MultiIndex.from_arrays( ... [[1, 1, 2, 2], [3, 4, 3, 4], ['a', 'b', 'c', 'd']], ... names=['x', 'y', 'z']) >>> sr = pd.Series([1, 2, 3, 4], index=index) >>> unstack_to_df(sr, index_levels=(0, 1), column_levels=2) z a b c d x y 1 3 1.0 NaN NaN NaN 1 4 NaN 2.0 NaN NaN 2 3 NaN NaN 3.0 NaN 2 4 NaN NaN NaN 4.0 ``` """ # Perform checks checks.assert_type(arg, (pd.Series, pd.DataFrame)) if checks.is_frame(arg): if arg.shape[0] == 1: arg = arg.iloc[0, :] elif arg.shape[1] == 1: arg = arg.iloc[:, 0] checks.assert_type(arg.index, pd.MultiIndex) sr = to_1d(arg) if len(sr.index.levels) > 2: if index_levels is None: raise ValueError("index_levels must be specified") if column_levels is None: raise ValueError("column_levels must be specified") else: index_levels = 0 column_levels = 1 # Build new index and column hierarchies new_index = index_fns.select_levels(arg.index, index_levels).unique() new_columns = index_fns.select_levels(arg.index, column_levels).unique() # Unstack and post-process unstacked = unstack_to_array(sr, levels=(index_levels, column_levels)) df = pd.DataFrame(unstacked, index=new_index, columns=new_columns) if symmetric: return make_symmetric(df, sort=sort) return df
def generate_stop_loss_exits(self, ts, stops, trailing=False, first=True, keys=None, broadcast_kwargs={}): """See `vectorbt.signals.nb.generate_stop_loss_exits_nb`. Arguments will be broadcasted using `vectorbt.base.reshape_fns.broadcast` with `broadcast_kwargs`. Argument `stops` can be either a single number, an array of numbers, or a 3D array, where each matrix corresponds to a single configuration. Use `keys` as the outermost level. Example: For each entry in `sig`, set stop loss for 10% and 20% below the entry price: ```python-repl >>> ts = pd.Series([1, 2, 3, 2, 1]) >>> print(sig.vbt.signals.generate_stop_loss_exits(ts, [0.1, 0.5])) stop_loss 0.1 0.5 a b c a b c 2020-01-01 False False False False False False 2020-01-02 False False False False False False 2020-01-03 False False False False False False 2020-01-04 False True True False False False 2020-01-05 False False False False False True >>> print(sig.vbt.signals.generate_stop_loss_exits(ts, [0.1, 0.5], trailing=True)) trail_stop 0.1 0.5 a b c a b c 2020-01-01 False False False False False False 2020-01-02 False False False False False False 2020-01-03 False False False False False False 2020-01-04 True True True False False False 2020-01-05 False False False True False True ```""" entries = self._obj checks.assert_type(ts, (pd.Series, pd.DataFrame)) entries, ts = reshape_fns.broadcast(entries, ts, **broadcast_kwargs, writeable=True) stops = reshape_fns.broadcast_to_array_of(stops, entries.vbt.to_2d_array()) exits = nb.generate_stop_loss_exits_nb(entries.vbt.to_2d_array(), ts.vbt.to_2d_array(), stops, trailing=trailing, first=first) # Build column hierarchy if keys is not None: param_columns = keys else: name = 'trail_stop' if trailing else 'stop_loss' param_columns = index_fns.index_from_values(stops, name=name) columns = index_fns.combine_indexes(param_columns, entries.vbt.columns) return entries.vbt.wrap(exits, columns=columns)
def test_assert_same_type(self): checks.assert_same_type(0, 1) checks.assert_same_type(np.zeros(1), np.empty(1)) try: checks.assert_type(0, np.zeros(1)) raise Exception except: pass
def annual(self, **kwargs) -> tp.SeriesFrame: """Annual returns.""" checks.assert_type(self._obj.index, DatetimeIndexes) if self.wrapper.freq == self.year_freq: return self._obj return self.resample_apply(self.year_freq, nb.total_return_apply_nb, **kwargs)
def select_levels(index, level_names): """Build a new index by selecting one or multiple `level_names` from `index`.""" checks.assert_type(index, pd.MultiIndex) if isinstance(level_names, (list, tuple)): levels = [index.get_level_values(level_name) for level_name in level_names] return pd.MultiIndex.from_arrays(levels) return index.get_level_values(level_names)
def __init__(self, main_price, init_capital, orders, cash, shares, freq=None, year_freq=None, levy_alpha=None, risk_free=None, required_return=None, cutoff=None, factor_returns=None, incl_unrealized_stats=False): # Perform checks checks.assert_type(main_price, (pd.Series, pd.DataFrame)) if checks.is_frame(main_price): checks.assert_type(init_capital, pd.Series) checks.assert_same(main_price.columns, init_capital.index) else: checks.assert_ndim(init_capital, 0) checks.assert_same_meta(main_price, cash) checks.assert_same_meta(main_price, shares) # Store passed arguments self._main_price = main_price self._init_capital = init_capital self._orders = orders self._cash = cash self._shares = shares self._incl_unrealized_stats = incl_unrealized_stats freq = main_price.vbt(freq=freq).freq if freq is None: raise ValueError( "Couldn't parse the frequency of index. You must set `freq`.") self._freq = freq year_freq = main_price.vbt.returns(year_freq=year_freq).year_freq if freq is None: raise ValueError("You must set `year_freq`.") self._year_freq = year_freq # Parameters self._levy_alpha = defaults.portfolio[ 'levy_alpha'] if levy_alpha is None else levy_alpha self._risk_free = defaults.portfolio[ 'risk_free'] if risk_free is None else risk_free self._required_return = defaults.portfolio[ 'required_return'] if required_return is None else required_return self._cutoff = defaults.portfolio[ 'cutoff'] if cutoff is None else cutoff self._factor_returns = defaults.portfolio[ 'factor_returns'] if factor_returns is None else factor_returns # Supercharge PandasIndexer.__init__(self, _indexing_func) self.wrapper = ArrayWrapper.from_obj(main_price, freq=freq)
def plot_position_profits(self, profit_trace_kwargs={}, loss_trace_kwargs={}, fig=None, **layout_kwargs): checks.assert_type(self.position_profits, pd.Series) profits = self.position_profits.copy() profits[self.position_profits <= 0] = np.nan losses = self.position_profits.copy() losses[self.position_profits >= 0] = np.nan # Set up figure if fig is None: fig = FigureWidget() fig.update_layout(showlegend=True) fig.update_layout(**layout_kwargs) # Plot markets profit_scatter = go.Scatter(x=self.position_profits.index, y=profits, mode='markers', marker=dict(symbol='circle', color='green', size=10), name='Profit') profit_scatter.update(**profit_trace_kwargs) fig.add_trace(profit_scatter) loss_scatter = go.Scatter(x=self.position_profits.index, y=losses, mode='markers', marker=dict(symbol='circle', color='red', size=10), name='Loss') loss_scatter.update(**loss_trace_kwargs) fig.add_trace(loss_scatter) # Set up axes maxval = np.nanmax(np.abs(self.position_profits.vbt.to_2d_array())) space = 0.1 * 2 * maxval fig.update_layout(yaxis=dict(range=[-(maxval + space), maxval + space]), shapes=[ dict(type="line", xref="paper", yref='y', x0=0, x1=1, y0=0, y1=0, line=dict( color="grey", width=2, dash="dot", )) ]) return fig
def __add__(self, other): checks.assert_type(other, self.__class__) checks.assert_same(self.price, other.price) return self.__class__(self.price, self.cash + other.cash, self.shares + other.shares, self.init_capital + other.init_capital, self.paid_fees + other.paid_fees, self.paid_slippage + other.paid_slippage)
def __init__(self, obj, mapper): checks.assert_type(mapper, pd.Series) self.obj = obj if mapper.dtype == 'O': # If params are objects, we must cast them to string first # The original mapper isn't touched mapper = mapper.astype(str) self.mapper = mapper