CrossRates
A matrix of cross rates among a few currencies, each worked out from their rates against one base, the diagonal left blank.
Demo
| 1 unit of | USD | EUR | GBP | JPY | CHF | CAD |
|---|---|---|---|---|---|---|
| USDbase | same currency | 0.9224 | 0.7841 | 156.24 | 0.8812 | 1.3746 |
| EUR | 1.0841 | same currency | 0.8501 | 169.38 | 0.9553 | 1.4902 |
| GBP | 1.2753 | 1.1764 | same currency | 199.26 | 1.1238 | 1.7531 |
| JPY | 0.006400 | 0.005904 | 0.005019 | same currency | 0.005640 | 0.008798 |
| CHF | 1.1348 | 1.0468 | 0.8898 | 177.30 | same currency | 1.5599 |
| CAD | 0.7275 | 0.6710 | 0.5704 | 113.66 | 0.6411 | same currency |
| 1 unit of | EUR | PLN | CZK | HUF |
|---|---|---|---|---|
| EURbase | same currency | 4.3125 | 25.2140 | 395.8000 |
| PLN | 0.2319 | same currency | 5.8467 | 91.7797 |
| CZK | 0.0397 | 0.1710 | same currency | 15.6976 |
| HUF | 0.0025 | 0.0109 | 0.0637 | same 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.qwemain.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>}Related
FxRateTableA board of exchange rates: each pair with its flags, bid, ask, the spread in pips and the day's change from the open, coloured.7 views · 8 downloadsAmortizationTableA loan's payment schedule worked out from its amount, rate and term: each month's principal, interest and balance, grouped by year with the year's totals.7 views · 7 downloadsFxPositionsOpen currency positions: pair, long or short, size, entry and current rate, profit or loss in pips and money, coloured, with the total.7 views · 6 downloadsShareCapitalLedgerA member's share capital contributions with a running balance, and the progress toward the share capital the co-op requires.6 views · 5 downloadsBankStatementA monthly statement that prints: the period, opening and closing balance, totals paid in and out, and each transaction with the balance after.7 views · 6 downloadsCollateralListWhat secures a loan: each item's valuation and documents, and the loan-to-value ratio as a meter against the lender's limit.7 views · 5 downloads
Thread
Thread 0 posts
On CrossRates: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.