marginpad 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/index.d.ts +27 -0
- package/index.js +153 -0
- package/package.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# MarginPad SDK
|
|
2
|
+
|
|
3
|
+
Free crypto-futures **paper trading for bots** and free market data. One key, no KYC, no deposit.
|
|
4
|
+
Test a strategy or an AI agent against live prices with real fee schedules, funding, liquidations, limit and stop orders, trailing stops, webhooks and a WebSocket stream, then take the same code to a real exchange.
|
|
5
|
+
|
|
6
|
+
- Guide: https://marginpad.io/trading-api/
|
|
7
|
+
- Reference (OpenAPI, try it live): https://marginpad.io/api-docs/
|
|
8
|
+
- Status: https://marginpad.io/status/ - Changelog: https://marginpad.io/api/changelog.xml
|
|
9
|
+
- MCP server for Claude, ChatGPT and Cursor: https://marginpad.io/trading-api/#mcp
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install marginpad # Python 3.8+, no dependencies (pip install "marginpad[stream]" for the WebSocket)
|
|
15
|
+
npm install marginpad # Node 18+, zero dependencies
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Python
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from marginpad import MarginPad
|
|
22
|
+
|
|
23
|
+
mp = MarginPad("mpb_...") # key from https://marginpad.io/trading-api/
|
|
24
|
+
print(mp.price("BTC")) # keyless market data
|
|
25
|
+
pos = mp.open("BTC", "long", margin_usd=100, leverage=10, sl=58000, tp=66000,
|
|
26
|
+
client_order_id="sig-2026-09-12-1403") # idempotent: a retry returns the same position
|
|
27
|
+
mp.sltp(pos["id"], trail_pct=1.5) # trailing stop, ratcheted server-side
|
|
28
|
+
print(mp.account())
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## JavaScript
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
const { MarginPad } = require('marginpad');
|
|
35
|
+
const mp = new MarginPad('mpb_...');
|
|
36
|
+
const { position } = await mp.open({ symbol: 'BTC', side: 'long', margin_usd: 100, leverage: 10, client_order_id: 'sig-1' });
|
|
37
|
+
mp.stream(ev => console.log(ev.type, ev.data)); // positions pushed on change, prices each tick
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Examples
|
|
41
|
+
|
|
42
|
+
- `examples/agent_loop.py`: a signal loop that opens, manages and closes positions once a minute without polling storms.
|
|
43
|
+
- `examples/webhook_server.js`: receive `position.closed` / `order.filled` events with signature verification.
|
|
44
|
+
- `examples/mcp.md`: let Claude Desktop or Cursor trade on your paper account through the MCP server.
|
|
45
|
+
|
|
46
|
+
## Limits
|
|
47
|
+
|
|
48
|
+
Free: 120 requests/minute per key, 3 keys, 50 open positions. Premium: 600/minute, 10 keys, 200 open positions.
|
|
49
|
+
Every response carries `X-RateLimit-Limit / Remaining / Reset`; on 429 both clients wait `Retry-After` and retry once.
|
|
50
|
+
Out-of-range input is refused with a named error (`leverage_max` carries the market's cap), never silently clamped.
|
|
51
|
+
|
|
52
|
+
## Honesty note
|
|
53
|
+
|
|
54
|
+
This is a simulator. Fees, funding and liquidation math mirror real exchanges; there is no order book, so slippage is modelled, not measured on depth. A bot that passes here still needs a small live test. Educational use, not financial advice.
|
|
55
|
+
|
|
56
|
+
MIT licensed.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface Position { id: string; symbol: string; side: 'long' | 'short'; entry_price: number; margin_usd: number; leverage: number; qty: number; liq_price: number; sl: number | null; tp: number | null; status: 'open' | 'closed' | 'liquidated'; opened_ts: number; mark_price?: number; unrealized_pnl_usd?: number; exit_price?: number | null; pnl_usd?: number | null; closed_ts?: number | null; trail_pct?: number; fee_rate_pct?: number; fee_venue?: string | null; }
|
|
2
|
+
export interface OpenParams { symbol: string; side: 'long' | 'short'; margin_usd: number; leverage?: number; sl?: number; tp?: number; trail_pct?: number; type?: 'market' | 'limit' | 'stop'; limit_price?: number; client_order_id?: string; fee_venue?: string | null; dry_run?: boolean; }
|
|
3
|
+
export class MarginPadError extends Error { code: string; status: number; body: any; }
|
|
4
|
+
export class MarginPad {
|
|
5
|
+
constructor(key?: string, opts?: { baseUrl?: string; autoRetry?: boolean; timeoutMs?: number });
|
|
6
|
+
price(symbol: string): Promise<{ symbol: string; price: number; change_24h_pct: number | null; ts: number }>;
|
|
7
|
+
klines(symbol: string, interval?: string, end?: number): Promise<any[]>;
|
|
8
|
+
time(): Promise<{ server_time_ms: number; server_time_iso: string }>;
|
|
9
|
+
open(p: OpenParams): Promise<{ position?: Position; order?: any; dry_run?: boolean }>;
|
|
10
|
+
close(id: string, opts?: { pct?: number; symbol?: string; client_order_id?: string }): Promise<{ position: Position }>;
|
|
11
|
+
closeAll(): Promise<{ closed: number; positions: Position[] }>;
|
|
12
|
+
sltp(id: string, p: { sl?: number | null; tp?: number | null; trail_pct?: number | null }): Promise<{ position: Position }>;
|
|
13
|
+
positions(): Promise<{ positions: Position[] }>;
|
|
14
|
+
orders(): Promise<{ orders: any[] }>;
|
|
15
|
+
cancelOrder(id: string): Promise<{ order_id: string; status: string }>;
|
|
16
|
+
modifyOrder(id: string, p: any): Promise<any>;
|
|
17
|
+
trades(opts?: { limit?: number; before?: number }): Promise<{ trades: Position[]; next_before: number | null }>;
|
|
18
|
+
account(): Promise<any>;
|
|
19
|
+
balance(): Promise<any>;
|
|
20
|
+
markets(): Promise<any>;
|
|
21
|
+
fees(): Promise<any>;
|
|
22
|
+
report(days?: number): Promise<any>;
|
|
23
|
+
usage(): Promise<any>;
|
|
24
|
+
webhooks(): Promise<any>;
|
|
25
|
+
stream(onEvent: (ev: { type: string; data: any }) => void, opts?: any): { close(): void };
|
|
26
|
+
}
|
|
27
|
+
export function verifyWebhook(secret: string, rawBody: string, signatureHeader: string): boolean;
|
package/index.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/* MarginPad Bot API client - one file, zero dependencies. Node 18+ (global fetch) or any browser.
|
|
2
|
+
*
|
|
3
|
+
* const { MarginPad } = require('./marginpad.js'); // or: import { MarginPad } from './marginpad.js'
|
|
4
|
+
* const mp = new MarginPad('mpb_...'); // key from https://marginpad.io/trading-api/
|
|
5
|
+
* console.log(await mp.price('BTC')); // keyless market data
|
|
6
|
+
* const { position } = await mp.open({ symbol: 'BTC', side: 'long', margin_usd: 100, leverage: 10,
|
|
7
|
+
* sl: 58000, tp: 66000, client_order_id: 'sig-2026-09-11-1403' });
|
|
8
|
+
* await mp.sltp(position.id, { trail_pct: 1.5 }); // trailing stop, ratcheted server-side
|
|
9
|
+
* mp.stream(ev => console.log(ev.type, ev.data)); // WebSocket push (Node 22+ or a browser; else npm i ws)
|
|
10
|
+
*
|
|
11
|
+
* Every method resolves with the `data` part of the v2 envelope and rejects with MarginPadError on {ok:false}.
|
|
12
|
+
* On 429 the client waits Retry-After and retries ONCE (autoRetry, default on).
|
|
13
|
+
* Docs: https://marginpad.io/trading-api/ · Spec: https://marginpad.io/api/openapi.json
|
|
14
|
+
*/
|
|
15
|
+
(function (root, factory) {
|
|
16
|
+
if (typeof module === 'object' && module.exports) module.exports = factory();
|
|
17
|
+
else root.MarginPadSDK = factory();
|
|
18
|
+
})(typeof self !== 'undefined' ? self : this, function () {
|
|
19
|
+
'use strict';
|
|
20
|
+
var VERSION = '2.9.1';
|
|
21
|
+
|
|
22
|
+
function MarginPadError(code, message, status, extra) {
|
|
23
|
+
var e = new Error(code + ': ' + (message || code));
|
|
24
|
+
e.name = 'MarginPadError'; e.code = code; e.message = message || code; e.status = status || 0; e.extra = extra || {};
|
|
25
|
+
return e;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function MarginPad(apiKey, opts) {
|
|
29
|
+
opts = opts || {};
|
|
30
|
+
this.apiKey = apiKey || null;
|
|
31
|
+
this.base = (opts.base || 'https://marginpad.io').replace(/\/+$/, '');
|
|
32
|
+
this.autoRetry = opts.autoRetry !== false;
|
|
33
|
+
this.timeout = opts.timeout || 15000;
|
|
34
|
+
this.lastHeaders = {};
|
|
35
|
+
this._etag = null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
MarginPad.prototype._call = async function (method, path, params, body, keyed, headers, retried) {
|
|
39
|
+
var url = this.base + path;
|
|
40
|
+
if (params) { var q = Object.keys(params).filter(function (k) { return params[k] != null; }).map(function (k) { return encodeURIComponent(k) + '=' + encodeURIComponent(params[k]); }).join('&'); if (q) url += (url.indexOf('?') > 0 ? '&' : '?') + q; }
|
|
41
|
+
var h = { accept: 'application/json', 'user-agent': 'marginpad-js/' + VERSION };
|
|
42
|
+
if (keyed !== false && this.apiKey) h['x-api-key'] = this.apiKey;
|
|
43
|
+
if (body != null) h['content-type'] = 'application/json';
|
|
44
|
+
if (headers) for (var k in headers) if (headers[k] != null) h[k] = headers[k];
|
|
45
|
+
var ctl = typeof AbortController !== 'undefined' ? new AbortController() : null, to = ctl ? setTimeout(function () { ctl.abort(); }, this.timeout) : null;
|
|
46
|
+
var r;
|
|
47
|
+
try { r = await fetch(url, { method: method, headers: h, body: body != null ? JSON.stringify(body) : undefined, signal: ctl ? ctl.signal : undefined }); }
|
|
48
|
+
finally { if (to) clearTimeout(to); }
|
|
49
|
+
var lh = {}; try { r.headers.forEach(function (v, k) { lh[k] = v; }); } catch (e) {}
|
|
50
|
+
this.lastHeaders = lh;
|
|
51
|
+
if (r.status === 304) return null;
|
|
52
|
+
if (r.status === 429 && this.autoRetry && !retried) { var ra = parseInt(r.headers.get('retry-after') || '5', 10); await new Promise(function (res) { setTimeout(res, Math.min(60000, Math.max(1000, (isNaN(ra) ? 5 : ra) * 1000))); }); return this._call(method, path, params, body, keyed, headers, true); }
|
|
53
|
+
var text = await r.text(); var j = null; try { j = JSON.parse(text); } catch (e) { j = null; }
|
|
54
|
+
if (!r.ok) { var err = j && j.error; if (err && typeof err === 'object') throw MarginPadError(err.code || 'error', err.message, r.status, err); throw MarginPadError(String(err || ('http_' + r.status)), (j && j.hint) || text.slice(0, 200), r.status, j || {}); }
|
|
55
|
+
if (j && j.ok === false) { var e2 = j.error || {}; throw MarginPadError(e2.code || 'error', e2.message, r.status, e2); }
|
|
56
|
+
return j && Object.prototype.hasOwnProperty.call(j, 'data') ? j.data : j;
|
|
57
|
+
};
|
|
58
|
+
MarginPad.prototype._get = function (path, params, keyed) { return this._call('GET', path, params, null, keyed); };
|
|
59
|
+
MarginPad.prototype._post = function (path, body) { var b = {}; for (var k in (body || {})) if (body[k] !== undefined) b[k] = body[k]; return this._call('POST', path, null, b); };
|
|
60
|
+
|
|
61
|
+
// ── market data (keyless; with a key it counts against your per-key budget instead of the per-IP one) ──
|
|
62
|
+
MarginPad.prototype.price = function (symbol) { return this._get('/api/v1/price', { symbol: symbol }); };
|
|
63
|
+
MarginPad.prototype.prices = function () { return this._get('/api/v1/prices'); };
|
|
64
|
+
MarginPad.prototype.klines = function (symbol, interval, endMs) { return this._get('/api/v1/klines', { symbol: symbol, interval: interval || 60, end: endMs }); };
|
|
65
|
+
MarginPad.prototype.markets = function (assetClass) { return this._get('/api/bot/v2/markets', { 'class': assetClass }); };
|
|
66
|
+
MarginPad.prototype.screener = function () { return this._get('/api/v1/screener'); };
|
|
67
|
+
MarginPad.prototype.funding = function () { return this._get('/api/v1/funding'); };
|
|
68
|
+
MarginPad.prototype.openInterest = function () { return this._get('/api/v1/open-interest'); };
|
|
69
|
+
MarginPad.prototype.liquidations = function () { return this._get('/api/v1/liquidations'); };
|
|
70
|
+
MarginPad.prototype.fearGreed = function () { return this._get('/api/v1/fear-greed'); };
|
|
71
|
+
MarginPad.prototype.calendar = function (year) { return this._get('/api/v1/calendar', { year: year }); };
|
|
72
|
+
MarginPad.prototype.serverTime = function () { return this._get('/api/bot/v2/time', { client_ts: Date.now() }, false); };
|
|
73
|
+
|
|
74
|
+
// ── paper trading ────────────────────────────────────────────────────────────────────────────────────
|
|
75
|
+
// open({symbol, side, margin_usd, leverage, sl, tp, trail_pct, client_order_id, dry_run})
|
|
76
|
+
MarginPad.prototype.open = function (o) { return this._post('/api/bot/v2/open', o); };
|
|
77
|
+
// limitOrder({symbol, side, limit_price, ...}) = pullback entry; stopOrder({..., stop_price}) = breakout entry
|
|
78
|
+
MarginPad.prototype.limitOrder = function (o) { return this._post('/api/bot/v2/open', Object.assign({ type: 'limit' }, o)); };
|
|
79
|
+
MarginPad.prototype.stopOrder = function (o) { var b = Object.assign({ type: 'stop' }, o); if (b.stop_price != null && b.limit_price == null) { b.limit_price = b.stop_price; delete b.stop_price; } return this._post('/api/bot/v2/open', b); };
|
|
80
|
+
MarginPad.prototype.orders = function () { return this._get('/api/bot/v2/orders'); };
|
|
81
|
+
MarginPad.prototype.modifyOrder = function (orderId, changes) { return this._post('/api/bot/v2/modify_order', Object.assign({ order_id: orderId }, changes || {})); };
|
|
82
|
+
MarginPad.prototype.cancelOrder = function (orderId) { return this._post('/api/bot/v2/cancel_order', { order_id: orderId }); };
|
|
83
|
+
// close(id, {symbol, pct, client_order_id}) - pass symbol: one price fetch, one round trip
|
|
84
|
+
MarginPad.prototype.close = function (id, o) { return this._post('/api/bot/v2/close', Object.assign({ id: id }, o || {})); };
|
|
85
|
+
MarginPad.prototype.closeAll = function () { return this._post('/api/bot/v2/close_all', {}); };
|
|
86
|
+
// sltp(id, {sl, tp, trail_pct}) - null clears a level, an omitted key keeps it
|
|
87
|
+
MarginPad.prototype.sltp = function (id, o) { return this._post('/api/bot/v2/sltp', Object.assign({ id: id }, o || {})); };
|
|
88
|
+
// positions({status:'open'|'closed', since}) - resolves null when nothing changed (ETag / 304)
|
|
89
|
+
MarginPad.prototype.positions = async function (o) { o = o || {}; var d = await this._call('GET', '/api/bot/v2/positions', { status: o.status, since: o.since }, null, true, this._etag && o.useEtag !== false ? { 'if-none-match': this._etag } : null); var et = this.lastHeaders.etag; if (et) this._etag = et; return d; };
|
|
90
|
+
MarginPad.prototype.trades = function (limit, beforeMs) { return this._get('/api/bot/v2/trades', { limit: limit || 100, before: beforeMs }); };
|
|
91
|
+
MarginPad.prototype.account = function () { return this._get('/api/bot/v2/account'); };
|
|
92
|
+
MarginPad.prototype.balance = function () { return this._get('/api/bot/v2/balance'); };
|
|
93
|
+
MarginPad.prototype.usage = function () { return this._get('/api/bot/v2/usage'); };
|
|
94
|
+
MarginPad.prototype.report = function (days) { return this._get('/api/bot/v2/report', { days: days || 30 }); };
|
|
95
|
+
// 2.5 books, reset, equity · 2.6 replay
|
|
96
|
+
MarginPad.prototype.accounts = function () { return this._get('/api/bot/v2/accounts'); };
|
|
97
|
+
MarginPad.prototype.reset = function () { return this._post('/api/bot/v2/reset', { confirm: true }); };
|
|
98
|
+
MarginPad.prototype.equity = function (days, stepMin) { return this._get('/api/bot/v2/equity', { days: days || 30, step_min: stepMin }); };
|
|
99
|
+
MarginPad.prototype.replayStart = function (o) { return this._post('/api/bot/v2/replay', { symbol: o.symbol, day: o.day, speed: o.speed }); };
|
|
100
|
+
MarginPad.prototype.replay = function (o) { o = o || {}; return this._get('/api/bot/v2/replay', { interval: o.interval, bars: o.bars }); };
|
|
101
|
+
MarginPad.prototype.replayStop = function () { return this._post('/api/bot/v2/replay', { act: 'stop' }); };
|
|
102
|
+
// ai({symbol, interval, question, lang}) - Premium; educational, not advice
|
|
103
|
+
MarginPad.prototype.ai = function (o) { return this._post('/api/bot/v2/ai', Object.assign({}, o, { interval: String((o && o.interval) || 60) })); };
|
|
104
|
+
|
|
105
|
+
// ── fee schedule and fill realism (2.9) ──────────────────────────────────
|
|
106
|
+
// fees() reads the table and your default; fees('binance') sets it; fees('') goes back to ours.
|
|
107
|
+
MarginPad.prototype.fees = function (venue) { return venue === undefined ? this._get('/api/bot/v2/fees') : this._post('/api/bot/v2/fees', { venue: venue }); };
|
|
108
|
+
|
|
109
|
+
// Fill realism. Both switches are OFF by default: a market order fills at the live price and maintenance
|
|
110
|
+
// margin is a flat 0.5%, which is easier than a real venue and increasingly so as a position grows.
|
|
111
|
+
// await c.realism() -> your setting, every venue rate, the tier ladder
|
|
112
|
+
// await c.realism({ slippage: true, margin_tiers: true }) -> test against something closer to a real book
|
|
113
|
+
// await c.realism({ margin_venue: 'binance' }) -> liquidate where Binance would
|
|
114
|
+
// Pass the same fields to open() to override the account for one fill only.
|
|
115
|
+
MarginPad.prototype.realism = function (o) { return o === undefined ? this._get('/api/bot/v2/realism') : this._post('/api/bot/v2/realism', o); };
|
|
116
|
+
|
|
117
|
+
// ── webhooks (API Pro and up) ───────────────────────────────────────────────────────────────────────────────
|
|
118
|
+
MarginPad.prototype.webhooks = function () { return this._get('/api/bot/v2/webhooks'); };
|
|
119
|
+
MarginPad.prototype.addWebhook = function (url, events) { return this._post('/api/bot/v2/webhooks', { act: 'add', url: url, events: events }); };
|
|
120
|
+
MarginPad.prototype.deleteWebhook = function (id) { return this._post('/api/bot/v2/webhooks', { act: 'delete', id: id }); };
|
|
121
|
+
MarginPad.prototype.testWebhook = function (id) { return this._post('/api/bot/v2/webhooks', { act: 'test', id: id }); };
|
|
122
|
+
|
|
123
|
+
// ── WebSocket stream ─────────────────────────────────────────────────────────────────────────────────
|
|
124
|
+
// stream(onEvent, {channels:['positions','prices'], reconnect:true}) -> returns { close() }
|
|
125
|
+
MarginPad.prototype.stream = function (onEvent, o) {
|
|
126
|
+
o = o || {}; var self = this, stopped = false, ws = null;
|
|
127
|
+
var WS = (typeof WebSocket !== 'undefined') ? WebSocket : null;
|
|
128
|
+
if (!WS && typeof require === 'function') { try { WS = require('ws'); } catch (e) { WS = null; } }
|
|
129
|
+
if (!WS) throw new Error('No WebSocket available: use Node 22+, a browser, or npm i ws');
|
|
130
|
+
var url = self.base.replace(/^http/, 'ws') + '/api/bot/v2/stream?api_key=' + encodeURIComponent(self.apiKey || '');
|
|
131
|
+
function connect() {
|
|
132
|
+
if (stopped) return;
|
|
133
|
+
ws = new WS(url);
|
|
134
|
+
ws.onopen = function () { try { ws.send(JSON.stringify({ op: 'subscribe', channels: o.channels || ['positions', 'prices'] })); } catch (e) {} };
|
|
135
|
+
ws.onmessage = function (m) { var d = null; try { d = JSON.parse(typeof m.data === 'string' ? m.data : String(m.data)); } catch (e) { return; } try { onEvent(d); } catch (e) {} };
|
|
136
|
+
ws.onclose = function () { if (!stopped && o.reconnect !== false) setTimeout(connect, 3000); };
|
|
137
|
+
ws.onerror = function () { try { ws.close(); } catch (e) {} };
|
|
138
|
+
}
|
|
139
|
+
connect();
|
|
140
|
+
var ping = setInterval(function () { try { if (ws && ws.readyState === 1) ws.send(JSON.stringify({ op: 'ping' })); } catch (e) {} }, 25000);
|
|
141
|
+
return { close: function () { stopped = true; clearInterval(ping); try { ws && ws.close(); } catch (e) {} } };
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// ── webhook receiver helper: verify X-MP-Signature over the RAW body before parsing it ───────────────
|
|
145
|
+
async function verifyWebhook(secret, rawBody, signatureHeader) {
|
|
146
|
+
var want;
|
|
147
|
+
if (typeof require === 'function') { try { var c = require('crypto'); want = 'sha256=' + c.createHmac('sha256', secret).update(rawBody).digest('hex'); } catch (e) { want = null; } }
|
|
148
|
+
if (!want) { var enc = new TextEncoder(); var key = await crypto.subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']); var sig = await crypto.subtle.sign('HMAC', key, typeof rawBody === 'string' ? enc.encode(rawBody) : rawBody); want = 'sha256=' + Array.prototype.map.call(new Uint8Array(sig), function (x) { return ('0' + x.toString(16)).slice(-2); }).join(''); }
|
|
149
|
+
return want === String(signatureHeader || '');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { MarginPad: MarginPad, MarginPadError: MarginPadError, verifyWebhook: verifyWebhook, VERSION: VERSION };
|
|
153
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "marginpad",
|
|
3
|
+
"version": "2.9.1",
|
|
4
|
+
"description": "MarginPad Bot API client: free crypto-futures paper trading for bots (REST + WebSocket + webhooks) and free market data. Zero dependencies.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": { "type": "git", "url": "https://github.com/marginpad/sdk" },
|
|
9
|
+
"homepage": "https://marginpad.io/trading-api/",
|
|
10
|
+
"bugs": { "url": "https://github.com/marginpad/sdk/issues" },
|
|
11
|
+
"keywords": ["crypto", "futures", "paper-trading", "trading-bot", "backtest", "liquidations", "bybit", "binance", "api", "mcp", "ai-agent"],
|
|
12
|
+
"engines": { "node": ">=18" },
|
|
13
|
+
"files": ["index.js", "index.d.ts", "README.md"]
|
|
14
|
+
}
|