group offers by price

with a checkbox that allows showing each individual order
This commit is contained in:
wireless_purple 2024-10-12 20:27:22 +00:00
parent a921561083
commit 6bbc71ae39
8 changed files with 146 additions and 62 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -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"

View file

@ -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 {

View file

@ -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

View file

@ -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),

View file

@ -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

View 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>

View file

@ -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