🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@2oolkit/pacifica-cli

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@2oolkit/pacifica-cli - npm Package Compare versions

Comparing version
0.1.2
to
0.1.3
+154
-12
dist/index.js

@@ -55,2 +55,38 @@ #!/usr/bin/env node

var BUILDER_MAX_FEE_RATE = "0.001";
var CANDLE_MAX_BARS = 4e3;
var CANDLE_INTERVALS = [
"1m",
"3m",
"5m",
"15m",
"30m",
"1h",
"2h",
"4h",
"8h",
"12h",
"1d"
];
var INTERVAL_MS = {
"1m": 6e4,
"3m": 3 * 6e4,
"5m": 5 * 6e4,
"15m": 15 * 6e4,
"30m": 30 * 6e4,
"1h": 60 * 6e4,
"2h": 2 * 60 * 6e4,
"4h": 4 * 60 * 6e4,
"8h": 8 * 60 * 6e4,
"12h": 12 * 60 * 6e4,
"1d": 24 * 60 * 6e4
};
function intervalToMs(interval) {
const ms = INTERVAL_MS[interval];
if (ms === void 0) {
throw new Error(
`Unknown interval: "${interval}". Valid intervals: ${CANDLE_INTERVALS.join(", ")}`
);
}
return ms;
}
function resolveEnv(input) {

@@ -318,3 +354,8 @@ const normalized = input.toLowerCase().trim();

}
async getCandles(symbol, interval, startTime, endTime) {
/**
* Single /kline request. The API caps the time range at {@link CANDLE_MAX_BARS}
* candles; a wider range returns HTTP 400. `limit` is clamped so an oversized
* caller-supplied value never produces a raw server error.
*/
async getCandles(symbol, interval, startTime, endTime, limit) {
const params = {

@@ -326,5 +367,47 @@ symbol,

if (endTime !== void 0) params.end_time = endTime;
if (limit !== void 0) {
params.limit = Math.min(Math.max(1, Math.floor(limit)), CANDLE_MAX_BARS);
}
const res = await this.http.get("/kline", { params });
return res.data;
}
/**
* Fetch up to `maxBars` candles ending at `endTime` by walking the time range
* backwards in windows of at most {@link CANDLE_MAX_BARS} candles (the API has
* no cursor — pagination is time-range windowing). Results are deduped by open
* time, sorted ascending, and trimmed to `maxBars`.
*
* @param startTime oldest open time to fetch back to (inclusive)
* @param endTime newest time to fetch up to (defaults to now)
* @param maxBars maximum number of candles to return
*/
async getCandlesPaginated(symbol, interval, startTime, endTime, maxBars) {
const intervalMs = intervalToMs(interval);
const windowSpanMs = (CANDLE_MAX_BARS - 1) * intervalMs;
const byOpenTime = /* @__PURE__ */ new Map();
let windowEnd = endTime ?? Date.now();
while (byOpenTime.size < maxBars && windowEnd > startTime) {
const windowStart = Math.max(startTime, windowEnd - windowSpanMs);
const res = await this.getCandles(
symbol,
interval,
windowStart,
windowEnd,
CANDLE_MAX_BARS
);
const batch = res.data || [];
if (batch.length === 0) break;
let oldest = windowEnd;
for (const candle of batch) {
byOpenTime.set(candle.t, candle);
if (candle.t < oldest) oldest = candle.t;
}
const nextEnd = oldest - 1;
if (nextEnd >= windowEnd) break;
windowEnd = nextEnd;
}
const sorted = Array.from(byOpenTime.values()).sort((a, b) => a.t - b.t);
const trimmed = sorted.length > maxBars ? sorted.slice(sorted.length - maxBars) : sorted;
return { data: trimmed };
}
async getHistoricalFunding(symbol, limit, cursor) {

@@ -578,2 +661,9 @@ const params = { symbol };

}
function parseIntStrict(value, name) {
const parsed = parseInt(value, 10);
if (isNaN(parsed)) {
throw new Error(`Invalid integer for ${name}: "${value}"`);
}
return parsed;
}

@@ -688,19 +778,71 @@ // src/commands/market.ts

});
market.command("candles").description("Get candlestick data").argument("<symbol>", "Trading symbol").option(
market.command("candles").description(
`Get candlestick (OHLCV) data. Up to ${CANDLE_MAX_BARS} bars per API request; --count above that auto-paginates across multiple requests`
).argument("<symbol>", "Trading symbol").option(
"-i, --interval <interval>",
"Candle interval (1m,5m,15m,1h,4h,1d)",
`Candle interval (${CANDLE_INTERVALS.join(",")})`,
"1h"
).option(
"-c, --count <n>",
`Number of most-recent candles to fetch (auto-paginates above ${CANDLE_MAX_BARS})`,
"200"
).option(
"--start <timestamp>",
"Start time in ms",
String(Date.now() - 24 * 60 * 60 * 1e3)
).option("--end <timestamp>", "End time in ms").action(async (symbol, options) => {
"Start time in ms (overrides --count; clamped to a single request)"
).option("--end <timestamp>", "End time in ms (defaults to now)").action(async (symbol, options) => {
try {
const client = createPublicClient();
const result = await client.getCandles(
symbol,
options.interval,
parseInt(options.start),
options.end ? parseInt(options.end) : void 0
);
const intervalMs = intervalToMs(options.interval);
const endTime = options.end ? parseIntStrict(options.end, "--end") : void 0;
const maxSpanBars = CANDLE_MAX_BARS - 1;
let result;
if (options.start !== void 0) {
const startTime = parseIntStrict(options.start, "--start");
const refEnd = endTime ?? Date.now();
const spanBars = Math.ceil((refEnd - startTime) / intervalMs);
if (spanBars > maxSpanBars) {
throw new ActionableError(
`Requested time range spans ~${spanBars} ${options.interval} candles, but a single request is capped at ${CANDLE_MAX_BARS}.`,
`Use --count <n> to auto-paginate, or narrow the --start/--end range.`
);
}
result = await client.getCandles(
symbol,
options.interval,
startTime,
endTime,
CANDLE_MAX_BARS
);
} else {
const count = parseIntStrict(options.count, "--count");
if (count <= 0) {
throw new ActionableError(
`--count must be a positive integer (got ${options.count}).`
);
}
const refEnd = endTime ?? Date.now();
if (count <= CANDLE_MAX_BARS) {
const startTime = refEnd - Math.min(count, maxSpanBars) * intervalMs;
const res = await client.getCandles(
symbol,
options.interval,
startTime,
endTime,
CANDLE_MAX_BARS
);
const bars = res.data || [];
result = {
data: bars.length > count ? bars.slice(bars.length - count) : bars
};
} else {
const startTime = refEnd - count * intervalMs;
result = await client.getCandlesPaginated(
symbol,
options.interval,
startTime,
endTime,
count
);
}
}
const formatted = (result.data || []).map((c) => ({

@@ -707,0 +849,0 @@ time: formatTimestamp(c.t),

@@ -46,2 +46,38 @@ #!/usr/bin/env node

var BUILDER_MAX_FEE_RATE = "0.001";
var CANDLE_MAX_BARS = 4e3;
var CANDLE_INTERVALS = [
"1m",
"3m",
"5m",
"15m",
"30m",
"1h",
"2h",
"4h",
"8h",
"12h",
"1d"
];
var INTERVAL_MS = {
"1m": 6e4,
"3m": 3 * 6e4,
"5m": 5 * 6e4,
"15m": 15 * 6e4,
"30m": 30 * 6e4,
"1h": 60 * 6e4,
"2h": 2 * 60 * 6e4,
"4h": 4 * 60 * 6e4,
"8h": 8 * 60 * 6e4,
"12h": 12 * 60 * 6e4,
"1d": 24 * 60 * 6e4
};
function intervalToMs(interval) {
const ms = INTERVAL_MS[interval];
if (ms === void 0) {
throw new Error(
`Unknown interval: "${interval}". Valid intervals: ${CANDLE_INTERVALS.join(", ")}`
);
}
return ms;
}

@@ -144,3 +180,8 @@ // src/signing/signer.ts

}
async getCandles(symbol, interval, startTime, endTime) {
/**
* Single /kline request. The API caps the time range at {@link CANDLE_MAX_BARS}
* candles; a wider range returns HTTP 400. `limit` is clamped so an oversized
* caller-supplied value never produces a raw server error.
*/
async getCandles(symbol, interval, startTime, endTime, limit) {
const params = {

@@ -152,5 +193,47 @@ symbol,

if (endTime !== void 0) params.end_time = endTime;
if (limit !== void 0) {
params.limit = Math.min(Math.max(1, Math.floor(limit)), CANDLE_MAX_BARS);
}
const res = await this.http.get("/kline", { params });
return res.data;
}
/**
* Fetch up to `maxBars` candles ending at `endTime` by walking the time range
* backwards in windows of at most {@link CANDLE_MAX_BARS} candles (the API has
* no cursor — pagination is time-range windowing). Results are deduped by open
* time, sorted ascending, and trimmed to `maxBars`.
*
* @param startTime oldest open time to fetch back to (inclusive)
* @param endTime newest time to fetch up to (defaults to now)
* @param maxBars maximum number of candles to return
*/
async getCandlesPaginated(symbol, interval, startTime, endTime, maxBars) {
const intervalMs = intervalToMs(interval);
const windowSpanMs = (CANDLE_MAX_BARS - 1) * intervalMs;
const byOpenTime = /* @__PURE__ */ new Map();
let windowEnd = endTime ?? Date.now();
while (byOpenTime.size < maxBars && windowEnd > startTime) {
const windowStart = Math.max(startTime, windowEnd - windowSpanMs);
const res = await this.getCandles(
symbol,
interval,
windowStart,
windowEnd,
CANDLE_MAX_BARS
);
const batch = res.data || [];
if (batch.length === 0) break;
let oldest = windowEnd;
for (const candle of batch) {
byOpenTime.set(candle.t, candle);
if (candle.t < oldest) oldest = candle.t;
}
const nextEnd = oldest - 1;
if (nextEnd >= windowEnd) break;
windowEnd = nextEnd;
}
const sorted = Array.from(byOpenTime.values()).sort((a, b) => a.t - b.t);
const trimmed = sorted.length > maxBars ? sorted.slice(sorted.length - maxBars) : sorted;
return { data: trimmed };
}
async getHistoricalFunding(symbol, limit, cursor) {

@@ -366,11 +449,39 @@ const params = { symbol };

"get_candles",
"Get candlestick data for a symbol",
`Get candlestick (OHLCV) data for a symbol. A single API request returns up to ${CANDLE_MAX_BARS} candles; pass "count" above that to auto-paginate across requests.`,
{
symbol: import_zod.z.string(),
interval: import_zod.z.string().describe("1m,5m,15m,1h,4h,1d"),
start_time: import_zod.z.number().describe("Start time in ms"),
end_time: import_zod.z.number().optional().describe("End time in ms")
interval: import_zod.z.enum(CANDLE_INTERVALS).describe(CANDLE_INTERVALS.join(",")),
count: import_zod.z.number().optional().describe(
`Number of most-recent candles to fetch (auto-paginates above ${CANDLE_MAX_BARS}). Ignored when start_time is given.`
),
start_time: import_zod.z.number().optional().describe("Start time in ms (overrides count; single request)"),
end_time: import_zod.z.number().optional().describe("End time in ms (defaults to now)")
},
async ({ symbol, interval, start_time, end_time }) => {
return withErrorHandling(() => createPublicClient().getCandles(symbol, interval, start_time, end_time));
async ({ symbol, interval, count, start_time, end_time }) => {
return withErrorHandling(async () => {
const client = createPublicClient();
if (start_time !== void 0) {
return client.getCandles(symbol, interval, start_time, end_time, CANDLE_MAX_BARS);
}
const n = count ?? 200;
const intervalMs = intervalToMs(interval);
const refEnd = end_time ?? Date.now();
const maxSpanBars = CANDLE_MAX_BARS - 1;
if (n <= CANDLE_MAX_BARS) {
const startTime2 = refEnd - Math.min(n, maxSpanBars) * intervalMs;
const res = await client.getCandles(
symbol,
interval,
startTime2,
end_time,
CANDLE_MAX_BARS
);
const bars = res.data || [];
return {
data: bars.length > n ? bars.slice(bars.length - n) : bars
};
}
const startTime = refEnd - n * intervalMs;
return client.getCandlesPaginated(symbol, interval, startTime, end_time, n);
});
}

@@ -377,0 +488,0 @@ );

+2
-2
{
"name": "@2oolkit/pacifica-cli",
"version": "0.1.2",
"version": "0.1.3",
"description": "CLI toolkit & MCP server for trading perpetual futures on Pacifica exchange — crypto, forex, commodities, equities on Solana",

@@ -68,3 +68,3 @@ "homepage": "https://github.com/haeminmoon/pacifica-cli#readme",

"tweetnacl": "^1.0.3",
"zod": "^3.24.0"
"zod": "^4.4.3"
},

@@ -71,0 +71,0 @@ "devDependencies": {

@@ -85,6 +85,15 @@ # @2oolkit/pacifica-cli

pacifica-cli market trades ETH # Recent trades
pacifica-cli market candles BTC -i 4h # Candlestick data
pacifica-cli market candles BTC -i 4h # Candlestick data (default 200 bars)
pacifica-cli market candles BTC -i 1m -c 4000 # Up to 4000 bars in one request (per-request max)
pacifica-cli market candles BTC -i 1m -c 8000 # >4000 auto-paginates across requests
pacifica-cli market funding SOL -l 50 # Funding rate history
```
**Candles (`market candles`):** The Pacifica `/kline` API returns at most **4000 bars per
request**. Use `-c, --count <n>` to fetch the N most recent bars (default `200`); when `n` exceeds
4000 the CLI auto-paginates by windowing the time range backwards, deduping and sorting ascending
by time, and returns up to `n` bars. Intervals: `1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 8h, 12h, 1d`.
For an explicit window use `--start <ms>` (and optional `--end <ms>`); a range wider than the
4000-bar cap is rejected with a clear message — use `--count` to page through more.
#### Orders (auth required)

@@ -91,0 +100,0 @@

@@ -129,6 +129,11 @@ # Market Data Reference

| `1m` | 1 minute |
| `3m` | 3 minutes |
| `5m` | 5 minutes |
| `15m` | 15 minutes |
| `30m` | 30 minutes |
| `1h` | 1 hour |
| `2h` | 2 hours |
| `4h` | 4 hours |
| `8h` | 8 hours |
| `12h` | 12 hours |
| `1d` | 1 day |

@@ -148,9 +153,26 @@

### Number of Bars & Pagination
The Pacifica `/kline` API returns **at most 4000 bars per request**. The CLI exposes this through
`-c, --count <n>` (default `200`), which fetches the N most-recent bars ending now (or at `--end`):
```bash
# Default: 200 most-recent bars
pacifica-cli market candles BTC -i 1m
# Up to the per-request maximum (single request; may be slightly fewer due to market gaps)
pacifica-cli market candles BTC -i 1m -c 4000 -o json
# More than 4000: the CLI AUTO-PAGINATES, windowing the time range backwards in
# <=4000-bar chunks, deduping and sorting ascending by time, capped at the requested count
pacifica-cli market candles BTC -i 1m -c 8000 -o json | jq 'length' # ~8000
```
When `--count` exceeds 4000 the CLI issues multiple `/kline` requests automatically — no manual
paging needed. Bars are returned oldest-first with no duplicates, and never more than `--count`.
### Custom Time Range
```bash
# Last 24 hours (default)
pacifica-cli market candles BTC -i 1h
# Custom start time (milliseconds)
# Custom start time (milliseconds). Overrides --count; a single request only.
pacifica-cli market candles BTC -i 1h --start 1700000000000

@@ -162,2 +184,5 @@

A `--start`/`--end` range wider than 4000 bars is rejected with a clear message (it does not leak a
raw server error). To pull more than 4000 bars, use `--count <n>` so the CLI auto-paginates.
## Funding Rates

@@ -164,0 +189,0 @@

@@ -140,6 +140,25 @@ ---

| `pacifica-cli market trades ETH` | Recent trades |
| `pacifica-cli market candles BTC -i 4h` | Candlestick data |
| `pacifica-cli market candles BTC -i 1h --start <ms>` | With custom start time |
| `pacifica-cli market candles BTC -i 4h` | Candlestick data (default 200 bars) |
| `pacifica-cli market candles BTC -i 1m -c 4000` | Up to 4000 bars in one request (per-request max) |
| `pacifica-cli market candles BTC -i 1m -c 8000` | More than 4000 bars (auto-paginates) |
| `pacifica-cli market candles BTC -i 1h --start <ms>` | Explicit window (single request, <=4000 bars) |
| `pacifica-cli market funding SOL -l 50` | Funding rate history |
#### Candlestick Data (intervals & pagination)
Supported intervals: `1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 8h, 12h, 1d`.
The `/kline` API returns **at most 4000 bars per request**. Use `-c, --count <n>` (default `200`) to
fetch the N most-recent bars. When `n` exceeds 4000, the CLI auto-paginates — it windows the time
range backwards in `<=4000`-bar chunks, dedupes, sorts ascending by time, and returns up to `n` bars.
```bash
pacifica-cli market candles BTC -i 1m -c 4000 -o json | jq 'length' # ~4000 (one request)
pacifica-cli market candles BTC -i 1m -c 8000 -o json | jq 'length' # ~8000 (auto-paginated)
```
`--start <ms>` (with optional `--end <ms>`) requests an explicit window in a single request; a range
wider than 4000 bars is rejected with a clear message rather than a raw server error — use `--count`
to page through more.
### Trading (Authentication Required)

@@ -146,0 +165,0 @@