Source code for alchemist_lib.portfolio.portfolio

from abc import ABC, abstractmethod

from decimal import Decimal

from ..database.ptf_allocation import PtfAllocation
from ..database.instrument import Instrument

from .. import utils

from .. import datafeed



[docs]class PortfolioBaseClass(ABC): """ Abstract class used by modules that manage the portfolio costructor. Abstract methods: - normalize_weights(df): It has to return a dataframe (pandas.DataFrame) with the following columns: * asset (alchemist_lib.database.asset.Asset): Must be the index. * weight (decimal.Decimal): The weight of the specified asset in the portfolio. The sum of all weights in the dataframe must be near 100 (or 1). - set_allocation(session, name, df): It has to return a list of allocations (alchemist_lib.database.ptf_allocation.PtfAllocation) based on the type of portfolio you want. Attributes: capital (decimal.Decimal): Capital allocated for the portfolio. """
[docs] def __init__(self, capital): """ Costructor method. Args: capital (int, float, str, decimal.Decimal): Capital allocated for the portfolio. """ self.capital = Decimal(capital)
@abstractmethod def set_allocation(self, session, name, df): pass
[docs] def rebalance(self, curr_ptf, target_ptf): """ This method returns a list of PtfAllocation (alchemist_lib.database.ptf_allocation.PtfAllocation) that will be executed in order to mantain the portfolio rebalanced. Args: curr_ptf (alchemist_lib.database.ptf_allocation.PtfAllocation, list[PtfAllocation]): Current portfolio, loaded from the database. target_ptf (alchemist_lib.database.ptf_allocation.PtfAllocation, list[PtfAllocation]): Ideal portfolio. Return: new_ptf (list[alchemist_lib.database.ptf_allocation.PtfAllocation]): List of PtfAllocation to execute in order to get the ideal portfolio. """ curr_ptf = utils.to_list(curr_ptf) target_ptf = utils.to_list(target_ptf) new_ptf = [] found = False for new_alloc in target_ptf: found = False for old_alloc in curr_ptf: if new_alloc.asset == old_alloc.asset: allocation = PtfAllocation(amount = new_alloc.amount - old_alloc.amount, base_currency_amount = new_alloc.base_currency_amount - old_alloc.base_currency_amount, ticker = new_alloc.ticker, instrument_id = new_alloc.instrument_id, ts_name = new_alloc.ts_name) allocation.asset = new_alloc.asset allocation.ts = new_alloc.ts new_ptf.append(allocation) found = True if found == False: allocation = new_alloc.deepcopy() """PtfAllocation(amount = new_alloc.amount, base_currency_amount = new_alloc.base_currency_amount, ticker = new_alloc.ticker, instrument_id = new_alloc.instrument_id, ts_name = new_alloc.ts_name) """ allocation.asset = new_alloc.asset allocation.ts = new_alloc.ts new_ptf.append(allocation) for old_alloc in curr_ptf: found = False for new_alloc in target_ptf: if old_alloc.asset == new_alloc.asset: found = True if found == False: allocation = PtfAllocation(amount = old_alloc.amount * Decimal(-1), base_currency_amount = old_alloc.base_currency_amount * Decimal(-1), ticker = old_alloc.ticker, instrument_id = old_alloc.instrument_id, ts_name = old_alloc.ts_name) allocation.asset = old_alloc.asset allocation.ts = old_alloc.ts new_ptf.append(allocation) return new_ptf
[docs] def load_ptf(self, session, name): """ Load the current portfolio from the database. After that, It updates the base_currency_amount attribute. Args: session (sqlalchemy.orm.session.Session): Database connection. name (str): Name of the trading system which manages the portfolio. Return: allocs (list[PtfAllocation]): List of allocations of the specified trading system. """ allocs = session.query(PtfAllocation).filter(PtfAllocation.ts_name == name).all() assets = [alloc.asset for alloc in allocs] last_price = datafeed.get_last_price(assets = assets) for alloc in allocs: alloc.base_currency_amount = alloc.amount * last_price.loc[alloc.asset, "last_price"] return allocs