I think it's useful to think of anyone's financial life as just a sequence of events. In their primitive form, these events are either tax events, spend events, income events, asset decline, or asset growth events. Admittedly, tax events in particular can get very complicated but the other ones are fairly simple to reason about. These primitives are something that can be built off of. For example, we can imagine that anytime someone wants to model buying from a convenience store, they'll put a spend event for some portion of the amount and then a tax event to model the sales tax.
I think this idea that these events are primitives and that they can be used to construct more complicated scenarios is super interesting to me. For example, we can imagine modelling the awarding and exercise of stock options as a series of a few events: 1. Income event receiving an asset in the form of stock options 2. Expense event modelling the exercise of the stock options 3. Tax event to account for the exercise of the stock options 4. Income event receiving an asset in the form of stock
We can also imagine modelling asset purchases as a pair of 2 events: 1. Expense event to represent the loss of cash 2. Income event to represent the gain of an asset
To illustrate how we might model this in Elixir code, we can start by modelling Income and Expense events. An income event can be considered as a gain in a particular asset. To start, we need to be able to identify assets:
defmodule FinancialSimulation.AssetIdentifier do
@moduledoc """
Identifies an asset with a unique ID pointing to its account or holder
and a type describing the nature of the asset (e.g., cash, stock, property).
"""
defstruct [:id, :type]
end
Then, construct an income event:
defmodule FinancialSimulation.Event do
alias FinancialSimulation.AssetIdentifier
defmodule IncomeEvent do
@moduledoc """
Represents the receipt of an asset. Records the amount, date, description,
and an `AssetIdentifier` that points to the account and type of asset received.
"""
defstruct [:amount, :date, :description, :asset]
@doc """
Creates a new `IncomeEvent` for receiving an asset, using an `AssetIdentifier`
to specify the asset's account and type.
"""
def new(amount, date, description, asset_id, asset_type) do
%__MODULE__{
amount: amount,
date: date,
description: description,
asset: %AssetIdentifier{id: asset_id, type: asset_type}
}
end
end
end
An example of usage is below:
alias FinancialSimulation.Event.IncomeEvent
# Creating an income event for receiving cash
cash_income = IncomeEvent.new(1000, ~D[2024-10-27], "Monthly salary payment", "account-123", "cash")
# Creating an income event for receiving stock
stock_income = IncomeEvent.new(50, ~D[2024-10-27], "Employee stock option grant", "account-456", "stock")
# Inspect the created events
IO.inspect(cash_income)
IO.inspect(stock_income)
Focusing now on ExpenseEvent
:
defmodule FinancialSimulation.Event do
alias FinancialSimulation.AssetIdentifier
defmodule ExpenseEvent do
@moduledoc """
Represents an expenditure or outflow of an asset. Records the amount, date,
description, and an `AssetIdentifier` indicating the account and type of asset spent.
"""
defstruct [:amount, :date, :description, :asset]
@doc """
Creates a new `ExpenseEvent` for spending an asset, using an `AssetIdentifier`
to specify the asset's account and type.
"""
def new(amount, date, description, asset_id, asset_type) do
%__MODULE__{
amount: amount, # Always positive
date: date,
description: description,
asset: %AssetIdentifier{id: asset_id, type: asset_type}
}
end
end
end
Now, we can define an asset purchase like this:
defmodule FinancialSimulation.Event do
alias FinancialSimulation.AssetIdentifier
alias FinancialSimulation.Event.{ExpenseEvent, IncomeEvent}
defmodule AssetPurchaseEvent do
@moduledoc """
Composite event representing the purchase of an asset, containing an `ExpenseEvent`
for the cash outflow and an `IncomeEvent` for the acquisition of the asset.
"""
defstruct [:expense_event, :income_event]
@doc """
Creates a new `AssetPurchaseEvent`, with an `ExpenseEvent` representing the cash spent
and an `IncomeEvent` representing the acquired asset.
"""
def new(amount, date, description, cash_account_id, asset_account_id, asset_type) do
expense_event = %ExpenseEvent{
amount: amount,
date: date,
description: "Cash outflow for purchase of #{description}",
asset: %AssetIdentifier{id: cash_account_id, type: "cash"}
}
income_event = %IncomeEvent{
amount: amount,
date: date,
description: "Acquisition of #{description}",
asset: %AssetIdentifier{id: asset_account_id, type: asset_type}
}
%__MODULE__{expense_event: expense_event, income_event: income_event}
end
@doc """
Converts an `AssetPurchaseEvent` into a list of its primitive events.
"""
def to_primitive_events(%__MODULE__{expense_event: expense, income_event: income}) do
[expense, income]
end
end
end
And then ideally someone would use these abstractions in LiveBook (the Elixir equivalent to Jupyter Notebook).