397 lines
12 KiB
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|