@2oolkit/kiwoom-cli 0.1.0 → 0.1.2
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 +24 -2
- package/dist/index.js +121 -34
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +90 -7
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +16 -9
- package/skill/references/market-data.md +23 -13
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>`(
|
|
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
|
```
|
|
@@ -129,6 +147,7 @@ kiwoom-cli ranking volume 당일 거래량 상위 (ka10030)
|
|
|
129
147
|
kiwoom-cli ranking amount 거래대금 상위 (ka10032)
|
|
130
148
|
kiwoom-cli ranking surge 거래량 급증 (ka10023)
|
|
131
149
|
kiwoom-cli ranking prev-volume 전일 거래량 상위 (ka10031)
|
|
150
|
+
kiwoom-cli ranking net-buy [옵션] 수급: 외국인·기관 순매수 상위 (ka90009)
|
|
132
151
|
|
|
133
152
|
kiwoom-cli sector price [-m -c] 업종 현재가 (ka20001)
|
|
134
153
|
kiwoom-cli sector stocks 업종 구성종목 (ka20002)
|
|
@@ -138,6 +157,9 @@ kiwoom-cli sector codes 업종 코드 목록 (ka10101)
|
|
|
138
157
|
```
|
|
139
158
|
순위: `-m 000=전체/001=코스피/101=코스닥`, `-x 1=KRX/2=NXT/3=통합`.
|
|
140
159
|
|
|
160
|
+
수급(`net-buy`, alias `supply`)은 한 번의 ka90009 호출로 외국인·기관 매매상위를 함께 반환합니다:
|
|
161
|
+
`-b foreign|institution|both`(기본 both), `--side buy|sell`(기본 buy=순매수), `-n <1-50>`(기본 10), `-q 1=금액/2=수량`(기본 1), `-d YYYYMMDD`(기본 최신). 예: `kiwoom-cli ranking net-buy -b both -n 10`.
|
|
162
|
+
|
|
141
163
|
### `order` — ⚠ `real`에서는 실제 자금
|
|
142
164
|
```
|
|
143
165
|
kiwoom-cli order buy <코드> <수량> [-p 가격] [-t 유형] [-x KRX|NXT|SOR] [--credit] [-y]
|
|
@@ -176,7 +198,7 @@ kiwoom-cli order cancel 0000139 005930 # 잔량 전부 취소
|
|
|
176
198
|
}
|
|
177
199
|
```
|
|
178
200
|
|
|
179
|
-
도구: `get_stock_info`, `get_price`, `get_orderbook`, `get_daily_price`, `get_recent_trades`, `search_stocks`, `get_chart`, `get_balance`, `get_deposit`, `get_open_orders`, `get_executions`, `get_realized_pnl`, `get_ranking`, `get_sector`, `place_order`, `modify_order`, `cancel_order`.
|
|
201
|
+
도구: `get_stock_info`, `get_price`, `get_orderbook`, `get_daily_price`, `get_recent_trades`, `search_stocks`, `get_chart`, `get_balance`, `get_deposit`, `get_open_orders`, `get_executions`, `get_realized_pnl`, `get_ranking`, `get_net_buy_ranking`, `get_sector`, `place_order`, `modify_order`, `cancel_order`.
|
|
180
202
|
|
|
181
203
|
주문 도구는 **`confirm: true`를 넘기지 않으면 미리보기만 반환하고 아무것도 실행하지 않습니다** — 에이전트가 실수로 실주문을 낼 수 없습니다.
|
|
182
204
|
|
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 =
|
|
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;
|
|
@@ -840,6 +851,7 @@ var ENDPOINTS = {
|
|
|
840
851
|
rankTradeAmount: { apiId: "ka10032", path: PATHS.rkinfo, korean: "\uAC70\uB798\uB300\uAE08\uC0C1\uC704\uC694\uCCAD", listKey: "trde_prica_upper" },
|
|
841
852
|
rankVolumeSurge: { apiId: "ka10023", path: PATHS.rkinfo, korean: "\uAC70\uB798\uB7C9\uAE09\uC99D\uC694\uCCAD", listKey: "trde_qty_sdnin" },
|
|
842
853
|
rankPrevVolume: { apiId: "ka10031", path: PATHS.rkinfo, korean: "\uC804\uC77C\uAC70\uB798\uB7C9\uC0C1\uC704\uC694\uCCAD", listKey: "pred_trde_qty_upper" },
|
|
854
|
+
rankForeignInst: { apiId: "ka90009", path: PATHS.rkinfo, korean: "\uC678\uAD6D\uC778\uAE30\uAD00\uB9E4\uB9E4\uC0C1\uC704\uC694\uCCAD", listKey: "frgnr_orgn_trde_upper" },
|
|
843
855
|
// ── Sector / industry (업종) ───────────────────────────────────────────────
|
|
844
856
|
sectorPrice: { apiId: "ka20001", path: PATHS.sect, korean: "\uC5C5\uC885\uD604\uC7AC\uAC00\uC694\uCCAD", listKey: "inds_cur_prc_tm" },
|
|
845
857
|
sectorStocks: { apiId: "ka20002", path: PATHS.sect, korean: "\uC5C5\uC885\uBCC4\uC8FC\uAC00\uC694\uCCAD", listKey: "inds_stkpc" },
|
|
@@ -1254,81 +1266,102 @@ var CHART_ROW_FORMATTERS = {
|
|
|
1254
1266
|
pred_pre: unpad,
|
|
1255
1267
|
trde_tern_rt: unpad
|
|
1256
1268
|
};
|
|
1269
|
+
var PER_PAGE_CAP = CHART_PER_PAGE_CAP;
|
|
1270
|
+
function parseCount(raw) {
|
|
1271
|
+
const n = parseIntStrict(raw, "count");
|
|
1272
|
+
if (n < 1) {
|
|
1273
|
+
throw new ActionableError(`--count must be a positive integer (got ${n}).`);
|
|
1274
|
+
}
|
|
1275
|
+
return Math.min(n, CHART_MAX_COUNT);
|
|
1276
|
+
}
|
|
1257
1277
|
function emitChart(data, ep, fmt, count) {
|
|
1278
|
+
const all = Array.isArray(data[ep.listKey]) ? data[ep.listKey] : [];
|
|
1279
|
+
const sliced = all.slice(0, count);
|
|
1258
1280
|
if (fmt === "json") {
|
|
1259
|
-
output(data, "json");
|
|
1281
|
+
output({ ...data, [ep.listKey]: sliced }, "json");
|
|
1260
1282
|
return;
|
|
1261
1283
|
}
|
|
1262
|
-
|
|
1263
|
-
if (all.length === 0) {
|
|
1284
|
+
if (sliced.length === 0) {
|
|
1264
1285
|
console.log("No data");
|
|
1265
1286
|
return;
|
|
1266
1287
|
}
|
|
1267
1288
|
output(
|
|
1268
|
-
|
|
1289
|
+
sliced.map((row) => formatFields(row, CHART_ROW_FORMATTERS)),
|
|
1269
1290
|
"table"
|
|
1270
1291
|
);
|
|
1271
1292
|
}
|
|
1272
1293
|
function registerChartCommands(program2) {
|
|
1273
|
-
const chart = program2.command("chart").description(
|
|
1274
|
-
|
|
1294
|
+
const chart = program2.command("chart").description(
|
|
1295
|
+
"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."
|
|
1296
|
+
);
|
|
1297
|
+
function planFetch(type, count, paginate) {
|
|
1298
|
+
const cap = PER_PAGE_CAP[type];
|
|
1299
|
+
const active = paginate || count > cap;
|
|
1300
|
+
const maxPages = active ? Math.max(1, Math.ceil(count / cap)) : 1;
|
|
1301
|
+
return { paginate: active, maxPages };
|
|
1302
|
+
}
|
|
1303
|
+
async function runIntraday(ep, type, code, ticScope, options) {
|
|
1275
1304
|
const client = createClient();
|
|
1276
1305
|
const stk = normalizeStockCode(code);
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1306
|
+
const count = parseCount(options.count);
|
|
1307
|
+
const plan = planFetch(type, count, !!options.paginate);
|
|
1308
|
+
const { data } = await client.callEndpoint(
|
|
1309
|
+
ep,
|
|
1310
|
+
{ stk_cd: stk, tic_scope: ticScope, upd_stkpc_tp: options.raw ? "0" : "1" },
|
|
1311
|
+
{ paginate: plan.paginate, maxPages: plan.maxPages }
|
|
1312
|
+
);
|
|
1313
|
+
emitChart(data, ep, getOutputFormat(options), count);
|
|
1283
1314
|
}
|
|
1284
|
-
async function runPeriod(ep, code, baseDt, options) {
|
|
1315
|
+
async function runPeriod(ep, type, code, baseDt, options) {
|
|
1285
1316
|
const client = createClient();
|
|
1286
1317
|
const stk = normalizeStockCode(code);
|
|
1287
|
-
const
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1318
|
+
const count = parseCount(options.count);
|
|
1319
|
+
const plan = planFetch(type, count, !!options.paginate);
|
|
1320
|
+
const { data } = await client.callEndpoint(
|
|
1321
|
+
ep,
|
|
1322
|
+
{ stk_cd: stk, base_dt: baseDt, upd_stkpc_tp: options.raw ? "0" : "1" },
|
|
1323
|
+
{ paginate: plan.paginate, maxPages: plan.maxPages }
|
|
1324
|
+
);
|
|
1325
|
+
emitChart(data, ep, getOutputFormat(options), count);
|
|
1293
1326
|
}
|
|
1294
|
-
chart.command("tick <code>").description(
|
|
1327
|
+
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
1328
|
try {
|
|
1296
|
-
await runIntraday(ENDPOINTS.tickChart, code, options.scope, options);
|
|
1329
|
+
await runIntraday(ENDPOINTS.tickChart, "tick", code, options.scope, options);
|
|
1297
1330
|
} catch (err) {
|
|
1298
1331
|
handleError(err);
|
|
1299
1332
|
}
|
|
1300
1333
|
});
|
|
1301
|
-
chart.command("min <code>").alias("minute").description(
|
|
1334
|
+
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
1335
|
try {
|
|
1303
|
-
await runIntraday(ENDPOINTS.minuteChart, code, options.interval, options);
|
|
1336
|
+
await runIntraday(ENDPOINTS.minuteChart, "minute", code, options.interval, options);
|
|
1304
1337
|
} catch (err) {
|
|
1305
1338
|
handleError(err);
|
|
1306
1339
|
}
|
|
1307
1340
|
});
|
|
1308
|
-
chart.command("day <code>").alias("daily").description(
|
|
1341
|
+
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
1342
|
try {
|
|
1310
|
-
await runPeriod(ENDPOINTS.dailyChart, code, options.date, options);
|
|
1343
|
+
await runPeriod(ENDPOINTS.dailyChart, "day", code, options.date, options);
|
|
1311
1344
|
} catch (err) {
|
|
1312
1345
|
handleError(err);
|
|
1313
1346
|
}
|
|
1314
1347
|
});
|
|
1315
|
-
chart.command("week <code>").description(
|
|
1348
|
+
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
1349
|
try {
|
|
1317
|
-
await runPeriod(ENDPOINTS.weeklyChart, code, options.date, options);
|
|
1350
|
+
await runPeriod(ENDPOINTS.weeklyChart, "week", code, options.date, options);
|
|
1318
1351
|
} catch (err) {
|
|
1319
1352
|
handleError(err);
|
|
1320
1353
|
}
|
|
1321
1354
|
});
|
|
1322
|
-
chart.command("month <code>").description(
|
|
1355
|
+
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
1356
|
try {
|
|
1324
|
-
await runPeriod(ENDPOINTS.monthlyChart, code, options.date, options);
|
|
1357
|
+
await runPeriod(ENDPOINTS.monthlyChart, "month", code, options.date, options);
|
|
1325
1358
|
} catch (err) {
|
|
1326
1359
|
handleError(err);
|
|
1327
1360
|
}
|
|
1328
1361
|
});
|
|
1329
|
-
chart.command("year <code>").description(
|
|
1362
|
+
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
1363
|
try {
|
|
1331
|
-
await runPeriod(ENDPOINTS.yearlyChart, code, options.date, options);
|
|
1364
|
+
await runPeriod(ENDPOINTS.yearlyChart, "year", code, options.date, options);
|
|
1332
1365
|
} catch (err) {
|
|
1333
1366
|
handleError(err);
|
|
1334
1367
|
}
|
|
@@ -1756,6 +1789,21 @@ ${side.toUpperCase()} ${credit ? "(credit) " : ""}${stk} qty ${qty} ${priceLab
|
|
|
1756
1789
|
await submit(def, body, options);
|
|
1757
1790
|
}
|
|
1758
1791
|
|
|
1792
|
+
// src/utils/ranking.ts
|
|
1793
|
+
var NETTRADE_FIELDS = {
|
|
1794
|
+
foreign: { buy: "for_netprps", sell: "for_netslmt" },
|
|
1795
|
+
institution: { buy: "orgn_netprps", sell: "orgn_netslmt" }
|
|
1796
|
+
};
|
|
1797
|
+
function extractNetTrade(rows, prefix, n) {
|
|
1798
|
+
return rows.slice(0, n).map((r, i) => ({
|
|
1799
|
+
rank: i + 1,
|
|
1800
|
+
code: r[`${prefix}_stk_cd`],
|
|
1801
|
+
name: r[`${prefix}_stk_nm`],
|
|
1802
|
+
\uAE08\uC561: won(unpad(r[`${prefix}_amt`] ?? "")),
|
|
1803
|
+
\uC218\uB7C9: won(unpad(r[`${prefix}_qty`] ?? ""))
|
|
1804
|
+
})).filter((x) => x.code);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1759
1807
|
// src/commands/ranking.ts
|
|
1760
1808
|
function formatRankRow(row) {
|
|
1761
1809
|
return formatFields(row, {
|
|
@@ -1857,6 +1905,45 @@ function registerRankingCommands(program2) {
|
|
|
1857
1905
|
handleError(err);
|
|
1858
1906
|
}
|
|
1859
1907
|
});
|
|
1908
|
+
ranking.command("net-buy").alias("supply").description("Foreign/institution net-buy \uC218\uAE09 ranking (ka90009)").option("-b, --by <foreign|institution|both>", "Investor: foreign=\uC678\uAD6D\uC778, institution=\uAE30\uAD00, both", "both").option("--side <buy|sell>", "buy=\uC21C\uB9E4\uC218, sell=\uC21C\uB9E4\uB3C4", "buy").option("-m, --market <000|001|101>", "Market: 000=all, 001=KOSPI, 101=KOSDAQ", "000").option("-x, --exchange <1|2|3>", "Exchange: 1=KRX, 2=NXT, 3=unified", "1").option("-n, --count <n>", "Top N (1-50)", "10").option("-q, --rank-by <1|2>", "Rank by 1=amount(\uAE08\uC561), 2=quantity(\uC218\uB7C9)", "1").option("-d, --date <YYYYMMDD>", "Query date (default: latest)").option("-o, --output <format>", "Output format (table/json)", "table").action(async (options) => {
|
|
1909
|
+
try {
|
|
1910
|
+
const side = options.side === "sell" ? "sell" : "buy";
|
|
1911
|
+
const by = ["foreign", "institution", "both"].includes(options.by) ? options.by : "both";
|
|
1912
|
+
const investors = by === "both" ? ["foreign", "institution"] : [by];
|
|
1913
|
+
const n = Math.min(Math.max(parseInt(String(options.count), 10) || 10, 1), 50);
|
|
1914
|
+
const client = createClient();
|
|
1915
|
+
const { data } = await client.callEndpoint(ENDPOINTS.rankForeignInst, {
|
|
1916
|
+
mrkt_tp: options.market,
|
|
1917
|
+
amt_qty_tp: options.rankBy === "2" ? "2" : "1",
|
|
1918
|
+
qry_dt_tp: options.date ? "1" : "0",
|
|
1919
|
+
date: options.date || "",
|
|
1920
|
+
stex_tp: options.exchange
|
|
1921
|
+
});
|
|
1922
|
+
const rows = Array.isArray(data?.[ENDPOINTS.rankForeignInst.listKey]) ? data[ENDPOINTS.rankForeignInst.listKey] : [];
|
|
1923
|
+
const fmt = getOutputFormat(options);
|
|
1924
|
+
const sideKo = side === "buy" ? "\uC21C\uB9E4\uC218" : "\uC21C\uB9E4\uB3C4";
|
|
1925
|
+
const labelKo = { foreign: "\uC678\uAD6D\uC778", institution: "\uAE30\uAD00" };
|
|
1926
|
+
if (fmt === "json") {
|
|
1927
|
+
const result = {
|
|
1928
|
+
side,
|
|
1929
|
+
rankBy: options.rankBy === "2" ? "quantity" : "amount"
|
|
1930
|
+
};
|
|
1931
|
+
for (const inv of investors) {
|
|
1932
|
+
result[inv] = extractNetTrade(rows, NETTRADE_FIELDS[inv][side], n);
|
|
1933
|
+
}
|
|
1934
|
+
output(result, "json");
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
for (const inv of investors) {
|
|
1938
|
+
const list = extractNetTrade(rows, NETTRADE_FIELDS[inv][side], n);
|
|
1939
|
+
console.log(`
|
|
1940
|
+
${labelKo[inv]} ${sideKo} TOP${n} (${options.rankBy === "2" ? "\uC218\uB7C9" : "\uAE08\uC561"} \uAE30\uC900)`);
|
|
1941
|
+
output(list, "table");
|
|
1942
|
+
}
|
|
1943
|
+
} catch (err) {
|
|
1944
|
+
handleError(err);
|
|
1945
|
+
}
|
|
1946
|
+
});
|
|
1860
1947
|
}
|
|
1861
1948
|
|
|
1862
1949
|
// src/commands/sector.ts
|