def debt_vs_liquid_assets(credit_accounts=None, liquid_asset_accounts=None):
    """
    Provides a report showing the balances of credit_accounts and liquid_asset_accounts.  This data can be used to show
    if there is enough liquid assets to be able to cover credit card debt.
    :param credit_accounts: account walking parameters for all of the credit accounts to be used in this report.
    :param liquid_asset_accounts: account walking parameters for all of the liquid asset accounts to be used in this
    report.
    :return: dictionary showing results
    - credit_used - currency amount showing how much credit is being used.
    - liquid_assets - currency amount showing how much is in the liquid asset_accounts
    """
    credit_accounts = parse_walker_parameters(credit_accounts)
    liquid_accounts = parse_walker_parameters(liquid_asset_accounts)
    credit_used = Decimal('0.0')
    liquid_assets = Decimal('0.0')

    currency = get_currency()

    for credit_account in account_walker(**credit_accounts):
        # Multiplying by account.sign because if this is a liability account, the value is negative.  For the report
        # it should be a positive value because it shows that there is credit used.
        credit_used += get_balance_on_date(credit_account,
                                           PeriodStart.today.date,
                                           currency) * credit_account.sign

    for liquid_asset_account in account_walker(**liquid_accounts):
        liquid_assets += get_balance_on_date(liquid_asset_account,
                                             PeriodStart.today.date, currency)

    return {'credit_used': credit_used, 'liquid_assets': liquid_assets}
Beispiel #2
0
def account_levels(account=None,
                   when=PeriodStart.today,
                   goal=5000,
                   warn_value=2500,
                   error_value=1000):
    """
    Build a simple stacked bar chart that shows a progress bar of the data.
    :param account: full account name of which account to get the balance from.
    :param when: PeriodStart enumeration containing when to calculate the value from.
    :param goal: this is the goal for the account, any thing less than this is considered an underage, but not
    necessary warning worthy.
    :param warn_value: if balance is less than this value, it should be displayed as a "warning"
    :param error_value: if balance is less than this value, it should be displayed as an "error"
    :return: dictionary containing:
    balance - the balance of the account as of 'when'
    good_value - the goal value from configuration
    warn_value - the warn value from configuration
    error_Value - the error value from configuration
    """
    when = PeriodStart(when)

    balance = get_balance_on_date(get_account(account), when.date)

    return {
        'balance': balance,
        'goal': goal,
        'warn_value': warn_value,
        'error_value': error_value
    }
def credit_usage(credit_accounts=None):
    """
    Report generates a usage vs limit data set for the credit accounts that are defined.

    definition contains:
    :param credit_accounts: list of dictionaries containing credit card accounts and limits on those accounts
      - account - full name of the account to look up balance of.
      - limit - maximum amount that can be debited from that account.
    :return: dictionary containing:
    - credit_limit - total limit from all accounts
    - credit_amount - the amount of credit used on all accounts
    """
    credit_accounts = credit_accounts or []

    credit_limit = Decimal(0.0)
    credit_used = Decimal(0.0)

    for credit_definition in credit_accounts:
        account = get_account(credit_definition['account'])
        limit = credit_definition.get('limit', '0.0')

        balance = get_balance_on_date(account)
        credit_used -= balance
        credit_limit += Decimal(limit)

    return {'credit_limit': credit_limit, 'credit_amount': credit_used}
Beispiel #4
0
def investment_allocation(investment_accounts=None):
    """
    Investment allocation report.  Walks through all of the investment accounts and determines the breakdowns of the
    assets based on the category mapping data.
    :param investment_accounts: the accounts to walk through when calculation asset breakdown
    :return: dictionary containing
    categories - list of tuples containing human readable term key to the value contained in the investment accounts.
    """
    investment_accounts = investment_accounts or []

    investment_accounts = parse_walker_parameters(investment_accounts)

    breakdown = dict()
    today = get_today()
    currency = get_currency()

    for account in account_walker(**investment_accounts):
        balance = get_balance_on_date(account, today, currency)
        commodity = account.commodity.mnemonic

        results = get_asset_allocation(commodity, balance)

        for key, value in results.iteritems():
            breakdown[key] = breakdown.get(key, Decimal('0.0')) + value

    return dict(categories=sorted([[key.replace('_', ' ').title(), value]
                                   for key, value in breakdown.iteritems()],
                                  key=itemgetter(0)))
