openbroker 1.3.2 → 1.5.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 (169) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/auto/audit.d.ts +57 -0
  3. package/dist/auto/audit.d.ts.map +1 -0
  4. package/dist/auto/audit.js +407 -0
  5. package/dist/auto/cli.d.ts +2 -0
  6. package/dist/auto/cli.d.ts.map +1 -0
  7. package/dist/auto/cli.js +423 -0
  8. package/dist/auto/events.d.ts +11 -0
  9. package/dist/auto/events.d.ts.map +1 -0
  10. package/dist/auto/events.js +36 -0
  11. package/dist/auto/examples/dca.d.ts +4 -0
  12. package/dist/auto/examples/dca.d.ts.map +1 -0
  13. package/dist/auto/examples/dca.js +60 -0
  14. package/dist/auto/examples/funding-arb.d.ts +4 -0
  15. package/dist/auto/examples/funding-arb.d.ts.map +1 -0
  16. package/dist/auto/examples/funding-arb.js +81 -0
  17. package/dist/auto/examples/grid.d.ts +4 -0
  18. package/dist/auto/examples/grid.d.ts.map +1 -0
  19. package/dist/auto/examples/grid.js +114 -0
  20. package/dist/auto/examples/mm-maker.d.ts +4 -0
  21. package/dist/auto/examples/mm-maker.d.ts.map +1 -0
  22. package/dist/auto/examples/mm-maker.js +131 -0
  23. package/dist/auto/examples/mm-spread.d.ts +4 -0
  24. package/dist/auto/examples/mm-spread.d.ts.map +1 -0
  25. package/dist/auto/examples/mm-spread.js +119 -0
  26. package/dist/auto/examples/price-alert.d.ts +4 -0
  27. package/dist/auto/examples/price-alert.d.ts.map +1 -0
  28. package/dist/auto/examples/price-alert.js +85 -0
  29. package/dist/auto/keep-awake.d.ts +11 -0
  30. package/dist/auto/keep-awake.d.ts.map +1 -0
  31. package/dist/auto/keep-awake.js +70 -0
  32. package/dist/auto/loader.d.ts +22 -0
  33. package/dist/auto/loader.d.ts.map +1 -0
  34. package/dist/auto/loader.js +127 -0
  35. package/dist/auto/prune.d.ts +40 -0
  36. package/dist/auto/prune.d.ts.map +1 -0
  37. package/dist/auto/prune.js +204 -0
  38. package/dist/auto/registry.d.ts +24 -0
  39. package/dist/auto/registry.d.ts.map +1 -0
  40. package/dist/auto/registry.js +93 -0
  41. package/dist/auto/report.d.ts +3 -0
  42. package/dist/auto/report.d.ts.map +1 -0
  43. package/dist/auto/report.js +385 -0
  44. package/dist/auto/runtime.d.ts +33 -0
  45. package/dist/auto/runtime.d.ts.map +1 -0
  46. package/dist/auto/runtime.js +844 -0
  47. package/dist/auto/types.d.ts +236 -0
  48. package/dist/auto/types.d.ts.map +1 -0
  49. package/dist/auto/types.js +3 -0
  50. package/dist/core/client.d.ts +691 -0
  51. package/dist/core/client.d.ts.map +1 -0
  52. package/dist/core/client.js +2061 -0
  53. package/dist/core/config.d.ts +22 -0
  54. package/dist/core/config.d.ts.map +1 -0
  55. package/dist/core/config.js +143 -0
  56. package/dist/core/types.d.ts +228 -0
  57. package/dist/core/types.d.ts.map +1 -0
  58. package/dist/core/types.js +2 -0
  59. package/dist/core/utils.d.ts +61 -0
  60. package/dist/core/utils.d.ts.map +1 -0
  61. package/dist/core/utils.js +142 -0
  62. package/dist/core/ws.d.ts +121 -0
  63. package/dist/core/ws.d.ts.map +1 -0
  64. package/dist/core/ws.js +222 -0
  65. package/dist/info/account.d.ts +3 -0
  66. package/dist/info/account.d.ts.map +1 -0
  67. package/dist/info/account.js +198 -0
  68. package/dist/info/all-markets.d.ts +3 -0
  69. package/dist/info/all-markets.d.ts.map +1 -0
  70. package/dist/info/all-markets.js +272 -0
  71. package/dist/info/candles.d.ts +3 -0
  72. package/dist/info/candles.d.ts.map +1 -0
  73. package/dist/info/candles.js +120 -0
  74. package/dist/info/fees.d.ts +3 -0
  75. package/dist/info/fees.d.ts.map +1 -0
  76. package/dist/info/fees.js +87 -0
  77. package/dist/info/fills.d.ts +3 -0
  78. package/dist/info/fills.d.ts.map +1 -0
  79. package/dist/info/fills.js +105 -0
  80. package/dist/info/funding-history.d.ts +3 -0
  81. package/dist/info/funding-history.d.ts.map +1 -0
  82. package/dist/info/funding-history.js +98 -0
  83. package/dist/info/funding-scan.d.ts +3 -0
  84. package/dist/info/funding-scan.d.ts.map +1 -0
  85. package/dist/info/funding-scan.js +178 -0
  86. package/dist/info/funding.d.ts +3 -0
  87. package/dist/info/funding.d.ts.map +1 -0
  88. package/dist/info/funding.js +158 -0
  89. package/dist/info/markets.d.ts +3 -0
  90. package/dist/info/markets.d.ts.map +1 -0
  91. package/dist/info/markets.js +178 -0
  92. package/dist/info/order-status.d.ts +3 -0
  93. package/dist/info/order-status.d.ts.map +1 -0
  94. package/dist/info/order-status.js +85 -0
  95. package/dist/info/orders.d.ts +3 -0
  96. package/dist/info/orders.d.ts.map +1 -0
  97. package/dist/info/orders.js +162 -0
  98. package/dist/info/outcomes.d.ts +3 -0
  99. package/dist/info/outcomes.d.ts.map +1 -0
  100. package/dist/info/outcomes.js +175 -0
  101. package/dist/info/positions.d.ts +3 -0
  102. package/dist/info/positions.d.ts.map +1 -0
  103. package/dist/info/positions.js +127 -0
  104. package/dist/info/rate-limit.d.ts +3 -0
  105. package/dist/info/rate-limit.d.ts.map +1 -0
  106. package/dist/info/rate-limit.js +58 -0
  107. package/dist/info/search-markets.d.ts +3 -0
  108. package/dist/info/search-markets.d.ts.map +1 -0
  109. package/dist/info/search-markets.js +296 -0
  110. package/dist/info/spot.d.ts +3 -0
  111. package/dist/info/spot.d.ts.map +1 -0
  112. package/dist/info/spot.js +192 -0
  113. package/dist/info/trades.d.ts +3 -0
  114. package/dist/info/trades.d.ts.map +1 -0
  115. package/dist/info/trades.js +97 -0
  116. package/dist/lib.d.ts +14 -0
  117. package/dist/lib.d.ts.map +1 -0
  118. package/dist/lib.js +17 -0
  119. package/dist/operations/bracket.d.ts +28 -0
  120. package/dist/operations/bracket.d.ts.map +1 -0
  121. package/dist/operations/bracket.js +266 -0
  122. package/dist/operations/cancel.d.ts +3 -0
  123. package/dist/operations/cancel.d.ts.map +1 -0
  124. package/dist/operations/cancel.js +107 -0
  125. package/dist/operations/chase.d.ts +25 -0
  126. package/dist/operations/chase.d.ts.map +1 -0
  127. package/dist/operations/chase.js +215 -0
  128. package/dist/operations/limit-order.d.ts +3 -0
  129. package/dist/operations/limit-order.d.ts.map +1 -0
  130. package/dist/operations/limit-order.js +144 -0
  131. package/dist/operations/market-order.d.ts +3 -0
  132. package/dist/operations/market-order.d.ts.map +1 -0
  133. package/dist/operations/market-order.js +153 -0
  134. package/dist/operations/outcome-order.d.ts +3 -0
  135. package/dist/operations/outcome-order.d.ts.map +1 -0
  136. package/dist/operations/outcome-order.js +171 -0
  137. package/dist/operations/scale.d.ts +3 -0
  138. package/dist/operations/scale.d.ts.map +1 -0
  139. package/dist/operations/scale.js +212 -0
  140. package/dist/operations/set-tpsl.d.ts +3 -0
  141. package/dist/operations/set-tpsl.d.ts.map +1 -0
  142. package/dist/operations/set-tpsl.js +277 -0
  143. package/dist/operations/spot-order.d.ts +3 -0
  144. package/dist/operations/spot-order.d.ts.map +1 -0
  145. package/dist/operations/spot-order.js +173 -0
  146. package/dist/operations/trigger-order.d.ts +3 -0
  147. package/dist/operations/trigger-order.d.ts.map +1 -0
  148. package/dist/operations/trigger-order.js +177 -0
  149. package/dist/operations/twap-cancel.d.ts +3 -0
  150. package/dist/operations/twap-cancel.d.ts.map +1 -0
  151. package/dist/operations/twap-cancel.js +57 -0
  152. package/dist/operations/twap-status.d.ts +3 -0
  153. package/dist/operations/twap-status.d.ts.map +1 -0
  154. package/dist/operations/twap-status.js +81 -0
  155. package/dist/operations/twap.d.ts +3 -0
  156. package/dist/operations/twap.d.ts.map +1 -0
  157. package/dist/operations/twap.js +124 -0
  158. package/dist/setup/approve-builder.d.ts +3 -0
  159. package/dist/setup/approve-builder.d.ts.map +1 -0
  160. package/dist/setup/approve-builder.js +155 -0
  161. package/dist/setup/env.d.ts +4 -0
  162. package/dist/setup/env.d.ts.map +1 -0
  163. package/dist/setup/env.js +8 -0
  164. package/dist/setup/onboard.d.ts +10 -0
  165. package/dist/setup/onboard.d.ts.map +1 -0
  166. package/dist/setup/onboard.js +462 -0
  167. package/package.json +10 -4
  168. package/scripts/core/client.ts +19 -1
  169. package/scripts/core/types.ts +7 -0
