Implement "index portfolio", "load index", "update index" command

This commit is contained in:
2023-06-29 11:01:34 +09:00
parent a289e1852a
commit 3a9fcd4a5e
11 changed files with 584 additions and 98 deletions

View File

@@ -78,6 +78,13 @@ extension Date {
return (hour, minute, second)
}
public var isBeforeMarketOpenning: Bool {
guard let (hour, _, _) = HHmmss_split else {
return true
}
return hour >= 0 && hour <= 9
}
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)
@@ -163,6 +170,14 @@ extension String {
return (hh, mm, ss)
}
public var krxDate: Date? {
// ex) 2023.06.28 PM 12:06:57
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "KST")
dateFormatter.dateFormat = "yyyy.MM.dd a hh:mm:ss"
return dateFormatter.date(from: self)
}
public var hasComma: Bool {
return nil != rangeOfCharacter(from: commaCharSet)
}

View File

@@ -0,0 +1,74 @@
//
// IndexContext.swift
// KissMe
//
// Created by ened-book-m1 on 2023/06/28.
//
import Foundation
open class IndexContext {
private var indicesLock = NSLock()
///
private var indices = [String: [DomesticExtra.IndexProduct]]()
public var indicesCount: Int {
indicesLock.lock()
defer {
indicesLock.unlock()
}
return indices.count
}
public init() {
}
}
extension IndexContext {
public func loadIndex(url: URL, loggable: Bool = false) {
do {
let products = try [DomesticExtra.IndexProduct].readCsv(fromFile: url)
var indices = [String: [DomesticExtra.IndexProduct]]()
for product in products {
if let _ = indices[product.productName] {
indices[product.productName]!.append(product)
}
else {
indices[product.productName] = [product]
}
}
setIndices(indices)
if loggable {
let totalCount = indices.reduce(0, { $0 + $1.value.count })
print("load indices \(totalCount) with \(products.count) key")
}
} catch {
print(error)
}
}
private func setIndices(_ indices: [String: [DomesticExtra.IndexProduct]]) {
indicesLock.lock()
self.indices = indices
indicesLock.unlock()
}
public func getAllIndices() -> [DomesticExtra.IndexProduct] {
indicesLock.lock()
defer {
indicesLock.unlock()
}
var all = [DomesticExtra.IndexProduct]()
for index in indices.values {
all.append(contentsOf: index)
}
all.sort(by: { $0.indexFullCode < $1.indexFullCode })
return all
}
}

View File

