Files
KissMe/KissMe/Sources/Domestic/Stock/DomesticStockSearch.swift

397 lines
12 KiB
Swift

//
// DomesticStockSearch.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/14.
//
import Foundation
public enum DivisionClassCode: String, Codable {
///
case all = "0"
///
case equity = "1"
///
case preferredStock = "2"
}
public enum WeekdayDivision: String, Codable {
///
case sunday = "01"
///
case monday = "02"
///
case tueday = "03"
///
case wednesday = "04"
///
case thursday = "05"
///
case friday = "06"
///
case saturday = "07"
}
public enum BelongClassCode: String, CustomStringConvertible {
///
case averageVolume = "0"
///
case volumeIncreaseRate = "1"
///
case averageVolumeTurnoverRate = "2"
///
case transactionValue = "3"
///
case averageTransactionValueTurnoverRate = "4"
public var description: String {
switch self {
case .averageVolume: return "0:평균거래량"
case .volumeIncreaseRate: return "1:거래증가율"
case .averageVolumeTurnoverRate: return "2:평균거래회전율"
case .transactionValue: return "3:거래금액순"
case .averageTransactionValueTurnoverRate: return "4:평균거래금액회전율"
}
}
public var fileBelong: String {
switch self {
case .averageVolume: return "av"
case .volumeIncreaseRate: return "vir"
case .averageVolumeTurnoverRate: return "avtr"
case .transactionValue: return "tv"
case .averageTransactionValueTurnoverRate: return "atvtr"
}
}
}
///
public enum MarketDivisionCode: String, Codable {
///
case all = "0000"
///
case kospi = "0001"
///
case kosdaq = "1001"
// TODO: ()
// https://apiportal.koreainvestment.com/community/10000000-0000-0011-0000-000000000002
}
extension Domestic {
/// - [v1_-047]
///
public struct StockVolumeRankRequest: TokenRequest {
public typealias KResult = VolumeRankResult
public var isMockAvailable: Bool {
credential.isMock == false
}
public var url: String {
"/uapi/domestic-stock/v1/quotations/volume-rank"
}
public var method: Method { .get }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
"custtype": CustomerType.personal.rawValue,
]
}
public var body: [String: Any] {
[
"FID_COND_MRKT_DIV_CODE": "J",
"FID_COND_SCR_DIV_CODE": "20171",
"FID_INPUT_ISCD": MarketDivisionCode.all.rawValue,
"FID_DIV_CLS_CODE": divisionClass.rawValue,
"FID_BLNG_CLS_CODE": belongClass.rawValue,
"FID_TRGT_CLS_CODE": "000000000",
"FID_TRGT_EXLS_CLS_CODE": "000000",
"FID_INPUT_PRICE_1": "",
"FID_INPUT_PRICE_2": "1000000",
"FID_VOL_CNT": "",
"FID_INPUT_DATE_1": "",
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
"FHPST01710000"
}
public let accessToken: String
let divisionClass: DivisionClassCode
let belongClass: BelongClassCode
public init(credential: Credential, accessToken: String, divisionClass: DivisionClassCode, belongClass: BelongClassCode) {
self.credential = credential
self.accessToken = accessToken
self.divisionClass = divisionClass
self.belongClass = belongClass
}
}
/// -
///
public struct HolidayRequest: OrderRequest {
public typealias KResult = HolidyResult
public var url: String { "/uapi/domestic-stock/v1/quotations/chk-holiday" }
public var method: Method { .get }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
"custtype": CustomerType.personal.rawValue,
]
}
public var body: [String: Any] {
[
"BASS_DT": baseDate,
"CTX_AREA_NK": " ",
"CTX_AREA_FK": " ",
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
"CTCA0903R"
}
public let accessToken: String
let baseDate: String /// yyyyMMdd
public init(credential: Credential, accessToken: String, baseDate: String) {
self.credential = credential
self.accessToken = accessToken
self.baseDate = baseDate
}
}
/// - [v1_-012]
///
public struct InvestorVolumeRequest: OrderRequest {
public typealias KResult = InvestorVolumeResult
public var url: String { "/uapi/domestic-stock/v1/quotations/inquire-investor" }
public var method: Method { .get }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
"custtype": CustomerType.personal.rawValue,
]
}
public var body: [String: Any] {
[
"FID_COND_MRKT_DIV_CODE": "J",
"FID_INPUT_ISCD": productNo,
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
"FHKST01010900"
}
public let accessToken: String
let productNo: String
public init(credential: Credential, accessToken: String, productNo: String) {
self.credential = credential
self.accessToken = accessToken
self.productNo = productNo
}
}
/// - _ [-037]
///
public struct ForeignOrganizationVolumeRequest: OrderRequest {
public typealias KResult = ForeignOrganizationVolumeResult
public var isMockAvailable: Bool {
credential.isMock == false
}
public var url: String { "/uapi/domestic-stock/v1/quotations/foreign-institution-total" }
public var method: Method { .get }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
"custtype": CustomerType.personal.rawValue,
]
}
public var body: [String: Any] {
[
"FID_COND_MRKT_DIV_CODE": "V",
"FID_COND_SCR_DIV_CODE": "16449",
"FID_INPUT_ISCD": marketDivision.rawValue,
"FID_DIV_CLS_CODE": order.rawValue,
"FID_RANK_SORT_CLS_CODE": tradeType.rawValue,
"FID_ETC_CLS_CODE": target.rawValue,
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
"FHPTJ04400000"
}
public let accessToken: String
let marketDivision: MarketDivisionCode
let order: Order
let tradeType: TradeType
let target: Target
///
public enum Order: String {
///
case volume = "0"
///
case amount = "1"
}
///
public enum TradeType: String {
///
case topBuying = "0"
///
case topSelling = "1"
}
///
public enum Target: String {
///
case all = "0"
///
case foreigner = "1"
///
case institution = "2"
///
case etc = "3"
}
public init(credential: Credential, accessToken: String, marketDivision: MarketDivisionCode, order: Order, tradeType: TradeType, target: Target) {
self.credential = credential
self.accessToken = accessToken
self.marketDivision = marketDivision
self.order = order
self.tradeType = tradeType
self.target = target
}
}
}
public struct RankingOption {
public let divisionClass: DivisionClassCode
public let belongClass: BelongClassCode
public init(divisionClass: DivisionClassCode, belongClass: BelongClassCode) {
self.divisionClass = divisionClass
self.belongClass = belongClass
}
}
// MARK: Stock Search
extension KissAccount {
///
///
public func getVolumeRanking(option: RankingOption) async throws -> VolumeRankResult {
return try await withUnsafeThrowingContinuation { continuation in
guard let accessToken = accessToken else {
continuation.resume(throwing: GeneralError.invalidAccessToken)
return
}
let request = Domestic.StockVolumeRankRequest(credential: credential, accessToken: accessToken, divisionClass: option.divisionClass, belongClass: option.belongClass)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
///
///
public func getHolyday(baseDate: String) async throws -> HolidyResult {
return try await withUnsafeThrowingContinuation { continuation in
guard let accessToken = accessToken else {
continuation.resume(throwing: GeneralError.invalidAccessToken)
return
}
let request = Domestic.HolidayRequest(credential: credential, accessToken: accessToken, baseDate: baseDate)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
///
///
public func getInvestorVolume(productNo: String) async throws -> InvestorVolumeResult {
return try await withUnsafeThrowingContinuation { continuation in
guard let accessToken = accessToken else {
continuation.resume(throwing: GeneralError.invalidAccessToken)
return
}
let request = Domestic.InvestorVolumeRequest(credential: credential, accessToken: accessToken, productNo: productNo)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}