Files
KissMe/KissMe/Sources/Domestic/Stock/DomesticStock.swift
2024-10-29 02:27:03 +09:00

414 lines
14 KiB
Swift

//
// DomesticStock.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
public struct Domestic {
public typealias Candle = MinutePriceResult.OutputPrice
public typealias CandlePeriod = PeriodPriceResult.OutputPrice
public typealias Top = VolumeRankResult.OutputDetail
public typealias CurrentPrice = CurrentPriceResult.OutputDetail
public typealias Investor = InvestorVolumeResult.OutputDetail
}
extension Domestic {
/// - ()
///
public struct StockOrderRequest: OrderRequest {
public typealias KResult = OrderResult
public var url: String { "/uapi/domestic-stock/v1/trading/order-cash" }
public var method: Method { .post }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
]
}
public var body: [String: Any] {
[
"CANO": accountNo8,
"ACNT_PRDT_CD": accountNo2,
"PDNO": productNo,
"ORD_DVSN": orderDivision.code,
"ORD_QTY": String(orderQuantity),
"ORD_UNPR": String(orderPrice),
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
if credential.isMock {
switch orderType {
case .buy: return "VTTC0802U"
case .sell: return "VTTC0801U"
}
}
else {
switch orderType {
case .buy: return "TTTC0802U"
case .sell: return "TTTC0801U"
}
}
}
public let accessToken: String
let productNo: String
let orderType: OrderType
let orderDivision: OrderDivision
let orderQuantity: Int
let orderPrice: Int
public init(credential: Credential, accessToken: String, contract: Contract) {
self.credential = credential
self.accessToken = accessToken
self.productNo = contract.productNo
self.orderType = contract.orderType
self.orderDivision = contract.orderDivision
self.orderQuantity = contract.orderQuantity
self.orderPrice = contract.orderPrice
}
}
/// - ()[v1_-003]
///
public struct StockOrderRevisionRequest: OrderRequest {
public typealias KResult = OrderRevisionResult
public var url: String {
"/uapi/domestic-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,
]
}
public var body: [String: Any] {
[
"CANO": accountNo8,
"ACNT_PRDT_CD": accountNo2,
"KRX_FWDG_ORD_ORGNO": orderOrganizationNo,
"ORGN_ODNO": orderNo,
"ORD_DVSN": orderDivision.code,
"RVSE_CNCL_DVSN_CD": orderRevisionType.code,
"ORD_QTY": String(orderQuantity),
"ORD_UNPR": String(orderPrice),
"QTY_ALL_ORD_YN": isAllQuantity ? "Y": "N"
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
if credential.isMock {
return "VTTC0803U"
}
else {
return "TTTC0803U"
}
}
public let accessToken: String
let productNo: String
let orderOrganizationNo: String
let orderNo: String
let orderDivision: OrderDivision
let orderRevisionType: OrderRevisionType
let orderQuantity: Int
let orderPrice: Int
let isAllQuantity: Bool
public init(credential: Credential, accessToken: String, productNo: String, orderOrganizationNo: String, orderNo: String, orderDivision: OrderDivision, orderRevisionType: OrderRevisionType, orderQuantity: Int, orderPrice: Int) {
self.credential = credential
self.accessToken = accessToken
self.productNo = productNo
self.orderOrganizationNo = orderOrganizationNo
self.orderNo = orderNo
self.orderDivision = orderDivision
self.orderRevisionType = orderRevisionType
self.orderQuantity = orderQuantity
self.orderPrice = orderPrice
self.isAllQuantity = (orderQuantity == 0)
}
}
/// - [v1_-006]
///
public struct StockBalanceRequest: OrderRequest {
public typealias KResult = BalanceResult
public var url: String {
"/uapi/domestic-stock/v1/trading/inquire-balance"
}
public var method: Method { .get }
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] {
[
"CANO": accountNo8,
"ACNT_PRDT_CD": accountNo2,
"AFHR_FLPR_YN": "N",
"OFL_YN": " ",
"INQR_DVSN": "02",
"UNPR_DVSN": "01",
"FUND_STTL_ICLD_YN": "N",
"FNCG_AMT_AUTO_RDPT_YN": "N",
"PRCS_DVSN": "01",
"CTX_AREA_FK100": " ",
"CTX_AREA_NK100": " ",
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
if credential.isMock {
return "VTTC8434R"
}
else {
return "TTTC8434R"
}
}
public let accessToken: String
let isNext: Bool
public init(credential: Credential, accessToken: String, isNext: Bool) {
self.credential = credential
self.accessToken = accessToken
self.isNext = isNext
}
}
/// - [v1_-007]
///
public struct StockPossibleOrderRequest: OrderRequest {
public typealias KResult = PossibleOrderResult
public var url: String {
"/uapi/domestic-stock/v1/trading/inquire-psbl-order"
}
public var method: Method { .get }
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] {
[
"CANO": accountNo8,
"ACNT_PRDT_CD": accountNo2,
"PDNO": productNo,
"ORD_UNPR": String(orderPrice),
"ORD_DVSN": orderDivision.code,
"CMA_EVLU_AMT_ICLD_YN": "N",
"OVRS_ICLD_YN": "N",
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
if credential.isMock {
return "VTTC8908R"
}
else {
return "TTTC8908R"
}
}
public let accessToken: String
let productNo: String
let orderDivision: OrderDivision
let orderPrice: Int
let isNext: Bool
public init(credential: Credential, accessToken: String, productNo: String, orderDivision: OrderDivision, orderPrice: Int, isNext: Bool) {
self.credential = credential
self.accessToken = accessToken
self.productNo = productNo
self.orderDivision = orderDivision
self.orderPrice = orderPrice
self.isNext = isNext
}
}
}
public struct Contract {
public let productNo: String
public let orderType: OrderType
public let orderDivision: OrderDivision
public let orderQuantity: Int
public let orderPrice: Int
public init(productNo: String, orderType: OrderType, orderDivision: OrderDivision, orderQuantity: Int, orderPrice: Int) {
self.productNo = productNo
self.orderType = orderType
self.orderDivision = orderDivision
self.orderQuantity = orderQuantity
self.orderPrice = orderPrice
}
}
public struct ContractCancel {
public let productNo: String
public let orderNo: String
// 0
public let orderQuantity: Int
public init(productNo: String, orderNo: String, orderQuantity: Int) {
self.productNo = productNo
self.orderNo = orderNo
self.orderQuantity = orderQuantity
}
}
// MARK: Stock Order
extension KissAccount {
///
///
public func orderStock(contract: Contract) async throws -> Domestic.OrderResult {
return try await withUnsafeThrowingContinuation { continuation in
guard let accessToken = accessToken else {
continuation.resume(throwing: GeneralError.invalidAccessToken)
return
}
let request = Domestic.StockOrderRequest(credential: credential, accessToken: accessToken, contract: contract)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
public func cancelOrder(cancel: ContractCancel) async throws -> Domestic.OrderRevisionResult {
return try await withUnsafeThrowingContinuation { continuation in
guard let accessToken = accessToken else {
continuation.resume(throwing: GeneralError.invalidAccessToken)
return
}
let request = Domestic.StockOrderRevisionRequest(credential: credential, accessToken: accessToken, productNo: cancel.productNo, orderOrganizationNo: "", orderNo: cancel.orderNo, orderDivision: .limits, orderRevisionType: .cancel, orderQuantity: cancel.orderQuantity, orderPrice: 0)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
public func changeOrder() async throws -> Bool {
return try await withUnsafeThrowingContinuation { continuation in
guard let _ = accessToken else {
continuation.resume(throwing: GeneralError.invalidAccessToken)
return
}
// TODO: work
}
}
///
///
public func getStockBalance() async throws -> Domestic.BalanceResult {
return try await withUnsafeThrowingContinuation { continuation in
guard let accessToken = accessToken else {
continuation.resume(throwing: GeneralError.invalidAccessToken)
return
}
let request = Domestic.StockBalanceRequest(credential: credential, accessToken: accessToken, isNext: false)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
///
///
public func canOrderStock(productNo: String, division: OrderDivision, price: Int) async throws -> Domestic.PossibleOrderResult {
return try await withUnsafeThrowingContinuation { continuation in
guard let accessToken = accessToken else {
continuation.resume(throwing: GeneralError.invalidAccessToken)
return
}
let request = Domestic.StockPossibleOrderRequest(credential: credential, accessToken: accessToken, productNo: productNo, orderDivision: division, orderPrice: price, isNext: false)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}