Add candle day/week commands

This commit is contained in:
2023-06-01 09:36:47 +09:00
parent 62e67fe441
commit fbf0cccfc8
10 changed files with 622 additions and 86 deletions

View File

@@ -0,0 +1,142 @@
//
// KissConsole+Candle.swift
// KissMeConsole
//
// Created by ened-book-m1 on 2023/06/01.
//
import Foundation
import KissMe
/// Limit to request a candle query
let PreferredCandleTPS: UInt64 = 19
/// How many seconds does 1 day have?
let SecondsForOneDay: TimeInterval = 60 * 60 * 24
extension KissConsole {
var last250Days: (startDate: Date, endDate: Date) {
let endDate = Date().changing(hour: 0, min: 0, sec: 0)!
let startDate = endDate.addingTimeInterval(-250 * SecondsForOneDay)
return (startDate, endDate)
}
var last52Weeks: (startDate: Date, endDate: Date) {
// TODO: , ?
let endDate = Date().changing(hour: 0, min: 0, sec: 0)!
let startDate = endDate.addingTimeInterval(-52 * 7 * SecondsForOneDay)
return (startDate, endDate)
}
enum CandleFilePeriod: String {
case minute = "min"
case day = "day"
case weak = "week"
}
func candleFileUrl(productNo: String, period: CandleFilePeriod, day: String) -> URL {
assert(day.count == 6)
let subPath = "data/\(productNo)/\(period.rawValue)"
let subFile = "\(subPath)/candle-\(day).csv"
let fileUrl = URL.currentDirectory().appending(path: subFile)
createSubpath(subPath)
return fileUrl
}
func getCandle(productNo: String, period: PeriodDivision, startDate: Date, endDate: Date) async -> Bool {
do {
guard currentCandleShortCode == nil else {
print("Already candle collecting")
return false
}
currentCandleShortCode = productNo
defer {
currentCandleShortCode = nil
}
var nextTime = Date()
var candles = [Domestic.Candle]()
var count = 0
let result = try await account!.getPeriodPrice(productNo: productNo, startDate: startDate, endDate: endDate, period: .daily)
// let fileUrl = candleFileUrl(productNo: productNo, period: "min", day: minTime)
// KissConsole.writeCandle(candles, fileUrl: fileUrl)
return true
} catch {
print(error)
return false
}
}
func getCandle(productNo: String) async -> Bool {
do {
guard currentCandleShortCode == nil else {
print("Already candle collecting")
return false
}
currentCandleShortCode = productNo
defer {
currentCandleShortCode = nil
}
var nextTime = Date()
var candles = [Domestic.Candle]()
var count = 0
while true {
let more = (count > 0)
count += 1
print("minute price \(productNo) from \(nextTime.yyyyMMdd_HHmmss_forTime) \(more)")
let result = try await account!.getMinutePrice(productNo: productNo, startTodayTime: nextTime, more: more)
if let prices = result.output2, prices.isEmpty == false {
candles.append(contentsOf: prices)
if let last = prices.last {
if nextTime.yyyyMMdd != last.stockBusinessDate {
if let (yyyy, mm, dd) = last.stockBusinessDate.yyyyMMdd {
print("next: \(last.stockBusinessDate)")
nextTime.change(year: yyyy, month: mm, day: dd)
}
}
if let (hh, mm, ss) = last.stockConclusionTime.HHmmss {
print("next: \(last.stockConclusionTime) / \(hh) \(mm) \(ss)")
nextTime.change(hour: hh, min: mm-1, sec: ss)
if hh == 9, mm == 0, ss == 0 {
print("minute price finished")
break
}
}
}
try await Task.sleep(nanoseconds: 1_000_000_000 / PreferredCandleTPS)
}
else {
print("minute price finished")
break
}
}
candles.sort(by: { $0.stockBusinessDate < $1.stockBusinessDate })
guard let minTime = candles.first?.stockBusinessDate else {
print("No price items")
return false
}
let fileUrl = candleFileUrl(productNo: productNo, period: .minute, day: minTime)
KissConsole.writeCandle(candles, fileUrl: fileUrl)
return true
} catch {
print("\(error)")
return false
}
}
}

