Implement "invest", "invest all" command
This commit is contained in:
@@ -139,7 +139,6 @@ extension Domestic {
|
||||
}
|
||||
public var result: KResult? = nil
|
||||
public let credential: Credential
|
||||
public var responseDataLoggable: Bool { true }
|
||||
|
||||
|
||||
private var trId: String {
|
||||
|
||||
@@ -61,6 +61,20 @@ public enum BelongClassCode: String, CustomStringConvertible {
|
||||
}
|
||||
|
||||
|
||||
/// 입력 종목코드
|
||||
public enum MarketDivisionCode: String, Codable {
|
||||
/// 전체
|
||||
case all = "0000"
|
||||
/// 코스피
|
||||
case kospi = "0001"
|
||||
/// 코스닥
|
||||
case kosdaq = "1001"
|
||||
|
||||
// TODO: 기타(업종코드)
|
||||
// https://apiportal.koreainvestment.com/community/10000000-0000-0011-0000-000000000002
|
||||
}
|
||||
|
||||
|
||||
extension Domestic {
|
||||
|
||||
/// 국내주식시세 - 거래량순위[v1_국내주식-047]
|
||||
@@ -90,7 +104,7 @@ extension Domestic {
|
||||
[
|
||||
"FID_COND_MRKT_DIV_CODE": "J",
|
||||
"FID_COND_SCR_DIV_CODE": "20171",
|
||||
"FID_INPUT_ISCD": "0000", // TODO: 기타(업종코드)
|
||||
"FID_INPUT_ISCD": MarketDivisionCode.all.rawValue,
|
||||
"FID_DIV_CLS_CODE": divisionClass.rawValue,
|
||||
"FID_BLNG_CLS_CODE": belongClass.rawValue,
|
||||
"FID_TRGT_CLS_CODE": "000000000",
|
||||
@@ -163,6 +177,136 @@ extension Domestic {
|
||||
self.baseDate = baseDate
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 국내주식주문 - 주식현재가 투자자[v1_국내주식-012]
|
||||
///
|
||||
public struct InvestorVolumeRequest: OrderRequest {
|
||||
public typealias KResult = InvestorVolumeResult
|
||||
|
||||
public var url: String { "/uapi/domestic-stock/v1/quotations/inquire-investor" }
|
||||
public var method: Method { .get }
|
||||
|
||||
public var header: [String: String?] {
|
||||
[
|
||||
"authorization": "Bearer \(accessToken)",
|
||||
"appkey": credential.appKey,
|
||||
"appsecret": credential.appSecret,
|
||||
"tr_id": trId,
|
||||
"custtype": CustomerType.personal.rawValue,
|
||||
]
|
||||
}
|
||||
public var body: [String: Any] {
|
||||
[
|
||||
"FID_COND_MRKT_DIV_CODE": "J",
|
||||
"FID_INPUT_ISCD": productNo,
|
||||
]
|
||||
}
|
||||
public var result: KResult? = nil
|
||||
public let credential: Credential
|
||||
public var responseDataLoggable: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
private var trId: String {
|
||||
"FHKST01010900"
|
||||
}
|
||||
|
||||
public let accessToken: String
|
||||
let productNo: String
|
||||
|
||||
public init(credential: Credential, accessToken: String, productNo: String) {
|
||||
self.credential = credential
|
||||
self.accessToken = accessToken
|
||||
self.productNo = productNo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 국내주식주문 - 국내기관_외국인 매매종목가집계[국내주식-037]
|
||||
///
|
||||
public struct ForeignInstitutionVolumeRequest: OrderRequest {
|
||||
public typealias KResult = String
|
||||
|
||||
public var isMockAvailable: Bool {
|
||||
credential.isMock == false
|
||||
}
|
||||
|
||||
public var url: String { "/uapi/domestic-stock/v1/quotations/foreign-institution-total" }
|
||||
public var method: Method { .get }
|
||||
|
||||
public var header: [String: String?] {
|
||||
[
|
||||
"authorization": "Bearer \(accessToken)",
|
||||
"appkey": credential.appKey,
|
||||
"appsecret": credential.appSecret,
|
||||
"tr_id": trId,
|
||||
"custtype": CustomerType.personal.rawValue,
|
||||
]
|
||||
}
|
||||
public var body: [String: Any] {
|
||||
[
|
||||
"FID_COND_MRKT_DIV_CODE": "V",
|
||||
"FID_COND_SCR_DIV_CODE": "16449",
|
||||
"FID_INPUT_ISCD": marketDivision.rawValue,
|
||||
"FID_DIV_CLS_CODE": order.rawValue,
|
||||
"FID_RANK_SORT_CLS_CODE": tradeType.rawValue,
|
||||
"FID_ETC_CLS_CODE": target.rawValue,
|
||||
]
|
||||
}
|
||||
public var result: KResult? = nil
|
||||
public let credential: Credential
|
||||
|
||||
|
||||
private var trId: String {
|
||||
"FHPTJ04400000"
|
||||
}
|
||||
|
||||
public let accessToken: String
|
||||
let marketDivision: MarketDivisionCode
|
||||
let order: Order
|
||||
let tradeType: TradeType
|
||||
let target: Target
|
||||
|
||||
/// 분류 구분 코드
|
||||
public enum Order: String {
|
||||
/// 수량정열
|
||||
case volume = "0"
|
||||
/// 금액정열
|
||||
case amount = "1"
|
||||
}
|
||||
|
||||
/// 순위 정렬 구분 코드
|
||||
public enum TradeType: String {
|
||||
/// 순매수상위
|
||||
case topBuying = "0"
|
||||
/// 순매도상위
|
||||
case topSelling = "1"
|
||||
}
|
||||
|
||||
/// 기타 구분 정렬
|
||||
public enum Target: String {
|
||||
/// 전체
|
||||
case all = "0"
|
||||
/// 외국인
|
||||
case foreigner = "1"
|
||||
/// 기관계
|
||||
case institution = "2"
|
||||
/// 기타
|
||||
case etc = "3"
|
||||
}
|
||||
|
||||
|
||||
public init(credential: Credential, accessToken: String, marketDivision: MarketDivisionCode, order: Order, tradeType: TradeType, target: Target) {
|
||||
self.credential = credential
|
||||
self.accessToken = accessToken
|
||||
self.marketDivision = marketDivision
|
||||
self.order = order
|
||||
self.tradeType = tradeType
|
||||
self.target = target
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -188,7 +332,7 @@ extension KissAccount {
|
||||
continuation.resume(throwing: GeneralError.invalidAccessToken)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let request = Domestic.StockVolumeRankRequest(credential: credential, accessToken: accessToken, divisionClass: option.divisionClass, belongClass: option.belongClass)
|
||||
request.query { result in
|
||||
switch result {
|
||||
@@ -221,4 +365,25 @@ extension KissAccount {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 투자자 거래량 가져오기
|
||||
///
|
||||
public func getInvestorVolume(productNo: String) async throws -> InvestorVolumeResult {
|
||||
return try await withUnsafeThrowingContinuation { continuation in
|
||||
guard let accessToken = accessToken else {
|
||||
continuation.resume(throwing: GeneralError.invalidAccessToken)
|
||||
return
|
||||
}
|
||||
|
||||
let request = Domestic.InvestorVolumeRequest(credential: credential, accessToken: accessToken, productNo: productNo)
|
||||
request.query { result in
|
||||
switch result {
|
||||
case .success(let result):
|
||||
continuation.resume(returning: result)
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,3 +190,148 @@ public struct HolidyResult: Codable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct InvestorVolumeResult: Codable {
|
||||
public let resultCode: String
|
||||
public let messageCode: String
|
||||
public let message: String
|
||||
public let output: [OutputDetail]?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case resultCode = "rt_cd"
|
||||
case messageCode = "msg_cd"
|
||||
case message = "msg1"
|
||||
case output = "output"
|
||||
}
|
||||
|
||||
public struct OutputDetail: Codable, PropertyIterable, ArrayDecodable {
|
||||
/// 주식 영업 일자
|
||||
public let stockBusinessDate: String
|
||||
|
||||
/// 주식 종가
|
||||
public let stockClosingPrice: String
|
||||
|
||||
/// 전일 대비
|
||||
public let previousDayVariableRatio: String
|
||||
|
||||
/// 전일 대비 부호
|
||||
public let previousDayVariableRatioSign: String
|
||||
|
||||
/// 개인 순매수 수량
|
||||
public let personalNetBuyingQuantity: String
|
||||
|
||||
/// 외국인 순매수 수량
|
||||
public let foreignNetBuyingQuantity: String
|
||||
|
||||
/// 기관계 순매수 수량
|
||||
public let organizationNetBuyingQuantity: String
|
||||
|
||||
/// 개인 순매수 거래 대금
|
||||
public let personalNetBuyingTradingAmount: String
|
||||
|
||||
/// 외국인 순매수 거래 대금
|
||||
public let foreignNetBuyingTradingAmount: String
|
||||
|
||||
/// 기관계 순매수 거래 대금
|
||||
public let organizationNetBuyingTradingAmount: String
|
||||
|
||||
/// 개인 매수2 거래량
|
||||
public let personalBuyingVolume: String
|
||||
|
||||
/// 외국인 매수2 거래량
|
||||
public let foreignBuyingVolume: String
|
||||
|
||||
/// 기관계 매수2 거래량
|
||||
public let organizationBuyingVolume: String
|
||||
|
||||
/// 개인 매수2 거래 대금
|
||||
public let personalBuyingTradingAmount: String
|
||||
|
||||
/// 외국인 매수2 거래 대금
|
||||
public let foreignBuyingTradingAmount: String
|
||||
|
||||
/// 기관계 매수2 거래 대금
|
||||
public let organizationBuyingTradingAmount: String
|
||||
|
||||
/// 개인 매도 거래량
|
||||
public let personalSellingVolume: String
|
||||
|
||||
/// 외국인 매도 거래량
|
||||
public let foreignSellingVolume: String
|
||||
|
||||
/// 기관계 매도 거래량
|
||||
public let organizationSellingVolume: String
|
||||
|
||||
/// 개인 매도 거래 대금
|
||||
public let personalSellingTradingAmount: String
|
||||
|
||||
/// 외국인 매도 거래 대금
|
||||
public let foreignSellingTradingAmount: String
|
||||
|
||||
/// 기관계 매도 거래 대금
|
||||
public let organizationSellingTradingAmount: String
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case stockBusinessDate = "stck_bsop_date"
|
||||
case stockClosingPrice = "stck_clpr"
|
||||
case previousDayVariableRatio = "prdy_vrss"
|
||||
case previousDayVariableRatioSign = "prdy_vrss_sign"
|
||||
case personalNetBuyingQuantity = "prsn_ntby_qty"
|
||||
case foreignNetBuyingQuantity = "frgn_ntby_qty"
|
||||
case organizationNetBuyingQuantity = "orgn_ntby_qty"
|
||||
case personalNetBuyingTradingAmount = "prsn_ntby_tr_pbmn"
|
||||
case foreignNetBuyingTradingAmount = "frgn_ntby_tr_pbmn"
|
||||
case organizationNetBuyingTradingAmount = "orgn_ntby_tr_pbmn"
|
||||
case personalBuyingVolume = "prsn_shnu_vol"
|
||||
case foreignBuyingVolume = "frgn_shnu_vol"
|
||||
case organizationBuyingVolume = "orgn_shnu_vol"
|
||||
case personalBuyingTradingAmount = "prsn_shnu_tr_pbmn"
|
||||
case foreignBuyingTradingAmount = "frgn_shnu_tr_pbmn"
|
||||
case organizationBuyingTradingAmount = "orgn_shnu_tr_pbmn"
|
||||
case personalSellingVolume = "prsn_seln_vol"
|
||||
case foreignSellingVolume = "frgn_seln_vol"
|
||||
case organizationSellingVolume = "orgn_seln_vol"
|
||||
case personalSellingTradingAmount = "prsn_seln_tr_pbmn"
|
||||
case foreignSellingTradingAmount = "frgn_seln_tr_pbmn"
|
||||
case organizationSellingTradingAmount = "orgn_seln_tr_pbmn"
|
||||
}
|
||||
|
||||
public init(array: [String]) throws {
|
||||
guard array.count == 22 else {
|
||||
throw GeneralError.incorrectArrayItems
|
||||
}
|
||||
self.stockBusinessDate = array[0]
|
||||
self.stockClosingPrice = array[1]
|
||||
self.previousDayVariableRatio = array[2]
|
||||
self.previousDayVariableRatioSign = array[3]
|
||||
self.personalNetBuyingQuantity = array[4]
|
||||
self.foreignNetBuyingQuantity = array[5]
|
||||
self.organizationNetBuyingQuantity = array[6]
|
||||
self.personalNetBuyingTradingAmount = array[7]
|
||||
self.foreignNetBuyingTradingAmount = array[8]
|
||||
self.organizationNetBuyingTradingAmount = array[9]
|
||||
self.personalBuyingVolume = array[10]
|
||||
self.foreignBuyingVolume = array[11]
|
||||
self.organizationBuyingVolume = array[12]
|
||||
self.personalBuyingTradingAmount = array[13]
|
||||
self.foreignBuyingTradingAmount = array[14]
|
||||
self.organizationBuyingTradingAmount = array[15]
|
||||
self.personalSellingVolume = array[16]
|
||||
self.foreignSellingVolume = array[17]
|
||||
self.organizationSellingVolume = array[18]
|
||||
self.personalSellingTradingAmount = array[19]
|
||||
self.foreignSellingTradingAmount = array[20]
|
||||
self.organizationSellingTradingAmount = array[21]
|
||||
}
|
||||
|
||||
public static func symbols() -> [String] {
|
||||
let i = try! OutputDetail(array: Array(repeating: "", count: 22))
|
||||
return Mirror(reflecting: i).children.compactMap { $0.label }
|
||||
}
|
||||
|
||||
public static func localizedSymbols() -> [String: String] {
|
||||
[:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ extension KissConsole {
|
||||
}
|
||||
|
||||
static func productPriceUrl(productNo: String) -> URL {
|
||||
let subPath = "data/\(productNo)"
|
||||
let subPath = "data/\(productNo)/price"
|
||||
let subFile = "\(subPath)/prices.csv"
|
||||
|
||||
let fileUrl = URL.currentDirectory().appending(path: subFile)
|
||||
@@ -40,6 +40,15 @@ extension KissConsole {
|
||||
return fileUrl
|
||||
}
|
||||
|
||||
static func investorFileUrl(productNo: String, day: String) -> URL {
|
||||
let subPath = "data/\(productNo)/investor"
|
||||
let subFile = "\(subPath)/investor-\(day).csv"
|
||||
|
||||
let fileUrl = URL.currentDirectory().appending(path: subFile)
|
||||
createSubpath(subPath)
|
||||
return fileUrl
|
||||
}
|
||||
|
||||
static func candleFileUrl(productNo: String, period: CandleFilePeriod, day: String) -> URL {
|
||||
assert(day.count == 8)
|
||||
let subPath = "data/\(productNo)/\(period.rawValue)"
|
||||
|
||||
@@ -78,12 +78,12 @@ extension KissConsole {
|
||||
}
|
||||
|
||||
candles.sort(by: { $0.stockBusinessDate > $1.stockBusinessDate })
|
||||
guard let maxTime = candles.first?.stockBusinessDate else {
|
||||
guard let recentDay = candles.first?.stockBusinessDate else {
|
||||
print("No price items")
|
||||
return false
|
||||
}
|
||||
|
||||
let fileUrl = KissConsole.candleFileUrl(productNo: productNo, period: period.filePeriod, day: maxTime)
|
||||
let fileUrl = KissConsole.candleFileUrl(productNo: productNo, period: period.filePeriod, day: recentDay)
|
||||
try candles.writeCsv(toFile: fileUrl, localized: localized)
|
||||
|
||||
print("wrote \(fileUrl.lastPathComponent) with \(candles.count)")
|
||||
@@ -159,6 +159,7 @@ extension KissConsole {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func checkHoliday(_ date: Date) async throws -> Bool {
|
||||
guard await KissContext.shared.targetDate.yyyyMMdd != date.yyyyMMdd else {
|
||||
return await KissContext.shared.isHoliday
|
||||
@@ -167,6 +168,40 @@ extension KissConsole {
|
||||
await KissContext.shared.updateHoliday(isHoliday, targetDate: date)
|
||||
return isHoliday
|
||||
}
|
||||
|
||||
|
||||
func validateAllCsvs(filePriod: CandleFilePeriod) throws {
|
||||
let urls = try FileManager.collectCsv(period: filePriod, candleDate: nil)
|
||||
|
||||
var lastTime = Date.appTime
|
||||
for (index, url) in urls.enumerated() {
|
||||
|
||||
let r = validateCsv(filePriod: filePriod, url: url)
|
||||
switch r {
|
||||
case .ok, .invalidFileName:
|
||||
break
|
||||
default:
|
||||
print("csv invalid: \(r) at \(url)")
|
||||
throw GeneralError.invalidCandleCsvFile(r.description)
|
||||
}
|
||||
|
||||
let curTime = Date.appTime
|
||||
if (curTime - lastTime) > 5 {
|
||||
lastTime = curTime
|
||||
print("checking... \(index+1)/\(urls.count)")
|
||||
}
|
||||
}
|
||||
print("DONE csv valid \(urls.count)")
|
||||
}
|
||||
|
||||
|
||||
func validateCsv(filePriod: CandleFilePeriod, url: URL) -> CandleValidation {
|
||||
switch filePriod {
|
||||
case .minute: return KissConsole.validateCandleMinute(url)
|
||||
case .day: return KissConsole.validateCandleDay(url)
|
||||
case .weak: return KissConsole.validateCandleWeek(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
28
KissMeConsole/Sources/KissConsole+Investor.swift
Normal file
28
KissMeConsole/Sources/KissConsole+Investor.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// KissConsole+Investor.swift
|
||||
// KissMeConsole
|
||||
//
|
||||
// Created by ened-book-m1 on 2023/06/11.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import KissMe
|
||||
|
||||
|
||||
extension KissConsole {
|
||||
func getInvestor(productNo: String) async throws -> Bool {
|
||||
let result = try await account!.getInvestorVolume(productNo: productNo)
|
||||
if let output = result.output {
|
||||
print(output.count)
|
||||
|
||||
guard let recentDay = output.first?.stockBusinessDate else {
|
||||
print("No investor items")
|
||||
return false
|
||||
}
|
||||
|
||||
let fileUrl = KissConsole.investorFileUrl(productNo: productNo, day: recentDay)
|
||||
try output.writeCsv(toFile: fileUrl, localized: localized)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,10 @@ class KissConsole {
|
||||
case candleWeek = "candle week"
|
||||
case candleValidate = "candle validate"
|
||||
|
||||
// 투자자 열람
|
||||
case investor = "investor"
|
||||
case investorAll = "investor all"
|
||||
|
||||
// 종목 열람
|
||||
case loadShop = "load shop"
|
||||
case updateShop = "update shop"
|
||||
@@ -85,6 +89,8 @@ class KissConsole {
|
||||
return true
|
||||
case .candleValidate:
|
||||
return false
|
||||
case .investor, .investorAll:
|
||||
return true
|
||||
case .loadShop, .updateShop, .look:
|
||||
return false
|
||||
case .showcase:
|
||||
@@ -203,6 +209,9 @@ class KissConsole {
|
||||
case .candleDay: onCandleDay(args)
|
||||
case .candleWeek: onCandleWeek(args)
|
||||
case .candleValidate: onCandleValidate(args)
|
||||
|
||||
case .investor: await onInvestor(args)
|
||||
case .investorAll: onInvestorAll(args)
|
||||
|
||||
case .loadShop: await onLoadShop()
|
||||
case .updateShop: await onUpdateShop()
|
||||
@@ -736,40 +745,6 @@ extension KissConsole {
|
||||
}
|
||||
|
||||
|
||||
func validateAllCsvs(filePriod: CandleFilePeriod) throws {
|
||||
let urls = try FileManager.collectCsv(period: filePriod, candleDate: nil)
|
||||
|
||||
var lastTime = Date.appTime
|
||||
for (index, url) in urls.enumerated() {
|
||||
|
||||
let r = validateCsv(filePriod: filePriod, url: url)
|
||||
switch r {
|
||||
case .ok, .invalidFileName:
|
||||
break
|
||||
default:
|
||||
print("csv invalid: \(r) at \(url)")
|
||||
throw GeneralError.invalidCandleCsvFile(r.description)
|
||||
}
|
||||
|
||||
let curTime = Date.appTime
|
||||
if (curTime - lastTime) > 5 {
|
||||
lastTime = curTime
|
||||
print("checking... \(index+1)/\(urls.count)")
|
||||
}
|
||||
}
|
||||
print("DONE csv valid \(urls.count)")
|
||||
}
|
||||
|
||||
|
||||
func validateCsv(filePriod: CandleFilePeriod, url: URL) -> CandleValidation {
|
||||
switch filePriod {
|
||||
case .minute: return KissConsole.validateCandleMinute(url)
|
||||
case .day: return KissConsole.validateCandleDay(url)
|
||||
case .weak: return KissConsole.validateCandleWeek(url)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func onCandleValidate(_ args: [String]) {
|
||||
let period: CandleFilePeriod?
|
||||
if args.count == 1 {
|
||||
@@ -788,6 +763,44 @@ extension KissConsole {
|
||||
}
|
||||
|
||||
|
||||
private func onInvestor(_ args: [String]) async {
|
||||
let productNo: String? = (args.isEmpty ? currentShortCode: args[0])
|
||||
guard let productNo = productNo else {
|
||||
print("Invalid productNo")
|
||||
return
|
||||
}
|
||||
do {
|
||||
_ = try await getInvestor(productNo: productNo)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func onInvestorAll(_ args: [String]) {
|
||||
let all = getAllProducts()
|
||||
for item in all {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
let holiday = try? await checkHoliday(Date())
|
||||
if holiday == true {
|
||||
print("DONE today is holiday")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let success = try await getInvestor(productNo: item.shortCode)
|
||||
print("DONE \(success) \(item.shortCode)")
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func onCandle(_ args: [String]) async {
|
||||
let productNo: String? = (args.isEmpty ? currentShortCode: args[0])
|
||||
guard let productNo = productNo else {
|
||||
@@ -983,6 +996,7 @@ extension KissConsole {
|
||||
symbols.formUnion(PeriodPriceResult.OutputPrice.symbols())
|
||||
symbols.formUnion(VolumeRankResult.OutputDetail.symbols())
|
||||
symbols.formUnion(CurrentPriceResult.OutputDetail.symbols())
|
||||
symbols.formUnion(InvestorVolumeResult.OutputDetail.symbols())
|
||||
let newNames = symbols.sorted(by: { $0 < $1 })
|
||||
|
||||
let nameUrl = KissConsole.localNamesUrl
|
||||
|
||||
@@ -32,6 +32,8 @@ WIP `modify (PNO) (ONO) (가격) (수량)` | 주문 내역을 변경. (수량)
|
||||
`candle week [PNO]` | 종목의 최근 52주 동안의 주봉 열람. PNO 은 생략 가능. **data/(PNO)/week/candle-(yyyyMMdd).csv** 파일로 저장.
|
||||
`candle week all` | 모든 종목의 최근 52주 동안의 주봉 열람. cron job 으로 오전 장이 시작전에 미리 수집. **data/(PNO)/week/candle-(yyyyMMdd).csv** 파일로 저장.
|
||||
`candle validate (기간)` | (기간) 타입의 모든 csv 파일에 대해서 데이터가 유효한지 검사. (기간) 으로는 **min**, **day**, **week** 을 지정하고, 생략되면 **min** 으로 간주.
|
||||
`investor [PNO]` | 종목의 투자자 거래량 열람. PNO 은 생략 가능. **data/(PNO)/investor/investor-(yyyyMMdd).csv** 파일로 저장.
|
||||
`investor all` | 모든 종목의 투자자 거래량 열람. **data/(PNO)/investor/investor-(yyyyMMdd).csv** 파일로 저장.
|
||||
`load shop` | data/shop-products.csv 로부터 전체 상품을 로딩.
|
||||
`update shop` | **금융위원회_KRX상장종목정보** 로부터 전체 상품을 얻어서 **data/shop-products.csv** 로 저장.
|
||||
`look (상품명)` | (상품명) 에 해당되는 PNO 를 표시함.
|
||||
|
||||
2
bin/data
2
bin/data
Submodule bin/data updated: ccb557db23...80c2298377
@@ -10,6 +10,7 @@
|
||||
341F5ED42A0A8B9000962D48 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5ED32A0A8B9000962D48 /* main.swift */; };
|
||||
341F5F052A13B82F00962D48 /* test.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F042A13B82F00962D48 /* test.swift */; };
|
||||
341F5F092A1463A100962D48 /* KissConsole.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F082A1463A100962D48 /* KissConsole.swift */; };
|
||||
3435A7F22A35A8A900D604F1 /* KissConsole+Investor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3435A7F12A35A8A900D604F1 /* KissConsole+Investor.swift */; };
|
||||
348168492A2F92AC00A50BD3 /* KissContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348168482A2F92AC00A50BD3 /* KissContext.swift */; };
|
||||
348168692A3420BD00A50BD3 /* LocalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348168682A3420BD00A50BD3 /* LocalContext.swift */; };
|
||||
349327F72A20E3E300097063 /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349327F62A20E3E300097063 /* Foundation+Extensions.swift */; };
|
||||
@@ -48,6 +49,7 @@
|
||||
341F5EDB2A0A8C4600962D48 /* KissMe.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KissMe.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
341F5F042A13B82F00962D48 /* test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = test.swift; sourceTree = "<group>"; };
|
||||
341F5F082A1463A100962D48 /* KissConsole.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissConsole.swift; sourceTree = "<group>"; };
|
||||
3435A7F12A35A8A900D604F1 /* KissConsole+Investor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Investor.swift"; sourceTree = "<group>"; };
|
||||
348168482A2F92AC00A50BD3 /* KissContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissContext.swift; sourceTree = "<group>"; };
|
||||
348168682A3420BD00A50BD3 /* LocalContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalContext.swift; sourceTree = "<group>"; };
|
||||
349327F62A20E3E300097063 /* Foundation+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Foundation+Extensions.swift"; sourceTree = "<group>"; };
|
||||
@@ -96,6 +98,7 @@
|
||||
341F5F082A1463A100962D48 /* KissConsole.swift */,
|
||||
34D3680C2A280801005E6756 /* KissConsole+Candle.swift */,
|
||||
349843202A242AC900E85B08 /* KissConsole+CSV.swift */,
|
||||
3435A7F12A35A8A900D604F1 /* KissConsole+Investor.swift */,
|
||||
349327F62A20E3E300097063 /* Foundation+Extensions.swift */,
|
||||
);
|
||||
name = KissMeConsole;
|
||||
@@ -182,6 +185,7 @@
|
||||
341F5ED42A0A8B9000962D48 /* main.swift in Sources */,
|
||||
349327F72A20E3E300097063 /* Foundation+Extensions.swift in Sources */,
|
||||
341F5F092A1463A100962D48 /* KissConsole.swift in Sources */,
|
||||
3435A7F22A35A8A900D604F1 /* KissConsole+Investor.swift in Sources */,
|
||||
341F5F052A13B82F00962D48 /* test.swift in Sources */,
|
||||
348168692A3420BD00A50BD3 /* LocalContext.swift in Sources */,
|
||||
349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */,
|
||||
|
||||
Reference in New Issue
Block a user