@2oolkit/kiwoom-cli 0.1.0 → 0.1.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 CHANGED
@@ -106,7 +106,25 @@ kiwoom-cli chart week <코드> 주봉 (ka10082)
106
106
  kiwoom-cli chart month <코드> 월봉 (ka10083)
107
107
  kiwoom-cli chart year <코드> 년봉 (ka10094)
108
108
  ```
109
- 모두 `-n, --count <n>`( 수, 기본 50), `--raw`(수정주가 미적용) 지원.
109
+ 모두 `-n, --count <n>`( 개수, 기본 50), `--raw`(수정주가 미적용), `-p, --paginate`(강제 다중 페이지) 지원.
110
+
111
+ **1회 요청당 최대 봉 개수 (per-request cap)** — 그 이상은 `cont-yn`/`next-key` 헤더로 **자동 페이지네이션**:
112
+
113
+ | 차트 | 1회 최대 | `--count` 한도 |
114
+ |---|---|---|
115
+ | tick / min | 900 | 100,000 (자동 분할) |
116
+ | day | 600 | 100,000 (자동 분할) |
117
+ | week | 300 | 100,000 (자동 분할) |
118
+ | month | 240 | 100,000 (자동 분할) |
119
+ | year | 30 | 100,000 (자동 분할) |
120
+
121
+ `--count`가 1회 최대를 넘으면 자동으로 여러 페이지를 받아 합칩니다(시간순 정렬·중복 제거는 API 순서를 그대로 유지). `-p/--paginate`로 강제할 수도 있습니다. `--count`는 양의 정수여야 하며 100,000으로 클램프됩니다.
122
+
123
+ ```bash
124
+ kiwoom-cli chart day 005930 -n 600 # 한 페이지(최대) — 일봉 600개
125
+ kiwoom-cli chart day 005930 -n 2000 -o json # 자동 페이지네이션 — 일봉 ~2000개
126
+ kiwoom-cli chart min 005930 -i 1 -n 2000 -o json # 1분봉 ~2000개 (여러 페이지)
127
+ ```
110
128
 
111
129
  ### `account` — 계좌
112
130
  ```
package/dist/index.js CHANGED
@@ -89,6 +89,15 @@ var ORDER_TYPES = {
89
89
  "81": "\uC7A5\uB9C8\uAC10\uD6C4\uC2DC\uAC04\uC678"
90
90
  };
91
91
  var MARKET_ORDER_TYPES = /* @__PURE__ */ new Set(["3", "13", "23"]);
92
+ var CHART_PER_PAGE_CAP = {
93
+ tick: 900,
94
+ minute: 900,
95
+ day: 600,
96
+ week: 300,
97
+ month: 240,
98
+ year: 30
99
+ };
100
+ var CHART_MAX_COUNT = 1e5;
92
101
 
93
102
  // src/config/store.ts
94
103
  var DEFAULT_CONFIG = { env: "real" };
