Source code for richy.transactions.dividends

import logging
from datetime import date

from ..core.models import User
from .models import (
    EtfDividendTransaction,
    ShareDividendTransaction,
    Transaction,
)
from .transactions import Transactions

LOGGER = logging.getLogger(__name__)


[docs] class DividendTransactions: """ This class handles ``ShareDividendTransaction`` or ``EtfDividendTransaction`` model creation/update. It calculates based on ``Transaction`` (currently owned stocks) and ``shares.Dividend`` (dividend data) transactions that are income for share holder. """ def __init__(self, *, share=None, etf=None): """ :param Share share. Share model instance. :param Etf etf. Etf model instance. """ if share is None and etf is None: raise Exception("share or etf parameters must be provided.") if share: self.item = share self.df_item_type = "share" self.dividend_model = ShareDividendTransaction else: self.item = etf self.df_item_type = "etf" self.dividend_model = EtfDividendTransaction
[docs] def calculate(self): """ Calculates (creates/updates) ``ShareDividendTransaction`` or ``EtfDividendTransaction`` model records based on owned stocks now and in past. Also recalculates/removes already created transaction in the past. """ # Fetch only those users that have at least one transaction. for user in User.objects.filter( pk__in=Transaction.objects.distinct("user").values_list("user", flat=True) ): dividends = self.item.dividend_set.filter( record_date__lte=date.today() ).order_by("record_date") df = Transactions(user).get_transaction_basic_stats() if df.empty: LOGGER.debug( "Empty dataframe (no transactions) - terminating dividend calculation." ) return df = df[df["type"] == self.df_item_type] for div in dividends: df_filtered = df[df["date"] <= div.payment_date] df_filtered = df_filtered[df_filtered["symbol"] == self.item.symbol] # If no shares were held at the time dividend ex date we skip it. if df_filtered.empty: continue # Calculate currently (div.payment_date) owned shares. df_filtered_uniq = df_filtered.groupby(df_filtered.index).first() owned_shares = df_filtered_uniq.amount.sum() # If some were owned. if owned_shares > 0: record, created = self.dividend_model.objects.update_or_create( user=user, dividend=div, defaults={ "shares": owned_shares, "amount": div.amount_value * owned_shares, }, ) record.transactions.set(df_filtered_uniq.index.to_list()) if created: LOGGER.debug( "Dividend transaction has been calculated for " f"{self.item} for date {div.payment_date} for user with ID {user.pk}." ) else: LOGGER.debug( "Dividend transaction has been recalculated for " f"{self.item} for date {div.payment_date} for user with ID {user.pk}." ) else: try: self.dividend_model.objects.by_user(user).get( dividend=div ).delete() LOGGER.debug( f"Dividend transaction for share {self.item.symbol} " f"and {div.payment_date} for user with ID {user.pk} has been deleted." ) except self.dividend_model.DoesNotExist: pass