@raintree-technology/perps 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +175 -0
  4. package/dist/adapters/aevo.d.ts +64 -0
  5. package/dist/adapters/aevo.js +899 -0
  6. package/dist/adapters/certification.d.ts +33 -0
  7. package/dist/adapters/certification.js +99 -0
  8. package/dist/adapters/decibel/order-manager.d.ts +45 -0
  9. package/dist/adapters/decibel/order-manager.js +140 -0
  10. package/dist/adapters/decibel/rest-client.d.ts +176 -0
  11. package/dist/adapters/decibel/rest-client.js +155 -0
  12. package/dist/adapters/decibel/ws-feed.d.ts +28 -0
  13. package/dist/adapters/decibel/ws-feed.js +166 -0
  14. package/dist/adapters/decibel.d.ts +108 -0
  15. package/dist/adapters/decibel.js +1377 -0
  16. package/dist/adapters/hyperliquid.d.ts +63 -0
  17. package/dist/adapters/hyperliquid.js +797 -0
  18. package/dist/adapters/index.d.ts +11 -0
  19. package/dist/adapters/index.js +12 -0
  20. package/dist/adapters/interface.d.ts +310 -0
  21. package/dist/adapters/interface.js +15 -0
  22. package/dist/adapters/orderly.d.ts +70 -0
  23. package/dist/adapters/orderly.js +936 -0
  24. package/dist/adapters/paradex.d.ts +69 -0
  25. package/dist/adapters/paradex.js +862 -0
  26. package/dist/adapters/utils.d.ts +17 -0
  27. package/dist/adapters/utils.js +122 -0
  28. package/dist/cli/command-metadata.d.ts +2 -0
  29. package/dist/cli/command-metadata.js +44 -0
  30. package/dist/cli/context.d.ts +14 -0
  31. package/dist/cli/context.js +59 -0
  32. package/dist/cli/experience.d.ts +48 -0
  33. package/dist/cli/experience.js +243 -0
  34. package/dist/cli/ink/app/AppShell.d.ts +12 -0
  35. package/dist/cli/ink/app/AppShell.js +32 -0
  36. package/dist/cli/ink/app/MetricStrip.d.ts +6 -0
  37. package/dist/cli/ink/app/MetricStrip.js +14 -0
  38. package/dist/cli/ink/app/Panel.d.ts +9 -0
  39. package/dist/cli/ink/app/Panel.js +7 -0
  40. package/dist/cli/ink/app/ascii.d.ts +2 -0
  41. package/dist/cli/ink/app/ascii.js +46 -0
  42. package/dist/cli/ink/app/index.d.ts +5 -0
  43. package/dist/cli/ink/app/index.js +4 -0
  44. package/dist/cli/ink/app/types.d.ts +15 -0
  45. package/dist/cli/ink/app/types.js +1 -0
  46. package/dist/cli/ink/components/PnL.d.ts +12 -0
  47. package/dist/cli/ink/components/PnL.js +23 -0
  48. package/dist/cli/ink/components/Spinner.d.ts +13 -0
  49. package/dist/cli/ink/components/Spinner.js +13 -0
  50. package/dist/cli/ink/components/Table.d.ts +14 -0
  51. package/dist/cli/ink/components/Table.js +42 -0
  52. package/dist/cli/ink/components/WatchHeader.d.ts +10 -0
  53. package/dist/cli/ink/components/WatchHeader.js +18 -0
  54. package/dist/cli/ink/components/index.d.ts +4 -0
  55. package/dist/cli/ink/components/index.js +4 -0
  56. package/dist/cli/ink/index.d.ts +4 -0
  57. package/dist/cli/ink/index.js +4 -0
  58. package/dist/cli/ink/render.d.ts +12 -0
  59. package/dist/cli/ink/render.js +21 -0
  60. package/dist/cli/ink/theme.d.ts +29 -0
  61. package/dist/cli/ink/theme.js +40 -0
  62. package/dist/cli/network-defaults.d.ts +10 -0
  63. package/dist/cli/network-defaults.js +35 -0
  64. package/dist/cli/output.d.ts +11 -0
  65. package/dist/cli/output.js +115 -0
  66. package/dist/cli/program.d.ts +18 -0
  67. package/dist/cli/program.js +164 -0
  68. package/dist/cli/watch.d.ts +19 -0
  69. package/dist/cli/watch.js +35 -0
  70. package/dist/client/index.d.ts +55 -0
  71. package/dist/client/index.js +157 -0
  72. package/dist/commands/account/add.d.ts +2 -0
  73. package/dist/commands/account/add.js +510 -0
  74. package/dist/commands/account/balances-simple.d.ts +5 -0
  75. package/dist/commands/account/balances-simple.js +63 -0
  76. package/dist/commands/account/index.d.ts +2 -0
  77. package/dist/commands/account/index.js +17 -0
  78. package/dist/commands/account/ls.d.ts +2 -0
  79. package/dist/commands/account/ls.js +95 -0
  80. package/dist/commands/account/positions-simple.d.ts +5 -0
  81. package/dist/commands/account/positions-simple.js +77 -0
  82. package/dist/commands/account/remove.d.ts +2 -0
  83. package/dist/commands/account/remove.js +47 -0
  84. package/dist/commands/account/set-default.d.ts +2 -0
  85. package/dist/commands/account/set-default.js +47 -0
  86. package/dist/commands/agent/index.d.ts +2 -0
  87. package/dist/commands/agent/index.js +126 -0
  88. package/dist/commands/arb/alert.d.ts +6 -0
  89. package/dist/commands/arb/alert.js +88 -0
  90. package/dist/commands/arb/basis-execute.d.ts +6 -0
  91. package/dist/commands/arb/basis-execute.js +332 -0
  92. package/dist/commands/arb/basis.d.ts +6 -0
  93. package/dist/commands/arb/basis.js +181 -0
  94. package/dist/commands/arb/compare.d.ts +6 -0
  95. package/dist/commands/arb/compare.js +216 -0
  96. package/dist/commands/arb/execute.d.ts +6 -0
  97. package/dist/commands/arb/execute.js +467 -0
  98. package/dist/commands/arb/funding.d.ts +6 -0
  99. package/dist/commands/arb/funding.js +201 -0
  100. package/dist/commands/arb/history.d.ts +6 -0
  101. package/dist/commands/arb/history.js +153 -0
  102. package/dist/commands/arb/index.d.ts +6 -0
  103. package/dist/commands/arb/index.js +29 -0
  104. package/dist/commands/arb/positions.d.ts +6 -0
  105. package/dist/commands/arb/positions.js +158 -0
  106. package/dist/commands/arb/spread.d.ts +6 -0
  107. package/dist/commands/arb/spread.js +253 -0
  108. package/dist/commands/arb/track.d.ts +6 -0
  109. package/dist/commands/arb/track.js +259 -0
  110. package/dist/commands/asset/book-simple.d.ts +5 -0
  111. package/dist/commands/asset/book-simple.js +77 -0
  112. package/dist/commands/asset/index.d.ts +2 -0
  113. package/dist/commands/asset/index.js +5 -0
  114. package/dist/commands/completion.d.ts +2 -0
  115. package/dist/commands/completion.js +161 -0
  116. package/dist/commands/config/index.d.ts +5 -0
  117. package/dist/commands/config/index.js +109 -0
  118. package/dist/commands/data/index.d.ts +31 -0
  119. package/dist/commands/data/index.js +1466 -0
  120. package/dist/commands/doctor.d.ts +2 -0
  121. package/dist/commands/doctor.js +201 -0
  122. package/dist/commands/exchange/index.d.ts +2 -0
  123. package/dist/commands/exchange/index.js +107 -0
  124. package/dist/commands/index.d.ts +2 -0
  125. package/dist/commands/index.js +48 -0
  126. package/dist/commands/markets/index.d.ts +2 -0
  127. package/dist/commands/markets/index.js +5 -0
  128. package/dist/commands/markets/ls-simple.d.ts +7 -0
  129. package/dist/commands/markets/ls-simple.js +277 -0
  130. package/dist/commands/operator/index.d.ts +2 -0
  131. package/dist/commands/operator/index.js +146 -0
  132. package/dist/commands/order/cancel-simple.d.ts +5 -0
  133. package/dist/commands/order/cancel-simple.js +104 -0
  134. package/dist/commands/order/index.d.ts +2 -0
  135. package/dist/commands/order/index.js +13 -0
  136. package/dist/commands/order/limit-simple.d.ts +5 -0
  137. package/dist/commands/order/limit-simple.js +195 -0
  138. package/dist/commands/order/market-simple.d.ts +5 -0
  139. package/dist/commands/order/market-simple.js +190 -0
  140. package/dist/commands/order/shared.d.ts +17 -0
  141. package/dist/commands/order/shared.js +51 -0
  142. package/dist/commands/order/trigger-simple.d.ts +5 -0
  143. package/dist/commands/order/trigger-simple.js +246 -0
  144. package/dist/commands/referral/index.d.ts +2 -0
  145. package/dist/commands/referral/index.js +7 -0
  146. package/dist/commands/referral/set.d.ts +2 -0
  147. package/dist/commands/referral/set.js +26 -0
  148. package/dist/commands/referral/status.d.ts +2 -0
  149. package/dist/commands/referral/status.js +31 -0
  150. package/dist/commands/replay/index.d.ts +2 -0
  151. package/dist/commands/replay/index.js +152 -0
  152. package/dist/commands/risk/analytics.d.ts +2 -0
  153. package/dist/commands/risk/analytics.js +64 -0
  154. package/dist/commands/risk/audit.d.ts +2 -0
  155. package/dist/commands/risk/audit.js +52 -0
  156. package/dist/commands/risk/index.d.ts +2 -0
  157. package/dist/commands/risk/index.js +9 -0
  158. package/dist/commands/risk/rules.d.ts +2 -0
  159. package/dist/commands/risk/rules.js +102 -0
  160. package/dist/commands/server.d.ts +2 -0
  161. package/dist/commands/server.js +208 -0
  162. package/dist/commands/setup/index.d.ts +2 -0
  163. package/dist/commands/setup/index.js +478 -0
  164. package/dist/commands/signal/index.d.ts +2 -0
  165. package/dist/commands/signal/index.js +129 -0
  166. package/dist/commands/state/index.d.ts +2 -0
  167. package/dist/commands/state/index.js +5 -0
  168. package/dist/commands/state/show.d.ts +2 -0
  169. package/dist/commands/state/show.js +105 -0
  170. package/dist/commands/strategy/index.d.ts +4 -0
  171. package/dist/commands/strategy/index.js +73 -0
  172. package/dist/commands/traces/index.d.ts +2 -0
  173. package/dist/commands/traces/index.js +76 -0
  174. package/dist/commands/ui/demo.d.ts +9 -0
  175. package/dist/commands/ui/demo.js +195 -0
  176. package/dist/commands/ui/index.d.ts +2 -0
  177. package/dist/commands/ui/index.js +7 -0
  178. package/dist/commands/ui/terminal.d.ts +2 -0
  179. package/dist/commands/ui/terminal.js +255 -0
  180. package/dist/commands/upgrade.d.ts +2 -0
  181. package/dist/commands/upgrade.js +98 -0
  182. package/dist/index.d.ts +2 -0
  183. package/dist/index.js +4 -0
  184. package/dist/lib/agent/audit.d.ts +12 -0
  185. package/dist/lib/agent/audit.js +13 -0
  186. package/dist/lib/agent/gateway.d.ts +13 -0
  187. package/dist/lib/agent/gateway.js +598 -0
  188. package/dist/lib/agent/metrics.d.ts +33 -0
  189. package/dist/lib/agent/metrics.js +175 -0
  190. package/dist/lib/agent/signature.d.ts +8 -0
  191. package/dist/lib/agent/signature.js +28 -0
  192. package/dist/lib/agent/tools.d.ts +28 -0
  193. package/dist/lib/agent/tools.js +453 -0
  194. package/dist/lib/agent/x402.d.ts +23 -0
  195. package/dist/lib/agent/x402.js +62 -0
  196. package/dist/lib/api-wallet.d.ts +69 -0
  197. package/dist/lib/api-wallet.js +101 -0
  198. package/dist/lib/balance-watcher.d.ts +25 -0
  199. package/dist/lib/balance-watcher.js +83 -0
  200. package/dist/lib/book-watcher.d.ts +25 -0
  201. package/dist/lib/book-watcher.js +48 -0
  202. package/dist/lib/config.d.ts +88 -0
  203. package/dist/lib/config.js +427 -0
  204. package/dist/lib/constants.d.ts +50 -0
  205. package/dist/lib/constants.js +84 -0
  206. package/dist/lib/contracts.d.ts +7 -0
  207. package/dist/lib/contracts.js +8 -0
  208. package/dist/lib/credential-vault.d.ts +22 -0
  209. package/dist/lib/credential-vault.js +109 -0
  210. package/dist/lib/db/accounts.d.ts +83 -0
  211. package/dist/lib/db/accounts.js +203 -0
  212. package/dist/lib/db/funding-history.d.ts +69 -0
  213. package/dist/lib/db/funding-history.js +183 -0
  214. package/dist/lib/db/index.d.ts +11 -0
  215. package/dist/lib/db/index.js +272 -0
  216. package/dist/lib/events/bus.d.ts +10 -0
  217. package/dist/lib/events/bus.js +17 -0
  218. package/dist/lib/events/types.d.ts +51 -0
  219. package/dist/lib/events/types.js +1 -0
  220. package/dist/lib/exchange.d.ts +30 -0
  221. package/dist/lib/exchange.js +84 -0
  222. package/dist/lib/execution/journal.d.ts +25 -0
  223. package/dist/lib/execution/journal.js +158 -0
  224. package/dist/lib/execution/safety.d.ts +34 -0
  225. package/dist/lib/execution/safety.js +197 -0
  226. package/dist/lib/exit-codes.d.ts +18 -0
  227. package/dist/lib/exit-codes.js +60 -0
  228. package/dist/lib/fetch.d.ts +18 -0
  229. package/dist/lib/fetch.js +66 -0
  230. package/dist/lib/fs-security.d.ts +10 -0
  231. package/dist/lib/fs-security.js +26 -0
  232. package/dist/lib/funding-tracker.d.ts +40 -0
  233. package/dist/lib/funding-tracker.js +118 -0
  234. package/dist/lib/logger.d.ts +27 -0
  235. package/dist/lib/logger.js +82 -0
  236. package/dist/lib/network-model.d.ts +13 -0
  237. package/dist/lib/network-model.js +30 -0
  238. package/dist/lib/onboarding.d.ts +133 -0
  239. package/dist/lib/onboarding.js +1459 -0
  240. package/dist/lib/operator-state.d.ts +25 -0
  241. package/dist/lib/operator-state.js +82 -0
  242. package/dist/lib/orders-watcher.d.ts +24 -0
  243. package/dist/lib/orders-watcher.js +74 -0
  244. package/dist/lib/paths.d.ts +20 -0
  245. package/dist/lib/paths.js +23 -0
  246. package/dist/lib/portfolio-watcher.d.ts +33 -0
  247. package/dist/lib/portfolio-watcher.js +95 -0
  248. package/dist/lib/position-watcher.d.ts +16 -0
  249. package/dist/lib/position-watcher.js +44 -0
  250. package/dist/lib/price-watcher.d.ts +15 -0
  251. package/dist/lib/price-watcher.js +84 -0
  252. package/dist/lib/prompts.d.ts +32 -0
  253. package/dist/lib/prompts.js +105 -0
  254. package/dist/lib/rate-limit.d.ts +32 -0
  255. package/dist/lib/rate-limit.js +88 -0
  256. package/dist/lib/risk/analytics.d.ts +39 -0
  257. package/dist/lib/risk/analytics.js +98 -0
  258. package/dist/lib/risk/drawdown.d.ts +18 -0
  259. package/dist/lib/risk/drawdown.js +49 -0
  260. package/dist/lib/risk/evaluation-log.d.ts +29 -0
  261. package/dist/lib/risk/evaluation-log.js +61 -0
  262. package/dist/lib/risk/index.d.ts +4 -0
  263. package/dist/lib/risk/index.js +4 -0
  264. package/dist/lib/risk/limits.d.ts +23 -0
  265. package/dist/lib/risk/limits.js +27 -0
  266. package/dist/lib/risk/manager.d.ts +32 -0
  267. package/dist/lib/risk/manager.js +85 -0
  268. package/dist/lib/risk/policy-middleware.d.ts +33 -0
  269. package/dist/lib/risk/policy-middleware.js +267 -0
  270. package/dist/lib/risk/position-sizer.d.ts +9 -0
  271. package/dist/lib/risk/position-sizer.js +14 -0
  272. package/dist/lib/risk/rules-store.d.ts +16 -0
  273. package/dist/lib/risk/rules-store.js +47 -0
  274. package/dist/lib/schema.d.ts +254 -0
  275. package/dist/lib/schema.js +199 -0
  276. package/dist/lib/secrets.d.ts +3 -0
  277. package/dist/lib/secrets.js +62 -0
  278. package/dist/lib/settings.d.ts +24 -0
  279. package/dist/lib/settings.js +86 -0
  280. package/dist/lib/signals.d.ts +73 -0
  281. package/dist/lib/signals.js +136 -0
  282. package/dist/lib/stable-stringify.d.ts +6 -0
  283. package/dist/lib/stable-stringify.js +17 -0
  284. package/dist/lib/state-context.d.ts +44 -0
  285. package/dist/lib/state-context.js +133 -0
  286. package/dist/lib/strategy/basis-trade.d.ts +2 -0
  287. package/dist/lib/strategy/basis-trade.js +24 -0
  288. package/dist/lib/strategy/funding-arb.d.ts +2 -0
  289. package/dist/lib/strategy/funding-arb.js +23 -0
  290. package/dist/lib/strategy/interface.d.ts +23 -0
  291. package/dist/lib/strategy/interface.js +1 -0
  292. package/dist/lib/strategy/registry.d.ts +4 -0
  293. package/dist/lib/strategy/registry.js +10 -0
  294. package/dist/lib/telemetry.d.ts +25 -0
  295. package/dist/lib/telemetry.js +101 -0
  296. package/dist/lib/trace-queries.d.ts +20 -0
  297. package/dist/lib/trace-queries.js +133 -0
  298. package/dist/lib/trace.d.ts +1 -0
  299. package/dist/lib/trace.js +4 -0
  300. package/dist/lib/trade-reputation.d.ts +6 -0
  301. package/dist/lib/trade-reputation.js +99 -0
  302. package/dist/lib/ui-tokens.d.ts +21 -0
  303. package/dist/lib/ui-tokens.js +26 -0
  304. package/dist/lib/validate.d.ts +39 -0
  305. package/dist/lib/validate.js +108 -0
  306. package/dist/lib/validation.d.ts +9 -0
  307. package/dist/lib/validation.js +64 -0
  308. package/dist/server/cache.d.ts +38 -0
  309. package/dist/server/cache.js +56 -0
  310. package/dist/server/index.d.ts +2 -0
  311. package/dist/server/index.js +89 -0
  312. package/dist/server/ipc.d.ts +18 -0
  313. package/dist/server/ipc.js +159 -0
  314. package/dist/server/subscriptions.d.ts +18 -0
  315. package/dist/server/subscriptions.js +114 -0
  316. package/package.json +124 -0