def savings_goal(savings=None,
                 goal='0.0',
                 as_of=PeriodStart.today,
                 contributions=None):
    """
    Report that shows progress towards a savings goal.
    :param savings: account walker parameters for the accounts that should have their balance count towards the goal
    :param goal: the amount that should be in the account for the goal to be met (in currency)
    :param as_of: when should the balance be looked up for
    :param contributions: list of additional contributions towards the goal which are not apart of the account
    :return: dictionary containing:
    balance - balance of the account and the contributions
    goal - the goal for the account to be at
    """
    savings = savings or []

    walker_params = parse_walker_parameters(savings)

    goal_amount = Decimal(goal)

    as_of = PeriodStart(as_of)
    contributions = contributions or []

    if not isinstance(contributions, list):
        contributions = [contributions]

    total_balance = Decimal('0.0')
    currency = get_currency()

    for account in account_walker(**walker_params):
        balance = get_balance_on_date(account, as_of.date, currency)
        total_balance += balance

    for contribution in contributions:
        total_balance += Decimal(contribution)

    return {'balance': total_balance, 'goal': goal_amount}
def net_worth(assets=None, liabilities=None, start=PeriodStart.this_month_year_ago,
              end=PeriodEnd.today, period_size=PeriodSize.month):
    """
    Create a graph that will calculate the assets, liabilities and net asset changes in values over time.
    :param assets: account walker parameters for the accounts containing assets
    :param liabilities: account walker parameters for the accounts containing liabilities
    :param start: the start of the period for collecting the data
    :param end: the end of the period for collecting data
    :param period_size: the size of the steps between start and end
    :return: dictionary containing:
    assets - time value data containing date and value keys
    liabilities - time value data containing date and liability keys
    net - time value data containing date and net keys
    inflation - time value data containing the inflation increase based on the net value at the start of the period
    """
    assets = assets or []
    liabilities = liabilities or []

    assets = parse_walker_parameters(assets)
    liabilities = parse_walker_parameters(liabilities)

    period_start = PeriodStart(start)
    period_end = PeriodEnd(end)
    period_size = PeriodSize(period_size)

    start_of_trend = period_start.date
    end_of_trend = period_end.date

    asset_bucket = PeriodCollate(start_of_trend, end_of_trend, decimal_generator, split_summation,
                                 frequency=period_size.frequency, interval=period_size.interval)
    liability_bucket = PeriodCollate(start_of_trend, end_of_trend, decimal_generator, split_summation,
                                     frequency=period_size.frequency, interval=period_size.interval)
    net_bucket = PeriodCollate(start_of_trend, end_of_trend, decimal_generator, split_summation,
                               frequency=period_size.frequency, interval=period_size.interval)

    currency = get_currency()

    # Calculate the asset balances
    for account in account_walker(**assets):
        for key, value in asset_bucket.container.iteritems():
            balance = get_balance_on_date(account, key, currency)
            asset_bucket.container[key] += balance

    # Calculate the liability balances
    for account in account_walker(**liabilities):
        for key, value in liability_bucket.container.iteritems():
            balance = get_balance_on_date(account, key, currency)
            liability_bucket.container[key] += balance

    # Now calculate the net values from the difference.
    for key, value in liability_bucket.container.iteritems():
        net_bucket.container[key] = asset_bucket.container[key] + liability_bucket.container[key]

    assets = time_series_dict_to_list(asset_bucket.container)
    # Convert the liabilities to positive values to show the amount of liabilities
    liabilities = time_series_dict_to_list(liability_bucket.container, value=lambda s: -s)
    net = time_series_dict_to_list(net_bucket.container)

    inflation = get_monthly_inflation()
    starting_point = None
    inflation_data = []
    for record in net:
        if starting_point:
            starting_point += (starting_point * inflation)
        else:
            starting_point = record[1]

        inflation_data.append((record[0], starting_point))

    inflation = inflation_data

    return {'assets': assets, 'liabilities': liabilities, 'net': net, 'inflation': inflation}
