418 lines
13 KiB
Swift
418 lines
13 KiB
Swift
//
|
|
// test.swift
|
|
// KissMeConsole
|
|
//
|
|
// Created by ened-book-m1 on 2023/05/16.
|
|
//
|
|
|
|
import Foundation
|
|
import KissMe
|
|
|
|
|
|
private func test_array_to_csv() {
|
|
|
|
}
|
|
|
|
|
|
private func test_login_get_volume_ranking() async {
|
|
let isMock = false
|
|
let credential: Credential
|
|
|
|
do {
|
|
/// isMock 이 true 이면, mock-server.json 에서 로딩하여 생성
|
|
/// isMock 이 false 이면, real-server.json 에서 로딩하여 생성
|
|
/// server.json 의 구조
|
|
/// {
|
|
/// "isMock": true,
|
|
/// "accountNo": "12345678-90"
|
|
/// "appKey": "xxxxxxxx",
|
|
/// "appSecret": "yyyyyyyyyyyyyyyyyy"
|
|
/// }
|
|
///
|
|
credential = try KissCredential(isMock: isMock)
|
|
} catch {
|
|
print(error)
|
|
return
|
|
}
|
|
|
|
|
|
let account = KissAccount(credential: credential)
|
|
do {
|
|
if try await account.login() {
|
|
let option = RankingOption(divisionClass: .equity, belongClass: .averageVolume)
|
|
let result = try await account.getVolumeRanking(option: option)
|
|
print("\(result)")
|
|
}
|
|
} catch {
|
|
print(error)
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
private func test_json_result() {
|
|
let str = "{\"rt_cd\":\"1\",\"msg_cd\":\"EGW00205\",\"msg1\":\"credentials_type이 유효하지 않습니다.(Bearer)\"}"
|
|
|
|
do {
|
|
let decoder = JSONDecoder()
|
|
decoder.keyDecodingStrategy = .useDefaultKeys
|
|
|
|
let data = Data(str.utf8)
|
|
let result = try decoder.decode(VolumeRankResult.self, from: data)
|
|
print("\(result)")
|
|
}
|
|
catch {
|
|
print(error)
|
|
}
|
|
}
|
|
|
|
|
|
private func test_xml_result() {
|
|
/*
|
|
let str = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><response><header><resultCode>00</resultCode><resultMsg>NORMAL SERVICE.</resultMsg></header><body><items><item><korSecnNm>이스트아시아홀딩스인베스트먼트리미티드</korSecnNm><shotnIsin>900110</shotnIsin></item><item><korSecnNm>삼천당제약</korSecnNm><shotnIsin>000250</shotnIsin></item><item><korSecnNm>중앙에너비스</korSecnNm><shotnIsin>000440</shotnIsin></item><item><korSecnNm>신라섬유</korSecnNm><shotnIsin>001000</shotnIsin></item><item><korSecnNm>안국약품</korSecnNm><shotnIsin>001540</shotnIsin></item><item><korSecnNm>무림에스피</korSecnNm><shotnIsin>001810</shotnIsin></item><item><korSecnNm>이화공영</korSecnNm><shotnIsin>001840</shotnIsin></item><item><korSecnNm>피에스텍</korSecnNm><shotnIsin>002230</shotnIsin></item><item><korSecnNm>삼일기업공사</korSecnNm><shotnIsin>002290</shotnIsin></item><item><korSecnNm>한일사료</korSecnNm><shotnIsin>005860</shotnIsin></item></items><numOfRows>10</numOfRows><pageNo>1</pageNo><totalCount>1637</totalCount></body></response>"
|
|
|
|
let data = Data(str.utf8)
|
|
|
|
class ResultHelper: NSObject, XMLParserDelegate {
|
|
|
|
struct Result: Codable {
|
|
|
|
}
|
|
|
|
var result: Result?
|
|
|
|
func parserDidStartDocument(_ parser: XMLParser) {
|
|
}
|
|
|
|
func parserDidEndDocument(_ parser: XMLParser) {
|
|
}
|
|
}
|
|
|
|
|
|
do {
|
|
let helper = ResultHelper()
|
|
let parser = XMLParser(data: data)
|
|
parser.shouldProcessNamespaces = true
|
|
parser.delegate = helper
|
|
if parser.parse() {
|
|
print(helper.result)
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
private func fix_first_csv_header_field() {
|
|
guard let enumerator = FileManager.subPathFiles("data") else {
|
|
return
|
|
}
|
|
for case let fileUrl as URL in enumerator {
|
|
guard fileUrl.pathExtension == "csv" else {
|
|
continue
|
|
}
|
|
guard var stringCsv = try? String(contentsOfFile: fileUrl.path) else {
|
|
print("Cannot load \(fileUrl)")
|
|
continue
|
|
}
|
|
|
|
guard let range = stringCsv.range(of: ",\n") else {
|
|
continue
|
|
}
|
|
stringCsv.remove(at: range.lowerBound)
|
|
|
|
do {
|
|
try stringCsv.write(toFile: fileUrl.path, atomically: true, encoding: .utf8)
|
|
print("wrote \(fileUrl.lastPathComponent)")
|
|
} catch {
|
|
print(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private func check_candle_csv() {
|
|
guard let enumerator = FileManager.subPathFiles("data") else {
|
|
return
|
|
}
|
|
|
|
for case let fileUrl as URL in enumerator {
|
|
let r = KissConsole.validateCandleMinute(fileUrl)
|
|
switch r {
|
|
case .ok, .invalidFileName:
|
|
print("OK \(fileUrl)")
|
|
continue
|
|
default:
|
|
assertionFailure()
|
|
print("\(r) at \(fileUrl)")
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
func check_today_candle_exist() {
|
|
guard let enumerator = FileManager.subPathFiles("data") else {
|
|
return
|
|
}
|
|
|
|
var productUrls = [String: URL]()
|
|
var candleUrls = [String: URL]()
|
|
|
|
for case let fileUrl as URL in enumerator {
|
|
|
|
guard true != fileUrl.isDirectoryExists else {
|
|
let productNo = fileUrl.lastPathComponent
|
|
if let _ = Int(productNo) {
|
|
productUrls[productNo] = fileUrl
|
|
}
|
|
continue
|
|
}
|
|
|
|
guard fileUrl.pathExtension == "csv" else {
|
|
continue
|
|
}
|
|
|
|
let fileName = fileUrl.lastPathComponent
|
|
let periodDir = fileUrl.deletingLastPathComponent()
|
|
let period = periodDir.lastPathComponent
|
|
let productNoDir = periodDir.deletingLastPathComponent()
|
|
let productNo = productNoDir.lastPathComponent
|
|
|
|
guard let _ = Int(productNo), period == "min" else {
|
|
continue
|
|
}
|
|
if Date().yyyyMMdd == fileName.parseCandleDate() {
|
|
candleUrls[productNo] = fileUrl
|
|
}
|
|
}
|
|
|
|
for productNo in productUrls.keys {
|
|
guard nil == candleUrls[productNo] else {
|
|
continue
|
|
}
|
|
print("\(productNo) have no today candle!!")
|
|
}
|
|
print("all scan finished")
|
|
}
|
|
|
|
|
|
private func move_candles_to_min_subdir() {
|
|
// Move all of csv into min sub-directory
|
|
// ex) data/000020/candle-20230530.csv --> /data/000020/min/candle-20230530.csv
|
|
//
|
|
guard let enumerator = FileManager.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 productNo = upper.lastPathComponent
|
|
|
|
guard let _ = Int(productNo) else {
|
|
continue
|
|
}
|
|
|
|
let newPath = upper.appending(path: "min")
|
|
let newUrl = newPath.appending(path: fileName)
|
|
|
|
//print("file: \(fileUrl) -> \(newUrl)")
|
|
do {
|
|
try FileManager.default.createDirectory(at: newPath, withIntermediateDirectories: true)
|
|
try FileManager.default.moveItem(at: fileUrl, to: newUrl)
|
|
}
|
|
catch {
|
|
print(error)
|
|
exit(1)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func update_candle_csv_header_field() {
|
|
let symbols = Domestic.Candle.symbols()
|
|
|
|
do {
|
|
let csvUrls = try FileManager.collectCsv(period: .minute, candleDate: nil)
|
|
for csvUrl in csvUrls {
|
|
print("processing \(csvUrl.path)")
|
|
let header = try String.readCsvHeader(fromFile: csvUrl)
|
|
if symbols != header {
|
|
let candles = try [Domestic.Candle].readCsv(fromFile: csvUrl, verifyHeader: false)
|
|
try candles.writeCsv(toFile: csvUrl, localized: false)
|
|
}
|
|
}
|
|
} catch {
|
|
print(error)
|
|
}
|
|
}
|
|
|
|
private func split_investor_csv() {
|
|
guard let enumerator = FileManager.subPathFiles("data") else {
|
|
return
|
|
}
|
|
|
|
var urls = [URL]()
|
|
for case let fileUrl as URL in enumerator {
|
|
guard fileUrl.pathExtension == "csv" else {
|
|
continue
|
|
}
|
|
if fileUrl.lastPathComponent.prefix(9) == "investor-" {
|
|
urls.append(fileUrl)
|
|
}
|
|
}
|
|
|
|
do {
|
|
for url in urls {
|
|
let investorDir = url.deletingLastPathComponent()
|
|
let productNoDir = investorDir.deletingLastPathComponent()
|
|
let productNo = productNoDir.lastPathComponent
|
|
|
|
let output = try [Domestic.Investor].readCsv(fromFile: url, verifyHeader: true)
|
|
|
|
var months = [String: [Domestic.Investor]]()
|
|
for item in output {
|
|
let yyyyMM = String(item.stockBusinessDate.prefix(6))
|
|
if let _ = months[yyyyMM] {
|
|
months[yyyyMM]!.append(item)
|
|
}
|
|
else {
|
|
months[yyyyMM] = [item]
|
|
}
|
|
}
|
|
|
|
for (month, items) in months {
|
|
let descItems = items.sorted(by: { $0.stockBusinessDate > $1.stockBusinessDate })
|
|
guard descItems.count > 0 else {
|
|
continue
|
|
}
|
|
|
|
let fileUrl = KissConsole.investorFileUrl(productNo: productNo, day: month+"01")
|
|
try descItems.mergeCsv(toFile: fileUrl, merging: { this, file in
|
|
var merged = this
|
|
for old in file {
|
|
if nil == this.first(where: { $0.stockBusinessDate == old.stockBusinessDate }) {
|
|
merged.append(old)
|
|
}
|
|
}
|
|
merged.sort(by: { $0.stockBusinessDate > $1.stockBusinessDate })
|
|
return merged
|
|
}, localized: false)
|
|
}
|
|
|
|
try FileManager.default.removeItem(at: url)
|
|
}
|
|
} catch {
|
|
print(error)
|
|
}
|
|
}
|
|
|
|
|
|
private func split_prices_csv() {
|
|
guard let enumerator = FileManager.subPathFiles("data") else {
|
|
return
|
|
}
|
|
|
|
var urls = [URL]()
|
|
for case let fileUrl as URL in enumerator {
|
|
guard fileUrl.pathExtension == "csv" else {
|
|
continue
|
|
}
|
|
if fileUrl.lastPathComponent.prefix(6) == "prices" {
|
|
urls.append(fileUrl)
|
|
}
|
|
}
|
|
|
|
do {
|
|
for url in urls {
|
|
print(url)
|
|
|
|
let priceDir = url.deletingLastPathComponent()
|
|
let productNoDir = priceDir.deletingLastPathComponent()
|
|
let productNo = productNoDir.lastPathComponent
|
|
|
|
let output = try [CapturePrice].readCsv(fromFile: url, verifyHeader: true)
|
|
|
|
var months = [String: [CapturePrice]]()
|
|
for item in output {
|
|
let yyyyMM = String(item.stockBusinessDate.prefix(6))
|
|
if let _ = months[yyyyMM] {
|
|
months[yyyyMM]!.append(item)
|
|
}
|
|
else {
|
|
months[yyyyMM] = [item]
|
|
}
|
|
}
|
|
|
|
for (month, items) in months {
|
|
let descItems = items.sorted(by: { $0.stockDateTime > $1.stockDateTime })
|
|
guard descItems.count > 0 else {
|
|
continue
|
|
}
|
|
|
|
let fileUrl = KissConsole.priceFileUrl(productNo: productNo, day: month+"01")
|
|
try descItems.mergeCsv(toFile: fileUrl, merging: { this, file in
|
|
var merged = file
|
|
for old in file {
|
|
if nil == this.first(where: { $0.stockDateTime == old.stockDateTime }) {
|
|
merged.append(old)
|
|
}
|
|
}
|
|
merged.sort(by: { $0.stockDateTime > $1.stockDateTime })
|
|
return merged
|
|
}, localized: false)
|
|
}
|
|
|
|
try FileManager.default.removeItem(at: url)
|
|
}
|
|
} catch {
|
|
print(error)
|
|
}
|
|
}
|
|
|
|
|
|
private func update_shorts_csv_header_field() {
|
|
let symbols = DomesticExtra.Shorts.symbols()
|
|
|
|
do {
|
|
let csvUrls = try FileManager.collectShorts()
|
|
for csvUrl in csvUrls {
|
|
print("processing \(csvUrl.path)")
|
|
let header = try String.readCsvHeader(fromFile: csvUrl)
|
|
if symbols != header {
|
|
let candles = try [DomesticExtra.Shorts].readCsv(fromFile: csvUrl, verifyHeader: false)
|
|
try candles.writeCsv(toFile: csvUrl, localized: false)
|
|
}
|
|
}
|
|
} catch {
|
|
print(error)
|
|
}
|
|
}
|
|
|
|
private func test_decode_html_chars() {
|
|
print("'알펜시아 입찰 방해' KH 자금총괄 구속영장 기각".htmlDecoded)
|
|
}
|
|
|
|
func test_request_naver_news_list() {
|
|
let semaphore = DispatchSemaphore(value: 0)
|
|
Task {
|
|
let day = "20230723".yyyyMMdd_toDate!
|
|
do {
|
|
let list = try await DomesticNews.collectList(day: day)
|
|
for item in list {
|
|
print("\(item.title) :: \(item.press) :: \(item.url)")
|
|
}
|
|
} catch {
|
|
print(error)
|
|
}
|
|
semaphore.signal()
|
|
}
|
|
semaphore.wait()
|
|
}
|