@pear-protocol/exchanges-sdk 0.6.7 → 0.8.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 (41) hide show
  1. package/README.md +105 -25
  2. package/dist/client/bybit/rest.js +8 -1
  3. package/dist/client/hyperliquid/rest.js +21 -1
  4. package/dist/client/lighter/rest.js +6 -12
  5. package/dist/client/okx/rest.js +5 -1
  6. package/dist/index.d.ts +1131 -78
  7. package/dist/sdk.js +9 -2
  8. package/dist/syncer/base.js +36 -14
  9. package/dist/tracker/balance-core.js +89 -0
  10. package/dist/tracker/balance.js +25 -0
  11. package/dist/tracker/basket-size.js +194 -0
  12. package/dist/tracker/binance/balance.js +11 -0
  13. package/dist/tracker/binance/mapper.js +87 -42
  14. package/dist/tracker/binance/tracker.js +29 -14
  15. package/dist/tracker/binance/types.js +0 -6
  16. package/dist/tracker/bybit/balance.js +14 -0
  17. package/dist/tracker/bybit/mapper.js +109 -52
  18. package/dist/tracker/bybit/tracker.js +242 -33
  19. package/dist/tracker/bybit/types.js +0 -7
  20. package/dist/tracker/hyperliquid/account-mode.js +14 -0
  21. package/dist/tracker/hyperliquid/balance.js +184 -0
  22. package/dist/tracker/hyperliquid/basket-size.js +93 -0
  23. package/dist/tracker/hyperliquid/coin-name.js +18 -0
  24. package/dist/tracker/hyperliquid/collateral.js +48 -0
  25. package/dist/tracker/hyperliquid/decimal.js +28 -0
  26. package/dist/tracker/hyperliquid/mapper.js +399 -176
  27. package/dist/tracker/hyperliquid/spot-pricing.js +106 -0
  28. package/dist/tracker/hyperliquid/tracker.js +237 -78
  29. package/dist/tracker/hyperliquid/types.js +1 -7
  30. package/dist/tracker/index.js +9 -7
  31. package/dist/tracker/lighter/balance.js +87 -0
  32. package/dist/tracker/lighter/basket-size.js +74 -0
  33. package/dist/tracker/lighter/mapper.js +324 -145
  34. package/dist/tracker/lighter/tracker.js +228 -117
  35. package/dist/tracker/lighter/types.js +1 -7
  36. package/dist/tracker/okx/balance.js +17 -0
  37. package/dist/tracker/okx/mapper.js +156 -58
  38. package/dist/tracker/okx/tracker.js +228 -29
  39. package/dist/tracker/okx/types.js +0 -8
  40. package/dist/tracker/types.js +0 -24
  41. package/package.json +7 -9
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import PearSDK from '@pear-protocol/core-sdk';
3
3
  import { ProxyConfig } from '@pear-protocol/utils';
4
4
  import { WebSocket } from 'partysocket';
5
5
  import { WalletClient, Transport, Chain, Account } from 'viem';
6
+ import { z } from 'zod';
6
7
 
7
8
  type SupportedCredentials = HttpTypes.ExchangeCredentialsResponse['credentials'];
