SPB Git

spb/air Public MIT

AIR — The Language of Accounting.

Python 100%
8.8 KB

# Research: Foreign Exchange Handling (Rate Sources, CRA Rules, System Design)

Date of research: 2026-08-05 Method: 4 targeted web searches (Bank of Canada Valet API / noon-rate history, CRA acceptable rates, CRA Folio S5-F4-C1, multi-currency system design). Purpose: Define where AIR's FX pass gets its rates, which rate the Canadian tax authority accepts, and how the compiler must record FX in the ledger. Complements accounting-standards.md §3 (IAS 21 semantics).


# 1. Bank of Canada exchange rates and the Valet API

  • The historical noon rate (and closing rate) was discontinued: legacy noon/closing rates were last updated 28 April 2017 and are frozen. Since 1 March 2017 the Bank publishes a single daily average exchange rate per currency pair.
  • Daily average rates are published once each business day by 16:30 ET, for CAD against ~26 currencies. The Bank also publishes monthly and annual average rates.
  • All Bank of Canada rates are indicative — averages of aggregated price quotes from financial institutions, not transactable rates.
  • The Valet API is the Bank's free, no-registration web API for all of this data:

# AIR design consequences

  • Build a rate provider service in the kernel (kernel/fx) with the Valet API as the primary CAD source; cache observations locally so compilation is deterministic and replayable (a recompilation must use the same stored rate, never a live fetch).
  • Every converted amount in the provenance graph carries {rate, series (e.g. FXUSDCAD), rate_date, provider: bank_of_canada_valet, retrieved_at}.
  • No business-day = no rate: weekends/holidays require an explicit, documented convention (e.g. last published business-day rate) — a policy parameter, not a hard-coded choice.
  • Cross rates (e.g. USD→EUR) must be derived through CAD from two BoC series when operating under Canadian tax rules (see §2).

Sources (consulted 2026-08-05):


# 2. CRA rules: which rate to use

Primary source: Income Tax Folio S5-F4-C1, Income Tax Reporting Currency (CRA).

  • Default: convert a foreign amount using the Bank of Canada rate in effect on the day the amount arises (the "relevant spot rate" — day income is received, expense paid, transaction occurs).
  • CRA will generally also accept another published rate if it is: widely available, verifiable, from an independent provider, market-recognized, used for financial reporting where applicable, and used consistently year over year.
  • Average rates (annual/monthly BoC averages) may be accepted for income items received throughout the year (interest, dividends, employment income, recurring revenue) — but not for capital transactions (purchase/sale of assets or investments), which require the transaction-date rate.
  • Where the statutory "relevant spot rate" definition applies (e.g. functional-currency elections under s. 261), the CRA has no discretion to accept anything other than the Bank of Canada rate; for non-CAD ↔ non-CAD conversions, the rate is derived via CAD from the two BoC series.

# AIR design consequences

  • Rate-selection policy is ALSL policy data, not engine code: use: transaction_date_rate as the default, with an opt-in annual_average policy scoped to eligible recurring-income event types and jurisdiction CA. The compiler enforces that capital-type events always use the transaction-date rate.
  • Consistency requirement → the chosen rate source/method must be versioned in the policy and stable across a fiscal year; changing it is a policy-version event that shows up in the audit trail.

Sources (consulted 2026-08-05):


# 3. How accounting systems record FX

Standard multi-currency ledger practice (consistent with IAS 21 / ASC 830):

  1. Dual-amount recording: every line stores the transaction-currency amount (as invoiced) and the functional-currency amount (converted at the transaction-date spot rate). The functional currency is the currency of the entity's primary economic environment — an entity-level configuration.
  2. Realized FX gain/loss — arises on settlement: the difference between the functional-currency value at booking and at payment. Example: invoice €10,000 booked at 1.10, collected at 1.12 → realized gain. Posted to a dedicated P&L account (Dr/Cr Realized FX Gain/Loss).
  3. Unrealized FX gain/loss — arises at period-end revaluation of open monetary balances (unpaid AR/AP, foreign-currency cash, loans): system compares booking rate to closing rate and posts the difference to Unrealized FX Gain/Loss. These are "paper" movements; systems typically reverse them at the next period open or track them cumulatively per open item.
  4. Separation for reporting: realized and unrealized are kept in separate accounts for clean reporting, tax treatment, and compliance.

# AIR design consequences

  • AIR Money = {amount: Decimal, currency}; the FX pass (not the LLM, not the event author) produces the functional-currency leg and appends the conversion to the provenance graph (Invoice → FX → Settlement chain, per the SSA-style traceability requirement).
  • Three deterministic compiler behaviors, matching §3 of accounting-standards.md:
    • Booking: spot rate at transaction date (BoC daily average for CA entities).
    • Period-end pass: revalue open monetary items at closing rate → unrealized entries, auto-reversal on period open (fits incremental compilation / contra-entry machinery).
    • Settlement: compute realized gain/loss against the booked rate, per open item.
  • Never floats: rates and amounts are fixed-point decimals; rounding rules (banker's rounding, per-jurisdiction) are documented policy parameters.
  • Golden tests: FX purchase booked/paid across a rate move (realized), open FX receivable across a period end (unrealized + reversal), CAD-only path (FX pass is a no-op).

Sources (consulted 2026-08-05):


# 4. Decisions / follow-ups

  • D1: Bank of Canada Valet API is the canonical rate source for Canadian entities; rates are fetched once, stored immutably, and replayed on recompilation.
  • D2: Rate-selection rules (transaction-date vs average; weekend fallback) live in ALSL policies, versioned; the engine only enforces provenance and the capital-transaction restriction.
  • D3: Ledger schema stores transaction-currency and functional-currency amounts on every line, with realized and unrealized FX in separate accounts.
  • Follow-up: research rounding rules for GST/QST on converted amounts before implementing the Tax pass × FX pass interaction; research MT940/camt.053 statement currencies for the reconciliation phase.