@openmarketsai/order-sheet 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +5 -0
  3. package/README.md +183 -0
  4. package/dist/auth.d.ts +24 -0
  5. package/dist/auth.js +36 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/client.d.ts +158 -0
  8. package/dist/client.js +253 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/errors.d.ts +34 -0
  11. package/dist/errors.js +34 -0
  12. package/dist/errors.js.map +1 -0
  13. package/dist/generated/contract.d.ts +28 -0
  14. package/dist/generated/contract.js +176 -0
  15. package/dist/generated/contract.js.map +1 -0
  16. package/dist/http.d.ts +19 -0
  17. package/dist/http.js +58 -0
  18. package/dist/http.js.map +1 -0
  19. package/dist/index.cjs +1742 -0
  20. package/dist/index.d.ts +41 -0
  21. package/dist/index.js +31 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/math.d.ts +22 -0
  24. package/dist/math.js +55 -0
  25. package/dist/math.js.map +1 -0
  26. package/dist/messages.d.ts +21 -0
  27. package/dist/messages.js +97 -0
  28. package/dist/messages.js.map +1 -0
  29. package/dist/odds.d.ts +12 -0
  30. package/dist/odds.js +33 -0
  31. package/dist/odds.js.map +1 -0
  32. package/dist/quote.d.ts +20 -0
  33. package/dist/quote.js +117 -0
  34. package/dist/quote.js.map +1 -0
  35. package/dist/react.cjs +2137 -0
  36. package/dist/react.d.ts +157 -0
  37. package/dist/react.js +297 -0
  38. package/dist/react.js.map +1 -0
  39. package/dist/sheet.d.ts +77 -0
  40. package/dist/sheet.js +648 -0
  41. package/dist/sheet.js.map +1 -0
  42. package/dist/stream.d.ts +71 -0
  43. package/dist/stream.js +160 -0
  44. package/dist/stream.js.map +1 -0
  45. package/dist/types.d.ts +287 -0
  46. package/dist/types.js +2 -0
  47. package/dist/types.js.map +1 -0
  48. package/dist/ui/styles.css +284 -0
  49. package/dist/ui/theme.d.ts +108 -0
  50. package/dist/ui/theme.js +180 -0
  51. package/dist/ui/theme.js.map +1 -0
  52. package/dist/ui.cjs +2606 -0
  53. package/dist/ui.d.ts +124 -0
  54. package/dist/ui.js +154 -0
  55. package/dist/ui.js.map +1 -0
  56. package/dist/validate.d.ts +20 -0
  57. package/dist/validate.js +126 -0
  58. package/dist/validate.js.map +1 -0
  59. package/dist/venues.d.ts +19 -0
  60. package/dist/venues.js +103 -0
  61. package/dist/venues.js.map +1 -0
  62. package/dist/wire.d.ts +382 -0
  63. package/dist/wire.js +2 -0
  64. package/dist/wire.js.map +1 -0
  65. package/package.json +78 -0
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @openmarketsai/order-sheet — the bet slip, solved.
3
+ *
4
+ * Headless core: framework-free, zero dependencies. Every venue rule is served
5
+ * by the OpenMarkets API (GET /flow/v1/auth/orders/constraints) and the server
6
+ * stays authoritative through /orders/preflight and /orders/buy — this package
7
+ * carries no venue name and no venue rule of its own.
8
+ *
9
+ * import { createOrderSheetClient } from '@openmarketsai/order-sheet'
10
+ *
11
+ * const om = createOrderSheetClient({ apiKey: process.env.OM_EXEC_KEY! })
12
+ * const sheet = om.openSheet({ liquidityHash: cell.liquidity_hash })
13
+ * sheet.set({ stake: { amount: 5 } })
14
+ * await sheet.preflight()
15
+ * const out = await sheet.place() // { status: 'placed', result } | 'blocked' | 'confirm_required'
16
+ *
17
+ * React bindings: '@openmarketsai/order-sheet/react'. Styled sheet: '/ui'.
18
+ */
19
+ export { createOrderSheetClient, resolveOptions, DEFAULT_API_BASE } from './client.js';
20
+ export type { OrderSheetClient, OrderSheetClientOptions, ResolvedClientOptions, OrderSheetLabels, ClientEvents, ClientEventBus } from './client.js';
21
+ export { createSheet, createInertSheet, initialSheetState, newIdempotencyKey, summarize, toPlaceResult, phaseFromOrders, phaseFromSummary } from './sheet.js';
22
+ export type { Sheet, SheetInit } from './sheet.js';
23
+ export { OrderSheetError, isOrderSheetError, userMessageFor } from './errors.js';
24
+ export type { OrderSheetErrorCode, ClientErrorCode } from './errors.js';
25
+ export type { AuthOptions, AppSdkAuthLike } from './auth.js';
26
+ export { credentialSupportsStream } from './auth.js';
27
+ export { createOrderStream } from './stream.js';
28
+ export type { OrderStream, OrderStreamOptions, StreamEvents, StreamStatus, WebSocketCtor, WebSocketLike } from './stream.js';
29
+ export { validateSheet } from './validate.js';
30
+ export type { ValidateInput } from './validate.js';
31
+ export { buildQuote, resolveMaxPrice } from './quote.js';
32
+ export type { QuoteInput } from './quote.js';
33
+ export { buildVenueOptions, fromServerIssue } from './venues.js';
34
+ export type { BuildVenuesInput } from './venues.js';
35
+ export { localIssue, SERVER_CODE_MESSAGES, CLIENT_CODE_SPECS } from './messages.js';
36
+ export { contractsFromAmount, snapPrice, floorToIncrement, isOnGrid, round } from './math.js';
37
+ export { formatPrice, formatMoney, toAmerican, toDecimal, ODDS_FORMATS } from './odds.js';
38
+ export type { OddsFormat } from './odds.js';
39
+ export * from './generated/contract.js';
40
+ export type * from './types.js';
41
+ export type * from './wire.js';
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @openmarketsai/order-sheet — the bet slip, solved.
3
+ *
4
+ * Headless core: framework-free, zero dependencies. Every venue rule is served
5
+ * by the OpenMarkets API (GET /flow/v1/auth/orders/constraints) and the server
6
+ * stays authoritative through /orders/preflight and /orders/buy — this package
7
+ * carries no venue name and no venue rule of its own.
8
+ *
9
+ * import { createOrderSheetClient } from '@openmarketsai/order-sheet'
10
+ *
11
+ * const om = createOrderSheetClient({ apiKey: process.env.OM_EXEC_KEY! })
12
+ * const sheet = om.openSheet({ liquidityHash: cell.liquidity_hash })
13
+ * sheet.set({ stake: { amount: 5 } })
14
+ * await sheet.preflight()
15
+ * const out = await sheet.place() // { status: 'placed', result } | 'blocked' | 'confirm_required'
16
+ *
17
+ * React bindings: '@openmarketsai/order-sheet/react'. Styled sheet: '/ui'.
18
+ */
19
+ export { createOrderSheetClient, resolveOptions, DEFAULT_API_BASE } from './client.js';
20
+ export { createSheet, createInertSheet, initialSheetState, newIdempotencyKey, summarize, toPlaceResult, phaseFromOrders, phaseFromSummary } from './sheet.js';
21
+ export { OrderSheetError, isOrderSheetError, userMessageFor } from './errors.js';
22
+ export { credentialSupportsStream } from './auth.js';
23
+ export { createOrderStream } from './stream.js';
24
+ export { validateSheet } from './validate.js';
25
+ export { buildQuote, resolveMaxPrice } from './quote.js';
26
+ export { buildVenueOptions, fromServerIssue } from './venues.js';
27
+ export { localIssue, SERVER_CODE_MESSAGES, CLIENT_CODE_SPECS } from './messages.js';
28
+ export { contractsFromAmount, snapPrice, floorToIncrement, isOnGrid, round } from './math.js';
29
+ export { formatPrice, formatMoney, toAmerican, toDecimal, ODDS_FORMATS } from './odds.js';
30
+ export * from './generated/contract.js';
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEtF,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE7J,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGhF,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAExD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAEhE,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACnF,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAC7F,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAEzF,cAAc,yBAAyB,CAAA"}
package/dist/math.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Arithmetic the sheet does locally, for INSTANT feedback. Every function here is
3
+ * a copy of the server's own (src/utils/order-constraints.ts) with the same
4
+ * tolerances, so a local estimate agrees with /preflight — and the server stays
5
+ * authoritative: a preflight result always replaces a local number.
6
+ */
7
+ export declare const AMOUNT_TOL = 0.005;
8
+ /**
9
+ * Whole contracts from a dollar amount at a per-contract price, epsilon-safe.
10
+ * `5.60 / 0.56` is 9.999999999999998 in IEEE-754; a plain floor says 9. The
11
+ * venue books 10. The epsilon only promotes a quotient already within ~1e-9 of
12
+ * an integer (identical to the server's contractsFromAmount).
13
+ */
14
+ export declare function contractsFromAmount(amount: number, pricePerContract: number): number;
15
+ export declare function isOnGrid(value: number, step: number | null | undefined): boolean;
16
+ export declare function decimalsFor(step: number): number;
17
+ /** Floor a USD amount to an increment (the server floors at execution). */
18
+ export declare function floorToIncrement(amount: number, step: number | null | undefined): number;
19
+ /** Snap a probability to a venue's tick and band (null tick = the server snaps; only clamp). */
20
+ export declare function snapPrice(price: number, tick: number | null, min: number, max: number): number;
21
+ export declare function round(n: number, dp: number): number;
22
+ export declare function approxEqual(a: number, b: number, tol?: number): boolean;
package/dist/math.js ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Arithmetic the sheet does locally, for INSTANT feedback. Every function here is
3
+ * a copy of the server's own (src/utils/order-constraints.ts) with the same
4
+ * tolerances, so a local estimate agrees with /preflight — and the server stays
5
+ * authoritative: a preflight result always replaces a local number.
6
+ */
7
+ const STEP_EPS = 1e-4;
8
+ export const AMOUNT_TOL = 0.005;
9
+ /**
10
+ * Whole contracts from a dollar amount at a per-contract price, epsilon-safe.
11
+ * `5.60 / 0.56` is 9.999999999999998 in IEEE-754; a plain floor says 9. The
12
+ * venue books 10. The epsilon only promotes a quotient already within ~1e-9 of
13
+ * an integer (identical to the server's contractsFromAmount).
14
+ */
15
+ export function contractsFromAmount(amount, pricePerContract) {
16
+ if (!Number.isFinite(amount) || !Number.isFinite(pricePerContract) || pricePerContract <= 0)
17
+ return 0;
18
+ return Math.floor(amount / pricePerContract + 1e-9);
19
+ }
20
+ export function isOnGrid(value, step) {
21
+ if (!step)
22
+ return true;
23
+ const ratio = value / step;
24
+ return Math.abs(ratio - Math.round(ratio)) <= STEP_EPS;
25
+ }
26
+ export function decimalsFor(step) {
27
+ if (!Number.isFinite(step) || step <= 0)
28
+ return 2;
29
+ const s = step.toString();
30
+ const i = s.indexOf('.');
31
+ return i === -1 ? 0 : s.length - i - 1;
32
+ }
33
+ /** Floor a USD amount to an increment (the server floors at execution). */
34
+ export function floorToIncrement(amount, step) {
35
+ if (!step)
36
+ return round(amount, 2);
37
+ const floored = Math.floor((amount + 1e-6) / step) * step;
38
+ return Number(floored.toFixed(decimalsFor(step)));
39
+ }
40
+ /** Snap a probability to a venue's tick and band (null tick = the server snaps; only clamp). */
41
+ export function snapPrice(price, tick, min, max) {
42
+ let p = price;
43
+ if (tick && tick > 0)
44
+ p = Math.round(p / tick) * tick;
45
+ p = Math.min(max, Math.max(min, p));
46
+ return round(p, 4);
47
+ }
48
+ export function round(n, dp) {
49
+ const f = Math.pow(10, dp);
50
+ return Math.round(n * f) / f;
51
+ }
52
+ export function approxEqual(a, b, tol = 1e-6) {
53
+ return Math.abs(a - b) <= tol;
54
+ }
55
+ //# sourceMappingURL=math.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math.js","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,QAAQ,GAAG,IAAI,CAAA;AACrB,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAA;AAE/B;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,gBAAwB;IACxE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,gBAAgB,IAAI,CAAC;QAAE,OAAO,CAAC,CAAA;IACrG,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAC,CAAA;AACvD,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,IAA+B;IACnE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IACtB,MAAM,KAAK,GAAG,KAAK,GAAG,IAAI,CAAA;IAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAA;AAC1D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACpC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,CAAC,CAAA;IACjD,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAA;IACzB,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACxB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAA;AAC1C,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,IAA+B;IAC5E,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAClC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;IACzD,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACrD,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,IAAmB,EAAE,GAAW,EAAE,GAAW;IAClF,IAAI,CAAC,GAAG,KAAK,CAAA;IACb,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC;QAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;IACrD,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAA;IACnC,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AACtB,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,CAAS,EAAE,EAAU;IACvC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;AAChC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,CAAS,EAAE,GAAG,GAAG,IAAI;IACxD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAA;AACjC,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * User-facing copy for every issue the sheet can raise LOCALLY, before the
3
+ * server has answered. Keyed on the generated closed union, so a new server
4
+ * code fails this build until it has a sentence here. Whenever a response
5
+ * carries `message_user` (preflight, buy, constraints reasons), THAT wins —
6
+ * this table is the instant-feedback fallback, written to say the same thing.
7
+ */
8
+ import { type IssueCode, type IssueFix } from './generated/contract.js';
9
+ import type { Issue, IssueContext, SheetIssueCode, ClientIssueCode } from './types.js';
10
+ export declare const SERVER_CODE_MESSAGES: Record<IssueCode, (ctx: IssueContext) => string>;
11
+ interface ClientSpec {
12
+ severity: Issue['severity'];
13
+ fix: IssueFix;
14
+ scope: Issue['scope'];
15
+ message: (ctx: IssueContext) => string;
16
+ }
17
+ export declare const CLIENT_CODE_SPECS: Record<ClientIssueCode, ClientSpec>;
18
+ export declare function isServerCode(code: SheetIssueCode): code is IssueCode;
19
+ /** Build a local issue. Server codes take the server's default severity/fix; client codes their own. */
20
+ export declare function localIssue(code: SheetIssueCode, ctx?: IssueContext, over?: Partial<Pick<Issue, 'severity' | 'fix' | 'scope' | 'detail'>>): Issue;
21
+ export {};
@@ -0,0 +1,97 @@
1
+ /**
2
+ * User-facing copy for every issue the sheet can raise LOCALLY, before the
3
+ * server has answered. Keyed on the generated closed union, so a new server
4
+ * code fails this build until it has a sentence here. Whenever a response
5
+ * carries `message_user` (preflight, buy, constraints reasons), THAT wins —
6
+ * this table is the instant-feedback fallback, written to say the same thing.
7
+ */
8
+ import { ISSUE_DEFAULT_FIX, ISSUE_SEVERITY } from './generated/contract.js';
9
+ import { formatMoney } from './odds.js';
10
+ const usd = (n, currency = 'USD', fallback = 'the stake') => typeof n === 'number' && Number.isFinite(n) ? formatMoney(n, currency) : fallback;
11
+ const cents = (p, fallback = 'this price') => typeof p === 'number' && Number.isFinite(p) ? `${Math.round(p * 100)}¢` : fallback;
12
+ const venue = (ctx) => ctx.venue ?? 'this venue';
13
+ const plural = (n, one, many) => (n === 1 ? one : many);
14
+ export const SERVER_CODE_MESSAGES = {
15
+ real_money_not_permitted: () => 'Real-money trading is not included in this plan.',
16
+ practice_not_permitted: () => 'Practice trading is not available with this credential.',
17
+ test_mode_requires_paper: () => 'This is a test key — it can only place practice orders.',
18
+ venue_disabled: (c) => `${venue(c)} isn't connected on this account.`,
19
+ orders_not_supported: (c) => `${venue(c)} is quotes-only — it doesn't take orders.`,
20
+ scope_not_granted: (c) => `This account hasn't allowed trading on ${venue(c)}.`,
21
+ execution_limit_exceeded: (c) => (c.detail ? `Blocked by your trading limits: ${c.detail}.` : 'Blocked by your trading limits.'),
22
+ insufficient_balance: (c) => typeof c.required === 'number' && typeof c.balance === 'number'
23
+ ? `Needs ${usd(c.required, c.currency)} at ${venue(c)}; you have ${usd(c.balance, c.currency)}.`
24
+ : `Not enough balance at ${venue(c)}.`,
25
+ venue_shard_underfunded: (c) => `Your ${venue(c)} balance couldn't be moved to cover this market.`,
26
+ below_min_order: (c) => `${venue(c)}'s minimum is ${usd(c.min, c.currency, 'higher')}.`,
27
+ above_max_order: (c) => `${venue(c)}'s maximum is ${usd(c.max, c.currency, 'lower')}.`,
28
+ unfillable: (c) => `${usd(c.amount, c.currency)} buys less than one contract at ${cents(c.price)} — it can't fill.`,
29
+ below_min_contracts: (c) => typeof c.min_contracts === 'number'
30
+ ? `${venue(c)} needs at least ${c.min_contracts} ${plural(c.min_contracts, 'contract', 'contracts')}; ${usd(c.amount, c.currency)} buys ${c.contracts ?? 'fewer'}.`
31
+ : `${usd(c.amount, c.currency)} buys fewer contracts than ${venue(c)} accepts.`,
32
+ amount_off_increment: (c) => `${venue(c)} takes stakes in steps of ${usd(c.increment, c.currency, 'a cent')} — this will be rounded down.`,
33
+ price_out_of_band: (c) => typeof c.min === 'number' && typeof c.max === 'number'
34
+ ? `Prices on ${venue(c)} run ${cents(c.min)}–${cents(c.max)}.`
35
+ : `That price is outside what ${venue(c)} accepts.`,
36
+ above_max_price: (c) => typeof c.price === 'number' && typeof c.max_price === 'number'
37
+ ? `The book is at ${cents(c.price)}, above your ${cents(c.max_price)} limit.`
38
+ : 'The book is above your price limit.',
39
+ price_off_tick: (c) => `${venue(c)} prices in fixed steps — your limit was rounded to ${cents(c.price)}.`,
40
+ insufficient_size: (c) => `Only ${usd(c.available, c.currency, 'less')} is available at ${cents(c.price)}.`,
41
+ insufficient_live_depth: () => 'Nothing is available at this price right now.',
42
+ no_viable_venues: (c) => `${venue(c)} isn't quoting this position right now.`,
43
+ no_live_price_for_paper: () => 'No live price to practise against.',
44
+ invalid_requested_amount: () => 'Enter a stake above zero.',
45
+ halted_upstream: () => 'Not placed — another leg failed first.',
46
+ unknown: (c) => c.detail ?? 'The venue declined this order.',
47
+ };
48
+ export const CLIENT_CODE_SPECS = {
49
+ no_venue: { severity: 'block', fix: 'switch_venue', scope: 'leg', message: () => 'Pick a venue.' },
50
+ no_stake: { severity: 'block', fix: 'raise_stake', scope: 'leg', message: () => 'Enter a stake.' },
51
+ no_quote: { severity: 'block', fix: 'switch_venue', scope: 'venue', message: (c) => `${venue(c)} has no live price for this position.` },
52
+ paper_limit_unsupported: { severity: 'block', fix: 'use_market', scope: 'leg', message: () => 'Practice orders fill instantly — limit orders need real money.' },
53
+ order_type_unsupported: { severity: 'block', fix: 'use_market', scope: 'venue', message: (c) => `${venue(c)} doesn't take ${c.detail ?? 'this type of'} orders.` },
54
+ mode_unavailable: { severity: 'block', fix: 'switch_to_paper', scope: 'account', message: (c) => `${c.detail === 'paper' ? 'Practice' : 'Real-money'} trading isn't available on this account.` },
55
+ real_money_disabled: { severity: 'block', fix: 'switch_to_paper', scope: 'request', message: () => 'Real-money orders are turned off in this app.' },
56
+ not_paper_bettable: { severity: 'block', fix: 'switch_to_real', scope: 'leg', message: () => "This position can't be practised on yet." },
57
+ stale_quote: { severity: 'warn', fix: 'retry', scope: 'venue', message: (c) => `${venue(c)}'s price is ${c.detail ?? 'a while'} old — re-check before placing.` },
58
+ better_price_available: { severity: 'info', fix: 'switch_venue', scope: 'venue', message: (c) => `${cents(c.price)} available on ${venue(c)} — switch?` },
59
+ constraints_stale: { severity: 'info', fix: 'retry', scope: 'account', message: () => 'Venue rules changed on the server — the preview will re-check.' },
60
+ confirm_required: { severity: 'info', fix: 'none', scope: 'request', message: (c) => `Confirm the real-money order for ${usd(c.amount, c.currency)} on ${venue(c)}.` },
61
+ confirm_expired: { severity: 'block', fix: 'retry', scope: 'request', message: () => 'The order changed since it was confirmed — review it and place it again.' },
62
+ not_ready: { severity: 'block', fix: 'retry', scope: 'request', message: () => 'The order sheet is still loading.' },
63
+ };
64
+ export function isServerCode(code) {
65
+ return Object.prototype.hasOwnProperty.call(SERVER_CODE_MESSAGES, code);
66
+ }
67
+ /** Build a local issue. Server codes take the server's default severity/fix; client codes their own. */
68
+ export function localIssue(code, ctx = {}, over = {}) {
69
+ if (isServerCode(code)) {
70
+ return {
71
+ code,
72
+ severity: over.severity ?? ISSUE_SEVERITY[code],
73
+ message: SERVER_CODE_MESSAGES[code](ctx),
74
+ detail: over.detail ?? ctx.detail ?? null,
75
+ fix: over.fix ?? fixFor(code, ctx),
76
+ scope: over.scope ?? 'leg',
77
+ source: 'local',
78
+ };
79
+ }
80
+ const spec = CLIENT_CODE_SPECS[code];
81
+ return {
82
+ code,
83
+ severity: over.severity ?? spec.severity,
84
+ message: spec.message(ctx),
85
+ detail: over.detail ?? ctx.detail ?? null,
86
+ fix: over.fix ?? spec.fix,
87
+ scope: over.scope ?? spec.scope,
88
+ source: 'local',
89
+ };
90
+ }
91
+ function fixFor(code, ctx) {
92
+ if (code === 'price_out_of_band' && typeof ctx.price === 'number' && typeof ctx.min === 'number') {
93
+ return ctx.price < ctx.min ? 'raise_price' : 'lower_price';
94
+ }
95
+ return ISSUE_DEFAULT_FIX[code];
96
+ }
97
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"}
package/dist/odds.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Display formats. The platform's price is a probability (0..1); the default
3
+ * display is cents (venue-native on the CLOB venues); American is an option.
4
+ * Derived American odds ignore vig, so a venue-supplied `american_odds` string
5
+ * wins when one is available.
6
+ */
7
+ export type OddsFormat = 'cents' | 'percent' | 'american' | 'decimal';
8
+ export declare const ODDS_FORMATS: readonly OddsFormat[];
9
+ export declare function toAmerican(p: number): string;
10
+ export declare function toDecimal(p: number): string;
11
+ export declare function formatPrice(p: number | null | undefined, format: OddsFormat, american?: string | null): string;
12
+ export declare function formatMoney(n: number | null | undefined, currency?: string): string;
package/dist/odds.js ADDED
@@ -0,0 +1,33 @@
1
+ export const ODDS_FORMATS = ['cents', 'percent', 'american', 'decimal'];
2
+ export function toAmerican(p) {
3
+ if (!(p > 0 && p < 1))
4
+ return '—';
5
+ const v = p >= 0.5 ? -Math.round((100 * p) / (1 - p)) : Math.round((100 * (1 - p)) / p);
6
+ return v > 0 ? `+${v}` : `${v}`;
7
+ }
8
+ export function toDecimal(p) {
9
+ if (!(p > 0 && p < 1))
10
+ return '—';
11
+ return (1 / p).toFixed(2);
12
+ }
13
+ export function formatPrice(p, format, american) {
14
+ if (p == null || !Number.isFinite(p) || p <= 0)
15
+ return '—';
16
+ switch (format) {
17
+ case 'cents': return `${Math.round(p * 100)}¢`;
18
+ case 'percent': return `${Math.round(p * 100)}%`;
19
+ case 'american': return american ?? toAmerican(p);
20
+ case 'decimal': return toDecimal(p);
21
+ }
22
+ }
23
+ export function formatMoney(n, currency = 'USD') {
24
+ if (n == null || !Number.isFinite(n))
25
+ return '—';
26
+ const abs = Math.abs(n);
27
+ const body = abs.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
28
+ const sign = n < 0 ? '−' : '';
29
+ if (currency === 'USD')
30
+ return `${sign}$${body}`;
31
+ return `${sign}${body} ${currency}`;
32
+ }
33
+ //# sourceMappingURL=odds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"odds.js","sourceRoot":"","sources":["../src/odds.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,YAAY,GAA0B,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAU,CAAA;AAEvG,MAAM,UAAU,UAAU,CAAC,CAAS;IAChC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAAE,OAAO,GAAG,CAAA;IACjC,MAAM,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACvF,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAA;AACnC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,CAAS;IAC/B,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAAE,OAAO,GAAG,CAAA;IACjC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAA4B,EAAE,MAAkB,EAAE,QAAwB;IAClG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAA;IAC1D,QAAQ,MAAM,EAAE,CAAC;QACb,KAAK,OAAO,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,CAAA;QAC9C,KAAK,SAAS,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,CAAA;QAChD,KAAK,UAAU,CAAC,CAAC,OAAO,QAAQ,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;QACjD,KAAK,SAAS,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAA;IACvC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAA4B,EAAE,QAAQ,GAAG,KAAK;IACtE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,CAAA;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IACvB,MAAM,IAAI,GAAG,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,qBAAqB,EAAE,CAAC,EAAE,qBAAqB,EAAE,CAAC,EAAE,CAAC,CAAA;IAChG,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IAC7B,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,GAAG,IAAI,IAAI,IAAI,EAAE,CAAA;IAChD,OAAO,GAAG,IAAI,GAAG,IAAI,IAAI,QAAQ,EAAE,CAAA;AACvC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type OddsFormat } from './odds.js';
2
+ import type { MoneyMode, OrderType, PreflightSnapshot, Quote, StakeUnit, VenueOption } from './types.js';
3
+ export interface QuoteInput {
4
+ venue: VenueOption;
5
+ mode: MoneyMode;
6
+ orderType: OrderType;
7
+ stake: {
8
+ unit: StakeUnit;
9
+ value: number | null;
10
+ };
11
+ /** The ceiling the sheet will send (already snapped by `resolveMaxPrice`). */
12
+ maxPrice: number | null;
13
+ oddsFormat: OddsFormat;
14
+ preflight: PreflightSnapshot | null;
15
+ currency: string;
16
+ stale: boolean;
17
+ }
18
+ /** The ceiling: the user's, or quote + slippage snapped into the venue's band. */
19
+ export declare function resolveMaxPrice(venue: VenueOption, orderType: OrderType, userMaxPrice: number | null, slippage: number): number | null;
20
+ export declare function buildQuote(input: QuoteInput): Quote | null;
package/dist/quote.js ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The quote: what the stake buys, what it costs, what it pays. Pure. Uses the
3
+ * venue's SERVED constraints and the same epsilon-safe floor the venue uses;
4
+ * when a preflight snapshot for these exact inputs exists, the server's numbers
5
+ * (fees, expected fill, would_rest, normalized contracts) replace the estimates.
6
+ */
7
+ import { contractsFromAmount, floorToIncrement, round, snapPrice } from './math.js';
8
+ import { formatMoney, formatPrice } from './odds.js';
9
+ /** The ceiling: the user's, or quote + slippage snapped into the venue's band. */
10
+ export function resolveMaxPrice(venue, orderType, userMaxPrice, slippage) {
11
+ const price = venue.price;
12
+ const pc = venue.constraints?.price;
13
+ // A null tick means the server snaps (non-linear ladder): clamp only.
14
+ const tick = pc ? pc.tick : 0.01;
15
+ const min = pc?.min ?? 0.01;
16
+ const max = pc?.max ?? 0.99;
17
+ if (userMaxPrice != null)
18
+ return snapPrice(userMaxPrice, tick, min, max);
19
+ if (price == null)
20
+ return null;
21
+ // A limit defaults to the quote itself (rest at the current price); a market
22
+ // order takes up to `slippage` above it.
23
+ return snapPrice(orderType === 'limit' ? price : price + slippage, tick, min, max);
24
+ }
25
+ export function buildQuote(input) {
26
+ const { venue, stake } = input;
27
+ if (stake.value == null || !(stake.value > 0))
28
+ return null;
29
+ const size = venue.constraints?.size;
30
+ const whole = size?.whole_contracts ?? false;
31
+ const increment = size?.increment_usd ?? null;
32
+ const price = input.orderType === 'limit' ? input.maxPrice : venue.price;
33
+ if (price == null || !(price > 0))
34
+ return null;
35
+ const maxPrice = input.maxPrice ?? price;
36
+ const pf = input.preflight?.leg;
37
+ const usePreflight = !!pf && pf.accepted;
38
+ // Basis: what a contract costs. The server's expected fill price when it has
39
+ // answered for these inputs; otherwise the venue quote (market) or limit.
40
+ const basis = usePreflight && input.orderType === 'market' && pf.total_allocated > 0 ? pf.expected_fill_price : price;
41
+ const notional = stake.unit === 'amount' ? stake.value : round(stake.value * basis, 4);
42
+ let contracts;
43
+ let cost;
44
+ if (usePreflight) {
45
+ contracts = pf.normalized.contracts;
46
+ cost = pf.normalized.amount;
47
+ }
48
+ else if (stake.unit === 'shares') {
49
+ contracts = whole ? Math.floor(stake.value + 1e-9) : stake.value;
50
+ cost = round(contracts * basis, 2);
51
+ }
52
+ else if (whole) {
53
+ contracts = contractsFromAmount(notional, basis);
54
+ cost = round(contracts * basis, 2);
55
+ }
56
+ else {
57
+ cost = floorToIncrement(notional, increment);
58
+ contracts = basis > 0 ? round(cost / basis, 4) : 0;
59
+ }
60
+ const payout = round(contracts, 2);
61
+ const leftover = round(Math.max(0, notional - cost), 2);
62
+ const direction = venue.constraints?.fees.direction ?? null;
63
+ let fee = null;
64
+ if (usePreflight) {
65
+ fee = { taker: pf.total_taker_fee, maker: pf.total_maker_fee, direction, estimated: false };
66
+ }
67
+ else if (venue.feePacket && input.mode === 'real') {
68
+ // The liquidity fee packet is on a canonical $100 stake; scale linearly as
69
+ // an estimate (non-linear venue fees are exact only from preflight).
70
+ const k = cost / 100;
71
+ fee = { taker: round(venue.feePacket.taker_fee * k, 2), maker: round(venue.feePacket.maker_fee * k, 2), direction, estimated: true };
72
+ }
73
+ else if (input.mode === 'paper') {
74
+ fee = { taker: 0, maker: 0, direction: null, estimated: false };
75
+ }
76
+ const takerFee = fee?.taker ?? 0;
77
+ const effectivePrice = contracts > 0 ? round((cost + takerFee) / contracts, 4) : null;
78
+ const profit = round(payout - cost - takerFee, 2);
79
+ const expectedFill = usePreflight
80
+ ? { allocated: pf.total_allocated, unfilled: pf.unfilled_amount, fillPrice: pf.expected_fill_price }
81
+ : null;
82
+ const wouldRest = usePreflight
83
+ ? pf.would_rest
84
+ : input.orderType === 'limit' && venue.price != null && venue.price > maxPrice + 1e-9;
85
+ const freshness = input.stale ? 'stale' : usePreflight ? 'preflight' : 'quote';
86
+ const cur = input.currency;
87
+ return {
88
+ venuePartnerId: venue.partnerId,
89
+ mode: input.mode,
90
+ orderType: input.orderType,
91
+ stake: { unit: stake.unit, value: stake.value },
92
+ price: round(basis, 4),
93
+ maxPrice: round(maxPrice, 4),
94
+ contracts,
95
+ cost,
96
+ payout,
97
+ leftover,
98
+ notional: round(notional, 2),
99
+ fee,
100
+ effectivePrice,
101
+ profit,
102
+ expectedFill,
103
+ wouldRest,
104
+ currency: cur,
105
+ freshness,
106
+ display: {
107
+ price: formatPrice(basis, input.oddsFormat, input.orderType === 'market' ? venue.americanOdds : null),
108
+ maxPrice: formatPrice(maxPrice, input.oddsFormat),
109
+ cost: formatMoney(cost, cur),
110
+ payout: formatMoney(payout, cur),
111
+ profit: formatMoney(profit, cur),
112
+ fee: fee ? `${formatMoney(takerFee, cur)}${fee.estimated ? ' est.' : ''}` : '—',
113
+ effectivePrice: effectivePrice != null ? formatPrice(Math.min(0.9999, effectivePrice), input.oddsFormat) : '—',
114
+ },
115
+ };
116
+ }
117
+ //# sourceMappingURL=quote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"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"}