globalOrderCoordinator.test.ts 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import { describe, it, expect, vi } from "vitest";
  2. import {
  3. GlobalOrderCoordinator,
  4. type GlobalOrderSnapshot,
  5. type ValidationIntent
  6. } from "../packages/execution/src/globalOrderCoordinator";
  7. function makeSnapshot(partial: Partial<GlobalOrderSnapshot>): GlobalOrderSnapshot {
  8. return {
  9. orderId: partial.orderId ?? "order-1",
  10. clientOrderId: partial.clientOrderId,
  11. accountId: partial.accountId ?? "maker",
  12. symbol: partial.symbol ?? "BTC",
  13. side: partial.side ?? "buy",
  14. price: partial.price ?? 50_000,
  15. timestamp: partial.timestamp ?? Date.now()
  16. };
  17. }
  18. describe("GlobalOrderCoordinator", () => {
  19. it("registers and releases orders", () => {
  20. const coordinator = new GlobalOrderCoordinator();
  21. const snapshot = makeSnapshot({ clientOrderId: "client-1" });
  22. coordinator.register(snapshot);
  23. expect(coordinator.list()).toHaveLength(1);
  24. expect(coordinator.peek(snapshot.orderId)).toEqual(snapshot);
  25. expect(coordinator.peekByClientId("client-1")).toEqual(snapshot);
  26. coordinator.release(snapshot.orderId);
  27. expect(coordinator.list()).toHaveLength(0);
  28. expect(coordinator.peek(snapshot.orderId)).toBeUndefined();
  29. expect(coordinator.peekByClientId("client-1")).toBeUndefined();
  30. });
  31. it("detects cross-account conflicts", () => {
  32. const coordinator = new GlobalOrderCoordinator();
  33. coordinator.register(makeSnapshot({ orderId: "sell-1", side: "sell", price: 50_100 }));
  34. const onConflict = vi.fn();
  35. coordinator.on("conflict", onConflict);
  36. const intent: ValidationIntent = {
  37. accountId: "hedger",
  38. symbol: "BTC",
  39. side: "buy",
  40. price: 50_200
  41. };
  42. expect(() => coordinator.validate(intent)).toThrow(/STP violation/);
  43. expect(onConflict).toHaveBeenCalledTimes(1);
  44. const payload = onConflict.mock.calls[0][0];
  45. expect(payload.intent).toEqual(intent);
  46. expect(payload.conflicts).toHaveLength(1);
  47. expect(payload.conflicts[0].orderId).toBe("sell-1");
  48. });
  49. it("ignores orders from the same account", () => {
  50. const coordinator = new GlobalOrderCoordinator();
  51. coordinator.register(makeSnapshot({ orderId: "sell-1", accountId: "maker", side: "sell" }));
  52. expect(() =>
  53. coordinator.validate({
  54. accountId: "maker",
  55. symbol: "BTC",
  56. side: "buy",
  57. price: 49_900
  58. })
  59. ).not.toThrow();
  60. });
  61. it("respects tolerance when evaluating potential crosses", () => {
  62. const coordinator = new GlobalOrderCoordinator({ stpToleranceBps: 5 });
  63. coordinator.register(makeSnapshot({ orderId: "ask-1", side: "sell", price: 50_000 }));
  64. // Within tolerance -> treated as conflict.
  65. expect(() =>
  66. coordinator.validate({
  67. accountId: "hedger",
  68. symbol: "BTC",
  69. side: "buy",
  70. price: 50_020
  71. })
  72. ).toThrow();
  73. // Far away (non-cross) -> no conflict.
  74. expect(() =>
  75. coordinator.validate({
  76. accountId: "hedger",
  77. symbol: "BTC",
  78. side: "buy",
  79. price: 49_000
  80. })
  81. ).not.toThrow();
  82. });
  83. });