Table of Contents

MoneyBag Class

Definition

Namespace
Bodu.Financial
Assembly
Bodu.Financial.dll
Package
Bodu.Financial 1.0.0
Source
MoneyBag.Accessors.cs

Aggregates monetary balances across multiple currencies. Immutable; every mutator returns a new instance.

public sealed class MoneyBag : IEquatable<MoneyBag>, IEnumerable<Money>, IEnumerable
Inheritance
MoneyBag
Implements
Inherited Members
Extension Methods

Remarks

MoneyBag is the multi-currency aggregate used to model portfolios, ledger totals that span currencies, FX positions, or any other context where amounts in different currencies must be tracked together but not silently merged. Zero balances are pruned automatically on every operation.

Enumeration yields one Money per non-zero currency, in ISO-code lexicographic order so the iteration is stable and reproducible across runs.

JSON serialization ships in the companion Bodu.Financial.Serialization.Json package; the type carries no [JsonConverter] attribute, so register the financial converters on the target JsonSerializerOptions via its AddFinancialJsonConverters extension.

using Bodu.Financial;
using Bodu.Financial.Currencies;

// Every mutator returns a new bag; same-currency amounts are summed automatically.
MoneyBag portfolio = MoneyBag.Empty
    .Add(new Money(1_000m, CurrencyCode.USD))
    .Add(new Money(250m, CurrencyCode.USD))      // folds into the USD slot -> 1,250.00 USD
    .Add<EUR>(new Money<EUR>(500m));             // typed overload

// Merge two bags, summing per currency.
MoneyBag combined = portfolio.Combine(new MoneyBag([new Money(200m, CurrencyCode.GBP)]));

// Read a single currency back, typed and null-safe.
Money<USD>? usd = combined.GetBalance<USD>();    // 1,250.00 USD
int currencies = combined.Count;                 // 3 (USD, EUR, GBP)

Constructors

MoneyBag()

Initializes a new instance of the MoneyBag class.

public MoneyBag()

MoneyBag(IEnumerable<Money>)

Initializes a new instance of the MoneyBag class from a sequence of Money balances, summing amounts with the same currency.

public MoneyBag(IEnumerable<Money> balances)

Parameters

balances IEnumerable<Money>

The starting balances.

Exceptions

ArgumentNullException

balances is null.

ArgumentException

A balance carries no currency (default-initialised).

Fields

Empty

The shared empty bag instance.

public static readonly MoneyBag Empty

Field Value

MoneyBag

Properties

Balances

Gets a read-only view of the balance map keyed by CurrencyCode, in ISO-code lexicographic order.

public IReadOnlyDictionary<CurrencyCode, decimal> Balances { get; }

Property Value

IReadOnlyDictionary<CurrencyCode, decimal>

The immutable balance map. Because the backing store is an ImmutableSortedDictionary<TKey, TValue>, the view is genuinely read-only and is returned without allocating a wrapper.

Count

Gets the number of distinct currencies carrying a non-zero balance.

public int Count { get; }

Property Value

int

The number of currency slots.

IsEmpty

Gets a value indicating whether this bag carries no balances.

public bool IsEmpty { get; }

Property Value

bool

true when the bag is empty; otherwise false.

Methods

Add(Money)

Returns a new bag with amount added to the balance for its currency.

public MoneyBag Add(Money amount)

Parameters

amount Money

The amount to add.

Returns

MoneyBag

The updated bag.

Remarks

The bag is a settlement-precision container: the incoming amount is rounded to its currency's registered minor units (banker's rounding) before it is folded into the balance, so an explicit-scale unit price settles on entry. Settle high-precision amounts deliberately - via RoundToMoney(MonetaryContext?) - when a different rounding rule is required.

Exceptions

ArgumentException

amount has no currency (default-initialised).

Add<TCurrency>(Money<TCurrency>)

Returns a new bag with the typed amount added to the balance for its currency.

public MoneyBag Add<TCurrency>(Money<TCurrency> amount) where TCurrency : ICurrency

Parameters

amount Money<TCurrency>

The amount to add.

Returns

MoneyBag

The updated bag.

Type Parameters

TCurrency

The currency type.

Combine(MoneyBag)