def _calculate_payload(account_list, delta_months, trend_months):
    """
    Calculate the delta and trend values for the account list provided.
    :param account_list: account walker parameters with additional field 'name'
    :param delta_months: list of delta months to calculate
    :param trend_months: list of trend months to calculate
    :return: dictionary containing
    records - the list of sub accounts stored in the report containing
        name - the name of the record
        current_data - the current value in the account
        deltas - a list of the value in the account for the deltas
        trend - a list of the value in the account for the trend query
    current_data - the current value of all the records
    deltas - the list of the deltas for all the records
    trend - the list of the trends for all the records
    """
    currency = get_currency()
    today = date.today()
    end_of_month = date(today.year, today.month, monthrange(today.year, today.month)[1])
    total_data = {'records': [], 'current_data': Decimal('0.0'), 'deltas': [Decimal('0.0')] * len(delta_months),
                  'delta_sub_total': [Decimal('0.0')] * len(delta_months),
                  'trend': [Decimal('0.0')] * len(trend_months)}

    for definition in account_list:
        definition_data = {'name': definition['name'], 'current_data': Decimal('0.0'),
                           'deltas': [Decimal('0.0')] * len(delta_months),
                           'trend': [Decimal('0.0')] * len(trend_months)}

        # Get Current Data first
        for account in account_walker(**parse_walker_parameters(definition)):
            balance = get_balance_on_date(account, end_of_month, currency)
            definition_data['current_data'] += balance
            total_data['current_data'] += balance

        # Calculate the trends
        for index, trend in enumerate(trend_months):
            for account in account_walker(**parse_walker_parameters(definition)):
                balance = get_balance_on_date(account, trend, currency)
                definition_data['trend'][index] += balance
                total_data['trend'][index] += balance

        # Calculate deltas
        for index, delta in enumerate(delta_months):
            value = Decimal(0.0)
            for account in account_walker(**parse_walker_parameters(definition)):
                balance = get_balance_on_date(account, delta, currency)
                value += balance

                # Store the balance in the subtotal delta as well so don't have to fetch the data again.
                total_data['delta_sub_total'][index] += balance

            try:
                delta = definition_data['current_data'] - value
                definition_data['deltas'][index] = (delta / value).copy_sign(delta)
            except:
                definition_data['deltas'][index] = 'N/A'

        total_data['records'].append(definition_data)

    # Calculate the deltas for the total values.
    for index, value in enumerate(total_data['delta_sub_total']):
        try:
            delta = total_data['current_data'] - value
            total_data['deltas'][index] = (delta / value).copy_sign(delta)
        except:
            total_data['deltas'][index] = 'N/A'

    return total_data
Beispiel #8
0
def investment_balance(account):
    """
    Generate report that calculates purchases, dividends, and current worth of the investment accounts provided.
    :param account: investment account definition
    :return: dictionary giving time series for the following information
    value - date/value tuples containing value in currency
    purchases - date/value tuples containing purchase information in currency
    dividend - date/value tuples containing dividend information in currency
    """
    # TODO: Change this to be account walker parameters
    account = get_account(account)

    last_dividend = Decimal('0.0')
    last_purchase = Decimal('0.0')

    currency = get_currency()

    purchases = dict()
    dividends = dict()
    values = dict()

    for split in sorted(account.splits, key=lambda x: x.transaction.post_date):
        other_account_name = get_corr_account_full_name(split)
        other_account = get_account(other_account_name)

        account_type = AccountTypes(other_account.type.upper())
        date = split.transaction.post_date

        # Store previous data
        if len(purchases):
            previous_date = date - relativedelta(days=1)
            purchases[previous_date] = last_purchase
            dividends[previous_date] = last_dividend
            values[previous_date] = get_balance_on_date(
                account, previous_date, currency)

        # Find the correct amount that was paid from the account into this account.
        change_amount = split.value

        if change_amount > 0:
            # Need to get the value from the corr account split.
            for parent_splits in split.transaction.splits:
                if parent_splits.account.fullname == other_account_name:
                    change_amount = -parent_splits.value

        if account_type == AccountTypes.mutual_fund or account_type == AccountTypes.asset:
            # Asset or mutual fund transfer
            last_purchase += change_amount
        else:
            last_dividend += split.value

        purchases[date] = last_purchase
        dividends[date] = last_dividend
        values[date] = get_balance_on_date(account, date, currency)

    # Sort the purchases and dividends so can keep track of purchases and dividends while the current value is
    # being put into place.  Because this is not the final value of the list, don't need to worry about converting the
    # dates into floats.
    sorted_purchases = time_series_dict_to_list(purchases, key=None)
    sorted_dividend = time_series_dict_to_list(dividends, key=None)

    # Index of where in the sorted purchases and dividends we are when updating the values
    sorted_index = 0
    sorted_length = len(sorted_purchases)

    # Now get all of the price updates in the database.
    for price in get_prices(account.commodity, currency):
        date = price.date

        # Find out where in the purchases/dividends this record would fall by iterating through the sorted list until
        # we pass the current date value, then use the previous key
        test_sorted_index = sorted_index
        while test_sorted_index < sorted_length and sorted_purchases[
                test_sorted_index][0] <= date:
            test_sorted_index += 1

        # Skip the first record, it's already handled by the purchase population code.  Otherwise put in placeholder
        # data so that the number of records is the same in all of the data structures.
        if test_sorted_index != 0:
            purchases[date] = sorted_purchases[test_sorted_index - 1][1]
            dividends[date] = sorted_dividend[test_sorted_index - 1][1]
            values[date] = max(
                values.get(date, Decimal('0.0')),
                get_balance_on_date(account, price.date, currency))

    # Resort all of the dictionaries into the appropriate pairs so that the data is displayed correctly by the
    # viewer.
    values = time_series_dict_to_list(values)
    purchases = time_series_dict_to_list(purchases)
    dividend = time_series_dict_to_list(dividends)

    return {'purchases': purchases, 'dividend': dividend, 'value': values}