8
9
  type ExchangesSDKConfig = {
@@ -561,9 +562,26 @@ interface AccountSyncerConfig {
561
562
  credentials: SupportedCredentials;
562
563
  demo: boolean;
563
564
  }
565
+ /**
566
+ * Outcome of one fill sync cycle, so batch/cron callers can tell a completed
567
+ * cycle apart from one that must be retried:
568
+ * - `synced`: the cycle posted, or there was nothing to post.
569
+ * - `deferred`: the server answered 409 SYNC_CREATE_*; the watermark did not
570
+ * advance, so a later cycle retries the same window.
571
+ * - `aborted`: the exchange fetch failed; nothing was posted.
572
+ */
573
+ type FillSyncOutcome = 'synced' | 'deferred' | 'aborted';
574
+ /**
575
+ * Outcome of one funding sync cycle. Funding has no server deferral contract,
576
+ * so the union is narrower than {@link FillSyncOutcome}:
577
+ * - `synced`: the cycle posted, or there were no new payments.
578
+ * - `aborted`: the exchange funding fetch failed; nothing was posted.
579
+ * A failed post still throws — only the exchange fetch is guarded.
580
+ */
581
+ type FundingSyncOutcome = 'synced' | 'aborted';
564
582
  interface AccountSyncer {
565
- synchronizeFills(): Promise<void>;
566
- synchronizeFunding(): Promise<void>;
583
+ synchronizeFills(): Promise<FillSyncOutcome>;
584
+ synchronizeFunding(): Promise<FundingSyncOutcome>;
567
585
  }
568
586
  interface ListExternalFillsParams {
569
587
  since: Date;
@@ -613,8 +631,8 @@ declare abstract class BaseAccountSyncer<ExternalFill> implements AccountSyncer
613
631
  * Independent of the fill sync cycle (`synchronizeFills`) — funding rows are
614
632
  * immutable and upsert idempotently, so this is driven on its own cadence.
615
633
  */
616
- synchronizeFunding(): Promise<void>;
617
- synchronizeFills(): Promise<void>;
634
+ synchronizeFunding(): Promise<FundingSyncOutcome>;
635
+ synchronizeFills(): Promise<FillSyncOutcome>;
618
636
  }
619
637
 
620
638
  interface BinanceRawUserTrade {
@@ -809,65 +827,440 @@ declare class OkxAccountSyncer extends BaseAccountSyncer<RawFill> {
809
827
  private contractsToBase;
810
828
  }
811
829
 
812
- declare enum BinanceAccountType {
813
- MULTI_ASSET = "MULTI_ASSET",
814
- SINGLE_ASSET = "SINGLE_ASSET"
815
- }
816
-
817
- declare enum BybitAccountType {
818
- ISOLATED_MARGIN = "ISOLATED_MARGIN",
819
- REGULAR_MARGIN = "REGULAR_MARGIN",
820
- PORTFOLIO_MARGIN = "PORTFOLIO_MARGIN"
821
- }
830
+ /**
831
+ * The account balance a consumer receives, as one discriminated union over `accountMode`.
832
+ *
833
+ * Narrowing on the tag gives exactly the fields that venue and mode support and no others, and a
834
+ * newly added mode fails every exhaustive `switch` at compile time. This module sits above the
835
+ * venue variant files, which in turn sit above `./balance-core` — one direction, no cycle.
836
+ */
822
837
 
823
- declare enum HyperliquidAccountType {
824
- DEFAULT = "default",
825
- DISABLED = "disabled",
826
- PORTFOLIO_MARGIN = "portfolioMargin",
827
- UNIFIED_ACCOUNT = "unifiedAccount"
828
- }
838
+ declare const AccountBalance: z.ZodDiscriminatedUnion<[z.ZodObject<{
839
+ totalEquity: z.ZodNullable<z.ZodString>;
840
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
841
+ availableMargin: z.ZodNullable<z.ZodString>;
842
+ poolsShared: z.ZodBoolean;
843
+ perCollateral: z.ZodArray<z.ZodObject<{
844
+ asset: z.ZodString;
845
+ equity: z.ZodString;
846
+ availableMargin: z.ZodNullable<z.ZodString>;
847
+ withdrawable: z.ZodNullable<z.ZodString>;
848
+ }, z.core.$strip>>;
849
+ timestamp: z.ZodNumber;
850
+ accountMode: z.ZodLiteral<"hyperliquid:standard">;
851
+ spotEquity: z.ZodNullable<z.ZodString>;
852
+ perpsEquity: z.ZodString;
853
+ vaultEquity: z.ZodString;
854
+ stakedEquity: z.ZodNullable<z.ZodString>;
855
+ unrealizedPnl: z.ZodString;
856
+ maintenanceMargin: z.ZodString;
857
+ crossMarginRatio: z.ZodString;
858
+ crossAccountLeverage: z.ZodString;
859
+ perDexState: z.ZodArray<z.ZodObject<{
860
+ dex: z.ZodString;
861
+ collateralToken: z.ZodNullable<z.ZodNumber>;
862
+ accountValue: z.ZodString;
863
+ crossNotional: z.ZodString;
864
+ crossMaintenanceMargin: z.ZodString;
865
+ withdrawable: z.ZodString;
866
+ }, z.core.$strip>>;
867
+ }, z.core.$strip>, z.ZodObject<{
868
+ totalEquity: z.ZodNullable<z.ZodString>;
869
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
870
+ availableMargin: z.ZodNullable<z.ZodString>;
871
+ poolsShared: z.ZodBoolean;
872
+ perCollateral: z.ZodArray<z.ZodObject<{
873
+ asset: z.ZodString;
874
+ equity: z.ZodString;
875
+ availableMargin: z.ZodNullable<z.ZodString>;
876
+ withdrawable: z.ZodNullable<z.ZodString>;
877
+ }, z.core.$strip>>;
878
+ timestamp: z.ZodNumber;
879
+ accountMode: z.ZodLiteral<"hyperliquid:unified">;
880
+ tradingEquity: z.ZodNullable<z.ZodString>;
881
+ vaultEquity: z.ZodString;
882
+ stakedEquity: z.ZodNullable<z.ZodString>;
883
+ portfolioValue: z.ZodNullable<z.ZodString>;
884
+ unrealizedPnl: z.ZodString;
885
+ perpsMaintenanceMargin: z.ZodString;
886
+ unifiedAccountRatio: z.ZodString;
887
+ unifiedAccountLeverage: z.ZodNullable<z.ZodString>;
888
+ perDexState: z.ZodArray<z.ZodObject<{
889
+ dex: z.ZodString;
890
+ collateralToken: z.ZodNullable<z.ZodNumber>;
891
+ accountValue: z.ZodString;
892
+ crossNotional: z.ZodString;
893
+ crossMaintenanceMargin: z.ZodString;
894
+ withdrawable: z.ZodString;
895
+ }, z.core.$strip>>;
896
+ }, z.core.$strip>, z.ZodObject<{
897
+ totalEquity: z.ZodNullable<z.ZodString>;
898
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
899
+ availableMargin: z.ZodNullable<z.ZodString>;
900
+ poolsShared: z.ZodBoolean;
901
+ timestamp: z.ZodNumber;
902
+ accountMode: z.ZodLiteral<"hyperliquid:portfolioMargin">;
903
+ perCollateral: z.ZodArray<z.ZodObject<{
904
+ asset: z.ZodString;
905
+ equity: z.ZodString;
906
+ availableMargin: z.ZodNullable<z.ZodString>;
907
+ withdrawable: z.ZodString;
908
+ ltv: z.ZodNullable<z.ZodString>;
909
+ borrowCapUsed: z.ZodNullable<z.ZodString>;
910
+ }, z.core.$strip>>;
911
+ tradingEquity: z.ZodNullable<z.ZodString>;
912
+ vaultEquity: z.ZodString;
913
+ stakedEquity: z.ZodNullable<z.ZodString>;
914
+ portfolioValue: z.ZodNullable<z.ZodString>;
915
+ unrealizedPnl: z.ZodString;
916
+ perpsMaintenanceMargin: z.ZodString;
917
+ portfolioMarginRatio: z.ZodNullable<z.ZodString>;
918
+ portfolioAccountLeverage: z.ZodNullable<z.ZodString>;
919
+ borrowCapUsed: z.ZodNullable<z.ZodString>;
920
+ perDexState: z.ZodArray<z.ZodObject<{
921
+ dex: z.ZodString;
922
+ collateralToken: z.ZodNullable<z.ZodNumber>;
923
+ accountValue: z.ZodString;
924
+ crossNotional: z.ZodString;
925
+ crossMaintenanceMargin: z.ZodString;
926
+ withdrawable: z.ZodString;
927
+ }, z.core.$strip>>;
928
+ }, z.core.$strip>, z.ZodObject<{
929
+ totalEquity: z.ZodNullable<z.ZodString>;
930
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
931
+ availableMargin: z.ZodNullable<z.ZodString>;
932
+ poolsShared: z.ZodBoolean;
933
+ perCollateral: z.ZodArray<z.ZodObject<{
934
+ asset: z.ZodString;
935
+ equity: z.ZodString;
936
+ availableMargin: z.ZodNullable<z.ZodString>;
937
+ withdrawable: z.ZodNullable<z.ZodString>;
938
+ }, z.core.$strip>>;
939
+ timestamp: z.ZodNumber;
940
+ tradingEquity: z.ZodNullable<z.ZodString>;
941
+ perpetualsEquity: z.ZodNullable<z.ZodString>;
942
+ spotEquity: z.ZodNullable<z.ZodString>;
943
+ publicPoolsEquity: z.ZodNullable<z.ZodString>;
944
+ stakingEquity: z.ZodNullable<z.ZodString>;
945
+ creditedInLit: z.ZodNullable<z.ZodString>;
946
+ unrealizedPnl: z.ZodNullable<z.ZodString>;
947
+ crossLeverage: z.ZodNullable<z.ZodString>;
948
+ maintenanceMargin: z.ZodNullable<z.ZodString>;
949
+ crossMarginUsage: z.ZodNullable<z.ZodString>;
950
+ crossMarginRatio: z.ZodNullable<z.ZodString>;
951
+ accountMode: z.ZodLiteral<"lighter:classic">;
952
+ }, z.core.$strip>, z.ZodObject<{
953
+ totalEquity: z.ZodNullable<z.ZodString>;
954
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
955
+ availableMargin: z.ZodNullable<z.ZodString>;
956
+ poolsShared: z.ZodBoolean;
957
+ perCollateral: z.ZodArray<z.ZodObject<{
958
+ asset: z.ZodString;
959
+ equity: z.ZodString;
960
+ availableMargin: z.ZodNullable<z.ZodString>;
961
+ withdrawable: z.ZodNullable<z.ZodString>;
962
+ }, z.core.$strip>>;
963
+ timestamp: z.ZodNumber;
964
+ portfolioMargin: z.ZodNullable<z.ZodString>;
965
+ tradingEquity: z.ZodNullable<z.ZodString>;
966
+ perpetualsEquity: z.ZodNullable<z.ZodString>;
967
+ spotEquity: z.ZodNullable<z.ZodString>;
968
+ publicPoolsEquity: z.ZodNullable<z.ZodString>;
969
+ stakingEquity: z.ZodNullable<z.ZodString>;
970
+ creditedInLit: z.ZodNullable<z.ZodString>;
971
+ unrealizedPnl: z.ZodNullable<z.ZodString>;
972
+ crossLeverage: z.ZodNullable<z.ZodString>;
973
+ maintenanceMargin: z.ZodNullable<z.ZodString>;
974
+ crossMarginUsage: z.ZodNullable<z.ZodString>;
975
+ crossMarginRatio: z.ZodNullable<z.ZodString>;
976
+ accountMode: z.ZodLiteral<"lighter:unified">;
977
+ }, z.core.$strip>, z.ZodObject<{
978
+ totalEquity: z.ZodNullable<z.ZodString>;
979
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
980
+ availableMargin: z.ZodNullable<z.ZodString>;
981
+ poolsShared: z.ZodBoolean;
982
+ perCollateral: z.ZodArray<z.ZodObject<{
983
+ asset: z.ZodString;
984
+ equity: z.ZodString;
985
+ availableMargin: z.ZodNullable<z.ZodString>;
986
+ withdrawable: z.ZodNullable<z.ZodString>;
987
+ }, z.core.$strip>>;
988
+ timestamp: z.ZodNumber;
989
+ accountMode: z.ZodLiteral<"binance:singleAsset">;
990
+ }, z.core.$strip>, z.ZodObject<{
991
+ totalEquity: z.ZodNullable<z.ZodString>;
992
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
993
+ availableMargin: z.ZodNullable<z.ZodString>;
994
+ poolsShared: z.ZodBoolean;
995
+ perCollateral: z.ZodArray<z.ZodObject<{
996
+ asset: z.ZodString;
997
+ equity: z.ZodString;
998
+ availableMargin: z.ZodNullable<z.ZodString>;
999
+ withdrawable: z.ZodNullable<z.ZodString>;
1000
+ }, z.core.$strip>>;
1001
+ timestamp: z.ZodNumber;
1002
+ accountMode: z.ZodLiteral<"binance:multiAsset">;
1003
+ }, z.core.$strip>, z.ZodObject<{
1004
+ totalEquity: z.ZodNullable<z.ZodString>;
1005
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1006
+ availableMargin: z.ZodNullable<z.ZodString>;
1007
+ poolsShared: z.ZodBoolean;
1008
+ perCollateral: z.ZodArray<z.ZodObject<{
1009
+ asset: z.ZodString;
1010
+ equity: z.ZodString;
1011
+ availableMargin: z.ZodNullable<z.ZodString>;
1012
+ withdrawable: z.ZodNullable<z.ZodString>;
1013
+ }, z.core.$strip>>;
1014
+ timestamp: z.ZodNumber;
1015
+ accountMode: z.ZodLiteral<"bybit:isolatedMargin">;
1016
+ }, z.core.$strip>, z.ZodObject<{
1017
+ totalEquity: z.ZodNullable<z.ZodString>;
1018
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1019
+ availableMargin: z.ZodNullable<z.ZodString>;
1020
+ poolsShared: z.ZodBoolean;
1021
+ perCollateral: z.ZodArray<z.ZodObject<{
1022
+ asset: z.ZodString;
1023
+ equity: z.ZodString;
1024
+ availableMargin: z.ZodNullable<z.ZodString>;
1025
+ withdrawable: z.ZodNullable<z.ZodString>;
1026
+ }, z.core.$strip>>;
1027
+ timestamp: z.ZodNumber;
1028
+ accountMode: z.ZodLiteral<"bybit:regularMargin">;
1029
+ }, z.core.$strip>, z.ZodObject<{
1030
+ totalEquity: z.ZodNullable<z.ZodString>;
1031
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1032
+ availableMargin: z.ZodNullable<z.ZodString>;
1033
+ poolsShared: z.ZodBoolean;
1034
+ perCollateral: z.ZodArray<z.ZodObject<{
1035
+ asset: z.ZodString;
1036
+ equity: z.ZodString;
1037
+ availableMargin: z.ZodNullable<z.ZodString>;
1038
+ withdrawable: z.ZodNullable<z.ZodString>;
1039
+ }, z.core.$strip>>;
1040
+ timestamp: z.ZodNumber;
1041
+ accountMode: z.ZodLiteral<"bybit:portfolioMargin">;
1042
+ }, z.core.$strip>, z.ZodObject<{
1043
+ totalEquity: z.ZodNullable<z.ZodString>;
1044
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1045
+ availableMargin: z.ZodNullable<z.ZodString>;
1046
+ poolsShared: z.ZodBoolean;
1047
+ perCollateral: z.ZodArray<z.ZodObject<{
1048
+ asset: z.ZodString;
1049
+ equity: z.ZodString;
1050
+ availableMargin: z.ZodNullable<z.ZodString>;
1051
+ withdrawable: z.ZodNullable<z.ZodString>;
1052
+ }, z.core.$strip>>;
1053
+ timestamp: z.ZodNumber;
1054
+ accountMode: z.ZodLiteral<"okx:spot">;
1055
+ }, z.core.$strip>, z.ZodObject<{
1056
+ totalEquity: z.ZodNullable<z.ZodString>;
1057
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1058
+ availableMargin: z.ZodNullable<z.ZodString>;
1059
+ poolsShared: z.ZodBoolean;
1060
+ perCollateral: z.ZodArray<z.ZodObject<{
1061
+ asset: z.ZodString;
1062
+ equity: z.ZodString;
1063
+ availableMargin: z.ZodNullable<z.ZodString>;
1064
+ withdrawable: z.ZodNullable<z.ZodString>;
1065
+ }, z.core.$strip>>;
1066
+ timestamp: z.ZodNumber;
1067
+ accountMode: z.ZodLiteral<"okx:futures">;
1068
+ }, z.core.$strip>, z.ZodObject<{
1069
+ totalEquity: z.ZodNullable<z.ZodString>;
1070
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1071
+ availableMargin: z.ZodNullable<z.ZodString>;
1072
+ poolsShared: z.ZodBoolean;
1073
+ perCollateral: z.ZodArray<z.ZodObject<{
1074
+ asset: z.ZodString;
1075
+ equity: z.ZodString;
1076
+ availableMargin: z.ZodNullable<z.ZodString>;
1077
+ withdrawable: z.ZodNullable<z.ZodString>;
1078
+ }, z.core.$strip>>;
1079
+ timestamp: z.ZodNumber;
1080
+ accountMode: z.ZodLiteral<"okx:multiCurrencyMargin">;
1081
+ }, z.core.$strip>, z.ZodObject<{
1082
+ totalEquity: z.ZodNullable<z.ZodString>;
1083
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1084
+ availableMargin: z.ZodNullable<z.ZodString>;
1085
+ poolsShared: z.ZodBoolean;
1086
+ perCollateral: z.ZodArray<z.ZodObject<{
1087
+ asset: z.ZodString;
1088
+ equity: z.ZodString;
1089
+ availableMargin: z.ZodNullable<z.ZodString>;
1090
+ withdrawable: z.ZodNullable<z.ZodString>;
1091
+ }, z.core.$strip>>;
1092
+ timestamp: z.ZodNumber;
1093
+ accountMode: z.ZodLiteral<"okx:portfolioMargin">;
1094
+ }, z.core.$strip>], "accountMode">;
1095
+ type AccountBalance = z.infer<typeof AccountBalance>;
829
1096
 
830
- declare enum LighterAccountType {
831
- /** @deprecated Use `CLASSIC`; retained as a source-compatible alias. */
832
- BASIC = "classic",
833
- CLASSIC = "classic",
834
- UNIFIED = "unified"
835
- }
1097
+ /**
1098
+ * The primitives every account-balance variant is built from: decimal string formats, the
1099
+ * `accountMode` union tag, and the four core fields every venue fills in every mode.
1100
+ *
1101
+ * This module is the root of the balance schema graph — venue variant files import it, and the
1102
+ * union in `./balance` imports those. Keeping the primitives out of `./balance` is what stops
1103
+ * that graph from becoming a cycle.
1104
+ */
836
1105
 
837
- declare enum OkxAccountType {
838
- SPOT = "OKX_SPOT",
839
- SINGLE_CURRENCY_MARGIN = "OKX_SINGLE",
840
- MULTI_CURRENCY_MARGIN = "OKX_MULTI",
841
- PORTFOLIO_MARGIN = "OKX_PORTFOLIO"
842
- }
1106
+ /** Unsigned decimal, e.g. "1234.56". Never a JS number — precision is load-bearing. */
1107
+ declare const DecimalString: z.ZodString;
1108
+ /** Signed decimal, e.g. "+1234.56" / "-1234.56". BUY positive, SELL negative. */
1109
+ declare const SignedDecimalString: z.ZodString;
1110
+ declare const MarginMode: z.ZodEnum<{
1111
+ isolated: "isolated";
1112
+ cross: "cross";
1113
+ }>;
1114
+ type MarginMode = z.infer<typeof MarginMode>;
1115
+ declare const Side: z.ZodEnum<{
1116
+ BUY: "BUY";
1117
+ SELL: "SELL";
1118
+ }>;
1119
+ type Side = z.infer<typeof Side>;
1120
+ /**
1121
+ * The union tag: venue AND mode in one literal, so narrowing needs no separate venue check.
1122
+ * `hyperliquid:standard` covers the API's `default` and `disabled` — verified identical.
1123
+ */
1124
+ declare const AccountMode: z.ZodEnum<{
1125
+ "hyperliquid:standard": "hyperliquid:standard";
1126
+ "hyperliquid:unified": "hyperliquid:unified";
1127
+ "hyperliquid:portfolioMargin": "hyperliquid:portfolioMargin";
1128
+ "lighter:classic": "lighter:classic";
1129
+ "lighter:unified": "lighter:unified";
1130
+ "binance:singleAsset": "binance:singleAsset";
1131
+ "binance:multiAsset": "binance:multiAsset";
1132
+ "bybit:isolatedMargin": "bybit:isolatedMargin";
1133
+ "bybit:regularMargin": "bybit:regularMargin";
1134
+ "bybit:portfolioMargin": "bybit:portfolioMargin";
1135
+ "okx:spot": "okx:spot";
1136
+ "okx:futures": "okx:futures";
1137
+ "okx:multiCurrencyMargin": "okx:multiCurrencyMargin";
1138
+ "okx:portfolioMargin": "okx:portfolioMargin";
1139
+ }>;
1140
+ type AccountMode = z.infer<typeof AccountMode>;
1141
+ /** One collateral pool's own figures, in THAT asset's own units. Never converted. */
1142
+ declare const CollateralBalance: z.ZodObject<{
1143
+ asset: z.ZodString;
1144
+ equity: z.ZodString;
1145
+ availableMargin: z.ZodNullable<z.ZodString>;
1146
+ withdrawable: z.ZodNullable<z.ZodString>;
1147
+ }, z.core.$strip>;
1148
+ type CollateralBalance = z.infer<typeof CollateralBalance>;
1149
+ /**
1150
+ * Filled by every venue in every mode: the USD scalars a consumer reads without narrowing, plus the
1151
+ * per-collateral rows that say what is actually spendable in each currency.
1152
+ *
1153
+ * The rows are per-asset by construction, so the only USD figures in a variant are the scalars —
1154
+ * which is what makes mixing units impossible rather than merely discouraged.
1155
+ */
1156
+ declare const AccountBalanceCore: z.ZodObject<{
1157
+ totalEquity: z.ZodNullable<z.ZodString>;
1158
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1159
+ availableMargin: z.ZodNullable<z.ZodString>;
1160
+ poolsShared: z.ZodBoolean;
1161
+ perCollateral: z.ZodArray<z.ZodObject<{
1162
+ asset: z.ZodString;
1163
+ equity: z.ZodString;
1164
+ availableMargin: z.ZodNullable<z.ZodString>;
1165
+ withdrawable: z.ZodNullable<z.ZodString>;
1166
+ }, z.core.$strip>>;
1167
+ timestamp: z.ZodNumber;
1168
+ }, z.core.$strip>;
1169
+ /** Human-readable name for each mode, as the venue's own account page labels it. */
1170
+ declare const ACCOUNT_MODE_LABELS: Record<AccountMode, string>;
843
1171
 
844
- type MarginMode = 'cross' | 'isolated';
845
- type AccountType = BinanceAccountType | BybitAccountType | HyperliquidAccountType | LighterAccountType | OkxAccountType;
846
- declare const ACCOUNT_TYPE_LABELS: Record<AccountType, string>;
847
- interface AssetBalance {
1172
+ /**
1173
+ * Mirrors the shipped basket model (`BasketItem` in the frontend): unsigned `weight`
1174
+ * with `side` alongside, weights summing to 100. Sizing ignores `side` — margin
1175
+ * requirement does not depend on direction — and normalises by Σweight.
1176
+ */
1177
+ declare const BasketLeg: z.ZodObject<{
1178
+ asset: z.ZodString;
1179
+ marginMode: z.ZodEnum<{
1180
+ isolated: "isolated";
1181
+ cross: "cross";
1182
+ }>;
1183
+ leverage: z.ZodString;
1184
+ side: z.ZodEnum<{
1185
+ BUY: "BUY";
1186
+ SELL: "SELL";
1187
+ }>;
1188
+ weight: z.ZodNumber;
1189
+ }, z.core.$strip>;
1190
+ type BasketLeg = z.infer<typeof BasketLeg>;
1191
+ declare const MaxBasketSizeInput: z.ZodObject<{
1192
+ legs: z.ZodArray<z.ZodObject<{
1193
+ asset: z.ZodString;
1194
+ marginMode: z.ZodEnum<{
1195
+ isolated: "isolated";
1196
+ cross: "cross";
1197
+ }>;
1198
+ leverage: z.ZodString;
1199
+ side: z.ZodEnum<{
1200
+ BUY: "BUY";
1201
+ SELL: "SELL";
1202
+ }>;
1203
+ weight: z.ZodNumber;
1204
+ }, z.core.$strip>>;
1205
+ }, z.core.$strip>;
1206
+ type MaxBasketSizeInput = z.infer<typeof MaxBasketSizeInput>;
1207
+ declare const MaxBasketSizeResult: z.ZodObject<{
1208
+ maxNotional: z.ZodNullable<z.ZodString>;
1209
+ legs: z.ZodArray<z.ZodObject<{
1210
+ asset: z.ZodString;
1211
+ notional: z.ZodString;
1212
+ marginRequired: z.ZodString;
1213
+ }, z.core.$strip>>;
1214
+ bindingConstraint: z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
1215
+ kind: z.ZodLiteral<"cross-pool">;
1216
+ pool: z.ZodString;
1217
+ }, z.core.$strip>, z.ZodObject<{
1218
+ kind: z.ZodLiteral<"isolated-leg">;
1219
+ asset: z.ZodString;
1220
+ }, z.core.$strip>], "kind">>;
1221
+ }, z.core.$strip>;
1222
+ type MaxBasketSizeResult = z.infer<typeof MaxBasketSizeResult>;
1223
+ /**
1224
+ * One basket leg with its leverage already capped by the venue adapter.
1225
+ *
1226
+ * `marginMode` does not enter the maths: an isolated leg gets dedicated margin, but that margin is
1227
+ * still transferred out of the same free balance, so it draws on the same budget as a cross leg.
1228
+ * It is carried so the caller passes its basket through unchanged.
1229
+ */
1230
+ type CappedBasketLeg = {
848
1231
  asset: string;
849
- free: string;
850
- used: string;
851
- total: string;
852
- walletBalance: string;
853
- usdValue: string;
854
- }
855
- interface AccountBalance {
856
- accountType: AccountType;
857
- accountTier?: string;
858
- free: Record<string, string>;
859
- used: Record<string, string>;
860
- total: Record<string, string>;
861
- availableToTrade: string;
862
- totalEquity: string;
863
- walletBalance: string;
864
- unrealizedPnl: string;
865
- initialMarginUsed: string;
866
- maintenanceMargin: string;
867
- marginRatio: string;
868
- assets: AssetBalance[];
869
- timestamp: number;
870
- }
1232
+ /**
1233
+ * The collateral pool this leg's margin comes out of, as the venue adapter resolves it — a CEX
1234
+ * from the symbol's settlement currency, Hyperliquid from the dex's collateral token. Legs in
1235
+ * different pools cannot fund each other.
1236
+ */
1237
+ pool: string;
1238
+ /** Unsigned, in the caller's own scale — `computeMaxBasketSize` normalises by Σweight. */
1239
+ weight: number;
1240
+ effectiveLeverage: string;
1241
+ marginMode: MarginMode;
1242
+ /** This leg's own venue ceiling in USD notional; `null` when the venue names no cap. */
1243
+ maxNotional: string | null;
1244
+ };
1245
+ /** The margin budget behind each collateral pool, plus the legs those pools must fund at once. */
1246
+ type MaxBasketSizeCalculation = {
1247
+ /**
1248
+ * Pool id to that pool's USD-equivalent budget. A pool whose collateral is not ~1 USD supplies no
1249
+ * entry until someone adds a price source, and a leg drawing on a missing entry voids the answer.
1250
+ */
1251
+ budgets: Record<string, string>;
1252
+ legs: CappedBasketLeg[];
1253
+ };
1254
+ /**
1255
+ * The largest gross notional this basket can reach while every leg stays funded at its weight.
1256
+ *
1257
+ * Returns a null size when a pool behind the basket cannot be valued — never a zero, which a
1258
+ * consumer would read as "no collateral" rather than "not known". A basket whose own figures cannot
1259
+ * be sized (see `isSizeableInput`) gets the same null, so this call never throws and never returns a
1260
+ * result that fails `MaxBasketSizeResult`.
1261
+ */
1262
+ declare function computeMaxBasketSize(input: MaxBasketSizeCalculation): MaxBasketSizeResult;
1263
+
871
1264
  interface AccountPosition {
872
1265
  symbol: string;
873
1266
  side: 'long' | 'short' | 'both';
@@ -924,12 +1317,16 @@ interface AccountTracker {
924
1317
  disconnect(): Promise<void>;
925
1318
  snapshot(): Promise<AccountSnapshot>;
926
1319
  snapshotAssets(coins: string[]): Promise<AssetSnapshotMap>;
1320
+ /** Sizes a basket over REST alone, so it stays usable without a running socket. */
1321
+ snapshotMaxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
927
1322
  readonly isConnected: boolean;
928
1323
  readonly isInitialized: boolean;
929
1324
  readonly hasListeners: boolean;
930
1325
  trackBalance(cb: BalanceCallback): () => void;
931
1326
  trackPosition(cb: PositionsCallback): () => void;
932
1327
  trackAsset(asset: string, cb: TrackedAssetCallback): () => void;
1328
+ /** Async: a basket may name assets this tracker has not subscribed to yet. */
1329
+ maxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
933
1330
  getBalance(): AccountBalance | null;
934
1331
  getPositions(): AccountPosition[];
935
1332
  getTrackedAsset(coin: string): TrackedAssetInfo | null;
@@ -970,6 +1367,9 @@ declare abstract class BaseAccountTracker implements AccountTracker {
970
1367
  private broadcast;
971
1368
  abstract start(): Promise<void>;
972
1369
  abstract snapshot(): Promise<AccountSnapshot>;
1370
+ /** Every venue implements both sizing paths — the live one and the REST one. */
1371
+ abstract maxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1372
+ abstract snapshotMaxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
973
1373
  protected abstract fetchAssetSnapshots(coins: string[]): Promise<AssetSnapshotMap>;
974
1374
  abstract get isConnected(): boolean;
975
1375
  protected abstract ensureAssetTracked(asset: string): void;
@@ -977,6 +1377,51 @@ declare abstract class BaseAccountTracker implements AccountTracker {
977
1377
  protected abstract cleanupConnections(): void | Promise<void>;
978
1378
  }
979
1379
 
1380
+ /**
1381
+ * Binance's two account-mode balance variants.
1382
+ *
1383
+ * Core fields only, with `null` wherever a figure cannot be derived. What differs by mode is not the
1384
+ * shape but when the core scalars go null, which follows each mode's collateral-pool model. One
1385
+ * `z.literal` per mode, because `z.discriminatedUnion` needs a single literal tag per member rather
1386
+ * than a `z.enum` shorthand.
1387
+ */
1388
+
1389
+ /**
1390
+ * Single-asset mode runs one collateral pool per settlement currency, and Binance's account-level
1391
+ * figures cover USDT only there. Once a second pool is funded no account total exists to report, so
1392
+ * `totalEquity` goes null and `perCollateral` is the whole answer.
1393
+ */
1394
+ declare const BinanceSingleAssetBalance: z.ZodObject<{
1395
+ totalEquity: z.ZodNullable<z.ZodString>;
1396
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1397
+ availableMargin: z.ZodNullable<z.ZodString>;
1398
+ poolsShared: z.ZodBoolean;
1399
+ perCollateral: z.ZodArray<z.ZodObject<{
1400
+ asset: z.ZodString;
1401
+ equity: z.ZodString;
1402
+ availableMargin: z.ZodNullable<z.ZodString>;
1403
+ withdrawable: z.ZodNullable<z.ZodString>;
1404
+ }, z.core.$strip>>;
1405
+ timestamp: z.ZodNumber;
1406
+ accountMode: z.ZodLiteral<"binance:singleAsset">;
1407
+ }, z.core.$strip>;
1408
+ type BinanceSingleAssetBalance = z.infer<typeof BinanceSingleAssetBalance>;
1409
+ declare const BinanceMultiAssetBalance: z.ZodObject<{
1410
+ totalEquity: z.ZodNullable<z.ZodString>;
1411
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1412
+ availableMargin: z.ZodNullable<z.ZodString>;
1413
+ poolsShared: z.ZodBoolean;
1414
+ perCollateral: z.ZodArray<z.ZodObject<{
1415
+ asset: z.ZodString;
1416
+ equity: z.ZodString;
1417
+ availableMargin: z.ZodNullable<z.ZodString>;
1418
+ withdrawable: z.ZodNullable<z.ZodString>;
1419
+ }, z.core.$strip>>;
1420
+ timestamp: z.ZodNumber;
1421
+ accountMode: z.ZodLiteral<"binance:multiAsset">;
1422
+ }, z.core.$strip>;
1423
+ type BinanceMultiAssetBalance = z.infer<typeof BinanceMultiAssetBalance>;
1424
+
980
1425
  type BinanceAccountTrackerConfig = AccountTrackerConfig & {
981
1426
  credentials: SupportedCredentials;
982
1427
  listenKeyRefreshMs?: number;
@@ -993,10 +1438,19 @@ declare class BinanceAccountTracker extends BaseAccountTracker {
993
1438
  private listenKeyTimer;
994
1439
  private pollTimer;
995
1440
  private currentPositions;
996
- private accountType;
1441
+ private accountMode;
997
1442
  private trackedAssetInfos;
998
1443
  constructor(config: BinanceAccountTrackerConfig);
999
1444
  snapshot(): Promise<AccountSnapshot>;
1445
+ /**
1446
+ * Binance sizes no basket in v1: the SDK reads no per-asset max leverage from it, and sizing off
1447
+ * `availableMargin` alone would ship a number no venue field backs. The missing input is
1448
+ * `/fapi/v2/positionRisk` → `maxNotionalValue` (the account's own per-symbol ceiling), with
1449
+ * `/fapi/v1/leverageBracket` behind it for the tier a larger size would fall into.
1450
+ */
1451
+ maxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1452
+ /** The REST path answers the same way — there is no size either way, not a slower one. */
1453
+ snapshotMaxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1000
1454
  /**
1001
1455
  * Stateless one-shot per-asset settings read: `positionRisk` with no symbol returns leverage +
1002
1456
  * margin type for every symbol (positioned or not), so any number of assets costs one call.
@@ -1017,6 +1471,64 @@ declare class BinanceAccountTracker extends BaseAccountTracker {
1017
1471
  private refreshAllPositions;
1018
1472
  }
1019
1473
 
1474
+ /**
1475
+ * Bybit's three account-mode balance variants.
1476
+ *
1477
+ * Core fields only — Bybit carries no per-mode detail yet, with `null` wherever a figure cannot
1478
+ * be derived. All three are unified-account modes sharing one margin pool, so the core scalars are
1479
+ * the whole answer and `perCollateral` only breaks them down — where Binance single-asset and OKX
1480
+ * Spot/Futures have to null those scalars once a second pool is funded. The mode still tags the
1481
+ * variant so detail can be added later without another breaking change. One `z.literal` per mode,
1482
+ * because `z.discriminatedUnion` needs a single literal tag per member rather than a `z.enum`
1483
+ * shorthand.
1484
+ */
1485
+
1486
+ declare const BybitIsolatedMarginBalance: z.ZodObject<{
1487
+ totalEquity: z.ZodNullable<z.ZodString>;
1488
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1489
+ availableMargin: z.ZodNullable<z.ZodString>;
1490
+ poolsShared: z.ZodBoolean;
1491
+ perCollateral: z.ZodArray<z.ZodObject<{
1492
+ asset: z.ZodString;
1493
+ equity: z.ZodString;
1494
+ availableMargin: z.ZodNullable<z.ZodString>;
1495
+ withdrawable: z.ZodNullable<z.ZodString>;
1496
+ }, z.core.$strip>>;
1497
+ timestamp: z.ZodNumber;
1498
+ accountMode: z.ZodLiteral<"bybit:isolatedMargin">;
1499
+ }, z.core.$strip>;
1500
+ type BybitIsolatedMarginBalance = z.infer<typeof BybitIsolatedMarginBalance>;
1501
+ declare const BybitRegularMarginBalance: z.ZodObject<{
1502
+ totalEquity: z.ZodNullable<z.ZodString>;
1503
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1504
+ availableMargin: z.ZodNullable<z.ZodString>;
1505
+ poolsShared: z.ZodBoolean;
1506
+ perCollateral: z.ZodArray<z.ZodObject<{
1507
+ asset: z.ZodString;
1508
+ equity: z.ZodString;
1509
+ availableMargin: z.ZodNullable<z.ZodString>;
1510
+ withdrawable: z.ZodNullable<z.ZodString>;
1511
+ }, z.core.$strip>>;
1512
+ timestamp: z.ZodNumber;
1513
+ accountMode: z.ZodLiteral<"bybit:regularMargin">;
1514
+ }, z.core.$strip>;
1515
+ type BybitRegularMarginBalance = z.infer<typeof BybitRegularMarginBalance>;
1516
+ declare const BybitPortfolioMarginBalance: z.ZodObject<{
1517
+ totalEquity: z.ZodNullable<z.ZodString>;
1518
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1519
+ availableMargin: z.ZodNullable<z.ZodString>;
1520
+ poolsShared: z.ZodBoolean;
1521
+ perCollateral: z.ZodArray<z.ZodObject<{
1522
+ asset: z.ZodString;
1523
+ equity: z.ZodString;
1524
+ availableMargin: z.ZodNullable<z.ZodString>;
1525
+ withdrawable: z.ZodNullable<z.ZodString>;
1526
+ }, z.core.$strip>>;
1527
+ timestamp: z.ZodNumber;
1528
+ accountMode: z.ZodLiteral<"bybit:portfolioMargin">;
1529
+ }, z.core.$strip>;
1530
+ type BybitPortfolioMarginBalance = z.infer<typeof BybitPortfolioMarginBalance>;
1531
+
1020
1532
  type BybitAccountTrackerConfig = AccountTrackerConfig & {
1021
1533
  credentials: SupportedCredentials;
1022
1534
  };
@@ -1027,10 +1539,25 @@ declare class BybitAccountTracker extends BaseAccountTracker {
1027
1539
  private openListenerId;
1028
1540
  private rawCoins;
1029
1541
  private lastWallet;
1030
- private accountType;
1542
+ private accountMode;
1543
+ private withdrawable;
1544
+ private lastWithdrawableReadAt;
1545
+ private withdrawableRefreshTimer;
1546
+ private withdrawableReadInFlight;
1547
+ private withdrawableReadPending;
1548
+ private lifecycleVersion;
1031
1549
  private trackedAssetInfos;
1032
1550
  constructor(config: BybitAccountTrackerConfig);
1033
1551
  snapshot(): Promise<AccountSnapshot>;
1552
+ /**
1553
+ * Bybit sizes no basket in v1: the SDK reads no per-asset max leverage from it, and sizing off
1554
+ * `availableMargin` alone would ship a number no venue field backs. The missing input is
1555
+ * `/v5/position/list` → `leverage` (the account's own per-symbol setting), with
1556
+ * `/v5/market/risk-limit` → `maxLeverage` behind it for the tier a larger size would fall into.
1557
+ */
1558
+ maxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1559
+ /** The REST path answers the same way — there is no size either way, not a slower one. */
1560
+ snapshotMaxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1034
1561
  /**
1035
1562
  * Stateless one-shot per-asset settings read. Bybit has no batch leverage endpoint, but
1036
1563
  * `position/list` by settleCoin returns every open position (with leverage) in one call, so
@@ -1044,15 +1571,232 @@ declare class BybitAccountTracker extends BaseAccountTracker {
1044
1571
  protected cleanupConnections(): Promise<void>;
1045
1572
  get isConnected(): boolean;
1046
1573
  private fetchSnapshotData;
1574
+ /**
1575
+ * Read how much can leave the trading account, for the coins the wallet payload lists.
1576
+ *
1577
+ * Best-effort on purpose: this is a second signed call that a read-only key may be refused, and
1578
+ * a refusal must cost the caller a `null` field rather than the whole balance.
1579
+ *
1580
+ * A refusal is reported as `failed` rather than as an empty answer, because only the caller knows
1581
+ * what to do with it: the snapshot path has no earlier figure and reports nothing known, while
1582
+ * the live path keeps the figure its last successful read published.
1583
+ */
1584
+ private readWithdrawable;
1047
1585
  protected ensureAssetTracked(coin: string): void;
1048
1586
  protected releaseAsset(coin: string): void;
1049
1587
  private subscribe;
1050
1588
  private handleInitialData;
1051
1589
  private routeRaw;
1052
1590
  private handleMessage;
1591
+ /**
1592
+ * Fold one wallet push into the whole account, then publish what that whole account says.
1593
+ *
1594
+ * Bybit pushes only what moved: an update names the coins whose figures changed and leaves every
1595
+ * other coin out. So the coin list is taken from the per-coin merge rather than from the push,
1596
+ * and everything read off it sees every coin the account holds — the published rows, and the
1597
+ * coin list the next transferable-amount read asks about. A push naming one of two funded coins
1598
+ * would otherwise drop the other's row and let its per-coin maximum answer for the account.
1599
+ *
1600
+ * The account-level figures inherit on `??`, never on `||`, because ABSENT and BLANK say
1601
+ * opposite things here. A field this push left out was not restated and its previous value still
1602
+ * stands; a field present as `''` is Bybit withdrawing the figure, which it does for
1603
+ * `totalAvailableBalance` among others. Inheriting a blank would republish a number the venue no
1604
+ * longer states, and it would do it BEHIND `decimalOrNull`, whose whole job is to publish a blank
1605
+ * as `null`: overwriting the blank here means that guard never sees it.
1606
+ */
1053
1607
  private handleWalletUpdate;
1608
+ /** Publish the balance built from the newest wallet payload and the cached transferable amounts. */
1609
+ private emitBalance;
1610
+ /**
1611
+ * Queue the next transferable-amount read, no sooner than the venue's own rate limit allows.
1612
+ *
1613
+ * Nothing waits on it: the wallet update is already published from the cached figure. One queued
1614
+ * read covers every wallet tick that lands before it runs, which is what stops a burst of ticks
1615
+ * becoming a burst of signed calls — and because a tick always leaves a read queued or marked
1616
+ * pending, a moved figure is still picked up within one interval rather than dropped.
1617
+ */
1618
+ private refreshWithdrawableSoon;
1619
+ /**
1620
+ * Hold one transferable-amount read open at a time, and queue the ticks that arrive during it.
1621
+ *
1622
+ * Serialising the reads is what keeps the published figure the newest one: two overlapping reads
1623
+ * can come back in either order, and the older answer landing last would publish an amount the
1624
+ * venue has already moved past.
1625
+ */
1626
+ private refreshWithdrawableBalance;
1627
+ /**
1628
+ * Re-read the transferable amount behind the live path, then re-publish only if it moved.
1629
+ *
1630
+ * Fire-and-forget: the wallet update is already published with the previous figure, so a slow
1631
+ * read delays the correction instead of blocking the balance. A read that FAILS changes nothing
1632
+ * at all — the last good figure stays published, because a refusal says Bybit did not answer,
1633
+ * not that the account has nothing to transfer. Both the read and the re-publish take the NEWEST
1634
+ * wallet rather than the one that queued them, so a wallet update that landed meanwhile is
1635
+ * neither missed nor rolled back.
1636
+ */
1637
+ private performWithdrawableRefresh;
1054
1638
  }
1055
1639
 
1640
+ /**
1641
+ * Hyperliquid's three account-mode balance variants.
1642
+ *
1643
+ * Hyperliquid has the most divergent risk model of the five venues, so each mode carries its own
1644
+ * figures rather than sharing one flat shape. HIP-3 dexes can share the main dex's collateral
1645
+ * token, so every account-level figure aggregates across dexes and the per-dex list is kept
1646
+ * alongside — the aggregate hides HIP-3 positions entirely.
1647
+ *
1648
+ * Every figure derived from a spot price is nullable, because a spot balance carries only a token
1649
+ * index and no route to USDC reaches every token. The `perCollateral` rows are not:
1650
+ * a row is a quantity of one collateral token in that token's own units, so it keeps working
1651
+ * precisely because it is never converted.
1652
+ */
1653
+
1654
+ declare const HyperliquidDexState: z.ZodObject<{
1655
+ dex: z.ZodString;
1656
+ collateralToken: z.ZodNullable<z.ZodNumber>;
1657
+ accountValue: z.ZodString;
1658
+ crossNotional: z.ZodString;
1659
+ crossMaintenanceMargin: z.ZodString;
1660
+ withdrawable: z.ZodString;
1661
+ }, z.core.$strip>;
1662
+ type HyperliquidDexState = z.infer<typeof HyperliquidDexState>;
1663
+ declare const HyperliquidStandardBalance: z.ZodObject<{
1664
+ totalEquity: z.ZodNullable<z.ZodString>;
1665
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1666
+ availableMargin: z.ZodNullable<z.ZodString>;
1667
+ poolsShared: z.ZodBoolean;
1668
+ perCollateral: z.ZodArray<z.ZodObject<{
1669
+ asset: z.ZodString;
1670
+ equity: z.ZodString;
1671
+ availableMargin: z.ZodNullable<z.ZodString>;
1672
+ withdrawable: z.ZodNullable<z.ZodString>;
1673
+ }, z.core.$strip>>;
1674
+ timestamp: z.ZodNumber;
1675
+ accountMode: z.ZodLiteral<"hyperliquid:standard">;
1676
+ spotEquity: z.ZodNullable<z.ZodString>;
1677
+ perpsEquity: z.ZodString;
1678
+ vaultEquity: z.ZodString;
1679
+ stakedEquity: z.ZodNullable<z.ZodString>;
1680
+ unrealizedPnl: z.ZodString;
1681
+ maintenanceMargin: z.ZodString;
1682
+ crossMarginRatio: z.ZodString;
1683
+ crossAccountLeverage: z.ZodString;
1684
+ perDexState: z.ZodArray<z.ZodObject<{
1685
+ dex: z.ZodString;
1686
+ collateralToken: z.ZodNullable<z.ZodNumber>;
1687
+ accountValue: z.ZodString;
1688
+ crossNotional: z.ZodString;
1689
+ crossMaintenanceMargin: z.ZodString;
1690
+ withdrawable: z.ZodString;
1691
+ }, z.core.$strip>>;
1692
+ }, z.core.$strip>;
1693
+ type HyperliquidStandardBalance = z.infer<typeof HyperliquidStandardBalance>;
1694
+ declare const HyperliquidUnifiedBalance: z.ZodObject<{
1695
+ totalEquity: z.ZodNullable<z.ZodString>;
1696
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1697
+ availableMargin: z.ZodNullable<z.ZodString>;
1698
+ poolsShared: z.ZodBoolean;
1699
+ perCollateral: z.ZodArray<z.ZodObject<{
1700
+ asset: z.ZodString;
1701
+ equity: z.ZodString;
1702
+ availableMargin: z.ZodNullable<z.ZodString>;
1703
+ withdrawable: z.ZodNullable<z.ZodString>;
1704
+ }, z.core.$strip>>;
1705
+ timestamp: z.ZodNumber;
1706
+ accountMode: z.ZodLiteral<"hyperliquid:unified">;
1707
+ tradingEquity: z.ZodNullable<z.ZodString>;
1708
+ vaultEquity: z.ZodString;
1709
+ stakedEquity: z.ZodNullable<z.ZodString>;
1710
+ portfolioValue: z.ZodNullable<z.ZodString>;
1711
+ unrealizedPnl: z.ZodString;
1712
+ perpsMaintenanceMargin: z.ZodString;
1713
+ unifiedAccountRatio: z.ZodString;
1714
+ unifiedAccountLeverage: z.ZodNullable<z.ZodString>;
1715
+ perDexState: z.ZodArray<z.ZodObject<{
1716
+ dex: z.ZodString;
1717
+ collateralToken: z.ZodNullable<z.ZodNumber>;
1718
+ accountValue: z.ZodString;
1719
+ crossNotional: z.ZodString;
1720
+ crossMaintenanceMargin: z.ZodString;
1721
+ withdrawable: z.ZodString;
1722
+ }, z.core.$strip>>;
1723
+ }, z.core.$strip>;
1724
+ type HyperliquidUnifiedBalance = z.infer<typeof HyperliquidUnifiedBalance>;
1725
+ /**
1726
+ * One Portfolio Margin spot holding, as the venue's own balances table states it.
1727
+ *
1728
+ * PM widens the shared `CollateralBalance` in two ways it cannot carry, which is why this variant
1729
+ * declares its own row type rather than reusing it:
1730
+ *
1731
+ * - **the figures are SIGNED.** A borrowed coin holds a negative `total`, and seven of the fourteen
1732
+ * accounts measured hold one — `0x0871deb3…` reports USDC at −957,802.15. An unsigned row cannot
1733
+ * state that at all, which is the defect that made a borrowed pool indistinguishable from an
1734
+ * untouched one.
1735
+ * - **two more columns.** `ltv` and `borrowCapUsed` are per-coin risk weights the venue renders
1736
+ * beside the quantities, and both are PM-only wire fields.
1737
+ *
1738
+ * The row is otherwise the shared shape: same keys, same meanings, so a consumer reading
1739
+ * `perCollateral` without narrowing still reads `asset` / `equity` / `availableMargin` /
1740
+ * `withdrawable` off every venue.
1741
+ */
1742
+ declare const HyperliquidPortfolioMarginCollateral: z.ZodObject<{
1743
+ asset: z.ZodString;
1744
+ equity: z.ZodString;
1745
+ availableMargin: z.ZodNullable<z.ZodString>;
1746
+ withdrawable: z.ZodString;
1747
+ ltv: z.ZodNullable<z.ZodString>;
1748
+ borrowCapUsed: z.ZodNullable<z.ZodString>;
1749
+ }, z.core.$strip>;
1750
+ type HyperliquidPortfolioMarginCollateral = z.infer<typeof HyperliquidPortfolioMarginCollateral>;
1751
+ declare const HyperliquidPortfolioMarginBalance: z.ZodObject<{
1752
+ totalEquity: z.ZodNullable<z.ZodString>;
1753
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1754
+ availableMargin: z.ZodNullable<z.ZodString>;
1755
+ poolsShared: z.ZodBoolean;
1756
+ timestamp: z.ZodNumber;
1757
+ accountMode: z.ZodLiteral<"hyperliquid:portfolioMargin">;
1758
+ perCollateral: z.ZodArray<z.ZodObject<{
1759
+ asset: z.ZodString;
1760
+ equity: z.ZodString;
1761
+ availableMargin: z.ZodNullable<z.ZodString>;
1762
+ withdrawable: z.ZodString;
1763
+ ltv: z.ZodNullable<z.ZodString>;
1764
+ borrowCapUsed: z.ZodNullable<z.ZodString>;
1765
+ }, z.core.$strip>>;
1766
+ tradingEquity: z.ZodNullable<z.ZodString>;
1767
+ vaultEquity: z.ZodString;
1768
+ stakedEquity: z.ZodNullable<z.ZodString>;
1769
+ portfolioValue: z.ZodNullable<z.ZodString>;
1770
+ unrealizedPnl: z.ZodString;
1771
+ perpsMaintenanceMargin: z.ZodString;
1772
+ portfolioMarginRatio: z.ZodNullable<z.ZodString>;
1773
+ portfolioAccountLeverage: z.ZodNullable<z.ZodString>;
1774
+ borrowCapUsed: z.ZodNullable<z.ZodString>;
1775
+ perDexState: z.ZodArray<z.ZodObject<{
1776
+ dex: z.ZodString;
1777
+ collateralToken: z.ZodNullable<z.ZodNumber>;
1778
+ accountValue: z.ZodString;
1779
+ crossNotional: z.ZodString;
1780
+ crossMaintenanceMargin: z.ZodString;
1781
+ withdrawable: z.ZodString;
1782
+ }, z.core.$strip>>;
1783
+ }, z.core.$strip>;
1784
+ type HyperliquidPortfolioMarginBalance = z.infer<typeof HyperliquidPortfolioMarginBalance>;
1785
+
1786
+ /**
1787
+ * Hyperliquid's two read paths: the live WebSocket tracker and the one-shot REST snapshot.
1788
+ *
1789
+ * Both fill the same `HyperliquidBalanceContext` and hand it to the same mapper, which is what
1790
+ * makes "same account state, same balance" true across two different transports. The tracker takes
1791
+ * every CHANGING value off a channel — `allDexsClearinghouseState`, `spotState`, `webData3`,
1792
+ * `allMids`, `activeAssetData` — including vault equity, which rides `webData3`. Its only REST
1793
+ * reads happen at `start()`: the static dex metadata, and the staking position, which the venue
1794
+ * publishes on no channel at all.
1795
+ *
1796
+ * The snapshot has no channels, so it reads the same figures over REST — vault equity from
1797
+ * `userVaultEquities`, which was measured equal to the `webData3` figure on ten live accounts.
1798
+ */
1799
+
1056
1800
  type HyperliquidAccountTrackerConfig = AccountTrackerConfig & {
1057
1801
  account: TradeAccount;
1058
1802
  credentials: SupportedCredentials;
@@ -1066,17 +1810,33 @@ declare class HyperliquidAccountTracker extends BaseAccountTracker {
1066
1810
  private subscribedCoins;
1067
1811
  private lastSummary;
1068
1812
  private spotCoinBalances;
1813
+ /** Portfolio Margin's own risk scalars, which ride the same `spotState` frame the balances do. */
1814
+ private portfolioMarginRatio;
1815
+ private borrowRatioByToken;
1069
1816
  private hasSpotState;
1070
1817
  private hasClearinghouseState;
1071
1818
  private accountType;
1072
- private collateralTokenMap;
1819
+ private spotMeta;
1820
+ private collateralTokenNames;
1821
+ private prices;
1822
+ private hasWebData3;
1823
+ private vaultEquity;
1824
+ private stakedHype;
1073
1825
  private dexToMeta;
1074
1826
  private trackedAssetData;
1075
1827
  constructor(config: HyperliquidAccountTrackerConfig);
1076
1828
  /**
1077
- * Reads account mode, collateral balances, per-DEX state, and positions without opening a socket.
1829
+ * Reads account mode, spot prices, collateral balances, per-DEX state, staking, vaults and
1830
+ * per-asset headroom without opening a socket.
1078
1831
  */
1079
1832
  snapshot(): Promise<AccountSnapshot>;
1833
+ /** Sizes a basket over REST alone, so the call stays usable without a running socket. */
1834
+ snapshotMaxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1835
+ /**
1836
+ * Sizes a basket against live state, fetching per-asset data for any leg the socket has not
1837
+ * subscribed to — which is exactly why the call is asynchronous on both paths.
1838
+ */
1839
+ maxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1080
1840
  /**
1081
1841
  * Stateless one-shot per-asset settings read via the `activeAssetData` info request (the REST
1082
1842
  * twin of the live WS subscription), one request per coin fired in parallel — the info endpoint
@@ -1100,41 +1860,120 @@ declare class HyperliquidAccountTracker extends BaseAccountTracker {
1100
1860
  private routeRaw;
1101
1861
  private handle;
1102
1862
  private loadPerpMetas;
1863
+ /** Loads the dex metadata a sizing call needs, reusing whatever `start()` already cached. */
1864
+ private loadPerpMetasOnce;
1865
+ /** Loads the token names a pool id is spelled with, reusing whatever `start()` already cached. */
1866
+ private loadCollateralTokenNamesOnce;
1867
+ /** Reads `activeAssetData` for each coin in parallel, dropping the coins the venue refuses. */
1868
+ private fetchActiveAssetEvents;
1869
+ /** The asset set `availableMargin` is the floor over: the locked coins plus every tracked one. */
1870
+ private coinsForAvailableMargin;
1103
1871
  private onClearinghouseState;
1104
1872
  private onActiveAssetData;
1873
+ private onAllMids;
1105
1874
  private onWebData3;
1106
1875
  private onSpotState;
1876
+ /**
1877
+ * Builds the live balance once every channel that feeds it has reported, or `undefined` until then.
1878
+ *
1879
+ * All four are required. Every mode now values the spot wallet, so a balance emitted before
1880
+ * `spotState` and `allMids` would report equity missing its whole spot half; and one emitted
1881
+ * before `webData3` would report vault equity as zero on an account that holds millions in a
1882
+ * vault. Partial equity is worse than no equity, so nothing is published early.
1883
+ */
1107
1884
  private buildBalance;
1885
+ /** The floor of `availableToTrade` over the tracked set; `null` when nothing is tracked. */
1886
+ private availableMargin;
1108
1887
  /** Clears both sides of the live balance join and the public account cache without emitting. */
1109
1888
  private resetLiveAccountState;
1110
1889
  private buildTrackedAssetsMap;
1111
1890
  }
1112
1891
 
1892
+ /**
1893
+ * Lighter's two account-mode balance variants.
1894
+ *
1895
+ * Classic is Unified minus `portfolioMargin`, so both modes share one field block.
1896
+ */
1897
+
1898
+ declare const LighterClassicBalance: z.ZodObject<{
1899
+ totalEquity: z.ZodNullable<z.ZodString>;
1900
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1901
+ availableMargin: z.ZodNullable<z.ZodString>;
1902
+ poolsShared: z.ZodBoolean;
1903
+ perCollateral: z.ZodArray<z.ZodObject<{
1904
+ asset: z.ZodString;
1905
+ equity: z.ZodString;
1906
+ availableMargin: z.ZodNullable<z.ZodString>;
1907
+ withdrawable: z.ZodNullable<z.ZodString>;
1908
+ }, z.core.$strip>>;
1909
+ timestamp: z.ZodNumber;
1910
+ tradingEquity: z.ZodNullable<z.ZodString>;
1911
+ perpetualsEquity: z.ZodNullable<z.ZodString>;
1912
+ spotEquity: z.ZodNullable<z.ZodString>;
1913
+ publicPoolsEquity: z.ZodNullable<z.ZodString>;
1914
+ stakingEquity: z.ZodNullable<z.ZodString>;
1915
+ creditedInLit: z.ZodNullable<z.ZodString>;
1916
+ unrealizedPnl: z.ZodNullable<z.ZodString>;
1917
+ crossLeverage: z.ZodNullable<z.ZodString>;
1918
+ maintenanceMargin: z.ZodNullable<z.ZodString>;
1919
+ crossMarginUsage: z.ZodNullable<z.ZodString>;
1920
+ crossMarginRatio: z.ZodNullable<z.ZodString>;
1921
+ accountMode: z.ZodLiteral<"lighter:classic">;
1922
+ }, z.core.$strip>;
1923
+ type LighterClassicBalance = z.infer<typeof LighterClassicBalance>;
1924
+ declare const LighterUnifiedBalance: z.ZodObject<{
1925
+ totalEquity: z.ZodNullable<z.ZodString>;
1926
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
1927
+ availableMargin: z.ZodNullable<z.ZodString>;
1928
+ poolsShared: z.ZodBoolean;
1929
+ perCollateral: z.ZodArray<z.ZodObject<{
1930
+ asset: z.ZodString;
1931
+ equity: z.ZodString;
1932
+ availableMargin: z.ZodNullable<z.ZodString>;
1933
+ withdrawable: z.ZodNullable<z.ZodString>;
1934
+ }, z.core.$strip>>;
1935
+ timestamp: z.ZodNumber;
1936
+ portfolioMargin: z.ZodNullable<z.ZodString>;
1937
+ tradingEquity: z.ZodNullable<z.ZodString>;
1938
+ perpetualsEquity: z.ZodNullable<z.ZodString>;
1939
+ spotEquity: z.ZodNullable<z.ZodString>;
1940
+ publicPoolsEquity: z.ZodNullable<z.ZodString>;
1941
+ stakingEquity: z.ZodNullable<z.ZodString>;
1942
+ creditedInLit: z.ZodNullable<z.ZodString>;
1943
+ unrealizedPnl: z.ZodNullable<z.ZodString>;
1944
+ crossLeverage: z.ZodNullable<z.ZodString>;
1945
+ maintenanceMargin: z.ZodNullable<z.ZodString>;
1946
+ crossMarginUsage: z.ZodNullable<z.ZodString>;
1947
+ crossMarginRatio: z.ZodNullable<z.ZodString>;
1948
+ accountMode: z.ZodLiteral<"lighter:unified">;
1949
+ }, z.core.$strip>;
1950
+ type LighterUnifiedBalance = z.infer<typeof LighterUnifiedBalance>;
1951
+
1113
1952
  type LighterAccountTrackerConfig = AccountTrackerConfig & {
1114
1953
  account: TradeAccount;
1115
1954
  credentials: SupportedCredentials;
1116
- sdk?: PearSDK;
1117
1955
  };
1118
1956
  declare class LighterAccountTracker extends BaseAccountTracker {
1119
1957
  private readonly accountIdentifier;
1120
- private readonly sdk;
1121
1958
  private ws;
1122
1959
  private messageListenerId;
1123
1960
  private openListenerId;
1124
- private pollTimer;
1125
- private pollInFlight;
1961
+ private positionPollTimer;
1962
+ private positionPollInFlight;
1963
+ private marketRefreshTimer;
1964
+ private marketRefreshInFlight;
1126
1965
  private startInFlight;
1127
1966
  private lifecycleVersion;
1128
1967
  private stateVersion;
1129
1968
  private assetsByAssetId;
1130
- private rawPositionsByMarketId;
1969
+ private wsPositionsByMarketId;
1131
1970
  private marketsByMarketId;
1132
- private summary;
1133
- private accountTier;
1134
- private lastAccountTierFetchAt;
1971
+ private marketsBySymbol;
1972
+ private spotPriceBySymbol;
1973
+ private stats;
1135
1974
  private hasAssets;
1136
1975
  constructor(config: LighterAccountTrackerConfig);
1137
- /** Stateless one-shot account read; tier enrichment is optional and requires SDK-backed auth. */
1976
+ /** Stateless one-shot account read — public endpoints only, and no websocket. */
1138
1977
  snapshot(): Promise<AccountSnapshot>;
1139
1978
  /**
1140
1979
  * Stateless one-shot per-asset settings read, keyed by market id (the same identifier the live
@@ -1143,6 +1982,10 @@ declare class LighterAccountTracker extends BaseAccountTracker {
1143
1982
  * supplies the exchange default for markets absent from the account payload.
1144
1983
  */
1145
1984
  protected fetchAssetSnapshots(marketIds: string[]): Promise<AssetSnapshotMap>;
1985
+ /** Sizes a basket against the live account state, falling back to REST before the socket is up. */
1986
+ maxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1987
+ /** Sizes a basket over REST alone, so the answer needs no running socket. */
1988
+ snapshotMaxBasketSize(input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1146
1989
  start(): Promise<void>;
1147
1990
  /** Creates one tracker lifecycle while repeated callers share the same pending start. */
1148
1991
  private performStart;
@@ -1156,15 +1999,148 @@ declare class LighterAccountTracker extends BaseAccountTracker {
1156
1999
  private routeRaw;
1157
2000
  private handle;
1158
2001
  private onAccountAll;
2002
+ /** Keeps both published scopes: cross drives every "Cross" field, total drives the account ones. */
1159
2003
  private onUserStats;
2004
+ /**
2005
+ * The account's balance, built from websocket state alone.
2006
+ *
2007
+ * Every input is websocket-sourced — `user_stats` for the two account scopes, `account_all` for
2008
+ * the assets and positions — except the market context, which is venue-wide metadata this
2009
+ * tracker re-reads on its own timer. This is the ONLY producer of the value `getBalance()`
2010
+ * returns while the tracker is running; `snapshot()` is the separate REST path and never writes
2011
+ * here.
2012
+ *
2013
+ * Null until both channels have delivered a frame, because a balance built from one of them
2014
+ * would be missing half the account.
2015
+ */
1160
2016
  private buildBalance;
2017
+ /** The market data both read paths join account state against, as the latest read left it. */
2018
+ private marketContext;
2019
+ /** The account's current per-market settings as the base tracker already holds them. */
2020
+ private liveAccountSettings;
1161
2021
  private fetchPrimaryAccount;
1162
- private fetchAccountTier;
1163
- private pollAccount;
1164
- private performAccountPoll;
1165
- private shouldRefreshAccountTier;
2022
+ private pollPositions;
2023
+ /**
2024
+ * Republishes positions and per-market settings from REST.
2025
+ *
2026
+ * Deliberately publishes NO balance. Every field of both Lighter variants comes off the two
2027
+ * websocket channels — `user_stats` carries both account scopes and `account_all` carries the
2028
+ * assets and positions behind them — so a REST balance here would only overwrite a live value
2029
+ * with an equal one, while making `getBalance()` REST-derived most of the time.
2030
+ *
2031
+ * It also leaves `wsPositionsByMarketId` alone. That store feeds the live balance's unrealized
2032
+ * PnL and maintenance margin, so writing REST rows into it would make a balance labelled live
2033
+ * partly REST-derived without any reader being able to tell.
2034
+ */
2035
+ private performPositionPoll;
2036
+ /** Shares one in-flight market read, so a slow response cannot stack requests behind the WAF. */
2037
+ private refreshMarkets;
2038
+ /**
2039
+ * Re-reads the venue's markets and republishes the balance they move.
2040
+ *
2041
+ * `spotEquity` values each holding at the last trade its own spot book printed, so an index read
2042
+ * only at `start()` values a session-long tracker at session-old prices — and every scalar built
2043
+ * on it drifts with it.
2044
+ *
2045
+ * The perp books ride along, which is what closes the gap `toPositionRisks` documents: a market
2046
+ * Lighter listed after `start()` was unknown to the balance for the rest of the session, so a
2047
+ * position on it left the maintenance figure unknown for just as long. The read already carries
2048
+ * them, so learning them costs nothing. Their risk parameters come along as a consequence, which
2049
+ * only makes a slowly-moving margin fraction current rather than session-old.
2050
+ *
2051
+ * Perp books are UPSERTED, never replaced. An account can still hold a position on a market that
2052
+ * has dropped out of the listing, and the parameters this tracker last saw price that position —
2053
+ * where forgetting the market would stop pricing it at all.
2054
+ *
2055
+ * A read that fails, or that comes back carrying no spot books at all, leaves the last good price
2056
+ * index in place. Stale prices still value the account; an empty index would null every spot
2057
+ * figure the account has at once, which is a worse answer than a slightly old one.
2058
+ *
2059
+ * Only the market data moves here. A newly listed market's exchange-default asset settings reach
2060
+ * `trackAsset` consumers on the next websocket epoch, where `handleWsOpen` already republishes
2061
+ * them for every known market — publishing them here would overwrite the account's own settings.
2062
+ */
2063
+ private performMarketRefresh;
1166
2064
  }
1167
2065
 
2066
+ /**
2067
+ * OKX's four account-mode balance variants.
2068
+ *
2069
+ * Core fields only, with `null` wherever a figure cannot be derived. OKX publishes no account-level
2070
+ * margin fields in Futures mode (`acctLv=2`) at all, which is exactly why the flat shape had to go
2071
+ * — and why `perCollateral` matters most there, since `details[].availEq` is per currency. What
2072
+ * differs by mode is not the shape but when the core scalars go null, which follows each mode's
2073
+ * collateral-pool model. One `z.literal` per mode, because `z.discriminatedUnion` needs a single
2074
+ * literal tag per member rather than a `z.enum` shorthand.
2075
+ */
2076
+
2077
+ /**
2078
+ * Spot and Futures mode run one collateral pool per currency, so `totalEq` is a USD total across
2079
+ * budgets that cannot fund each other. Once a second pool is funded there is no spendable account
2080
+ * total, so `totalEquity` goes null and `perCollateral` is the whole answer. `OkxFuturesBalance`
2081
+ * carries the same rule.
2082
+ */
2083
+ declare const OkxSpotBalance: z.ZodObject<{
2084
+ totalEquity: z.ZodNullable<z.ZodString>;
2085
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
2086
+ availableMargin: z.ZodNullable<z.ZodString>;
2087
+ poolsShared: z.ZodBoolean;
2088
+ perCollateral: z.ZodArray<z.ZodObject<{
2089
+ asset: z.ZodString;
2090
+ equity: z.ZodString;
2091
+ availableMargin: z.ZodNullable<z.ZodString>;
2092
+ withdrawable: z.ZodNullable<z.ZodString>;
2093
+ }, z.core.$strip>>;
2094
+ timestamp: z.ZodNumber;
2095
+ accountMode: z.ZodLiteral<"okx:spot">;
2096
+ }, z.core.$strip>;
2097
+ type OkxSpotBalance = z.infer<typeof OkxSpotBalance>;
2098
+ declare const OkxFuturesBalance: z.ZodObject<{
2099
+ totalEquity: z.ZodNullable<z.ZodString>;
2100
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
2101
+ availableMargin: z.ZodNullable<z.ZodString>;
2102
+ poolsShared: z.ZodBoolean;
2103
+ perCollateral: z.ZodArray<z.ZodObject<{
2104
+ asset: z.ZodString;
2105
+ equity: z.ZodString;
2106
+ availableMargin: z.ZodNullable<z.ZodString>;
2107
+ withdrawable: z.ZodNullable<z.ZodString>;
2108
+ }, z.core.$strip>>;
2109
+ timestamp: z.ZodNumber;
2110
+ accountMode: z.ZodLiteral<"okx:futures">;
2111
+ }, z.core.$strip>;
2112
+ type OkxFuturesBalance = z.infer<typeof OkxFuturesBalance>;
2113
+ declare const OkxMultiCurrencyMarginBalance: z.ZodObject<{
2114
+ totalEquity: z.ZodNullable<z.ZodString>;
2115
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
2116
+ availableMargin: z.ZodNullable<z.ZodString>;
2117
+ poolsShared: z.ZodBoolean;
2118
+ perCollateral: z.ZodArray<z.ZodObject<{
2119
+ asset: z.ZodString;
2120
+ equity: z.ZodString;
2121
+ availableMargin: z.ZodNullable<z.ZodString>;
2122
+ withdrawable: z.ZodNullable<z.ZodString>;
2123
+ }, z.core.$strip>>;
2124
+ timestamp: z.ZodNumber;
2125
+ accountMode: z.ZodLiteral<"okx:multiCurrencyMargin">;
2126
+ }, z.core.$strip>;
2127
+ type OkxMultiCurrencyMarginBalance = z.infer<typeof OkxMultiCurrencyMarginBalance>;
2128
+ declare const OkxPortfolioMarginBalance: z.ZodObject<{
2129
+ totalEquity: z.ZodNullable<z.ZodString>;
2130
+ withdrawableBalance: z.ZodNullable<z.ZodString>;
2131
+ availableMargin: z.ZodNullable<z.ZodString>;
2132
+ poolsShared: z.ZodBoolean;
2133
+ perCollateral: z.ZodArray<z.ZodObject<{
2134
+ asset: z.ZodString;
2135
+ equity: z.ZodString;
2136
+ availableMargin: z.ZodNullable<z.ZodString>;
2137
+ withdrawable: z.ZodNullable<z.ZodString>;
2138
+ }, z.core.$strip>>;
2139
+ timestamp: z.ZodNumber;
2140
+ accountMode: z.ZodLiteral<"okx:portfolioMargin">;
2141
+ }, z.core.$strip>;
2142
+ type OkxPortfolioMarginBalance = z.infer<typeof OkxPortfolioMarginBalance>;
2143
+
1168
2144
  type OkxAccountTrackerConfig = AccountTrackerConfig & {
1169
2145
  credentials: SupportedCredentials;
1170
2146
  };
@@ -1175,10 +2151,26 @@ declare class OkxAccountTracker extends BaseAccountTracker {
1175
2151
  private openListenerId;
1176
2152
  private rawCoins;
1177
2153
  private lastBalance;
1178
- private accountType;
2154
+ private accountMode;
2155
+ private withdrawable;
2156
+ private lastWithdrawableReadAt;
2157
+ private withdrawableRefreshTimer;
2158
+ private withdrawableReadInFlight;
2159
+ private withdrawableReadPending;
2160
+ private lifecycleVersion;
1179
2161
  private trackedAssetInfos;
1180
2162
  constructor(config: OkxAccountTrackerConfig);
1181
2163
  snapshot(): Promise<AccountSnapshot>;
2164
+ /**
2165
+ * OKX sizes no basket in v1: the SDK reads no per-asset max leverage from it, and sizing off
2166
+ * `availableMargin` alone would ship a number no venue field backs. The missing input is
2167
+ * `GET /api/v5/public/position-tiers` → `maxLever`/`imr` per tier, which the client does not
2168
+ * call — and it is NOT `GET /api/v5/account/position-tiers`, a different endpoint carrying no
2169
+ * margin or leverage data at all.
2170
+ */
2171
+ maxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
2172
+ /** The REST path answers the same way — there is no size either way, not a slower one. */
2173
+ snapshotMaxBasketSize(_input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1182
2174
  /**
1183
2175
  * Stateless one-shot per-asset leverage read via `leverage-info`, which accepts up to 20 instIds
1184
2176
  * per request (chunked in the client), so N assets cost ceil(N/20) calls. Cross margin only,
@@ -1189,19 +2181,75 @@ declare class OkxAccountTracker extends BaseAccountTracker {
1189
2181
  protected cleanupConnections(): Promise<void>;
1190
2182
  get isConnected(): boolean;
1191
2183
  private fetchSnapshotData;
2184
+ /**
2185
+ * Read how much can leave the trading account, per currency, across everything it owns.
2186
+ *
2187
+ * Best-effort on purpose: this is a fourth signed call that a read-only key may be refused, and
2188
+ * a refusal must cost the caller a `null` field rather than the whole balance.
2189
+ *
2190
+ * A refusal is reported as `failed` rather than as an empty row set, because only the caller
2191
+ * knows what to do with it: the snapshot path has no earlier figure and reports nothing known,
2192
+ * while the live path keeps the figure its last successful read published.
2193
+ */
2194
+ private readMaxWithdrawals;
1192
2195
  protected ensureAssetTracked(instId: string): void;
1193
2196
  protected releaseAsset(instId: string): void;
1194
2197
  private subscribe;
1195
2198
  private handleInitialData;
1196
2199
  private routeRaw;
1197
2200
  private handleMessage;
2201
+ /**
2202
+ * Fold one account push into the whole account, then publish what that whole account says.
2203
+ *
2204
+ * OKX pushes only what moved: an update names the currencies whose figures changed and leaves
2205
+ * every other currency out. So the rows are taken from the per-currency merge rather than from
2206
+ * the push, and everything read off them — the pool count above all — sees every currency the
2207
+ * account holds. A push naming one of two funded currencies would otherwise read as a
2208
+ * single-pool account and publish scalars that describe neither pool.
2209
+ *
2210
+ * The account-level figures inherit on `??`, never on `||`, because ABSENT and BLANK say
2211
+ * opposite things here. A field this push left out was not restated and its previous value still
2212
+ * stands; a field present as `''` is OKX withdrawing the figure, which it does per account mode —
2213
+ * `availableMarginOf` records the mode that blanks `availEq`. Inheriting a blank would republish
2214
+ * a number the venue no longer states, and it would do it BEHIND `decimalOrNull`, whose whole job
2215
+ * is to publish a blank as `null`: overwriting the blank here means that guard never sees it.
2216
+ */
1198
2217
  private handleBalanceUpdate;
2218
+ /** Publish the balance built from the newest account payload and the cached transferable amounts. */
2219
+ private emitBalance;
2220
+ /**
2221
+ * Queue the next transferable-amount read, no sooner than the venue's own rate limit allows.
2222
+ *
2223
+ * Nothing waits on it: the account update is already published from the cached figure. One
2224
+ * queued read covers every account tick that lands before it runs, which is what stops a burst
2225
+ * of ticks becoming a burst of signed calls — and because a tick always leaves a read queued or
2226
+ * marked pending, a moved figure is still picked up within one interval rather than dropped.
2227
+ */
2228
+ private refreshWithdrawableSoon;
2229
+ /**
2230
+ * Hold one transferable-amount read open at a time, and queue the ticks that arrive during it.
2231
+ *
2232
+ * Serialising the reads is what keeps the published figure the newest one: two overlapping reads
2233
+ * can come back in either order, and the older answer landing last would publish an amount the
2234
+ * venue has already moved past.
2235
+ */
2236
+ private refreshWithdrawableBalance;
2237
+ /**
2238
+ * Re-read the transferable amount behind the live path, then re-publish only if it moved.
2239
+ *
2240
+ * Fire-and-forget: the account update is already published with the previous figure, so a slow
2241
+ * read delays the correction instead of blocking the balance. A read that FAILS changes nothing
2242
+ * at all — the last good figure stays published, because a refusal says OKX did not answer, not
2243
+ * that the account has nothing to transfer. The re-publish reads the newest account payload
2244
+ * rather than the one this read started from, so an account update that landed meanwhile is not
2245
+ * rolled back.
2246
+ */
2247
+ private performWithdrawableRefresh;
1199
2248
  }
1200
2249
 
1201
2250
  type CreateAccountTrackerConfig = AccountTrackerConfig & {
1202
2251
  account: TradeAccount;
1203
2252
  credentials: SupportedCredentials;
1204
- sdk?: PearSDK;
1205
2253
  };
1206
2254
  declare function createAccountTracker(exchange: Connector, config: CreateAccountTrackerConfig): AccountTracker;
1207
2255
 
@@ -1218,6 +2266,11 @@ declare class ExchangesSDK {
1218
2266
  status(connection: ExchangeConnection, options?: ConnectOptions): Promise<AccountStatus>;
1219
2267
  createTracker(connection: ExchangeConnection): AccountTracker;
1220
2268
  snapshot(tradeAccountId: string): Promise<AccountSnapshot>;
2269
+ /**
2270
+ * Sizes a basket in one shot. Routed to the tracker's REST path on purpose: this entry point
2271
+ * builds a throwaway tracker per call, so it must open no socket.
2272
+ */
2273
+ maxBasketSize(tradeAccountId: string, input: MaxBasketSizeInput): Promise<MaxBasketSizeResult>;
1221
2274
  snapshotAssets(tradeAccountId: string, coins: string[]): Promise<AssetSnapshotMap>;
1222
2275
  createHelper<E extends ExchangeHelperExchange>(exchange: E): ExchangeHelperMap[E];
1223
2276
  createSyncer(connection: ExchangeConnection): AccountSyncer;
@@ -1225,4 +2278,4 @@ declare class ExchangesSDK {
1225
2278
  private fetchCredentials;
1226
2279
  }
1227
2280
 
1228
- export { ACCOUNT_TYPE_LABELS, ASSET_DECIMALS, type AccountBalance, type AccountHealthStatus, type AccountPosition, type AccountSnapshot, type AccountStatus, type AccountSyncer, type AccountSyncerConfig, type AccountTracker, type AccountTrackerConfig, type AccountType, type AccountWarningReason, type ApiKey, type ApproveIntegratorParams, type ApproveIntegratorResult, type AssetBalance, AssetId, type AssetSnapshotMap, type BalanceCallback, BaseAccountSyncer, BaseAccountTracker, BaseWs, BinanceAccountSyncer, BinanceAccountTracker, type BinanceAccountTrackerConfig, BinanceAccountType, BinanceExchangeWs, BinanceUserDataWs, type BinanceUserDataWsConfig, BinanceWsApi, type BinanceWsApiConfig, BybitAccountSyncer, BybitAccountTracker, type BybitAccountTrackerConfig, BybitAccountType, BybitExchangeWs, BybitWs, type BybitWsConfig, type CctpDepositHistorySettlement, type CctpDepositIntentParams, type CctpDepositIntentResult, type CctpDepositParams, type CctpDepositProgress, type CctpDepositRegistrationSettlement, type CctpDepositResult, type CctpDepositSettlement, type CctpDepositWalletAdapter, type CheckApiWalletParams, type ConnectOptions, type ConnectWalletParams, type ConnectWalletResult, type CreateAccountTrackerConfig, type CreateExchangeWsConfig, type DepositParams, type DepositResult, type ExchangeConnection, type ExchangeHelperExchange, type ExchangeHelperMap, type ExchangeState, ExchangeWs, type ExchangeWsByConnector, ExchangesSDK, type ExchangesSDKConfig, type FastWithdrawParams, type FastWithdrawResult, HyperliquidAccountSyncer, HyperliquidAccountTracker, type HyperliquidAccountTrackerConfig, HyperliquidAccountType, HyperliquidExchangeWs, type HyperliquidHelper, HyperliquidWs, type IntegratorApprovalRequirements, L2_ASSET_DECIMALS, LIGHTER_CCTP_USDC_ADDRESSES, LIGHTER_CCTP_USDC_DECIMALS, LighterAccountSyncer, LighterAccountTracker, type LighterAccountTrackerConfig, LighterAccountType, type LighterDeposit, type LighterDepositNetwork, LighterExchangeWs, type LighterHelper, type LighterLookupResult, type LighterOnboardingStatus, LighterWs, type ListExternalFillsParams, type ListExternalFundingParams, type ListExternalPositionsParams, MAX_API_KEY_INDEX, MIN_LIGHTER_CCTP_DEPOSIT_USDC, MIN_LIGHTER_FAST_WITHDRAW_USDC, MIN_USER_API_KEY_INDEX, type MarginMode, type MessageListener, OkxAccountSyncer, OkxAccountTracker, type OkxAccountTrackerConfig, OkxAccountType, OkxExchangeWs, OkxWs, type OkxWsConfig, type OpenListener, type PositionsCallback, RouteType, type StateUpdate, type SupportedCredentials, type TrackedAssetCallback, type TrackedAssetInfo, type WalletClientWithAccount, type WithdrawParams, type WithdrawResult, type WsMessage, createAccountSyncer, createAccountTracker, createExchangeWs, createHyperliquidHelper, createLighterHelper, findAvailableApiKeyIndex, getAssetDecimals, getLighterCctpUsdcAddress, usdcAbi };
2281
+ export { ACCOUNT_MODE_LABELS, ASSET_DECIMALS, AccountBalance, AccountBalanceCore, type AccountHealthStatus, AccountMode, type AccountPosition, type AccountSnapshot, type AccountStatus, type AccountSyncer, type AccountSyncerConfig, type AccountTracker, type AccountTrackerConfig, type AccountWarningReason, type ApiKey, type ApproveIntegratorParams, type ApproveIntegratorResult, AssetId, type AssetSnapshotMap, type BalanceCallback, BaseAccountSyncer, BaseAccountTracker, BaseWs, BasketLeg, BinanceAccountSyncer, BinanceAccountTracker, type BinanceAccountTrackerConfig, BinanceExchangeWs, BinanceMultiAssetBalance, BinanceSingleAssetBalance, BinanceUserDataWs, type BinanceUserDataWsConfig, BinanceWsApi, type BinanceWsApiConfig, BybitAccountSyncer, BybitAccountTracker, type BybitAccountTrackerConfig, BybitExchangeWs, BybitIsolatedMarginBalance, BybitPortfolioMarginBalance, BybitRegularMarginBalance, BybitWs, type BybitWsConfig, type CappedBasketLeg, type CctpDepositHistorySettlement, type CctpDepositIntentParams, type CctpDepositIntentResult, type CctpDepositParams, type CctpDepositProgress, type CctpDepositRegistrationSettlement, type CctpDepositResult, type CctpDepositSettlement, type CctpDepositWalletAdapter, type CheckApiWalletParams, CollateralBalance, type ConnectOptions, type ConnectWalletParams, type ConnectWalletResult, type CreateAccountTrackerConfig, type CreateExchangeWsConfig, DecimalString, type DepositParams, type DepositResult, type ExchangeConnection, type ExchangeHelperExchange, type ExchangeHelperMap, type ExchangeState, ExchangeWs, type ExchangeWsByConnector, ExchangesSDK, type ExchangesSDKConfig, type FastWithdrawParams, type FastWithdrawResult, type FillSyncOutcome, type FundingSyncOutcome, HyperliquidAccountSyncer, HyperliquidAccountTracker, type HyperliquidAccountTrackerConfig, HyperliquidDexState, HyperliquidExchangeWs, type HyperliquidHelper, HyperliquidPortfolioMarginBalance, HyperliquidPortfolioMarginCollateral, HyperliquidStandardBalance, HyperliquidUnifiedBalance, HyperliquidWs, type IntegratorApprovalRequirements, L2_ASSET_DECIMALS, LIGHTER_CCTP_USDC_ADDRESSES, LIGHTER_CCTP_USDC_DECIMALS, LighterAccountSyncer, LighterAccountTracker, type LighterAccountTrackerConfig, LighterClassicBalance, type LighterDeposit, type LighterDepositNetwork, LighterExchangeWs, type LighterHelper, type LighterLookupResult, type LighterOnboardingStatus, LighterUnifiedBalance, LighterWs, type ListExternalFillsParams, type ListExternalFundingParams, type ListExternalPositionsParams, MAX_API_KEY_INDEX, MIN_LIGHTER_CCTP_DEPOSIT_USDC, MIN_LIGHTER_FAST_WITHDRAW_USDC, MIN_USER_API_KEY_INDEX, MarginMode, type MaxBasketSizeCalculation, MaxBasketSizeInput, MaxBasketSizeResult, type MessageListener, OkxAccountSyncer, OkxAccountTracker, type OkxAccountTrackerConfig, OkxExchangeWs, OkxFuturesBalance, OkxMultiCurrencyMarginBalance, OkxPortfolioMarginBalance, OkxSpotBalance, OkxWs, type OkxWsConfig, type OpenListener, type PositionsCallback, RouteType, Side, SignedDecimalString, type StateUpdate, type SupportedCredentials, type TrackedAssetCallback, type TrackedAssetInfo, type WalletClientWithAccount, type WithdrawParams, type WithdrawResult, type WsMessage, computeMaxBasketSize, createAccountSyncer, createAccountTracker, createExchangeWs, createHyperliquidHelper, createLighterHelper, findAvailableApiKeyIndex, getAssetDecimals, getLighterCctpUsdcAddress, usdcAbi };