@@ -0,0 +1,266 @@
1
+ #!/usr/bin/env npx tsx
2
+ // Bracket Order - Entry with Take Profit and Stop Loss
3
+ import { fileURLToPath } from 'url';
4
+ import { getClient } from '../core/client.js';
5
+ import { formatUsd, parseArgs, sleep } from '../core/utils.js';
6
+ function printUsage() {
7
+ console.log(`
8
+ Open Broker - Bracket Order
9
+ ===========================
10
+
11
+ Execute an entry order with automatic take-profit and stop-loss orders.
12
+ Creates a complete trade setup in one command.
13
+
14
+ Usage:
15
+ npx tsx scripts/operations/bracket.ts --coin <COIN> --side <buy|sell> --size <SIZE> --tp <PCT> --sl <PCT>
16
+
17
+ Options:
18
+ --coin Asset to trade (e.g., ETH, BTC)
19
+ --side Entry side: buy (long) or sell (short)
20
+ --size Position size in base asset
21
+ --entry Entry type: market or limit (default: market)
22
+ --price Entry price (required if --entry limit)
23
+ --tp Take profit distance in % from entry
24
+ --sl Stop loss distance in % from entry
25
+ --slippage Slippage for market entry in bps (default: 50)
26
+ --leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
27
+ --dry Dry run - show bracket plan without executing
28
+
29
+ Take Profit / Stop Loss:
30
+ For LONG (buy): TP is above entry, SL is below entry
31
+ For SHORT (sell): TP is below entry, SL is above entry
32
+
33
+ Examples:
34
+ # Long ETH with 3% take profit and 1.5% stop loss
35
+ npx tsx scripts/operations/bracket.ts --coin ETH --side buy --size 0.5 --tp 3 --sl 1.5
36
+
37
+ # Short BTC with limit entry at $100k, 5% TP, 2% SL
38
+ npx tsx scripts/operations/bracket.ts --coin BTC --side sell --size 0.1 --entry limit --price 100000 --tp 5 --sl 2
39
+
40
+ # Preview bracket setup
41
+ npx tsx scripts/operations/bracket.ts --coin SOL --side buy --size 10 --tp 5 --sl 2 --dry
42
+ `);
43
+ }
44
+ export async function runBracket(opts) {
45
+ const out = opts.output ?? ((line) => console.log(line));
46
+ const entryType = opts.entryType ?? 'market';
47
+ const isLong = opts.side === 'buy';
48
+ if (opts.size <= 0 || isNaN(opts.size))
49
+ throw new Error('size must be positive');
50
+ if (opts.tpPct <= 0 || opts.slPct <= 0)
51
+ throw new Error('tp and sl must be positive percentages');
52
+ if (entryType === 'limit' && opts.entryPrice === undefined) {
53
+ throw new Error('entryPrice is required for limit entry');
54
+ }
55
+ const client = getClient();
56
+ if (opts.verbose)
57
+ client.verbose = true;
58
+ out('Open Broker - Bracket Order');
59
+ out('===========================\n');
60
+ const mids = await client.getAllMids();
61
+ const midPrice = parseFloat(mids[opts.coin]);
62
+ if (!midPrice)
63
+ throw new Error(`No market data for ${opts.coin}`);
64
+ const entry = entryType === 'limit' ? opts.entryPrice : midPrice;
65
+ let tpPrice = isLong
66
+ ? entry * (1 + opts.tpPct / 100)
67
+ : entry * (1 - opts.tpPct / 100);
68
+ let slPrice = isLong
69
+ ? entry * (1 - opts.slPct / 100)
70
+ : entry * (1 + opts.slPct / 100);
71
+ const riskReward = opts.tpPct / opts.slPct;
72
+ const notional = entry * opts.size;
73
+ out('Bracket Plan');
74
+ out('------------');
75
+ out(`Coin: ${opts.coin}`);
76
+ out(`Position: ${isLong ? 'LONG' : 'SHORT'}`);
77
+ out(`Size: ${opts.size}`);
78
+ out(`Entry Type: ${entryType.toUpperCase()}`);
79
+ out(`Current Mid: ${formatUsd(midPrice)}`);
80
+ out(`Entry Price: ${formatUsd(entry)}${entryType === 'market' ? ' (approx)' : ''}`);
81
+ out(`Take Profit: ${formatUsd(tpPrice)} (+${opts.tpPct}%)`);
82
+ out(`Stop Loss: ${formatUsd(slPrice)} (-${opts.slPct}%)`);
83
+ out(`Risk/Reward: 1:${riskReward.toFixed(2)}`);
84
+ out(`Est. Notional: ${formatUsd(notional)}`);
85
+ const potentialProfit = notional * (opts.tpPct / 100);
86
+ const potentialLoss = notional * (opts.slPct / 100);
87
+ out('\nRisk Analysis');
88
+ out('-------------');
89
+ out(`Potential Profit: ${formatUsd(potentialProfit)}`);
90
+ out(`Potential Loss: ${formatUsd(potentialLoss)}`);
91
+ if (opts.dryRun) {
92
+ out('\n🔍 Dry run - bracket not executed');
93
+ return { status: 'dry', entryPrice: entry, tpPrice, slPrice };
94
+ }
95
+ out('\nExecuting bracket...\n');
96
+ // Step 1: Entry
97
+ out('Step 1: Entry order');
98
+ let actualEntry = entry;
99
+ let entryOid = null;
100
+ if (entryType === 'market') {
101
+ const entryResponse = await client.marketOrder(opts.coin, isLong, opts.size, opts.slippage, opts.leverage);
102
+ if (entryResponse.status === 'ok' && entryResponse.response && typeof entryResponse.response === 'object') {
103
+ const status = entryResponse.response.data.statuses[0];
104
+ if (status?.filled) {
105
+ actualEntry = parseFloat(status.filled.avgPx);
106
+ out(` ✅ Filled @ ${formatUsd(actualEntry)}`);
107
+ }
108
+ else if (status?.error) {
109
+ out(` ❌ Entry failed: ${status.error}`);
110
+ out('\n⚠️ Bracket aborted - no position opened');
111
+ return { status: 'entry_failed', reason: status.error };
112
+ }
113
+ }
114
+ else {
115
+ const reason = typeof entryResponse.response === 'string' ? entryResponse.response : 'Unknown error';
116
+ out(` ❌ Entry failed: ${reason}`);
117
+ out('\n⚠️ Bracket aborted - no position opened');
118
+ return { status: 'entry_failed', reason };
119
+ }
120
+ }
121
+ else {
122
+ const entryResponse = await client.limitOrder(opts.coin, isLong, opts.size, entry, 'Gtc', false, opts.leverage);
123
+ if (entryResponse.status === 'ok' && entryResponse.response && typeof entryResponse.response === 'object') {
124
+ const status = entryResponse.response.data.statuses[0];
125
+ if (status?.resting) {
126
+ entryOid = status.resting.oid;
127
+ out(` ✅ Limit order placed @ ${formatUsd(entry)} (OID: ${entryOid})`);
128
+ out(` ⏳ Waiting for fill before placing TP/SL...`);
129
+ out('\n⚠️ Note: TP/SL will be placed after entry fills. Monitor manually or use a strategy script.');
130
+ return { status: 'limit_resting', entryOid, entryPrice: entry };
131
+ }
132
+ else if (status?.filled) {
133
+ actualEntry = parseFloat(status.filled.avgPx);
134
+ out(` ✅ Filled immediately @ ${formatUsd(actualEntry)}`);
135
+ }
136
+ else if (status?.error) {
137
+ out(` ❌ Entry failed: ${status.error}`);
138
+ return { status: 'entry_failed', reason: status.error };
139
+ }
140
+ }
141
+ else {
142
+ out(` ❌ Entry failed`);
143
+ return { status: 'entry_failed', reason: 'Unknown error' };
144
+ }
145
+ }
146
+ // Recalculate TP/SL based on actual entry
147
+ if (isLong) {
148
+ tpPrice = actualEntry * (1 + opts.tpPct / 100);
149
+ slPrice = actualEntry * (1 - opts.slPct / 100);
150
+ }
151
+ else {
152
+ tpPrice = actualEntry * (1 - opts.tpPct / 100);
153
+ slPrice = actualEntry * (1 + opts.slPct / 100);
154
+ }
155
+ await sleep(500);
156
+ // Step 2: Take Profit (trigger order)
157
+ out('\nStep 2: Take Profit order (trigger)');
158
+ const tpSide = !isLong;
159
+ const tpResponse = await client.takeProfit(opts.coin, tpSide, opts.size, tpPrice);
160
+ let tpOid = null;
161
+ if (tpResponse.status === 'ok' && tpResponse.response && typeof tpResponse.response === 'object') {
162
+ const status = tpResponse.response.data.statuses[0];
163
+ if (status?.resting) {
164
+ tpOid = status.resting.oid;
165
+ out(` ✅ TP trigger placed @ ${formatUsd(tpPrice)} (OID: ${tpOid})`);
166
+ }
167
+ else if (status?.error) {
168
+ out(` ❌ TP failed: ${status.error}`);
169
+ }
170
+ else {
171
+ out(` ⚠️ TP status: ${JSON.stringify(status)}`);
172
+ }
173
+ }
174
+ else {
175
+ const reason = typeof tpResponse.response === 'string' ? tpResponse.response : 'Unknown error';
176
+ out(` ❌ TP failed: ${reason}`);
177
+ }
178
+ await sleep(500);
179
+ // Step 3: Stop Loss (trigger order)
180
+ out('\nStep 3: Stop Loss order (trigger)');
181
+ const slSide = !isLong;
182
+ const slResponse = await client.stopLoss(opts.coin, slSide, opts.size, slPrice);
183
+ let slOid = null;
184
+ if (slResponse.status === 'ok' && slResponse.response && typeof slResponse.response === 'object') {
185
+ const status = slResponse.response.data.statuses[0];
186
+ if (status?.resting) {
187
+ slOid = status.resting.oid;
188
+ out(` ✅ SL trigger placed @ ${formatUsd(slPrice)} (OID: ${slOid})`);
189
+ }
190
+ else if (status?.error) {
191
+ out(` ❌ SL failed: ${status.error}`);
192
+ }
193
+ else {
194
+ out(` ⚠️ SL status: ${JSON.stringify(status)}`);
195
+ }
196
+ }
197
+ else {
198
+ const reason = typeof slResponse.response === 'string' ? slResponse.response : 'Unknown error';
199
+ out(` ❌ SL failed: ${reason}`);
200
+ }
201
+ out('\n========== Bracket Summary ==========');
202
+ out(`Position: ${isLong ? 'LONG' : 'SHORT'} ${opts.size} ${opts.coin}`);
203
+ out(`Entry: ${formatUsd(actualEntry)}`);
204
+ out(`Take Profit: ${formatUsd(tpPrice)} (+${opts.tpPct}%) - Trigger order`);
205
+ out(`Stop Loss: ${formatUsd(slPrice)} (-${opts.slPct}%) - Trigger order`);
206
+ if (tpOid && slOid) {
207
+ out(`\n✅ Bracket complete! TP and SL are trigger orders.`);
208
+ out(` They will only execute when price reaches trigger level.`);
209
+ out(` When one fills, cancel the other manually.`);
210
+ }
211
+ return {
212
+ status: tpOid && slOid ? 'complete' : 'partial',
213
+ entryPrice: actualEntry,
214
+ tpPrice,
215
+ slPrice,
216
+ tpOid,
217
+ slOid,
218
+ };
219
+ }
220
+ async function main() {
221
+ const args = parseArgs(process.argv.slice(2));
222
+ const coin = args.coin;
223
+ const side = args.side;
224
+ const size = parseFloat(args.size);
225
+ const entryType = (args.entry || 'market');
226
+ const entryPrice = args.price ? parseFloat(args.price) : undefined;
227
+ const tpPct = parseFloat(args.tp);
228
+ const slPct = parseFloat(args.sl);
229
+ const slippage = args.slippage ? parseInt(args.slippage) : undefined;
230
+ const leverage = args.leverage ? parseInt(args.leverage) : undefined;
231
+ const dryRun = args.dry;
232
+ if (!coin || !side || isNaN(size) || isNaN(tpPct) || isNaN(slPct)) {
233
+ printUsage();
234
+ process.exit(1);
235
+ }
236
+ if (side !== 'buy' && side !== 'sell') {
237
+ console.error('Error: --side must be "buy" or "sell"');
238
+ process.exit(1);
239
+ }
240
+ try {
241
+ const result = await runBracket({
242
+ coin,
243
+ side: side,
244
+ size,
245
+ tpPct,
246
+ slPct,
247
+ entryType,
248
+ entryPrice,
249
+ slippage,
250
+ leverage,
251
+ dryRun,
252
+ verbose: args.verbose,
253
+ });
254
+ if (result.status === 'entry_failed')
255
+ process.exit(1);
256
+ }
257
+ catch (error) {
258
+ console.error('Error:', error instanceof Error ? error.message : error);
259
+ process.exit(1);
260
+ }
261
+ }
262
+ // Only run when invoked as a script — not when imported as a module
263
+ // (e.g. by `openbroker-plugin` via the lib re-export of `runBracket`).
264
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
265
+ main();
266
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env npx tsx
2
+ export {};
3
+ //# sourceMappingURL=cancel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cancel.d.ts","sourceRoot":"","sources":["../../scripts/operations/cancel.ts"],"names":[],"mappings":""}
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env npx tsx
2
+ // Cancel orders on Hyperliquid
3
+ import { getClient } from '../core/client.js';
4
+ import { formatUsd, parseArgs } from '../core/utils.js';
5
+ function printUsage() {
6
+ console.log(`
7
+ Open Broker - Cancel Orders
8
+ ===========================
9
+
10
+ Cancel open orders.
11
+
12
+ Usage:
13
+ npx tsx scripts/operations/cancel.ts [--coin <COIN>] [--oid <ORDER_ID>] [--all]
14
+
15
+ Options:
16
+ --coin Cancel orders for specific coin only
17
+ --oid Cancel specific order by ID
18
+ --all Cancel all open orders
19
+ --dry Dry run - show what would be cancelled
20
+
21
+ Examples:
22
+ npx tsx scripts/operations/cancel.ts --all # Cancel all orders
23
+ npx tsx scripts/operations/cancel.ts --coin ETH # Cancel all ETH orders
24
+ npx tsx scripts/operations/cancel.ts --coin ETH --oid 123456 # Cancel specific order
25
+ npx tsx scripts/operations/cancel.ts --all --dry # Show all orders (dry run)
26
+ `);
27
+ }
28
+ async function main() {
29
+ const args = parseArgs(process.argv.slice(2));
30
+ const coin = args.coin;
31
+ const oid = args.oid ? parseInt(args.oid) : undefined;
32
+ const all = args.all;
33
+ const dryRun = args.dry;
34
+ // Must specify something to cancel
35
+ if (!coin && !oid && !all) {
36
+ printUsage();
37
+ process.exit(1);
38
+ }
39
+ const client = getClient();
40
+ console.log('Open Broker - Cancel Orders');
41
+ console.log('===========================\n');
42
+ try {
43
+ // Get open orders
44
+ const orders = await client.getOpenOrders();
45
+ // Filter orders based on arguments
46
+ let targetOrders = orders;
47
+ if (coin) {
48
+ targetOrders = orders.filter(o => o.coin === coin);
49
+ }
50
+ if (oid) {
51
+ targetOrders = orders.filter(o => o.oid === oid);
52
+ }
53
+ if (targetOrders.length === 0) {
54
+ if (oid) {
55
+ console.log(`No order found with ID ${oid}`);
56
+ }
57
+ else if (coin) {
58
+ console.log(`No open orders for ${coin}`);
59
+ }
60
+ else {
61
+ console.log('No open orders to cancel');
62
+ }
63
+ return;
64
+ }
65
+ // Display orders to be cancelled
66
+ console.log('Orders to Cancel');
67
+ console.log('----------------');
68
+ console.log('Coin | Side | Size | Price | Order ID');
69
+ console.log('---------|------|------------|------------|----------');
70
+ for (const order of targetOrders) {
71
+ const side = order.side === 'B' ? 'BUY ' : 'SELL';
72
+ console.log(`${order.coin.padEnd(8)} | ${side} | ${parseFloat(order.sz).toFixed(4).padStart(10)} | ` +
73
+ `${formatUsd(parseFloat(order.limitPx)).padStart(10)} | ${order.oid}`);
74
+ }
75
+ console.log(`\nTotal: ${targetOrders.length} order(s)`);
76
+ if (dryRun) {
77
+ console.log('\n🔍 Dry run - orders not cancelled');
78
+ return;
79
+ }
80
+ console.log('\nCancelling...');
81
+ let successCount = 0;
82
+ let failCount = 0;
83
+ for (const order of targetOrders) {
84
+ try {
85
+ const response = await client.cancel(order.coin, order.oid);
86
+ if (response.status === 'ok') {
87
+ console.log(`✅ Cancelled ${order.coin} order ${order.oid}`);
88
+ successCount++;
89
+ }
90
+ else {
91
+ console.log(`❌ Failed to cancel ${order.coin} order ${order.oid}`);
92
+ failCount++;
93
+ }
94
+ }
95
+ catch (err) {
96
+ console.log(`❌ Error cancelling ${order.coin} order ${order.oid}: ${err}`);
97
+ failCount++;
98
+ }
99
+ }
100
+ console.log(`\nResult: ${successCount} cancelled, ${failCount} failed`);
101
+ }
102
+ catch (error) {
103
+ console.error('Error:', error);
104
+ process.exit(1);
105
+ }
106
+ }
107
+ main();
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env npx tsx
2
+ export interface ChaseOptions {
3
+ coin: string;
4
+ side: 'buy' | 'sell';
5
+ size: number;
6
+ offsetBps?: number;
7
+ timeoutSec?: number;
8
+ intervalMs?: number;
9
+ maxChaseBps?: number;
10
+ leverage?: number;
11
+ reduceOnly?: boolean;
12
+ dryRun?: boolean;
13
+ verbose?: boolean;
14
+ /** Receives each output line. Defaults to console.log. */
15
+ output?: (line: string) => void;
16
+ }
17
+ export interface ChaseResult {
18
+ status: 'dry' | 'filled' | 'timeout' | 'max_chase_exceeded';
19
+ iterations: number;
20
+ durationSec: number;
21
+ startMid: number;
22
+ endMid: number;
23
+ }
24
+ export declare function runChase(opts: ChaseOptions): Promise<ChaseResult>;
25
+ //# sourceMappingURL=chase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chase.d.ts","sourceRoot":"","sources":["../../scripts/operations/chase.ts"],"names":[],"mappings":";AAuCA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,oBAAoB,CAAC;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA2JvE"}
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env npx tsx
2
+ // Chase Order - Follow price with limit orders until filled
3
+ import { fileURLToPath } from 'url';
4
+ import { getClient } from '../core/client.js';
5
+ import { formatUsd, parseArgs, sleep } from '../core/utils.js';
6
+ function printUsage() {
7
+ console.log(`
8
+ Open Broker - Chase Order
9
+ =========================
10
+
11
+ Place a limit order that chases the price until filled.
12
+ Keeps adjusting the order to stay near the best price while avoiding taker fees.
13
+
14
+ Usage:
15
+ npx tsx scripts/operations/chase.ts --coin <COIN> --side <buy|sell> --size <SIZE>
16
+
17
+ Options:
18
+ --coin Asset to trade (e.g., ETH, BTC)
19
+ --side Order side: buy or sell
20
+ --size Order size in base asset
21
+ --offset Offset from mid price in bps (default: 5 = 0.05%)
22
+ --timeout Max time to chase in seconds (default: 300 = 5 min)
23
+ --interval Update interval in ms (default: 2000)
24
+ --max-chase Max price to chase to in bps from start (default: 100 = 1%)
25
+ --leverage Set leverage (e.g., 10 for 10x). Cross for main perps, isolated for HIP-3
26
+ --reduce Reduce-only order
27
+ --dry Dry run - show chase parameters without executing
28
+
29
+ Examples:
30
+ # Chase buy 0.5 ETH with 5 bps offset, 5 min timeout
31
+ npx tsx scripts/operations/chase.ts --coin ETH --side buy --size 0.5
32
+
33
+ # Chase sell with tighter offset and longer timeout
34
+ npx tsx scripts/operations/chase.ts --coin BTC --side sell --size 0.1 --offset 2 --timeout 600
35
+ `);
36
+ }
37
+ export async function runChase(opts) {
38
+ const out = opts.output ?? ((line) => console.log(line));
39
+ const offsetBps = opts.offsetBps ?? 5;
40
+ const timeoutSec = opts.timeoutSec ?? 300;
41
+ const intervalMs = opts.intervalMs ?? 2000;
42
+ const maxChaseBps = opts.maxChaseBps ?? 100;
43
+ const isBuy = opts.side === 'buy';
44
+ if (opts.size <= 0 || isNaN(opts.size))
45
+ throw new Error('size must be positive');
46
+ const client = getClient();
47
+ if (opts.verbose)
48
+ client.verbose = true;
49
+ out('Open Broker - Chase Order');
50
+ out('=========================\n');
51
+ const mids = await client.getAllMids();
52
+ const startMid = parseFloat(mids[opts.coin]);
53
+ if (!startMid)
54
+ throw new Error(`No market data for ${opts.coin}`);
55
+ const maxChasePrice = isBuy
56
+ ? startMid * (1 + maxChaseBps / 10000)
57
+ : startMid * (1 - maxChaseBps / 10000);
58
+ out('Chase Parameters');
59
+ out('----------------');
60
+ out(`Coin: ${opts.coin}`);
61
+ out(`Side: ${isBuy ? 'BUY' : 'SELL'}`);
62
+ out(`Size: ${opts.size}`);
63
+ out(`Start Mid: ${formatUsd(startMid)}`);
64
+ out(`Offset: ${offsetBps} bps (${(offsetBps / 100).toFixed(2)}%)`);
65
+ out(`Max Chase: ${maxChaseBps} bps to ${formatUsd(maxChasePrice)}`);
66
+ out(`Timeout: ${timeoutSec}s`);
67
+ out(`Update Rate: ${intervalMs}ms`);
68
+ out(`Order Type: ALO (post-only)`);
69
+ if (opts.dryRun) {
70
+ out('\n🔍 Dry run - chase not started');
71
+ return { status: 'dry', iterations: 0, durationSec: 0, startMid, endMid: startMid };
72
+ }
73
+ out('\nChasing...\n');
74
+ const startTime = Date.now();
75
+ let currentOid = null;
76
+ let lastPrice = null;
77
+ let iteration = 0;
78
+ let filled = false;
79
+ let exitReason = 'timeout';
80
+ while (Date.now() - startTime < timeoutSec * 1000) {
81
+ iteration++;
82
+ const currentMids = await client.getAllMids();
83
+ const currentMid = parseFloat(currentMids[opts.coin]);
84
+ if (isBuy && currentMid > maxChasePrice) {
85
+ out(`\n⚠️ Price ${formatUsd(currentMid)} exceeded max chase ${formatUsd(maxChasePrice)}`);
86
+ exitReason = 'max_chase_exceeded';
87
+ break;
88
+ }
89
+ if (!isBuy && currentMid < maxChasePrice) {
90
+ out(`\n⚠️ Price ${formatUsd(currentMid)} exceeded max chase ${formatUsd(maxChasePrice)}`);
91
+ exitReason = 'max_chase_exceeded';
92
+ break;
93
+ }
94
+ const orderPrice = isBuy
95
+ ? currentMid * (1 - offsetBps / 10000)
96
+ : currentMid * (1 + offsetBps / 10000);
97
+ const priceChanged = !lastPrice || Math.abs(orderPrice - lastPrice) / lastPrice > 0.0001;
98
+ if (priceChanged) {
99
+ if (currentOid !== null) {
100
+ try {
101
+ await client.cancel(opts.coin, currentOid);
102
+ }
103
+ catch {
104
+ // Order might have filled
105
+ }
106
+ currentOid = null;
107
+ }
108
+ const orders = await client.getOpenOrders();
109
+ const ourOrder = orders.find(o => o.coin === opts.coin && o.oid === currentOid);
110
+ if (!ourOrder && currentOid !== null) {
111
+ const state = await client.getUserState();
112
+ const pos = state.assetPositions.find(p => p.position.coin === opts.coin);
113
+ if (pos && Math.abs(parseFloat(pos.position.szi)) >= opts.size * 0.99) {
114
+ filled = true;
115
+ exitReason = 'filled';
116
+ out(`\n✅ Order filled!`);
117
+ break;
118
+ }
119
+ }
120
+ out(`[${iteration}] Mid: ${formatUsd(currentMid)} → Order: ${formatUsd(orderPrice)}...`);
121
+ const response = await client.limitOrder(opts.coin, isBuy, opts.size, orderPrice, 'Alo', opts.reduceOnly, opts.leverage);
122
+ if (response.status === 'ok' && response.response && typeof response.response === 'object') {
123
+ const status = response.response.data.statuses[0];
124
+ if (status?.resting) {
125
+ currentOid = status.resting.oid;
126
+ lastPrice = orderPrice;
127
+ out(`OID: ${currentOid}`);
128
+ }
129
+ else if (status?.filled) {
130
+ filled = true;
131
+ exitReason = 'filled';
132
+ out(`✅ Filled @ ${formatUsd(parseFloat(status.filled.avgPx))}`);
133
+ break;
134
+ }
135
+ else if (status?.error) {
136
+ out(`❌ ${status.error}`);
137
+ }
138
+ }
139
+ else {
140
+ out(`❌ Failed`);
141
+ }
142
+ }
143
+ else {
144
+ if (currentOid !== null) {
145
+ const orders = await client.getOpenOrders();
146
+ const ourOrder = orders.find(o => o.oid === currentOid);
147
+ if (!ourOrder) {
148
+ filled = true;
149
+ exitReason = 'filled';
150
+ out(`\n✅ Order filled!`);
151
+ break;
152
+ }
153
+ }
154
+ }
155
+ await sleep(intervalMs);
156
+ }
157
+ if (currentOid !== null && !filled) {
158
+ out(`\nCancelling unfilled order...`);
159
+ try {
160
+ await client.cancel(opts.coin, currentOid);
161
+ out(`✅ Cancelled`);
162
+ }
163
+ catch {
164
+ out(`⚠️ Could not cancel (may have filled)`);
165
+ }
166
+ }
167
+ const elapsed = (Date.now() - startTime) / 1000;
168
+ const endMid = parseFloat((await client.getAllMids())[opts.coin]);
169
+ const priceMove = ((endMid - startMid) / startMid) * 10000;
170
+ out('\n========== Chase Summary ==========');
171
+ out(`Duration: ${elapsed.toFixed(1)}s`);
172
+ out(`Iterations: ${iteration}`);
173
+ out(`Start Mid: ${formatUsd(startMid)}`);
174
+ out(`End Mid: ${formatUsd(endMid)} (${priceMove >= 0 ? '+' : ''}${priceMove.toFixed(1)} bps)`);
175
+ out(`Result: ${filled ? '✅ Filled' : '❌ Not filled'}`);
176
+ return { status: exitReason, iterations: iteration, durationSec: elapsed, startMid, endMid };
177
+ }
178
+ async function main() {
179
+ const args = parseArgs(process.argv.slice(2));
180
+ const coin = args.coin;
181
+ const side = args.side;
182
+ const size = parseFloat(args.size);
183
+ if (!coin || !side || isNaN(size)) {
184
+ printUsage();
185
+ process.exit(1);
186
+ }
187
+ if (side !== 'buy' && side !== 'sell') {
188
+ console.error('Error: --side must be "buy" or "sell"');
189
+ process.exit(1);
190
+ }
191
+ try {
192
+ await runChase({
193
+ coin,
194
+ side: side,
195
+ size,
196
+ offsetBps: args.offset ? parseInt(args.offset) : undefined,
197
+ timeoutSec: args.timeout ? parseInt(args.timeout) : undefined,
198
+ intervalMs: args.interval ? parseInt(args.interval) : undefined,
199
+ maxChaseBps: args['max-chase'] ? parseInt(args['max-chase']) : undefined,
200
+ leverage: args.leverage ? parseInt(args.leverage) : undefined,
201
+ reduceOnly: args.reduce,
202
+ dryRun: args.dry,
203
+ verbose: args.verbose,
204
+ });
205
+ }
206
+ catch (error) {
207
+ console.error('Error:', error instanceof Error ? error.message : error);
208
+ process.exit(1);
209
+ }
210
+ }
211
+ // Only run when invoked as a script — not when imported as a module
212
+ // (e.g. by `openbroker-plugin` via the lib re-export of `runChase`).
213
+ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
214
+ main();
215
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env npx tsx
2
+ export {};
3
+ //# sourceMappingURL=limit-order.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limit-order.d.ts","sourceRoot":"","sources":["../../scripts/operations/limit-order.ts"],"names":[],"mappings":""}