Implement "shorts all resume" command
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 를 표시함.
|
||||
|
||||
2
bin/data
2
bin/data
Submodule bin/data updated: 1117afa184...659b8ff169
@@ -1,2 +1,16 @@
|
||||
# KMI-0002
|
||||
|
||||
## How to
|
||||
|
||||
공매도 거래량의 변화와 잔고를 분석하여, 변화를 감지하면 매수/매도 성향의 추천을 제공합니다.
|
||||
|
||||
공매도 거래량 정보는 다음의 API 를 통해서 얻고 있습니다.
|
||||
|
||||
*
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
현재 여기에는 환경설정 정보가 없습니다.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user