Source code for portfoliofinder.portfolio.returns

import pandas as pd

from ..util.progressbar import progressbar
from ..util.self_pickling import SelfPickling
from ..util.to_dataframe import to_dataframe
from .backtested_timeframes import BacktestedTimeframes
from .backtested_values import BacktestedValues
from typing import Dict
from ..contributions import *


[docs]class Returns(SelfPickling): """Portfolio returns by year for a set of allocation mixes.""" def __init__(self, returns_by_symbol: pd.DataFrame, allocations: pd.DataFrame, use_progressbar: bool = False): returns_by_symbol = returns_by_symbol[allocations.columns] self._portfolio_returns_by_allocation = {} allocations_tuples = allocations.itertuples(name="Allocation", index=False) for allocation in progressbar(allocations_tuples, use_progressbar): portfolio_returns = _get_portfolio_returns( allocation, returns_by_symbol) self._portfolio_returns_by_allocation[allocation] = portfolio_returns def __repr__(self): return self.to_dataframe().__repr__() def __str__(self): return self.to_dataframe().__str__()
[docs] def to_dataframe(self) -> pd.DataFrame: """Converts this to a pandas DataFrame.""" return to_dataframe( self._portfolio_returns_by_allocation)
[docs] def get_series(self, allocation) -> pd.Series: """Gets the portfolio returns as a pandas Series for a given allocation. :param allocation: allocation to get returns for """ return self._portfolio_returns_by_allocation[allocation]
[docs] def with_contributions(self, contributions: Contributions = DEFAULT_CONTRIBUTION): """Add contributions to these portfolio returns. :param contributions: contribution schedule :return: portfolio returns with contributions """ return ReturnsWithContributions(self._portfolio_returns_by_allocation, contributions)
[docs] def with_initial_contribution(self, starting_value: float): """Add an initial contribution to these portfolio returns. :param starting_value: initial contribution at inception of portfolio """ return self.with_contributions(InitialContribution(starting_value))
[docs] def with_regular_contributions(self, starting_value: float, annual_contribution: float): """Add regular contributions to these portfolio returns. :param starting_value: initial contribution made at portfolio inception :param annual_contribution: subsequent annual contributions """ return self.with_contributions(RegularContributions(starting_value, annual_contribution))
[docs] def with_scheduled_contributions(self, scheduled_contributions: Dict[int, float]): """Add scheduled contributions to these portfolio returns. :param scheduled_contributions: contributions by year relative to inception of portfolio """ return self.with_contributions(ScheduledContributions(scheduled_contributions))
class ReturnsWithContributions(Returns): """Portfolio returns by year for a set of allocation mixes, along with a contributions schedule. """ def __init__(self, portfolio_returns_by_allocation, contributions: Contributions): self._portfolio_returns_by_allocation = portfolio_returns_by_allocation self._contributions = contributions def get_backtested_values(self, timeframe: int, use_progressbar: bool = False) -> BacktestedValues: """Calculates final portfolio values, by start year, after a fixed timeframe. :param timeframe: timeframe in years :param use_progressbar: whether are not to display a progressbar to provide the status of large calculations :return: portfolio values, by start year, after a fixed timeframe """ return BacktestedValues( self._portfolio_returns_by_allocation, timeframe, use_progressbar, self._contributions) def get_backtested_timeframes(self, target_value: float, use_progressbar: bool = False) -> BacktestedTimeframes: """Calculates portfolio timeframes, by start year, required to achieve a target value. :param target_value: portfolio value to target :param use_progressbar: whether are not to display a progressbar to provide the status of large calculations :return: portfolio values, by start year, after a fixed timeframe """ return BacktestedTimeframes( self._portfolio_returns_by_allocation, target_value, use_progressbar, self._contributions) def _get_portfolio_returns(portfolio_allocation, returns_by_symbol: pd.DataFrame) -> pd.Series: portfolio_returns = [] for row in returns_by_symbol.iterrows(): return_by_symbol = row[1] return_for_year = sum(return_by_symbol * portfolio_allocation) portfolio_returns.append(return_for_year) years = returns_by_symbol.axes[0] return pd.Series(portfolio_returns, index=years, name="Portfolio Return")