implement /markets
This commit is contained in:
parent
f0e8c417eb
commit
a01060ba62
7 changed files with 248 additions and 95 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
12
package.json
12
package.json
|
@ -10,14 +10,14 @@
|
|||
"lint": "biome check *"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.8.3",
|
||||
"@sveltejs/kit": "^2.5.20",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||
"sass": "^1.77.8",
|
||||
"svelte": "^5.0.0-next.210",
|
||||
"@biomejs/biome": "^1.9.0",
|
||||
"@sveltejs/kit": "^2.5.27",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.7",
|
||||
"sass": "^1.78.0",
|
||||
"svelte": "^5.0.0-next.246",
|
||||
"svelte-adapter-bun": "^0.5.2",
|
||||
"svelte-preprocess": "^6.0.2",
|
||||
"vite": "^5.3.5"
|
||||
"vite": "^5.4.5"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
|
|
@ -15,76 +15,82 @@ import {
|
|||
TimeScale,
|
||||
} from "svelte-lightweight-charts";
|
||||
|
||||
let {data} = $props();
|
||||
let { data } = $props();
|
||||
const grouped = Object.groupBy(data.trades, ({ currency }) => currency);
|
||||
let interval = $state("86400000");
|
||||
let key = $state("USD");
|
||||
let trades = $derived((() => {
|
||||
let trades = grouped[key]
|
||||
.map((e) => {
|
||||
return {
|
||||
time: new Date(e.date),
|
||||
value: getPrice(e.price, e.currency),
|
||||
};
|
||||
})
|
||||
.toSorted((a, b) => (a.time - b.time));
|
||||
|
||||
trades = Object.groupBy(
|
||||
trades,
|
||||
({ time }) => new Date(time - (time % interval)) / 1000,
|
||||
);
|
||||
|
||||
for (const intervalDate in trades) {
|
||||
trades[intervalDate] = trades[intervalDate].reduce((a, c) => {
|
||||
return {
|
||||
open: a.open ?? c.value,
|
||||
close: c.value,
|
||||
high: (c.value > a.high ? c.value : a.high) ?? c.value,
|
||||
low: (c.value < a.low ? c.value : a.low) ?? c.value,
|
||||
};
|
||||
}, {});
|
||||
trades[intervalDate].time = Number.parseInt(intervalDate, 10);
|
||||
}
|
||||
return Object.values(trades);
|
||||
})());
|
||||
let [volume, swaps] = $derived((() => {
|
||||
let volume = Object.groupBy(
|
||||
data.trades
|
||||
let trades = $derived(
|
||||
(() => {
|
||||
let trades = grouped[key]
|
||||
.map((e) => {
|
||||
return {
|
||||
volume: e.xmrAmount,
|
||||
time: e.date,
|
||||
time: new Date(e.date),
|
||||
value: getPrice(e.price, e.currency),
|
||||
};
|
||||
})
|
||||
.toSorted((a, b) => (a.time - b.time)),
|
||||
({ time }) => new Date(time - (time % interval)) / 1000,
|
||||
);
|
||||
let swaps = {};
|
||||
for (const intervalDate in volume) {
|
||||
swaps[intervalDate] = volume[intervalDate].reduce(
|
||||
(a) => {
|
||||
return {
|
||||
value: a.value + 1,
|
||||
};
|
||||
},
|
||||
{ value: 0 },
|
||||
.toSorted((a, b) => a.time - b.time);
|
||||
|
||||
trades = Object.groupBy(
|
||||
trades,
|
||||
({ time }) => new Date(time - (time % interval)) / 1000,
|
||||
);
|
||||
|
||||
volume[intervalDate] = volume[intervalDate].reduce(
|
||||
(a, c) => {
|
||||
for (const intervalDate in trades) {
|
||||
trades[intervalDate] = trades[intervalDate].reduce((a, c) => {
|
||||
return {
|
||||
value: a.value + c.volume / 10 ** 12,
|
||||
open: a.open ?? c.value,
|
||||
close: c.value,
|
||||
high: (c.value > a.high ? c.value : a.high) ?? c.value,
|
||||
low: (c.value < a.low ? c.value : a.low) ?? c.value,
|
||||
};
|
||||
},
|
||||
{ value: 0 },
|
||||
}, {});
|
||||
trades[intervalDate].time = Number.parseInt(intervalDate, 10);
|
||||
}
|
||||
return Object.values(trades);
|
||||
})(),
|
||||
);
|
||||
let [volume, swaps] = $derived(
|
||||
(() => {
|
||||
let volume = Object.groupBy(
|
||||
data.trades
|
||||
.map((e) => {
|
||||
return {
|
||||
volume: e.xmrAmount,
|
||||
time: e.date,
|
||||
};
|
||||
})
|
||||
.toSorted((a, b) => a.time - b.time),
|
||||
({ time }) => new Date(time - (time % interval)) / 1000,
|
||||
);
|
||||
let swaps = {};
|
||||
for (const intervalDate in volume) {
|
||||
swaps[intervalDate] = volume[intervalDate].reduce(
|
||||
(a) => {
|
||||
return {
|
||||
value: a.value + 1,
|
||||
};
|
||||
},
|
||||
{ value: 0 },
|
||||
);
|
||||
|
||||
volume[intervalDate].time = Number.parseInt(intervalDate, 10);
|
||||
swaps[intervalDate].time = Number.parseInt(intervalDate, 10);
|
||||
}
|
||||
return [Object.values(volume), Object.values(swaps)];
|
||||
})());
|
||||
let precision = $derived(getSignificantDigits(trades.flatMap((e) => [e.open, e.close])));
|
||||
volume[intervalDate] = volume[intervalDate].reduce(
|
||||
(a, c) => {
|
||||
return {
|
||||
value: a.value + c.volume / 10 ** 12,
|
||||
};
|
||||
},
|
||||
{ value: 0 },
|
||||
);
|
||||
|
||||
volume[intervalDate].time = Number.parseInt(intervalDate, 10);
|
||||
swaps[intervalDate].time = Number.parseInt(intervalDate, 10);
|
||||
}
|
||||
return [Object.values(volume), Object.values(swaps)];
|
||||
})(),
|
||||
);
|
||||
let precision = $derived(
|
||||
getSignificantDigits(trades.flatMap((e) => [e.open, e.close])),
|
||||
);
|
||||
|
||||
const chartLayout = {
|
||||
background: {
|
||||
|
@ -188,6 +194,6 @@ let w = $state();
|
|||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
<h4><a href="trades">View more »</a></h4>
|
||||
<h4><a href="markets">View more »</a></h4>
|
||||
</div>
|
||||
</div>
|
|
@ -10,38 +10,42 @@ import {
|
|||
import { CandlestickSeries, Chart, TimeScale } from "svelte-lightweight-charts";
|
||||
|
||||
const market = $page.params.market;
|
||||
let {data} = $props();
|
||||
let { data } = $props();
|
||||
const interval = 86400000;
|
||||
let trades = $derived((() => {
|
||||
let trades = data.trades
|
||||
.map((e) => {
|
||||
return {
|
||||
time: new Date(e.date),
|
||||
value: getPrice(e.price, e.currency, false, false),
|
||||
};
|
||||
})
|
||||
.toSorted((a, b) => (a.time - b.time));
|
||||
let trades = $derived(
|
||||
(() => {
|
||||
let trades = data.trades
|
||||
.map((e) => {
|
||||
return {
|
||||
time: new Date(e.date),
|
||||
value: getPrice(e.price, e.currency, false, false),
|
||||
};
|
||||
})
|
||||
.toSorted((a, b) => a.time - b.time);
|
||||
|
||||
trades = Object.groupBy(
|
||||
trades,
|
||||
({ time }) => new Date(time - (time % interval)) / 1000,
|
||||
);
|
||||
trades = Object.groupBy(
|
||||
trades,
|
||||
({ time }) => new Date(time - (time % interval)) / 1000,
|
||||
);
|
||||
|
||||
for (const intervalDate in trades) {
|
||||
trades[intervalDate] = trades[intervalDate].reduce((a, c) => {
|
||||
return {
|
||||
open: a.open ?? c.value,
|
||||
close: c.value,
|
||||
high: (c.value > a.high ? c.value : a.high) ?? c.value,
|
||||
low: (c.value < a.low ? c.value : a.low) ?? c.value,
|
||||
};
|
||||
}, {});
|
||||
trades[intervalDate].time = Number.parseInt(intervalDate, 10);
|
||||
}
|
||||
return Object.values(trades);
|
||||
})());
|
||||
for (const intervalDate in trades) {
|
||||
trades[intervalDate] = trades[intervalDate].reduce((a, c) => {
|
||||
return {
|
||||
open: a.open ?? c.value,
|
||||
close: c.value,
|
||||
high: (c.value > a.high ? c.value : a.high) ?? c.value,
|
||||
low: (c.value < a.low ? c.value : a.low) ?? c.value,
|
||||
};
|
||||
}, {});
|
||||
trades[intervalDate].time = Number.parseInt(intervalDate, 10);
|
||||
}
|
||||
return Object.values(trades);
|
||||
})(),
|
||||
);
|
||||
|
||||
let precision = $derived(getSignificantDigits(trades.flatMap((e) => [e.open, e.close])));
|
||||
let precision = $derived(
|
||||
getSignificantDigits(trades.flatMap((e) => [e.open, e.close])),
|
||||
);
|
||||
let w = $state();
|
||||
|
||||
const chartLayout = {
|
||||
|
@ -117,7 +121,7 @@ const BUY_SELL = isMoneroQuote(market) ? ["SELL", "BUY"] : ["BUY", "SELL"];
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col card">
|
||||
<h4>Last Trades</h4>
|
||||
<h4>Latest Trades</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
|
|
6
src/routes/markets/+page.server.js
Normal file
6
src/routes/markets/+page.server.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { trades } from "$lib/server/context";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
export function load() {
|
||||
return { trades: get(trades) };
|
||||
}
|
|
@ -1 +1,139 @@
|
|||
Under construction
|
||||
<svelte:options runes={true} />
|
||||
<script>
|
||||
import { formatPrice, getAsset } from "$lib/formatPrice";
|
||||
import {
|
||||
Chart,
|
||||
HistogramSeries,
|
||||
LineSeries,
|
||||
PriceScale,
|
||||
TimeScale,
|
||||
} from "svelte-lightweight-charts";
|
||||
|
||||
let { data } = $props();
|
||||
let interval = $state("86400000");
|
||||
let [volume, swaps] = $derived(
|
||||
(() => {
|
||||
let volume = Object.groupBy(
|
||||
data.trades
|
||||
.map((e) => {
|
||||
return {
|
||||
volume: e.xmrAmount,
|
||||
time: e.date,
|
||||
};
|
||||
})
|
||||
.toSorted((a, b) => a.time - b.time),
|
||||
({ time }) => new Date(time - (time % interval)) / 1000,
|
||||
);
|
||||
let swaps = {};
|
||||
for (const intervalDate in volume) {
|
||||
swaps[intervalDate] = volume[intervalDate].reduce(
|
||||
(a) => {
|
||||
return {
|
||||
value: a.value + 1,
|
||||
};
|
||||
},
|
||||
{ value: 0 },
|
||||
);
|
||||
|
||||
volume[intervalDate] = volume[intervalDate].reduce(
|
||||
(a, c) => {
|
||||
return {
|
||||
value: a.value + c.volume / 10 ** 12,
|
||||
};
|
||||
},
|
||||
{ value: 0 },
|
||||
);
|
||||
|
||||
volume[intervalDate].time = Number.parseInt(intervalDate, 10);
|
||||
swaps[intervalDate].time = Number.parseInt(intervalDate, 10);
|
||||
}
|
||||
return [Object.values(volume), Object.values(swaps)];
|
||||
})(),
|
||||
);
|
||||
|
||||
const chartLayout = {
|
||||
background: {
|
||||
color: "#090020",
|
||||
},
|
||||
textColor: "#f6efff",
|
||||
};
|
||||
const gridLayout = {
|
||||
vertLines: {
|
||||
visible: false,
|
||||
},
|
||||
horzLines: {
|
||||
color: "#FFF5",
|
||||
},
|
||||
};
|
||||
let w = $state();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Markets - Haveno Markets</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="row">
|
||||
<div class="col card" style="flex:1;" bind:clientWidth={w}>
|
||||
<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={500} 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 class="row">
|
||||
<div class="card col">
|
||||
<h4>Markets</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Currency</th>
|
||||
<th>Price</th>
|
||||
<th>Volume (XMR)</th>
|
||||
<th>Trades</th>
|
||||
</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}
|
||||
<tr>
|
||||
<td><a href="market/{market[0].currency}">{getAsset(market[0].currency).name} ({market[0].currency})</a></td>
|
||||
<td>{formatPrice(market[0].price, market[0].currency, true, false)}</td>
|
||||
<td>{formatPrice(market.reduce((a,b) => a + b.xmrAmount, 0), "XMR", false, false)}</td>
|
||||
<td>{market.length}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="card col">
|
||||
<h4>Latest Trades</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Price</th>
|
||||
<th>Amount (XMR)</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
{#each data.trades.slice(0, 64) as trade}
|
||||
<tr>
|
||||
<td>{new Date(trade.date).toISOString().replace("T", " ").replace(/\.\d*Z/, "")}</td>
|
||||
<td>{formatPrice(trade.price, trade.currency, true, false)}</td>
|
||||
<td>{formatPrice(trade.xmrAmount, "XMR", false, false)}</td>
|
||||
<td>{formatPrice(trade.amount, trade.currency, false, false)} <span class="trade-currency">{trade.currency}</span></td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
|
@ -1 +0,0 @@
|
|||
Under construction
|
Loading…
Reference in a new issue