Returns a new bag containing the union of this bag's balances and other's, summing per-currency amounts.

public MoneyBag Combine(MoneyBag other)

Parameters

other MoneyBag

The other bag to combine.

Returns

MoneyBag

The combined bag.

Exceptions

ArgumentNullException

other is null.

ConvertToWithAudit<TTarget>(IDatedRateProvider, DateOnly, RateLookupOptions?, MoneyBagConversionRoundingPolicy)

Converts the entire bag to a single target currency and returns the audit metadata describing which observation was used for each per-currency line.

public MoneyBagConversionAudit<TTarget> ConvertToWithAudit<TTarget>(IDatedRateProvider rates, DateOnly date, RateLookupOptions? options, MoneyBagConversionRoundingPolicy policy = MoneyBagConversionRoundingPolicy.SumRawThenRound) where TTarget : ICurrency

Parameters

rates IDatedRateProvider

The dated provider that resolves each rate.

date DateOnly

The valuation date supplied to every lookup.

options RateLookupOptions

The lookup rules supplied to every lookup.

policy MoneyBagConversionRoundingPolicy

The rounding policy applied during aggregation.

Returns

MoneyBagConversionAudit<TTarget>

A MoneyBagConversionAudit<TTarget> containing the aggregated total and one MoneyBagConversionLine per source currency. Lines for balances already in TTarget report a null rate (identity pass-through).

Type Parameters

TTarget

The destination currency type.

Examples

using Bodu.Financial;
using Bodu.Financial.Currencies;
using Bodu.Financial.ExchangeRates;

MoneyBag ledger = MoneyBag.Of(
    Money.From(1450.00m, CurrencyCode.AUD),
    Money.From(370.75m, CurrencyCode.USD));

MoneyBagConversionAudit<AUD> audit = ledger.ConvertToWithAudit<AUD>(
    rates, new DateOnly(2024, 3, 15), RateLookupOptions.PreviousWithin(3));

// audit.Total is the aggregated Money<AUD>; each line explains one balance.
foreach (MoneyBagConversionLine line in audit.Lines)
{
    // line.SourceIsoCode, line.SourceAmount, line.RawConvertedAmount, and
    // line.Rate (null for the AUD balance - identity pass-through).
}

Exceptions

ArgumentNullException

rates is null.

ArgumentOutOfRangeException

policy is not a defined value.

KeyNotFoundException

No rate is available for one of the bag's currencies under options.

ConvertTo<TTarget>(IDatedRateProvider, DateOnly, RateLookupOptions?)

Converts the entire bag to a single target currency by resolving every non-target balance through a dated provider at the supplied valuation date and options. Uses the SumRawThenRound policy.

public Money<TTarget> ConvertTo<TTarget>(IDatedRateProvider rates, DateOnly date, RateLookupOptions? options = null) where TTarget : ICurrency

Parameters

rates IDatedRateProvider

The dated provider that resolves each rate.

date DateOnly

The valuation date supplied to every lookup.

options RateLookupOptions

The lookup rules supplied to every lookup.

Returns

Money<TTarget>

The aggregated Money<TCurrency> total.

Type Parameters

TTarget

The destination currency type.

Exceptions

ArgumentNullException

rates is null.

KeyNotFoundException

No rate is available for one of the bag's currencies under options.

ConvertTo<TTarget>(IDatedRateProvider, DateOnly, RateLookupOptions?, MoneyBagConversionRoundingPolicy)

Converts the entire bag to a single target currency through a dated provider, using the supplied rounding policy.

public Money<TTarget> ConvertTo<TTarget>(IDatedRateProvider rates, DateOnly date, RateLookupOptions? options, MoneyBagConversionRoundingPolicy policy) where TTarget : ICurrency

Parameters

rates IDatedRateProvider

The dated provider that resolves each rate.

date DateOnly

The valuation date supplied to every lookup.

options RateLookupOptions

The lookup rules supplied to every lookup.

policy MoneyBagConversionRoundingPolicy

The rounding policy applied during aggregation.

Returns

Money<TTarget>

The aggregated Money<TCurrency> total.

Type Parameters

TTarget

The destination currency type.

Exceptions

ArgumentNullException

rates is null.

ArgumentOutOfRangeException

