mirror of
https://gitea.gf4.pw/gf4/haveno-markets.git
synced 2024-12-22 08:09:21 +00:00
group offers by price
with a checkbox that allows showing each individual order
This commit is contained in:
parent
a921561083
commit
6bbc71ae39
8 changed files with 146 additions and 62 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
12
package.json
12
package.json
|
@ -11,12 +11,12 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.3",
|
"@biomejs/biome": "^1.9.3",
|
||||||
"@sveltejs/kit": "^2.6.1",
|
"@sveltejs/kit": "^2.7.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.7",
|
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.8",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-svelte": "^3.2.5",
|
"prettier-plugin-svelte": "^3.2.7",
|
||||||
"sass": "^1.79.4",
|
"sass": "^1.79.5",
|
||||||
"svelte": "^5.0.0-next.262",
|
"svelte": "^5.0.0-next.264",
|
||||||
"svelte-adapter-bun": "^0.5.2",
|
"svelte-adapter-bun": "^0.5.2",
|
||||||
"svelte-preprocess": "^6.0.3",
|
"svelte-preprocess": "^6.0.3",
|
||||||
"vite": "^5.4.8"
|
"vite": "^5.4.8"
|
||||||
|
|
|
@ -67,6 +67,9 @@ Object.groupBy ||= (values, keyFinder) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss" global>
|
<style lang="scss" global>
|
||||||
|
html {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
.app {
|
.app {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -127,7 +130,7 @@ Object.groupBy ||= (values, keyFinder) => {
|
||||||
td:first-child {
|
td:first-child {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
tbody tr:nth-child(2n) {
|
tbody tr:nth-child(2n + 1) {
|
||||||
background-color: #0002;
|
background-color: #0002;
|
||||||
}
|
}
|
||||||
tfoot {
|
tfoot {
|
||||||
|
|
|
@ -190,12 +190,14 @@ let w = $state();
|
||||||
<div class="card col">
|
<div class="card col">
|
||||||
<h4>Markets</h4>
|
<h4>Markets</h4>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Currency</th>
|
<th>Currency</th>
|
||||||
<th>Price</th>
|
<th>Price</th>
|
||||||
<th>Trades</th>
|
<th>Trades</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
{#each Object.values(Object.groupBy(data.trades, ({ currency }) => currency))
|
{#each Object.values(Object.groupBy(data.trades, ({ currency }) => currency))
|
||||||
.toSorted((a, b) => b.length - a.length || (b[0].currency < a[0].currency ? 1 : -1))
|
.toSorted((a, b) => b.length - a.length || (b[0].currency < a[0].currency ? 1 : -1))
|
||||||
.slice(0, 16) as market}
|
.slice(0, 16) as market}
|
||||||
|
@ -223,12 +225,14 @@ let w = $state();
|
||||||
<div class="card col">
|
<div class="card col">
|
||||||
<h4>Trades</h4>
|
<h4>Trades</h4>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
<th>Amount (XMR)</th>
|
<th>Amount (XMR)</th>
|
||||||
<th>Amount</th>
|
<th>Amount</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
{#each data.trades.slice(0, 16) as trade}
|
{#each data.trades.slice(0, 16) as trade}
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
|
|
|
@ -8,6 +8,12 @@ export async function load({ params }) {
|
||||||
get(offers)[params.market],
|
get(offers)[params.market],
|
||||||
({ direction }) => direction,
|
({ direction }) => direction,
|
||||||
);
|
);
|
||||||
|
groupedOffers.BUY = groupedOffers.BUY
|
||||||
|
? Object.groupBy(groupedOffers.BUY, ({ price }) => price)
|
||||||
|
: [];
|
||||||
|
groupedOffers.SELL = groupedOffers.SELL
|
||||||
|
? Object.groupBy(groupedOffers.SELL, ({ price }) => price)
|
||||||
|
: [];
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
trades: get(trades).filter(({ currency }) => currency === params.market),
|
trades: get(trades).filter(({ currency }) => currency === params.market),
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
isMoneroQuote,
|
isMoneroQuote,
|
||||||
} from "$lib/formatPrice";
|
} from "$lib/formatPrice";
|
||||||
import { CandlestickSeries, Chart, TimeScale } from "svelte-lightweight-charts";
|
import { CandlestickSeries, Chart, TimeScale } from "svelte-lightweight-charts";
|
||||||
|
import Offers from "./Offers.svelte";
|
||||||
|
|
||||||
const market = $page.params.market;
|
const market = $page.params.market;
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
@ -66,6 +67,8 @@ const gridLayout = {
|
||||||
|
|
||||||
const marketPair = isMoneroQuote(market) ? `${market}/XMR` : `XMR/${market}`;
|
const marketPair = isMoneroQuote(market) ? `${market}/XMR` : `XMR/${market}`;
|
||||||
const BUY_SELL = isMoneroQuote(market) ? ["SELL", "BUY"] : ["BUY", "SELL"];
|
const BUY_SELL = isMoneroQuote(market) ? ["SELL", "BUY"] : ["BUY", "SELL"];
|
||||||
|
|
||||||
|
let showOrders = $state(false);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
@ -96,70 +99,41 @@ const BUY_SELL = isMoneroQuote(market) ? ["SELL", "BUY"] : ["BUY", "SELL"];
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col card">
|
<Offers
|
||||||
<h4>Buy Offers</h4>
|
offers={Object.values(data.offers[BUY_SELL[0]])?.toSorted(
|
||||||
<table>
|
(a, b) => b[0].price - a[0].price,
|
||||||
<tbody>
|
)}
|
||||||
<tr>
|
{market}
|
||||||
<th>Price</th>
|
title="Buy Offers"
|
||||||
<th>Amount (XMR)</th>
|
{showOrders}
|
||||||
<th>Amount ({market})</th>
|
/>
|
||||||
</tr>
|
<Offers
|
||||||
{#each data.offers[BUY_SELL[0]]?.toSorted((a, b) => b.price - a.price) || [] as offer}
|
offers={Object.values(data.offers[BUY_SELL[1]])?.toSorted(
|
||||||
<tr title={offer.paymentMethod}>
|
(a, b) => a[0].price - b[0].price,
|
||||||
<td>{formatPrice(offer.price, market, false, false)}</td>
|
)}
|
||||||
<td>{formatPrice(offer.amount, "XMR", false, false)}</td>
|
{market}
|
||||||
<td
|
title="Sell Offers"
|
||||||
>{formatPrice(
|
{showOrders}
|
||||||
offer.primaryMarketAmount,
|
/>
|
||||||
market,
|
</div>
|
||||||
false,
|
<div class="row">
|
||||||
false,
|
<div class="col">
|
||||||
)}</td
|
<input type="checkbox" bind:checked={showOrders} />Show Individual Offers?
|
||||||
>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="col card">
|
|
||||||
<h4>Sell Offers</h4>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th>Price</th>
|
|
||||||
<th>Amount (XMR)</th>
|
|
||||||
<th>Amount ({market})</th>
|
|
||||||
</tr>
|
|
||||||
{#each data.offers[BUY_SELL[1]]?.toSorted((a, b) => a.price - b.price) || [] as offer}
|
|
||||||
<tr title={offer.paymentMethod}>
|
|
||||||
<td>{formatPrice(offer.price, market, false, false)}</td>
|
|
||||||
<td>{formatPrice(offer.amount, "XMR", false, false)}</td>
|
|
||||||
<td
|
|
||||||
>{formatPrice(
|
|
||||||
offer.primaryMarketAmount,
|
|
||||||
market,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
)}</td
|
|
||||||
>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col card">
|
<div class="col card">
|
||||||
<h4>Latest Trades</h4>
|
<h4>Latest Trades</h4>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
<th>Price</th>
|
<th>Price</th>
|
||||||
<th>Amount (XMR)</th>
|
<th>Amount (XMR)</th>
|
||||||
<th>Amount ({market})</th>
|
<th>Amount ({market})</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
{#each data.trades as trade}
|
{#each data.trades as trade}
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
|
|
95
src/routes/market/[market]/Offers.svelte
Normal file
95
src/routes/market/[market]/Offers.svelte
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { formatPrice } from "$lib/formatPrice";
|
||||||
|
|
||||||
|
let { offers = [], market, title, showOrders } = $props();
|
||||||
|
offers = Object.values(offers);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="col card" style="--text-align: {showOrders ? 'left' : 'right'}">
|
||||||
|
<h4>{title}</h4>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Price</th>
|
||||||
|
{#if showOrders}<th>Payment Method</th>{/if}
|
||||||
|
<th>Amount (XMR)</th>
|
||||||
|
<th>Amount ({market})</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#if !showOrders}
|
||||||
|
{#each offers as offer}
|
||||||
|
<tr>
|
||||||
|
<td>{formatPrice(offer[0].price, market, false, false)}</td>
|
||||||
|
<td
|
||||||
|
>{formatPrice(
|
||||||
|
offer.reduce((a, b) => a + b.amount, 0),
|
||||||
|
"XMR",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)}</td
|
||||||
|
>
|
||||||
|
<td
|
||||||
|
>{formatPrice(
|
||||||
|
offer.reduce((a, b) => a + b.primaryMarketAmount, 0),
|
||||||
|
market,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)}</td
|
||||||
|
>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
{:else}
|
||||||
|
{#each offers.flat() as offer}
|
||||||
|
<tr>
|
||||||
|
<td>{formatPrice(offer.price, market, false, false)}</td>
|
||||||
|
{#if showOrders}<td>{offer.paymentMethod}</td>{/if}
|
||||||
|
<td>{formatPrice(offer.amount, "XMR", false, false)}</td>
|
||||||
|
<td
|
||||||
|
>{formatPrice(
|
||||||
|
offer.primaryMarketAmount,
|
||||||
|
market,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)}</td
|
||||||
|
>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td>{offers.flat().reduce((a, b) => a + 1, 0)} Offers</td>
|
||||||
|
|
||||||
|
{#if showOrders}<td></td>{/if}
|
||||||
|
<td
|
||||||
|
>{formatPrice(
|
||||||
|
offers.flat().reduce((a, b) => a + b.amount, 0),
|
||||||
|
"XMR",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
) || ""}</td
|
||||||
|
>
|
||||||
|
<td
|
||||||
|
>{formatPrice(
|
||||||
|
offers.flat().reduce((a, b) => a + b.primaryMarketAmount, 0),
|
||||||
|
market,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
) || ""}</td
|
||||||
|
>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
&:nth-child(2) {
|
||||||
|
text-align: var(--text-align);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -124,7 +124,7 @@ let w = $state();
|
||||||
<div class="card col">
|
<div class="card col">
|
||||||
<h4>Markets</h4>
|
<h4>Markets</h4>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Currency</th>
|
<th>Currency</th>
|
||||||
<th>Price</th>
|
<th>Price</th>
|
||||||
|
@ -132,6 +132,8 @@ let w = $state();
|
||||||
<th>Volume (XMR)</th>
|
<th>Volume (XMR)</th>
|
||||||
<th>Trades</th>
|
<th>Trades</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
{#each Object.values(markets).toSorted((a, b) => (b.trades?.length || 0) - (a.trades?.length || 0) || (b.offers?.length || 0) - (a.offers?.length || 0) || (b.code < a.code ? 1 : -1)) as market}
|
{#each Object.values(markets).toSorted((a, b) => (b.trades?.length || 0) - (a.trades?.length || 0) || (b.offers?.length || 0) - (a.offers?.length || 0) || (b.code < a.code ? 1 : -1)) as market}
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
|
|
Loading…
Reference in a new issue