Implement "shorts all resume" command

This commit is contained in:
2023-06-20 23:22:02 +09:00
parent f66375d39c
commit f28f6277c4
7 changed files with 94 additions and 11 deletions

View File

@@ -9,6 +9,13 @@ import Foundation
extension Date {
public var yyyyMM: String {
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "KST")
dateFormatter.dateFormat = "yyyyMM"
return dateFormatter.string(from: self)
}
public var yyyyMMdd: String {
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "KST")
@@ -199,6 +206,17 @@ public func valueToString(_ any: Any) -> String {
extension Array where Element: PropertyIterable {
public func mergeCsv(toFile file: URL, merging: (_ this: [Element], _ file: [Element]) -> [Element], localized: Bool) throws where Element: ArrayDecodable {
guard file.isFileExists == true else {
try writeCsv(toFile: file, localized: localized)
return
}
let oldData = try Self.readCsv(fromFile: file, verifyHeader: true)
let finalData = merging(self, oldData)
try finalData.writeCsv(toFile: file, localized: localized)
}
public func writeCsv(toFile file: URL, appendable: Bool = false, localized: Bool) throws {
if appendable, file.isFileExists == true {
try appendAtEnd(ofCsv: file)

View File

@@ -65,14 +65,46 @@ extension KissConsole {
print("\t주식 단축 종목코드", output.shortProductCode)
}
func getShorts(productNo: String) async throws -> Bool {
func getShortsLastDate(productNo: String) -> Date? {
let startDate = Date().addingTimeInterval(-365 * SecondsForOneDay)
var backDate = Date()
/// -29 , csv .
while startDate < backDate {
let day = backDate.yyyyMM + "01"
let fileUrl = KissConsole.shortsFileUrl(productNo: productNo, day: day)
guard let _ = fileUrl.isFileExists else {
backDate = backDate.addingTimeInterval(-29 * SecondsForOneDay)
continue
}
guard let shorts = try? [DomesticExtra.Shorts].readCsv(fromFile: fileUrl) else {
return Date.date(yyyyMMdd: day, HHmmss: "000000")
}
guard let (yyyy, mm, dd) = shorts.first?.stockBusinessDate.yyyyMMdd else {
return Date.date(yyyyMMdd: day, HHmmss: "000000")
}
guard let newDate = backDate.changing(year: yyyy, month: mm, day: dd) else {
return Date.date(yyyyMMdd: day, HHmmss: "000000")
}
return newDate.addingTimeInterval(SecondsForOneDay)
}
return nil
}
func getShorts(productNo: String, startAt: Date?) async throws -> Bool {
guard let product = getProduct(shortCode: productNo) else {
print("Invalid product \(productNo)")
return false
}
let endDate = Date()
let startDate = endDate.addingTimeInterval(-365 * SecondsForOneDay)
let startDate: Date
if let startAt = startAt {
startDate = startAt
}
else {
startDate = endDate.addingTimeInterval(-365 * SecondsForOneDay)
}
print("Getting \(product.isinCode), \(startDate.yyyyMMdd) ~ \(endDate.yyyyMMdd)")
let result = try await KissAccount.getShortSellingBalance(isinCode: product.isinCode, startDate: startDate, endDate: endDate)
@@ -97,7 +129,16 @@ extension KissConsole {
}
let fileUrl = KissConsole.shortsFileUrl(productNo: productNo, day: month+"01")
try descBlock.writeCsv(toFile: fileUrl, localized: false)
try descBlock.mergeCsv(toFile: fileUrl, merging: { this, file in
var merged = this
for old in file {
if nil == this.first(where: { $0.stockBusinessDate == old.stockBusinessDate }) {
merged.append(old)
}
}
merged.sort(by: { $0.stockBusinessDate > $1.stockBusinessDate })
return merged
}, localized: false)
}
}
try await Task.sleep(nanoseconds: 1_000_000_000 / PreferredShortsTPS)

View File

@@ -656,7 +656,7 @@ extension KissConsole {
let semaphore = DispatchSemaphore(value: 0)
Task {
await KissContext.shared.update(resuming: false)
if args.count == 1, args[0] == "resume" {
if args.count == 1, args[0].lowercased() == "resume" {
await KissContext.shared.update(resuming: true)
}
semaphore.signal()
@@ -854,7 +854,7 @@ extension KissConsole {
return
}
do {
_ = try await getShorts(productNo: productNo)
_ = try await getShorts(productNo: productNo, startAt: nil)
} catch {
print(error)
}
@@ -867,7 +867,15 @@ extension KissConsole {
let semaphore = DispatchSemaphore(value: 0)
Task {
do {
let success = try await getShorts(productNo: item.shortCode)
let startAt: Date?
if args.count == 1, args[0].lowercased() == "resume" {
startAt = getShortsLastDate(productNo: item.shortCode)
}
else {
startAt = nil
}
let success = try await getShorts(productNo: item.shortCode, startAt: startAt)
print("DONE \(success)")
} catch {
print(error)

View File

@@ -35,7 +35,7 @@ WIP `modify (PNO) (ONO) (가격) (수량)` | 주문 내역을 변경. (수량)
`investor [PNO]` | 종목의 투자자 거래량 열람. PNO 은 생략 가능. **data/(PNO)/investor/investor-(yyyyMMdd).csv** 파일로 저장.
`investor all` | 모든 종목의 투자자 거래량 열람. **data/(PNO)/investor/investor-(yyyyMMdd).csv** 파일로 저장.
`shorts [PNO]` | 공매도 잔고를 열람. PNO 은 생략 가능. **data/shorts/(yyyy)/shorts-(yyyyMMdd).csv** 파일로 저장.
`shorts all` | 모든 종목의 공매도 잔고를 열람. **data/shorts/(yyyy)/shorts-(yyyyMMdd).csv** 파일로 저장.
`shorts all [resume]` | 모든 종목의 공매도 잔고를 열람. **data/shorts/(yyyy)/shorts-(yyyyMMdd).csv** 파일로 저장.
`load shop` | data/shop-products.csv 로부터 전체 상품을 로딩.
`update shop` | **금융위원회_KRX상장종목정보** 로부터 전체 상품을 얻어서 **data/shop-products.csv** 로 저장.
`look (상품명)` | (상품명) 에 해당되는 PNO 를 표시함.

View File

@@ -1,2 +1,16 @@
# KMI-0002
## How to
공매도 거래량의 변화와 잔고를 분석하여, 변화를 감지하면 매수/매도 성향의 추천을 제공합니다.
공매도 거래량 정보는 다음의 API 를 통해서 얻고 있습니다.
*
## Usage
## Configuration
현재 여기에는 환경설정 정보가 없습니다.

View File

@@ -5,7 +5,9 @@
## $ ./scripts/build.sh
##
./build_any.sh KissMeConsole
#./build_any.sh KissMeBatch
#./build_any.sh KissGram
THIS_PATH=`dirname "$0"`
${THIS_PATH}/build_any.sh KissMeConsole
#${THIS_PATH}/build_any.sh KissMeBatch
#${THIS_PATH}/build_any.sh KissGram