@taphubhq/sdk-core 0.26.1 → 0.28.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.
- package/README.md +12 -0
- package/dist/index.cjs +140 -15
- package/dist/index.d.mts +121 -3
- package/dist/index.d.ts +121 -3
- package/dist/index.js +138 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,18 @@ For React bindings (hooks, providers, i18n), use
|
|
|
12
12
|
pnpm add @taphubhq/sdk-core
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
### Server compatibility
|
|
16
|
+
|
|
17
|
+
The SDK's queries select the fields they need unconditionally, so a release that
|
|
18
|
+
adds a field also raises the minimum grid-api it runs against. Check this before
|
|
19
|
+
upgrading in an environment you do not deploy the server to.
|
|
20
|
+
|
|
21
|
+
| sdk-core | Needs grid-api serving | Or else |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| 0.27.0+ | `Bid.cellCandle { o h l c final touchSec touchPrice }` | `myBids` fails GraphQL validation and returns **no bids** |
|
|
24
|
+
|
|
25
|
+
Deploy grid-api before the client, on every environment.
|
|
26
|
+
|
|
15
27
|
## Minimum Integration
|
|
16
28
|
|
|
17
29
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -60,6 +60,8 @@ __export(index_exports, {
|
|
|
60
60
|
calculateProbHit: () => calculateProbHit,
|
|
61
61
|
calculateProbWin: () => calculateProbWin,
|
|
62
62
|
calculateProbWin_v2: () => calculateProbWin_v2,
|
|
63
|
+
coefMultBandIndex: () => coefMultBandIndex,
|
|
64
|
+
coefMultiplierAt: () => coefMultiplierAt,
|
|
63
65
|
computeBaseline: () => computeBaseline,
|
|
64
66
|
errorFunction: () => errorFunction,
|
|
65
67
|
isCancelled: () => isCancelled,
|
|
@@ -661,7 +663,22 @@ function normaliseBid(node) {
|
|
|
661
663
|
refundAmount: node.refund_amount ?? null,
|
|
662
664
|
slippage: node.slippage,
|
|
663
665
|
createdAt: node.created_at || null,
|
|
664
|
-
meta: parseMeta(node.meta)
|
|
666
|
+
meta: parseMeta(node.meta),
|
|
667
|
+
// Absent and explicit null both mean "no candle" — an older server, an unselected
|
|
668
|
+
// field, a pending bid and purged ticks must all read the same to a consumer.
|
|
669
|
+
// `final` null (nullable Boolean) collapses to absent = settled, so only a real
|
|
670
|
+
// `false` puts the row in the in-progress presentation.
|
|
671
|
+
cellCandle: node.cellCandle ? {
|
|
672
|
+
o: node.cellCandle.o,
|
|
673
|
+
h: node.cellCandle.h,
|
|
674
|
+
l: node.cellCandle.l,
|
|
675
|
+
c: node.cellCandle.c,
|
|
676
|
+
...typeof node.cellCandle.final === "boolean" ? { final: node.cellCandle.final } : {},
|
|
677
|
+
// Both legs or neither — see BidCellCandle.touchSec. Nulls (a non-win,
|
|
678
|
+
// or an older server) collapse to absent so a consumer only has to
|
|
679
|
+
// check one of them.
|
|
680
|
+
...typeof node.cellCandle.touchSec === "number" && typeof node.cellCandle.touchPrice === "string" ? { touchSec: node.cellCandle.touchSec, touchPrice: node.cellCandle.touchPrice } : {}
|
|
681
|
+
} : null
|
|
665
682
|
};
|
|
666
683
|
}
|
|
667
684
|
function normaliseBids(list) {
|
|
@@ -682,6 +699,7 @@ var PLACE_BID_MUTATION = `mutation PlaceBid($input: PlaceBidInput!) {
|
|
|
682
699
|
var MY_BIDS_QUERY = `query MyBids($statuses: [BidStatus!], $limit: Int, $offset: Int, $pairId: ID) {
|
|
683
700
|
myBids(statuses: $statuses, limit: $limit, offset: $offset, pairId: $pairId) {
|
|
684
701
|
id user_id pair_id currency amount coefficient time1 time2 price1 price2 status payout refund_rate refund_amount slippage created_at meta
|
|
702
|
+
cellCandle { o h l c final touchSec touchPrice }
|
|
685
703
|
}
|
|
686
704
|
}`;
|
|
687
705
|
var CANCEL_BID_MUTATION = `mutation CancelBid($input: CancelBidInput!) {
|
|
@@ -1238,7 +1256,9 @@ function normaliseConstraints(node) {
|
|
|
1238
1256
|
priceMinRange: node.priceMinRange,
|
|
1239
1257
|
priceMaxRange: node.priceMaxRange,
|
|
1240
1258
|
coefMults: node.coefMults,
|
|
1241
|
-
maxCoef: node.maxCoef
|
|
1259
|
+
maxCoef: node.maxCoef,
|
|
1260
|
+
// bid-260819: pass through untouched — pricing consumes it as-is.
|
|
1261
|
+
coefMultsMatrix: node.coefMultsMatrix ?? null
|
|
1242
1262
|
};
|
|
1243
1263
|
}
|
|
1244
1264
|
function normaliseCandles(list) {
|
|
@@ -1291,7 +1311,7 @@ var PAIR_QUERY = `query AgencyPair($pairId: ID!) {
|
|
|
1291
1311
|
id pair { id pair source thumb } status createdAt
|
|
1292
1312
|
config {
|
|
1293
1313
|
gridConfig { cellSizeTime cellSizeValue candleSize baseline baselineTime }
|
|
1294
|
-
constraints { minBetTime maxBetTime slippage priceMinRange priceMaxRange coefMults maxCoef }
|
|
1314
|
+
constraints { minBetTime maxBetTime slippage priceMinRange priceMaxRange coefMults maxCoef coefMultsMatrix { timeBandsSec rows } }
|
|
1295
1315
|
acceptableBids minBidAmount maxBidAmount
|
|
1296
1316
|
bidCancelRefundRate bidCancelMinSeconds
|
|
1297
1317
|
}
|
|
@@ -1849,6 +1869,23 @@ function mapWireBidResult(raw, topicPairId) {
|
|
|
1849
1869
|
userId: p.user_id
|
|
1850
1870
|
};
|
|
1851
1871
|
if (typeof p.balance === "string") data.balance = p.balance;
|
|
1872
|
+
const candle = p.cellCandle;
|
|
1873
|
+
if (candle && typeof candle.o === "string" && typeof candle.h === "string" && typeof candle.l === "string" && typeof candle.c === "string") {
|
|
1874
|
+
data.cellCandle = {
|
|
1875
|
+
o: candle.o,
|
|
1876
|
+
h: candle.h,
|
|
1877
|
+
l: candle.l,
|
|
1878
|
+
c: candle.c,
|
|
1879
|
+
final: candle.final !== "false"
|
|
1880
|
+
};
|
|
1881
|
+
if (typeof candle.touchSec === "string" && typeof candle.touchPrice === "string") {
|
|
1882
|
+
const touchSec = Number.parseInt(candle.touchSec, 10);
|
|
1883
|
+
if (Number.isFinite(touchSec)) {
|
|
1884
|
+
data.cellCandle.touchSec = touchSec;
|
|
1885
|
+
data.cellCandle.touchPrice = candle.touchPrice;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1852
1889
|
if (topicPairId !== void 0) data.pairId = topicPairId;
|
|
1853
1890
|
return { event: "bidWon", data };
|
|
1854
1891
|
}
|
|
@@ -1857,6 +1894,32 @@ function mapWireBidResult(raw, topicPairId) {
|
|
|
1857
1894
|
if (topicPairId !== void 0) data.pairId = topicPairId;
|
|
1858
1895
|
return { event: "bidLost", data };
|
|
1859
1896
|
}
|
|
1897
|
+
case "cell_candle_final": {
|
|
1898
|
+
const candle = p.cellCandle;
|
|
1899
|
+
if (!candle || typeof candle.o !== "string" || typeof candle.h !== "string" || typeof candle.l !== "string" || typeof candle.c !== "string") {
|
|
1900
|
+
return null;
|
|
1901
|
+
}
|
|
1902
|
+
const data = {
|
|
1903
|
+
bidId: p.bidId,
|
|
1904
|
+
userId: p.user_id,
|
|
1905
|
+
cellCandle: {
|
|
1906
|
+
o: candle.o,
|
|
1907
|
+
h: candle.h,
|
|
1908
|
+
l: candle.l,
|
|
1909
|
+
c: candle.c,
|
|
1910
|
+
final: candle.final !== "false"
|
|
1911
|
+
}
|
|
1912
|
+
};
|
|
1913
|
+
if (typeof candle.touchSec === "string" && typeof candle.touchPrice === "string") {
|
|
1914
|
+
const touchSec = Number.parseInt(candle.touchSec, 10);
|
|
1915
|
+
if (Number.isFinite(touchSec)) {
|
|
1916
|
+
data.cellCandle.touchSec = touchSec;
|
|
1917
|
+
data.cellCandle.touchPrice = candle.touchPrice;
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
if (topicPairId !== void 0) data.pairId = topicPairId;
|
|
1921
|
+
return { event: "cellCandleFinal", data };
|
|
1922
|
+
}
|
|
1860
1923
|
default:
|
|
1861
1924
|
console.warn(`Unknown bid_result type "${String(p.type)}"`);
|
|
1862
1925
|
return null;
|
|
@@ -1974,6 +2037,7 @@ var RealtimeModule = class extends import_eventemitter35.default {
|
|
|
1974
2037
|
else if (mapped.event === "bidAccepted") channel.emit("bidAccepted", mapped.data);
|
|
1975
2038
|
else if (mapped.event === "bidWon") channel.emit("bidWon", mapped.data);
|
|
1976
2039
|
else if (mapped.event === "bidLost") channel.emit("bidLost", mapped.data);
|
|
2040
|
+
else if (mapped.event === "cellCandleFinal") channel.emit("cellCandleFinal", mapped.data);
|
|
1977
2041
|
else if (mapped.event === "balanceUpdate") channel.emit("balanceUpdate", mapped.data);
|
|
1978
2042
|
else if (mapped.event === "configUpdate") channel.emit("configUpdate", mapped.data);
|
|
1979
2043
|
else if (mapped.event === "idealConfigUpdate")
|
|
@@ -2106,6 +2170,7 @@ var RealtimeModule = class extends import_eventemitter35.default {
|
|
|
2106
2170
|
if (!mapped) return;
|
|
2107
2171
|
if (mapped.event === "bidWon") channel.emit("bidWon", mapped.data);
|
|
2108
2172
|
else if (mapped.event === "bidLost") channel.emit("bidLost", mapped.data);
|
|
2173
|
+
else if (mapped.event === "cellCandleFinal") channel.emit("cellCandleFinal", mapped.data);
|
|
2109
2174
|
};
|
|
2110
2175
|
const onError = (err) => {
|
|
2111
2176
|
channel.emit("error", err);
|
|
@@ -3786,6 +3851,40 @@ function calculateProbWin_v2(time1, time2, _price1, price2, creationTime, curren
|
|
|
3786
3851
|
const prob = 1 - normalCDF(z);
|
|
3787
3852
|
return Math.max(0, Math.min(1, prob));
|
|
3788
3853
|
}
|
|
3854
|
+
function coefMultBandIndex(timeBandsSec, timeToStartSec) {
|
|
3855
|
+
for (let i = 0; i < timeBandsSec.length; i++) {
|
|
3856
|
+
if (timeToStartSec <= timeBandsSec[i]) return i;
|
|
3857
|
+
}
|
|
3858
|
+
return timeBandsSec.length;
|
|
3859
|
+
}
|
|
3860
|
+
function coefMultiplierAt(timeBandsSec, row, timeToStartSec) {
|
|
3861
|
+
if (row.length === 0) return 1;
|
|
3862
|
+
if (row.length === 1 || timeBandsSec.length === 0) return row[0];
|
|
3863
|
+
let valueCount = row.length;
|
|
3864
|
+
if (valueCount > timeBandsSec.length + 1) valueCount = timeBandsSec.length + 1;
|
|
3865
|
+
const edges = timeBandsSec.slice(0, valueCount - 1);
|
|
3866
|
+
const anchors = new Array(valueCount);
|
|
3867
|
+
anchors[0] = edges[0] / 2;
|
|
3868
|
+
for (let i = 1; i < valueCount - 1; i++) {
|
|
3869
|
+
anchors[i] = (edges[i - 1] + edges[i]) / 2;
|
|
3870
|
+
}
|
|
3871
|
+
const last = valueCount - 1;
|
|
3872
|
+
if (edges.length >= 2) {
|
|
3873
|
+
anchors[last] = edges[last - 1] + (edges[last - 1] - edges[last - 2]) / 2;
|
|
3874
|
+
} else {
|
|
3875
|
+
anchors[last] = 1.5 * edges[0];
|
|
3876
|
+
}
|
|
3877
|
+
const t = timeToStartSec;
|
|
3878
|
+
if (t <= anchors[0]) return row[0];
|
|
3879
|
+
if (t >= anchors[last]) return row[last];
|
|
3880
|
+
for (let i = 1; i <= last; i++) {
|
|
3881
|
+
if (t <= anchors[i]) {
|
|
3882
|
+
const frac = (t - anchors[i - 1]) / (anchors[i] - anchors[i - 1]);
|
|
3883
|
+
return row[i - 1] + (row[i] - row[i - 1]) * frac;
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
return row[last];
|
|
3887
|
+
}
|
|
3789
3888
|
function simpsonForHit(f, a, b, eps = 3e-5, maxDepth = 14) {
|
|
3790
3889
|
function simpsonRule(a2, b2) {
|
|
3791
3890
|
const c = (a2 + b2) / 2;
|
|
@@ -3855,7 +3954,7 @@ function calculateCoefficientWrapper(params) {
|
|
|
3855
3954
|
candleClose,
|
|
3856
3955
|
volatility,
|
|
3857
3956
|
coefMults,
|
|
3858
|
-
|
|
3957
|
+
coefMultsMatrix,
|
|
3859
3958
|
minCoef
|
|
3860
3959
|
} = params;
|
|
3861
3960
|
const currentPrice = candleClose;
|
|
@@ -3872,22 +3971,46 @@ function calculateCoefficientWrapper(params) {
|
|
|
3872
3971
|
if (probability <= 0) return Number.POSITIVE_INFINITY;
|
|
3873
3972
|
const rawCoef = 1 / probability;
|
|
3874
3973
|
const rawIndex = rawCoef < 1 ? 0 : Math.floor(Math.log2(rawCoef));
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3974
|
+
let multiplier = 1;
|
|
3975
|
+
if (coefMultsMatrix && coefMultsMatrix.rows.length > 0) {
|
|
3976
|
+
const bucketIndex = Math.min(rawIndex, coefMultsMatrix.rows.length - 1);
|
|
3977
|
+
const row = coefMultsMatrix.rows[bucketIndex];
|
|
3978
|
+
if (row && row.length > 0) {
|
|
3979
|
+
multiplier = coefMultiplierAt(coefMultsMatrix.timeBandsSec, row, time1 - candleTime);
|
|
3980
|
+
}
|
|
3981
|
+
} else {
|
|
3982
|
+
const coefMultIndex = Math.min(rawIndex, coefMults.length - 1);
|
|
3983
|
+
multiplier = coefMults[coefMultIndex] || 1;
|
|
3984
|
+
}
|
|
3880
3985
|
const floor = typeof minCoef === "number" && minCoef > 0 ? minCoef : 1;
|
|
3881
|
-
const finalCoef = Math.max(floor, multiplier /
|
|
3986
|
+
const finalCoef = Math.max(floor, multiplier / probability);
|
|
3882
3987
|
return roundCoefToSignificantDigits(finalCoef);
|
|
3883
3988
|
}
|
|
3884
3989
|
function roundCoefToSignificantDigits(value) {
|
|
3885
3990
|
if (!Number.isFinite(value) || value <= 0) return value;
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3991
|
+
let exponent = 0;
|
|
3992
|
+
let scaled = value;
|
|
3993
|
+
while (scaled >= 10) {
|
|
3994
|
+
scaled /= 10;
|
|
3995
|
+
exponent++;
|
|
3996
|
+
}
|
|
3997
|
+
while (scaled < 1) {
|
|
3998
|
+
scaled *= 10;
|
|
3999
|
+
exponent--;
|
|
4000
|
+
}
|
|
4001
|
+
const digits = Math.floor(scaled) === 1 ? 3 : 2;
|
|
4002
|
+
const factorExponent = digits - exponent - 1;
|
|
4003
|
+
if (factorExponent >= 0) {
|
|
4004
|
+
const factor = exactPowerOfTen(factorExponent);
|
|
4005
|
+
return Math.round(value * factor) / factor;
|
|
4006
|
+
}
|
|
4007
|
+
const divisor = exactPowerOfTen(-factorExponent);
|
|
4008
|
+
return Math.round(value / divisor) * divisor;
|
|
4009
|
+
}
|
|
4010
|
+
function exactPowerOfTen(n) {
|
|
4011
|
+
let power = 1;
|
|
4012
|
+
for (let i = 0; i < n; i++) power *= 10;
|
|
4013
|
+
return power;
|
|
3891
4014
|
}
|
|
3892
4015
|
function computeBaseline(candleClose, candleTimeSec, cellSizeValue, cellSizeTime, candleSize) {
|
|
3893
4016
|
const baseline = Math.floor(candleClose / cellSizeValue) * cellSizeValue;
|
|
@@ -3927,6 +4050,8 @@ function computeBaseline(candleClose, candleTimeSec, cellSizeValue, cellSizeTime
|
|
|
3927
4050
|
calculateProbHit,
|
|
3928
4051
|
calculateProbWin,
|
|
3929
4052
|
calculateProbWin_v2,
|
|
4053
|
+
coefMultBandIndex,
|
|
4054
|
+
coefMultiplierAt,
|
|
3930
4055
|
computeBaseline,
|
|
3931
4056
|
errorFunction,
|
|
3932
4057
|
isCancelled,
|
package/dist/index.d.mts
CHANGED
|
@@ -326,6 +326,39 @@ interface BidMeta {
|
|
|
326
326
|
/** Display pair, e.g. "ETH/USD". */
|
|
327
327
|
pairName: string;
|
|
328
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* The OHLC of a bid's cell window — what price did inside [time1, time2] on the bid's
|
|
331
|
+
* own pair, reduced from every stored tick covering it.
|
|
332
|
+
*
|
|
333
|
+
* Values are Decimal strings, not numbers: the archive stores 8 decimal places and
|
|
334
|
+
* parsing to a JS number here would round them before the UI ever sees them.
|
|
335
|
+
*/
|
|
336
|
+
interface BidCellCandle {
|
|
337
|
+
o: string;
|
|
338
|
+
h: string;
|
|
339
|
+
l: string;
|
|
340
|
+
c: string;
|
|
341
|
+
/**
|
|
342
|
+
* Whether the candle had CLOSED when it settled the bid.
|
|
343
|
+
*
|
|
344
|
+
* `false` = the values can still move: a 1m/5m win settles the instant the
|
|
345
|
+
* band is touched (the verdict is permanent) but the candle's O/C and extremes
|
|
346
|
+
* keep moving until it closes — delivered live over MQTT, and by `myBids` when
|
|
347
|
+
* the touching candle has not closed at read time. The UI shows such a value
|
|
348
|
+
* in the same not-yet-settled language the pending row uses. Absent (a server
|
|
349
|
+
* that predates the flag) means settled.
|
|
350
|
+
*/
|
|
351
|
+
final?: boolean;
|
|
352
|
+
/**
|
|
353
|
+
* The WINNING TICK (TH-146 "success ticker"): the unix SECOND at which price
|
|
354
|
+
* entered the bid's band, and the price inside the band it entered at — what
|
|
355
|
+
* the popover shows as "reached <price> at <time>". Both present or both
|
|
356
|
+
* absent: a bid that did not win has no single moment that won it, and a
|
|
357
|
+
* server predating the field sends neither.
|
|
358
|
+
*/
|
|
359
|
+
touchSec?: number;
|
|
360
|
+
touchPrice?: string;
|
|
361
|
+
}
|
|
329
362
|
interface Bid {
|
|
330
363
|
id: string;
|
|
331
364
|
userId: string;
|
|
@@ -363,6 +396,16 @@ interface Bid {
|
|
|
363
396
|
* display pair instead of mapping `pairId` yourself.
|
|
364
397
|
*/
|
|
365
398
|
meta?: BidMeta | null;
|
|
399
|
+
/**
|
|
400
|
+
* What price did inside this bid's cell window, served with the bid itself so a
|
|
401
|
+
* consumer never has to fetch it separately (openspec ux-260819-bid-detail-popover).
|
|
402
|
+
*
|
|
403
|
+
* Null in three cases a consumer should treat alike: the bid is still pending, its
|
|
404
|
+
* ticks are no longer stored, or the caller's query did not select the field. A
|
|
405
|
+
* cancelled bid DOES carry one — its window still elapsed — and the server attaches
|
|
406
|
+
* no verdict about what the outcome would have been.
|
|
407
|
+
*/
|
|
408
|
+
cellCandle?: BidCellCandle | null;
|
|
366
409
|
}
|
|
367
410
|
interface CancelBidResult {
|
|
368
411
|
bid: Bid;
|
|
@@ -823,6 +866,11 @@ interface Constraints {
|
|
|
823
866
|
priceMaxRange: number;
|
|
824
867
|
coefMults: number[];
|
|
825
868
|
maxCoef: number;
|
|
869
|
+
coefMultsMatrix?: CoefMultsMatrix$1 | null;
|
|
870
|
+
}
|
|
871
|
+
interface CoefMultsMatrix$1 {
|
|
872
|
+
timeBandsSec: number[];
|
|
873
|
+
rows: number[][];
|
|
826
874
|
}
|
|
827
875
|
interface PairInfo {
|
|
828
876
|
/**
|
|
@@ -1166,6 +1214,37 @@ interface MqttBidWonEvent {
|
|
|
1166
1214
|
payout: string;
|
|
1167
1215
|
userId: string;
|
|
1168
1216
|
balance?: string;
|
|
1217
|
+
/**
|
|
1218
|
+
* OHLC of the candle whose band touch declared this win — the bid's
|
|
1219
|
+
* `cellCandle`, shipped with the result so the client never has to refetch the
|
|
1220
|
+
* bid list to learn it (openspec ux-260819, revised D2e).
|
|
1221
|
+
*
|
|
1222
|
+
* Sent on live wins and on D10 recovery alike (recovery hands over the stored
|
|
1223
|
+
* candle whose touch it found). Absent only on any grid-api older than that
|
|
1224
|
+
* change.
|
|
1225
|
+
*/
|
|
1226
|
+
cellCandle?: {
|
|
1227
|
+
o: string;
|
|
1228
|
+
h: string;
|
|
1229
|
+
l: string;
|
|
1230
|
+
c: string;
|
|
1231
|
+
/**
|
|
1232
|
+
* Whether that candle had CLOSED when it settled the bid. A 1m/5m win settles
|
|
1233
|
+
* the instant the band is touched — safe, because a touch is permanent — but
|
|
1234
|
+
* the candle's O/C and extremes can still move until it closes. `false` means
|
|
1235
|
+
* the client must present the value as not-yet-settled rather than final.
|
|
1236
|
+
*/
|
|
1237
|
+
final: boolean;
|
|
1238
|
+
/**
|
|
1239
|
+
* The WINNING TICK (TH-146 "success ticker"): the unix SECOND at which price
|
|
1240
|
+
* entered the bid's band, and the price inside the band it entered at. The
|
|
1241
|
+
* engine's predicate is a range overlap, so this is neither the candle's
|
|
1242
|
+
* close nor a raw extreme. Both present or both absent — a server that
|
|
1243
|
+
* predates the field sends neither.
|
|
1244
|
+
*/
|
|
1245
|
+
touchSec?: number;
|
|
1246
|
+
touchPrice?: string;
|
|
1247
|
+
};
|
|
1169
1248
|
/**
|
|
1170
1249
|
* Pair the won bid belongs to, e.g. "grid-ETH-USD". Derived from the result
|
|
1171
1250
|
* topic (`game/{agencyId}:{pairId}/user/.../bid_result`) since the won payload
|
|
@@ -1175,6 +1254,38 @@ interface MqttBidWonEvent {
|
|
|
1175
1254
|
*/
|
|
1176
1255
|
pairId?: string;
|
|
1177
1256
|
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Follow-up patch grid-api publishes when the window of a building-candle win
|
|
1259
|
+
* closes (wire type `cell_candle_final`): the CLOSED o/h/l/c of the candle the
|
|
1260
|
+
* bid settled on, replacing the in-progress values the `bidWon` event carried
|
|
1261
|
+
* with `cellCandle.final: false` (openspec ux-260819, design D2g).
|
|
1262
|
+
*
|
|
1263
|
+
* A patch, not a result: consumers must only update the bid's stored candle —
|
|
1264
|
+
* never replay win side effects (sound, payout, balance).
|
|
1265
|
+
*/
|
|
1266
|
+
interface MqttCellCandleFinalEvent {
|
|
1267
|
+
bidId: string;
|
|
1268
|
+
userId: string;
|
|
1269
|
+
cellCandle: {
|
|
1270
|
+
o: string;
|
|
1271
|
+
h: string;
|
|
1272
|
+
l: string;
|
|
1273
|
+
c: string;
|
|
1274
|
+
/** Always true — the whole point of the event is that the candle closed. */
|
|
1275
|
+
final: boolean;
|
|
1276
|
+
/**
|
|
1277
|
+
* The WINNING TICK (TH-146 "success ticker"): the unix SECOND at which price
|
|
1278
|
+
* entered the bid's band, and the price inside the band it entered at. The
|
|
1279
|
+
* engine's predicate is a range overlap, so this is neither the candle's
|
|
1280
|
+
* close nor a raw extreme. Both present or both absent — a server that
|
|
1281
|
+
* predates the field sends neither.
|
|
1282
|
+
*/
|
|
1283
|
+
touchSec?: number;
|
|
1284
|
+
touchPrice?: string;
|
|
1285
|
+
};
|
|
1286
|
+
/** Pair the bid belongs to (see {@link MqttBidWonEvent.pairId}). */
|
|
1287
|
+
pairId?: string;
|
|
1288
|
+
}
|
|
1178
1289
|
interface MqttBidLostEvent {
|
|
1179
1290
|
bidId: string;
|
|
1180
1291
|
/** Pair the lost bid belongs to (see {@link MqttBidWonEvent.pairId}). */
|
|
@@ -1268,6 +1379,7 @@ interface GameChannelEvents {
|
|
|
1268
1379
|
bidAccepted: [MqttBidAcceptedEvent];
|
|
1269
1380
|
bidWon: [MqttBidWonEvent];
|
|
1270
1381
|
bidLost: [MqttBidLostEvent];
|
|
1382
|
+
cellCandleFinal: [MqttCellCandleFinalEvent];
|
|
1271
1383
|
bidCancelled: [MqttBidCancelledEvent];
|
|
1272
1384
|
balanceUpdate: [MqttBalanceEvent];
|
|
1273
1385
|
configUpdate: [MqttConfigEvent];
|
|
@@ -1298,6 +1410,7 @@ declare class MigrationChannel extends EventEmitter<MigrationChannelEvents> {
|
|
|
1298
1410
|
interface UserBidsChannelEvents {
|
|
1299
1411
|
bidWon: [MqttBidWonEvent];
|
|
1300
1412
|
bidLost: [MqttBidLostEvent];
|
|
1413
|
+
cellCandleFinal: [MqttCellCandleFinalEvent];
|
|
1301
1414
|
error: [Error];
|
|
1302
1415
|
}
|
|
1303
1416
|
/**
|
|
@@ -1828,10 +1941,15 @@ interface CoefficientInput {
|
|
|
1828
1941
|
candleClose: number;
|
|
1829
1942
|
volatility: number;
|
|
1830
1943
|
coefMults: number[];
|
|
1831
|
-
|
|
1832
|
-
candleSize: number;
|
|
1944
|
+
coefMultsMatrix?: CoefMultsMatrix | null;
|
|
1833
1945
|
minCoef?: number;
|
|
1834
1946
|
}
|
|
1947
|
+
interface CoefMultsMatrix {
|
|
1948
|
+
timeBandsSec: number[];
|
|
1949
|
+
rows: number[][];
|
|
1950
|
+
}
|
|
1951
|
+
declare function coefMultBandIndex(timeBandsSec: number[], timeToStartSec: number): number;
|
|
1952
|
+
declare function coefMultiplierAt(timeBandsSec: number[], row: number[], timeToStartSec: number): number;
|
|
1835
1953
|
/**
|
|
1836
1954
|
* First-passage hitting probability: chance the price visits [price1, price2]
|
|
1837
1955
|
* at any point during [time1, time2], given GBM dynamics.
|
|
@@ -1848,4 +1966,4 @@ declare function computeBaseline(candleClose: number, candleTimeSec: number, cel
|
|
|
1848
1966
|
baselineTime: number;
|
|
1849
1967
|
};
|
|
1850
1968
|
|
|
1851
|
-
export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, CANDLE_EVENT, type CancelBidResult, type Candle, type CandleEventType, type CoefficientInput, type Constraints, type Currency, DEFAULT_CHART_HISTORY_LIMIT, DEFAULT_PROBE_TTL_SECONDS, DEFAULT_REGION, type ErrorMessageCatalog, type ErrorMessageMeta, type ErrorMessagesResponse, GameChannel, type GameConfig, type GridConfig, KNOWN_REGIONS, type KnownRegion, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, MigrationChannel, type MigrationChannelEvents, type MigrationCompletedEvent, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttAuthConfig, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type MqttWalletBalanceEvent, type MyRank, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type Pair, type PairInfo, PairModule, type PlaceBidInput, type ProbeNearestRegionOptions, REGION_PROBE_CACHE_KEY, type RankBoard, type RankEntry, type RankPeriod, type RankSort, RealtimeModule, type RefreshTokenResult, type RegionDomainMap, type Sample, type SampleReason, type SignalSource, type SortDir, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserBidsChannel, type UserBidsChannelEvents, UserModule, type UserPnL, type UserPnLPeriod, type Wallet as UserWallet, type Wallet$1 as Wallet, type WalletBalanceReason, WalletChannel, adaptiveSimpson, autoDetectStorage, calculateCoefficientWrapper, calculateProbHit, calculateProbWin, calculateProbWin_v2, computeBaseline, errorFunction, isCancelled, isKnownRegion, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF, normaliseLang, pairIdFromBidResultTopic, probeNearestRegion, resolveRegionBaseUrl, roundCoefToSignificantDigits };
|
|
1969
|
+
export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, type BidCellCandle, BidModule, type BidStatus, CANDLE_EVENT, type CancelBidResult, type Candle, type CandleEventType, type CoefMultsMatrix, type CoefficientInput, type Constraints, type Currency, DEFAULT_CHART_HISTORY_LIMIT, DEFAULT_PROBE_TTL_SECONDS, DEFAULT_REGION, type ErrorMessageCatalog, type ErrorMessageMeta, type ErrorMessagesResponse, GameChannel, type GameConfig, type GridConfig, KNOWN_REGIONS, type KnownRegion, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, MigrationChannel, type MigrationChannelEvents, type MigrationCompletedEvent, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttAuthConfig, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttCellCandleFinalEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type MqttWalletBalanceEvent, type MyRank, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type Pair, type PairInfo, PairModule, type PlaceBidInput, type ProbeNearestRegionOptions, REGION_PROBE_CACHE_KEY, type RankBoard, type RankEntry, type RankPeriod, type RankSort, RealtimeModule, type RefreshTokenResult, type RegionDomainMap, type Sample, type SampleReason, type SignalSource, type SortDir, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserBidsChannel, type UserBidsChannelEvents, UserModule, type UserPnL, type UserPnLPeriod, type Wallet as UserWallet, type Wallet$1 as Wallet, type WalletBalanceReason, WalletChannel, adaptiveSimpson, autoDetectStorage, calculateCoefficientWrapper, calculateProbHit, calculateProbWin, calculateProbWin_v2, coefMultBandIndex, coefMultiplierAt, computeBaseline, errorFunction, isCancelled, isKnownRegion, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF, normaliseLang, pairIdFromBidResultTopic, probeNearestRegion, resolveRegionBaseUrl, roundCoefToSignificantDigits };
|
package/dist/index.d.ts
CHANGED
|
@@ -326,6 +326,39 @@ interface BidMeta {
|
|
|
326
326
|
/** Display pair, e.g. "ETH/USD". */
|
|
327
327
|
pairName: string;
|
|
328
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* The OHLC of a bid's cell window — what price did inside [time1, time2] on the bid's
|
|
331
|
+
* own pair, reduced from every stored tick covering it.
|
|
332
|
+
*
|
|
333
|
+
* Values are Decimal strings, not numbers: the archive stores 8 decimal places and
|
|
334
|
+
* parsing to a JS number here would round them before the UI ever sees them.
|
|
335
|
+
*/
|
|
336
|
+
interface BidCellCandle {
|
|
337
|
+
o: string;
|
|
338
|
+
h: string;
|
|
339
|
+
l: string;
|
|
340
|
+
c: string;
|
|
341
|
+
/**
|
|
342
|
+
* Whether the candle had CLOSED when it settled the bid.
|
|
343
|
+
*
|
|
344
|
+
* `false` = the values can still move: a 1m/5m win settles the instant the
|
|
345
|
+
* band is touched (the verdict is permanent) but the candle's O/C and extremes
|
|
346
|
+
* keep moving until it closes — delivered live over MQTT, and by `myBids` when
|
|
347
|
+
* the touching candle has not closed at read time. The UI shows such a value
|
|
348
|
+
* in the same not-yet-settled language the pending row uses. Absent (a server
|
|
349
|
+
* that predates the flag) means settled.
|
|
350
|
+
*/
|
|
351
|
+
final?: boolean;
|
|
352
|
+
/**
|
|
353
|
+
* The WINNING TICK (TH-146 "success ticker"): the unix SECOND at which price
|
|
354
|
+
* entered the bid's band, and the price inside the band it entered at — what
|
|
355
|
+
* the popover shows as "reached <price> at <time>". Both present or both
|
|
356
|
+
* absent: a bid that did not win has no single moment that won it, and a
|
|
357
|
+
* server predating the field sends neither.
|
|
358
|
+
*/
|
|
359
|
+
touchSec?: number;
|
|
360
|
+
touchPrice?: string;
|
|
361
|
+
}
|
|
329
362
|
interface Bid {
|
|
330
363
|
id: string;
|
|
331
364
|
userId: string;
|
|
@@ -363,6 +396,16 @@ interface Bid {
|
|
|
363
396
|
* display pair instead of mapping `pairId` yourself.
|
|
364
397
|
*/
|
|
365
398
|
meta?: BidMeta | null;
|
|
399
|
+
/**
|
|
400
|
+
* What price did inside this bid's cell window, served with the bid itself so a
|
|
401
|
+
* consumer never has to fetch it separately (openspec ux-260819-bid-detail-popover).
|
|
402
|
+
*
|
|
403
|
+
* Null in three cases a consumer should treat alike: the bid is still pending, its
|
|
404
|
+
* ticks are no longer stored, or the caller's query did not select the field. A
|
|
405
|
+
* cancelled bid DOES carry one — its window still elapsed — and the server attaches
|
|
406
|
+
* no verdict about what the outcome would have been.
|
|
407
|
+
*/
|
|
408
|
+
cellCandle?: BidCellCandle | null;
|
|
366
409
|
}
|
|
367
410
|
interface CancelBidResult {
|
|
368
411
|
bid: Bid;
|
|
@@ -823,6 +866,11 @@ interface Constraints {
|
|
|
823
866
|
priceMaxRange: number;
|
|
824
867
|
coefMults: number[];
|
|
825
868
|
maxCoef: number;
|
|
869
|
+
coefMultsMatrix?: CoefMultsMatrix$1 | null;
|
|
870
|
+
}
|
|
871
|
+
interface CoefMultsMatrix$1 {
|
|
872
|
+
timeBandsSec: number[];
|
|
873
|
+
rows: number[][];
|
|
826
874
|
}
|
|
827
875
|
interface PairInfo {
|
|
828
876
|
/**
|
|
@@ -1166,6 +1214,37 @@ interface MqttBidWonEvent {
|
|
|
1166
1214
|
payout: string;
|
|
1167
1215
|
userId: string;
|
|
1168
1216
|
balance?: string;
|
|
1217
|
+
/**
|
|
1218
|
+
* OHLC of the candle whose band touch declared this win — the bid's
|
|
1219
|
+
* `cellCandle`, shipped with the result so the client never has to refetch the
|
|
1220
|
+
* bid list to learn it (openspec ux-260819, revised D2e).
|
|
1221
|
+
*
|
|
1222
|
+
* Sent on live wins and on D10 recovery alike (recovery hands over the stored
|
|
1223
|
+
* candle whose touch it found). Absent only on any grid-api older than that
|
|
1224
|
+
* change.
|
|
1225
|
+
*/
|
|
1226
|
+
cellCandle?: {
|
|
1227
|
+
o: string;
|
|
1228
|
+
h: string;
|
|
1229
|
+
l: string;
|
|
1230
|
+
c: string;
|
|
1231
|
+
/**
|
|
1232
|
+
* Whether that candle had CLOSED when it settled the bid. A 1m/5m win settles
|
|
1233
|
+
* the instant the band is touched — safe, because a touch is permanent — but
|
|
1234
|
+
* the candle's O/C and extremes can still move until it closes. `false` means
|
|
1235
|
+
* the client must present the value as not-yet-settled rather than final.
|
|
1236
|
+
*/
|
|
1237
|
+
final: boolean;
|
|
1238
|
+
/**
|
|
1239
|
+
* The WINNING TICK (TH-146 "success ticker"): the unix SECOND at which price
|
|
1240
|
+
* entered the bid's band, and the price inside the band it entered at. The
|
|
1241
|
+
* engine's predicate is a range overlap, so this is neither the candle's
|
|
1242
|
+
* close nor a raw extreme. Both present or both absent — a server that
|
|
1243
|
+
* predates the field sends neither.
|
|
1244
|
+
*/
|
|
1245
|
+
touchSec?: number;
|
|
1246
|
+
touchPrice?: string;
|
|
1247
|
+
};
|
|
1169
1248
|
/**
|
|
1170
1249
|
* Pair the won bid belongs to, e.g. "grid-ETH-USD". Derived from the result
|
|
1171
1250
|
* topic (`game/{agencyId}:{pairId}/user/.../bid_result`) since the won payload
|
|
@@ -1175,6 +1254,38 @@ interface MqttBidWonEvent {
|
|
|
1175
1254
|
*/
|
|
1176
1255
|
pairId?: string;
|
|
1177
1256
|
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Follow-up patch grid-api publishes when the window of a building-candle win
|
|
1259
|
+
* closes (wire type `cell_candle_final`): the CLOSED o/h/l/c of the candle the
|
|
1260
|
+
* bid settled on, replacing the in-progress values the `bidWon` event carried
|
|
1261
|
+
* with `cellCandle.final: false` (openspec ux-260819, design D2g).
|
|
1262
|
+
*
|
|
1263
|
+
* A patch, not a result: consumers must only update the bid's stored candle —
|
|
1264
|
+
* never replay win side effects (sound, payout, balance).
|
|
1265
|
+
*/
|
|
1266
|
+
interface MqttCellCandleFinalEvent {
|
|
1267
|
+
bidId: string;
|
|
1268
|
+
userId: string;
|
|
1269
|
+
cellCandle: {
|
|
1270
|
+
o: string;
|
|
1271
|
+
h: string;
|
|
1272
|
+
l: string;
|
|
1273
|
+
c: string;
|
|
1274
|
+
/** Always true — the whole point of the event is that the candle closed. */
|
|
1275
|
+
final: boolean;
|
|
1276
|
+
/**
|
|
1277
|
+
* The WINNING TICK (TH-146 "success ticker"): the unix SECOND at which price
|
|
1278
|
+
* entered the bid's band, and the price inside the band it entered at. The
|
|
1279
|
+
* engine's predicate is a range overlap, so this is neither the candle's
|
|
1280
|
+
* close nor a raw extreme. Both present or both absent — a server that
|
|
1281
|
+
* predates the field sends neither.
|
|
1282
|
+
*/
|
|
1283
|
+
touchSec?: number;
|
|
1284
|
+
touchPrice?: string;
|
|
1285
|
+
};
|
|
1286
|
+
/** Pair the bid belongs to (see {@link MqttBidWonEvent.pairId}). */
|
|
1287
|
+
pairId?: string;
|
|
1288
|
+
}
|
|
1178
1289
|
interface MqttBidLostEvent {
|
|
1179
1290
|
bidId: string;
|
|
1180
1291
|
/** Pair the lost bid belongs to (see {@link MqttBidWonEvent.pairId}). */
|
|
@@ -1268,6 +1379,7 @@ interface GameChannelEvents {
|
|
|
1268
1379
|
bidAccepted: [MqttBidAcceptedEvent];
|
|
1269
1380
|
bidWon: [MqttBidWonEvent];
|
|
1270
1381
|
bidLost: [MqttBidLostEvent];
|
|
1382
|
+
cellCandleFinal: [MqttCellCandleFinalEvent];
|
|
1271
1383
|
bidCancelled: [MqttBidCancelledEvent];
|
|
1272
1384
|
balanceUpdate: [MqttBalanceEvent];
|
|
1273
1385
|
configUpdate: [MqttConfigEvent];
|
|
@@ -1298,6 +1410,7 @@ declare class MigrationChannel extends EventEmitter<MigrationChannelEvents> {
|
|
|
1298
1410
|
interface UserBidsChannelEvents {
|
|
1299
1411
|
bidWon: [MqttBidWonEvent];
|
|
1300
1412
|
bidLost: [MqttBidLostEvent];
|
|
1413
|
+
cellCandleFinal: [MqttCellCandleFinalEvent];
|
|
1301
1414
|
error: [Error];
|
|
1302
1415
|
}
|
|
1303
1416
|
/**
|
|
@@ -1828,10 +1941,15 @@ interface CoefficientInput {
|
|
|
1828
1941
|
candleClose: number;
|
|
1829
1942
|
volatility: number;
|
|
1830
1943
|
coefMults: number[];
|
|
1831
|
-
|
|
1832
|
-
candleSize: number;
|
|
1944
|
+
coefMultsMatrix?: CoefMultsMatrix | null;
|
|
1833
1945
|
minCoef?: number;
|
|
1834
1946
|
}
|
|
1947
|
+
interface CoefMultsMatrix {
|
|
1948
|
+
timeBandsSec: number[];
|
|
1949
|
+
rows: number[][];
|
|
1950
|
+
}
|
|
1951
|
+
declare function coefMultBandIndex(timeBandsSec: number[], timeToStartSec: number): number;
|
|
1952
|
+
declare function coefMultiplierAt(timeBandsSec: number[], row: number[], timeToStartSec: number): number;
|
|
1835
1953
|
/**
|
|
1836
1954
|
* First-passage hitting probability: chance the price visits [price1, price2]
|
|
1837
1955
|
* at any point during [time1, time2], given GBM dynamics.
|
|
@@ -1848,4 +1966,4 @@ declare function computeBaseline(candleClose: number, candleTimeSec: number, cel
|
|
|
1848
1966
|
baselineTime: number;
|
|
1849
1967
|
};
|
|
1850
1968
|
|
|
1851
|
-
export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, CANDLE_EVENT, type CancelBidResult, type Candle, type CandleEventType, type CoefficientInput, type Constraints, type Currency, DEFAULT_CHART_HISTORY_LIMIT, DEFAULT_PROBE_TTL_SECONDS, DEFAULT_REGION, type ErrorMessageCatalog, type ErrorMessageMeta, type ErrorMessagesResponse, GameChannel, type GameConfig, type GridConfig, KNOWN_REGIONS, type KnownRegion, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, MigrationChannel, type MigrationChannelEvents, type MigrationCompletedEvent, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttAuthConfig, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type MqttWalletBalanceEvent, type MyRank, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type Pair, type PairInfo, PairModule, type PlaceBidInput, type ProbeNearestRegionOptions, REGION_PROBE_CACHE_KEY, type RankBoard, type RankEntry, type RankPeriod, type RankSort, RealtimeModule, type RefreshTokenResult, type RegionDomainMap, type Sample, type SampleReason, type SignalSource, type SortDir, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserBidsChannel, type UserBidsChannelEvents, UserModule, type UserPnL, type UserPnLPeriod, type Wallet as UserWallet, type Wallet$1 as Wallet, type WalletBalanceReason, WalletChannel, adaptiveSimpson, autoDetectStorage, calculateCoefficientWrapper, calculateProbHit, calculateProbWin, calculateProbWin_v2, computeBaseline, errorFunction, isCancelled, isKnownRegion, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF, normaliseLang, pairIdFromBidResultTopic, probeNearestRegion, resolveRegionBaseUrl, roundCoefToSignificantDigits };
|
|
1969
|
+
export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, type BidCellCandle, BidModule, type BidStatus, CANDLE_EVENT, type CancelBidResult, type Candle, type CandleEventType, type CoefMultsMatrix, type CoefficientInput, type Constraints, type Currency, DEFAULT_CHART_HISTORY_LIMIT, DEFAULT_PROBE_TTL_SECONDS, DEFAULT_REGION, type ErrorMessageCatalog, type ErrorMessageMeta, type ErrorMessagesResponse, GameChannel, type GameConfig, type GridConfig, KNOWN_REGIONS, type KnownRegion, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, MigrationChannel, type MigrationChannelEvents, type MigrationCompletedEvent, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttAuthConfig, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttCellCandleFinalEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type MqttWalletBalanceEvent, type MyRank, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type Pair, type PairInfo, PairModule, type PlaceBidInput, type ProbeNearestRegionOptions, REGION_PROBE_CACHE_KEY, type RankBoard, type RankEntry, type RankPeriod, type RankSort, RealtimeModule, type RefreshTokenResult, type RegionDomainMap, type Sample, type SampleReason, type SignalSource, type SortDir, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserBidsChannel, type UserBidsChannelEvents, UserModule, type UserPnL, type UserPnLPeriod, type Wallet as UserWallet, type Wallet$1 as Wallet, type WalletBalanceReason, WalletChannel, adaptiveSimpson, autoDetectStorage, calculateCoefficientWrapper, calculateProbHit, calculateProbWin, calculateProbWin_v2, coefMultBandIndex, coefMultiplierAt, computeBaseline, errorFunction, isCancelled, isKnownRegion, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF, normaliseLang, pairIdFromBidResultTopic, probeNearestRegion, resolveRegionBaseUrl, roundCoefToSignificantDigits };
|
package/dist/index.js
CHANGED
|
@@ -581,7 +581,22 @@ function normaliseBid(node) {
|
|
|
581
581
|
refundAmount: node.refund_amount ?? null,
|
|
582
582
|
slippage: node.slippage,
|
|
583
583
|
createdAt: node.created_at || null,
|
|
584
|
-
meta: parseMeta(node.meta)
|
|
584
|
+
meta: parseMeta(node.meta),
|
|
585
|
+
// Absent and explicit null both mean "no candle" — an older server, an unselected
|
|
586
|
+
// field, a pending bid and purged ticks must all read the same to a consumer.
|
|
587
|
+
// `final` null (nullable Boolean) collapses to absent = settled, so only a real
|
|
588
|
+
// `false` puts the row in the in-progress presentation.
|
|
589
|
+
cellCandle: node.cellCandle ? {
|
|
590
|
+
o: node.cellCandle.o,
|
|
591
|
+
h: node.cellCandle.h,
|
|
592
|
+
l: node.cellCandle.l,
|
|
593
|
+
c: node.cellCandle.c,
|
|
594
|
+
...typeof node.cellCandle.final === "boolean" ? { final: node.cellCandle.final } : {},
|
|
595
|
+
// Both legs or neither — see BidCellCandle.touchSec. Nulls (a non-win,
|
|
596
|
+
// or an older server) collapse to absent so a consumer only has to
|
|
597
|
+
// check one of them.
|
|
598
|
+
...typeof node.cellCandle.touchSec === "number" && typeof node.cellCandle.touchPrice === "string" ? { touchSec: node.cellCandle.touchSec, touchPrice: node.cellCandle.touchPrice } : {}
|
|
599
|
+
} : null
|
|
585
600
|
};
|
|
586
601
|
}
|
|
587
602
|
function normaliseBids(list) {
|
|
@@ -602,6 +617,7 @@ var PLACE_BID_MUTATION = `mutation PlaceBid($input: PlaceBidInput!) {
|
|
|
602
617
|
var MY_BIDS_QUERY = `query MyBids($statuses: [BidStatus!], $limit: Int, $offset: Int, $pairId: ID) {
|
|
603
618
|
myBids(statuses: $statuses, limit: $limit, offset: $offset, pairId: $pairId) {
|
|
604
619
|
id user_id pair_id currency amount coefficient time1 time2 price1 price2 status payout refund_rate refund_amount slippage created_at meta
|
|
620
|
+
cellCandle { o h l c final touchSec touchPrice }
|
|
605
621
|
}
|
|
606
622
|
}`;
|
|
607
623
|
var CANCEL_BID_MUTATION = `mutation CancelBid($input: CancelBidInput!) {
|
|
@@ -1158,7 +1174,9 @@ function normaliseConstraints(node) {
|
|
|
1158
1174
|
priceMinRange: node.priceMinRange,
|
|
1159
1175
|
priceMaxRange: node.priceMaxRange,
|
|
1160
1176
|
coefMults: node.coefMults,
|
|
1161
|
-
maxCoef: node.maxCoef
|
|
1177
|
+
maxCoef: node.maxCoef,
|
|
1178
|
+
// bid-260819: pass through untouched — pricing consumes it as-is.
|
|
1179
|
+
coefMultsMatrix: node.coefMultsMatrix ?? null
|
|
1162
1180
|
};
|
|
1163
1181
|
}
|
|
1164
1182
|
function normaliseCandles(list) {
|
|
@@ -1211,7 +1229,7 @@ var PAIR_QUERY = `query AgencyPair($pairId: ID!) {
|
|
|
1211
1229
|
id pair { id pair source thumb } status createdAt
|
|
1212
1230
|
config {
|
|
1213
1231
|
gridConfig { cellSizeTime cellSizeValue candleSize baseline baselineTime }
|
|
1214
|
-
constraints { minBetTime maxBetTime slippage priceMinRange priceMaxRange coefMults maxCoef }
|
|
1232
|
+
constraints { minBetTime maxBetTime slippage priceMinRange priceMaxRange coefMults maxCoef coefMultsMatrix { timeBandsSec rows } }
|
|
1215
1233
|
acceptableBids minBidAmount maxBidAmount
|
|
1216
1234
|
bidCancelRefundRate bidCancelMinSeconds
|
|
1217
1235
|
}
|
|
@@ -1769,6 +1787,23 @@ function mapWireBidResult(raw, topicPairId) {
|
|
|
1769
1787
|
userId: p.user_id
|
|
1770
1788
|
};
|
|
1771
1789
|
if (typeof p.balance === "string") data.balance = p.balance;
|
|
1790
|
+
const candle = p.cellCandle;
|
|
1791
|
+
if (candle && typeof candle.o === "string" && typeof candle.h === "string" && typeof candle.l === "string" && typeof candle.c === "string") {
|
|
1792
|
+
data.cellCandle = {
|
|
1793
|
+
o: candle.o,
|
|
1794
|
+
h: candle.h,
|
|
1795
|
+
l: candle.l,
|
|
1796
|
+
c: candle.c,
|
|
1797
|
+
final: candle.final !== "false"
|
|
1798
|
+
};
|
|
1799
|
+
if (typeof candle.touchSec === "string" && typeof candle.touchPrice === "string") {
|
|
1800
|
+
const touchSec = Number.parseInt(candle.touchSec, 10);
|
|
1801
|
+
if (Number.isFinite(touchSec)) {
|
|
1802
|
+
data.cellCandle.touchSec = touchSec;
|
|
1803
|
+
data.cellCandle.touchPrice = candle.touchPrice;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1772
1807
|
if (topicPairId !== void 0) data.pairId = topicPairId;
|
|
1773
1808
|
return { event: "bidWon", data };
|
|
1774
1809
|
}
|
|
@@ -1777,6 +1812,32 @@ function mapWireBidResult(raw, topicPairId) {
|
|
|
1777
1812
|
if (topicPairId !== void 0) data.pairId = topicPairId;
|
|
1778
1813
|
return { event: "bidLost", data };
|
|
1779
1814
|
}
|
|
1815
|
+
case "cell_candle_final": {
|
|
1816
|
+
const candle = p.cellCandle;
|
|
1817
|
+
if (!candle || typeof candle.o !== "string" || typeof candle.h !== "string" || typeof candle.l !== "string" || typeof candle.c !== "string") {
|
|
1818
|
+
return null;
|
|
1819
|
+
}
|
|
1820
|
+
const data = {
|
|
1821
|
+
bidId: p.bidId,
|
|
1822
|
+
userId: p.user_id,
|
|
1823
|
+
cellCandle: {
|
|
1824
|
+
o: candle.o,
|
|
1825
|
+
h: candle.h,
|
|
1826
|
+
l: candle.l,
|
|
1827
|
+
c: candle.c,
|
|
1828
|
+
final: candle.final !== "false"
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
if (typeof candle.touchSec === "string" && typeof candle.touchPrice === "string") {
|
|
1832
|
+
const touchSec = Number.parseInt(candle.touchSec, 10);
|
|
1833
|
+
if (Number.isFinite(touchSec)) {
|
|
1834
|
+
data.cellCandle.touchSec = touchSec;
|
|
1835
|
+
data.cellCandle.touchPrice = candle.touchPrice;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
if (topicPairId !== void 0) data.pairId = topicPairId;
|
|
1839
|
+
return { event: "cellCandleFinal", data };
|
|
1840
|
+
}
|
|
1780
1841
|
default:
|
|
1781
1842
|
console.warn(`Unknown bid_result type "${String(p.type)}"`);
|
|
1782
1843
|
return null;
|
|
@@ -1894,6 +1955,7 @@ var RealtimeModule = class extends EventEmitter5 {
|
|
|
1894
1955
|
else if (mapped.event === "bidAccepted") channel.emit("bidAccepted", mapped.data);
|
|
1895
1956
|
else if (mapped.event === "bidWon") channel.emit("bidWon", mapped.data);
|
|
1896
1957
|
else if (mapped.event === "bidLost") channel.emit("bidLost", mapped.data);
|
|
1958
|
+
else if (mapped.event === "cellCandleFinal") channel.emit("cellCandleFinal", mapped.data);
|
|
1897
1959
|
else if (mapped.event === "balanceUpdate") channel.emit("balanceUpdate", mapped.data);
|
|
1898
1960
|
else if (mapped.event === "configUpdate") channel.emit("configUpdate", mapped.data);
|
|
1899
1961
|
else if (mapped.event === "idealConfigUpdate")
|
|
@@ -2026,6 +2088,7 @@ var RealtimeModule = class extends EventEmitter5 {
|
|
|
2026
2088
|
if (!mapped) return;
|
|
2027
2089
|
if (mapped.event === "bidWon") channel.emit("bidWon", mapped.data);
|
|
2028
2090
|
else if (mapped.event === "bidLost") channel.emit("bidLost", mapped.data);
|
|
2091
|
+
else if (mapped.event === "cellCandleFinal") channel.emit("cellCandleFinal", mapped.data);
|
|
2029
2092
|
};
|
|
2030
2093
|
const onError = (err) => {
|
|
2031
2094
|
channel.emit("error", err);
|
|
@@ -3706,6 +3769,40 @@ function calculateProbWin_v2(time1, time2, _price1, price2, creationTime, curren
|
|
|
3706
3769
|
const prob = 1 - normalCDF(z);
|
|
3707
3770
|
return Math.max(0, Math.min(1, prob));
|
|
3708
3771
|
}
|
|
3772
|
+
function coefMultBandIndex(timeBandsSec, timeToStartSec) {
|
|
3773
|
+
for (let i = 0; i < timeBandsSec.length; i++) {
|
|
3774
|
+
if (timeToStartSec <= timeBandsSec[i]) return i;
|
|
3775
|
+
}
|
|
3776
|
+
return timeBandsSec.length;
|
|
3777
|
+
}
|
|
3778
|
+
function coefMultiplierAt(timeBandsSec, row, timeToStartSec) {
|
|
3779
|
+
if (row.length === 0) return 1;
|
|
3780
|
+
if (row.length === 1 || timeBandsSec.length === 0) return row[0];
|
|
3781
|
+
let valueCount = row.length;
|
|
3782
|
+
if (valueCount > timeBandsSec.length + 1) valueCount = timeBandsSec.length + 1;
|
|
3783
|
+
const edges = timeBandsSec.slice(0, valueCount - 1);
|
|
3784
|
+
const anchors = new Array(valueCount);
|
|
3785
|
+
anchors[0] = edges[0] / 2;
|
|
3786
|
+
for (let i = 1; i < valueCount - 1; i++) {
|
|
3787
|
+
anchors[i] = (edges[i - 1] + edges[i]) / 2;
|
|
3788
|
+
}
|
|
3789
|
+
const last = valueCount - 1;
|
|
3790
|
+
if (edges.length >= 2) {
|
|
3791
|
+
anchors[last] = edges[last - 1] + (edges[last - 1] - edges[last - 2]) / 2;
|
|
3792
|
+
} else {
|
|
3793
|
+
anchors[last] = 1.5 * edges[0];
|
|
3794
|
+
}
|
|
3795
|
+
const t = timeToStartSec;
|
|
3796
|
+
if (t <= anchors[0]) return row[0];
|
|
3797
|
+
if (t >= anchors[last]) return row[last];
|
|
3798
|
+
for (let i = 1; i <= last; i++) {
|
|
3799
|
+
if (t <= anchors[i]) {
|
|
3800
|
+
const frac = (t - anchors[i - 1]) / (anchors[i] - anchors[i - 1]);
|
|
3801
|
+
return row[i - 1] + (row[i] - row[i - 1]) * frac;
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
return row[last];
|
|
3805
|
+
}
|
|
3709
3806
|
function simpsonForHit(f, a, b, eps = 3e-5, maxDepth = 14) {
|
|
3710
3807
|
function simpsonRule(a2, b2) {
|
|
3711
3808
|
const c = (a2 + b2) / 2;
|
|
@@ -3775,7 +3872,7 @@ function calculateCoefficientWrapper(params) {
|
|
|
3775
3872
|
candleClose,
|
|
3776
3873
|
volatility,
|
|
3777
3874
|
coefMults,
|
|
3778
|
-
|
|
3875
|
+
coefMultsMatrix,
|
|
3779
3876
|
minCoef
|
|
3780
3877
|
} = params;
|
|
3781
3878
|
const currentPrice = candleClose;
|
|
@@ -3792,22 +3889,46 @@ function calculateCoefficientWrapper(params) {
|
|
|
3792
3889
|
if (probability <= 0) return Number.POSITIVE_INFINITY;
|
|
3793
3890
|
const rawCoef = 1 / probability;
|
|
3794
3891
|
const rawIndex = rawCoef < 1 ? 0 : Math.floor(Math.log2(rawCoef));
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3892
|
+
let multiplier = 1;
|
|
3893
|
+
if (coefMultsMatrix && coefMultsMatrix.rows.length > 0) {
|
|
3894
|
+
const bucketIndex = Math.min(rawIndex, coefMultsMatrix.rows.length - 1);
|
|
3895
|
+
const row = coefMultsMatrix.rows[bucketIndex];
|
|
3896
|
+
if (row && row.length > 0) {
|
|
3897
|
+
multiplier = coefMultiplierAt(coefMultsMatrix.timeBandsSec, row, time1 - candleTime);
|
|
3898
|
+
}
|
|
3899
|
+
} else {
|
|
3900
|
+
const coefMultIndex = Math.min(rawIndex, coefMults.length - 1);
|
|
3901
|
+
multiplier = coefMults[coefMultIndex] || 1;
|
|
3902
|
+
}
|
|
3800
3903
|
const floor = typeof minCoef === "number" && minCoef > 0 ? minCoef : 1;
|
|
3801
|
-
const finalCoef = Math.max(floor, multiplier /
|
|
3904
|
+
const finalCoef = Math.max(floor, multiplier / probability);
|
|
3802
3905
|
return roundCoefToSignificantDigits(finalCoef);
|
|
3803
3906
|
}
|
|
3804
3907
|
function roundCoefToSignificantDigits(value) {
|
|
3805
3908
|
if (!Number.isFinite(value) || value <= 0) return value;
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3909
|
+
let exponent = 0;
|
|
3910
|
+
let scaled = value;
|
|
3911
|
+
while (scaled >= 10) {
|
|
3912
|
+
scaled /= 10;
|
|
3913
|
+
exponent++;
|
|
3914
|
+
}
|
|
3915
|
+
while (scaled < 1) {
|
|
3916
|
+
scaled *= 10;
|
|
3917
|
+
exponent--;
|
|
3918
|
+
}
|
|
3919
|
+
const digits = Math.floor(scaled) === 1 ? 3 : 2;
|
|
3920
|
+
const factorExponent = digits - exponent - 1;
|
|
3921
|
+
if (factorExponent >= 0) {
|
|
3922
|
+
const factor = exactPowerOfTen(factorExponent);
|
|
3923
|
+
return Math.round(value * factor) / factor;
|
|
3924
|
+
}
|
|
3925
|
+
const divisor = exactPowerOfTen(-factorExponent);
|
|
3926
|
+
return Math.round(value / divisor) * divisor;
|
|
3927
|
+
}
|
|
3928
|
+
function exactPowerOfTen(n) {
|
|
3929
|
+
let power = 1;
|
|
3930
|
+
for (let i = 0; i < n; i++) power *= 10;
|
|
3931
|
+
return power;
|
|
3811
3932
|
}
|
|
3812
3933
|
function computeBaseline(candleClose, candleTimeSec, cellSizeValue, cellSizeTime, candleSize) {
|
|
3813
3934
|
const baseline = Math.floor(candleClose / cellSizeValue) * cellSizeValue;
|
|
@@ -3846,6 +3967,8 @@ export {
|
|
|
3846
3967
|
calculateProbHit,
|
|
3847
3968
|
calculateProbWin,
|
|
3848
3969
|
calculateProbWin_v2,
|
|
3970
|
+
coefMultBandIndex,
|
|
3971
|
+
coefMultiplierAt,
|
|
3849
3972
|
computeBaseline,
|
|
3850
3973
|
errorFunction,
|
|
3851
3974
|
isCancelled,
|