@@ -438,7 +447,7 @@ var KiwoomClient = class {
438
447
  */
439
448
  async callEndpoint(def, body = {}, opts = {}) {
440
449
  if (opts.paginate && def.listKey) {
441
- const data = await this.requestAll(def.apiId, def.path, body, def.listKey);
450
+ const data = await this.requestAll(def.apiId, def.path, body, def.listKey, opts.maxPages);
442
451
  return { data, contYn: false, nextKey: "" };
443
452
  }
444
453
  return this.request(def.apiId, def.path, body, {
@@ -448,9 +457,11 @@ var KiwoomClient = class {
448
457
  }
449
458
  /**
450
459
  * Fetch all pages of a TR, concatenating the array under `listKey`.
451
- * Caps at `maxPages` to avoid runaway loops.
460
+ * Caps at `maxPages` to avoid runaway loops (default 100 — high enough for
461
+ * large chart pulls; callers pass a tighter bound when they know how many
462
+ * pages a target row count needs).
452
463
  */
453
- async requestAll(apiId, path2, body, listKey, maxPages = 20) {
464
+ async requestAll(apiId, path2, body, listKey, maxPages = 100) {
454
465
  let page = await this.request(apiId, path2, body);
455
466
  const acc = Array.isArray(page.data[listKey]) ? [...page.data[listKey]] : [];
456
467
  let pages = 1;
@@ -1254,81 +1265,102 @@ var CHART_ROW_FORMATTERS = {
1254
1265
  pred_pre: unpad,
1255
1266
  trde_tern_rt: unpad
1256
1267
  };
1268
+ var PER_PAGE_CAP = CHART_PER_PAGE_CAP;
1269
+ function parseCount(raw) {
1270
+ const n = parseIntStrict(raw, "count");
1271
+ if (n < 1) {
1272
+ throw new ActionableError(`--count must be a positive integer (got ${n}).`);
1273
+ }
1274
+ return Math.min(n, CHART_MAX_COUNT);
1275
+ }
1257
1276
  function emitChart(data, ep, fmt, count) {
1277
+ const all = Array.isArray(data[ep.listKey]) ? data[ep.listKey] : [];
1278
+ const sliced = all.slice(0, count);
1258
1279
  if (fmt === "json") {
1259
- output(data, "json");
1280
+ output({ ...data, [ep.listKey]: sliced }, "json");
1260
1281
  return;
1261
1282
  }
1262
- const all = Array.isArray(data[ep.listKey]) ? data[ep.listKey] : [];
1263
- if (all.length === 0) {
1283
+ if (sliced.length === 0) {
1264
1284
  console.log("No data");
1265
1285
  return;
1266
1286
  }
1267
1287
  output(
1268
- all.slice(0, count).map((row) => formatFields(row, CHART_ROW_FORMATTERS)),
1288
+ sliced.map((row) => formatFields(row, CHART_ROW_FORMATTERS)),
1269
1289
  "table"
1270
1290
  );
1271
1291
  }
1272
1292
  function registerChartCommands(program2) {
1273
- const chart = program2.command("chart").description("OHLC charts \u2014 tick / minute / daily / weekly / monthly / yearly");
1274
- async function runIntraday(ep, code, ticScope, options) {
1293
+ const chart = program2.command("chart").description(
1294
+ "OHLC charts \u2014 tick / minute / daily / weekly / monthly / yearly. Per-request caps: tick/min 900, day 600, week 300, month 240, year 30. --count beyond the cap auto-paginates via cont-yn/next-key."
1295
+ );
1296
+ function planFetch(type, count, paginate) {
1297
+ const cap = PER_PAGE_CAP[type];
1298
+ const active = paginate || count > cap;
1299
+ const maxPages = active ? Math.max(1, Math.ceil(count / cap)) : 1;
1300
+ return { paginate: active, maxPages };
1301
+ }
1302
+ async function runIntraday(ep, type, code, ticScope, options) {
1275
1303
  const client = createClient();
1276
1304
  const stk = normalizeStockCode(code);
1277
- const { data } = await client.callEndpoint(ep, {
1278
- stk_cd: stk,
1279
- tic_scope: ticScope,
1280
- upd_stkpc_tp: options.raw ? "0" : "1"
1281
- });
1282
- emitChart(data, ep, getOutputFormat(options), parseIntStrict(options.count, "count"));
1305
+ const count = parseCount(options.count);
1306
+ const plan = planFetch(type, count, !!options.paginate);
1307
+ const { data } = await client.callEndpoint(
1308
+ ep,
1309
+ { stk_cd: stk, tic_scope: ticScope, upd_stkpc_tp: options.raw ? "0" : "1" },
1310
+ { paginate: plan.paginate, maxPages: plan.maxPages }
1311
+ );
1312
+ emitChart(data, ep, getOutputFormat(options), count);
1283
1313
  }
1284
- async function runPeriod(ep, code, baseDt, options) {
1314
+ async function runPeriod(ep, type, code, baseDt, options) {
1285
1315
  const client = createClient();
1286
1316
  const stk = normalizeStockCode(code);
1287
- const { data } = await client.callEndpoint(ep, {
1288
- stk_cd: stk,
1289
- base_dt: baseDt,
1290
- upd_stkpc_tp: options.raw ? "0" : "1"
1291
- });
1292
- emitChart(data, ep, getOutputFormat(options), parseIntStrict(options.count, "count"));
1317
+ const count = parseCount(options.count);
1318
+ const plan = planFetch(type, count, !!options.paginate);
1319
+ const { data } = await client.callEndpoint(
1320
+ ep,
1321
+ { stk_cd: stk, base_dt: baseDt, upd_stkpc_tp: options.raw ? "0" : "1" },
1322
+ { paginate: plan.paginate, maxPages: plan.maxPages }
1323
+ );
1324
+ emitChart(data, ep, getOutputFormat(options), count);
1293
1325
  }
1294
- chart.command("tick <code>").description("Tick chart (ka10079)").option("-s, --scope <n>", "Ticks per candle (1/3/5/10/30)", "1").option("-n, --count <n>", "Number of candles to display", "50").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1326
+ chart.command("tick <code>").description(`Tick chart (ka10079) \u2014 up to ${PER_PAGE_CAP.tick} candles/request, auto-paginates beyond that`).option("-s, --scope <n>", "Ticks per candle (1/3/5/10/30)", "1").option("-n, --count <n>", "Number of candles (auto-paginates when > 900)", "50").option("-p, --paginate", "Force fetching multiple pages (cont-yn/next-key)").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1295
1327
  try {
1296
- await runIntraday(ENDPOINTS.tickChart, code, options.scope, options);
1328
+ await runIntraday(ENDPOINTS.tickChart, "tick", code, options.scope, options);
1297
1329
  } catch (err) {
1298
1330
  handleError(err);
1299
1331
  }
1300
1332
  });
1301
- chart.command("min <code>").alias("minute").description("Minute chart (ka10080)").option("-i, --interval <n>", "Minutes per candle (1/3/5/10/15/30/45/60)", "1").option("-n, --count <n>", "Number of candles to display", "50").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1333
+ chart.command("min <code>").alias("minute").description(`Minute chart (ka10080) \u2014 up to ${PER_PAGE_CAP.minute} candles/request, auto-paginates beyond that`).option("-i, --interval <n>", "Minutes per candle (1/3/5/10/15/30/45/60)", "1").option("-n, --count <n>", "Number of candles (auto-paginates when > 900)", "50").option("-p, --paginate", "Force fetching multiple pages (cont-yn/next-key)").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1302
1334
  try {
1303
- await runIntraday(ENDPOINTS.minuteChart, code, options.interval, options);
1335
+ await runIntraday(ENDPOINTS.minuteChart, "minute", code, options.interval, options);
1304
1336
  } catch (err) {
1305
1337
  handleError(err);
1306
1338
  }
1307
1339
  });
1308
- chart.command("day <code>").alias("daily").description("Daily chart (ka10081)").option("-d, --date <yyyymmdd>", "Base date (most recent candle)", todayKst()).option("-n, --count <n>", "Number of candles to display", "50").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1340
+ chart.command("day <code>").alias("daily").description(`Daily chart (ka10081) \u2014 up to ${PER_PAGE_CAP.day} candles/request, auto-paginates beyond that`).option("-d, --date <yyyymmdd>", "Base date (most recent candle)", todayKst()).option("-n, --count <n>", "Number of candles (auto-paginates when > 600)", "50").option("-p, --paginate", "Force fetching multiple pages (cont-yn/next-key)").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1309
1341
  try {
1310
- await runPeriod(ENDPOINTS.dailyChart, code, options.date, options);
1342
+ await runPeriod(ENDPOINTS.dailyChart, "day", code, options.date, options);
1311
1343
  } catch (err) {
1312
1344
  handleError(err);
1313
1345
  }
1314
1346
  });
1315
- chart.command("week <code>").description("Weekly chart (ka10082)").option("-d, --date <yyyymmdd>", "Base date (most recent candle)", todayKst()).option("-n, --count <n>", "Number of candles to display", "50").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1347
+ chart.command("week <code>").description(`Weekly chart (ka10082) \u2014 up to ${PER_PAGE_CAP.week} candles/request, auto-paginates beyond that`).option("-d, --date <yyyymmdd>", "Base date (most recent candle)", todayKst()).option("-n, --count <n>", "Number of candles (auto-paginates when > 300)", "50").option("-p, --paginate", "Force fetching multiple pages (cont-yn/next-key)").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1316
1348
  try {
1317
- await runPeriod(ENDPOINTS.weeklyChart, code, options.date, options);
1349
+ await runPeriod(ENDPOINTS.weeklyChart, "week", code, options.date, options);
1318
1350
  } catch (err) {
1319
1351
  handleError(err);
1320
1352
  }
1321
1353
  });
1322
- chart.command("month <code>").description("Monthly chart (ka10083)").option("-d, --date <yyyymmdd>", "Base date (most recent candle)", todayKst()).option("-n, --count <n>", "Number of candles to display", "50").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1354
+ chart.command("month <code>").description(`Monthly chart (ka10083) \u2014 up to ${PER_PAGE_CAP.month} candles/request, auto-paginates beyond that`).option("-d, --date <yyyymmdd>", "Base date (most recent candle)", todayKst()).option("-n, --count <n>", "Number of candles (auto-paginates when > 240)", "50").option("-p, --paginate", "Force fetching multiple pages (cont-yn/next-key)").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1323
1355
  try {
1324
- await runPeriod(ENDPOINTS.monthlyChart, code, options.date, options);
1356
+ await runPeriod(ENDPOINTS.monthlyChart, "month", code, options.date, options);
1325
1357
  } catch (err) {
1326
1358
  handleError(err);
1327
1359
  }
1328
1360
  });
1329
- chart.command("year <code>").description("Yearly chart (ka10094)").option("-d, --date <yyyymmdd>", "Base date (most recent candle)", todayKst()).option("-n, --count <n>", "Number of candles to display", "50").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1361
+ chart.command("year <code>").description(`Yearly chart (ka10094) \u2014 up to ${PER_PAGE_CAP.year} candles/request, auto-paginates beyond that`).option("-d, --date <yyyymmdd>", "Base date (most recent candle)", todayKst()).option("-n, --count <n>", "Number of candles (auto-paginates when > 30)", "50").option("-p, --paginate", "Force fetching multiple pages (cont-yn/next-key)").option("--raw", "Unadjusted (\uC218\uC815\uC8FC\uAC00 \uBBF8\uBC18\uC601) prices").option("-o, --output <format>", "Output format (table/json)", "table").action(async (code, options) => {
1330
1362
  try {
1331
- await runPeriod(ENDPOINTS.yearlyChart, code, options.date, options);
1363
+ await runPeriod(ENDPOINTS.yearlyChart, "year", code, options.date, options);
1332
1364
  } catch (err) {
1333
1365
  handleError(err);
1334
1366
  }