optimizations
This commit is contained in:
parent
cdfbf26c98
commit
bb10145f56
8 changed files with 304 additions and 277 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -14,7 +14,7 @@
|
||||||
"@sveltejs/kit": "^2.5.20",
|
"@sveltejs/kit": "^2.5.20",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||||
"sass": "^1.77.8",
|
"sass": "^1.77.8",
|
||||||
"svelte": "^4.2.18",
|
"svelte": "^5.0.0-next.210",
|
||||||
"svelte-adapter-bun": "^0.5.2",
|
"svelte-adapter-bun": "^0.5.2",
|
||||||
"svelte-preprocess": "^6.0.2",
|
"svelte-preprocess": "^6.0.2",
|
||||||
"vite": "^5.3.5"
|
"vite": "^5.3.5"
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import { watch } from "fs";
|
import { watch } from "fs";
|
||||||
import { writable } from "svelte/store";
|
import { derived, writable } from "svelte/store";
|
||||||
|
|
||||||
const offers = writable([]);
|
const offers = writable([]);
|
||||||
const trades = writable([]);
|
const trades = writable([]);
|
||||||
const crypto = writable([]);
|
const crypto = writable([]);
|
||||||
const fiat = writable([]);
|
const fiat = writable([]);
|
||||||
|
const liquidity = derived(offers, ($offers) =>
|
||||||
|
Object.values($offers)
|
||||||
|
.flat()
|
||||||
|
.reduce((a, b) => a + Number.parseInt(b.amount), 0),
|
||||||
|
);
|
||||||
|
|
||||||
const formatTrades = (e) => {
|
const formatTrades = (e) => {
|
||||||
return e.map((e) => {
|
return e.map((e) => {
|
||||||
|
@ -20,11 +25,26 @@ const formatTrades = (e) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const formatOffers = (e) => {
|
const formatOffers = (e) => {
|
||||||
return Object.groupBy(e, ({ currencyCode }) => currencyCode);
|
return Object.groupBy(
|
||||||
|
e.map((e) => {
|
||||||
|
return {
|
||||||
|
direction: e.direction,
|
||||||
|
currencyCode: e.currencyCode,
|
||||||
|
amount: e.amount,
|
||||||
|
price: e.price,
|
||||||
|
paymentMethod: e.paymentMethod,
|
||||||
|
primaryMarketAmount: e.primaryMarketAmount,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
({ currencyCode }) => currencyCode,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
const formatCrypto = (e) => {
|
const formatCrypto = (e) => {
|
||||||
e[e.findIndex((e) => e.code === "XMR")].precision = 12;
|
e.push({
|
||||||
e[e.findIndex((e) => e.code === "XMR")].sign = "ɱ";
|
precision: 12,
|
||||||
|
code: "XMR",
|
||||||
|
sign: "ɱ",
|
||||||
|
});
|
||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
const formatFiat = (e) => {
|
const formatFiat = (e) => {
|
||||||
|
@ -47,16 +67,16 @@ Bun.file(`${import.meta.env.VITE_DB_PATH}trade_statistics.json`)
|
||||||
.then((j) => {
|
.then((j) => {
|
||||||
trades.set(formatTrades(j));
|
trades.set(formatTrades(j));
|
||||||
});
|
});
|
||||||
Bun.file(`${import.meta.env.VITE_DB_PATH}crypto_currency_list.json`)
|
Bun.file(`${import.meta.env.VITE_DB_PATH}active_traditional_currency_list.json`)
|
||||||
.json()
|
|
||||||
.then((j) => {
|
|
||||||
crypto.set(formatCrypto(j));
|
|
||||||
});
|
|
||||||
Bun.file(`${import.meta.env.VITE_DB_PATH}/traditional_currency_list.json`)
|
|
||||||
.json()
|
.json()
|
||||||
.then((j) => {
|
.then((j) => {
|
||||||
fiat.set(formatFiat(j));
|
fiat.set(formatFiat(j));
|
||||||
});
|
});
|
||||||
|
Bun.file(`${import.meta.env.VITE_DB_PATH}active_crypto_currency_list.json`)
|
||||||
|
.json()
|
||||||
|
.then((j) => {
|
||||||
|
crypto.set(formatCrypto(j));
|
||||||
|
});
|
||||||
|
|
||||||
const watcher = watch(import.meta.env.VITE_DB_PATH, async (_, filename) => {
|
const watcher = watch(import.meta.env.VITE_DB_PATH, async (_, filename) => {
|
||||||
const file = Bun.file(import.meta.env.VITE_DB_PATH + filename);
|
const file = Bun.file(import.meta.env.VITE_DB_PATH + filename);
|
||||||
|
@ -84,4 +104,4 @@ process.on("SIGINT", () => {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
export { offers, trades, crypto, fiat };
|
export { offers, trades, crypto, fiat, liquidity };
|
||||||
|
|
|
@ -15,153 +15,153 @@ Object.groupBy ||= (values, keyFinder) => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="col app">
|
<div class="col app">
|
||||||
<div class="row header" style="align-content:center;">
|
<div class="row header" style="align-content:center;">
|
||||||
<span>
|
<span>
|
||||||
<img src="/haveno_logo.png" alt="" style="height:1em;"/>
|
<img src="/haveno_logo.png" alt="" style="height:1em;"/>
|
||||||
<a href="https://haveno.exchange">haveno.exchange</a>
|
<a href="https://haveno.exchange">haveno.exchange</a>
|
||||||
</span>
|
</span>
|
||||||
<a href="/">haveno.markets</a>
|
<a href="/">haveno.markets</a>
|
||||||
<span>
|
<span>
|
||||||
<img src="/monero_logo.png" alt="" style="height:1em;"/>
|
<img src="/monero_logo.png" alt="" style="height:1em;"/>
|
||||||
<a href="https://xmrchain.net">xmrchain.net</a>
|
<a href="https://xmrchain.net">xmrchain.net</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col container">
|
<div class="col container">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="col footer">
|
<div class="col footer">
|
||||||
<span>
|
<span>
|
||||||
Links:
|
Links:
|
||||||
<a href="https://haveno.markets">Clearnet</a> |
|
<a href="https://haveno.markets">Clearnet</a> |
|
||||||
<a href="http://lsj5o4i7iiogz6q4rstiv75nk7kots2f2nhuga75y7s23leskz2uh6id.onion">Tor</a> |
|
<a href="http://lsj5o4i7iiogz6q4rstiv75nk7kots2f2nhuga75y7s23leskz2uh6id.onion">Tor</a> |
|
||||||
<a href="http://haveno-markets.i2p">I2P</a>
|
<a href="http://haveno-markets.i2p">I2P</a>
|
||||||
<a href="http://okoeicsihmjkqcqaiqow3arcrzm5ascwhpxq34incxg6a5z4tjza.b32.i2p">(b32)</a>
|
<a href="http://okoeicsihmjkqcqaiqow3arcrzm5ascwhpxq34incxg6a5z4tjza.b32.i2p">(b32)</a>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span style="display:flex;gap:.2em;">
|
||||||
Data from:
|
Data from:
|
||||||
<a href="https://haveno-reto.com" style="display:inline-flex;gap:.2em;align-items:center;">
|
<a href="https://haveno-reto.com" style="display:inline-flex;gap:.2em;align-items:center;">
|
||||||
<img src="/haveno-reto_logo.svg" alt="" style="height:1em;width:1em;"/>
|
<img src="/haveno-reto_logo.svg" alt="" style="height:1em;width:1em;"/>
|
||||||
haveno-reto
|
haveno-reto
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
Donations:
|
Donations:
|
||||||
<a style="word-break:break-all;" href="monero:84LnuW3YpCQirNMN6y6Px1E3DfwnqwXVRARi9eHjzeSVFJqEQmJCxkP5WpkysbcktqUNhXxQLowhJGSknNjJWZNQ7FKp5bu">
|
<a style="word-break:break-all;" href="monero:84LnuW3YpCQirNMN6y6Px1E3DfwnqwXVRARi9eHjzeSVFJqEQmJCxkP5WpkysbcktqUNhXxQLowhJGSknNjJWZNQ7FKp5bu">
|
||||||
84LnuW3YpCQirNMN6y6Px1E3DfwnqwXVRARi9eHjzeSVFJqEQmJCxkP5WpkysbcktqUNhXxQLowhJGSknNjJWZNQ7FKp5bu
|
84LnuW3YpCQirNMN6y6Px1E3DfwnqwXVRARi9eHjzeSVFJqEQmJCxkP5WpkysbcktqUNhXxQLowhJGSknNjJWZNQ7FKp5bu
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<style lang="scss" global>
|
<style lang="scss" global>
|
||||||
.app {
|
.app {
|
||||||
display:flex;
|
display:flex;
|
||||||
width:100%;
|
width:100%;
|
||||||
justify-content:center;
|
justify-content:center;
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
width:80%!important;
|
width:80%!important;
|
||||||
}
|
}
|
||||||
.header {
|
.header {
|
||||||
background-color:#5555;
|
background-color:#5555;
|
||||||
padding:1em 0;
|
padding:1em 0;
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
background-color:#4444;
|
background-color:#4444;
|
||||||
margin-top:auto;
|
margin-top:auto;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
.col{
|
.col{
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width:100%;
|
width:100%;
|
||||||
height:100%;
|
height:100%;
|
||||||
}
|
}
|
||||||
.row {
|
.row {
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
width:100%;
|
width:100%;
|
||||||
height:100%;
|
height:100%;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
background-color:#290040;
|
background-color:#290040;
|
||||||
color:white;
|
color:white;
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:0;
|
padding:0;
|
||||||
}
|
}
|
||||||
.card {
|
.card {
|
||||||
margin:1em;
|
margin:1em;
|
||||||
padding:.5em;
|
padding:.5em;
|
||||||
background-color:#552A64;
|
background-color:#552A64;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
:global(h4) {
|
:global(h4) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color:#F1482D;
|
color:#F1482D;
|
||||||
}
|
}
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse:collapse;
|
border-collapse:collapse;
|
||||||
th, td {
|
th, td {
|
||||||
text-align:right;
|
text-align:right;
|
||||||
padding:.3em;
|
padding:.3em;
|
||||||
}
|
}
|
||||||
th:first-child, td:first-child{
|
th:first-child, td:first-child{
|
||||||
text-align:left;
|
text-align:left;
|
||||||
}
|
}
|
||||||
tr:nth-child(2n){
|
tr:nth-child(2n){
|
||||||
background-color: #0002;
|
background-color: #0002;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a, .price {
|
a, .price {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color:#f60;
|
color:#f60;
|
||||||
}
|
}
|
||||||
a:hover{
|
a:hover{
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
.price {
|
.price {
|
||||||
font-size:2em;
|
font-size:2em;
|
||||||
margin-bottom:.4em;
|
margin-bottom:.4em;
|
||||||
}
|
}
|
||||||
h4 {
|
h4 {
|
||||||
margin:.4em;
|
margin:.4em;
|
||||||
}
|
}
|
||||||
.trade-currency {
|
.trade-currency {
|
||||||
font-size:1em;
|
font-size:1em;
|
||||||
color:#fff6;
|
color:#fff6;
|
||||||
font-weight:bold;
|
font-weight:bold;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
@media only screen and (max-width: 600px) {
|
@media only screen and (max-width: 600px) {
|
||||||
.row {
|
.row {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
width: 97%!important;
|
width: 97%!important;
|
||||||
}
|
}
|
||||||
.card {
|
.card {
|
||||||
margin:.5em 0;
|
margin:.5em 0;
|
||||||
padding:.5em 0;
|
padding:.5em 0;
|
||||||
}
|
}
|
||||||
.header {
|
.header {
|
||||||
padding:.2em 0;
|
padding:.2em 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.header > * {
|
.header > * {
|
||||||
display:flex;
|
display:flex;
|
||||||
width:33.3%;
|
width:33.3%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap:.2em;
|
gap:.2em;
|
||||||
}
|
}
|
||||||
@media only screen and (max-width: 600px){
|
@media only screen and (max-width: 600px){
|
||||||
.header > * {
|
.header > * {
|
||||||
width:initial;
|
width:initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,6 +1,6 @@
|
||||||
import { offers, trades } from "$lib/server/context";
|
import { liquidity, trades } from "$lib/server/context";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
|
|
||||||
export function load() {
|
export function load() {
|
||||||
return { trades: get(trades), offers: get(offers) };
|
return { trades: get(trades), liquidity: get(liquidity) };
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,9 +111,6 @@ const gridLayout = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let w;
|
let w;
|
||||||
const liquidity = Object.values(data.offers)
|
|
||||||
.flat()
|
|
||||||
.reduce((a, b) => a + Number.parseInt(b.amount), 0);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
@ -125,77 +122,81 @@ const liquidity = Object.values(data.offers)
|
||||||
<h4>XMR/USD</h4>
|
<h4>XMR/USD</h4>
|
||||||
<span class="price">{formatPrice(grouped["USD"][0].price, "USD", true)}</span>
|
<span class="price">{formatPrice(grouped["USD"][0].price, "USD", true)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col card">
|
<div class="col card">
|
||||||
<h4>Liquidity</h4>
|
<h4>Liquidity</h4>
|
||||||
<span class="price">{formatPrice(liquidity, "XMR", true, false)}</span>
|
<span class="price">{formatPrice(data.liquidity, "XMR", true, false)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col card" style="flex:1;" bind:clientWidth={w}>
|
<div class="col card" style="flex:1;" bind:clientWidth={w}>
|
||||||
<h4>Price XMR/<select bind:value={key}>
|
<h4>Price XMR/<select bind:value={key}>
|
||||||
{#each Object.keys(grouped) as key}
|
{#each Object.keys(grouped) as key}
|
||||||
<option>{key}</option>
|
<option>{key}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select></h4>
|
</select></h4>
|
||||||
|
|
||||||
<Chart width={w-20} height={300} container={{class:"row"}} layout={chartLayout} grid={gridLayout}>
|
<Chart width={w-20} height={300} container={{class:"row"}} layout={chartLayout} grid={gridLayout}>
|
||||||
<CandlestickSeries data={trades} reactive={true} priceFormat={{minMove:10**-precision, precision:precision}}></CandlestickSeries>
|
<CandlestickSeries data={trades} reactive={true} priceFormat={{minMove:10**-precision, precision:precision}}></CandlestickSeries>
|
||||||
<TimeScale rightBarStaysOnScroll={true} rightOffset={0}/>
|
|
||||||
</Chart>
|
|
||||||
</div>
|
|
||||||
<div class="col card" style="flex:1">
|
|
||||||
<h4>
|
|
||||||
<select bind:value={interval}>
|
|
||||||
<option value="3600000">Hourly</option>
|
|
||||||
<option value="86400000">Daily</option>
|
|
||||||
<option value="604800000">Weekly</option>
|
|
||||||
</select> Volume</h4>
|
|
||||||
<Chart width={w-20} height={300} container={{class:"row"}} layout={chartLayout} grid={gridLayout}>
|
|
||||||
<LineSeries data={volume} reactive={true} priceFormat={{precision:2, minMove:.01}}>
|
|
||||||
<PriceScale scaleMargins={{bottom:.4, top:.1}}/>
|
|
||||||
</LineSeries>
|
|
||||||
<HistogramSeries data={swaps} reactive={true} priceScaleId="" priceFormat={{precision:0, minMove:1}}>
|
|
||||||
<PriceScale scaleMargins={{top:.7, bottom:0}}/>
|
|
||||||
</HistogramSeries>
|
|
||||||
<TimeScale rightBarStaysOnScroll={true} rightOffset={0}/>
|
<TimeScale rightBarStaysOnScroll={true} rightOffset={0}/>
|
||||||
</Chart>
|
</Chart>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col card" style="flex:1">
|
||||||
|
<h4>
|
||||||
|
<select bind:value={interval}>
|
||||||
|
<option value="3600000">Hourly</option>
|
||||||
|
<option value="86400000">Daily</option>
|
||||||
|
<option value="604800000">Weekly</option>
|
||||||
|
</select> Volume</h4>
|
||||||
|
<Chart width={w-20} height={300} container={{class:"row"}} layout={chartLayout} grid={gridLayout}>
|
||||||
|
<LineSeries data={volume} reactive={true} priceFormat={{precision:2, minMove:.01}}>
|
||||||
|
<PriceScale scaleMargins={{bottom:.4, top:.1}}/>
|
||||||
|
</LineSeries>
|
||||||
|
<HistogramSeries data={swaps} reactive={true} priceScaleId="" priceFormat={{precision:0, minMove:1}}>
|
||||||
|
<PriceScale scaleMargins={{top:.7, bottom:0}}/>
|
||||||
|
</HistogramSeries>
|
||||||
|
<TimeScale rightBarStaysOnScroll={true} rightOffset={0}/>
|
||||||
|
</Chart>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="card col">
|
<div class="card col">
|
||||||
<h4>Markets</h4>
|
<h4>Markets</h4>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tbody>
|
||||||
<th>Currency</th>
|
<tr>
|
||||||
<th>Price</th>
|
<th>Currency</th>
|
||||||
<th>Trades</th>
|
<th>Price</th>
|
||||||
</tr>
|
<th>Trades</th>
|
||||||
{#each Object.values(Object.groupBy(data.trades, ({currency}) => currency)).toSorted((a,b) => b.length - a.length || (b[0].currency < a[0].currency ? 1 : -1)).slice(0, 16) as market}
|
</tr>
|
||||||
<tr>
|
{#each Object.values(Object.groupBy(data.trades, ({currency}) => currency)).toSorted((a,b) => b.length - a.length || (b[0].currency < a[0].currency ? 1 : -1)).slice(0, 16) as market}
|
||||||
<td><a href="market/{market[0].currency}">{getAsset(market[0].currency).name} ({market[0].currency})</a></td>
|
<tr>
|
||||||
<td>{formatPrice(market[0].price, market[0].currency, true, false)}</td>
|
<td><a href="market/{market[0].currency}">{getAsset(market[0].currency).name} ({market[0].currency})</a></td>
|
||||||
<td>{market.length}</td>
|
<td>{formatPrice(market[0].price, market[0].currency, true, false)}</td>
|
||||||
</tr>
|
<td>{market.length}</td>
|
||||||
{/each}
|
</tr>
|
||||||
</table>
|
{/each}
|
||||||
<h4><a href="markets">View more »</a></h4>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h4><a href="markets">View more »</a></h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="card col">
|
<div class="card col">
|
||||||
<h4>Trades</h4>
|
<h4>Trades</h4>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tbody>
|
||||||
<th>Date</th>
|
<tr>
|
||||||
<th>Amount (XMR)</th>
|
<th>Date</th>
|
||||||
<th>Amount</th>
|
<th>Amount (XMR)</th>
|
||||||
</tr>
|
<th>Amount</th>
|
||||||
{#each data.trades.slice(0, 16) as trade}
|
</tr>
|
||||||
<tr>
|
{#each data.trades.slice(0, 16) as trade}
|
||||||
<td>{new Date(trade.date).toISOString().replace("T", " ").replace(/\.\d*Z/, "")}</td>
|
<tr>
|
||||||
<td>{formatPrice(trade.xmrAmount, "XMR", false, false)}</td>
|
<td>{new Date(trade.date).toISOString().replace("T", " ").replace(/\.\d*Z/, "")}</td>
|
||||||
<td>{formatPrice(trade.amount, trade.currency, false, false)} <span class="trade-currency">{trade.currency}</span></td>
|
<td>{formatPrice(trade.xmrAmount, "XMR", false, false)}</td>
|
||||||
</tr>
|
<td>{formatPrice(trade.amount, trade.currency, false, false)} <span class="trade-currency">{trade.currency}</span></td>
|
||||||
{/each}
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<h4><a href="trades">View more »</a></h4>
|
<h4><a href="trades">View more »</a></h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -71,69 +71,75 @@ const BUY_SELL = isMoneroQuote(market) ? ["SELL", "BUY"] : ["BUY", "SELL"];
|
||||||
<title>{marketPair} - Haveno Markets</title>
|
<title>{marketPair} - Haveno Markets</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col card" bind:clientWidth={w}>
|
<div class="col card" bind:clientWidth={w}>
|
||||||
<h4>{marketPair}</h4>
|
<h4>{marketPair}</h4>
|
||||||
<span class="price">{formatPrice(data.trades?.[0]?.price, market, true, false)}</span>
|
<span class="price">{formatPrice(data.trades?.[0]?.price, market, true, false)}</span>
|
||||||
<Chart width={w-20} height={500} container={{class:"row"}} layout={chartLayout} grid={gridLayout}>
|
<Chart width={w-20} height={500} container={{class:"row"}} layout={chartLayout} grid={gridLayout}>
|
||||||
<CandlestickSeries data={trades} reactive={true} priceFormat={{minMove:10**-precision, precision:precision}}></CandlestickSeries>
|
<CandlestickSeries data={trades} reactive={true} priceFormat={{minMove:10**-precision, precision:precision}}></CandlestickSeries>
|
||||||
<TimeScale rightBarStaysOnScroll={true} rightOffset={0}/>
|
<TimeScale rightBarStaysOnScroll={true} rightOffset={0}/>
|
||||||
</Chart>
|
</Chart>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col card">
|
<div class="col card">
|
||||||
<h4>Buy Offers</h4>
|
<h4>Buy Offers</h4>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tbody>
|
||||||
<th>Price</th>
|
<tr>
|
||||||
<th>Amount (XMR)</th>
|
<th>Price</th>
|
||||||
<th>Amount ({market})</th>
|
<th>Amount (XMR)</th>
|
||||||
</tr>
|
<th>Amount ({market})</th>
|
||||||
{#each data.offers[BUY_SELL[0]]?.toSorted((a,b) => a.price < b.price ? 1 : -1)||[] as offer}
|
</tr>
|
||||||
<tr title={offer.paymentMethod}>
|
{#each data.offers[BUY_SELL[0]]?.toSorted((a,b) => a.price < b.price ? 1 : -1)||[] as offer}
|
||||||
<td>{formatPrice(offer.price, market, false, false)}</td>
|
<tr title={offer.paymentMethod}>
|
||||||
<td>{formatPrice(offer.amount, "XMR", false, false)}</td>
|
<td>{formatPrice(offer.price, market, false, false)}</td>
|
||||||
<td>{formatPrice(offer.primaryMarketAmount, market, false, false)}</td>
|
<td>{formatPrice(offer.amount, "XMR", false, false)}</td>
|
||||||
</tr>
|
<td>{formatPrice(offer.primaryMarketAmount, market, false, false)}</td>
|
||||||
{/each}
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="col card">
|
<div class="col card">
|
||||||
<h4>Sell Offers</h4>
|
<h4>Sell Offers</h4>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tbody>
|
||||||
<th>Price</th>
|
<tr>
|
||||||
<th>Amount (XMR)</th>
|
<th>Price</th>
|
||||||
<th>Amount ({market})</th>
|
<th>Amount (XMR)</th>
|
||||||
</tr>
|
<th>Amount ({market})</th>
|
||||||
{#each data.offers[BUY_SELL[1]]?.toSorted((a,b) => a.price > b.price ? 1 : -1)||[] as offer}
|
</tr>
|
||||||
<tr title={offer.paymentMethod}>
|
{#each data.offers[BUY_SELL[1]]?.toSorted((a,b) => a.price > b.price ? 1 : -1)||[] as offer}
|
||||||
<td>{formatPrice(offer.price, market, false, false)}</td>
|
<tr title={offer.paymentMethod}>
|
||||||
<td>{formatPrice(offer.amount, "XMR", false, false)}</td>
|
<td>{formatPrice(offer.price, market, false, false)}</td>
|
||||||
<td>{formatPrice(offer.primaryMarketAmount, market, false, false)}</td>
|
<td>{formatPrice(offer.amount, "XMR", false, false)}</td>
|
||||||
</tr>
|
<td>{formatPrice(offer.primaryMarketAmount, market, false, false)}</td>
|
||||||
{/each}
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col card">
|
<div class="col card">
|
||||||
<h4>Last Trades</h4>
|
<h4>Last Trades</h4>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tbody>
|
||||||
<th>Date</th>
|
<tr>
|
||||||
<th>Price</th>
|
<th>Date</th>
|
||||||
<th>Amount (XMR)</th>
|
<th>Price</th>
|
||||||
<th>Amount ({market})</th>
|
<th>Amount (XMR)</th>
|
||||||
</tr>
|
<th>Amount ({market})</th>
|
||||||
{#each data.trades as trade}
|
</tr>
|
||||||
<tr>
|
{#each data.trades as trade}
|
||||||
<td>{new Date(trade.date).toISOString().replace("T", " ").replace(/\.\d*Z/, "")}</td>
|
<tr>
|
||||||
<td>{formatPrice(trade.price, trade.currency, false, false)}</td>
|
<td>{new Date(trade.date).toISOString().replace("T", " ").replace(/\.\d*Z/, "")}</td>
|
||||||
<td>{formatPrice(trade.xmrAmount, "XMR", false, false)}</td>
|
<td>{formatPrice(trade.price, trade.currency, false, false)}</td>
|
||||||
<td>{formatPrice(trade.amount, trade.currency, false, false)}</td>
|
<td>{formatPrice(trade.xmrAmount, "XMR", false, false)}</td>
|
||||||
</tr>
|
<td>{formatPrice(trade.amount, trade.currency, false, false)}</td>
|
||||||
{/each}
|
</tr>
|
||||||
</table>
|
{/each}
|
||||||
</div>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -1,12 +1,12 @@
|
||||||
import adapter from "svelte-adapter-bun";
|
import adapter from "svelte-adapter-bun";
|
||||||
import preprocess from "svelte-preprocess";
|
import { sveltePreprocess } from "svelte-preprocess";
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
kit: {
|
kit: {
|
||||||
adapter: adapter(),
|
adapter: adapter(),
|
||||||
},
|
},
|
||||||
preprocess: [preprocess()],
|
preprocess: sveltePreprocess(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
Loading…
Reference in a new issue