marginpad 2.9.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -4
- package/index.d.ts +44 -7
- package/index.js +17 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -37,6 +37,31 @@ const { position } = await mp.open({ symbol: 'BTC', side: 'long', margin_usd: 10
|
|
|
37
37
|
mp.stream(ev => console.log(ev.type, ev.data)); // positions pushed on change, prices each tick
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
## The simulation is yours to set
|
|
41
|
+
<!-- sdk-sim: generated, do not hand-edit -->
|
|
42
|
+
|
|
43
|
+
Eighteen fields and eight presets, so the paper venue can be the one you will actually trade. The defaults are deliberately frictionless, and nothing you have already built moves until you change them.
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
mp.sim() # your profile, every field with its range, the presets
|
|
47
|
+
mp.sim(preset="realistic") # the honest middle, in one call
|
|
48
|
+
mp.sim(preset="bybit") # Bybit's published taker fee and maintenance margin
|
|
49
|
+
mp.sim(preset="brutal") # refusals, partial fills, a second of latency
|
|
50
|
+
mp.sim(start_balance_usd=2500, margin_enforced=True, slippage_bps=8, latency_ms=250)
|
|
51
|
+
mp.sim(reset=True) # back to the defaults
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
await mp.sim({ preset: 'realistic' });
|
|
56
|
+
await mp.sim({ start_balance_usd: 2500, margin_enforced: true, reject_rate_pct: 2 });
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The **book** (`start_balance_usd`, `margin_enforced`, `leverage_max`, `max_notional_usd`), the **fill** (`slippage`, `slippage_bps`, `impact_bps_per_100k`, `exit_slippage`, `latency_ms`, `partial_fills`, `min_fill_pct`, `reject_rate_pct`), the **cost** (`taker_bps`, `funding`, `funding_mult`) and the **liquidation** (`margin_venue`, `margin_tiers`, `mmr_pct`).
|
|
60
|
+
|
|
61
|
+
Out-of-range values are refused, never clamped, and a position keeps the simulation it was filled under for life. `margin_enforced` turns the balance into a real budget: size past it and the open comes back 402, which is the failure a live bot has to handle and a frictionless simulator never shows you. `reject_rate_pct` is the one a retry loop is worth writing for.
|
|
62
|
+
|
|
63
|
+
Every field with its range: https://marginpad.io/trading-api/#sim
|
|
64
|
+
|
|
40
65
|
## Examples
|
|
41
66
|
|
|
42
67
|
- `examples/agent_loop.py`: a signal loop that opens, manages and closes positions once a minute without polling storms.
|
|
@@ -44,13 +69,23 @@ mp.stream(ev => console.log(ev.type, ev.data)); // positions pushed
|
|
|
44
69
|
- `examples/mcp.md`: let Claude Desktop or Cursor trade on your paper account through the MCP server.
|
|
45
70
|
|
|
46
71
|
## Limits
|
|
72
|
+
<!-- sdk-limits: generated from API_PLANS, do not hand-edit -->
|
|
73
|
+
|
|
74
|
+
| Plan | Requests/min | Keys | Open positions | Books | Webhooks |
|
|
75
|
+
|---|---|---|---|---|---|
|
|
76
|
+
| Free $0 | 120 | 3 | 50 | 1 | - |
|
|
77
|
+
| Pro $39 | 600 | 10 | 200 | 5 | 3 |
|
|
78
|
+
| Max $99 | 2000 | 30 | 500 | 20 | 15 |
|
|
79
|
+
| Business $199 | 5000 | 100 | 1000 | 50 | 50 |
|
|
80
|
+
|
|
81
|
+
An API plan is its own product: a MarginPad Premium subscription buys nothing here, and an API plan buys nothing on the site. The whole engine, the simulation, several books per key, the replay, the WebSocket stream and every market-data endpoint are free - only webhooks and the AI read are gated. Trade history is 30 days on Free and 90 on a paid plan.
|
|
82
|
+
|
|
83
|
+
Measured in production: the busiest bot peaks at 66 requests a minute against the Free ceiling of 120, and no key has ever been rate-limited on any plan. Choose on keys and history, not on throughput.
|
|
47
84
|
|
|
48
|
-
|
|
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.
|
|
85
|
+
Every response carries `X-RateLimit-Limit / Remaining / Reset`; on 429 both clients wait `Retry-After` and retry once. Out-of-range input is refused with a named error (`leverage_max` carries the market's cap), never silently clamped.
|
|
51
86
|
|
|
52
87
|
## Honesty note
|
|
53
88
|
|
|
54
|
-
This is a simulator. Fees, funding and liquidation
|
|
89
|
+
This is a simulator. Fees, funding and liquidation maths mirror real exchanges and every fill condition above is yours to set - but there is no order book behind `impact_bps_per_100k`, so market impact is modelled rather than measured on depth, and a stop, target or liquidation fills at its own level without paying the crossing cost. A bot that passes here still needs a small live test. Educational use, not financial advice.
|
|
55
90
|
|
|
56
91
|
MIT licensed.
|
package/index.d.ts
CHANGED
|
@@ -1,24 +1,61 @@
|
|
|
1
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;
|
|
2
|
+
export interface OpenParams { symbol: string; side: 'long' | 'short' | 'buy' | 'sell'; 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
|
+
/** Override the account's simulation profile for THIS fill only. Writes nothing. */
|
|
4
|
+
slippage?: boolean; slippage_bps?: number; impact_bps_per_100k?: number; margin_tiers?: boolean; mmr_pct?: number; }
|
|
5
|
+
/** What actually filled, on v2 opens. position.margin_usd is already the filled size; this says what you asked for. */
|
|
6
|
+
export interface FillInfo { requested_margin_usd: number; filled_margin_usd: number; fill_pct: number; partial: boolean; slippage_pct: number; exit_slippage: boolean; latency_ms: number; taker_fee_pct: number; maintenance_margin_pct: number; maintenance_margin_from: string; }
|
|
7
|
+
/** The eight ready-made simulations. A preset starts from the defaults, so it is a clean slate plus that configuration. */
|
|
8
|
+
export type SimPreset = 'frictionless' | 'realistic' | 'binance' | 'bybit' | 'okx' | 'hyperliquid' | 'thin_book' | 'brutal';
|
|
9
|
+
/**
|
|
10
|
+
* The whole paper-trading simulation. Send any subset; out-of-range values are REFUSED, never clamped.
|
|
11
|
+
* The three nullable fields take null to mean "use the measured table / the venue", not zero.
|
|
12
|
+
*/
|
|
13
|
+
export interface SimProfile {
|
|
14
|
+
preset?: SimPreset; reset?: boolean;
|
|
15
|
+
/** the book */
|
|
16
|
+
start_balance_usd?: number; margin_enforced?: boolean; leverage_max?: number; max_notional_usd?: number;
|
|
17
|
+
/** the fill */
|
|
18
|
+
slippage?: boolean; slippage_bps?: number | null; impact_bps_per_100k?: number; exit_slippage?: boolean;
|
|
19
|
+
latency_ms?: number; partial_fills?: boolean; min_fill_pct?: number; reject_rate_pct?: number;
|
|
20
|
+
/** the cost */
|
|
21
|
+
taker_bps?: number | null; funding?: boolean; funding_mult?: number;
|
|
22
|
+
/** the liquidation */
|
|
23
|
+
margin_venue?: string; margin_tiers?: boolean; mmr_pct?: number | null;
|
|
24
|
+
}
|
|
3
25
|
export class MarginPadError extends Error { code: string; status: number; body: any; }
|
|
4
26
|
export class MarginPad {
|
|
27
|
+
static SIM_PRESETS: SimPreset[];
|
|
5
28
|
constructor(key?: string, opts?: { baseUrl?: string; autoRetry?: boolean; timeoutMs?: number });
|
|
6
29
|
price(symbol: string): Promise<{ symbol: string; price: number; change_24h_pct: number | null; ts: number }>;
|
|
30
|
+
prices(): Promise<any>;
|
|
7
31
|
klines(symbol: string, interval?: string, end?: number): Promise<any[]>;
|
|
8
|
-
time()
|
|
9
|
-
|
|
32
|
+
// NOTE: the method is serverTime(). This file declared a `time()` that the client has never had - a
|
|
33
|
+
// declaration for a method that does not exist is worse than no declaration, so it is gone.
|
|
34
|
+
serverTime(): Promise<{ server_time_ms: number; server_time_iso: string }>;
|
|
35
|
+
open(p: OpenParams): Promise<{ position?: Position; order?: any; dry_run?: boolean; fill?: FillInfo }>;
|
|
36
|
+
limitOrder(p: OpenParams): Promise<any>;
|
|
37
|
+
stopOrder(p: OpenParams & { stop_price?: number }): Promise<any>;
|
|
10
38
|
close(id: string, opts?: { pct?: number; symbol?: string; client_order_id?: string }): Promise<{ position: Position }>;
|
|
11
39
|
closeAll(): Promise<{ closed: number; positions: Position[] }>;
|
|
12
40
|
sltp(id: string, p: { sl?: number | null; tp?: number | null; trail_pct?: number | null }): Promise<{ position: Position }>;
|
|
13
|
-
positions(): Promise<{ positions: Position[] }>;
|
|
41
|
+
positions(opts?: { status?: 'open' | 'closed'; since?: number; useEtag?: boolean }): Promise<{ positions: Position[] } | null>;
|
|
14
42
|
orders(): Promise<{ orders: any[] }>;
|
|
15
43
|
cancelOrder(id: string): Promise<{ order_id: string; status: string }>;
|
|
16
44
|
modifyOrder(id: string, p: any): Promise<any>;
|
|
17
|
-
trades(
|
|
45
|
+
trades(limit?: number, before?: number): Promise<{ trades: Position[]; next_before: number | null }>;
|
|
18
46
|
account(): Promise<any>;
|
|
19
47
|
balance(): Promise<any>;
|
|
20
|
-
|
|
21
|
-
|
|
48
|
+
equity(days?: number, stepMin?: number): Promise<any>;
|
|
49
|
+
reset(): Promise<any>;
|
|
50
|
+
accounts(): Promise<any>;
|
|
51
|
+
markets(assetClass?: string): Promise<any>;
|
|
52
|
+
/** The fee schedule. fees() reads, fees('binance') sets the account default, fees('') goes back to ours. */
|
|
53
|
+
fees(venue?: string): Promise<any>;
|
|
54
|
+
/** Three switches of the simulation, kept for compatibility. sim() is all of it. */
|
|
55
|
+
realism(o?: { slippage?: boolean; margin_tiers?: boolean; margin_venue?: string }): Promise<any>;
|
|
56
|
+
/** The whole simulation: read with no argument, set with any subset or a preset. */
|
|
57
|
+
sim(o?: SimProfile): Promise<any>;
|
|
58
|
+
/** The trading report. `costs` (fees, funding, gross vs net, kept_pct) is on every plan. */
|
|
22
59
|
report(days?: number): Promise<any>;
|
|
23
60
|
usage(): Promise<any>;
|
|
24
61
|
webhooks(): Promise<any>;
|
package/index.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
else root.MarginPadSDK = factory();
|
|
18
18
|
})(typeof self !== 'undefined' ? self : this, function () {
|
|
19
19
|
'use strict';
|
|
20
|
-
var VERSION = '
|
|
20
|
+
var VERSION = '3.0.0';
|
|
21
21
|
|
|
22
22
|
function MarginPadError(code, message, status, extra) {
|
|
23
23
|
var e = new Error(code + ': ' + (message || code));
|
|
@@ -114,6 +114,22 @@
|
|
|
114
114
|
// Pass the same fields to open() to override the account for one fill only.
|
|
115
115
|
MarginPad.prototype.realism = function (o) { return o === undefined ? this._get('/api/bot/v2/realism') : this._post('/api/bot/v2/realism', o); };
|
|
116
116
|
|
|
117
|
+
// ── the whole simulation (3.0) ───────────────────────────────────────────
|
|
118
|
+
// realism() above is three switches of this; sim() is all eighteen fields. The defaults are deliberately
|
|
119
|
+
// easy - a market order fills whole at the live price, maintenance margin is a flat 0.5%, the balance never
|
|
120
|
+
// refuses an open, there is no latency and no size impact - which is right for getting a bot RUNNING and
|
|
121
|
+
// optimistic for judging its edge.
|
|
122
|
+
// await c.sim() -> your profile, every field with its range, the presets
|
|
123
|
+
// await c.sim({ preset: 'realistic' }) -> the honest middle, in one call
|
|
124
|
+
// await c.sim({ preset: 'bybit' }) -> Bybit's published fee and maintenance margin
|
|
125
|
+
// await c.sim({ preset: 'brutal' }) -> worse than anything you will meet; if it survives this, it survives
|
|
126
|
+
// await c.sim({ start_balance_usd: 2500, margin_enforced: true, slippage_bps: 8, latency_ms: 250 })
|
|
127
|
+
// Out-of-range values are REFUSED, never clamped, so a strategy is never measured under a setting you did
|
|
128
|
+
// not choose. A position keeps the simulation it was filled under, for life.
|
|
129
|
+
MarginPad.prototype.sim = function (o) { return o === undefined ? this._get('/api/bot/v2/sim') : this._post('/api/bot/v2/sim', o); };
|
|
130
|
+
// The eight presets, so an editor can complete them and a script can loop over them.
|
|
131
|
+
MarginPad.SIM_PRESETS = ['frictionless', 'realistic', 'binance', 'bybit', 'okx', 'hyperliquid', 'thin_book', 'brutal'];
|
|
132
|
+
|
|
117
133
|
// ── webhooks (API Pro and up) ───────────────────────────────────────────────────────────────────────────────
|
|
118
134
|
MarginPad.prototype.webhooks = function () { return this._get('/api/bot/v2/webhooks'); };
|
|
119
135
|
MarginPad.prototype.addWebhook = function (url, events) { return this._post('/api/bot/v2/webhooks', { act: 'add', url: url, events: events }); };
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "marginpad",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "MarginPad Bot API client: free crypto-futures paper trading for bots (REST + WebSocket + webhooks) and free market data. Zero dependencies.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"repository": { "type": "git", "url": "https://github.com/
|
|
8
|
+
"repository": { "type": "git", "url": "git+https://github.com/Marginpad/sdk.git" },
|
|
9
9
|
"homepage": "https://marginpad.io/trading-api/",
|
|
10
|
-
"bugs": { "url": "https://github.com/
|
|
10
|
+
"bugs": { "url": "https://github.com/Marginpad/sdk/issues" },
|
|
11
11
|
"keywords": ["crypto", "futures", "paper-trading", "trading-bot", "backtest", "liquidations", "bybit", "binance", "api", "mcp", "ai-agent"],
|
|
12
12
|
"engines": { "node": ">=18" },
|
|
13
13
|
"files": ["index.js", "index.d.ts", "README.md"]
|