policy is not a defined value.

KeyNotFoundException

No rate is available for one of the bag's currencies under options.

ConvertTo<TTarget>(IRateProvider)

Converts the entire bag to a single target currency by applying the supplied rate provider to every non-target balance and aggregating into the destination precision per SumRawThenRound.

public Money<TTarget> ConvertTo<TTarget>(IRateProvider rates) where TTarget : ICurrency

Parameters

rates IRateProvider

The provider that returns the rate for each (from, to) pair.

Returns

Money<TTarget>

The aggregated Money<TCurrency> total.

Type Parameters

TTarget

The destination currency type.

Exceptions

ArgumentNullException

rates is null.

KeyNotFoundException

rates cannot supply a rate for one of the bag's currencies.

InvalidOperationException

A rate returned by rates is zero or negative.

ConvertTo<TTarget>(IRateProvider, MoneyBagConversionRoundingPolicy)

Converts the entire bag to a single target currency using the explicit rounding policy.

public Money<TTarget> ConvertTo<TTarget>(IRateProvider rates, MoneyBagConversionRoundingPolicy policy) where TTarget : ICurrency

Parameters

rates IRateProvider

The provider that returns the rate for each (from, to) pair.

policy MoneyBagConversionRoundingPolicy

The rounding policy applied during aggregation.

Returns

Money<TTarget>

The aggregated Money<TCurrency> total.

Type Parameters

TTarget

The destination currency type.

Exceptions

ArgumentNullException

rates is null.

ArgumentOutOfRangeException

policy is not a defined value.

KeyNotFoundException

rates cannot supply a rate for one of the bag's currencies.

InvalidOperationException

A rate returned by rates is zero or negative.

ConvertTo<TTarget>(Func<string, string, decimal>)

Converts the entire bag to a single target currency using a delegate-based rate lookup.

public Money<TTarget> ConvertTo<TTarget>(Func<string, string, decimal> rateLookup) where TTarget : ICurrency

Parameters

rateLookup Func<string, string, decimal>

A delegate that returns the rate for a (from, to) pair.

Returns

Money<TTarget>

The aggregated Money<TCurrency> total.

Type Parameters

TTarget

The destination currency type.

Exceptions

ArgumentNullException

rateLookup is null.

InvalidOperationException

A rate returned by rateLookup is zero or negative.

ConvertTo<TTarget>(Func<string, string, decimal>, MoneyBagConversionRoundingPolicy)

Converts the entire bag to a single target currency using a delegate-based rate lookup and the supplied rounding policy.

public Money<TTarget> ConvertTo<TTarget>(Func<string, string, decimal> rateLookup, MoneyBagConversionRoundingPolicy policy) where TTarget : ICurrency

Parameters

rateLookup Func<string, string, decimal>

A delegate that returns the rate for a (from, to) pair.

policy MoneyBagConversionRoundingPolicy

The rounding policy applied during aggregation.

Returns

Money<TTarget>

The aggregated Money<TCurrency> total.

Type Parameters

TTarget

The destination currency type.

Exceptions

ArgumentNullException

rateLookup is null.

ArgumentOutOfRangeException

policy is not a defined value.

InvalidOperationException

A rate returned by rateLookup is zero or negative.

Equals(MoneyBag?)

Determines whether two bags carry the same balances.

public bool Equals(MoneyBag? other)

Parameters

other MoneyBag

The other bag.

Returns

bool

true on equality.

Equals(object?)

Determines whether the specified object is equal to the current object.

public override bool Equals(object? obj)

Parameters

obj object

The object to compare with the current object.

Returns

bool

true if the specified object is equal to the current object; otherwise, false.

FromBalances(IEnumerable<Money>)

Creates a MoneyBag from a sequence of balances, summing amounts with the same ISO code.

public static MoneyBag FromBalances(IEnumerable<Money> balances)

Parameters

balances IEnumerable<Money>

The starting balances.

Returns

MoneyBag

The constructed bag.

Exceptions

ArgumentNullException

balances is null.

ArgumentException

A balance carries no ISO code (default-initialised).

GetBalance(CurrencyCode)

Returns the balance for the currency identified by code, or null when the bag has no entry for that currency.

