src/lib/export/__tests__/pricerWorkbook.test.ts

64 lines
import Excel from "exceljs";
import { describe, expect, it } from "vitest";
import { initialTable, setSolveForTable, twoWay } from "../../grid/rowModel";
import { DEFAULT_ALL_IN, SX5E_MARKET as m, SX5E_TRF_RUN } from "../../market/sx5e";
import { buildPricerWorkbook } from "../pricerWorkbook";

const rows = setSolveForTable(
  m,
  initialTable(m, SX5E_TRF_RUN.map((q) => ({ expiry: q.expiry, trf: twoWay(q.bid, q.offer) })), DEFAULT_ALL_IN),
  2,
  "price",
);

/** Round-trips through an actual .xlsx buffer, as Excel would read it. */
async function roundTrip() {
  const buffer = await buildPricerWorkbook(Excel, m, rows).xlsx.writeBuffer();
  const wb = new Excel.Workbook();
  await wb.xlsx.load(buffer as ArrayBuffer);
  return wb.getWorksheet("Pricer")!;
}

describe("pricer workbook", () => {
  it("has the market block, both header rows and one row per tenor", async () => {
    const ws = await roundTrip();
    expect(ws.getCell("A2").value).toBe("Valuation date");
    expect(ws.getCell("B3").value).toBe(m.spot);
    expect(ws.getCell("E2").value).toBe("Exported at");
    expect(ws.getCell("E3").value).toBeInstanceOf(Date);
    expect(ws.getCell("A5").value).toBe("Expiry");
    expect(ws.getCell("D5").value).toBe("Fwd (pts)");
    expect(ws.getCell("D6").value).toBe("Bid");
    expect(ws.getCell("E6").value).toBe("Mark");
    expect(ws.getCell("A7").value).toBe("Dec26");
    expect(ws.getCell("A11").value).toBe("Dec30");
    expect(ws.getCell("C9").value).toBe("Prices"); // Dec28 solves for prices
    expect(ws.getCell("C7").value).toBe("Funding");
  });

  it("writes values as numbers, with all-in as a percentage", async () => {
    const ws = await roundTrip();
    // Dec27 TRF (columns J/K/L) and All In (columns V/W/X)
    expect(ws.getCell("J8").value).toBeCloseTo(rows[1].trf.bid, 10);
    expect(ws.getCell("K8").value).toBeCloseTo(rows[1].trf.mark, 10);
    expect(ws.getCell("V8").value).toBeCloseTo(0.87, 10);
    expect(ws.getCell("V8").numFmt).toBe("0.00%");
    expect(ws.getCell("J8").numFmt).toBe("#,##0.00");
  });

  it("colours cells like the grid and outlines the solved slot", async () => {
    const ws = await roundTrip();
    const fill = (a: string) => (ws.getCell(a).fill as { fgColor?: { argb?: string } }).fgColor?.argb;
    expect(fill("J7")).toBe("FFC9C4F3"); // Dec26 TRF bid: lit price
    expect(fill("K7")).toBe("FFF6EBCF"); // mark
    expect(fill("M7")).toBe("FFECF7F2"); // Dec26 Term Funding bid: solved assumption
    // Dec26 solves funding: outline on top and left of Term Funding bid, none between Term and Forward Funding
    expect(ws.getCell("M7").border?.top?.style).toBe("medium");
    expect(ws.getCell("M7").border?.left?.style).toBe("medium");
    expect(ws.getCell("O7").border?.right).toBeUndefined();
    // Dec28 solves prices: its Fwd cells are outlined, its funding is not
    expect(ws.getCell("D9").border?.left?.style).toBe("medium");
    expect(ws.getCell("M9").border?.top).toBeUndefined();
  });
});