Parsing websocket data

This commit is contained in:
2023-08-26 15:46:30 +09:00
parent 19dbc2ca05
commit aababdae8e

View File

@@ -12,7 +12,8 @@ import Foundation
import KissMe
//test_get_websocket_key_and_contact_price()
test_get_websocket_key_and_asking_price()
//test_get_websocket_key_and_asking_price()
test_parse_contact_price_response()
enum VariableRatioSignType: UInt8 {
@@ -53,24 +54,41 @@ enum HourClassCode: Character {
}
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
typealias MarketOperationCode = String
extension MarketOperationCode {
// MARK:
///
public var isBeforeMarket: Bool { first == "1" }
///
public var isOnMarket: Bool { first == "2" }
///
public var isAfterMarket: Bool { first == "3" }
///
public var isOverTime_SinglePrice: Bool { first == "4" }
/// Buy-in
public var isGeneralBuyIn: Bool { first == "5" }
/// Buy-in
public var isTheDayBuyIn: Bool { first == "6" }
// MARK:
///
public var isGeneral: Bool { second == "0" }
///
public var isClosingPrice: Bool { second == "1" }
///
public var isBulk: Bool { second == "2" }
///
public var isBasket: Bool { second == "3" }
///
public var isClearanceSale: Bool { second == "7" }
/// Buy-in
public var isBuyIn: Bool { second == "8" }
var first: Character { self[startIndex] }
var second: Character { self[index(startIndex, offsetBy: 1)] }
}
@@ -167,6 +185,62 @@ struct ContractPrice {
public let marketTerminationCode: String
/// VI
public let viStandardPrice: Int
static let propertiesCount = 46
public init(array: [Substring], source: Substring) throws {
guard array.count == ContractPrice.propertiesCount else {
throw GeneralError.incorrectArrayItems(String(source), array.count, ContractPrice.propertiesCount)
}
self.shortCode = String(array[0])
self.conclusionTime = String(array[1])
self.currentPrice = Int(array[2]) ?? 0
self.previousDayVariableRatioSign = VariableRatioSignType(rawValue: UInt8(array[3])!)!
self.previousDayVariableRatio = Double(array[4]) ?? 0
self.previousDayDiffRatio = Double(array[5]) ?? 0
self.weightedAveragePrice = Int(array[6]) ?? 0
self.openningPrice = Int(array[7]) ?? 0
self.highestPrice = Int(array[8]) ?? 0
self.lowestPrice = Int(array[9]) ?? 0
self.askingPrice = Int(array[10]) ?? 0
self.biddingPrice = Int(array[11]) ?? 0
self.conclusionVolume = Int(array[12]) ?? 0
self.accumulatedVolume = Int(array[13]) ?? 0
self.accumulatedTradingAmount = Int(array[14]) ?? 0
self.sellingConclusionCount = Int(array[15]) ?? 0
self.buyingConclusionCount = Int(array[16]) ?? 0
self.netBuyingConclusionCount = Int(array[17]) ?? 0
self.conclusionStrength = Double(array[18]) ?? 0
self.totalSellingQuantity = Int(array[19]) ?? 0
self.totalBuyingQuantity = Int(array[20]) ?? 0
self.conclusionType = ConclusionType(rawValue: UInt8(array[21])!)!
self.buyingRatio = Double(array[22]) ?? 0
self.previousDay_VolumeDiff_FluctuationRate = Double(array[23]) ?? 0
self.openningPriceHour = String(array[24])
self.openningPrice_VariableRatioSign = VariableRatioSignType(rawValue: UInt8(array[25])!)!
self.openningPrice_VariableRatio = Int(array[26]) ?? 0
self.highestPriceHour = String(array[27])
self.highPrice_VariableRatioSign = VariableRatioSignType(rawValue: UInt8(array[28])!)!
self.highPrice_VariableRatio = Int(array[29]) ?? 0
self.lowestPriceHour = String(array[30])
self.lowPrice_VariableRatioSign = VariableRatioSignType(rawValue: UInt8(array[31])!)!
self.lowPrice_VariableRatio = Int(array[32]) ?? 0
self.businessDate = String(array[33])
self.marketOperationCode = String(array[34])
self.tradeStopped = String(array[35]) == "Y"
self.askingPrice_ResidualQuantity = Int(array[36]) ?? 0
self.biddingPrice_ResidualQuantity = Int(array[37]) ?? 0
self.askingPrice_TotalResidualQuantity = Int(array[38]) ?? 0
self.biddingPrice_TotalResidualQuantity = Int(array[39]) ?? 0
self.volumeTurnoverRate = Double(array[40]) ?? 0
self.previousDaySameTime_AccumulatedTradingQuantity = Int(array[41]) ?? 0
self.previousDaySameTime_AccumulatedTradingQuantityRatio = Double(array[42]) ?? 0
self.hourClassCode = HourClassCode(rawValue: String(array[43]).first!)!
self.marketTerminationCode = String(array[44])
self.viStandardPrice = Int(array[45]) ?? 0
}
}
@@ -217,6 +291,48 @@ struct AskingPrice {
public let biddingPrice_Overtime_TotalResidualQuantity_IncreaseDecrease: Int
///
public let tradeCode: String
static let propertiesCount = 59
public init(array: [Substring], source: Substring) throws {
guard array.count == AskingPrice.propertiesCount else {
throw GeneralError.incorrectArrayItems(String(source), array.count, AskingPrice.propertiesCount)
}
func getIntArray(_ fromIndex: Int, _ toIndex: Int) -> [Int] {
var array = [Int]()
for i in fromIndex ..< toIndex {
let value = Int(array[i])
array.append(value)
}
return array
}
self.shortCode = String(array[0])
self.businessTime = String(array[1])
self.hourClassCode = HourClassCode(rawValue: String(array[2]).first!)!
self.askingPrices = getIntArray(3, 3+10)
self.biddingPrices = getIntArray(13, 13+10)
self.askingPriceVolumes = getIntArray(23, 23+10)
self.biddingPriceVolumes = getIntArray(33, 33+10)
self.askingPrice_TotalResidualQuantity = Int(array[43]) ?? 0
self.biddingPrice_TotalResidualQuantity = Int(array[44]) ?? 0
self.askingPrice_Overtime_TotalResidualQuantity = Int(array[45]) ?? 0
self.biddingPrice_Overtime_TotalResidualQuantity = Int(array[46]) ?? 0
self.expectedConclusionPrice = Int(array[47]) ?? 0
self.expectedConclusionQuantity = Int(array[48]) ?? 0
self.expectedVolume = Int(array[49]) ?? 0
self.expectedConclusion_VariableRatio = Double(array[50]) ?? 0
self.expectedConclusion_VariableRatioSign = VariableRatioSignType(rawValue: UInt8(array[51])!)!
self.expectedConclusion_PreviousDayDiffRatio = Double(array[52]) ?? 0
self.accumulatedVolume = Int(array[53]) ?? 0
self.askingPrice_TotalResidualQuantity_IncreaseDecrease = Int(array[54]) ?? 0
self.biddingPrice_TotalResidualQuantity_IncreaseDecrease = Int(array[55]) ?? 0
self.askingPrice_Overtime_TotalResidualQuantity_IncreaseDecrease = Int(array[56]) ?? 0
self.biddingPrice_Overtime_TotalResidualQuantity_IncreaseDecrease = Int(array[57]) ?? 0
self.tradeCode = String(array[58])
}
}
@@ -339,12 +455,62 @@ struct ContractNotice {
public let productName40: String
///
public let orderPrice: Int
static let propertiesCount = 23
public init(array: [Substring], source: Substring) throws {
guard array.count == ContractNotice.propertiesCount else {
throw GeneralError.incorrectArrayItems(String(source), array.count, ContractNotice.propertiesCount)
}
self.customerID = String(array[0])
self.accountNo = String(array[1])
self.orderNo = String(array[2])
self.originalOrderNo = String(array[3])
self.contractType = ContractType(rawValue: String(array[4]))!
self.revisionType = String(array[5])
self.orderType = OrderType(rawValue: String(array[6]))!
self.orderCondition = String(array[7])
self.shortCode = String(array[8])
self.conclusionQuantity = Int(array[9]) ?? 0
self.conclusionPrice = Int(array[10]) ?? 0
self.conclusionTime = String(array[11])
self.refused = RefuseYesNo(rawValue: Int(array[12])!)!
self.conclused = ConclusionYesNo(rawValue: Int(array[13])!)!
self.accepted = AcceptYesNo(rawValue: Int(array[14])!)!
self.branchNo = String(array[15])
self.orderQuantity = Int(array[16]) ?? 0
self.accountName = String(array[17])
self.productName = String(array[18])
self.creditClass = String(array[19])
self.creditLoanDate = String(array[20])
self.productName40 = String(array[21])
self.orderPrice = Int(array[22]) ?? 0
}
}
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\"}}"
do {
let dataArray = try parseJsonTrData(str)
for data in dataArray {
switch data {
case .json(let str):
print("json: \(str)")
case .contractPrice(let price):
print("contractPrice: \(price)")
case .askingPrice(let price):
print("askingPrice: \(price)")
case .contractNotice(let notice):
print("contractNotice: \(notice)")
}
}
} catch {
print(error)
}
}
@@ -358,16 +524,21 @@ enum JsonTrDataType {
func parseJsonTrData(_ str: String) throws -> [JsonTrDataType] {
var dataArray = [JsonTrDataType]()
var startAt = str.startIndex
while startAt < str.endIndex {
switch str.first {
var nextAt = str.startIndex
let charset = CharacterSet(charactersIn: "{}")
while nextAt < str.endIndex {
print("nextAt... \(nextAt.utf16Offset(in: str))")
switch str[nextAt] {
case "{":
var openedCount = 0
let charset = CharacterSet(charactersIn: "{}")
/// Scan end of json data
var openedCount = 1
let startAt = nextAt
nextAt = str.index(nextAt, offsetBy: 1)
repeat {
guard let r = str.rangeOfCharacter(from: charset, options: [], range: startAt ..< str.endIndex) else {
while openedCount > 0 {
guard let r = str.rangeOfCharacter(from: charset, options: [], range: nextAt ..< str.endIndex) else {
break
}
@@ -378,25 +549,29 @@ func parseJsonTrData(_ str: String) throws -> [JsonTrDataType] {
openedCount -= 1
if openedCount == 0 {
/// end of json data
let jsonString = String(str[str.startIndex ..< r.upperBound])
let jsonString = String(str[startAt ..< r.upperBound])
dataArray.append(.json(jsonString))
startAt = r.upperBound
nextAt = r.upperBound
break
}
default:
throw GeneralError.impossibleJsonCharacter
}
startAt = r.upperBound
} while true
nextAt = r.upperBound
}
case "0":
let data = try parseTrData(false, str: str, startAt: startAt)
let (data, endAt) = try parseTrData(false, str: str, startAt: nextAt)
dataArray.append(contentsOf: data)
nextAt = endAt
case "1":
let data = try parseTrData(true, str: str, startAt: startAt)
let (data, endAt) = try parseTrData(true, str: str, startAt: nextAt)
dataArray.append(contentsOf: data)
nextAt = endAt
default:
print(str[nextAt ..< str.endIndex])
throw GeneralError.invalidWebSocketData
}
}
@@ -405,52 +580,96 @@ func parseJsonTrData(_ str: String) throws -> [JsonTrDataType] {
}
private func parseTrData(_ encrypted: Bool, str: String, startAt: String.Index) throws -> [JsonTrDataType] {
private func parseTrData(_ encrypted: Bool, str: String, startAt: String.Index) throws -> ([JsonTrDataType], String.Index) {
var dataArray = [JsonTrDataType]()
var startAt = str.startIndex
var nextAt = startAt
let charset = CharacterSet(charactersIn: "|")
guard let i1 = str.rangeOfCharacter(from: charset, options: [], range: startAt ..< str.endIndex) else {
guard let i1 = str.rangeOfCharacter(from: charset, options: [], range: nextAt ..< str.endIndex) else {
throw GeneralError.notEnoughWebSocketData
}
let encryption = String(str[startAt ..< i1.upperBound])
/// Get encryption field
let encryption = String(str[nextAt ..< i1.lowerBound])
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 {
nextAt = i1.upperBound
guard let i2 = str.rangeOfCharacter(from: charset, options: [], range: nextAt ..< 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 {
/// Get trId field
let trId = String(str[nextAt ..< i2.lowerBound])
nextAt = i2.upperBound
guard let i3 = str.rangeOfCharacter(from: charset, options: [], range: nextAt ..< str.endIndex) else {
throw GeneralError.notEnoughWebSocketData
}
let dataCountString = String(str[startAt ..< i3.upperBound])
/// Get data count field
let dataCountString = String(str[nextAt ..< i3.lowerBound])
guard let dataCount = Int(dataCountString) else {
throw GeneralError.notEnoughWebSocketData
}
nextAt = i3.upperBound
/// Retrieve multiple data
switch trId {
case "H0STCNT0":
for _ in 0 ..< dataCount {
let (stringArray, string, endAt) = try getTrDataString(str, startAt: nextAt, seperatorCount: ContractPrice.propertiesCount-1)
let price = try ContractPrice(array: stringArray, source: string)
dataArray.append(.contractPrice(price))
nextAt = endAt
}
case "H0STASP0":
break
for _ in 0 ..< dataCount {
let (stringArray, string, endAt) = try getTrDataString(str, startAt: nextAt, seperatorCount: AskingPrice.propertiesCount-1)
let price = try ContractPrice(array: stringArray, source: string)
dataArray.append(.contractPrice(price))
nextAt = endAt
}
case "H0STCNI0":
break
for _ in 0 ..< dataCount {
let (stringArray, string, endAt) = try getTrDataString(str, startAt: startAt, seperatorCount: ContractNotice.propertiesCount-1)
let notice = try ContractNotice(array: stringArray, source: string)
dataArray.append(.contractNotice(notice))
nextAt = endAt
}
default:
throw GeneralError.invalidWebSocketData_TrIdField
}
return dataArray
return (dataArray, nextAt)
}
private func getTrDataString(_ str: String, startAt: String.Index, seperatorCount: Int) throws -> ([Substring], Substring, String.Index) {
var currentSeperator = 0
var nextAt = startAt
var array = [Substring]()
/// Find last seperator
while currentSeperator < seperatorCount {
guard let last = str.range(of: "^", options: [], range: nextAt ..< str.endIndex) else {
throw GeneralError.notEnoughWebSocketData
}
currentSeperator += 1
array.append(str[nextAt ..< last.lowerBound])
nextAt = last.upperBound
}
/// Look forward to find final field
if let end = str.rangeOfCharacter(from: .alphanumerics.inverted, options: [], range: nextAt ..< str.endIndex) {
array.append(str[nextAt ..< end.lowerBound])
let string = str[startAt ..< end.lowerBound]
return (array, string, end.lowerBound)
}
else {
array.append(str[nextAt ..< str.endIndex])
let string = str[startAt ..< str.endIndex]
return (array, string, str.endIndex)
}
}