Skip to content

Commit

Permalink
Refactor valuation visitors and enhance mock instrument handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mgao6767 committed Feb 25, 2025
1 parent 9052d88 commit 2a4ce34
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 11 deletions.
28 changes: 25 additions & 3 deletions src/brms/instruments/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
from typing import TYPE_CHECKING, Optional

from brms.instruments.base import Instrument
from brms.instruments.visitors.valuation import BankingBookValuationVisitor
from brms.instruments.visitors.valuation import ValuationVisitor
from brms.models.scenario import ScenarioManager

if TYPE_CHECKING:
from brms.instruments.amortizing_fixed_rate_loan import AmortizingFixedRateLoan
from brms.instruments.base import CreditRating, Issuer
from brms.instruments.covered_bond import CoveredBond
from brms.instruments.credit_card import CreditCard
from brms.instruments.fixed_rate_bond import FixedRateBond
from brms.instruments.personal_loan import PersonalLoan
from brms.instruments.visitors import Visitor
from brms.models.base import BookType

Expand All @@ -30,17 +36,33 @@ def accept(self, visitor: "Visitor") -> None:
visitor.visit_mock_instrument(self)


class MockValuationVisitor(BankingBookValuationVisitor):
class MockValuationVisitor(ValuationVisitor):
"""A mock valuation visitor that sets a new value for mock instruments."""

def __init__(self, new_value: float) -> None:
"""Initialize the MockValuationVisitor with a new value.
:param new_value: The new value to set for the mock instrument.
"""
super().__init__(scenario=None)
sm = ScenarioManager()
super().__init__(scenario_manager=sm, valuation_date=None)
self.new_value = new_value

def visit_mock_instrument(self, instrument: "MockInstrument") -> None:
"""Visit a mock instrument."""
instrument.value = self.new_value

def visit_fixed_rate_bond(self, instrument: "FixedRateBond") -> None:
"""Value a fixed rate bond."""

def visit_amortizing_fixed_rate_loan(self, instrument: "AmortizingFixedRateLoan") -> None:
"""Value an amortizing fixed rate bond."""

def visit_covered_bond(self, instrument: "CoveredBond") -> None:
"""Value a covered bond."""

def visit_personal_loan(self, instrument: "PersonalLoan") -> None:
"""Value a personal loan."""

def visit_credit_card(self, instrument: "CreditCard") -> None:
"""Value a credit card."""
34 changes: 26 additions & 8 deletions src/brms/models/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@
from brms.instruments.cash import Cash
from brms.instruments.common_equity import CommonEquity
from brms.instruments.deposit import Deposit
from brms.instruments.mock import MockValuationVisitor
from brms.instruments.mortgage import Mortgage
from brms.instruments.visitors.valuation import ValuationVisitor
from brms.models.bank import Bank
from brms.models.bank_book import BookType, Position, UnrealizedOCIGainLossTracker, UnrealizedTradingGainLossTracker
from brms.models.bank_book import (
BookType,
Position,
UnrealizedOCIGainLossTracker,
UnrealizedTradingGainLossTracker,
)


class Action(Enum):
Expand Down Expand Up @@ -144,6 +150,8 @@ def execute(self) -> bool:
Returns:
bool: True if the transaction was executed, False if it was already executed.
"""
if isinstance(self.valuation_visitor, MockValuationVisitor):
pass
if not self.executed:
self._execute()
self.executed = True
Expand Down Expand Up @@ -238,7 +246,10 @@ def create_transaction(
# Marking to market transactions must have a valuation visitor
if (
transaction_type
in (TransactionType.SECURITY_FVOCI_MARK_TO_MARKET, TransactionType.SECURITY_FVTPL_MARK_TO_MARKET)
in (
TransactionType.SECURITY_FVOCI_MARK_TO_MARKET,
TransactionType.SECURITY_FVTPL_MARK_TO_MARKET,
)
and valuation_visitor is None
):
error_message = f"ValuationVisitor must be provided for transaction type {transaction_type}."
Expand Down Expand Up @@ -404,7 +415,11 @@ class LoanDisbursementTransaction(Transaction):

def controller_actions(self) -> GUIControllerInstruction:
return {
self.cash_to_disburse: (Action.REMOVE, BookType.BANKING_BOOK, Position.LONG),
self.cash_to_disburse: (
Action.REMOVE,
BookType.BANKING_BOOK,
Position.LONG,
),
self.instrument: (Action.ADD, BookType.BANKING_BOOK, Position.LONG),
}

Expand Down Expand Up @@ -536,7 +551,8 @@ def controller_actions(self) -> GUIControllerInstruction:

def _execute(self) -> None:
self.cash_to_receive = self.instrument
self.valuation_visitor.set_date(self.transaction_date, date_must_be_in_simulation=False)
if not isinstance(self.valuation_visitor, MockValuationVisitor):
self.valuation_visitor.set_date(self.transaction_date, date_must_be_in_simulation=False)
self.mortgage.accept(self.valuation_visitor)
self.bank.banking_book.add_instrument(self.cash_to_receive, Position.LONG)
self.bank.ledger.post(self.journal_entry)
Expand Down Expand Up @@ -856,7 +872,7 @@ def _execute(self) -> None:
if not isinstance(self.valuation_visitor, ValuationVisitor):
error = "ValuationVisitor not set"
raise TypeError(error)
if self.transaction_date is None:
if self.transaction_date is None and not isinstance(self.valuation_visitor, MockValuationVisitor):
error = "Transaction date not set"
raise TypeError(error)

Expand All @@ -866,7 +882,8 @@ def _execute(self) -> None:
self.old_unrealized_trading_loss = self.tracker.get_unrealized_loss(self.instrument)
self.old_value = self.instrument.value
# Value the FVTPL instrument at transaction date
self.valuation_visitor.set_date(self.transaction_date)
if not isinstance(self.valuation_visitor, MockValuationVisitor):
self.valuation_visitor.set_date(self.transaction_date)
self.instrument.accept(self.valuation_visitor)
self.new_value = self.instrument.value
# P&L
Expand Down Expand Up @@ -923,7 +940,7 @@ def _execute(self) -> None:
if not isinstance(self.valuation_visitor, ValuationVisitor):
error = "ValuationVisitor not set"
raise TypeError(error)
if self.transaction_date is None:
if self.transaction_date is None and not isinstance(self.valuation_visitor, MockValuationVisitor):
error = "Transaction date not set"
raise TypeError(error)

Expand All @@ -933,7 +950,8 @@ def _execute(self) -> None:
self.old_unrealized_oci_loss = self.tracker.get_unrealized_loss(self.instrument)
self.old_value = self.instrument.value
# Value the FVOCI instrument at transaction date
self.valuation_visitor.set_date(self.transaction_date)
if not isinstance(self.valuation_visitor, MockValuationVisitor):
self.valuation_visitor.set_date(self.transaction_date)
self.instrument.accept(self.valuation_visitor)
self.new_value = self.instrument.value
# P&L
Expand Down

0 comments on commit 2a4ce34

Please sign in to comment.