Skip to content

Commit

Permalink
currency_exchange() can now charge fees in the desination currency, #56
Browse files Browse the repository at this point in the history
  • Loading branch information
adamcharnock committed May 27, 2024
1 parent e8d320c commit 3d401a3
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 10 deletions.
23 changes: 22 additions & 1 deletion hordak/tests/utilities/test_currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def test_peter_selinger_tutorial_table_4_4(self):
self.assertEqual(cad_cash.balance(), Balance(135, "CAD"))
self.assertEqual(food.balance(), Balance(72, "CAD"))

def test_fees(self):
def test_fees_source_currency(self):
cad_cash = self.account(type=Account.TYPES.asset, currencies=["CAD"])
usd_cash = self.account(type=Account.TYPES.asset, currencies=["USD"])
initial_capital = self.account(type=Account.TYPES.equity, currencies=["CAD"])
Expand All @@ -385,3 +385,24 @@ def test_fees(self):
self.assertEqual(cad_cash.balance(), Balance(80, "CAD"))
self.assertEqual(usd_cash.balance(), Balance(100, "USD"))
self.assertEqual(banking_fees.balance(), Balance(1.50, "CAD"))

def test_fees_destination_currency(self):
cad_cash = self.account(type=Account.TYPES.asset, currencies=["CAD"])
usd_cash = self.account(type=Account.TYPES.asset, currencies=["USD"])
initial_capital = self.account(type=Account.TYPES.equity, currencies=["CAD"])
trading = self.account(type=Account.TYPES.trading, currencies=["CAD", "USD"])
banking_fees = self.account(type=Account.TYPES.expense, currencies=["USD"])

initial_capital.transfer_to(cad_cash, Money(200, "CAD"))
currency_exchange(
source=cad_cash,
source_amount=Money(120, "CAD"),
destination=usd_cash,
destination_amount=Money(100, "USD"),
trading_account=trading,
fee_destination=banking_fees,
fee_amount=Money(1.50, "USD"),
)
self.assertEqual(cad_cash.balance(), Balance(80, "CAD"))
self.assertEqual(usd_cash.balance(), Balance(100, "USD"))
self.assertEqual(banking_fees.balance(), Balance(1.50, "USD"))
30 changes: 21 additions & 9 deletions hordak/utilities/currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,12 @@ def currency_exchange(
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.
``fee_account`` of amount ``fee_amount``.
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 sent CAD 120, and received USD 100. We were also charged 1.50 CAD in fees.
We can represent this exchange in Hordak as follows::
Expand Down Expand Up @@ -182,11 +181,13 @@ def currency_exchange(
fee_amount = Money(0, source_amount.currency)
else:
# If we do have fees then make sure the fee currency matches the source currency
if fee_amount.currency != source_amount.currency:
if fee_amount.currency not in (
source_amount.currency,
destination_amount.currency,
):
raise InvalidFeeCurrency(
"Fee amount currency ({}) must match source amount currency ({})".format(
fee_amount.currency, source_amount.currency
)
f"Fee amount currency ({fee_amount.currency}) must match source "
f"({source_amount.currency}) or destination ({destination_amount.currency}) amount currency "
)

# Checks over and done now. Let's create the transaction
Expand All @@ -201,14 +202,19 @@ def currency_exchange(
),
)

# Are we charging the fee at the source or destination?
charge_fee_at_source = source_amount.currency == fee_amount.currency

# Source currency into trading account
Leg.objects.create(
transaction=transaction, account=source, amount=source_amount
)
Leg.objects.create(
transaction=transaction,
account=trading_account,
amount=-(source_amount - fee_amount),
amount=-(
(source_amount - fee_amount) if charge_fee_at_source else source_amount
),
)

# Any fees
Expand All @@ -222,7 +228,13 @@ def currency_exchange(

# Destination currency out of trading account
Leg.objects.create(
transaction=transaction, account=trading_account, amount=destination_amount
transaction=transaction,
account=trading_account,
amount=(
destination_amount
if charge_fee_at_source
else destination_amount + fee_amount
),
)
Leg.objects.create(
transaction=transaction, account=destination, amount=-destination_amount
Expand Down

0 comments on commit 3d401a3

Please sign in to comment.