Implement love command
This commit is contained in:
@@ -31,10 +31,3 @@ extension Date {
|
||||
ProcessInfo.processInfo.systemUptime
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Array {
|
||||
func write(toCSV: URL) where Element == Encodable {
|
||||
// TODO: later
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ extension KissAccount {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: work
|
||||
// TODO: 현재 시세 정보를 가져오기
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,6 @@ extension KissAccount {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: work
|
||||
// TODO: 분봉 정보를 가져오기
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
// TODO: WORK
|
||||
// TODO: 선물은 나중에...
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
// TODO: WORK
|
||||
// TODO: 해외주식은 나중에...
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
// TODO: WORK
|
||||
// TODO: 해외주식은 나중에...
|
||||
|
||||
@@ -78,9 +78,13 @@ extension KissProfile {
|
||||
}
|
||||
|
||||
public struct Love: Codable {
|
||||
let key: String
|
||||
let iscd: String
|
||||
let name: String
|
||||
public let isin: String
|
||||
public let name: String
|
||||
|
||||
public init(isin: String, name: String) {
|
||||
self.isin = isin
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
|
||||
private var profileJsonUrl: URL {
|
||||
@@ -126,16 +130,44 @@ extension KissProfile {
|
||||
}
|
||||
}
|
||||
|
||||
public func removeLove(_ love: Love, for key: String) -> Bool {
|
||||
public func removeLove(isin: String, for key: String) -> KissProfile.Love? {
|
||||
profileLock.lock()
|
||||
defer {
|
||||
profileLock.unlock()
|
||||
}
|
||||
|
||||
if let index = profile.loves[key]?.firstIndex(where: { $0.iscd == love.iscd }) {
|
||||
profile.loves[key]?.remove(at: index)
|
||||
if let index = profile.loves[key]?.firstIndex(where: { $0.isin == isin }) {
|
||||
return profile.loves[key]?.remove(at: index)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func setLove(_ love: Love, index: Int, for key: String) -> Bool {
|
||||
profileLock.lock()
|
||||
defer {
|
||||
profileLock.unlock()
|
||||
}
|
||||
|
||||
if let array = profile.loves[key], index < array.count {
|
||||
profile.loves[key]?[index] = love
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public func getLoves() -> [String: [Love]] {
|
||||
profileLock.lock()
|
||||
defer {
|
||||
profileLock.unlock()
|
||||
}
|
||||
return profile.loves
|
||||
}
|
||||
|
||||
public func getLoves(for key: String) -> [Love]? {
|
||||
profileLock.lock()
|
||||
defer {
|
||||
profileLock.unlock()
|
||||
}
|
||||
return profile.loves[key]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +172,15 @@ extension KissConsole {
|
||||
//return products.filter { $0.key.decomposedStringWithCanonicalMapping.contains(similarName) }
|
||||
}
|
||||
|
||||
private func getProductName(isin: String) -> String? {
|
||||
productsLock.lock()
|
||||
defer {
|
||||
productsLock.unlock()
|
||||
}
|
||||
|
||||
return products.compactMap { $0.value.first(where: { $0.isinCode == isin })?.itemName }.first
|
||||
}
|
||||
|
||||
private func loadShop(_ profile: Bool = false) {
|
||||
let appTime1 = Date.appTime
|
||||
guard let stringCsv = try? String(contentsOfFile: shopProducts.path) else {
|
||||
@@ -290,9 +299,13 @@ extension KissConsole {
|
||||
|
||||
|
||||
private func onBuy(_ args: [String]) async {
|
||||
guard args.count == 3,
|
||||
let price = Int(args[1]),
|
||||
let quantity = Int(args[2]) else {
|
||||
guard args.count == 3 else {
|
||||
return
|
||||
}
|
||||
guard let price = Int(args[1]) else {
|
||||
return
|
||||
}
|
||||
guard let quantity = Int(args[2]) else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -308,6 +321,8 @@ extension KissConsole {
|
||||
orderQuantity: quantity, orderPrice: price)
|
||||
do {
|
||||
let result = try await account?.orderStock(contract: contract)
|
||||
|
||||
// TODO: 매수처리 이후
|
||||
print(result)
|
||||
} catch {
|
||||
print("\(error)")
|
||||
@@ -446,17 +461,83 @@ extension KissConsole {
|
||||
|
||||
|
||||
private func onLoves() async {
|
||||
|
||||
guard let account = account else { return }
|
||||
let loves = account.getLoves()
|
||||
for tab in loves.sorted(by: { $0.key < $1.key }) {
|
||||
printLoveTab(tab.key, loves: tab.value)
|
||||
}
|
||||
}
|
||||
|
||||
private func printLoveTab(_ key: String, loves: [KissProfile.Love]) {
|
||||
print("Page: \(key)")
|
||||
for item in loves {
|
||||
print(" \(item.isin) \(item.name)")
|
||||
}
|
||||
}
|
||||
|
||||
private func onLove(_ args: [String]) async {
|
||||
guard let account = account else { return }
|
||||
guard args.count > 0 else {
|
||||
print("Invalid love\nlove ")
|
||||
return
|
||||
}
|
||||
|
||||
if args.count == 1 {
|
||||
let key = args[0]
|
||||
guard let loves = account.getLoves(for: key) else { return }
|
||||
printLoveTab(key, loves: loves)
|
||||
}
|
||||
else if args.count == 2 {
|
||||
let keys = args[0].split(separator: ".").map { String($0) }
|
||||
let isin = args[1]
|
||||
if keys.count == 2 {
|
||||
/// key 탭의 index 위치에 Love 를 추가
|
||||
///
|
||||
let key = keys[0]
|
||||
let index = keys[1]
|
||||
if let loves = account.getLoves(for: key), let index = Int(index) {
|
||||
if index < loves.count {
|
||||
guard let name = getProductName(isin: isin) else {
|
||||
print("No product about isin: \(isin)")
|
||||
return
|
||||
}
|
||||
if account.setLove(KissProfile.Love(isin: isin, name: name), index: index, for: key) {
|
||||
print("Success \(name)")
|
||||
account.saveProfile()
|
||||
}
|
||||
else {
|
||||
print("Invalid index: \(index) for \(key)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/// key 탭의 맨뒤에 Love 를 추가
|
||||
///
|
||||
guard let name = getProductName(isin: isin) else {
|
||||
print("No product about isin: \(isin)")
|
||||
return
|
||||
}
|
||||
account.addLove(KissProfile.Love(isin: isin, name: name), for: keys[0])
|
||||
print("Success \(name)")
|
||||
account.saveProfile()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func onHate(_ args: [String]) async {
|
||||
guard let account = account else { return }
|
||||
guard args.count > 1 else {
|
||||
print("Invalid hate")
|
||||
return
|
||||
}
|
||||
|
||||
let key = args[0]
|
||||
let isin = args[1]
|
||||
if let love = account.removeLove(isin: isin, for: key) {
|
||||
print("Success \(love.name)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
README.md
12
README.md
@@ -13,8 +13,8 @@ KissMeConsole 에서 유효한 command line 명령어는 다음과 같습니다.
|
||||
command | 설명
|
||||
------- | ---
|
||||
`quit` | 종료
|
||||
`login mock` | Mock 서버로 로그인, mock-server.json 을 credential 로 사용.
|
||||
`login real` | Real 서버로 로그인, real-server.json 을 credential 로 사용.
|
||||
`login mock` | Mock 서버로 로그인. mock-server.json 을 credential 로 사용.
|
||||
`login real` | Real 서버로 로그인. real-server.json 을 credential 로 사용.
|
||||
`logout` | 접속한 서버에서 로그아웃
|
||||
`top` | 상위 거래량 30종목 (평균거래량)
|
||||
WIP `buy (ISCD) (수량)` | 구매
|
||||
@@ -22,12 +22,12 @@ WIP `sell (ISCD) (수량)` | 판매
|
||||
WIP `cancel (ISCD)` | 주문 취소
|
||||
`open bag` | 보유 종목 열람
|
||||
`load shop` | data/shop-products.csv 로부터 전체 상품을 로딩
|
||||
`update shop` | 금융위원회_KRX상장종목정보 로부터 전체 상품을 얻어서 data/shop-products.csv 로 저장
|
||||
`update shop` | **금융위원회_KRX상장종목정보** 로부터 전체 상품을 얻어서 data/shop-products.csv 로 저장
|
||||
`look (상품명)` | (상품명) 에 해당되는 ISCD 를 표시함
|
||||
WIP `showcase` | 추천 상품을 제안함
|
||||
WIP `loves` | 관심 종목 전체를 열람. profile.json 에 저장된 관심 종목을 표시함.
|
||||
WIP `love (탭).(번호) (ISCD)` | 관심 종목에 추가함. (번호) 를 지정하지 않으면 (탭) 마지막에 추가함.
|
||||
WIP `hate (탭).(번호) (ISCD)` | 관심 종목에서 삭제함.
|
||||
`loves` | 관심 종목 전체를 열람. profile.json 에 저장된 관심 종목을 표시함.
|
||||
`love (탭).(번호) (ISCD)` | 관심 종목에 추가함. (번호) 를 지정하지 않으면 (탭) 마지막에 추가함.
|
||||
`hate (탭) (ISCD)` | 관심 종목에서 삭제함.
|
||||
|
||||
|
||||
# KissCredential
|
||||
|
||||
Reference in New Issue
Block a user