Write candle data
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Extensions.swift
|
||||
// Foundation+Extensions.swift
|
||||
// KissMe
|
||||
//
|
||||
// Created by ened-book-m1 on 2023/05/17.
|
||||
@@ -39,6 +39,23 @@ extension Date {
|
||||
}
|
||||
|
||||
|
||||
extension String {
|
||||
public var HHmmss: (Int, Int, Int)? {
|
||||
guard utf8.count == 6 else {
|
||||
return nil
|
||||
}
|
||||
let mmStartIndex = index(startIndex, offsetBy: 2)
|
||||
let mmEndIndex = index(mmStartIndex, offsetBy: 2)
|
||||
guard let hh = Int(String(prefix(2))),
|
||||
let mm = Int(self[mmStartIndex..<mmEndIndex]),
|
||||
let ss = Int(String(suffix(2))) else {
|
||||
return nil
|
||||
}
|
||||
return (hh, mm, ss)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if os(Linux) || os(Windows) || os(FreeBSD)
|
||||
extension URL {
|
||||
public static func currentDirectory() -> URL {
|
||||
@@ -134,7 +134,7 @@ extension KissAccount {
|
||||
|
||||
/// 현재 종목 분봉을 가져오기
|
||||
///
|
||||
public func getMinutePrice(productNo: String, startTodayTime: Date) async throws -> MinutePriceResult {
|
||||
public func getMinutePrice(productNo: String, startTodayTime: Date, more: Bool = false) async throws -> MinutePriceResult {
|
||||
return try await withUnsafeThrowingContinuation { continuation in
|
||||
|
||||
guard let accessToken = accessToken else {
|
||||
@@ -142,7 +142,7 @@ extension KissAccount {
|
||||
return
|
||||
}
|
||||
|
||||
let request = Domestic.StockTodayMinutePriceRequest(credential: credential, accessToken: accessToken, productNo: productNo, startTodayTime: startTodayTime.HHmmss, isNext: false)
|
||||
let request = Domestic.StockTodayMinutePriceRequest(credential: credential, accessToken: accessToken, productNo: productNo, startTodayTime: startTodayTime.HHmmss, isNext: more)
|
||||
request.query { result in
|
||||
switch result {
|
||||
case .success(let result):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// ConsoleExtensions.swift
|
||||
// Foundation+Extensions.swift
|
||||
// KissMeConsole
|
||||
//
|
||||
// Created by ened-book-m1 on 2023/05/26.
|
||||
@@ -50,4 +50,10 @@ extension Date {
|
||||
components.second = sec
|
||||
return Calendar.current.date(from: components)
|
||||
}
|
||||
|
||||
public mutating func change(hour: Int, min: Int, sec: Int, timeZone: String = "KST") {
|
||||
if let newDate = changing(hour: hour, min: min, sec: sec, timeZone: timeZone) {
|
||||
self = newDate
|
||||
}
|
||||
}
|
||||
}
|
||||
60
KissMeConsole/Sources/KissConsole+CSV.swift
Normal file
60
KissMeConsole/Sources/KissConsole+CSV.swift
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// KissConsole+CSV.swift
|
||||
// KissMeConsole
|
||||
//
|
||||
// Created by ened-book-m1 on 2023/05/29.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import KissMe
|
||||
|
||||
|
||||
extension KissConsole {
|
||||
|
||||
func writeShop(_ shopItems: [DomesticShop.Product], fileUrl: URL) {
|
||||
var stringCsv: String = ""
|
||||
let header = "baseDate,shortCode,isinCode,marketCategory,itemName,corporationNo,\n"
|
||||
stringCsv.append(header)
|
||||
for item in shopItems {
|
||||
let stringItem = [item.baseDate,
|
||||
item.shortCode,
|
||||
item.isinCode,
|
||||
item.marketCategory,
|
||||
item.itemName.trimmingCharacters(in: .whitespaces),
|
||||
item.corporationNo].joined(separator: ",")
|
||||
stringCsv.append(stringItem)
|
||||
stringCsv.append("\n")
|
||||
}
|
||||
|
||||
do {
|
||||
try stringCsv.write(toFile: fileUrl.path, atomically: true, encoding: .utf8)
|
||||
print("wrote \(fileUrl.lastPathComponent) with \(shopItems.count)")
|
||||
} catch {
|
||||
print("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func writeCandle(_ prices: [MinutePriceResult.OutputPrice], fileUrl: URL) {
|
||||
var stringCsv: String = "stockBusinessDate,stockConclusionTime,accumulatedTradingAmount,currentStockPrice,secondStockPrice,highestStockPrice,lowestStockPrice,conclusionVolume"
|
||||
let header = ""
|
||||
stringCsv.append(header)
|
||||
for item in prices {
|
||||
let stringItem = [item.stockBusinessDate,
|
||||
item.stockConclusionTime,
|
||||
item.accumulatedTradingAmount,
|
||||
item.currentStockPrice,
|
||||
item.secondStockPrice,
|
||||
item.highestStockPrice,
|
||||
item.lowestStockPrice,
|
||||
item.conclusionVolume].joined(separator: ",")
|
||||
stringCsv.append(stringItem)
|
||||
stringCsv.append("\n")
|
||||
}
|
||||
do {
|
||||
try stringCsv.write(toFile: fileUrl.path, atomically: true, encoding: .utf8)
|
||||
print("wrote \(fileUrl.lastPathComponent) with \(prices.count)")
|
||||
} catch {
|
||||
print("\(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,10 +164,6 @@ extension KissConsole {
|
||||
try? FileManager.default.createDirectory(at: subPath, withIntermediateDirectories: true)
|
||||
}
|
||||
|
||||
private var shopProducts: URL {
|
||||
URL.currentDirectory().appending(path: "data/shop-products.csv")
|
||||
}
|
||||
|
||||
private func setProducts(_ products: [String: [DomesticShop.Product]]) {
|
||||
productsLock.lock()
|
||||
self.products = products
|
||||
@@ -201,9 +197,13 @@ extension KissConsole {
|
||||
return products.compactMap { $0.value.first(where: { $0.shortCode == shortCode }) }.first
|
||||
}
|
||||
|
||||
private var shopProductsUrl: URL {
|
||||
URL.currentDirectory().appending(path: "data/shop-products.csv")
|
||||
}
|
||||
|
||||
private func loadShop(_ profile: Bool = false) {
|
||||
let appTime1 = Date.appTime
|
||||
guard let stringCsv = try? String(contentsOfFile: shopProducts.path) else {
|
||||
guard let stringCsv = try? String(contentsOfFile: shopProductsUrl.path) else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -267,6 +267,7 @@ extension KissConsole {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension KissConsole {
|
||||
|
||||
private func onLogin(isMock: Bool) async {
|
||||
@@ -292,6 +293,7 @@ extension KissConsole {
|
||||
_ = try await account?.logout()
|
||||
credential = nil
|
||||
account = nil
|
||||
print("Success")
|
||||
} catch {
|
||||
print("\(error)")
|
||||
}
|
||||
@@ -433,16 +435,40 @@ extension KissConsole {
|
||||
}
|
||||
|
||||
do {
|
||||
let lastWeek = Date().addingTimeInterval(-60 * 60 * 24 * 7)
|
||||
guard let startTime = lastWeek.changing(hour: 9, min: 0, sec: 0) else {
|
||||
print("Invalid start time")
|
||||
var nextTime = Date()
|
||||
nextTime.change(hour: 17, min: 0, sec: 0)
|
||||
|
||||
var candles = [MinutePriceResult.OutputPrice]()
|
||||
var count = 0
|
||||
|
||||
while count < 3 {
|
||||
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 {
|
||||
candles.append(contentsOf: prices)
|
||||
if let last = prices.last {
|
||||
if let (hh, mm, ss) = last.stockConclusionTime.HHmmss {
|
||||
print("next: \(last.stockConclusionTime) / \(hh) \(mm) \(ss)")
|
||||
nextTime.change(hour: hh, min: mm-1, sec: ss)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
candles.sort(by: { $0.stockBusinessDate < $1.stockBusinessDate })
|
||||
guard let minTime = candles.first?.stockBusinessDate else {
|
||||
print("No price items")
|
||||
return
|
||||
}
|
||||
let result = try await account!.getMinutePrice(productNo: productNo, startTodayTime: startTime)
|
||||
|
||||
// TODO: work on result
|
||||
|
||||
print(result)
|
||||
let subPath = "data/\(productNo)"
|
||||
let subFile = "\(subPath)/candle-\(minTime).csv"
|
||||
let fileUrl = URL.currentDirectory().appending(path: subFile)
|
||||
createSubpath(subPath)
|
||||
writeCandle(candles, fileUrl: fileUrl)
|
||||
} catch {
|
||||
print("\(error)")
|
||||
}
|
||||
@@ -478,21 +504,7 @@ extension KissConsole {
|
||||
}
|
||||
}
|
||||
|
||||
var stringCsv: String = ""
|
||||
let header = "baseDate,shortCode,isinCode,marketCategory,itemName,corporationNo,\n"
|
||||
stringCsv.append(header)
|
||||
for item in shopItems {
|
||||
let stringItem = [item.baseDate, item.shortCode, item.isinCode, item.marketCategory, item.itemName.trimmingCharacters(in: .whitespaces), item.corporationNo].joined(separator: ",")
|
||||
stringCsv.append(stringItem)
|
||||
stringCsv.append("\n")
|
||||
}
|
||||
|
||||
do {
|
||||
try stringCsv.write(toFile: shopProducts.path, atomically: true, encoding: .utf8)
|
||||
print("wrote \(shopProducts.lastPathComponent) with \(shopItems.count)")
|
||||
} catch {
|
||||
print("\(error)")
|
||||
}
|
||||
writeShop(shopItems, fileUrl: shopProductsUrl)
|
||||
}
|
||||
|
||||
private func getAllProduct(baseDate: Date) async -> [DomesticShop.Product] {
|
||||
@@ -562,6 +574,7 @@ extension KissConsole {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func onLove(_ args: [String]) async {
|
||||
guard let account = account else { return }
|
||||
guard args.count > 0 else {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
341F5EFF2A10955D00962D48 /* OrderRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EFE2A10955D00962D48 /* OrderRequest.swift */; };
|
||||
341F5F012A11155100962D48 /* DomesticStockSearchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F002A11155100962D48 /* DomesticStockSearchResult.swift */; };
|
||||
341F5F032A11A2BC00962D48 /* Credential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F022A11A2BC00962D48 /* Credential.swift */; };
|
||||
341F5F072A14634F00962D48 /* KissExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F062A14634F00962D48 /* KissExtensions.swift */; };
|
||||
341F5F072A14634F00962D48 /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F062A14634F00962D48 /* Foundation+Extensions.swift */; };
|
||||
341F5F0B2A15115400962D48 /* KissShop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0A2A15115400962D48 /* KissShop.swift */; };
|
||||
341F5F0D2A15222E00962D48 /* AuthRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0C2A15222E00962D48 /* AuthRequest.swift */; };
|
||||
341F5F0F2A15223A00962D48 /* SeibroRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0E2A15223A00962D48 /* SeibroRequest.swift */; };
|
||||
@@ -68,7 +68,7 @@
|
||||
341F5EFE2A10955D00962D48 /* OrderRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderRequest.swift; sourceTree = "<group>"; };
|
||||
341F5F002A11155100962D48 /* DomesticStockSearchResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticStockSearchResult.swift; sourceTree = "<group>"; };
|
||||
341F5F022A11A2BC00962D48 /* Credential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credential.swift; sourceTree = "<group>"; };
|
||||
341F5F062A14634F00962D48 /* KissExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissExtensions.swift; sourceTree = "<group>"; };
|
||||
341F5F062A14634F00962D48 /* Foundation+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Foundation+Extensions.swift"; sourceTree = "<group>"; };
|
||||
341F5F0A2A15115400962D48 /* KissShop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissShop.swift; sourceTree = "<group>"; };
|
||||
341F5F0C2A15222E00962D48 /* AuthRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRequest.swift; sourceTree = "<group>"; };
|
||||
341F5F0E2A15223A00962D48 /* SeibroRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeibroRequest.swift; sourceTree = "<group>"; };
|
||||
@@ -183,7 +183,7 @@
|
||||
341F5F0E2A15223A00962D48 /* SeibroRequest.swift */,
|
||||
341F5F102A1685E700962D48 /* ShopRequest.swift */,
|
||||
341F5F022A11A2BC00962D48 /* Credential.swift */,
|
||||
341F5F062A14634F00962D48 /* KissExtensions.swift */,
|
||||
341F5F062A14634F00962D48 /* Foundation+Extensions.swift */,
|
||||
);
|
||||
path = Common;
|
||||
sourceTree = "<group>";
|
||||
@@ -307,7 +307,7 @@
|
||||
files = (
|
||||
341F5EFB2A10909D00962D48 /* LoginResult.swift in Sources */,
|
||||
341F5F0B2A15115400962D48 /* KissShop.swift in Sources */,
|
||||
341F5F072A14634F00962D48 /* KissExtensions.swift in Sources */,
|
||||
341F5F072A14634F00962D48 /* Foundation+Extensions.swift in Sources */,
|
||||
341F5F032A11A2BC00962D48 /* Credential.swift in Sources */,
|
||||
341F5EB02A0A80EC00962D48 /* KissMe.docc in Sources */,
|
||||
341F5EFD2A10931B00962D48 /* DomesticStockSearch.swift in Sources */,
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
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 */; };
|
||||
349327F72A20E3E300097063 /* ConsoleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349327F62A20E3E300097063 /* ConsoleExtensions.swift */; };
|
||||
349327F72A20E3E300097063 /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349327F62A20E3E300097063 /* Foundation+Extensions.swift */; };
|
||||
349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349843202A242AC900E85B08 /* KissConsole+CSV.swift */; };
|
||||
34EE76862A1C391B009761D2 /* KissMe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 341F5EDB2A0A8C4600962D48 /* KissMe.framework */; };
|
||||
34EE76872A1C391B009761D2 /* KissMe.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 341F5EDB2A0A8C4600962D48 /* KissMe.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
/* End PBXBuildFile section */
|
||||
@@ -44,7 +45,9 @@
|
||||
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>"; };
|
||||
349327F62A20E3E300097063 /* ConsoleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleExtensions.swift; sourceTree = "<group>"; };
|
||||
349327F62A20E3E300097063 /* Foundation+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Foundation+Extensions.swift"; sourceTree = "<group>"; };
|
||||
3498431E2A24287600E85B08 /* KissMeConsoleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KissMeConsoleTests.swift; sourceTree = "<group>"; };
|
||||
349843202A242AC900E85B08 /* KissConsole+CSV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+CSV.swift"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -63,6 +66,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
341F5ED22A0A8B9000962D48 /* KissMeConsole */,
|
||||
3498431D2A24284600E85B08 /* KissMeConsoleTests */,
|
||||
341F5ED12A0A8B9000962D48 /* Products */,
|
||||
341F5EDA2A0A8C4600962D48 /* Frameworks */,
|
||||
);
|
||||
@@ -82,7 +86,8 @@
|
||||
341F5ED32A0A8B9000962D48 /* main.swift */,
|
||||
341F5F042A13B82F00962D48 /* test.swift */,
|
||||
341F5F082A1463A100962D48 /* KissConsole.swift */,
|
||||
349327F62A20E3E300097063 /* ConsoleExtensions.swift */,
|
||||
349843202A242AC900E85B08 /* KissConsole+CSV.swift */,
|
||||
349327F62A20E3E300097063 /* Foundation+Extensions.swift */,
|
||||
);
|
||||
name = KissMeConsole;
|
||||
path = ../../KissMeConsole/Sources;
|
||||
@@ -96,6 +101,15 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3498431D2A24284600E85B08 /* KissMeConsoleTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3498431E2A24287600E85B08 /* KissMeConsoleTests.swift */,
|
||||
);
|
||||
name = KissMeConsoleTests;
|
||||
path = ../../KissMeConsole/Tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -156,9 +170,10 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
341F5ED42A0A8B9000962D48 /* main.swift in Sources */,
|
||||
349327F72A20E3E300097063 /* ConsoleExtensions.swift in Sources */,
|
||||
349327F72A20E3E300097063 /* Foundation+Extensions.swift in Sources */,
|
||||
341F5F092A1463A100962D48 /* KissConsole.swift in Sources */,
|
||||
341F5F052A13B82F00962D48 /* test.swift in Sources */,
|
||||
349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user