Add overseas stock API

This commit is contained in:
2024-06-23 09:38:22 +09:00
parent cc1009c987
commit efc680b524
11 changed files with 343 additions and 45 deletions

View File

@@ -69,3 +69,33 @@ public enum OrderRevisionType {
}
}
}
public enum OverseasExchangeType: String {
///
case nasdaq = "NASD"
///
case newyork = "NYSE"
///
case amex = "AMEX"
///
case hongkong = "SEHK"
///
case shanghai = "SHAA"
///
case shenzhen = "SZAA"
///
case tokyo = "TKSE"
///
case hanoi = "HASE"
///
case hochiminh = "VNSE"
}

View File

@@ -23,7 +23,6 @@ public protocol WebSocket {
var socketDelegate: URLSessionWebSocketDelegate? { get }
var queue: DispatchQueue { get }
var timer: DispatchSourceTimer? { get set }
var streamContinuation: AsyncStream<String>.Continuation? { get set }
var parser: WebSocketParser { get }
@@ -97,6 +96,13 @@ extension AuthWebSocket {
config.httpMaximumConnectionsPerHost = 1
config.networkServiceType = .responsiveData
config.sessionSendsLaunchEvents = false
config.isDiscretionary = false
config.shouldUseExtendedBackgroundIdleMode = false
// config.sharedContainerIdentifier = nil
config.httpShouldUsePipelining = false
let queue = OperationQueue()
// 6 -1
@@ -121,15 +127,7 @@ extension AuthWebSocket {
socket.resume()
self.socket = socket
let timer = DispatchSource.makeTimerSource(flags: .strict, queue: queue)
timer.schedule(deadline: .now(), repeating: 0.02, leeway: .nanoseconds(1_000_000_000 / 100))
let _self = self
timer.setEventHandler {
_self.receiveRepeating()
}
timer.resume()
self.timer = timer
receiveRepeating()
continuation.resume(returning: ())
}
@@ -139,9 +137,6 @@ extension AuthWebSocket {
public mutating func disconnect() {
socket?.cancel(with: .normalClosure, reason: nil)
socket = nil
timer?.cancel()
timer = nil
}
@@ -206,7 +201,6 @@ extension AuthWebSocket {
private func receiveRepeating() {
guard let socket = socket else { return }
let semaphore = DispatchSemaphore(value: 0)
Task {
do {
let newMessage = try await socket.receive()
@@ -224,9 +218,7 @@ extension AuthWebSocket {
} catch {
print(error)
}
semaphore.signal()
}
semaphore.wait()
}

View File

@@ -27,7 +27,6 @@ extension Domestic {
public var socketDelegate: URLSessionWebSocketDelegate? { event }
public var message: String = ""
public var queue: DispatchQueue = DispatchQueue(label: "kissme.asking_price_websocket")
public var timer: DispatchSourceTimer?
public var streamContinuation: AsyncStream<String>.Continuation?
public var parser: WebSocketParser = KissWebSocketParser()

View File

@@ -27,7 +27,6 @@ extension Domestic {
public var socketDelegate: URLSessionWebSocketDelegate? { event }
public var message: String = ""
public var queue: DispatchQueue = DispatchQueue(label: "kissme.contact_notice_websocket")
public var timer: DispatchSourceTimer?
public var streamContinuation: AsyncStream<String>.Continuation?
public var parser: WebSocketParser = KissWebSocketParser()

View File

@@ -27,7 +27,6 @@ extension Domestic {
public var socketDelegate: URLSessionWebSocketDelegate? { event }
public var message: String = ""
public var queue: DispatchQueue = DispatchQueue(label: "kissme.contact_price_websocket")
public var timer: DispatchSourceTimer?
public var streamContinuation: AsyncStream<String>.Continuation?
public var parser: WebSocketParser = KissWebSocketParser()

View File

@@ -1,10 +0,0 @@
//
// ForeignStock.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
// TODO: ...

View File

@@ -0,0 +1,247 @@
//
// OverseasStock.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
public struct Overseas {
}
extension Overseas {
/// [v1_-001]
///
public struct StockOrderRequest: OrderRequest {
public typealias KResult = OrderResult
public var url: String {
"/uapi/overseas-stock/v1/trading/order"
}
public var method: Method { .post }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
"tr_cont": isNext ? "N": " ",
"custtype": CustomerType.personal.rawValue,
]
}
public var body: [String: Any] {
[
"CANNO": accountNo8,
"ACNT_PRDT_CD": accountNo2,
"OVRS_EXCG_CD": overseasExchangeCode,
"PDNO": productNo,
"ORD_QTY": String(orderQuantity),
"OVRS_ORD_UNPR": String(orderPrice),
//"CTAC_TLNO": String(contactPhoneNo),
//"MGCO_APTM_ODNO": String(companyGivenOrderNo)
"ORD_SVR_DVSN_CD": "0",
"ORD_DVSN": orderDivision.code,
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
if credential.isMock {
switch overseasExchangeCode {
case .nasdaq, .newyork, .amex: //
switch orderType {
case .buy: return "VTTT1002U"
case .sell: return "VTTT1001U"
}
case .tokyo: //
switch orderType {
case .buy: return "VTTS0308U"
case .sell: return "VTTS0307U"
}
case .shanghai: //
switch orderType {
case .buy: return "VTTS0202U"
case .sell: return "VTTS1005U"
}
case .hongkong: //
switch orderType {
case .buy: return "VTTS1002U"
case .sell: return "VTTS1001U"
}
case .shenzhen: //
switch orderType {
case .buy: return "VTTS0305U"
case .sell: return "VTTS0304U"
}
case .hanoi, .hochiminh: //
switch orderType {
case .buy: return "VTTS0311U"
case .sell: return "VTTS0310U"
}
}
}
else {
switch overseasExchangeCode {
case .nasdaq, .newyork, .amex: //
switch orderType {
case .buy: return "TTTT1002U"
case .sell: return "TTTT1006U"
}
case .tokyo: //
switch orderType {
case .buy: return "TTTS0308U"
case .sell: return "TTTS0307U"
}
case .shanghai: //
switch orderType {
case .buy: return "TTTS0202U"
case .sell: return "TTTS1005U"
}
case .hongkong: //
switch orderType {
case .buy: return "TTTS1002U"
case .sell: return "TTTS1001U"
}
case .shenzhen: //
switch orderType {
case .buy: return "TTTS0305U"
case .sell: return "TTTS0304U"
}
case .hanoi, .hochiminh: //
switch orderType {
case .buy: return "TTTS0311U"
case .sell: return "TTTS0310U"
}
}
}
}
public let accessToken: String
let overseasExchangeCode: OverseasExchangeType
let productNo: String
let orderType: OrderType
let orderDivision: OrderDivision
let orderQuantity: Int
let orderPrice: Int
//let contactPhoneNo: String
let isNext: Bool
public init(credential: Credential, accessToken: String, overseasExchangeCode: OverseasExchangeType, productNo: String, orderType: OrderType, orderDivision: OrderDivision, orderQuantity: Int, orderPrice: Int, isNext: Bool) {
self.credential = credential
self.accessToken = accessToken
self.overseasExchangeCode = overseasExchangeCode
self.productNo = productNo
self.orderType = orderType
self.orderDivision = orderDivision
self.orderQuantity = orderQuantity
self.orderPrice = orderPrice
self.isNext = isNext
}
}
/// [v1_-003]
///
public struct StockOrderRevisionRequest: OrderRequest {
public typealias KResult = OrderRevisionResult
public var url: String {
"/uapi/overseas-stock/v1/trading/order-rvsecncl"
}
public var method: Method { .post }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
"tr_cont": isNext ? "N": " ",
]
}
public var body: [String: Any] {
[
"CANNO": accountNo8,
"ACNT_PRDT_CD": accountNo2,
"OVRS_EXCG_CD": overseasExchangeCode,
"PDNO": productNo,
"ORGN_ODNO": orderNo,
"RVSE_CNCL_DVSN_CD": orderRevisionType.code,
"ORD_QTY": String(orderQuantity),
"OVRS_ORD_UNPR": String(orderPrice),
//"MGCO_APTM_ODNO": String(companyGivenOrderNo)
"ORD_SVR_DVSN_CD": "0",
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
if credential.isMock {
switch overseasExchangeCode {
case .nasdaq, .newyork, .amex: //
return "VTTT1004U"
case .hongkong: //
return "VTTS1003U"
case .tokyo: //
return "VTTS0309U"
case .shanghai: //
return "VTTS0302U"
case .shenzhen: //
return "VTTS0306U"
case .hanoi, .hochiminh: //
return "VTTS0312U"
}
}
else {
switch overseasExchangeCode {
case .nasdaq, .newyork, .amex: //
return "TTTT1004U"
case .hongkong: //
return "TTTS1003U"
case .tokyo: //
return "TTTS0309U"
case .shanghai: //
return "TTTS0302U"
case .shenzhen: //
return "TTTS0306U"
case .hanoi, .hochiminh: //
return "TTTS0312U"
}
}
}
public let accessToken: String
let overseasExchangeCode: OverseasExchangeType
let productNo: String
let orderNo: String
let orderDivision: OrderDivision
let orderRevisionType: OrderRevisionType
let orderQuantity: Int
let orderPrice: Int
let isNext: Bool
public init(credential: Credential, accessToken: String, overseasExchangeCode: OverseasExchangeType, productNo: String, orderNo: String, orderDivision: OrderDivision, orderRevisionType: OrderRevisionType, orderQuantity: Int, orderPrice: Int, isNext: Bool) {
self.credential = credential
self.accessToken = accessToken
self.overseasExchangeCode = overseasExchangeCode
self.productNo = productNo
self.orderNo = orderNo
self.orderDivision = orderDivision
self.orderRevisionType = orderRevisionType
self.orderQuantity = orderQuantity
self.orderPrice = orderPrice
self.isNext = isNext
}
}
}

View File

@@ -7,4 +7,3 @@
import Foundation
// TODO: ...

View File

@@ -0,0 +1,39 @@
//
// OverseasStockResult.swift
// KissMe
//
// Created by ened-book-m1 on 6/21/24.
//
import Foundation
extension Overseas {
/*
public struct OrderResult: Codable {
public let resultCode: String
public let messageCode: String
public let message: String
public let output: OutputDetail?
private enum CodingKeys: String, CodingKey {
case resultCode = "rt_cd"
case messageCode = "msg_cd"
case message = "msg1"
case output
}
public struct OutputDetail: Codable {
public let orderOrganizationNo: String
public let orderNo: String
public let orderTime: String
private enum CodingKeys: String, CodingKey {
case orderOrganizationNo = "KRX_FWDG_ORD_ORGNO"
case orderNo = "ODNO"
case orderTime = "ORD_TMD"
}
}
}
*/
}

View File

@@ -19,9 +19,9 @@
341F5EE12A0F373B00962D48 /* Login.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EE02A0F373B00962D48 /* Login.swift */; };
341F5EE52A0F3EF400962D48 /* DomesticStock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EE42A0F3EF400962D48 /* DomesticStock.swift */; };
341F5EE92A0F87FB00962D48 /* DomesticStockPrice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EE82A0F87FB00962D48 /* DomesticStockPrice.swift */; };
341F5EEC2A0F883900962D48 /* ForeignStock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EEB2A0F883900962D48 /* ForeignStock.swift */; };
341F5EEE2A0F884300962D48 /* ForeignStockPrice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EED2A0F884300962D48 /* ForeignStockPrice.swift */; };
341F5EF02A0F886600962D48 /* ForeignFutures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EEF2A0F886600962D48 /* ForeignFutures.swift */; };
341F5EEC2A0F883900962D48 /* OverseasStock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EEB2A0F883900962D48 /* OverseasStock.swift */; };
341F5EEE2A0F884300962D48 /* OverseasStockPrice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EED2A0F884300962D48 /* OverseasStockPrice.swift */; };
341F5EF02A0F886600962D48 /* OverseasFutures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EEF2A0F886600962D48 /* OverseasFutures.swift */; };
341F5EF22A0F887200962D48 /* DomesticFutures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EF12A0F887200962D48 /* DomesticFutures.swift */; };
341F5EF52A0F891200962D48 /* KissAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EF42A0F891200962D48 /* KissAccount.swift */; };
341F5EF72A0F8B0500962D48 /* DomesticStockResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EF62A0F8B0500962D48 /* DomesticStockResult.swift */; };
@@ -38,6 +38,7 @@
341F5F112A1685E700962D48 /* ShopRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F102A1685E700962D48 /* ShopRequest.swift */; };
341F5F142A16CD7A00962D48 /* DomesticShopProduct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F132A16CD7A00962D48 /* DomesticShopProduct.swift */; };
3435A7F72A35D82000D604F1 /* DomesticShortSelling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3435A7F62A35D82000D604F1 /* DomesticShortSelling.swift */; };
349B05172C25B7C600378D55 /* OverseasStockResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349B05162C25B7C600378D55 /* OverseasStockResult.swift */; };
349C26AB2A1EAE2400F3EC91 /* KissProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349C26AA2A1EAE2400F3EC91 /* KissProfile.swift */; };
349F5D142A6BC8D3009A0526 /* String+Html.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349F5D132A6BC8D3009A0526 /* String+Html.swift */; };
34BC44762A8656570052D8EB /* Domestic.ContractNoticeWebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34BC44752A8656570052D8EB /* Domestic.ContractNoticeWebSocket.swift */; };
@@ -165,9 +166,9 @@
341F5EE02A0F373B00962D48 /* Login.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Login.swift; sourceTree = "<group>"; };
341F5EE42A0F3EF400962D48 /* DomesticStock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DomesticStock.swift; path = "../New Group/DomesticStock.swift"; sourceTree = "<group>"; };
341F5EE82A0F87FB00962D48 /* DomesticStockPrice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticStockPrice.swift; sourceTree = "<group>"; };
341F5EEB2A0F883900962D48 /* ForeignStock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForeignStock.swift; sourceTree = "<group>"; };
341F5EED2A0F884300962D48 /* ForeignStockPrice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForeignStockPrice.swift; sourceTree = "<group>"; };
341F5EEF2A0F886600962D48 /* ForeignFutures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForeignFutures.swift; sourceTree = "<group>"; };
341F5EEB2A0F883900962D48 /* OverseasStock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverseasStock.swift; sourceTree = "<group>"; };
341F5EED2A0F884300962D48 /* OverseasStockPrice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverseasStockPrice.swift; sourceTree = "<group>"; };
341F5EEF2A0F886600962D48 /* OverseasFutures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverseasFutures.swift; sourceTree = "<group>"; };
341F5EF12A0F887200962D48 /* DomesticFutures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticFutures.swift; sourceTree = "<group>"; };
341F5EF42A0F891200962D48 /* KissAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissAccount.swift; sourceTree = "<group>"; };
341F5EF62A0F8B0500962D48 /* DomesticStockResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticStockResult.swift; sourceTree = "<group>"; };
@@ -184,6 +185,7 @@
341F5F102A1685E700962D48 /* ShopRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopRequest.swift; sourceTree = "<group>"; };
341F5F132A16CD7A00962D48 /* DomesticShopProduct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticShopProduct.swift; sourceTree = "<group>"; };
3435A7F62A35D82000D604F1 /* DomesticShortSelling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticShortSelling.swift; sourceTree = "<group>"; };
349B05162C25B7C600378D55 /* OverseasStockResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverseasStockResult.swift; sourceTree = "<group>"; };
349C26AA2A1EAE2400F3EC91 /* KissProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissProfile.swift; sourceTree = "<group>"; };
349F5D132A6BC8D3009A0526 /* String+Html.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Html.swift"; sourceTree = "<group>"; };
34BC44752A8656570052D8EB /* Domestic.ContractNoticeWebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Domestic.ContractNoticeWebSocket.swift; sourceTree = "<group>"; };
@@ -330,7 +332,7 @@
34F1900D2A426C1C0068C697 /* Context */,
34F1900A2A41981A0068C697 /* Index */,
341F5EF32A0F88AC00962D48 /* Common */,
341F5EEA2A0F882300962D48 /* Foreign */,
341F5EEA2A0F882300962D48 /* Overseas */,
341F5EE62A0F3EFB00962D48 /* Domestic */,
341F5EDF2A0F372000962D48 /* Login */,
341F5EAE2A0A80EC00962D48 /* KissMe.h */,
@@ -376,14 +378,15 @@
path = Domestic;
sourceTree = "<group>";
};
341F5EEA2A0F882300962D48 /* Foreign */ = {
341F5EEA2A0F882300962D48 /* Overseas */ = {
isa = PBXGroup;
children = (
341F5EEB2A0F883900962D48 /* ForeignStock.swift */,
341F5EED2A0F884300962D48 /* ForeignStockPrice.swift */,
341F5EEF2A0F886600962D48 /* ForeignFutures.swift */,
341F5EEB2A0F883900962D48 /* OverseasStock.swift */,
349B05162C25B7C600378D55 /* OverseasStockResult.swift */,
341F5EED2A0F884300962D48 /* OverseasStockPrice.swift */,
341F5EEF2A0F886600962D48 /* OverseasFutures.swift */,
);
path = Foreign;
path = Overseas;
sourceTree = "<group>";
};
341F5EF32A0F88AC00962D48 /* Common */ = {
@@ -837,13 +840,14 @@
341F5EF72A0F8B0500962D48 /* DomesticStockResult.swift in Sources */,
34F1900F2A426D150068C697 /* ShopContext.swift in Sources */,
341F5F0F2A15223A00962D48 /* SeibroRequest.swift in Sources */,
341F5EF02A0F886600962D48 /* ForeignFutures.swift in Sources */,
341F5EF02A0F886600962D48 /* OverseasFutures.swift in Sources */,
34F1900C2A41982A0068C697 /* KissIndexResult.swift in Sources */,
341F5EEC2A0F883900962D48 /* ForeignStock.swift in Sources */,
341F5EEC2A0F883900962D48 /* OverseasStock.swift in Sources */,
34F190112A4394EB0068C697 /* LocalContext.swift in Sources */,
349B05172C25B7C600378D55 /* OverseasStockResult.swift in Sources */,
341F5EFF2A10955D00962D48 /* OrderRequest.swift in Sources */,
341F5EE92A0F87FB00962D48 /* DomesticStockPrice.swift in Sources */,
341F5EEE2A0F884300962D48 /* ForeignStockPrice.swift in Sources */,
341F5EEE2A0F884300962D48 /* OverseasStockPrice.swift in Sources */,
34C1BA8A2A5DA00A00423D64 /* DomesticDartMajorReport.swift in Sources */,
34F844A12A683A4200152D98 /* DomesticNews.swift in Sources */,
341F5EDE2A0F300100962D48 /* Request.swift in Sources */,