View File

@@ -11,13 +11,13 @@ import KissMe
class KissConsole {
private var credential: Credential? = nil
private var account: KissAccount? = nil
var account: KissAccount? = nil
private var shop: KissShop? = nil
private var productsLock = NSLock()
private var products = [String: [DomesticShop.Product]]()
private var currentShortCode: String?
private var currentCandleShortCode: String?
var currentCandleShortCode: String?
private enum KissCommand: String {
case quit = "quit"
@@ -42,6 +42,8 @@ class KissConsole {
case now = "now"
case candle = "candle"
case candleAll = "candle all"
case candleDay = "candle day"
case candleWeek = "candle week"
//
case loadShop = "load shop"
@@ -64,7 +66,7 @@ class KissConsole {
return true
case .openBag:
return true
case .now, .candle, .candleAll:
case .now, .candle, .candleAll, .candleDay, .candleWeek:
return true
case .loadShop, .updateShop, .look:
return false
@@ -180,6 +182,8 @@ class KissConsole {
case .now: await onNow(args)
case .candle: await onCandle(args)
case .candleAll: onCancleAll()
case .candleDay: onCandleDay(args)
case .candleWeek: onCandleWeek(args)
case .loadShop: await onLoadShop()
case .updateShop: await onUpdateShop()
@@ -200,7 +204,7 @@ class KissConsole {
extension KissConsole {
private func createSubpath(_ name: String) {
func createSubpath(_ name: String) {
let subPath = URL.currentDirectory().appending(path: name)
try? FileManager.default.createDirectory(at: subPath, withIntermediateDirectories: true)
}
@@ -319,8 +323,17 @@ extension KissConsole {
}
private func onTop(_ arg: [String]) async {
let option = RankingOption(divisionClass: .all, belongClass: .averageVolume)
private func onTop(_ args: [String]) async {
var belongCode = "0"
if args.count == 1, let code = Int(args[0]) {
belongCode = String(code)
}
guard let belongClass = BelongClassCode(rawValue: belongCode) else {
print("Incorrect belong type: \(belongCode)")
return
}
print("TOP: \(belongClass.description)")
let option = RankingOption(divisionClass: .all, belongClass: belongClass)
do {
let rank = try await account!.getVolumeRanking(option: option)
@@ -558,6 +571,82 @@ extension KissConsole {
}
private func onCandleDay(_ args: [String]) {
if args.count == 1, args[0] == "all" {
onCandleDayAll()
return
}
let productNo: String? = (args.isEmpty ? currentShortCode: args[0])
guard let productNo = productNo else {
print("Invalid productNo")
return
}
let (startDate, endDate) = last250Days
let semaphore = DispatchSemaphore(value: 0)
Task {
let success = await getCandle(productNo: productNo, period: .daily, startDate: startDate, endDate: endDate)
print("DONE \(success) \(productNo)")
semaphore.signal()
}
semaphore.wait()
}
private func onCandleDayAll() {
let (startDate, endDate) = last250Days
let all = getAllProducts()
for item in all {
let semaphore = DispatchSemaphore(value: 0)
Task {
let success = await getCandle(productNo: item.shortCode, period: .daily, startDate: startDate, endDate: endDate)
print("DONE \(success) \(item.shortCode)")
semaphore.signal()
}
semaphore.wait()
}
}
private func onCandleWeek(_ args: [String]) {
if args.count == 1, args[0] == "all" {
onCandleDayAll()
return
}
let productNo: String? = (args.isEmpty ? currentShortCode: args[0])
guard let productNo = productNo else {
print("Invalid productNo")
return
}
let (startDate, endDate) = last52Weeks
let semaphore = DispatchSemaphore(value: 0)
Task {
let success = await getCandle(productNo: productNo, period: .weekly, startDate: startDate, endDate: endDate)
print("DONE \(success) \(productNo)")
semaphore.signal()
}
semaphore.wait()
}
private func onCandleWeekAll() {
let (startDate, endDate) = last52Weeks
let all = getAllProducts()
for item in all {
let semaphore = DispatchSemaphore(value: 0)
Task {
let success = await getCandle(productNo: item.shortCode, period: .weekly, startDate: startDate, endDate: endDate)
print("DONE \(success) \(item.shortCode)")
semaphore.signal()
}
semaphore.wait()
}
}
private func onCandle(_ args: [String]) async {
let productNo: String? = (args.isEmpty ? currentShortCode: args[0])
guard let productNo = productNo else {
@@ -566,80 +655,6 @@ extension KissConsole {
}
_ = await getCandle(productNo: productNo)
}
/// Limit to request candle with `preferCandleTPS`
private var preferCandleTPS: UInt64 {
return 19
}
private func getCandle(productNo: String) async -> Bool {
do {
guard currentCandleShortCode == nil else {
print("Already candle collecting")
return false
}
currentCandleShortCode = productNo
defer {
currentCandleShortCode = nil
}
var nextTime = Date()
//nextTime.change(hour: 17, min: 0, sec: 0)
//nextTime.change(year: 2023, month: 5, day: 26)
//nextTime.change(hour: 9, min: 1, sec: 0)
var candles = [Domestic.Candle]()
var count = 0
while true {
let more = (count > 0)
count += 1
print("minute price \(productNo) from \(nextTime.yyyyMMdd_HHmmss_forTime) \(more)")
let result = try await account!.getMinutePrice(productNo: productNo, startTodayTime: nextTime, more: more)
if let prices = result.output2, prices.isEmpty == false {
candles.append(contentsOf: prices)
if let last = prices.last {
if nextTime.yyyyMMdd != last.stockBusinessDate {
if let (yyyy, mm, dd) = last.stockBusinessDate.yyyyMMdd {
print("next: \(last.stockBusinessDate)")
nextTime.change(year: yyyy, month: mm, day: dd)
}
}
if let (hh, mm, ss) = last.stockConclusionTime.HHmmss {
print("next: \(last.stockConclusionTime) / \(hh) \(mm) \(ss)")
nextTime.change(hour: hh, min: mm-1, sec: ss)
if hh == 9, mm == 0, ss == 0 {
print("minute price finished")
break
}
}
}
try await Task.sleep(nanoseconds: 1_000_000_000 / preferCandleTPS)
}
else {
print("minute price finished")
break
}
}
candles.sort(by: { $0.stockBusinessDate < $1.stockBusinessDate })
guard let minTime = candles.first?.stockBusinessDate else {
print("No price items")
return false
}
let subPath = "data/\(productNo)"
let subFile = "\(subPath)/candle-\(minTime).csv"
let fileUrl = URL.currentDirectory().appending(path: subFile)
createSubpath(subPath)
KissConsole.writeCandle(candles, fileUrl: fileUrl)
return true
} catch {
print("\(error)")
return false
}
}
private func onLoadShop() async {

View File

@@ -8,3 +8,5 @@
import Foundation
KissConsole().run()
//move_candles_to_min_subdir()

View File

@@ -159,3 +159,36 @@ private func check_candle_csv() {
}
}
}
func move_candles_to_min_subdir() {
guard let enumerator = subPathFiles("data") else {
return
}
var urls = [URL]()
for case let fileUrl as URL in enumerator {
guard fileUrl.pathExtension == "csv" else {
continue
}
urls.append(fileUrl)
}
for fileUrl in urls {
let fileName = fileUrl.lastPathComponent
let upper = fileUrl.deletingLastPathComponent()
let newPath = upper.appending(path: "min")
let newUrl = newPath.appending(path: fileName)
//print("file: \(fileUrl) -> \(newUrl)")
do {
try FileManager.default.createDirectory(at: upper, withIntermediateDirectories: true)
try FileManager.default.moveItem(at: fileUrl, to: newUrl)
}
catch {
print(error)
exit(1)
}
}
}