public Money? GetBalance(CurrencyCode code)

Parameters

code CurrencyCode

The currency identifying the balance.

Returns

Money?

The balance, or null when absent.

GetBalance(CurrencyInfo)

Returns the balance for currency, or null when the bag has no entry for that currency.

public Money? GetBalance(CurrencyInfo currency)

Parameters

currency CurrencyInfo

The currency metadata identifying the balance.

Returns

Money?

The balance, or null when absent.

Exceptions

ArgumentNullException

currency is null.

InvalidOperationException

currency's ISO code does not correspond to any CurrencyCode member.

GetBalance<TCurrency>()

Returns the typed balance for TCurrency, or null when absent.

public Money<TCurrency>? GetBalance<TCurrency>() where TCurrency : ICurrency

Returns

Money<TCurrency>?

The typed balance, or null when the bag has no entry for that currency.

Type Parameters

TCurrency

The currency type.

GetEnumerator()

Enumerates the non-zero balances in ISO-code lexicographic order.

public IEnumerator<Money> GetEnumerator()

Returns

IEnumerator<Money>

An enumerator over Money entries.

GetHashCode()

Serves as the default hash function.

public override int GetHashCode()

Returns

int

A hash code for the current object.

Of(params Money[])

Creates a MoneyBag from the supplied balances, summing amounts with the same ISO code.

public static MoneyBag Of(params Money[] balances)

Parameters

balances Money[]

The starting balances.

Returns

MoneyBag

The constructed bag.

Exceptions

ArgumentNullException

balances is null.

ArgumentException

A balance carries no ISO code (default-initialised).

Subtract(Money)

Returns a new bag with amount subtracted from the balance for its currency.

public MoneyBag Subtract(Money amount)

Parameters

amount Money

The amount to subtract.

Returns

MoneyBag

The updated bag.

Subtract<TCurrency>(Money<TCurrency>)

Returns a new bag with the typed amount subtracted.

public MoneyBag Subtract<TCurrency>(Money<TCurrency> amount) where TCurrency : ICurrency

Parameters

amount Money<TCurrency>

The amount to subtract.

Returns

MoneyBag

The updated bag.

Type Parameters

TCurrency

The currency type.

TryGetBalance(CurrencyCode, out Money)

Attempts to retrieve the balance for the currency identified by code.

public bool TryGetBalance(CurrencyCode code, out Money balance)

Parameters

code CurrencyCode

The currency identifying the balance.

balance Money

When this method returns true, the runtime-tagged balance.

Returns

bool

true when the bag has an entry for the currency; otherwise false.

Operators

operator +(MoneyBag, Money)

Adds a single Money to a bag.

public static MoneyBag operator +(MoneyBag left, Money right)

Parameters

left MoneyBag

The bag.

right Money

The amount to add.

Returns

MoneyBag

The updated bag.

operator +(MoneyBag, MoneyBag)

Combines two bags.

public static MoneyBag operator +(MoneyBag left, MoneyBag right)

Parameters

left MoneyBag

The first bag.

right MoneyBag

The second bag.

Returns

MoneyBag

The combined bag.

operator ==(MoneyBag?, MoneyBag?)

Determines whether two bags are equal.

public static bool operator ==(MoneyBag? left, MoneyBag? right)

Parameters

left MoneyBag

The first bag.

right MoneyBag

The second bag.

Returns

bool

true when equal.

operator !=(MoneyBag?, MoneyBag?)

Determines whether two bags differ.

public static bool operator !=(MoneyBag? left, MoneyBag? right)

Parameters

left MoneyBag

The first bag.

right MoneyBag

The second bag.

Returns

bool

true when they differ.

operator -(MoneyBag, Money)

Subtracts a Money from a bag.

public static MoneyBag operator -(MoneyBag left, Money right)

Parameters

left MoneyBag

The bag.

right Money

The amount to subtract.

Returns

MoneyBag

The updated bag.

Explicit Interface Implementations

IEnumerable.GetEnumerator()

Returns an enumerator that iterates through a collection.

IEnumerator IEnumerable.GetEnumerator()

Returns

IEnumerator

An IEnumerator object that can be used to iterate through the collection.

Applies to

ProductVersions
.NET8, 10