Files
KissMe/KissMeConsole/Sources/KissConsole+CSV.swift

181 lines
5.6 KiB
Swift

//
// KissConsole+CSV.swift
// KissMeConsole
//
// Created by ened-book-m1 on 2023/05/29.
//
import Foundation
import KissMe
extension KissConsole {
static var shopProductsUrl: URL {
URL.currentDirectory().appending(path: "data/shop-products.csv")
}
static func topProductsUrl(_ belong: BelongClassCode, date: Date) -> URL {
let subPath = "data/top30/\(date.yyyyMMdd)"
let subFile = "\(subPath)/top30-\(belong.fileBelong)-\(date.yyyyMMdd)-\(date.HHmmss).csv"
let fileUrl = URL.currentDirectory().appending(path: subFile)
createSubpath(subPath)
return fileUrl
}
static var accountStocksUrl: URL {
URL.currentDirectory().appending(path: "data/account-stocks.csv")
}
static var accountAmountUrl: URL {
URL.currentDirectory().appending(path: "data/account-amount.csv")
}
static var localNamesUrl: URL {
URL.currentDirectory().appending(path: "data/localized-names.csv")
}
static var holidayUrl: URL {
URL.currentDirectory().appending(path: "data/holiday.csv")
}
static func productPriceUrl(productNo: String) -> URL {
let subPath = "data/\(productNo)/price"
let subFile = "\(subPath)/prices.csv"
let fileUrl = URL.currentDirectory().appending(path: subFile)
createSubpath(subPath)
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 shortsFileUrl(productNo: String, day: String) -> URL {
let subPath = "data/\(productNo)/shorts"
let subFile = "\(subPath)/shorts-\(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)"
let subFile: String
switch period {
case .minute:
subFile = "\(subPath)/candle-\(day).csv"
case .day:
subFile = "\(subPath)/candle-day-250.csv"
case .weak:
subFile = "\(subPath)/candle-week-42.csv"
}
let fileUrl = URL.currentDirectory().appending(path: subFile)
createSubpath(subPath)
return fileUrl
}
}
extension KissConsole {
enum CandleValidation: CustomStringConvertible {
case ok
case invalidFileName
case cannotRead
case invalidCsvHeader
case invalidBusinessDate
case invalidConclusionTime
case unimplemetedFunction
var description: String {
switch self {
case .ok: return "ok"
case .invalidFileName: return "invalidFileName"
case .cannotRead: return "cannotRead"
case .invalidCsvHeader: return "invalidCsvHeader"
case .invalidBusinessDate: return "invalidBusinessDate"
case .invalidConclusionTime: return "invalidConclusionTime"
case .unimplemetedFunction: return "unimplemetedFunction"
}
}
}
static func validateCandleDay(_ fileUrl: URL) -> CandleValidation {
// TODO: validateCandleDay
return .unimplemetedFunction
}
static func validateCandleWeek(_ fileUrl: URL) -> CandleValidation {
// TODO: validateCandleWeek
return .unimplemetedFunction
}
static func validateCandleMinute(_ fileUrl: URL) -> CandleValidation {
let fileNameFrag = fileUrl.lastPathComponent.split(separator: ".")
guard fileNameFrag.count == 2 else {
return .invalidFileName
}
let candlePrefix = "candle-"
guard fileNameFrag[0].prefix(candlePrefix.count) == candlePrefix, fileNameFrag[1] == "csv" else {
return .invalidFileName
}
let fileDateFrag = String(fileNameFrag[0].suffix(fileNameFrag[0].count - candlePrefix.count))
guard let stringCsv = try? String(contentsOfFile: fileUrl.path) else {
return .cannotRead
}
var candles = [Domestic.Candle]()
let rows = stringCsv.split(separator: "\n")
for (i, row) in rows.enumerated() {
let array = row.split(separator: ",", omittingEmptySubsequences: false).map { String($0) }
if i == 0 {
if array.count != 8 {
return .invalidCsvHeader
}
continue
}
let candle = try! Domestic.Candle(array: array, source: row)
candles.append(candle)
}
var curHH = 9, curMM = 0
for candle in candles.reversed() {
if candle.stockBusinessDate != fileDateFrag {
return .invalidBusinessDate
}
guard let (hh, mm, _) = candle.stockConclusionTime.HHmmss else {
return .invalidConclusionTime
}
guard hh == curHH, mm == curMM else {
return .invalidConclusionTime
}
// Finished to check
if hh == 18, mm == 0 {
break
}
curMM += 1
if curMM >= 60 {
curMM = 0
curHH += 1
}
}
return .ok
}
}