π FX Configuration & Routing
LibreFolio supports a sophisticated multi-provider routing system for Foreign Exchange (FX) rates. This allows administrators to define exactly which provider should be used for each currency pair, with automatic fallback capabilities and multi-step conversion chains.
π€οΈ The Routing Logic
When the system needs to fetch FX rates (e.g., via the /api/v1/fx/currencies/sync endpoint), it consults the fx_conversion_routes table.
π’ Priority System
Each currency pair can have multiple routes assigned, ranked by priority (1 = highest/primary).
- Primary Route: The system first attempts to use the route with
priority=1. - Fallback: If the primary route fails (API error, timeout, provider down), the system automatically tries the route with
priority=2, and so on. - Failure: If all configured routes fail, the sync operation reports an error for that specific pair.
π Direct vs. Chain Routes
Each route is defined as a chain of steps (chain_steps JSON array):
-
Direct (1-step): A single provider converts the pair directly. Example:
EUR β USDvia ECB β[{"from": "EUR", "to": "USD", "provider": "ECB"}] -
Multi-step (chain): Multiple providers are chained to compute a derived rate. Example:
RON β USDvia ECB+ECB β[{"from": "RON", "to": "EUR", "provider": "ECB"}, {"from": "EUR", "to": "USD", "provider": "ECB"}]
The system multiplies the intermediate rates to compute the final derived rate, which is stored in fx_rates with source = "CHAIN:ECB+ECB".
π Example Configuration
| Base | Quote | Priority | Chain Steps | Description |
|---|---|---|---|---|
| EUR | USD | 1 | EUR β[ECB]β USD |
Direct via ECB (primary) |
| EUR | USD | 2 | EUR β[FED]β USD |
Direct via FED (fallback) |
| RON | USD | 1 | RON β[ECB]β EUR β[ECB]β USD |
2-step chain via ECB |
| GBP | USD | 1 | GBP β[BOE]β USD |
Direct via BOE |
In this example:
- For EUR/USD, the system prefers ECB. If ECB is down, it falls back to FED.
- For RON/USD, no provider offers this pair directly, so it uses a 2-step chain through EUR.
- For GBP/USD, it uses BOE directly.
ποΈ Database Schema
The configuration is stored in the FxConversionRoute model:
class FxConversionRoute(SQLModel, table=True):
__tablename__ = "fx_conversion_routes"
id: int # Primary key
base: str # e.g., "EUR" (alphabetically first: base < quote)
quote: str # e.g., "USD"
priority: int # 1 = primary, 2+ = fallback
chain_steps: str # JSON array of conversion steps
fetch_interval: int | None # Optional refresh frequency (minutes), default 1440
created_at: datetime
updated_at: datetime
chain_steps JSON format:
// Direct (1-step):
[{"from": "EUR", "to": "USD", "provider": "ECB"}]
// Multi-step chain (2-step):
[
{"from": "RON", "to": "EUR", "provider": "ECB"},
{"from": "EUR", "to": "USD", "provider": "ECB"}
]
// Manual-only pair:
[{"from": "NOK", "to": "SEK", "provider": "MANUAL"}]
Validation rules:
- Continuity:
step[i].to == step[i+1].fromfor consecutive steps - No repeated edges: The same currency pair cannot appear twice in a chain (any direction)
- Alphabetical ordering:
base < quoteconstraint
π API Endpoints
Configuration is managed via the /api/v1/fx/providers/routes endpoints.
π List Routes
GET /api/v1/fx/providers/routes
Returns all configured routes ordered by base, quote, and priority. Each route includes the full chain_steps array.
β Create/Update Routes (Bulk)
POST /api/v1/fx/providers/routes
Creates or updates multiple conversion routes atomically.
Request:
[
{
"base": "EUR",
"quote": "USD",
"priority": 1,
"chain_steps": [
{"from": "EUR", "to": "USD", "provider": "ECB"}
]
},
{
"base": "EUR",
"quote": "USD",
"priority": 2,
"chain_steps": [
{"from": "EUR", "to": "USD", "provider": "FED"}
]
},
{
"base": "RON",
"quote": "USD",
"priority": 1,
"chain_steps": [
{"from": "RON", "to": "EUR", "provider": "ECB"},
{"from": "EUR", "to": "USD", "provider": "ECB"}
]
}
]
ποΈ Delete Routes
DELETE /api/v1/fx/providers/routes
Removes routing rules. Can delete a specific priority or all rules for a pair.
Request:
If priority is omitted, all routes for the pair are deleted. When all real-provider routes are deleted, a MANUAL sentinel route is automatically reinstated to preserve the pair in the system.
π Auto-Sync Behavior
When calling POST /api/v1/fx/currencies/sync:
- The system queries
fx_conversion_routesto find all configured pairs and their routes. - For 1-step routes, it groups target currencies by provider and fetches in batch.
- For multi-step (chain) routes, it fetches each leg's rates from the respective providers, then computes the derived rate by multiplying the intermediate rates.
- If a route fails, the system tries the next priority route for that pair.
- Results are saved to the
fx_ratestable. Chain-derived rates are stored withsource = "CHAIN:provider1+provider2".
This ensures resilience to individual provider outages and supports currency pairs that no single provider covers directly.