bursa / client / test / view.test.mjs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// The view's pure decisions: the decimal⇄rational bridge, the book, and the
// projections. Prices never touch a double, and these tests are where that is
// cheap to prove — a conversion that rounded would fail the round trips.

import test from "node:test";
import assert from "node:assert/strict";
import * as L from "../test-dist/testlib.js";
import {
  ACTORS, PAIR, SEED_MAKER, SEED_TAKER, TS_FIXED,
  entry, makeQuote, opQuote, opTake,
} from "./vectors/independent.mjs";

const { makerPub: MAKER, takerPub: TAKER } = ACTORS;
const T = TS_FIXED;

async function snap(entries) {
  const f = new L.BursaFold();
  for (const e of structuredClone(entries)) f.ingest(e);
  await f.awaitVerified();
  return f.snapshot();
}

// ---- decimal ⇄ rational -----------------------------------------------------

test("a human's decimal price becomes the wire's rational, dec exponents included", () => {
  // equal exponents: 1.50 quote-major per base-major = 3/2 minor per minor
  assert.deepEqual(L.pxOfDecimal("1.50", 2, 2), { num: 3, den: 2 });
  assert.deepEqual(L.pxOfDecimal("1", 2, 2), { num: 1, den: 1 });
  // differing exponents scale by 10^(qdec-bdec)
  assert.deepEqual(L.pxOfDecimal("1", 0, 2), { num: 100, den: 1 });
  assert.deepEqual(L.pxOfDecimal("1", 2, 0), { num: 1, den: 100 });
  assert.deepEqual(L.pxOfDecimal("0.07", 2, 2), { num: 7, den: 100 });
});

test("unpriceable input is nil, not a guess", () => {
  for (const bad of ["0", "0.00", "", "-1", "1e5", "1,5", ".5", "abc", null, 7]) {
    assert.equal(L.pxOfDecimal(bad, 2, 2), null, JSON.stringify(bad));
  }
});

test("the decimal round-trips, and display TRUNCATES rather than promising more", () => {
  const px = L.pxOfDecimal("1.50", 2, 2);
  assert.equal(L.pxToDecimal(px, 2, 2, 4), "1.5");
  assert.equal(L.pxToDecimal({ num: 1, den: 3 }, 2, 2, 4), "0.3333", "…never 0.3334");
  assert.equal(L.pxToDecimal({ num: 100, den: 1 }, 0, 2, 2), "1", "scaling reverses");
  assert.equal(L.pxToDecimal({ num: 7, den: 100 }, 2, 2, 2), "0.07");
});

// ---- the book -----------------------------------------------------------------

test("bids sort best-first descending, asks ascending, ties in fold order", async () => {
  const q = (idN, side, num) => makeQuote({ seed: SEED_MAKER, idN, side, px: { num, den: 1 }, qty: 100 });
  const s = await snap([
    entry("h01", 1, MAKER, opQuote(T, q(1, "ask", 5))),
    entry("h02", 2, MAKER, opQuote(T, q(2, "ask", 3))),
    entry("h03", 3, MAKER, opQuote(T, q(3, "bid", 1))),
    entry("h04", 4, MAKER, opQuote(T, q(4, "bid", 2))),
    entry("h05", 5, MAKER, opQuote(T, q(5, "bid", 2))), // tie: fold order holds
  ]);
  const b = L.bookOf(s, PAIR);
  assert.deepEqual(b.asks.map((x) => x.o.px.num), [3, 5], "lowest ask first");
  assert.deepEqual(b.bids.map((x) => x.h), ["h04", "h05", "h03"], "highest bid first, tie stable");
  assert.deepEqual([...L.bookPairs(s)], [PAIR]);
});

test("the projections know whose is whose", async () => {
  const ask = makeQuote({ seed: SEED_MAKER, idN: 1, side: "ask", px: { num: 3, den: 2 }, qty: 1000 });
  const s = await snap([
    entry("h01", 1, MAKER, opQuote(T, ask)),
    entry("h02", 2, TAKER, opTake(T, "h01", 300)),
  ]);
  assert.equal(L.myQuotes(s, MAKER).length, 1);
  assert.equal(L.myQuotes(s, TAKER).length, 0);
  assert.equal(L.myMatches(s, MAKER).length, 1, "the maker is a party");
  assert.equal(L.myMatches(s, TAKER).length, 1, "and so is the taker");
  assert.equal(L.myMatches(s, ACTORS.bankBPub).length, 0);
});

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/bursa.git