@@ -0,0 +1,936 @@
1
+ /**
2
+ * Orderly Network Adapter
3
+ * Implements PerpDEXAdapter interface for Orderly Network
4
+ *
5
+ * API Docs: https://orderly.network/docs
6
+ * Base URL: https://api-evm.orderly.org/v1
7
+ */
8
+ import { KeyPair } from "near-api-js";
9
+ import { secp256k1 } from "@noble/curves/secp256k1";
10
+ import { keccak_256 } from "@noble/hashes/sha3";
11
+ import { bytesToHex } from "@noble/hashes/utils";
12
+ import { registerAdapter, } from "./interface.js";
13
+ import { fetchWithTimeout } from "../lib/fetch.js";
14
+ import { createLogger } from "../lib/logger.js";
15
+ import { MAJOR_MARKETS } from "../lib/constants.js";
16
+ import { isRecord, pickArray, pickObject, pickUnknown, pickString, pickNumber, pickBoolean, toTimestampMs } from "./utils.js";
17
+ const log = createLogger("orderly");
18
+ export class OrderlyAdapter {
19
+ info = {
20
+ id: "orderly",
21
+ name: "Orderly Network",
22
+ type: "dex",
23
+ chains: ["arbitrum", "optimism", "polygon", "base"],
24
+ features: {
25
+ spot: true,
26
+ perp: true,
27
+ margin: true,
28
+ crossMargin: true,
29
+ isolatedMargin: false,
30
+ stopOrders: true,
31
+ takeProfitOrders: true,
32
+ postOnly: true,
33
+ reduceOnly: true,
34
+ subaccounts: false,
35
+ modifyOrders: false,
36
+ batchOrders: true,
37
+ cancelAllAfter: true,
38
+ publicTrades: true,
39
+ fundingHistory: true,
40
+ orderHistory: true,
41
+ mmp: false,
42
+ twapOrders: false,
43
+ },
44
+ urls: {
45
+ app: "https://app.orderly.network",
46
+ api: "https://api-evm.orderly.org/v1",
47
+ docs: "https://orderly.network/docs",
48
+ testnet: "https://testnet-api.orderly.org/v1",
49
+ },
50
+ implementation: {
51
+ marketData: "full",
52
+ authenticatedReads: "full",
53
+ orderLifecycle: "full",
54
+ orderCancellation: "full",
55
+ subscriptions: "full",
56
+ advancedTrading: "full",
57
+ },
58
+ };
59
+ baseUrl = "https://api-evm.orderly.org/v1";
60
+ config = null;
61
+ connected = false;
62
+ marketsCache = null;
63
+ subscriptionTimers = new Set();
64
+ async connect(config) {
65
+ this.config = config;
66
+ const configuredBase = config.credentials?.restUrl;
67
+ const network = config.credentials?.network;
68
+ if (configuredBase) {
69
+ this.baseUrl = configuredBase.replace(/\/$/, "");
70
+ }
71
+ else if (network === "testnet" || (network !== "mainnet" && config.testnet)) {
72
+ this.baseUrl = "https://testnet-api.orderly.org/v1";
73
+ }
74
+ else {
75
+ this.baseUrl = "https://api-evm.orderly.org/v1";
76
+ }
77
+ await this.fetchMarkets();
78
+ this.connected = true;
79
+ }
80
+ async disconnect() {
81
+ for (const timer of this.subscriptionTimers) {
82
+ clearInterval(timer);
83
+ }
84
+ this.subscriptionTimers.clear();
85
+ this.config = null;
86
+ this.marketsCache = null;
87
+ this.connected = false;
88
+ }
89
+ isConnected() {
90
+ return this.connected;
91
+ }
92
+ // --------------------------------------------------------------------------
93
+ // Private API helpers
94
+ // --------------------------------------------------------------------------
95
+ async fetchPublic(endpoint, options) {
96
+ const url = `${this.baseUrl}${endpoint}`;
97
+ log.debug(`Fetching ${endpoint}`);
98
+ return fetchWithTimeout(url, options);
99
+ }
100
+ async fetchPrivate(args) {
101
+ const auth = this.getAuthConfig(!!args.signedPayload);
102
+ let payload = args.body;
103
+ const query = { ...(args.query ?? {}) };
104
+ if (args.signedPayload) {
105
+ if (!auth.tradingSecret) {
106
+ throw new Error("Orderly write operations require ORDERLY_TRADING_SECRET");
107
+ }
108
+ const withSignature = {
109
+ ...args.signedPayload,
110
+ };
111
+ withSignature.signature = this.signTradingPayload(withSignature, auth.tradingSecret);
112
+ if (args.method === "DELETE") {
113
+ for (const [key, value] of Object.entries(withSignature)) {
114
+ query[key] = stringifyValue(value);
115
+ }
116
+ }
117
+ else {
118
+ payload = withSignature;
119
+ }
120
+ }
121
+ const queryString = this.buildQueryString(query);
122
+ const endpointPath = `${args.endpoint}${queryString}`;
123
+ const signingPath = `/v1${endpointPath}`;
124
+ const includeQuery = args.method === "GET" || (args.method === "DELETE" && !payload);
125
+ const signaturePayload = includeQuery ? "" : payload ? JSON.stringify(payload) : "";
126
+ const headers = this.buildSignedHeaders({
127
+ method: args.method,
128
+ signingPath,
129
+ signaturePayload,
130
+ auth,
131
+ includeTradingKey: !!args.signedPayload,
132
+ });
133
+ const url = `${this.baseUrl}${endpointPath}`;
134
+ const response = await fetchWithTimeout(url, {
135
+ method: args.method,
136
+ headers,
137
+ body: payload ? JSON.stringify(payload) : undefined,
138
+ });
139
+ if (isRecord(response) && response.success === false) {
140
+ const message = (typeof response.message === "string" && response.message) || "Orderly request failed";
141
+ throw new Error(message);
142
+ }
143
+ return response;
144
+ }
145
+ async fetchMarkets() {
146
+ if (this.marketsCache)
147
+ return this.marketsCache;
148
+ const response = await this.fetchPublic("/public/info");
149
+ if (!response.success) {
150
+ throw new Error("Failed to fetch Orderly markets");
151
+ }
152
+ this.marketsCache = response.data.rows.filter((m) => m.symbol.startsWith("PERP_"));
153
+ return this.marketsCache;
154
+ }
155
+ getAuthConfig(requireTrading) {
156
+ const credentials = this.config?.credentials;
157
+ const accountId = credentials?.orderlyAccountId ?? credentials?.accountAddress ?? credentials?.subaccountId;
158
+ const key = credentials?.orderlyKey ?? credentials?.apiKey;
159
+ const secret = credentials?.orderlySecret ?? credentials?.apiSecret;
160
+ const tradingSecret = credentials?.orderlyTradingSecret ??
161
+ credentials?.passphrase ??
162
+ (credentials?.privateKey ? credentials.privateKey : undefined);
163
+ const tradingKey = credentials?.orderlyTradingKey ??
164
+ credentials?.subaccountId ??
165
+ (tradingSecret ? this.deriveTradingPublicKey(tradingSecret) : undefined);
166
+ if (!accountId || !key || !secret) {
167
+ throw new Error("Orderly authenticated endpoints require ORDERLY_ACCOUNT_ID, ORDERLY_KEY, and ORDERLY_SECRET");
168
+ }
169
+ if (requireTrading && !tradingSecret) {
170
+ throw new Error("Orderly trading requires ORDERLY_TRADING_SECRET");
171
+ }
172
+ return {
173
+ accountId,
174
+ key,
175
+ secret,
176
+ tradingSecret,
177
+ tradingKey,
178
+ };
179
+ }
180
+ buildSignedHeaders(args) {
181
+ const timestamp = Date.now().toString();
182
+ const signingMessage = `${timestamp}${args.method.toUpperCase()}${args.signingPath}${args.signaturePayload}`;
183
+ const orderlyKeyPair = KeyPair.fromString(args.auth.secret);
184
+ const signature = Buffer.from(orderlyKeyPair.sign(Buffer.from(signingMessage)).signature).toString("base64");
185
+ const headers = {
186
+ Accept: "application/json",
187
+ "Content-Type": "application/json",
188
+ "orderly-account-id": args.auth.accountId,
189
+ "orderly-key": args.auth.key,
190
+ "orderly-signature": signature,
191
+ "orderly-timestamp": timestamp,
192
+ };
193
+ if (args.includeTradingKey && args.auth.tradingKey) {
194
+ headers["orderly-trading-key"] = args.auth.tradingKey;
195
+ }
196
+ return headers;
197
+ }
198
+ signTradingPayload(payload, tradingSecret) {
199
+ const canonical = Object.keys(payload)
200
+ .sort()
201
+ .map((key) => `${key}=${stringifyValue(payload[key])}`)
202
+ .join("&");
203
+ const privateKeyHex = stripHexPrefix(tradingSecret);
204
+ const msgHash = keccak_256(new TextEncoder().encode(canonical));
205
+ const sig = secp256k1.sign(msgHash, privateKeyHex, { lowS: true });
206
+ const r = sig.r.toString(16).padStart(64, "0");
207
+ const s = sig.s.toString(16).padStart(64, "0");
208
+ const recovery = sig.recovery ?? 0;
209
+ return `${r}${s}0${recovery}`;
210
+ }
211
+ deriveTradingPublicKey(tradingSecret) {
212
+ const pub = secp256k1.getPublicKey(stripHexPrefix(tradingSecret), false);
213
+ return bytesToHex(pub).slice(2); // remove 04 prefix
214
+ }
215
+ buildQueryString(query) {
216
+ const params = new URLSearchParams();
217
+ for (const [key, value] of Object.entries(query)) {
218
+ if (value === undefined || value === null)
219
+ continue;
220
+ params.set(key, String(value));
221
+ }
222
+ const encoded = params.toString();
223
+ return encoded ? `?${encoded}` : "";
224
+ }
225
+ canUseReadAuth() {
226
+ const credentials = this.config?.credentials;
227
+ return !!(credentials &&
228
+ (credentials.orderlyAccountId ?? credentials.accountAddress) &&
229
+ (credentials.orderlyKey ?? credentials.apiKey) &&
230
+ (credentials.orderlySecret ?? credentials.apiSecret));
231
+ }
232
+ // --------------------------------------------------------------------------
233
+ // Public Market Data
234
+ // --------------------------------------------------------------------------
235
+ async getMarkets() {
236
+ const markets = await this.fetchMarkets();
237
+ return markets.map((m) => {
238
+ const [_, baseAsset = m.symbol, quoteAsset = "USDC"] = m.symbol.split("_");
239
+ const maxLeverage = Math.floor(1 / m.base_imr) || 20;
240
+ return {
241
+ symbol: this.toStandardSymbol(m.symbol),
242
+ baseAsset,
243
+ quoteAsset,
244
+ type: "perp",
245
+ maxLeverage,
246
+ minSize: m.base_tick.toString(),
247
+ tickSize: m.quote_tick.toString(),
248
+ fundingInterval: m.funding_period,
249
+ isActive: true,
250
+ };
251
+ });
252
+ }
253
+ async getMarket(symbol) {
254
+ const markets = await this.getMarkets();
255
+ const normalized = this.normalizeSymbol(symbol);
256
+ return markets.find((m) => m.symbol === normalized) ?? null;
257
+ }
258
+ async getTicker(market) {
259
+ const orderlySymbol = this.toOrderlySymbol(market);
260
+ const markPriceData = await this.fetchPublic(`/public/futures/${orderlySymbol}`);
261
+ if (!markPriceData.success) {
262
+ throw new Error(`Market ${market} not found`);
263
+ }
264
+ const mp = markPriceData.data;
265
+ const change24h = mp["24h_close"] && mp["24h_open"]
266
+ ? (((mp["24h_close"] - mp["24h_open"]) / mp["24h_open"]) * 100).toFixed(2)
267
+ : "0";
268
+ // Fetch real bid/ask from orderbook
269
+ let bid = mp.mark_price.toString();
270
+ let ask = mp.mark_price.toString();
271
+ try {
272
+ const book = await this.fetchPublic(`/public/orderbook/${orderlySymbol}?max_level=1`);
273
+ const data = pickObject(book, ["data", "result", "results.0"]) ?? {};
274
+ const bids = this.toBookLevels(pickUnknown(data, ["bids"]));
275
+ const asks = this.toBookLevels(pickUnknown(data, ["asks"]));
276
+ if (bids.length > 0)
277
+ bid = bids[0].price;
278
+ if (asks.length > 0)
279
+ ask = asks[0].price;
280
+ }
281
+ catch {
282
+ // Fall back to mark price if orderbook unavailable
283
+ }
284
+ return {
285
+ market: this.toStandardSymbol(orderlySymbol),
286
+ lastPrice: mp["24h_close"]?.toString() || mp.mark_price.toString(),
287
+ markPrice: mp.mark_price.toString(),
288
+ indexPrice: mp.index_price.toString(),
289
+ bid,
290
+ ask,
291
+ volume24h: mp["24h_amount"]?.toString() || "0",
292
+ change24h,
293
+ high24h: mp["24h_high"]?.toString() || "0",
294
+ low24h: mp["24h_low"]?.toString() || "0",
295
+ openInterest: mp.open_interest.toString(),
296
+ fundingRate: mp.est_funding_rate.toString(),
297
+ timestamp: Date.now(),
298
+ };
299
+ }
300
+ async getTickers() {
301
+ const markets = await this.fetchMarkets();
302
+ // Use batch public/futures endpoint to avoid N+1 requests
303
+ const tickers = [];
304
+ try {
305
+ const response = await this.fetchPublic("/public/futures");
306
+ if (response.success && response.data?.rows) {
307
+ const markPriceMap = new Map();
308
+ for (const mp of response.data.rows) {
309
+ markPriceMap.set(mp.symbol, mp);
310
+ }
311
+ for (const m of markets) {
312
+ const mp = markPriceMap.get(m.symbol);
313
+ if (!mp)
314
+ continue;
315
+ const change24h = mp["24h_close"] && mp["24h_open"]
316
+ ? (((mp["24h_close"] - mp["24h_open"]) / mp["24h_open"]) * 100).toFixed(2)
317
+ : "0";
318
+ tickers.push({
319
+ market: this.toStandardSymbol(m.symbol),
320
+ lastPrice: mp["24h_close"]?.toString() || mp.mark_price.toString(),
321
+ markPrice: mp.mark_price.toString(),
322
+ indexPrice: mp.index_price.toString(),
323
+ bid: mp.mark_price.toString(),
324
+ ask: mp.mark_price.toString(),
325
+ volume24h: mp["24h_amount"]?.toString() || "0",
326
+ change24h,
327
+ high24h: mp["24h_high"]?.toString() || "0",
328
+ low24h: mp["24h_low"]?.toString() || "0",
329
+ openInterest: mp.open_interest.toString(),
330
+ fundingRate: mp.est_funding_rate.toString(),
331
+ timestamp: Date.now(),
332
+ });
333
+ }
334
+ return tickers;
335
+ }
336
+ }
337
+ catch {
338
+ // Fall back to individual fetches
339
+ }
340
+ // Fallback: sequential per-market
341
+ for (const m of markets.slice(0, 80)) {
342
+ try {
343
+ tickers.push(await this.getTicker(m.symbol));
344
+ }
345
+ catch {
346
+ // Best effort across all symbols.
347
+ }
348
+ }
349
+ return tickers;
350
+ }
351
+ async getOrderBook(market, depth = 20) {
352
+ const orderlySymbol = this.toOrderlySymbol(market);
353
+ // Orderly has a public orderbook endpoint
354
+ const response = this.canUseReadAuth()
355
+ ? await this.fetchPrivate({
356
+ method: "GET",
357
+ endpoint: `/orderbook/${orderlySymbol}`,
358
+ query: { max_level: depth },
359
+ })
360
+ : await this.fetchPublic(`/public/orderbook/${orderlySymbol}?max_level=${depth}`);
361
+ const data = pickObject(response, ["data", "result", "results.0"]) ?? {};
362
+ const bids = this.toBookLevels(pickUnknown(data, ["bids"]));
363
+ const asks = this.toBookLevels(pickUnknown(data, ["asks"]));
364
+ return {
365
+ market: this.toStandardSymbol(orderlySymbol),
366
+ bids: bids.slice(0, depth),
367
+ asks: asks.slice(0, depth),
368
+ timestamp: toTimestampMs(pickUnknown(data, ["timestamp", "updated_time"]), Date.now()),
369
+ };
370
+ }
371
+ async getFundingRate(market) {
372
+ const orderlySymbol = this.toOrderlySymbol(market);
373
+ const markPriceData = await this.fetchPublic(`/public/futures/${orderlySymbol}`);
374
+ if (!markPriceData.success) {
375
+ throw new Error(`Funding rate not available for ${market}`);
376
+ }
377
+ const mp = markPriceData.data;
378
+ return {
379
+ market: this.toStandardSymbol(orderlySymbol),
380
+ rate: mp.est_funding_rate.toString(),
381
+ nextFundingTime: mp.next_funding_time,
382
+ timestamp: Date.now(),
383
+ };
384
+ }
385
+ async getFundingRates() {
386
+ // Use batch futures endpoint to get all funding rates at once
387
+ try {
388
+ const response = await this.fetchPublic("/public/futures");
389
+ if (response.success && response.data?.rows) {
390
+ return response.data.rows.map((mp) => ({
391
+ market: this.toStandardSymbol(mp.symbol),
392
+ rate: mp.est_funding_rate.toString(),
393
+ nextFundingTime: mp.next_funding_time,
394
+ timestamp: Date.now(),
395
+ }));
396
+ }
397
+ }
398
+ catch {
399
+ // Fall back to individual fetches
400
+ }
401
+ const rates = [];
402
+ for (const market of MAJOR_MARKETS) {
403
+ try {
404
+ rates.push(await this.getFundingRate(market));
405
+ }
406
+ catch {
407
+ // Best effort across majors.
408
+ }
409
+ }
410
+ return rates;
411
+ }
412
+ // --------------------------------------------------------------------------
413
+ // Account Data (requires auth)
414
+ // --------------------------------------------------------------------------
415
+ async getPositions() {
416
+ const response = await this.fetchPrivate({
417
+ method: "GET",
418
+ endpoint: "/positions",
419
+ });
420
+ const rows = pickArray(response, ["data.rows", "rows", "data", "results"]);
421
+ return rows
422
+ .map((row) => {
423
+ const symbol = this.toStandardSymbol(pickString(row, ["symbol", "market", "instrument_name"]) ?? "");
424
+ if (!symbol)
425
+ return null;
426
+ const quantity = pickNumber(row, ["position_qty", "quantity", "size", "holding"], 0);
427
+ if (quantity === 0)
428
+ return null;
429
+ const side = quantity < 0 ? "short" : "long";
430
+ const absSize = Math.abs(quantity);
431
+ return {
432
+ market: symbol,
433
+ side,
434
+ size: absSize.toString(),
435
+ entryPrice: pickString(row, ["average_open_price", "entry_price", "avg_price"]) ?? "0",
436
+ markPrice: pickString(row, ["mark_price", "close_price", "last_price"]) ?? "0",
437
+ liquidationPrice: pickString(row, ["est_liq_price", "liquidation_price"]) ?? null,
438
+ unrealizedPnl: pickString(row, ["unrealized_pnl", "pnl_24_h"]) ?? "0",
439
+ realizedPnl: pickString(row, ["realized_pnl"]) ?? "0",
440
+ leverage: pickNumber(row, ["leverage"], 1),
441
+ marginType: "cross",
442
+ margin: pickString(row, ["position_margin", "initial_margin"]) ?? "0",
443
+ timestamp: toTimestampMs(pickUnknown(row, ["updated_time", "created_time", "timestamp"]), Date.now()),
444
+ };
445
+ })
446
+ .filter((row) => row !== null);
447
+ }
448
+ async getPosition(market) {
449
+ const positions = await this.getPositions();
450
+ const symbol = this.normalizeSymbol(market);
451
+ return positions.find((p) => p.market === symbol) ?? null;
452
+ }
453
+ async getOrders(market) {
454
+ const response = await this.fetchPrivate({
455
+ method: "GET",
456
+ endpoint: "/orders",
457
+ query: {
458
+ symbol: market ? this.toOrderlySymbol(market) : undefined,
459
+ size: 500,
460
+ },
461
+ });
462
+ const rows = pickArray(response, ["data.rows", "rows", "data", "results"]);
463
+ return rows
464
+ .map((row) => this.toOrder(row))
465
+ .filter((row) => row !== null);
466
+ }
467
+ async getOrder(orderId) {
468
+ const response = await this.fetchPrivate({
469
+ method: "GET",
470
+ endpoint: `/order/${encodeURIComponent(orderId)}`,
471
+ });
472
+ const row = pickObject(response, ["data", "result", "results.0"]) ??
473
+ pickArray(response, ["rows", "data.rows"])[0] ??
474
+ null;
475
+ if (row) {
476
+ const mapped = this.toOrder(row);
477
+ if (mapped)
478
+ return mapped;
479
+ }
480
+ const orders = await this.getOrders();
481
+ return orders.find((o) => o.id === orderId) ?? null;
482
+ }
483
+ async getBalances() {
484
+ const response = await this.fetchPrivate({
485
+ method: "GET",
486
+ endpoint: "/client/holding",
487
+ query: { all: "true" },
488
+ });
489
+ // Fetch positions to compute unrealized PnL
490
+ let totalUnrealizedPnl = 0;
491
+ let totalMarginUsed = 0;
492
+ try {
493
+ const positions = await this.getPositions();
494
+ for (const p of positions) {
495
+ totalUnrealizedPnl += parseFloat(p.unrealizedPnl || "0");
496
+ totalMarginUsed += parseFloat(p.margin || "0");
497
+ }
498
+ }
499
+ catch {
500
+ // Best effort - continue without position data
501
+ }
502
+ const rows = pickArray(response, ["data.holding", "data.rows", "rows", "results"]);
503
+ if (rows.length > 0) {
504
+ return rows.map((row) => {
505
+ const holding = pickNumber(row, ["holding", "balance"], 0);
506
+ const frozen = pickNumber(row, ["frozen", "locked"], 0);
507
+ const total = holding + frozen;
508
+ return {
509
+ asset: pickString(row, ["token", "asset", "currency"]) ?? "USDC",
510
+ total: total.toString(),
511
+ available: holding.toString(),
512
+ locked: frozen.toString(),
513
+ unrealizedPnl: totalUnrealizedPnl.toString(),
514
+ marginUsed: totalMarginUsed > 0 ? totalMarginUsed.toString() : frozen.toString(),
515
+ };
516
+ });
517
+ }
518
+ const info = await this.fetchPrivate({ method: "GET", endpoint: "/client/info" });
519
+ const data = pickObject(info, ["data", "result", "results.0"]) ?? {};
520
+ const total = pickString(data, ["equity", "balance"]) ?? "0";
521
+ const available = pickString(data, ["free_collateral", "available_balance"]) ?? total;
522
+ return [
523
+ {
524
+ asset: "USDC",
525
+ total,
526
+ available,
527
+ locked: totalMarginUsed > 0 ? totalMarginUsed.toString() : "0",
528
+ unrealizedPnl: totalUnrealizedPnl.toString(),
529
+ marginUsed: totalMarginUsed > 0 ? totalMarginUsed.toString() : "0",
530
+ },
531
+ ];
532
+ }
533
+ async getTrades(market, limit = 100) {
534
+ const response = await this.fetchPrivate({
535
+ method: "GET",
536
+ endpoint: "/trades",
537
+ query: {
538
+ symbol: market ? this.toOrderlySymbol(market) : undefined,
539
+ size: Math.max(1, Math.min(limit, 500)),
540
+ },
541
+ });
542
+ const rows = pickArray(response, ["data.rows", "rows", "data", "results"]);
543
+ return rows
544
+ .map((row) => {
545
+ const symbol = this.toStandardSymbol(pickString(row, ["symbol"]) ?? "");
546
+ if (!symbol)
547
+ return null;
548
+ const sideRaw = pickString(row, ["side"])?.toLowerCase() ?? "buy";
549
+ const side = sideRaw === "sell" ? "short" : "long";
550
+ const orderId = pickString(row, ["order_id"]);
551
+ return {
552
+ id: pickString(row, ["id", "trade_id", "execution_id"]) ?? `${symbol}-${Date.now()}`,
553
+ market: symbol,
554
+ side,
555
+ price: pickString(row, ["executed_price", "price"]) ?? "0",
556
+ size: pickString(row, ["executed_quantity", "size", "quantity"]) ?? "0",
557
+ fee: pickString(row, ["fee", "total_fee"]) ?? "0",
558
+ feeAsset: pickString(row, ["fee_asset", "fee_currency"]) ?? "USDC",
559
+ timestamp: toTimestampMs(pickUnknown(row, ["executed_timestamp", "created_time", "timestamp"]), Date.now()),
560
+ ...(orderId ? { orderId } : {}),
561
+ };
562
+ })
563
+ .filter((row) => row !== null);
564
+ }
565
+ // --------------------------------------------------------------------------
566
+ // Trading
567
+ // --------------------------------------------------------------------------
568
+ async placeOrder(params) {
569
+ const symbol = this.toOrderlySymbol(params.market);
570
+ const payload = {
571
+ symbol,
572
+ side: params.side === "long" ? "BUY" : "SELL",
573
+ order_type: this.toOrderlyOrderType(params.type),
574
+ order_quantity: params.size,
575
+ client_order_id: params.clientOrderId,
576
+ };
577
+ if (params.type !== "market") {
578
+ if (!params.price) {
579
+ throw new Error("Order price is required for non-market orders");
580
+ }
581
+ payload.order_price = params.price;
582
+ }
583
+ const response = await this.fetchPrivate({
584
+ method: "POST",
585
+ endpoint: "/order",
586
+ body: payload,
587
+ signedPayload: payload,
588
+ });
589
+ const data = pickObject(response, ["data", "result", "results.0"]) ?? payload;
590
+ const mapped = this.toOrder(data);
591
+ if (mapped) {
592
+ return mapped;
593
+ }
594
+ return {
595
+ id: pickString(data, ["order_id", "id", "client_order_id"]) ?? `${Date.now()}`,
596
+ market: this.toStandardSymbol(symbol),
597
+ side: params.side,
598
+ type: params.type,
599
+ size: params.size,
600
+ price: params.price ?? null,
601
+ filled: "0",
602
+ remaining: params.size,
603
+ status: "open",
604
+ reduceOnly: params.reduceOnly ?? false,
605
+ postOnly: params.postOnly ?? false,
606
+ timestamp: Date.now(),
607
+ triggerPrice: params.triggerPrice,
608
+ };
609
+ }
610
+ async cancelOrder(params) {
611
+ const payload = {};
612
+ if (params.orderId) {
613
+ payload.order_id = params.orderId;
614
+ }
615
+ if (params.clientOrderId) {
616
+ payload.client_order_id = params.clientOrderId;
617
+ }
618
+ if (Object.keys(payload).length === 0) {
619
+ throw new Error("cancelOrder requires orderId or clientOrderId");
620
+ }
621
+ await this.fetchPrivate({
622
+ method: "DELETE",
623
+ endpoint: "/order",
624
+ query: payload,
625
+ signedPayload: payload,
626
+ });
627
+ return true;
628
+ }
629
+ async cancelAllOrders(market) {
630
+ const payload = {};
631
+ if (market) {
632
+ payload.symbol = this.toOrderlySymbol(market);
633
+ }
634
+ const response = await this.fetchPrivate({
635
+ method: "DELETE",
636
+ endpoint: "/orders",
637
+ query: payload,
638
+ signedPayload: payload,
639
+ });
640
+ const count = pickNumber(response, ["data.count", "count", "cancelled"], NaN);
641
+ if (Number.isFinite(count) && count >= 0) {
642
+ return Math.floor(count);
643
+ }
644
+ return market ? 1 : 0;
645
+ }
646
+ async setLeverage(_market, leverage) {
647
+ const payload = { leverage };
648
+ await this.fetchPrivate({
649
+ method: "POST",
650
+ endpoint: "/client/leverage",
651
+ body: payload,
652
+ signedPayload: payload,
653
+ });
654
+ }
655
+ async setMarginType(_market, type) {
656
+ if (type !== "cross") {
657
+ throw new Error("Orderly currently supports cross margin only");
658
+ }
659
+ }
660
+ // --------------------------------------------------------------------------
661
+ // Advanced Trading
662
+ // --------------------------------------------------------------------------
663
+ async modifyOrder(_params) {
664
+ throw new Error("Orderly does not support order modification. Cancel and replace instead.");
665
+ }
666
+ async batchPlaceOrders(paramsList) {
667
+ // Orderly has batch create order endpoint
668
+ const results = [];
669
+ for (const params of paramsList) {
670
+ results.push(await this.placeOrder(params));
671
+ }
672
+ return results;
673
+ }
674
+ async batchCancelOrders(paramsList) {
675
+ const results = [];
676
+ for (const params of paramsList) {
677
+ try {
678
+ results.push(await this.cancelOrder(params));
679
+ }
680
+ catch {
681
+ results.push(false);
682
+ }
683
+ }
684
+ return results;
685
+ }
686
+ async cancelAllAfter(timeoutMs) {
687
+ // Orderly cancel_all_after endpoint
688
+ const payload = {
689
+ cancel_after: Math.max(0, Math.floor(timeoutMs / 1000)),
690
+ };
691
+ await this.fetchPrivate({
692
+ method: "POST",
693
+ endpoint: "/cancel_all_after",
694
+ body: payload,
695
+ signedPayload: payload,
696
+ });
697
+ }
698
+ async getOrderHistory(market, limit = 100) {
699
+ const response = await this.fetchPrivate({
700
+ method: "GET",
701
+ endpoint: "/orders",
702
+ query: {
703
+ symbol: market ? this.toOrderlySymbol(market) : undefined,
704
+ size: Math.max(1, Math.min(limit, 500)),
705
+ status: "COMPLETED",
706
+ },
707
+ });
708
+ const rows = pickArray(response, ["data.rows", "rows", "data", "results"]);
709
+ return rows
710
+ .map((row) => this.toOrder(row))
711
+ .filter((row) => row !== null);
712
+ }
713
+ async getFundingHistory(market, limit = 100) {
714
+ const response = await this.fetchPrivate({
715
+ method: "GET",
716
+ endpoint: "/funding_fee/history",
717
+ query: {
718
+ symbol: market ? this.toOrderlySymbol(market) : undefined,
719
+ size: Math.max(1, Math.min(limit, 500)),
720
+ },
721
+ });
722
+ const rows = pickArray(response, ["data.rows", "rows", "data", "results"]);
723
+ return rows.map((row) => ({
724
+ market: this.toStandardSymbol(pickString(row, ["symbol"]) ?? ""),
725
+ amount: pickString(row, ["funding_fee", "fee", "payment"]) ?? "0",
726
+ rate: pickString(row, ["funding_rate", "rate"]) ?? "0",
727
+ timestamp: toTimestampMs(pickUnknown(row, ["created_time", "timestamp"]), Date.now()),
728
+ }));
729
+ }
730
+ async getPublicTrades(market, limit = 100) {
731
+ const orderlySymbol = this.toOrderlySymbol(market);
732
+ const response = await this.fetchPublic(`/public/market_trades?symbol=${orderlySymbol}&limit=${Math.min(limit, 100)}`);
733
+ const rows = pickArray(response, ["data.rows", "rows", "data", "results"]);
734
+ return rows.map((row) => ({
735
+ id: pickString(row, ["id", "trade_id"]) ?? `${Date.now()}`,
736
+ market: this.toStandardSymbol(orderlySymbol),
737
+ side: (pickString(row, ["side"])?.toLowerCase() === "sell" ? "short" : "long"),
738
+ price: pickString(row, ["executed_price", "price"]) ?? "0",
739
+ size: pickString(row, ["executed_quantity", "size", "quantity"]) ?? "0",
740
+ timestamp: toTimestampMs(pickUnknown(row, ["executed_timestamp", "timestamp"]), Date.now()),
741
+ }));
742
+ }
743
+ // --------------------------------------------------------------------------
744
+ // Market Maker Protection
745
+ // --------------------------------------------------------------------------
746
+ async setMMP(_config) {
747
+ throw new Error("Orderly does not support Market Maker Protection (MMP)");
748
+ }
749
+ async getMMP(_market) {
750
+ throw new Error("Orderly does not support Market Maker Protection (MMP)");
751
+ }
752
+ async resetMMP(_market) {
753
+ throw new Error("Orderly does not support Market Maker Protection (MMP)");
754
+ }
755
+ // --------------------------------------------------------------------------
756
+ // TWAP Orders
757
+ // --------------------------------------------------------------------------
758
+ async placeTWAP(_params) {
759
+ throw new Error("Orderly does not support TWAP orders");
760
+ }
761
+ async cancelTWAP(_twapId) {
762
+ throw new Error("Orderly does not support TWAP orders");
763
+ }
764
+ async getTWAPStatus(_twapId) {
765
+ throw new Error("Orderly does not support TWAP orders");
766
+ }
767
+ // --------------------------------------------------------------------------
768
+ // Margin Management
769
+ // --------------------------------------------------------------------------
770
+ async updateIsolatedMargin(_market, _amount) {
771
+ throw new Error("Orderly does not support isolated margin adjustment");
772
+ }
773
+ // --------------------------------------------------------------------------
774
+ // Subscriptions (polling)
775
+ // --------------------------------------------------------------------------
776
+ subscribe(callbacks) {
777
+ const timer = setInterval(async () => {
778
+ try {
779
+ if (callbacks.onPositions)
780
+ callbacks.onPositions(await this.getPositions());
781
+ if (callbacks.onOrders)
782
+ callbacks.onOrders(await this.getOrders());
783
+ if (callbacks.onBalances)
784
+ callbacks.onBalances(await this.getBalances());
785
+ }
786
+ catch (err) {
787
+ callbacks.onError?.(err instanceof Error ? err : new Error(String(err)));
788
+ }
789
+ }, 3000);
790
+ this.subscriptionTimers.add(timer);
791
+ return () => {
792
+ clearInterval(timer);
793
+ this.subscriptionTimers.delete(timer);
794
+ };
795
+ }
796
+ subscribeOrderBook(market, callback) {
797
+ const normalized = this.normalizeSymbol(market);
798
+ const timer = setInterval(async () => {
799
+ try {
800
+ callback(await this.getOrderBook(normalized));
801
+ }
802
+ catch {
803
+ // Ignore transient polling errors.
804
+ }
805
+ }, 1500);
806
+ this.subscriptionTimers.add(timer);
807
+ return () => {
808
+ clearInterval(timer);
809
+ this.subscriptionTimers.delete(timer);
810
+ };
811
+ }
812
+ subscribeTicker(market, callback) {
813
+ const normalized = this.normalizeSymbol(market);
814
+ const timer = setInterval(async () => {
815
+ try {
816
+ callback(await this.getTicker(normalized));
817
+ }
818
+ catch {
819
+ // Ignore transient polling errors.
820
+ }
821
+ }, 1200);
822
+ this.subscriptionTimers.add(timer);
823
+ return () => {
824
+ clearInterval(timer);
825
+ this.subscriptionTimers.delete(timer);
826
+ };
827
+ }
828
+ // --------------------------------------------------------------------------
829
+ // Private Helpers
830
+ // --------------------------------------------------------------------------
831
+ toOrderlyOrderType(type) {
832
+ if (type === "market")
833
+ return "MARKET";
834
+ return "LIMIT";
835
+ }
836
+ toOrderlySymbol(symbol) {
837
+ const upper = symbol.toUpperCase().trim();
838
+ if (upper.startsWith("PERP_"))
839
+ return upper;
840
+ const base = upper
841
+ .replace("-USD-PERP", "")
842
+ .replace("-PERP", "")
843
+ .replace("/USDC", "")
844
+ .replace("/USD", "");
845
+ return `PERP_${base}_USDC`;
846
+ }
847
+ toStandardSymbol(orderlySymbol) {
848
+ const parts = orderlySymbol.split("_");
849
+ if (parts.length >= 2 && parts[0] === "PERP") {
850
+ return `${parts[1]}-PERP`;
851
+ }
852
+ if (!orderlySymbol)
853
+ return "";
854
+ const normalized = orderlySymbol.toUpperCase();
855
+ if (normalized.endsWith("-PERP"))
856
+ return normalized;
857
+ return `${normalized}-PERP`;
858
+ }
859
+ normalizeSymbol(symbol) {
860
+ const upper = symbol.toUpperCase().trim();
861
+ if (!upper)
862
+ return "";
863
+ if (upper.startsWith("PERP_")) {
864
+ return this.toStandardSymbol(upper);
865
+ }
866
+ if (upper.endsWith("-PERP"))
867
+ return upper;
868
+ return `${upper}-PERP`;
869
+ }
870
+ toOrder(row) {
871
+ const market = this.toStandardSymbol(pickString(row, ["symbol", "market", "instrument_name"]) ?? "");
872
+ if (!market)
873
+ return null;
874
+ const sideRaw = pickString(row, ["side"])?.toLowerCase() ?? "buy";
875
+ const side = sideRaw === "sell" ? "short" : "long";
876
+ const size = pickString(row, ["quantity", "order_quantity", "size"]) ?? "0";
877
+ const filled = pickString(row, ["executed", "executed_quantity", "filled"]) ?? "0";
878
+ const remaining = pickString(row, ["remaining", "leaves_qty"]) ??
879
+ Math.max(0, parseFloat(size) - parseFloat(filled)).toString();
880
+ const statusRaw = pickString(row, ["status"])?.toLowerCase() ?? "new";
881
+ const status = statusRaw.includes("cancel")
882
+ ? "cancelled"
883
+ : statusRaw.includes("filled")
884
+ ? parseFloat(remaining) > 0
885
+ ? "partial"
886
+ : "filled"
887
+ : statusRaw.includes("reject") || statusRaw.includes("expire")
888
+ ? "expired"
889
+ : "open";
890
+ const typeRaw = pickString(row, ["type", "order_type"])?.toLowerCase() ?? "limit";
891
+ const type = typeRaw.includes("market") ? "market" : "limit";
892
+ return {
893
+ id: pickString(row, ["order_id", "id", "client_order_id"]) ?? `${Date.now()}`,
894
+ market,
895
+ side,
896
+ type,
897
+ size,
898
+ price: pickString(row, ["price", "order_price"]),
899
+ filled,
900
+ remaining,
901
+ status,
902
+ reduceOnly: pickBoolean(row, ["reduce_only", "reduce_only_order"], false),
903
+ postOnly: pickBoolean(row, ["post_only"], false),
904
+ timestamp: toTimestampMs(pickUnknown(row, ["created_time", "updated_time", "timestamp"]), Date.now()),
905
+ triggerPrice: pickString(row, ["trigger_price"]) ?? undefined,
906
+ };
907
+ }
908
+ toBookLevels(value) {
909
+ if (!Array.isArray(value))
910
+ return [];
911
+ const levels = [];
912
+ for (const row of value) {
913
+ if (!Array.isArray(row) || row.length < 2)
914
+ continue;
915
+ const [price, size] = row;
916
+ const priceStr = stringifyValue(price);
917
+ const sizeStr = stringifyValue(size);
918
+ levels.push({ price: priceStr, size: sizeStr });
919
+ }
920
+ return levels;
921
+ }
922
+ }
923
+ function stripHexPrefix(value) {
924
+ return value.startsWith("0x") ? value.slice(2) : value;
925
+ }
926
+ function stringifyValue(value) {
927
+ if (typeof value === "string")
928
+ return value;
929
+ if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
930
+ return String(value);
931
+ }
932
+ return "";
933
+ }
934
+ // Register the adapter
935
+ registerAdapter("orderly", () => new OrderlyAdapter());
936
+ export default OrderlyAdapter;