@luisjpf/alpaca-sdk 0.2.0 → 0.2.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 +337 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# Alpaca SDK for TypeScript
|
|
2
|
+
|
|
3
|
+
[](https://github.com/luisjpf/alpaca-sdk-ts/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@luisjpf/alpaca-sdk)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
|
|
9
|
+
Modern, type-safe TypeScript SDK for Alpaca's Trading, Broker, and Market Data APIs.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Type-safe**: Auto-generated types from OpenAPI specs
|
|
14
|
+
- **Universal**: Works in Node.js 18+, Deno, Bun, browsers, and edge runtimes
|
|
15
|
+
- **Minimal**: Native `fetch`, zero HTTP dependencies
|
|
16
|
+
- **Tree-shakeable**: Import only what you need
|
|
17
|
+
- **WebSocket Streaming**: Real-time market data and trade updates
|
|
18
|
+
- **Modern**: ESM-first with CommonJS compatibility
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Full SDK (recommended)
|
|
24
|
+
pnpm add @luisjpf/alpaca-sdk
|
|
25
|
+
|
|
26
|
+
# Or individual packages
|
|
27
|
+
pnpm add @luisjpf/trading
|
|
28
|
+
pnpm add @luisjpf/market-data
|
|
29
|
+
pnpm add @luisjpf/broker
|
|
30
|
+
pnpm add @luisjpf/streaming
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { createAlpacaClient } from '@luisjpf/alpaca-sdk'
|
|
37
|
+
|
|
38
|
+
const alpaca = createAlpacaClient({
|
|
39
|
+
keyId: process.env.ALPACA_KEY_ID!,
|
|
40
|
+
secretKey: process.env.ALPACA_SECRET_KEY!,
|
|
41
|
+
paper: true, // Use paper trading
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// Get account info
|
|
45
|
+
const account = await alpaca.trading.account.get()
|
|
46
|
+
|
|
47
|
+
// Place an order
|
|
48
|
+
const order = await alpaca.trading.orders.create({
|
|
49
|
+
symbol: 'AAPL',
|
|
50
|
+
qty: '10',
|
|
51
|
+
side: 'buy',
|
|
52
|
+
type: 'market',
|
|
53
|
+
time_in_force: 'day',
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Get market data
|
|
57
|
+
const bars = await alpaca.marketData.stocks.getSymbolBars('AAPL', {
|
|
58
|
+
start: '2024-01-01',
|
|
59
|
+
end: '2024-01-31',
|
|
60
|
+
timeframe: '1Day',
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Packages
|
|
65
|
+
|
|
66
|
+
| Package | Description |
|
|
67
|
+
| ------------------------- | ----------------------------------------------- |
|
|
68
|
+
| `@luisjpf/alpaca-sdk` | Complete SDK with all APIs |
|
|
69
|
+
| `@luisjpf/trading` | Trading API (orders, positions, account) |
|
|
70
|
+
| `@luisjpf/market-data` | Market Data API (stocks, crypto, options, news) |
|
|
71
|
+
| `@luisjpf/broker` | Broker API (sub-accounts, funding, KYC) |
|
|
72
|
+
| `@luisjpf/streaming` | WebSocket clients for real-time data |
|
|
73
|
+
| `@luisjpf/core` | Shared utilities (auth, errors, types) |
|
|
74
|
+
|
|
75
|
+
## Configuration
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { createTradingClient } from '@luisjpf/trading'
|
|
79
|
+
|
|
80
|
+
const client = createTradingClient({
|
|
81
|
+
keyId: 'YOUR_API_KEY',
|
|
82
|
+
secretKey: 'YOUR_SECRET_KEY',
|
|
83
|
+
paper: true, // default: true
|
|
84
|
+
timeout: 30_000, // default: 30s
|
|
85
|
+
maxRetries: 2, // default: 2
|
|
86
|
+
baseUrl: 'custom-url', // optional override
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Error Handling
|
|
91
|
+
|
|
92
|
+
### Automatic Retries
|
|
93
|
+
|
|
94
|
+
The SDK automatically retries failed requests for:
|
|
95
|
+
|
|
96
|
+
- **429 Rate Limit** - Uses `retry-after` header or exponential backoff
|
|
97
|
+
- **500+ Server Errors** - Uses exponential backoff with jitter
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const client = createTradingClient({
|
|
101
|
+
keyId: 'YOUR_API_KEY',
|
|
102
|
+
secretKey: 'YOUR_SECRET_KEY',
|
|
103
|
+
maxRetries: 2, // default: 2 (set to 0 to disable)
|
|
104
|
+
timeout: 30_000, // default: 30s
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Error Classes
|
|
109
|
+
|
|
110
|
+
All errors extend `AlpacaError` and can be caught with `instanceof`:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import {
|
|
114
|
+
AlpacaError,
|
|
115
|
+
AuthenticationError,
|
|
116
|
+
RateLimitError,
|
|
117
|
+
InsufficientFundsError,
|
|
118
|
+
ForbiddenError,
|
|
119
|
+
NotFoundError,
|
|
120
|
+
ValidationError,
|
|
121
|
+
MarketClosedError,
|
|
122
|
+
ServerError,
|
|
123
|
+
} from '@luisjpf/core'
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
await client.orders.create({ ... })
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (error instanceof RateLimitError) {
|
|
129
|
+
// Auto-retried, but still failed after maxRetries
|
|
130
|
+
console.log(`Rate limited. Retry after ${error.retryAfter}s`)
|
|
131
|
+
} else if (error instanceof InsufficientFundsError) {
|
|
132
|
+
console.log('Not enough buying power')
|
|
133
|
+
} else if (error instanceof AuthenticationError) {
|
|
134
|
+
console.log('Invalid API credentials')
|
|
135
|
+
} else if (error instanceof ValidationError) {
|
|
136
|
+
console.log('Invalid order parameters')
|
|
137
|
+
} else if (error instanceof MarketClosedError) {
|
|
138
|
+
console.log('Market is closed')
|
|
139
|
+
} else if (error instanceof NotFoundError) {
|
|
140
|
+
console.log('Resource not found')
|
|
141
|
+
} else if (error instanceof AlpacaError) {
|
|
142
|
+
// Base class for all Alpaca errors
|
|
143
|
+
console.log(`Error ${error.code}: ${error.message}`)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Error Classes Reference
|
|
149
|
+
|
|
150
|
+
| Error Class | Status | Description |
|
|
151
|
+
| ------------------------ | ------ | ---------------------------------- |
|
|
152
|
+
| `AuthenticationError` | 401 | Invalid API credentials |
|
|
153
|
+
| `ForbiddenError` | 403 | Insufficient permissions |
|
|
154
|
+
| `InsufficientFundsError` | 403 | Not enough buying power |
|
|
155
|
+
| `MarketClosedError` | 403 | Market is currently closed |
|
|
156
|
+
| `NotFoundError` | 404 | Resource not found |
|
|
157
|
+
| `ValidationError` | 422 | Invalid request parameters |
|
|
158
|
+
| `RateLimitError` | 429 | Rate limit exceeded (auto-retried) |
|
|
159
|
+
| `ServerError` | 500+ | Server error (auto-retried) |
|
|
160
|
+
|
|
161
|
+
### Error Properties
|
|
162
|
+
|
|
163
|
+
All errors include:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
interface AlpacaError {
|
|
167
|
+
message: string // Human-readable error message
|
|
168
|
+
code: number // Alpaca error code
|
|
169
|
+
status: number // HTTP status code
|
|
170
|
+
requestId?: string // Request ID for support
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// RateLimitError also includes:
|
|
174
|
+
interface RateLimitError extends AlpacaError {
|
|
175
|
+
retryAfter?: number // Seconds until retry (from retry-after header)
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Type Guards
|
|
180
|
+
|
|
181
|
+
For type-safe error handling without `instanceof`:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import {
|
|
185
|
+
isAuthenticationError,
|
|
186
|
+
isRateLimitError,
|
|
187
|
+
isInsufficientFundsError,
|
|
188
|
+
isValidationError,
|
|
189
|
+
isNotFoundError,
|
|
190
|
+
isMarketClosedError,
|
|
191
|
+
isServerError,
|
|
192
|
+
} from '@luisjpf/core'
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
await client.orders.create({ ... })
|
|
196
|
+
} catch (error) {
|
|
197
|
+
if (error instanceof AlpacaError) {
|
|
198
|
+
const apiError = error.toApiError()
|
|
199
|
+
|
|
200
|
+
if (isRateLimitError(apiError)) {
|
|
201
|
+
console.log(`Retry after ${apiError.retryAfter}s`)
|
|
202
|
+
} else if (isInsufficientFundsError(apiError)) {
|
|
203
|
+
console.log('Not enough buying power')
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## WebSocket Streaming
|
|
210
|
+
|
|
211
|
+
Real-time market data and trade updates via WebSocket.
|
|
212
|
+
|
|
213
|
+
### Stock Data Streaming
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { createStockStream } from '@luisjpf/streaming'
|
|
217
|
+
|
|
218
|
+
const stream = createStockStream({
|
|
219
|
+
keyId: 'YOUR_API_KEY',
|
|
220
|
+
secretKey: 'YOUR_SECRET_KEY',
|
|
221
|
+
feed: 'iex', // 'iex' (free), 'sip' (paid), or 'delayed_sip'
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// Register handlers
|
|
225
|
+
stream.onTrade((trade) => {
|
|
226
|
+
console.log(`Trade: ${trade.S} @ $${trade.p}`)
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
stream.onQuote((quote) => {
|
|
230
|
+
console.log(`Quote: ${quote.S} bid: $${quote.bp} ask: $${quote.ap}`)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
stream.onBar((bar) => {
|
|
234
|
+
console.log(`Bar: ${bar.S} O:${bar.o} H:${bar.h} L:${bar.l} C:${bar.c}`)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
stream.onConnect(() => console.log('Connected!'))
|
|
238
|
+
stream.onError((err) => console.error('Error:', err))
|
|
239
|
+
|
|
240
|
+
// Connect and subscribe
|
|
241
|
+
stream.connect()
|
|
242
|
+
stream.subscribeForTrades(['AAPL', 'MSFT', 'GOOGL'])
|
|
243
|
+
stream.subscribeForQuotes(['AAPL'])
|
|
244
|
+
stream.subscribeForBars(['AAPL'])
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Crypto Data Streaming
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { createCryptoStream } from '@luisjpf/streaming'
|
|
251
|
+
|
|
252
|
+
const stream = createCryptoStream({
|
|
253
|
+
keyId: 'YOUR_API_KEY',
|
|
254
|
+
secretKey: 'YOUR_SECRET_KEY',
|
|
255
|
+
location: 'us', // 'us' (Alpaca), 'us-1' (Kraken US), 'eu-1' (Kraken EU)
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
stream.onTrade((trade) => {
|
|
259
|
+
console.log(`Crypto Trade: ${trade.S} @ $${trade.p}`)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
stream.connect()
|
|
263
|
+
stream.subscribeForTrades(['BTC/USD', 'ETH/USD'])
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Trade Updates Streaming
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { createTradeUpdatesStream } from '@luisjpf/streaming'
|
|
270
|
+
|
|
271
|
+
const stream = createTradeUpdatesStream({
|
|
272
|
+
keyId: 'YOUR_API_KEY',
|
|
273
|
+
secretKey: 'YOUR_SECRET_KEY',
|
|
274
|
+
paper: true, // true for paper trading, false for live
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
stream.onTradeUpdate((update) => {
|
|
278
|
+
console.log(`Order ${update.event}: ${JSON.stringify(update.order)}`)
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
stream.connect()
|
|
282
|
+
stream.subscribe()
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Stock Feed Types
|
|
286
|
+
|
|
287
|
+
| Feed | Description | Subscription |
|
|
288
|
+
| ------------- | ----------------------------- | ----------------- |
|
|
289
|
+
| `iex` | IEX Exchange data only | Free (Basic plan) |
|
|
290
|
+
| `sip` | Full consolidated market data | Algo Trader Plus |
|
|
291
|
+
| `delayed_sip` | 15-minute delayed SIP data | Free |
|
|
292
|
+
|
|
293
|
+
### Crypto Locations
|
|
294
|
+
|
|
295
|
+
| Location | Exchange | Notes |
|
|
296
|
+
| -------- | --------- | ------------------------- |
|
|
297
|
+
| `us` | Alpaca | Default, recommended |
|
|
298
|
+
| `us-1` | Kraken US | Available in 23 US states |
|
|
299
|
+
| `eu-1` | Kraken EU | European markets |
|
|
300
|
+
|
|
301
|
+
## Development
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# Install dependencies
|
|
305
|
+
pnpm install
|
|
306
|
+
|
|
307
|
+
# Generate types from OpenAPI specs
|
|
308
|
+
pnpm generate:types
|
|
309
|
+
|
|
310
|
+
# Build all packages
|
|
311
|
+
pnpm build
|
|
312
|
+
|
|
313
|
+
# Run tests
|
|
314
|
+
pnpm test
|
|
315
|
+
|
|
316
|
+
# Run tests with coverage
|
|
317
|
+
pnpm test:coverage
|
|
318
|
+
|
|
319
|
+
# Lint code
|
|
320
|
+
pnpm lint
|
|
321
|
+
|
|
322
|
+
# Type check
|
|
323
|
+
pnpm typecheck
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Requirements
|
|
327
|
+
|
|
328
|
+
- Node.js >= 18.0.0 (for native `fetch`)
|
|
329
|
+
- pnpm >= 9.0.0
|
|
330
|
+
|
|
331
|
+
## Contributing
|
|
332
|
+
|
|
333
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
334
|
+
|
|
335
|
+
## License
|
|
336
|
+
|
|
337
|
+
MIT
|