Currency — Django Hordak 1.0 documentation

# Currency

Contents

- Currency
  - Overview
  - Classes
  - Caching
  - Currency Exchange
  - Balance
  - Exchange Rate Backends

## Overview

Hordak features multi currency support. Each account in Hordak can support one or more currencies. Hordak does provide currency conversion functionality, but should be as part of the display logic only. It is also a good idea to make it clear to users that you are showing converted values.

The preference for Hordak internals is to always store & process values in the intended currency. This is because currency conversion is an inherently lossy process. Exchange rates vary over time, and rounding errors mean that currency conversions are not reversible without data loss (e.g. ¥176.51 -> $1.54 -> ¥176.20).

## Classes

`Money` instances:

> The `Money` class is provided by moneyd and combines both an amount and a currency into a single value. Hordak uses these these as the core unit of monetary value.

`Balance` instances (see below for more details):

> An account can hold multiple currencies, and a Balance instance is how we represent this.

> A Balance may contain one or more `Money` objects. There will be precisely one `Money` object for each currency which the account holds.

> Balance objects may be added, subtracted etc. This will produce a new Balance object containing a union of all the currencies involved in the calculation, even where the result was zero.

> Accounts with `is_bank_account=True` may only support a single currency.

## Caching

Currency conversion makes use of Django’s cache. It is therefore recommended that you setup your Django cache to something other than the default in-memory store.

## Currency Exchange

The `currency_exchange()` helper function is provided to assist in creating currency conversion Transactions.

`hordak.utilities.currency.` `currency_exchange`(source, source_amount, destination, destination_amount, trading_account, fee_destination=None, fee_amount=None, date=None, description=None)

  Exchange funds from one currency to another

  Use this method to represent a real world currency transfer. Note this process doesn’t care about exchange rates, only about the value of currency going in and out of the transaction.

  You can also record any exchange fees by syphoning off funds to `fee_account` of amount `fee_amount`. Note that the free currency must be the same as the source currency.

  Examples

  For example, imagine our Canadian bank has obligingly transferred 120 CAD into our US bank account. We sent CAD 120, and received USD 100. We were also changed 1.50 CAD in fees.

  We can represent this exchange in Hordak as follows:

      from hordak.utilities.currency import currency_exchange

      currency_exchange(
          # Source account and amount
          source=cad_cash,
          source_amount=Money(120, 'CAD'),
          # Destination account and amount
          destination=usd_cash,
          destination_amount=Money(100, 'USD'),
          # Trading account the exchange will be done through
          trading_account=trading,
          # We also incur some fees
          fee_destination=banking_fees,
          fee_amount=Money(1.50, 'CAD')
      )

  We should now find that:

  > 1. `cad_cash.balance()` has decreased by `CAD 120`
  > 2. `usd_cash.balance()` has increased by `USD 100`
  > 3. `banking_fees.balance()` is `CAD 1.50`
  > 4. `trading_account.balance()` is `USD 100, CAD -120`

  You can perform `trading_account.normalise()` to discover your unrealised gains/losses on currency traded through that account.

  Parameters: | source (Account) – The account the funds will be taken from source_amount (Money) – A `Money` instance containing the inbound amount and currency. destination (Account) – The account the funds will be placed into destination_amount (Money) – A `Money` instance containing the outbound amount and currency trading_account (Account) – The trading account to be used. The normalised balance of this account will indicate gains/losses you have made as part of your activity via this account. Note that the normalised balance fluctuates with the current exchange rate. fee_destination (Account) – Your exchange may incur fees. Specifying this will move incurred fees into this account (optional). fee_amount (Money) – The amount and currency of any incurred fees (optional). description (str) – Description for the transaction. Will default to describing funds in/out & fees (optional). date (datetime.date) – The date on which the transaction took place. Defaults to today (optional).
  Returns: | The transaction created
  Return type: | (Transaction)

  See also

  You can see the above example in practice in `CurrencyExchangeTestCase.test_fees` in test_currency.py.

## Balance

class `hordak.utilities.currency.` `Balance`(_money_obs=None, *args)

  An account balance

  Accounts may have multiple currencies. This class represents these multi-currency balances and provides math functionality. Balances can be added, subtracted, multiplied, divided, absolute’ed, and have their sign changed.

  Examples

  Example use:

      Balance([Money(100, 'USD'), Money(200, 'EUR')])

      # Or in short form
      Balance(100, 'USD', 200, 'EUR')

  Important

  Balances can also be compared, but note that this requires a currency conversion step. Therefore it is possible that balances will compare differently as exchange rates change over time.

  `monies`()

    Get a list of the underlying `Money` instances

    Returns: | A list of zero or money money instances. Currencies will be unique.
    Return type: | ([Money])

  `currencies`()

    Get all currencies with non-zero values

  `normalise`(to_currency)

    Normalise this balance into a single currency

    Parameters: | to_currency (str) – Destination currency
    Returns: | A new balance object containing a single Money value in the specified currency
    Return type: | (Balance)

## Exchange Rate Backends

class `hordak.utilities.currency.` `BaseBackend`

  Top-level exchange rate backend

  This should be extended to hook into your preferred exchange rate service. The primary method which needs defining is `_get_rate()`.

  `cache_rate`(currency, date, rate)

    Cache a rate for future use

  `get_rate`(currency, date)

    Get the exchange rate for `currency` against `_INTERNAL_CURRENCY`

    If implementing your own backend, you should probably override `_get_rate()` rather than this.

  `_get_rate`(currency, date)

    Get the exchange rate for `currency` against `INTERNAL_CURRENCY`

    You should implement this in any custom backend. For each rate you should call `cache_rate()`.

    Normally you will only need to call `cache_rate()` once. However, some services provide multiple exchange rates in a single response, in which it will likely be expedient to cache them all.

    Important

    Not calling `cache_rate()` will result in your backend service being called for every currency conversion. This could be very slow and may result in your software being rate limited (or, if you pay for your exchange rates, you may get a big bill).

class `hordak.utilities.currency.` `FixerBackend`

  Use fixer.io for currency conversions