Beispiel #9
0
def investment_trend(investment_accounts=None,
                     start=PeriodStart.this_month_year_ago,
                     end=PeriodEnd.this_month,
                     period_size=PeriodSize.month):
    """
    Report showing how the investment has changed over a period of time.  This report provides data based on the period
    size provided in the arguments.
    :param investment_accounts: account walker parameters for all of the accounts that this report should provide
    data on
    :param start: the start time frame of the report
    :param end: the end time frame of the report
    :param period_size: the step size between start and end
    :return: dictionary containing the following
    start_value - no clue
    income - time series data containing the income generated by account (dividends)
    money_in - time series data containing the purchases into the accounts
    expense - time series data containing the expenses of the accounts
    value - time series data containing the current value of the accounts
    basis - time series data containing the basis value of the accounts (not sure if this is correct)
    """

    # TODO: Figure out what this report is really doing with start_value and basis values. Verify they are calculated
    # correctly.
    investment_accounts = investment_accounts or []

    investment_accounts = parse_walker_parameters(investment_accounts)
    period_start = PeriodStart(start)
    period_end = PeriodEnd(end)
    period_size = PeriodSize(period_size)

    investment_value = dict()
    buckets = PeriodCollate(period_start.date,
                            period_end.date,
                            investment_bucket_generator,
                            store_investment,
                            frequency=period_size.frequency,
                            interval=period_size.interval)

    start_value = Decimal('0.0')
    start_value_date = period_start.date - relativedelta(days=1)
    currency = get_currency()

    for account in account_walker(**investment_accounts):
        for split in get_splits(account, period_start.date, period_end.date):
            buckets.store_value(split)

        start_value += get_balance_on_date(account, start_value_date, currency)

        for key in buckets.container.keys():
            date_value = key + relativedelta(months=1) - relativedelta(days=1)
            investment_value[key] = investment_value.get(
                key, Decimal('0.0')) + get_balance_on_date(
                    account, date_value, currency)

    results = {
        'start_value':
        start_value,
        'income':
        time_series_dict_to_list(buckets.container,
                                 value=lambda x: x['income']),
        'money_in':
        time_series_dict_to_list(buckets.container,
                                 value=lambda x: x['money_in']),
        'expense':
        time_series_dict_to_list(buckets.container,
                                 value=lambda x: x['expense']),
        'value':
        time_series_dict_to_list(investment_value),
        'basis':
        sorted([[time.mktime(key.timetuple()),
                 Decimal('0.0')] for key in buckets.container.keys()],
               key=itemgetter(0))
    }

    monthly_start = start_value
    for index, record in enumerate(results['basis']):
        record[1] += (monthly_start + results['income'][index][1] +
                      results['money_in'][index][1] +
                      results['expense'][index][1])
        monthly_start = record[1]

    return results