Implement "now all" command to capture all current prices
This commit is contained in:
@@ -35,6 +35,8 @@ public enum GeneralError: Error {
|
||||
case emptyData
|
||||
case cannotReadFile
|
||||
case cannotWriteFile
|
||||
case cannotReadFileLine
|
||||
case cannotReadFileToConvertString
|
||||
case incorrectArrayItems
|
||||
case headerNoFiendName(String)
|
||||
case noCsvFile
|
||||
|
||||
@@ -18,7 +18,7 @@ public enum YesNo: String, Codable {
|
||||
|
||||
/// 보증금 비율 구분
|
||||
public enum MarginalRateClass: String, Codable {
|
||||
case undefined = ""
|
||||
case undefined = " "
|
||||
|
||||
/// 20%, 30%, 40%
|
||||
case _40 = "40"
|
||||
@@ -149,7 +149,7 @@ public struct CurrentPriceResult: Codable {
|
||||
public let newHighLowPriceClassCode: String?
|
||||
|
||||
/// 업종 한글 종목명
|
||||
public let koreanBusinessTypeName: String
|
||||
public let koreanBusinessTypeName: String?
|
||||
|
||||
/// 임시 정지 여부
|
||||
public let temporaryStopped: YesNo
|
||||
@@ -568,6 +568,443 @@ public struct CurrentPriceResult: Codable {
|
||||
}
|
||||
|
||||
|
||||
public struct CapturePrice: Codable, PropertyIterable, ArrayDecodable {
|
||||
/// 주식 영업 일자
|
||||
public let stockBusinessDate: String
|
||||
|
||||
/// 수집 시간
|
||||
public let captureTime: String // HHmmss
|
||||
|
||||
|
||||
// MARK: 이하는 CurrentPriceResult.OutputDetail 와 동일
|
||||
|
||||
/// 종목 상태 구분 코드
|
||||
public let itemStateCode: CurrentPriceResult.StateClass
|
||||
|
||||
/// 증거금 비율
|
||||
public let marginalRate: String
|
||||
|
||||
/// 대표 시장 한글 명
|
||||
public let koreanMarketName: String
|
||||
|
||||
/// 신 고가 저가 구분 코드
|
||||
public let newHighLowPriceClassCode: String?
|
||||
|
||||
/// 업종 한글 종목명
|
||||
public let koreanBusinessTypeName: String?
|
||||
|
||||
/// 임시 정지 여부
|
||||
public let temporaryStopped: YesNo
|
||||
|
||||
/// 시가 범위 연장 여부
|
||||
public let marketPriceRangeExtended: YesNo
|
||||
|
||||
/// 종가 범위 연장 여부
|
||||
public let closingPriceRangeExtended: YesNo
|
||||
|
||||
/// 신용 가능 여부
|
||||
public let creditAllowable: YesNo
|
||||
|
||||
/// 보증금 비율 구분 코드
|
||||
public let marginalRateClassCode: MarginalRateClass
|
||||
|
||||
/// ELW 발행 여부
|
||||
public let elwPublished: YesNo
|
||||
|
||||
/// 주식 현재가 (주식 종가)
|
||||
public let currentStockPrice: String
|
||||
|
||||
/// 전일 대비
|
||||
public let previousDayVariableRatio: String
|
||||
|
||||
/// 전일 대비 부호
|
||||
public let previousDayVariableRatioSign: String
|
||||
|
||||
/// 전일 대비율
|
||||
public let previousDayDiffRatio: String
|
||||
|
||||
/// 누적 거래 대금
|
||||
public let accumulatedTradingAmount: String
|
||||
|
||||
/// 누적 거래량
|
||||
public let accumulatedVolume: String
|
||||
|
||||
/// 전일 대비 거래량 비율
|
||||
public let previousDayDiffVolumeRatio: String
|
||||
|
||||
/// 주식 시가
|
||||
public let stockOpenningPrice: String
|
||||
|
||||
/// 주식 최고가
|
||||
public let highestStockPrice: String
|
||||
|
||||
/// 주식 최저가
|
||||
public let lowestStockPrice: String
|
||||
|
||||
/// 주식 상한가
|
||||
public let maximumStockPrice: String
|
||||
|
||||
/// 주식 하한가
|
||||
public let minimumStockPrice: String
|
||||
|
||||
/// 주식 기준가
|
||||
public let standardStockPrice: String
|
||||
|
||||
/// 가중 평균 주식 가격
|
||||
public let weightedAverageStockPrice: String
|
||||
|
||||
/// HTS 외국인 소진율
|
||||
public let htsForeignRunoutRate: String
|
||||
|
||||
/// 외국인 순매수 수량
|
||||
public let foreignNetBuyingQuantity: String
|
||||
|
||||
/// 프로그램매매 순매수 수량
|
||||
public let programTradeNetBuyingQuantity: String
|
||||
|
||||
/// 피벗 2차 디저항 가격
|
||||
public let pivotSecondDResistancePrice: String
|
||||
|
||||
/// 피벗 1차 디저항 가격
|
||||
public let pivotFirstDResistancePrice: String
|
||||
|
||||
/// 피벗 포인트 값
|
||||
public let pivotPointValue: String
|
||||
|
||||
/// 피벗 1차 디지지 가격
|
||||
public let pivotFirstDSupportPrice: String
|
||||
|
||||
/// 피벗 2차 디지지 가격
|
||||
public let pivotSecondDSupportPrice: String
|
||||
|
||||
/// 디저항 값
|
||||
public let dResistanceValue: String
|
||||
|
||||
/// 디지지 값
|
||||
public let dSupportValue: String
|
||||
|
||||
/// 자본금
|
||||
public let capital: String
|
||||
|
||||
/// 제한 폭 가격
|
||||
public let limitWidthPrice: String
|
||||
|
||||
/// 주식 액면가
|
||||
public let stockFacePrice: String
|
||||
|
||||
/// 주식 대용가
|
||||
public let stockSubstitudePrice: String
|
||||
|
||||
/// 호가단위
|
||||
public let askingPriceUnit: String
|
||||
|
||||
/// HTS 매매 수량 단위 값
|
||||
public let htsDealQuantityUnit: String
|
||||
|
||||
/// 상장 주수
|
||||
public let listedStockCount: String
|
||||
|
||||
/// HTS 시가총액
|
||||
public let htsTotalMarketValue: String
|
||||
|
||||
/// PER
|
||||
public let per: String
|
||||
|
||||
/// PBR
|
||||
public let pbr: String
|
||||
|
||||
/// 결산 월
|
||||
public let settlingMonth: String
|
||||
|
||||
/// 거래량 회전율
|
||||
public let volumeTurnoverRate: String
|
||||
|
||||
/// EPS
|
||||
public let eps: String
|
||||
|
||||
/// BPS
|
||||
public let bps: String
|
||||
|
||||
/// 250일 최고가
|
||||
public let day250HighestPrice: String
|
||||
|
||||
/// 250일 최고가 일자
|
||||
public let day250HighestPriceDate: String
|
||||
|
||||
/// 250일 최고가 대비 현재가 비율
|
||||
public let day250HighestPriceDiffRatio: String
|
||||
|
||||
/// 250일 최저가
|
||||
public let day250LowestPrice: String
|
||||
|
||||
/// 250일 최저가 일자
|
||||
public let day250LowestPriceDate: String
|
||||
|
||||
/// 250일 최저가 대비 현재가 비율
|
||||
public let day250LowestPriceDiffRatio: String
|
||||
|
||||
/// 주식 연중 최고가
|
||||
public let annualHighestPrice: String
|
||||
|
||||
/// 연중 최고가 대비 현재가 비율
|
||||
public let annualHighestPriceDiffRatio: String
|
||||
|
||||
/// 연중 최고가 일자
|
||||
public let annualHighestPriceDate: String
|
||||
|
||||
/// 주식 연중 최저가
|
||||
public let annualLowestPrice: String
|
||||
|
||||
/// 연중 최저가 대비 현재가 비율
|
||||
public let annualLowestPriceDiffRatio: String
|
||||
|
||||
/// 연중 최저가 일자
|
||||
public let annualLowestPriceDate: String
|
||||
|
||||
/// 52주일 최고가
|
||||
public let week52HighestPrice: String
|
||||
|
||||
/// 52주일 최고가 대비 현재가 대비
|
||||
public let week52HighestPriceDiffRatio: String
|
||||
|
||||
/// 52주일 최고가 일자
|
||||
public let week52HighestPriceDate: String
|
||||
|
||||
/// 52주일 최저가
|
||||
public let week52LowestPrice: String
|
||||
|
||||
/// 52주일 최저가 대비 현재가 대비
|
||||
public let week52LowestPriceDiffRatio: String
|
||||
|
||||
/// 52주일 최저가 일자
|
||||
public let week52LowestPriceDate: String
|
||||
|
||||
/// 전체 융자 잔고 비율
|
||||
public let totalOutstandingloanRate: String
|
||||
|
||||
/// 공매도가능여부
|
||||
public let shortSellingAllowable: YesNo
|
||||
|
||||
/// 주식 단축 종목코드
|
||||
public let shortProductCode: String
|
||||
|
||||
/// 액면가 통화명
|
||||
public let facePriceCurrency: String
|
||||
|
||||
/// 자본금 통화명
|
||||
public let capitalCurrency: String
|
||||
|
||||
/// 접근도
|
||||
public let approachRate: String?
|
||||
|
||||
/// 외국인 보유 수량
|
||||
public let foreignHoldQuantity: String
|
||||
|
||||
/// VI적용구분코드
|
||||
public let viClassCode: String
|
||||
|
||||
/// 시간외단일가VI적용구분코드
|
||||
public let viClassCodeForOvertimeMarketPrice: String
|
||||
|
||||
/// 최종 공매도 체결 수량
|
||||
public let lastShortSellingConclusionQuantity: String
|
||||
|
||||
/// 투자유의여부
|
||||
public let investmentCareful: YesNo
|
||||
|
||||
/// 시장경고코드
|
||||
public let marketWarningCode: MarketWarning
|
||||
|
||||
/// 단기과열여부
|
||||
public let shortOverheated: YesNo
|
||||
|
||||
|
||||
public init(captureTime: Date, price p: CurrentPriceResult.OutputDetail) {
|
||||
self.stockBusinessDate = captureTime.yyyyMMdd
|
||||
self.captureTime = captureTime.HHmmss
|
||||
|
||||
// MARK: CurrentPriceResult.OutputDetail
|
||||
self.itemStateCode = p.itemStateCode
|
||||
self.marginalRate = p.marginalRate
|
||||
self.koreanMarketName = p.koreanMarketName
|
||||
self.newHighLowPriceClassCode = p.newHighLowPriceClassCode
|
||||
self.koreanBusinessTypeName = p.koreanBusinessTypeName
|
||||
self.temporaryStopped = p.temporaryStopped
|
||||
self.marketPriceRangeExtended = p.marketPriceRangeExtended
|
||||
self.closingPriceRangeExtended = p.closingPriceRangeExtended
|
||||
self.creditAllowable = p.creditAllowable
|
||||
self.marginalRateClassCode = p.marginalRateClassCode
|
||||
self.elwPublished = p.elwPublished
|
||||
self.currentStockPrice = p.currentStockPrice
|
||||
self.previousDayVariableRatio = p.previousDayVariableRatio
|
||||
self.previousDayVariableRatioSign = p.previousDayVariableRatioSign
|
||||
self.previousDayDiffRatio = p.previousDayDiffRatio
|
||||
self.accumulatedTradingAmount = p.accumulatedTradingAmount
|
||||
self.accumulatedVolume = p.accumulatedVolume
|
||||
self.previousDayDiffVolumeRatio = p.previousDayDiffVolumeRatio
|
||||
self.stockOpenningPrice = p.stockOpenningPrice
|
||||
self.highestStockPrice = p.highestStockPrice
|
||||
self.lowestStockPrice = p.lowestStockPrice
|
||||
self.maximumStockPrice = p.maximumStockPrice
|
||||
self.minimumStockPrice = p.minimumStockPrice
|
||||
self.standardStockPrice = p.standardStockPrice
|
||||
self.weightedAverageStockPrice = p.weightedAverageStockPrice
|
||||
self.htsForeignRunoutRate = p.htsForeignRunoutRate
|
||||
self.foreignNetBuyingQuantity = p.foreignNetBuyingQuantity
|
||||
self.programTradeNetBuyingQuantity = p.programTradeNetBuyingQuantity
|
||||
self.pivotSecondDResistancePrice = p.pivotSecondDResistancePrice
|
||||
self.pivotFirstDResistancePrice = p.pivotFirstDResistancePrice
|
||||
self.pivotPointValue = p.pivotPointValue
|
||||
self.pivotFirstDSupportPrice = p.pivotFirstDSupportPrice
|
||||
self.pivotSecondDSupportPrice = p.pivotSecondDSupportPrice
|
||||
self.dResistanceValue = p.dResistanceValue
|
||||
self.dSupportValue = p.dSupportValue
|
||||
self.capital = p.capital
|
||||
self.limitWidthPrice = p.limitWidthPrice
|
||||
self.stockFacePrice = p.stockFacePrice
|
||||
self.stockSubstitudePrice = p.stockSubstitudePrice
|
||||
self.askingPriceUnit = p.askingPriceUnit
|
||||
self.htsDealQuantityUnit = p.htsDealQuantityUnit
|
||||
self.listedStockCount = p.listedStockCount
|
||||
self.htsTotalMarketValue = p.htsTotalMarketValue
|
||||
self.per = p.per
|
||||
self.pbr = p.pbr
|
||||
self.settlingMonth = p.settlingMonth
|
||||
self.volumeTurnoverRate = p.volumeTurnoverRate
|
||||
self.eps = p.eps
|
||||
self.bps = p.bps
|
||||
self.day250HighestPrice = p.day250HighestPrice
|
||||
self.day250HighestPriceDate = p.day250HighestPriceDate
|
||||
self.day250HighestPriceDiffRatio = p.day250HighestPriceDiffRatio
|
||||
self.day250LowestPrice = p.day250LowestPrice
|
||||
self.day250LowestPriceDate = p.day250LowestPriceDate
|
||||
self.day250LowestPriceDiffRatio = p.day250LowestPriceDiffRatio
|
||||
self.annualHighestPrice = p.annualHighestPrice
|
||||
self.annualHighestPriceDiffRatio = p.annualHighestPriceDiffRatio
|
||||
self.annualHighestPriceDate = p.annualHighestPriceDate
|
||||
self.annualLowestPrice = p.annualLowestPrice
|
||||
self.annualLowestPriceDiffRatio = p.annualLowestPriceDiffRatio
|
||||
self.annualLowestPriceDate = p.annualLowestPriceDate
|
||||
self.week52HighestPrice = p.week52HighestPrice
|
||||
self.week52HighestPriceDiffRatio = p.week52HighestPriceDiffRatio
|
||||
self.week52HighestPriceDate = p.week52HighestPriceDate
|
||||
self.week52LowestPrice = p.week52LowestPrice
|
||||
self.week52LowestPriceDiffRatio = p.week52LowestPriceDiffRatio
|
||||
self.week52LowestPriceDate = p.week52LowestPriceDate
|
||||
self.totalOutstandingloanRate = p.totalOutstandingloanRate
|
||||
self.shortSellingAllowable = p.shortSellingAllowable
|
||||
self.shortProductCode = p.shortProductCode
|
||||
self.facePriceCurrency = p.facePriceCurrency
|
||||
self.capitalCurrency = p.capitalCurrency
|
||||
self.approachRate = p.approachRate
|
||||
self.foreignHoldQuantity = p.foreignHoldQuantity
|
||||
self.viClassCode = p.viClassCode
|
||||
self.viClassCodeForOvertimeMarketPrice = p.viClassCodeForOvertimeMarketPrice
|
||||
self.lastShortSellingConclusionQuantity = p.lastShortSellingConclusionQuantity
|
||||
self.investmentCareful = p.investmentCareful
|
||||
self.marketWarningCode = p.marketWarningCode
|
||||
self.shortOverheated = p.shortOverheated
|
||||
}
|
||||
|
||||
public init(array: [String]) throws {
|
||||
guard array.count == 82 else {
|
||||
throw GeneralError.incorrectArrayItems
|
||||
}
|
||||
self.stockBusinessDate = array[0]
|
||||
self.captureTime = array[1]
|
||||
self.itemStateCode = CurrentPriceResult.StateClass(rawValue: array[2])!
|
||||
self.marginalRate = array[3]
|
||||
self.koreanMarketName = array[4]
|
||||
self.newHighLowPriceClassCode = array[5]
|
||||
self.koreanBusinessTypeName = array[6]
|
||||
self.temporaryStopped = YesNo(rawValue: array[7])!
|
||||
self.marketPriceRangeExtended = YesNo(rawValue: array[8])!
|
||||
self.closingPriceRangeExtended = YesNo(rawValue: array[9])!
|
||||
self.creditAllowable = YesNo(rawValue: array[10])!
|
||||
self.marginalRateClassCode = MarginalRateClass(rawValue: array[11])!
|
||||
self.elwPublished = YesNo(rawValue: array[12])!
|
||||
self.currentStockPrice = array[13]
|
||||
self.previousDayVariableRatio = array[14]
|
||||
self.previousDayVariableRatioSign = array[15]
|
||||
self.previousDayDiffRatio = array[16]
|
||||
self.accumulatedTradingAmount = array[17]
|
||||
self.accumulatedVolume = array[18]
|
||||
self.previousDayDiffVolumeRatio = array[19]
|
||||
self.stockOpenningPrice = array[20]
|
||||
self.highestStockPrice = array[21]
|
||||
self.lowestStockPrice = array[22]
|
||||
self.maximumStockPrice = array[23]
|
||||
self.minimumStockPrice = array[24]
|
||||
self.standardStockPrice = array[25]
|
||||
self.weightedAverageStockPrice = array[26]
|
||||
self.htsForeignRunoutRate = array[27]
|
||||
self.foreignNetBuyingQuantity = array[28]
|
||||
self.programTradeNetBuyingQuantity = array[29]
|
||||
self.pivotSecondDResistancePrice = array[30]
|
||||
self.pivotFirstDResistancePrice = array[31]
|
||||
self.pivotPointValue = array[32]
|
||||
self.pivotFirstDSupportPrice = array[33]
|
||||
self.pivotSecondDSupportPrice = array[34]
|
||||
self.dResistanceValue = array[35]
|
||||
self.dSupportValue = array[36]
|
||||
self.capital = array[37]
|
||||
self.limitWidthPrice = array[38]
|
||||
self.stockFacePrice = array[39]
|
||||
self.stockSubstitudePrice = array[40]
|
||||
self.askingPriceUnit = array[41]
|
||||
self.htsDealQuantityUnit = array[42]
|
||||
self.listedStockCount = array[43]
|
||||
self.htsTotalMarketValue = array[44]
|
||||
self.per = array[45]
|
||||
self.pbr = array[46]
|
||||
self.settlingMonth = array[47]
|
||||
self.volumeTurnoverRate = array[48]
|
||||
self.eps = array[49]
|
||||
self.bps = array[50]
|
||||
self.day250HighestPrice = array[51]
|
||||
self.day250HighestPriceDate = array[52]
|
||||
self.day250HighestPriceDiffRatio = array[53]
|
||||
self.day250LowestPrice = array[54]
|
||||
self.day250LowestPriceDate = array[55]
|
||||
self.day250LowestPriceDiffRatio = array[56]
|
||||
self.annualHighestPrice = array[57]
|
||||
self.annualHighestPriceDiffRatio = array[58]
|
||||
self.annualHighestPriceDate = array[59]
|
||||
self.annualLowestPrice = array[60]
|
||||
self.annualLowestPriceDiffRatio = array[61]
|
||||
self.annualLowestPriceDate = array[62]
|
||||
self.week52HighestPrice = array[63]
|
||||
self.week52HighestPriceDiffRatio = array[64]
|
||||
self.week52HighestPriceDate = array[65]
|
||||
self.week52LowestPrice = array[66]
|
||||
self.week52LowestPriceDiffRatio = array[67]
|
||||
self.week52LowestPriceDate = array[68]
|
||||
self.totalOutstandingloanRate = array[69]
|
||||
self.shortSellingAllowable = YesNo(rawValue: array[70])!
|
||||
self.shortProductCode = array[71]
|
||||
self.facePriceCurrency = array[72]
|
||||
self.capitalCurrency = array[73]
|
||||
self.approachRate = array[74]
|
||||
self.foreignHoldQuantity = array[75]
|
||||
self.viClassCode = array[76]
|
||||
self.viClassCodeForOvertimeMarketPrice = array[77]
|
||||
self.lastShortSellingConclusionQuantity = array[78]
|
||||
self.investmentCareful = YesNo(rawValue: array[79])!
|
||||
self.marketWarningCode = MarketWarning(rawValue: array[80])!
|
||||
self.shortOverheated = YesNo(rawValue: array[81])!
|
||||
}
|
||||
|
||||
public static func symbols() -> [String] {
|
||||
let i = try! CapturePrice(array: Array(repeating: "", count: 82))
|
||||
return Mirror(reflecting: i).children.compactMap { $0.label }
|
||||
}
|
||||
|
||||
public static func localizedSymbols() -> [String: String] {
|
||||
[:]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct MinutePriceResult: Codable {
|
||||
public let resultCode: String
|
||||
public let messageCode: String
|
||||
|
||||
@@ -79,13 +79,19 @@ extension Date {
|
||||
return (hour, minute, second)
|
||||
}
|
||||
|
||||
public func changing(hour: Int, min: Int, sec: Int, timeZone: String = "KST") -> Date? {
|
||||
public func changing(hour: Int?, min: Int?, sec: Int?, timeZone: String = "KST") -> Date? {
|
||||
let sets: Set<Calendar.Component> = [.year, .month, .day, .hour, .minute, .second]
|
||||
var components = Calendar.current.dateComponents(sets, from: self)
|
||||
components.timeZone = TimeZone(abbreviation: timeZone)
|
||||
components.hour = hour
|
||||
components.minute = min
|
||||
components.second = sec
|
||||
if let hour = hour {
|
||||
components.hour = hour
|
||||
}
|
||||
if let min = min {
|
||||
components.minute = min
|
||||
}
|
||||
if let sec = sec {
|
||||
components.second = sec
|
||||
}
|
||||
components.nanosecond = 0
|
||||
return Calendar.current.date(from: components)
|
||||
}
|
||||
@@ -256,6 +262,30 @@ extension Array where Element == String {
|
||||
|
||||
|
||||
extension String {
|
||||
init(firstLineOfFile path: String) throws {
|
||||
guard let filePointer = fopen(path, "r") else {
|
||||
throw GeneralError.cannotReadFile
|
||||
}
|
||||
|
||||
var cLineBytes: UnsafeMutablePointer<CChar>? = nil
|
||||
defer {
|
||||
fclose(filePointer)
|
||||
cLineBytes?.deallocate()
|
||||
}
|
||||
|
||||
var lineCap: Int = 0
|
||||
let bytesRead = getline(&cLineBytes, &lineCap, filePointer)
|
||||
|
||||
guard bytesRead > 0, let cLineBytes = cLineBytes else {
|
||||
throw GeneralError.cannotReadFileLine
|
||||
}
|
||||
guard let str = String(cString: cLineBytes, encoding: .utf8) else {
|
||||
throw GeneralError.cannotReadFileToConvertString
|
||||
}
|
||||
self = str.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
}
|
||||
|
||||
/*
|
||||
init(firstLineOfFile path: String) throws {
|
||||
guard let handle = FileHandle(forReadingAtPath: path) else {
|
||||
throw GeneralError.cannotReadFile
|
||||
@@ -264,13 +294,15 @@ extension String {
|
||||
try? handle.close()
|
||||
}
|
||||
|
||||
var readData = Data()
|
||||
var headerString = ""
|
||||
while (true) {
|
||||
guard let data = try handle.read(upToCount: 512) else {
|
||||
break
|
||||
}
|
||||
guard let part = String(data: data, encoding: .utf8) else {
|
||||
throw GeneralError.cannotReadFile
|
||||
readData.append(data)
|
||||
guard let part = String(data: readData, encoding: .utf8) else {
|
||||
continue
|
||||
}
|
||||
|
||||
if let range = part.range(of: "\n") {
|
||||
@@ -284,6 +316,7 @@ extension String {
|
||||
|
||||
self = headerString
|
||||
}
|
||||
*/
|
||||
|
||||
static func readCsvHeader(fromFile: URL) throws -> [String] {
|
||||
let header = try String(firstLineOfFile: fromFile.path)
|
||||
|
||||
@@ -76,6 +76,7 @@ extension KissConsole {
|
||||
}
|
||||
}
|
||||
}
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 / PreferredCandleTPS)
|
||||
|
||||
candles.sort(by: { $0.stockBusinessDate > $1.stockBusinessDate })
|
||||
guard let recentDay = candles.first?.stockBusinessDate else {
|
||||
@@ -142,6 +143,7 @@ extension KissConsole {
|
||||
break
|
||||
}
|
||||
}
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 / PreferredCandleTPS)
|
||||
|
||||
candles.sort(by: { $0.stockFullDate > $1.stockFullDate })
|
||||
guard let maxTime = candles.first?.stockBusinessDate else {
|
||||
|
||||
@@ -22,6 +22,8 @@ extension KissConsole {
|
||||
|
||||
let fileUrl = KissConsole.investorFileUrl(productNo: productNo, day: recentDay)
|
||||
try output.writeCsv(toFile: fileUrl, localized: localized)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 / PreferredCandleTPS)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
67
KissMeConsole/Sources/KissConsole+Price.swift
Normal file
67
KissMeConsole/Sources/KissConsole+Price.swift
Normal file
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// KissConsole+Price.swift
|
||||
// KissMeConsole
|
||||
//
|
||||
// Created by ened-book-m1 on 2023/06/11.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import KissMe
|
||||
|
||||
|
||||
struct PriceTime {
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension KissConsole {
|
||||
|
||||
func getCurrentPrice(productNo: String, printConsole: Bool = false) async -> Bool {
|
||||
do {
|
||||
let result = try await account!.getCurrentPrice(productNo: productNo)
|
||||
guard let output = result.output else {
|
||||
print("Invalid result's output for \(productNo)")
|
||||
return false
|
||||
}
|
||||
|
||||
if printConsole {
|
||||
printCurrentPrice(output)
|
||||
}
|
||||
|
||||
let price = CapturePrice(captureTime: Date(), price: output)
|
||||
|
||||
let fileUrl = KissConsole.productPriceUrl(productNo: productNo)
|
||||
try [price].writeCsv(toFile: fileUrl, appendable: true, localized: localized)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 / PreferredCandleTPS)
|
||||
} catch {
|
||||
print(error)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private func printCurrentPrice(_ output: CurrentPriceResult.OutputDetail) {
|
||||
let productName = getProduct(shortCode: output.shortProductCode)?.itemName ?? ""
|
||||
print("\t종목명: ", productName)
|
||||
print("\t업종명: ", output.koreanMarketName, output.koreanBusinessTypeName ?? "")
|
||||
print("\t주식 현재가: ", output.currentStockPrice)
|
||||
print("\t전일 대비: ", output.previousDayVariableRatio)
|
||||
print("\t누적 거래 대금: ", output.accumulatedTradingAmount)
|
||||
print("\t누적 거래량: ", output.accumulatedVolume)
|
||||
print("\t전일 대비 거래량 비율: ", output.previousDayDiffVolumeRatio)
|
||||
print("\t주식 시가: ", output.stockOpenningPrice)
|
||||
print("\t주식 최고가: ", output.highestStockPrice)
|
||||
print("\t주식 최저가: ", output.lowestStockPrice)
|
||||
print("\t외국인 순매수 수량: ", output.foreignNetBuyingQuantity)
|
||||
print("\t외국인 보유 수량: ", output.foreignHoldQuantity)
|
||||
print("\t최종 공매도 체결 수량: ", output.lastShortSellingConclusionQuantity)
|
||||
print("\t프로그램매매 순매수 수량: ", output.programTradeNetBuyingQuantity)
|
||||
print("\t자본금: ", output.capital)
|
||||
print("\t상장 주수: ", output.listedStockCount)
|
||||
print("\tHTS 시가총액: ", output.htsTotalMarketValue)
|
||||
print("\tPER: ", output.per)
|
||||
print("\tPBR: ", output.pbr)
|
||||
print("\t주식 단축 종목코드", output.shortProductCode)
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ class KissConsole {
|
||||
|
||||
// 종목 시세
|
||||
case now = "now"
|
||||
case nowAll = "now all"
|
||||
case candle = "candle"
|
||||
case candleAll = "candle all"
|
||||
case candleDay = "candle day"
|
||||
@@ -85,7 +86,7 @@ class KissConsole {
|
||||
return true
|
||||
case .openBag:
|
||||
return true
|
||||
case .now, .candle, .candleAll, .candleDay, .candleWeek:
|
||||
case .now, .nowAll, .candle, .candleAll, .candleDay, .candleWeek:
|
||||
return true
|
||||
case .candleValidate:
|
||||
return false
|
||||
@@ -204,6 +205,7 @@ class KissConsole {
|
||||
case .openBag: await onOpenBag()
|
||||
|
||||
case .now: await onNow(args)
|
||||
case .nowAll: onNowAll(args)
|
||||
case .candle: await onCandle(args)
|
||||
case .candleAll: onCancleAll(args)
|
||||
case .candleDay: onCandleDay(args)
|
||||
@@ -263,7 +265,7 @@ extension KissConsole {
|
||||
return products.compactMap { $0.value.first(where: { $0.isinCode == isin }) }.first
|
||||
}
|
||||
|
||||
private func getProduct(shortCode: String) -> DomesticShop.Product? {
|
||||
func getProduct(shortCode: String) -> DomesticShop.Product? {
|
||||
productsLock.lock()
|
||||
defer {
|
||||
productsLock.unlock()
|
||||
@@ -314,7 +316,7 @@ extension KissConsole {
|
||||
_ = LocalContext.shared.localNamesDic
|
||||
}
|
||||
|
||||
private func setCurrent(productNo: String) {
|
||||
func setCurrent(productNo: String) {
|
||||
productsLock.lock()
|
||||
currentShortCode = productNo
|
||||
productsLock.unlock()
|
||||
@@ -569,53 +571,45 @@ extension KissConsole {
|
||||
print("Invalid productNo")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let result = try await account!.getCurrentPrice(productNo: productNo)
|
||||
guard let output = result.output else {
|
||||
print("Invalid result's output")
|
||||
return
|
||||
}
|
||||
|
||||
let productName = getProduct(shortCode: output.shortProductCode)?.itemName ?? ""
|
||||
print("\t종목명: ", productName)
|
||||
print("\t업종명: ", output.koreanMarketName, output.koreanBusinessTypeName)
|
||||
print("\t주식 현재가: ", output.currentStockPrice)
|
||||
print("\t전일 대비: ", output.previousDayVariableRatio)
|
||||
print("\t누적 거래 대금: ", output.accumulatedTradingAmount)
|
||||
print("\t누적 거래량: ", output.accumulatedVolume)
|
||||
print("\t전일 대비 거래량 비율: ", output.previousDayDiffVolumeRatio)
|
||||
print("\t주식 시가: ", output.stockOpenningPrice)
|
||||
print("\t주식 최고가: ", output.highestStockPrice)
|
||||
print("\t주식 최저가: ", output.lowestStockPrice)
|
||||
print("\t외국인 순매수 수량: ", output.foreignNetBuyingQuantity)
|
||||
print("\t외국인 보유 수량: ", output.foreignHoldQuantity)
|
||||
print("\t최종 공매도 체결 수량: ", output.lastShortSellingConclusionQuantity)
|
||||
print("\t프로그램매매 순매수 수량: ", output.programTradeNetBuyingQuantity)
|
||||
print("\t자본금: ", output.capital)
|
||||
print("\t상장 주수: ", output.listedStockCount)
|
||||
print("\tHTS 시가총액: ", output.htsTotalMarketValue)
|
||||
print("\tPER: ", output.per)
|
||||
print("\tPBR: ", output.pbr)
|
||||
print("\t주식 단축 종목코드", output.shortProductCode)
|
||||
setCurrent(productNo: output.shortProductCode)
|
||||
|
||||
|
||||
let fileUrl = KissConsole.productPriceUrl(productNo: productNo)
|
||||
try [output].writeCsv(toFile: fileUrl, appendable: true, localized: localized)
|
||||
|
||||
} catch {
|
||||
print("\(error)")
|
||||
let success = await getCurrentPrice(productNo: productNo)
|
||||
if success {
|
||||
setCurrent(productNo: productNo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func onNowAll(_ 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
|
||||
}
|
||||
|
||||
let success = await getCurrentPrice(productNo: item.shortCode)
|
||||
#if DEBUG
|
||||
if !success {
|
||||
exit(-100)
|
||||
}
|
||||
#endif
|
||||
print("DONE \(success) \(item.shortCode)")
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
print("FINISHED")
|
||||
}
|
||||
|
||||
|
||||
private func onCancleAll(_ args: [String]) {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
await KissContext.shared.update(candleResuming: false)
|
||||
await KissContext.shared.update(resuming: false)
|
||||
if args.count == 1, args[0] == "resume" {
|
||||
await KissContext.shared.update(candleResuming: true)
|
||||
await KissContext.shared.update(resuming: true)
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
@@ -632,7 +626,7 @@ extension KissConsole {
|
||||
return
|
||||
}
|
||||
|
||||
if await KissContext.shared.isCandleResuming {
|
||||
if await KissContext.shared.isResuming {
|
||||
let curDate = Date()
|
||||
let url = KissConsole.candleFileUrl(productNo: item.shortCode, period: .minute, day: curDate.yyyyMMdd)
|
||||
let r = validateCsv(filePriod: .minute, url: url)
|
||||
@@ -648,6 +642,7 @@ extension KissConsole {
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
print("FINISHED")
|
||||
}
|
||||
|
||||
|
||||
@@ -695,6 +690,7 @@ extension KissConsole {
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
print("FINISHED")
|
||||
}
|
||||
|
||||
|
||||
@@ -742,6 +738,7 @@ extension KissConsole {
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
print("FINISHED")
|
||||
}
|
||||
|
||||
|
||||
@@ -798,6 +795,7 @@ extension KissConsole {
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
print("FINISHED")
|
||||
}
|
||||
|
||||
|
||||
@@ -996,6 +994,7 @@ extension KissConsole {
|
||||
symbols.formUnion(PeriodPriceResult.OutputPrice.symbols())
|
||||
symbols.formUnion(VolumeRankResult.OutputDetail.symbols())
|
||||
symbols.formUnion(CurrentPriceResult.OutputDetail.symbols())
|
||||
symbols.formUnion(CapturePrice.symbols())
|
||||
symbols.formUnion(InvestorVolumeResult.OutputDetail.symbols())
|
||||
let newNames = symbols.sorted(by: { $0 < $1 })
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ actor KissContext {
|
||||
|
||||
private(set) var targetDate: Date = Date(timeIntervalSince1970: 0)
|
||||
private(set) var isHoliday: Bool = false
|
||||
private(set) var isCandleResuming: Bool = false
|
||||
private(set) var isResuming: Bool = false
|
||||
|
||||
private init() { }
|
||||
|
||||
@@ -22,7 +22,7 @@ actor KissContext {
|
||||
self.targetDate = targetDate
|
||||
}
|
||||
|
||||
func update(candleResuming: Bool) {
|
||||
self.isCandleResuming = candleResuming
|
||||
func update(resuming: Bool) {
|
||||
self.isResuming = resuming
|
||||
}
|
||||
}
|
||||
|
||||
2
bin/data
2
bin/data
Submodule bin/data updated: 80c2298377...3aea689c73
@@ -11,6 +11,7 @@
|
||||
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 */; };
|
||||
3435A7F42A35B4D000D604F1 /* KissConsole+Price.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3435A7F32A35B4D000D604F1 /* KissConsole+Price.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 */; };
|
||||
@@ -50,6 +51,7 @@
|
||||
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>"; };
|
||||
3435A7F32A35B4D000D604F1 /* KissConsole+Price.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Price.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>"; };
|
||||
@@ -98,6 +100,7 @@
|
||||
341F5F082A1463A100962D48 /* KissConsole.swift */,
|
||||
34D3680C2A280801005E6756 /* KissConsole+Candle.swift */,
|
||||
349843202A242AC900E85B08 /* KissConsole+CSV.swift */,
|
||||
3435A7F32A35B4D000D604F1 /* KissConsole+Price.swift */,
|
||||
3435A7F12A35A8A900D604F1 /* KissConsole+Investor.swift */,
|
||||
349327F62A20E3E300097063 /* Foundation+Extensions.swift */,
|
||||
);
|
||||
@@ -190,6 +193,7 @@
|
||||
348168692A3420BD00A50BD3 /* LocalContext.swift in Sources */,
|
||||
349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */,
|
||||
348168492A2F92AC00A50BD3 /* KissContext.swift in Sources */,
|
||||
3435A7F42A35B4D000D604F1 /* KissConsole+Price.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user