Files
KissMe/KissMeConsole/Sources/main.swift
2023-08-26 01:05:41 +09:00

565 lines
17 KiB
Swift

//
// main.swift
// KissMeConsole
//
// Created by ened-book-m1 on 2023/05/09.
//
import Foundation
//KissConsole().run()
import KissMe
//test_get_websocket_key_and_contact_price()
test_get_websocket_key_and_asking_price()
enum VariableRatioSignType: UInt8 {
///
case upLimit = 1
///
case up = 2
///
case noChange = 3
///
case downLimit = 4
///
case down = 5
}
enum ConclusionType: UInt8 {
/// (+)
case buying = 1
///
case load = 3
/// (-)
case selling = 5
}
enum HourClassCode: Character {
///
case onMarket = "0"
///
case afterMarket = "A"
///
case beforeMarket = "B"
/// 9 , VI
case viInvoking = "C"
///
case overTime_SinglePrice = "D"
}
enum MarketOperationCode: UInt8 {
// TODO: work
// (1)
// 1 :
// 2 :
// 3 :
// 4 :
// 7 : Buy-in
// 8 : Buy-in
//
// (2)
// 0 :
// 1 :
// 2 :
// 3 :
// 7 :
// 8 : Buy-in
case aa = 1
}
struct ContractPrice {
///
public let shortCode: String
/// (HHmmss)
public let conclusionTime: String
///
public let currentPrice: Int
///
public let previousDayVariableRatioSign: VariableRatioSignType
///
public let previousDayVariableRatio: Double
///
public let previousDayDiffRatio: Double
///
public let weightedAveragePrice: Int
///
public let openningPrice: Int
///
public let highestPrice: Int
///
public let lowestPrice: Int
/// 1
public let askingPrice: Int
/// 1
public let biddingPrice: Int
///
public let conclusionVolume: Int
///
public let accumulatedVolume: Int
///
public let accumulatedTradingAmount: Int
///
public let sellingConclusionCount: Int
///
public let buyingConclusionCount: Int
///
public let netBuyingConclusionCount: Int
///
public let conclusionStrength: Double
///
public let totalSellingQuantity: Int
///
public let totalBuyingQuantity: Int
///
public let conclusionType: ConclusionType
///
public let buyingRatio: Double
///
public let previousDay_VolumeDiff_FluctuationRate: Double
///
public let openningPriceHour: String
///
public let openningPrice_VariableRatioSign: VariableRatioSignType
///
public let openningPrice_VariableRatio: Int
///
public let highestPriceHour: String
///
public let highPrice_VariableRatioSign: VariableRatioSignType
///
public let highPrice_VariableRatio: Int
///
public let lowestPriceHour: String
///
public let lowPrice_VariableRatioSign: VariableRatioSignType
///
public let lowPrice_VariableRatio: Int
///
public let businessDate: String
///
public let marketOperationCode: MarketOperationCode
///
public let tradeStopped: Bool
/// 1
public let askingPrice_ResidualQuantity: Int
/// 1
public let biddingPrice_ResidualQuantity: Int
///
public let askingPrice_TotalResidualQuantity: Int
///
public let biddingPrice_TotalResidualQuantity: Int
///
public let volumeTurnoverRate: Double
///
public let previousDaySameTime_AccumulatedTradingQuantity: Int
///
public let previousDaySameTime_AccumulatedTradingQuantityRatio: Double
///
public let hourClassCode: HourClassCode
///
public let marketTerminationCode: String
/// VI
public let viStandardPrice: Int
}
struct AskingPrice {
///
public let shortCode: String
///
public let businessTime: String
///
public let hourClassCode: HourClassCode
/// 1 ~ 10
public let askingPrices: [Int]
/// 1 ~ 10
public let biddingPrices: [Int]
/// 1 ~ 4
public let askingPriceVolumes: [Int]
/// 1 ~ 10
public let biddingPriceVolumes: [Int]
///
public let askingPrice_TotalResidualQuantity: Int
///
public let biddingPrice_TotalResidualQuantity: Int
///
public let askingPrice_Overtime_TotalResidualQuantity: Int
///
public let biddingPrice_Overtime_TotalResidualQuantity: Int
///
public let expectedConclusionPrice: Int
///
public let expectedConclusionQuantity: Int
///
public let expectedVolume: Int
///
public let expectedConclusion_VariableRatio: Double
///
public let expectedConclusion_VariableRatioSign: VariableRatioSignType
///
public let expectedConclusion_PreviousDayDiffRatio: Double
///
public let accumulatedVolume: Int
///
public let askingPrice_TotalResidualQuantity_IncreaseDecrease: Int
///
public let biddingPrice_TotalResidualQuantity_IncreaseDecrease: Int
///
public let askingPrice_Overtime_TotalResidualQuantity_IncreaseDecrease: Int
///
public let biddingPrice_Overtime_TotalResidualQuantity_IncreaseDecrease: Int
///
public let tradeCode: String
}
enum ContractType: String {
///
case selling = "01"
///
case buying = "02"
}
enum OrderType: String {
///
case limits = "00"
///
case marketPrice = "01"
///
case conditionalLimits = "02"
///
case mostFavorLimits = "03"
///
case mostPriorLimits = "04"
///
case overTime_BeforeMarket = "05"
///
case overTime_AfterMarket = "06"
///
case overTime_SinglePrice = "07"
///
case treasury = "08"
/// S-Option
case treasury_SOption = "09"
///
case treasury_MoneyTrust = "10"
/// IOC (,)
case iocLimits_Conclusion_CancelResidualQuantity = "11"
/// FOK (,)
case fokLimits_Conclusion_CancelEntireQuantity = "12"
/// IOC (,)
case iocMarketPrice_Conclusion_CancelResidualQuantity = "13"
/// FOK (,)
case fokMarketPrice_Conclusion_CancelEntireQuantity = "14"
/// IOC (,)
case iocMostFavor_Conclusion_CancelResidualQuantity = "15"
/// FOK (,)
case fokMostFavor_Conclusion_CancelEntireQuantity = "16"
}
enum RefuseYesNo: Int {
///
case approval = 0
///
case refuse = 1
}
enum ConclusionYesNo: Int {
/// ,,,,
case unconclused = 1
/// ( 2 )
case conclused = 2
}
enum AcceptYesNo: Int {
///
case orderAccept = 1
///
case confirm = 2
/// (FOK/IOC)
case cancel = 3
}
struct ContractNotice {
/// ID
public let customerID: String
///
public let accountNo: String
///
public let orderNo: String
///
public let originalOrderNo: String
///
public let contractType: ContractType
///
public let revisionType: String
///
public let orderType: OrderType
///
public let orderCondition: String
///
public let shortCode: String
///
public let conclusionQuantity: Int
///
public let conclusionPrice: Int
///
public let conclusionTime: String
///
public let refused: RefuseYesNo
///
public let conclused: ConclusionYesNo
///
public let accepted: AcceptYesNo
///
public let branchNo: String
///
public let orderQuantity: Int
///
public let accountName: String
///
public let productName: String
///
public let creditClass: String
///
public let creditLoanDate: String
/// 40
public let productName40: String
///
public let orderPrice: Int
}
func test_parse_contact_price_response() {
let str = "{\"header\":{\"tr_id\":\"H0STCNT0\",\"tr_key\":\"005930\",\"encrypt\":\"N\"},\"body\":{\"rt_cd\":\"0\",\"msg_cd\":\"OPSP0000\",\"msg1\":\"SUBSCRIBE SUCCESS\",\"output\":{\"iv\":\"dcc3c442acfb8b9a\",\"key\":\"vcvxscahuklwkiawiuxbsfcmsulqjejf\"}}}0|H0STCNT0|001|005930^134305^68100^2^1000^1.49^68305.67^68300^68700^67900^68100^68000^1^11393808^778261604700^32559^26679^-5880^79.02^6084367^4807987^1^0.43^119.32^090027^5^-200^091809^5^-600^113615^2^200^20230824^20^N^309354^354766^2143494^2041321^0.19^6698642^170.09^0^^68300{\"header\":{\"tr_id\":\"PINGPONG\",\"datetime\":\"20230824212922\"}}"
}
enum JsonTrDataType {
case json(String)
case contractPrice(ContractPrice)
case askingPrice(AskingPrice)
case contractNotice(ContractNotice)
}
func parseJsonTrData(_ str: String) throws -> [JsonTrDataType] {
var dataArray = [JsonTrDataType]()
var startAt = str.startIndex
while startAt < str.endIndex {
switch str.first {
case "{":
var openedCount = 0
let charset = CharacterSet(charactersIn: "{}")
repeat {
guard let r = str.rangeOfCharacter(from: charset, options: [], range: startAt ..< str.endIndex) else {
break
}
switch str[r.lowerBound] {
case "{":
openedCount += 1
case "}":
openedCount -= 1
if openedCount == 0 {
/// end of json data
let jsonString = String(str[str.startIndex ..< r.upperBound])
dataArray.append(.json(jsonString))
startAt = r.upperBound
}
default:
throw GeneralError.impossibleJsonCharacter
}
startAt = r.upperBound
} while true
case "0":
let data = try parseTrData(false, str: str, startAt: startAt)
dataArray.append(contentsOf: data)
case "1":
let data = try parseTrData(true, str: str, startAt: startAt)
dataArray.append(contentsOf: data)
default:
throw GeneralError.invalidWebSocketData
}
}
return dataArray
}
private func parseTrData(_ encrypted: Bool, str: String, startAt: String.Index) throws -> [JsonTrDataType] {
var dataArray = [JsonTrDataType]()
var startAt = str.startIndex
let charset = CharacterSet(charactersIn: "|")
guard let i1 = str.rangeOfCharacter(from: charset, options: [], range: startAt ..< str.endIndex) else {
throw GeneralError.notEnoughWebSocketData
}
let encryption = String(str[startAt ..< i1.upperBound])
guard encryption == "0" || encryption == "1" else {
throw GeneralError.invalidWebSocketData_EncryptionField
}
startAt = i1.upperBound
guard let i2 = str.rangeOfCharacter(from: charset, options: [], range: startAt ..< str.endIndex) else {
throw GeneralError.notEnoughWebSocketData
}
let trId = String(str[startAt ..< i2.upperBound])
startAt = i2.upperBound
guard let i3 = str.rangeOfCharacter(from: charset, options: [], range: startAt ..< str.endIndex) else {
throw GeneralError.notEnoughWebSocketData
}
let dataCountString = String(str[startAt ..< i3.upperBound])
guard let dataCount = Int(dataCountString) else {
throw GeneralError.notEnoughWebSocketData
}
switch trId {
case "H0STCNT0":
for _ in 0 ..< dataCount {
}
case "H0STASP0":
break
case "H0STCNI0":
break
default:
throw GeneralError.invalidWebSocketData_TrIdField
}
return dataArray
}
func test_get_websocket_key_and_asking_price() {
let isMock = false
let semaphore = DispatchSemaphore(value: 0)
Task {
guard let (account, approvalKey) = await test_get_websocket_key(isMock: isMock) else {
return
}
let webSocketCredential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
var socket = Domestic.AskingPriceWebSocket(credential: webSocketCredential, productCode: KissConsole.defaultProductNo)
do {
try await socket.connect()
let result = try await socket.subscribe()
print(result)
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
let result2 = try await socket.unsubscribe()
print(result2)
if let message = try await socket.receive() {
print(message)
}
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
socket.disconnect()
try await Task.sleep(nanoseconds: 1_000_000_000 * 1)
} catch {
print(error)
}
semaphore.signal()
}
semaphore.wait()
}
func test_get_websocket_key_and_contact_price() {
let isMock = false
let semaphore = DispatchSemaphore(value: 0)
Task {
guard let (account, approvalKey) = await test_get_websocket_key(isMock: isMock) else {
return
}
let webSocketCredential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
var socket = Domestic.ContractPriceWebSocket(credential: webSocketCredential, productCode: KissConsole.defaultProductNo)
do {
try await socket.connect()
let result = try await socket.subscribe()
print(result)
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
let result2 = try await socket.unsubscribe()
print(result2)
if let message = try await socket.receive() {
print(message)
}
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
socket.disconnect()
try await Task.sleep(nanoseconds: 1_000_000_000 * 1)
} catch {
print(error)
}
semaphore.signal()
}
semaphore.wait()
}
func test_get_websocket_key(isMock: Bool) async -> (KissAccount, String)? {
let credential: Credential
do {
credential = try KissCredential(isMock: isMock)
} catch {
print(error)
return nil
}
let account = KissAccount(credential: credential)
do {
/// Return existing valid key
if let approvalKey = account.approvalKey {
return (account, approvalKey)
}
if try await account.login() {
let approvalKey = try await account.getApprovalKey()
print("approvalKey : \(approvalKey)")
return (account, approvalKey)
}
} catch {
print(error)
return nil
}
return nil
}