Finance

CrossRates

A matrix of cross rates among a few currencies, each worked out from their rates against one base, the diagonal left blank.

#finance#forex#table#data#no-js

Demo

Preview
Cross ratesOne unit of the row's currency, in the column's · mid-market, 14:05 UTC. Worked out from rates against USD.
1 unit ofUSDEURGBPJPYCHFCAD
USDbasesame currency0.92240.7841156.240.88121.3746
EUR1.0841same currency0.8501169.380.95531.4902
GBP1.27531.1764same currency199.261.12381.7531
JPY0.0064000.0059040.005019same currency0.0056400.008798
CHF1.13481.04680.8898177.30same currency1.5599
CAD0.72750.67100.5704113.660.6411same currency
Central EuropeOne unit of the row's currency, in the column's. Worked out from rates against EUR.
1 unit ofEURPLNCZKHUF
EURbasesame currency4.312525.2140395.8000
PLN0.2319same currency5.846791.7797
CZK0.03970.1710same currency15.6976
HUF0.00250.01090.0637same currency

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/cross_rates.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/cross_rates.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/cross_rates.qwe
view CrossRatesDemo() =    <div.demo-stack>        <CrossRates id="cross-rates-demo" rates={{ USD: 1, EUR: 0.9224m, GBP: 0.7841m, JPY: 156.24m, CHF: 0.8812m, CAD: 1.3746m }}                    updated="mid-market, 14:05 UTC"/>        <div style="max-width: 560px">            <CrossRates id="cross-rates-demo-cee" label="Central Europe" base="EUR" highlight="PLN" decimals={4}                        rates={{ EUR: 1, PLN: 4.3125m, CZK: 25.214m, HUF: 395.80m }}/>        </div>    </div>

Source

ui/cross_rates.qwe
// CrossRates: a matrix of exchange rates among a few currencies, each worked out// from their rates against one base: the cell in a currency's row and another's// column is what one unit of the first buys of the second. The diagonal, a currency// against itself, is left blank. A currency can be picked out, its row and column// tinted.////   <CrossRates rates={{ USD: 1, EUR: 0.9224m, GBP: 0.7841m, JPY: 156.24m, CHF: 0.8812m }} base="USD"/>//   <CrossRates rates={rates} currencies={["EUR", "USD", "GBP"]} highlight="EUR" updated="Mid-market, 14:05 UTC"/>//// rates: a map of ISO code to the units of it one `base` buys ({ USD: 1, EUR: 0.9224m });// decimals are best (numbers are read as decimals). base: the code they are against// ("USD"), named in the note. currencies: which to show, in order (all the rates').// highlight: a code whose row and column are tinted. decimals: digits for every cell// (by default fewer for large rates: 169.38, 1.0841, 0.006400). locale: how numbers// are written ("en-US"). label: the caption ("Cross rates"). updated: when the rates// are from, after the caption. id: the element's id.//// cross_rates_rate(rates, from, to) gives one cell's rate as a decimal (null when a// rate is missing), for other code.import ui/theme.qwe use ui_clslet cross_rates_css = css`.ui-fxcross { min-width: 0; overflow-x: auto; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface);  font: 13px/1.3 var(--ui-font); color: var(--ui-fg); }.ui-fxcross-table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }.ui-fxcross-table caption { padding: 12px 14px 4px; text-align: left; font-size: 14px; font-weight: 650; }.ui-fxcross-table caption small { display: block; margin-top: 2px; color: var(--ui-muted); font-size: 12px; font-weight: 400; }.ui-fxcross-table th, .ui-fxcross-table td { padding: 9px 12px; text-align: right; white-space: nowrap; }.ui-fxcross-table thead th { border-bottom: 1px solid var(--ui-border); color: var(--ui-muted); font-size: 12px; font-weight: 650; letter-spacing: .03em; }.ui-fxcross-table tbody th { border-right: 1px solid var(--ui-border); text-align: left; font-weight: 650; }.ui-fxcross-table .ui-fxcross-corner { text-align: left; font-weight: 400; font-size: 11px; }.ui-fxcross-table tbody tr:not(:last-child) > * { border-bottom: 1px solid color-mix(in srgb, var(--ui-border) 60%, transparent); }.ui-fxcross-table td:hover { background: var(--ui-subtle); }.ui-fxcross-self { background: repeating-linear-gradient(135deg, transparent 0 5px, color-mix(in srgb, var(--ui-border) 45%, transparent) 5px 6px); }.ui-fxcross-table .ui-fxcross-hl { background: var(--ui-accent-soft); }.ui-fxcross-table th.ui-fxcross-hl { color: var(--ui-accent); }.ui-fxcross-base { margin-left: 4px; padding: 0 5px; border-radius: 3px; background: var(--ui-subtle); color: var(--ui-muted); font-size: 10px; font-weight: 600; }`// What one `from` buys of `to`, from rates against one base; null when either is missing.fn cross_rates_rate(rates: map, from: str, to: str) {    let a = rates[from]    let b = rates[to]    if a == null || b == null { return null }    let da = dec(a)    let db = dec(b)    da > 0 && db > 0 ? db / da : null}// The decimals a rate is written with: fewer for a large one (169.38), more for a small one (0.006400).fn cross_rates_places(r: dec) -> int = r >= 100 ? 2 : (r >= 0.1m ? 4 : (r >= 0.001m ? 6 : 8))view CrossRates(rates: map, base: str = "USD", currencies: list? = null, highlight: str? = null, decimals: int? = null,                locale: str = "en-US", label: str = "Cross rates", updated: str? = null, id: str? = null, class: str? = null) {    style global cross_rates_css    let codes = currencies ?? keys(rates)    let hl = (c: str) => highlight != null && c == highlight ? "ui-fxcross-hl" : null    <div class={ui_cls(["ui-fxcross", class])} id={id}>        <table.ui-fxcross-table>            <caption>{label}<small>One unit of the row's currency, in the column's{updated != null ? ` · ${updated}` : ""}. Worked out from rates against {base}.</small></caption>            <thead><tr>                <th.ui-fxcross-corner scope="col"><span.ui-sr>1 unit of</span></th>                {for c in codes => <th key={c} scope="col" class={hl(c)}>{c}</th>}            </tr></thead>            <tbody>                {for r in codes => <tr key={r} data-code={r}>                    <th scope="row" class={hl(r)}>{r}{r == base && <span.ui-fxcross-base>base</span>}</th>                    {for c in codes {                        let x = cross_rates_rate(rates, r, c)                        let places = decimals ?? (x != null ? cross_rates_places(x) : 2)                        r == c                            ? <td key={c} class="ui-fxcross-self" data-code={c}><span.ui-sr>same currency</span></td>                            : <td key={c} class={hl(r) ?? hl(c)} data-code={c}                                  data-rate={x != null ? str(round(x, 10)) : null}>                                  {x != null ? format_number(round(x, places, "half_up"), locale, places) : "—"}                              </td>                    }}                </tr>}            </tbody>        </table>    </div>}
Thread
66Useful? Like it, or add to its thread below.

Thread 0 posts

On CrossRates: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.