@@ -18,6 +18,7 @@ public enum YesNo: String, Codable {
///
public enum MarginalRateClass: String, Codable {
case undefined2 = ""
case undefined = " "
/// 20%, 30%, 40%

View File

@@ -15,6 +15,15 @@ extension DomesticExtra {
case kospi = "02"
case kosdaq = "03"
case theme = "04"
public var name: String {
switch self {
case .krx: return "krx"
case .kospi: return "kospi"
case .kosdaq: return "kosdaq"
case .theme: return "theme"
}
}
}
public enum ShareType: Int {
@@ -33,7 +42,7 @@ extension DomesticExtra {
/// - -
///
public struct IndexPriceRequest: KrxRequest {
public typealias KResult = String
public typealias KResult = IndexPriceResult
public var domain: String {
"http://data.krx.co.kr"
@@ -41,7 +50,7 @@ extension DomesticExtra {
public var url: String {
"/comm/bldAttendant/getJsonData.cmd"
}
public var method: Method { .post }
public var method: Method { .get }
public var header: [String : String?] {
[:]
@@ -49,67 +58,17 @@ extension DomesticExtra {
public var body: [String: Any] {
return [
"idxIndMidclssCd": indexType.rawValue,
"strtDd": range.startDate.yyyyMMdd,
"endDd": range.endDate.yyyyMMdd,
"trdDd": date.yyyyMMdd,
"share": shareType.rawValue,
"money": moneyType.rawValue,
"csvxls_isNo": false,
"bld": "dbms/MDC/STAT/standard/MDCSTAT00201"
"bld": "dbms/MDC/STAT/standard/MDCSTAT00101"
]
}
public var result: KResult? = nil
struct DataRange {
let startDate: Date // yyyyMMdd
let endDate: Date // yyyyMMdd
}
let range: DataRange
let indexType: IndexType
let shareType: ShareType
let moneyType: MoneyType
init(range: DataRange, indexType: IndexType) {
self.range = range
self.indexType = indexType
self.shareType = .unitOne
self.moneyType = .moneyWon
}
}
/// - -
///
public struct IndexPortfolioRequest: KrxRequest {
public typealias KResult = String
public var domain: String {
"http://data.krx.co.kr"
}
public var url: String {
"/comm/bldAttendant/getJsonData.cmd"
}
public var method: Method { .post }
public var header: [String : String?] {
[:]
}
public var body: [String: Any] {
return [
"idxIndMidclssCd": indexType.rawValue,
"trdDb": date.yyyyMMdd,
"share": shareType.rawValue,
"money": moneyType.rawValue,
"csvxls_isNo": false,
"bld": "dbms/MDC/STAT/standard/MDCSTAT00601"
]
}
public var result: KResult? = nil
let indexType: IndexType
let date: Date
let indexType: IndexType
let shareType: ShareType
let moneyType: MoneyType
@@ -120,6 +79,306 @@ extension DomesticExtra {
self.moneyType = .moneyWon
}
}
/// - -
///
public struct IndexPortfolioRequest: KrxRequest {
public typealias KResult = IndexPortfolioResult
public var domain: String {
"http://data.krx.co.kr"
}
public var url: String {
"/comm/bldAttendant/getJsonData.cmd"
}
public var method: Method { .get }
public var header: [String : String?] {
[:]
}
public var body: [String: Any] {
return [
"indIdx": indexId,
"indIdx2": indexId2,
"trdDd": date.yyyyMMdd,
"money": moneyType.rawValue,
"bld": "dbms/MDC/STAT/standard/MDCSTAT00601"
]
}
public var result: KResult? = nil
let indexId: String
let indexId2: String
let date: Date
let moneyType: MoneyType
init(indexId: String, indexId2: String, date: Date) {
self.indexId = indexId
self.indexId2 = indexId2
self.date = date
self.moneyType = .moneyWon
}
}
///
///
public struct AllIndicesRequest: KrxRequest {
public typealias KResult = AllIndicesResult
public var domain: String {
"http://data.krx.co.kr"
}
public var url: String {
"/comm/bldAttendant/getJsonData.cmd"
}
public var method: Method { .get }
public var header: [String : String?] {
[:]
}
public var body: [String: Any] {
return [
"mktsel": market.rawValue,
"bld": "dbms/comm/finder/finder_equidx"
]
}
public var result: KResult? = nil
enum MarketSelection: String {
case all = "1"
case krx = "2"
case kospi = "3"
case kosdaq = "4"
case theme = "T"
}
let market: MarketSelection = .all
}
}
extension DomesticExtra {
public struct IndexPriceResult: Codable {
///
public let output: [Output]
///
public let currentDatetime: String /// 2023.06.28 PM 12:06:57
public var currentDate: Date {
currentDatetime.krxDate!
}
private enum CodingKeys: String, CodingKey, CaseIterable {
case output
case currentDatetime = "CURRENT_DATETIME"
}
public struct Output: Codable, PropertyIterable, ArrayDecodable {
///
public let indexName: String
///
public let indexClosingPrice: String
///
public let fluctuationTypeCode: String
///
public let previousDayVariableRatio: String
///
public let fluctuationRate: String
///
public let indexOpenningPrice: String
///
public let highestIndexPrice: String
///
public let lowestIndexPrice: String
///
public let accumulatedVolume: String
///
public let accumulatedTradingAmount: String
///
public let marketCapital: String
private enum CodingKeys: String, CodingKey, CaseIterable {
case indexName = "IDX_NM"
case indexClosingPrice = "CLSPRC_IDX"
case fluctuationTypeCode = "FLUC_TP_CD"
case previousDayVariableRatio = "CMPPREVDD_IDX"
case fluctuationRate = "FLUC_RT"
case indexOpenningPrice = "OPNPRC_IDX"
case highestIndexPrice = "HGPRC_IDX"
case lowestIndexPrice = "LWPRC_IDX"
case accumulatedVolume = "ACC_TRDVOL"
case accumulatedTradingAmount = "ACC_TRDVAL"
case marketCapital = "MKTCAP"
}
public init(array: [String], source: String.SubSequence) throws {
guard array.count == 11 else {
throw GeneralError.incorrectArrayItems(String(source), array.count, 11)
}
self.indexName = array[0]
self.indexClosingPrice = array[1]
self.fluctuationTypeCode = array[2]
self.previousDayVariableRatio = array[3]
self.fluctuationRate = array[4]
self.indexOpenningPrice = array[5]
self.highestIndexPrice = array[6]
self.lowestIndexPrice = array[7]
self.accumulatedVolume = array[8]
self.accumulatedTradingAmount = array[9]
self.marketCapital = array[10]
}
public static func symbols() -> [String] {
let i = try! Output(array: Array(repeating: "", count: 11), source: #function)
return Mirror(reflecting: i).children.compactMap { $0.label }
}
public static func localizedSymbols() -> [String: String] {
[:]
}
}
}
public struct IndexPortfolioResult: Codable {
public let output: [Output]
///
public let currentDatetime: String
public var currentDate: Date {
currentDatetime.krxDate!
}
private enum CodingKeys: String, CodingKey, CaseIterable {
case output = "output"
case currentDatetime = "CURRENT_DATETIME"
}
public struct Output: Codable, PropertyIterable, ArrayDecodable {
///
public let shortProductCode: String
///
public let productName: String
///
public let indexClosingPrice: String
///
public let fluctuationTypeCode: String
///
public let comparisionPrice: String
///
public let fluctuationRate: String
///
public let marketCapital: String
private enum CodingKeys: String, CodingKey, CaseIterable {
case shortProductCode = "ISU_SRT_CD"
case productName = "ISU_ABBRV"
case indexClosingPrice = "TDD_CLSPRC"
case fluctuationTypeCode = "FLUC_TP_CD"
case comparisionPrice = "STR_CMP_PRC"
case fluctuationRate = "FLUC_RT"
case marketCapital = "MKTCAP"
}
public init(array: [String], source: String.SubSequence) throws {
guard array.count == 7 else {
throw GeneralError.incorrectArrayItems(String(source), array.count, 7)
}
self.shortProductCode = array[0]
self.productName = array[1]
self.indexClosingPrice = array[2]
self.fluctuationTypeCode = array[3]
self.comparisionPrice = array[4]
self.fluctuationRate = array[5]
self.marketCapital = array[6]
}
public static func symbols() -> [String] {
let i = try! Output(array: Array(repeating: "", count: 7), source: #function)
return Mirror(reflecting: i).children.compactMap { $0.label }
}
public static func localizedSymbols() -> [String: String] {
[:]
}
}
}
public struct AllIndicesResult: Codable {
public let block: [Block]
///
public let currentDatetime: String
public var currentDate: Date {
currentDatetime.krxDate!
}
private enum CodingKeys: String, CodingKey, CaseIterable {
case block = "block1"
case currentDatetime = "CURRENT_DATETIME"
}
public struct Block: Codable, PropertyIterable, ArrayDecodable {
/// 1
public let index1Code: String
/// 2
public let index2Code: String
///
public let productName: String
///
public let indexMarketCode: KrxMarketCode
///
public let marketName: String
public var indexFullCode: String {
index1Code + index2Code
}
public var indexType: DomesticExtra.IndexType {
switch marketName {
case "KRX": return .krx
case "STK": return .kospi
case "KOSDAQ": return .kosdaq
case "테마": return .theme
default: return .krx
}
}
private enum CodingKeys: String, CodingKey, CaseIterable {
case index1Code = "full_code"
case index2Code = "short_code"
case productName = "codeName"
case indexMarketCode = "marketCode"
case marketName
}
public init(array: [String], source: String.SubSequence) throws {
guard array.count == 5 else {
throw GeneralError.incorrectArrayItems(String(source), array.count, 5)
}
self.index1Code = array[0]
self.index2Code = array[1]
self.productName = array[2]
self.indexMarketCode = KrxMarketCode(rawValue: array[3])!
self.marketName = array[4]
}
public static func symbols() -> [String] {
let i = try! Block(array: Array(repeating: "", count: 5), source: #function)
return Mirror(reflecting: i).children.compactMap { $0.label }
}
public static func localizedSymbols() -> [String: String] {
[:]
}
}
}
}
@@ -127,16 +386,14 @@ extension KissAccount {
///
///
static public func getIndexPrice(indexType: DomesticExtra.IndexType, startDate: Date, endDate: Date) async throws -> String {
static public func getIndexPrice(indexType: DomesticExtra.IndexType, date: Date) async throws -> DomesticExtra.IndexPriceResult {
return try await withUnsafeThrowingContinuation { continuation in
let range = DomesticExtra.IndexPriceRequest.DataRange(startDate: startDate, endDate: endDate)
let request = DomesticExtra.IndexPriceRequest(range: range, indexType: indexType)
let request = DomesticExtra.IndexPriceRequest(indexType: indexType, date: date)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: (result))
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
@@ -146,14 +403,31 @@ extension KissAccount {
///
///
static public func getIndexPortfolio(indexType: DomesticExtra.IndexType, date: Date) async throws -> String {
static public func getIndexPortfolio(indexId: String, indexId2: String, date: Date) async throws -> DomesticExtra.IndexPortfolioResult {
return try await withUnsafeThrowingContinuation { continuation in
let request = DomesticExtra.IndexPortfolioRequest(indexType: indexType, date: date)
let request = DomesticExtra.IndexPortfolioRequest(indexId: indexId, indexId2: indexId2, date: date)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: (result))
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
///
///
static public func getAllIndices() async throws -> DomesticExtra.AllIndicesResult {
return try await withUnsafeThrowingContinuation { continuation in
let request = DomesticExtra.AllIndicesRequest()
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}

View File

@@ -10,6 +10,7 @@ import Foundation
public struct DomesticExtra {
public typealias Shorts = ShortSellingBalanceResult.OutBlock
public typealias IndexProduct = AllIndicesResult.Block
}
protocol KrxRequest: Request {
@@ -66,6 +67,17 @@ extension DomesticExtra {
}
}
public enum KrxMarketCode: String, Codable {
case undefined = ""
case stock = "STK"
case kosdaq = "KSQ"
case krx = "KRX"
case gbl = "GBL"
}
extension DomesticExtra {
public struct ShortSellingBalanceResult: Codable {
@@ -73,11 +85,11 @@ extension DomesticExtra {
public let block: [Block]?
///
public let outBlock: [OutBlock]?
public let currentDatetime: String
///
public let currentDatetime: String /// 2023.06.11 PM 07:26:14
public var currentDate: Date {
// 2023.06.11 PM 07:26:14
return Date()
currentDatetime.krxDate!
}
private enum CodingKeys: String, CodingKey, CaseIterable {
@@ -87,21 +99,22 @@ extension DomesticExtra {
}
public struct Block: Codable {
///
public let fullCode: String
///
public let shortCode: String
/// 1
public let index1Code: String
/// 2
public let index2Code: String
///
public let productName: String
///
public let marketCode: String
///
public let indexMarketCode: KrxMarketCode
///
public let marketName: String
private enum CodingKeys: String, CodingKey, CaseIterable {
case fullCode = "full_code"
case shortCode = "short_code"
case index1Code = "full_code"
case index2Code = "short_code"
case productName = "codeName"
case marketCode
case indexMarketCode = "marketCode"
case marketName
}
}
@@ -116,7 +129,7 @@ extension DomesticExtra {
///
public let shortSellingBalanceAmount: String
///
public let totalMarketValue: String
public let marketCapital: String
///
public let shortSellingBalanceRatio: String
@@ -125,7 +138,7 @@ extension DomesticExtra {
case shortSellingBalanceQuantity = "BAL_QTY" /// 29,330
case listedStockCount = "LIST_SHRS" /// 46,822,295
case shortSellingBalanceAmount = "BAL_AMT" /// 130,518,500
case totalMarketValue = "MKTCAP" /// 208,359,212,750
case marketCapital = "MKTCAP" /// 208,359,212,750
case shortSellingBalanceRatio = "BAL_RTO" /// 0.06
}
@@ -148,9 +161,9 @@ extension DomesticExtra {
shortSellingBalanceAmount.removeAll(where: { $0 == "," })
self.shortSellingBalanceAmount = shortSellingBalanceAmount
var totalMarketValue = try container.decode(String.self, forKey: DomesticExtra.ShortSellingBalanceResult.OutBlock.CodingKeys.totalMarketValue)
totalMarketValue.removeAll(where: { $0 == "," })
self.totalMarketValue = totalMarketValue
var marketCapital = try container.decode(String.self, forKey: DomesticExtra.ShortSellingBalanceResult.OutBlock.CodingKeys.marketCapital)
marketCapital.removeAll(where: { $0 == "," })
self.marketCapital = marketCapital
self.shortSellingBalanceRatio = try container.decode(String.self, forKey: DomesticExtra.ShortSellingBalanceResult.OutBlock.CodingKeys.shortSellingBalanceRatio)
}
@@ -163,9 +176,18 @@ extension DomesticExtra {
self.shortSellingBalanceQuantity = array[1]
self.listedStockCount = array[2]
self.shortSellingBalanceAmount = array[3]
self.totalMarketValue = array[4]
self.marketCapital = array[4]
self.shortSellingBalanceRatio = array[5]
}
public static func symbols() -> [String] {
let i = try! OutBlock(array: Array(repeating: "", count: 6), source: #function)
return Mirror(reflecting: i).children.compactMap { $0.label }
}
public static func localizedSymbols() -> [String: String] {
[:]
}
}
}
}

View File

@@ -84,6 +84,33 @@ extension KissConsole {
createSubpath(subPath)
return fileUrl
}
static func indexPriceFileUrl(date: Date) -> URL {
let subPath = "data/index/\(date.yyyyMMdd)"
let subFile = "\(subPath)/price-\(date.yyyyMMdd)-\(date.HHmmss).csv"
let fileUrl = URL.currentDirectory().appending(path: subFile)
createSubpath(subPath)
return fileUrl
}
static func indexPortfolioFileUrl(date: Date, type: DomesticExtra.IndexType, indexFullCode: String) -> URL {
let subPath = "data/index/\(date.yyyyMMdd)"
let subFile = "\(subPath)/portfolio-\(type.name)-\(indexFullCode).csv"
let fileUrl = URL.currentDirectory().appending(path: subFile)
createSubpath(subPath)
return fileUrl
}
static func indexProductsFileUrl() -> URL {
let subPath = "data"
let subFile = "\(subPath)/index-products.csv"
let fileUrl = URL.currentDirectory().appending(path: subFile)
createSubpath(subPath)
return fileUrl
}
}

View File

@@ -21,6 +21,9 @@ let PreferredShortsTPS: UInt64 = 5
/// Limit to request a top query
let PreferredTopTPS: UInt64 = 5
/// Limit to reqeust a index query
let PreferredIndexTPS: UInt64 = 5
/// Limit to request a investor query
let PreferredInvestorTPS: UInt64 = 19

View File

@@ -10,8 +10,13 @@ import KissMe
class KissConsole: KissMe.ShopContext {
/// json
private var credential: Credential? = nil
///
var account: KissAccount? = nil
///
private var shop: KissShop? = nil
/// candle productNo
@@ -20,6 +25,8 @@ class KissConsole: KissMe.ShopContext {
/// CSV , field name (true) (false)
var localized: Bool = false
var indexContext: IndexContext
private enum KissCommand: String {
case quit = "quit"
@@ -62,13 +69,16 @@ class KissConsole: KissMe.ShopContext {
// KRX
case index = "index"
case indexAll = "index all"
case indexPortfolio = "index portfolio"
//
case loadShop = "load shop"
case updateShop = "update shop"
case look = "look"
case loadIndex = "load index"
case updateIndex = "update index"
//
case holiday = "holiday"
@@ -99,10 +109,12 @@ class KissConsole: KissMe.ShopContext {
return false
case .investor, .investorAll:
return true
case .shorts, .shortsAll, .index, .indexAll:
case .shorts, .shortsAll, .index, .indexPortfolio:
return false
case .loadShop, .updateShop, .look, .holiday:
return false
case .loadIndex, .updateIndex:
return false
case .showcase:
return false
case .loves, .love, .hate, .localizeNames, .localizeOnOff:
@@ -124,6 +136,8 @@ class KissConsole: KissMe.ShopContext {
KissConsole.createSubpath("log")
KissConsole.createSubpath("data")
indexContext = IndexContext()
super.init()
lastLogin()
loadLocalName()
@@ -131,6 +145,7 @@ class KissConsole: KissMe.ShopContext {
let semaphore = DispatchSemaphore(value: 0)
Task {
await onLoadShop()
await onLoadIndex()
semaphore.signal()
}
semaphore.wait()
@@ -233,13 +248,16 @@ class KissConsole: KissMe.ShopContext {
case .shortsAll: onShortsAll(args)
case .index: await onIndex(args)
case .indexAll: onIndexAll(args)
case .indexPortfolio: await onIndexPortfolio(args)
case .loadShop: await onLoadShop()
case .updateShop: await onUpdateShop()
case .look: await onLook(args)
case .holiday: await onHoliday(args)
case .loadIndex: await onLoadIndex()
case .updateIndex: await onUpdateIndex()
case .showcase: await onShowcase()
case .loves: await onLoves()
case .love: await onLove(args)
@@ -853,16 +871,47 @@ extension KissConsole {
return
}
do {
// TODO: Work more
// _ = try await getIndexPrice(indexType, startDate: , endDate: )
let curDate = Date()
if curDate.isBeforeMarketOpenning {
print("Before market openning")
return
}
let result = try await KissAccount.getIndexPrice(indexType: indexType, date: curDate)
guard result.output.isEmpty == false else {
print("empty result")
return
}
let fileUrl = KissConsole.indexPriceFileUrl(date: result.currentDate)
try result.output.writeCsv(toFile: fileUrl, localized: localized)
print("DONE \(result.output.count)")
} catch {
print(error)
}
}
private func onIndexAll(_ args: [String]) {
// TODO: Work more
private func onIndexPortfolio(_ args: [String]) async {
do {
let indices = indexContext.getAllIndices()
let date = Date()
for index in indices {
let result = try await KissAccount.getIndexPortfolio(indexId: index.index1Code, indexId2: index.index2Code, date: date)
guard result.output.isEmpty == false else {
print("empty result on \(index.indexFullCode)")
continue
}
let fileUrl = KissConsole.indexPortfolioFileUrl(date: date, type: index.indexType, indexFullCode: index.indexFullCode)
try result.output.writeCsv(toFile: fileUrl, localized: localized)
print("DONE \(result.output.count) \(index.indexFullCode)")
try await Task.sleep(nanoseconds: 1_000_000_000 / PreferredIndexTPS)
}
} catch {
print(error)
}
}
@@ -915,6 +964,28 @@ extension KissConsole {
}
private func onLoadIndex() async {
return await withUnsafeContinuation { continuation in
self.indexContext.loadIndex(url: KissConsole.indexProductsFileUrl(), loggable: true)
continuation.resume()
}
}
private func onUpdateIndex() async {
do {
let result = try await KissAccount.getAllIndices()
if result.block.isEmpty == false {
let fileUrl = KissConsole.indexProductsFileUrl()
try result.block.writeCsv(toFile: fileUrl, localized: localized)
}
print("DONE \(result.block.count)")
} catch {
print(error)
}
}
func getAllProduct(baseDate: Date) async -> [DomesticShop.Product] {
var pageNo = 0
var shopItems = [DomesticShop.Product]()
@@ -1085,6 +1156,10 @@ extension KissConsole {
symbols.formUnion(CurrentPriceResult.OutputDetail.symbols())
symbols.formUnion(CapturePrice.symbols())
symbols.formUnion(InvestorVolumeResult.OutputDetail.symbols())
symbols.formUnion(DomesticExtra.IndexPriceResult.Output.symbols())
symbols.formUnion(DomesticExtra.IndexPortfolioResult.Output.symbols())
symbols.formUnion(DomesticExtra.AllIndicesResult.Block.symbols())
symbols.formUnion(DomesticExtra.ShortSellingBalanceResult.OutBlock.symbols())
let newNames = symbols.sorted(by: { $0 < $1 })
let nameUrl = KissConsole.localNamesUrl

View File

@@ -15,15 +15,6 @@ enum RunMode: String {
}
struct Param: Codable {
/// simulator (begin ~ end)
///
let beginDate: String // yyyyMMdd HHmmss
let endDate: String // yyyyMMdd HHmmss
}
struct Model: Codable {
let indexSets: [IndexSet]

View File

@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
340A4DBD2A4C34BE005A1FBA /* IndexContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340A4DBC2A4C34BE005A1FBA /* IndexContext.swift */; };
341F5EB02A0A80EC00962D48 /* KissMe.docc in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EAF2A0A80EC00962D48 /* KissMe.docc */; };
341F5EB62A0A80EC00962D48 /* KissMe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 341F5EAB2A0A80EC00962D48 /* KissMe.framework */; };
341F5EBB2A0A80EC00962D48 /* KissMeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EBA2A0A80EC00962D48 /* KissMeTests.swift */; };
@@ -53,6 +54,7 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
340A4DBC2A4C34BE005A1FBA /* IndexContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndexContext.swift; sourceTree = "<group>"; };
341F5EAB2A0A80EC00962D48 /* KissMe.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KissMe.framework; sourceTree = BUILT_PRODUCTS_DIR; };
341F5EAE2A0A80EC00962D48 /* KissMe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KissMe.h; sourceTree = "<group>"; };
341F5EAF2A0A80EC00962D48 /* KissMe.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; name = KissMe.docc; path = ../KissMe.docc; sourceTree = "<group>"; };
@@ -242,6 +244,7 @@
children = (
34F190102A4394EB0068C697 /* LocalContext.swift */,
34F1900E2A426D150068C697 /* ShopContext.swift */,
340A4DBC2A4C34BE005A1FBA /* IndexContext.swift */,
);
path = Context;
sourceTree = "<group>";
@@ -383,6 +386,7 @@
341F5EF92A0F907300962D48 /* DomesticStockPriceResult.swift in Sources */,
341F5EE12A0F373B00962D48 /* Login.swift in Sources */,
341F5EF52A0F891200962D48 /* KissAccount.swift in Sources */,
340A4DBD2A4C34BE005A1FBA /* IndexContext.swift in Sources */,
34E7B9112A49BD2800B3AB9F /* DomesticIndex.swift in Sources */,
341F5F0D2A15222E00962D48 /* AuthRequest.swift in Sources */,
);