@farazirfan/costar-server-executor 1.7.19 → 1.7.21
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/dist/cli/skill-cmd.d.ts.map +1 -1
- package/dist/cli/skill-cmd.js +6 -2
- package/dist/cli/skill-cmd.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +9 -2
- package/dist/server.js.map +1 -1
- package/dist/web-server.d.ts.map +1 -1
- package/dist/web-server.js +99 -0
- package/dist/web-server.js.map +1 -1
- package/package.json +1 -1
- package/public/index.html +203 -0
- package/skills/coinbase/LEARNING.md +117 -0
- package/skills/coinbase/SKILL.md +217 -0
- package/skills/coinbase/references/api-reference.md +124 -0
- package/skills/coinbase/references/order-types.md +150 -0
- package/skills/coinbase/references/products.md +93 -0
- package/skills/coinbase/scripts/coinbase-trade.ts +274 -0
- package/skills/trading/SKILL.md +2 -4
package/public/index.html
CHANGED
|
@@ -1142,6 +1142,30 @@
|
|
|
1142
1142
|
|
|
1143
1143
|
<!-- ═══ Settings Page ═══ -->
|
|
1144
1144
|
<div class="page" id="page-settings">
|
|
1145
|
+
<div class="card">
|
|
1146
|
+
<div class="card-header">
|
|
1147
|
+
<h3>Environment Variables</h3>
|
|
1148
|
+
<div style="display:flex;gap:8px;">
|
|
1149
|
+
<button class="btn btn-sm" onclick="loadEnvVariables()">Refresh</button>
|
|
1150
|
+
<button class="btn btn-sm btn-primary" onclick="openEnvModal()">+ Add Variable</button>
|
|
1151
|
+
</div>
|
|
1152
|
+
</div>
|
|
1153
|
+
<div class="table-wrap">
|
|
1154
|
+
<table>
|
|
1155
|
+
<thead>
|
|
1156
|
+
<tr>
|
|
1157
|
+
<th>Key</th>
|
|
1158
|
+
<th>Value</th>
|
|
1159
|
+
<th>Actions</th>
|
|
1160
|
+
</tr>
|
|
1161
|
+
</thead>
|
|
1162
|
+
<tbody id="env-table-body">
|
|
1163
|
+
<tr><td colspan="3"><div class="loading-center"><div class="spinner"></div></div></td></tr>
|
|
1164
|
+
</tbody>
|
|
1165
|
+
</table>
|
|
1166
|
+
</div>
|
|
1167
|
+
</div>
|
|
1168
|
+
|
|
1145
1169
|
<div class="card">
|
|
1146
1170
|
<div class="card-header">
|
|
1147
1171
|
<h3>Configuration</h3>
|
|
@@ -1211,6 +1235,25 @@
|
|
|
1211
1235
|
</div>
|
|
1212
1236
|
</div>
|
|
1213
1237
|
|
|
1238
|
+
<!-- Environment Variable Modal -->
|
|
1239
|
+
<div class="modal-overlay" id="env-modal">
|
|
1240
|
+
<div class="modal">
|
|
1241
|
+
<h3 id="env-modal-title">Add Environment Variable</h3>
|
|
1242
|
+
<div class="form-group">
|
|
1243
|
+
<label>Key (UPPERCASE_WITH_UNDERSCORES)</label>
|
|
1244
|
+
<input type="text" id="env-key" placeholder="e.g. MY_API_KEY" style="text-transform:uppercase;" />
|
|
1245
|
+
</div>
|
|
1246
|
+
<div class="form-group">
|
|
1247
|
+
<label>Value</label>
|
|
1248
|
+
<input type="text" id="env-value" placeholder="Enter value..." />
|
|
1249
|
+
</div>
|
|
1250
|
+
<div class="modal-actions">
|
|
1251
|
+
<button class="btn" onclick="closeEnvModal()">Cancel</button>
|
|
1252
|
+
<button class="btn btn-primary" onclick="saveEnvVariable()">Save</button>
|
|
1253
|
+
</div>
|
|
1254
|
+
</div>
|
|
1255
|
+
</div>
|
|
1256
|
+
|
|
1214
1257
|
<script>
|
|
1215
1258
|
/* ═══════════════════════════════════════════════
|
|
1216
1259
|
* CoStar Dashboard - Client-side JS
|
|
@@ -2072,6 +2115,166 @@
|
|
|
2072
2115
|
} catch (err) {
|
|
2073
2116
|
document.getElementById('settings-config').innerHTML = `<div style="color:var(--red);padding:12px;">Error: ${esc(err.message)}</div>`;
|
|
2074
2117
|
}
|
|
2118
|
+
|
|
2119
|
+
// Also load env variables
|
|
2120
|
+
loadEnvVariables();
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
// ─── Environment Variables ───────────────────────
|
|
2124
|
+
let envVariables = [];
|
|
2125
|
+
|
|
2126
|
+
async function loadEnvVariables() {
|
|
2127
|
+
try {
|
|
2128
|
+
const data = await api('/api/env');
|
|
2129
|
+
envVariables = data.variables || [];
|
|
2130
|
+
const tbody = document.getElementById('env-table-body');
|
|
2131
|
+
|
|
2132
|
+
if (envVariables.length === 0) {
|
|
2133
|
+
tbody.innerHTML = `<tr><td colspan="3"><div class="empty-state"><div class="empty-icon">🔧</div><p>No environment variables configured.</p></div></td></tr>`;
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
tbody.innerHTML = envVariables.map((env, idx) => {
|
|
2138
|
+
const valueDisplay = env.masked
|
|
2139
|
+
? `<span style="font-family:var(--mono);color:var(--text-muted);" id="env-value-${idx}">${esc(env.value)}</span>
|
|
2140
|
+
<button class="btn btn-sm" onclick="revealEnvValue('${esc(env.key)}', ${idx})" id="env-reveal-${idx}" style="margin-left:8px;">👁 Reveal</button>`
|
|
2141
|
+
: `<span style="font-family:var(--mono);">${esc(env.value)}</span>`;
|
|
2142
|
+
|
|
2143
|
+
return `<tr>
|
|
2144
|
+
<td><code style="font-size:13px;color:var(--accent);">${esc(env.key)}</code></td>
|
|
2145
|
+
<td>${valueDisplay}</td>
|
|
2146
|
+
<td>
|
|
2147
|
+
<button class="btn btn-sm" onclick="editEnvVariable('${esc(env.key)}')">Edit</button>
|
|
2148
|
+
<button class="btn btn-sm btn-danger" onclick="deleteEnvVariable('${esc(env.key)}')">Delete</button>
|
|
2149
|
+
</td>
|
|
2150
|
+
</tr>`;
|
|
2151
|
+
}).join('');
|
|
2152
|
+
} catch (err) {
|
|
2153
|
+
document.getElementById('env-table-body').innerHTML = `<tr><td colspan="3" style="color:var(--red);padding:16px;">Error: ${esc(err.message)}</td></tr>`;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
async function revealEnvValue(key, idx) {
|
|
2158
|
+
try {
|
|
2159
|
+
const btn = document.getElementById(`env-reveal-${idx}`);
|
|
2160
|
+
const valueEl = document.getElementById(`env-value-${idx}`);
|
|
2161
|
+
|
|
2162
|
+
if (!btn || !valueEl) return;
|
|
2163
|
+
|
|
2164
|
+
// If already revealed, hide it again
|
|
2165
|
+
if (btn.textContent.includes('Hide')) {
|
|
2166
|
+
const maskedVar = envVariables[idx];
|
|
2167
|
+
valueEl.textContent = maskedVar.value;
|
|
2168
|
+
btn.innerHTML = '👁 Reveal';
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
btn.disabled = true;
|
|
2173
|
+
btn.textContent = 'Loading...';
|
|
2174
|
+
|
|
2175
|
+
const data = await api(`/api/env/${encodeURIComponent(key)}`);
|
|
2176
|
+
valueEl.textContent = data.value;
|
|
2177
|
+
btn.disabled = false;
|
|
2178
|
+
btn.innerHTML = '👁 Hide';
|
|
2179
|
+
|
|
2180
|
+
// Auto-hide after 10 seconds
|
|
2181
|
+
setTimeout(() => {
|
|
2182
|
+
if (btn.textContent.includes('Hide')) {
|
|
2183
|
+
const maskedVar = envVariables[idx];
|
|
2184
|
+
valueEl.textContent = maskedVar.value;
|
|
2185
|
+
btn.innerHTML = '👁 Reveal';
|
|
2186
|
+
}
|
|
2187
|
+
}, 10000);
|
|
2188
|
+
} catch (err) {
|
|
2189
|
+
alert('Failed to reveal value: ' + err.message);
|
|
2190
|
+
const btn = document.getElementById(`env-reveal-${idx}`);
|
|
2191
|
+
if (btn) {
|
|
2192
|
+
btn.disabled = false;
|
|
2193
|
+
btn.innerHTML = '👁 Reveal';
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
function openEnvModal() {
|
|
2199
|
+
document.getElementById('env-modal').classList.add('open');
|
|
2200
|
+
document.getElementById('env-modal-title').textContent = 'Add Environment Variable';
|
|
2201
|
+
document.getElementById('env-key').value = '';
|
|
2202
|
+
document.getElementById('env-key').disabled = false;
|
|
2203
|
+
document.getElementById('env-value').value = '';
|
|
2204
|
+
document.getElementById('env-key').dataset.editMode = 'false';
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
function closeEnvModal() {
|
|
2208
|
+
document.getElementById('env-modal').classList.remove('open');
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
function editEnvVariable(key) {
|
|
2212
|
+
const envVar = envVariables.find(e => e.key === key);
|
|
2213
|
+
if (!envVar) return;
|
|
2214
|
+
|
|
2215
|
+
document.getElementById('env-modal').classList.add('open');
|
|
2216
|
+
document.getElementById('env-modal-title').textContent = 'Edit Environment Variable';
|
|
2217
|
+
document.getElementById('env-key').value = key;
|
|
2218
|
+
document.getElementById('env-key').disabled = true; // Can't change key when editing
|
|
2219
|
+
document.getElementById('env-value').value = ''; // Don't pre-fill for security
|
|
2220
|
+
document.getElementById('env-value').placeholder = 'Enter new value...';
|
|
2221
|
+
document.getElementById('env-key').dataset.editMode = 'true';
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
async function saveEnvVariable() {
|
|
2225
|
+
const keyInput = document.getElementById('env-key');
|
|
2226
|
+
const valueInput = document.getElementById('env-value');
|
|
2227
|
+
const key = keyInput.value.trim().toUpperCase();
|
|
2228
|
+
const value = valueInput.value.trim();
|
|
2229
|
+
const isEdit = keyInput.dataset.editMode === 'true';
|
|
2230
|
+
|
|
2231
|
+
if (!key) {
|
|
2232
|
+
alert('Key is required.');
|
|
2233
|
+
return;
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
if (!value) {
|
|
2237
|
+
alert('Value is required.');
|
|
2238
|
+
return;
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
// Validate key format
|
|
2242
|
+
if (!/^[A-Z0-9_]+$/.test(key)) {
|
|
2243
|
+
alert('Key must contain only uppercase letters, numbers, and underscores.');
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
try {
|
|
2248
|
+
if (isEdit) {
|
|
2249
|
+
await api(`/api/env/${encodeURIComponent(key)}`, {
|
|
2250
|
+
method: 'PUT',
|
|
2251
|
+
body: JSON.stringify({ value }),
|
|
2252
|
+
});
|
|
2253
|
+
} else {
|
|
2254
|
+
await api('/api/env', {
|
|
2255
|
+
method: 'POST',
|
|
2256
|
+
body: JSON.stringify({ key, value }),
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
closeEnvModal();
|
|
2261
|
+
loadEnvVariables();
|
|
2262
|
+
} catch (err) {
|
|
2263
|
+
alert('Failed to save: ' + err.message);
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
async function deleteEnvVariable(key) {
|
|
2268
|
+
if (!confirm(`Delete environment variable "${key}"?\n\nThis will remove it from ~/.costar/.env and the current process.`)) {
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
try {
|
|
2273
|
+
await api(`/api/env/${encodeURIComponent(key)}`, { method: 'DELETE' });
|
|
2274
|
+
loadEnvVariables();
|
|
2275
|
+
} catch (err) {
|
|
2276
|
+
alert('Failed to delete: ' + err.message);
|
|
2277
|
+
}
|
|
2075
2278
|
}
|
|
2076
2279
|
|
|
2077
2280
|
// ─── Utilities ───────────────────────────────────
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Coinbase Trading Journal
|
|
2
|
+
|
|
3
|
+
> **Read this before every Coinbase operation.** Update after every trade.
|
|
4
|
+
> Never delete entries — mark outdated ones as `[OUTDATED]` with date and reason.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Trade Log
|
|
9
|
+
|
|
10
|
+
| Date | Pair | Side | Type | Size | Fill Price | Fees | P&L | Notes |
|
|
11
|
+
|------|------|------|------|------|------------|------|-----|-------|
|
|
12
|
+
| *(No trades yet)* | | | | | | | | |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## API Quirks & Gotchas
|
|
17
|
+
|
|
18
|
+
> Things that tripped you up. Save others (your future self) the pain.
|
|
19
|
+
|
|
20
|
+
1. All numeric values must be strings — `"100.00"` not `100`
|
|
21
|
+
2. HTTP 200 doesn't mean success — always check `order.success`
|
|
22
|
+
3. Duplicate `client_order_id` returns existing order, not error
|
|
23
|
+
|
|
24
|
+
*(Add more as you discover them)*
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Fee Insights
|
|
29
|
+
|
|
30
|
+
> Track fee rates and patterns to optimize execution.
|
|
31
|
+
|
|
32
|
+
- **Current fee tier:** *Unknown — check with `getTransactionSummary()`*
|
|
33
|
+
- **Maker rate:** *TBD*
|
|
34
|
+
- **Taker rate:** *TBD*
|
|
35
|
+
|
|
36
|
+
<!--
|
|
37
|
+
- [DATE] Fee observation. Example: post_only limit orders save 0.2% in fees vs market orders
|
|
38
|
+
-->
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Order Patterns That Work
|
|
43
|
+
|
|
44
|
+
> Reusable patterns you've validated with real trades.
|
|
45
|
+
|
|
46
|
+
<!--
|
|
47
|
+
- [DATE] PATTERN: description. PAIR: which pair. RESULT: outcome.
|
|
48
|
+
Example:
|
|
49
|
+
- [2026-02-14] PATTERN: Limit buy 0.5% below current price with GTD 1-hour expiry. PAIR: BTC-USD. RESULT: Filled 70% of the time, saved avg $15 in slippage vs market orders.
|
|
50
|
+
-->
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Order Patterns That Failed
|
|
55
|
+
|
|
56
|
+
> Equally important — what NOT to do.
|
|
57
|
+
|
|
58
|
+
<!--
|
|
59
|
+
- [DATE] PATTERN: description. PAIR: which pair. PROBLEM: what went wrong.
|
|
60
|
+
Example:
|
|
61
|
+
- [2026-02-14] PATTERN: Market sell large SOL position ($5000). PAIR: SOL-USD. PROBLEM: 1.2% slippage due to thin order book. Should have used limit or split into smaller orders.
|
|
62
|
+
-->
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Product-Specific Notes
|
|
67
|
+
|
|
68
|
+
> Observations about specific trading pairs.
|
|
69
|
+
|
|
70
|
+
<!--
|
|
71
|
+
- [DATE] PRODUCT: observation
|
|
72
|
+
Example:
|
|
73
|
+
- [2026-02-14] BTC-USD: min order $1, base_increment 0.00000001, quote_increment 0.01
|
|
74
|
+
- [2026-02-14] SOL-USD: wider spreads after hours, better to trade during US market hours
|
|
75
|
+
-->
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Scripts I've Created
|
|
80
|
+
|
|
81
|
+
| Script | Purpose | Works Well? | Last Modified |
|
|
82
|
+
|--------|---------|-------------|---------------|
|
|
83
|
+
| *(None yet)* | | | |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Mistakes to Never Repeat
|
|
88
|
+
|
|
89
|
+
1. *(Will accumulate from real trading)*
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Performance Summary
|
|
94
|
+
|
|
95
|
+
### All Time
|
|
96
|
+
- **Total Trades:** 0
|
|
97
|
+
- **Total Fees Paid:** $0
|
|
98
|
+
- **Net P&L:** $0
|
|
99
|
+
- **Avg Slippage:** N/A
|
|
100
|
+
|
|
101
|
+
### Current Week
|
|
102
|
+
- **Trades:** 0
|
|
103
|
+
- **Fees:** $0
|
|
104
|
+
- **P&L:** $0
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Open Questions
|
|
109
|
+
|
|
110
|
+
1. What are the actual minimum order sizes for each product?
|
|
111
|
+
2. How much slippage to expect on market orders for different pair sizes?
|
|
112
|
+
3. What's the optimal order type for different trade sizes?
|
|
113
|
+
4. When does `post_only` get rejected vs filled?
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
> Every entry makes your next trade better. Record everything.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coinbase
|
|
3
|
+
description: "Execute real trades on Coinbase Advanced Trade. Supports market, limit, stop-limit, and bracket orders for crypto. Handles account balances, order management, price data, portfolio tracking, and fee estimation. Use when the user wants to buy/sell crypto, check balances, place orders, manage positions, or do anything on Coinbase."
|
|
4
|
+
metadata:
|
|
5
|
+
emoji: "🪙"
|
|
6
|
+
requires:
|
|
7
|
+
env: ["COINBASE_API_KEY", "COINBASE_API_SECRET"]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Coinbase Trading Executor
|
|
11
|
+
|
|
12
|
+
You can execute real trades on Coinbase using the Advanced Trade API. This is real money — be precise, confirm with the user, and never skip risk checks.
|
|
13
|
+
|
|
14
|
+
## CRITICAL: Always Read LEARNING.md First
|
|
15
|
+
|
|
16
|
+
**Before ANY Coinbase action, read [LEARNING.md](LEARNING.md).** It contains API quirks you've discovered, order patterns that work, mistakes to avoid, and fee insights from real trades.
|
|
17
|
+
|
|
18
|
+
## Authentication
|
|
19
|
+
|
|
20
|
+
The user's Coinbase API credentials are available as environment variables:
|
|
21
|
+
- `COINBASE_API_KEY` — format: `organizations/{org_id}/apiKeys/{key_id}`
|
|
22
|
+
- `COINBASE_API_SECRET` — EC private key in PEM format
|
|
23
|
+
|
|
24
|
+
These use **CDP (Coinbase Developer Platform) keys** with **ES256 JWT** authentication. The `coinbase-api` npm package handles JWT generation internally.
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
Before first use, ensure the SDK is installed in the workspace:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm list coinbase-api 2>/dev/null || npm install coinbase-api
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## How to Execute Trades
|
|
35
|
+
|
|
36
|
+
All Coinbase operations use TypeScript/JavaScript scripts via `execute_code` or shell commands. Use the `coinbase-api` package:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { CBAdvancedTradeClient } from 'coinbase-api';
|
|
40
|
+
|
|
41
|
+
const client = new CBAdvancedTradeClient({
|
|
42
|
+
apiKey: process.env.COINBASE_API_KEY,
|
|
43
|
+
apiSecret: process.env.COINBASE_API_SECRET,
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Execution Workflow
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
- [ ] Step 1: Read LEARNING.md
|
|
51
|
+
- [ ] Step 2: Verify balances and account status
|
|
52
|
+
- [ ] Step 3: Get current price and market data
|
|
53
|
+
- [ ] Step 4: Preview the order (estimate fees/slippage)
|
|
54
|
+
- [ ] Step 5: Execute the order
|
|
55
|
+
- [ ] Step 6: Verify order fill and record result
|
|
56
|
+
- [ ] Step 7: Update LEARNING.md
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Step 2: Check Balances
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
const accounts = await client.getAccounts();
|
|
63
|
+
for (const acct of accounts.accounts) {
|
|
64
|
+
if (parseFloat(acct.available_balance.value) > 0) {
|
|
65
|
+
console.log(`${acct.currency}: ${acct.available_balance.value}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Step 3: Get Price Data
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Current price
|
|
74
|
+
const product = await client.getProduct({ product_id: 'BTC-USD' });
|
|
75
|
+
console.log(`Price: $${product.price}, 24h change: ${product.price_percentage_change_24h}%`);
|
|
76
|
+
|
|
77
|
+
// Best bid/ask spread
|
|
78
|
+
const spread = await client.getBestBidAsk({ product_ids: ['BTC-USD'] });
|
|
79
|
+
|
|
80
|
+
// OHLCV candles (last 24h, 1-hour granularity)
|
|
81
|
+
const candles = await client.getProductCandles({
|
|
82
|
+
product_id: 'BTC-USD',
|
|
83
|
+
start: String(Math.floor(Date.now() / 1000) - 86400),
|
|
84
|
+
end: String(Math.floor(Date.now() / 1000)),
|
|
85
|
+
granularity: 'ONE_HOUR',
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Step 4: Preview Order
|
|
90
|
+
|
|
91
|
+
**Always preview before executing.** This shows estimated fees and slippage:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const preview = await client.previewOrder({
|
|
95
|
+
product_id: 'BTC-USD',
|
|
96
|
+
side: 'BUY',
|
|
97
|
+
order_configuration: {
|
|
98
|
+
market_market_ioc: { quote_size: '100.00' },
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
// Shows: quote_size, base_size, best_bid, best_ask, total_fees, slippage
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Step 5: Place Orders
|
|
105
|
+
|
|
106
|
+
See [references/order-types.md](references/order-types.md) for all order types and configurations.
|
|
107
|
+
|
|
108
|
+
**Market Buy** (spend $X of quote currency):
|
|
109
|
+
```typescript
|
|
110
|
+
const order = await client.submitOrder({
|
|
111
|
+
client_order_id: crypto.randomUUID(),
|
|
112
|
+
product_id: 'BTC-USD',
|
|
113
|
+
side: 'BUY',
|
|
114
|
+
order_configuration: {
|
|
115
|
+
market_market_ioc: { quote_size: '100.00' }, // spend $100
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Market Sell** (sell X units of base asset):
|
|
121
|
+
```typescript
|
|
122
|
+
const order = await client.submitOrder({
|
|
123
|
+
client_order_id: crypto.randomUUID(),
|
|
124
|
+
product_id: 'BTC-USD',
|
|
125
|
+
side: 'SELL',
|
|
126
|
+
order_configuration: {
|
|
127
|
+
market_market_ioc: { base_size: '0.001' }, // sell 0.001 BTC
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Limit Buy** (Good Till Cancel):
|
|
133
|
+
```typescript
|
|
134
|
+
const order = await client.submitOrder({
|
|
135
|
+
client_order_id: crypto.randomUUID(),
|
|
136
|
+
product_id: 'BTC-USD',
|
|
137
|
+
side: 'BUY',
|
|
138
|
+
order_configuration: {
|
|
139
|
+
limit_limit_gtc: {
|
|
140
|
+
base_size: '0.001',
|
|
141
|
+
limit_price: '95000.00',
|
|
142
|
+
post_only: false,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Step 6: Verify Order
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Check response immediately
|
|
152
|
+
if (order.success) {
|
|
153
|
+
const orderId = order.success_response.order_id;
|
|
154
|
+
// Get fill details
|
|
155
|
+
const status = await client.getOrder({ order_id: orderId });
|
|
156
|
+
console.log(`Status: ${status.status}, Filled: ${status.filled_size} @ ${status.average_filled_price}`);
|
|
157
|
+
} else {
|
|
158
|
+
console.error(`Order failed: ${order.error_response.message}`);
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**IMPORTANT:** Coinbase returns HTTP 200 even on order failure. Always check `order.success === true`.
|
|
163
|
+
|
|
164
|
+
### Step 7: Update LEARNING.md
|
|
165
|
+
|
|
166
|
+
After every trade, record: asset, side, type, size, fill price, fees, outcome, and any API quirks encountered.
|
|
167
|
+
|
|
168
|
+
## Order Management
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// List open orders
|
|
172
|
+
const orders = await client.getOrders({ order_status: ['OPEN', 'PENDING'] });
|
|
173
|
+
|
|
174
|
+
// Cancel specific orders
|
|
175
|
+
const cancel = await client.cancelOrders({ order_ids: ['order-id-here'] });
|
|
176
|
+
|
|
177
|
+
// Get fills (trade executions)
|
|
178
|
+
const fills = await client.getFills({ order_id: 'order-id-here' });
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Portfolio & Fees
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// Portfolio breakdown
|
|
185
|
+
const portfolios = await client.getPortfolios({});
|
|
186
|
+
const breakdown = await client.getPortfolioBreakdown({ portfolio_id: portfolios.portfolios[0].uuid });
|
|
187
|
+
|
|
188
|
+
// Fee tier info
|
|
189
|
+
const fees = await client.getTransactionSummary({});
|
|
190
|
+
// Shows: fee_tier, total_volume, maker_fee_rate, taker_fee_rate
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Critical Rules
|
|
194
|
+
|
|
195
|
+
1. **ALWAYS preview orders before executing** — know the fees and slippage
|
|
196
|
+
2. **All values are STRINGS** — `"100.00"` not `100`. The API rejects numbers.
|
|
197
|
+
3. **client_order_id must be unique** — always use `crypto.randomUUID()`
|
|
198
|
+
4. **Check `order.success`** — HTTP 200 doesn't mean order succeeded
|
|
199
|
+
5. **Product ID format** — always `BASE-QUOTE` e.g. `BTC-USD`, `ETH-USD`, `SOL-USD`
|
|
200
|
+
6. **Minimum order sizes vary** — check product details before ordering
|
|
201
|
+
7. **Rate limits** — ~10 requests/second for private endpoints
|
|
202
|
+
8. **Never place orders in a loop without delays** — respect rate limits
|
|
203
|
+
9. **Log everything in LEARNING.md** — every trade, every error, every quirk
|
|
204
|
+
|
|
205
|
+
## Reference Files
|
|
206
|
+
|
|
207
|
+
- [references/order-types.md](references/order-types.md) — All order configurations with examples
|
|
208
|
+
- [references/api-reference.md](references/api-reference.md) — Complete API endpoint list
|
|
209
|
+
- [references/products.md](references/products.md) — Common trading pairs and their details
|
|
210
|
+
|
|
211
|
+
## Self-Evolution
|
|
212
|
+
|
|
213
|
+
You can modify any file in this skill:
|
|
214
|
+
- Found an API quirk? Update LEARNING.md and references
|
|
215
|
+
- Built a reusable script? Save it in `scripts/`
|
|
216
|
+
- Discovered a better workflow? Update this SKILL.md
|
|
217
|
+
- New order type pattern? Add to `references/order-types.md`
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Coinbase Advanced Trade API Reference
|
|
2
|
+
|
|
3
|
+
> Agent-editable. Add notes and quirks as you discover them.
|
|
4
|
+
> Last updated: Initial version
|
|
5
|
+
|
|
6
|
+
## Base URL
|
|
7
|
+
|
|
8
|
+
`https://api.coinbase.com/api/v3/brokerage/`
|
|
9
|
+
|
|
10
|
+
## SDK Methods (coinbase-api package)
|
|
11
|
+
|
|
12
|
+
### Accounts & Balances
|
|
13
|
+
|
|
14
|
+
| Method | Description |
|
|
15
|
+
|--------|-------------|
|
|
16
|
+
| `client.getAccounts()` | List all accounts with balances |
|
|
17
|
+
| `client.getAccount({ account_id })` | Get specific account |
|
|
18
|
+
|
|
19
|
+
### Market Data
|
|
20
|
+
|
|
21
|
+
| Method | Description |
|
|
22
|
+
|--------|-------------|
|
|
23
|
+
| `client.getProducts({})` | List all trading pairs |
|
|
24
|
+
| `client.getProduct({ product_id })` | Get product details + current price |
|
|
25
|
+
| `client.getProductCandles({ product_id, start, end, granularity })` | OHLCV candle data |
|
|
26
|
+
| `client.getProductBook({ product_id, limit })` | Order book depth |
|
|
27
|
+
| `client.getBestBidAsk({ product_ids: [...] })` | Best bid/ask spread |
|
|
28
|
+
| `client.getMarketTrades({ product_id, limit })` | Recent market trades |
|
|
29
|
+
|
|
30
|
+
### Orders
|
|
31
|
+
|
|
32
|
+
| Method | Description |
|
|
33
|
+
|--------|-------------|
|
|
34
|
+
| `client.submitOrder({ ... })` | Place an order |
|
|
35
|
+
| `client.previewOrder({ ... })` | Preview order (estimate fees/slippage) |
|
|
36
|
+
| `client.getOrder({ order_id })` | Get order details |
|
|
37
|
+
| `client.getOrders({ order_status, product_id, ... })` | List orders with filters |
|
|
38
|
+
| `client.cancelOrders({ order_ids: [...] })` | Cancel one or more orders |
|
|
39
|
+
| `client.getFills({ order_id, product_id, ... })` | Get trade fills |
|
|
40
|
+
|
|
41
|
+
### Portfolios
|
|
42
|
+
|
|
43
|
+
| Method | Description |
|
|
44
|
+
|--------|-------------|
|
|
45
|
+
| `client.getPortfolios({})` | List portfolios |
|
|
46
|
+
| `client.getPortfolioBreakdown({ portfolio_id })` | Detailed breakdown with positions |
|
|
47
|
+
|
|
48
|
+
### Fees
|
|
49
|
+
|
|
50
|
+
| Method | Description |
|
|
51
|
+
|--------|-------------|
|
|
52
|
+
| `client.getTransactionSummary({})` | Fee tier, volume, maker/taker rates |
|
|
53
|
+
|
|
54
|
+
## Candle Granularity Options
|
|
55
|
+
|
|
56
|
+
| Value | Period |
|
|
57
|
+
|-------|--------|
|
|
58
|
+
| `ONE_MINUTE` | 1 min |
|
|
59
|
+
| `FIVE_MINUTE` | 5 min |
|
|
60
|
+
| `FIFTEEN_MINUTE` | 15 min |
|
|
61
|
+
| `THIRTY_MINUTE` | 30 min |
|
|
62
|
+
| `ONE_HOUR` | 1 hr |
|
|
63
|
+
| `TWO_HOUR` | 2 hr |
|
|
64
|
+
| `SIX_HOUR` | 6 hr |
|
|
65
|
+
| `ONE_DAY` | 1 day |
|
|
66
|
+
|
|
67
|
+
## Candle Timestamps
|
|
68
|
+
|
|
69
|
+
- `start` and `end` are **Unix timestamps in seconds** (as strings)
|
|
70
|
+
- Example: last 24 hours
|
|
71
|
+
```typescript
|
|
72
|
+
start: String(Math.floor(Date.now() / 1000) - 86400),
|
|
73
|
+
end: String(Math.floor(Date.now() / 1000)),
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Rate Limits
|
|
77
|
+
|
|
78
|
+
| Type | Limit |
|
|
79
|
+
|------|-------|
|
|
80
|
+
| Private endpoints | ~10 requests/second |
|
|
81
|
+
| Private endpoints | ~5,000 requests/hour |
|
|
82
|
+
| Public endpoints | ~10,000 requests/hour |
|
|
83
|
+
|
|
84
|
+
Public endpoints have 1-second cache. Use WebSocket for real-time data.
|
|
85
|
+
|
|
86
|
+
## Error Handling
|
|
87
|
+
|
|
88
|
+
The API returns HTTP 200 for most responses, including failures. Always check:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
if (order.success) {
|
|
92
|
+
// order.success_response.order_id
|
|
93
|
+
} else {
|
|
94
|
+
// order.error_response.error, order.error_response.message
|
|
95
|
+
// Common: INSUFFICIENT_FUND, INVALID_LIMIT_PRICE_POST_ONLY, UNKNOWN_FAILURE_REASON
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Common Error Codes
|
|
100
|
+
|
|
101
|
+
| Error | Meaning | Fix |
|
|
102
|
+
|-------|---------|-----|
|
|
103
|
+
| `INSUFFICIENT_FUND` | Not enough balance | Check available balance first |
|
|
104
|
+
| `INVALID_LIMIT_PRICE_POST_ONLY` | Post-only order would immediately fill | Adjust price or set `post_only: false` |
|
|
105
|
+
| `UNKNOWN_FAILURE_REASON` | Generic failure | Check order params, product status |
|
|
106
|
+
| `UNSUPPORTED_ORDER_CONFIGURATION` | Invalid order config | Check order type and required fields |
|
|
107
|
+
| `INVALID_PRODUCT_ID` | Product doesn't exist | Verify product_id with `getProducts()` |
|
|
108
|
+
|
|
109
|
+
## WebSocket Channels (Real-Time)
|
|
110
|
+
|
|
111
|
+
| Channel | Auth | Description |
|
|
112
|
+
|---------|------|-------------|
|
|
113
|
+
| `ticker` | Public | Real-time price per trade |
|
|
114
|
+
| `ticker_batch` | Public | Batched price updates |
|
|
115
|
+
| `level2` | Public | Order book updates |
|
|
116
|
+
| `market_trades` | Public | Trade executions |
|
|
117
|
+
| `candles` | Public | OHLCV updates |
|
|
118
|
+
| `status` | Public | Product status changes |
|
|
119
|
+
| `user` | Private | Your order fills and updates |
|
|
120
|
+
| `heartbeats` | Public | Connection keepalive |
|
|
121
|
+
|
|
122
|
+
WebSocket endpoints:
|
|
123
|
+
- Market data: `wss://advanced-trade-ws.coinbase.com`
|
|
124
|
+
- User data: `wss://advanced-trade-ws-user.coinbase.com`
|