Split investor.csv by monthly data
This commit is contained in:
@@ -21,6 +21,9 @@ let PreferredShortsTPS: UInt64 = 5
|
||||
/// Limit to request a top query
|
||||
let PreferredTopTPS: UInt64 = 5
|
||||
|
||||
/// Limit to request a investor query
|
||||
let PreferredInvestorTPS: UInt64 = 19
|
||||
|
||||
/// How many seconds does 1 day have?
|
||||
let SecondsForOneDay: TimeInterval = 60 * 60 * 24
|
||||
|
||||
|
||||
@@ -13,18 +13,39 @@ extension KissConsole {
|
||||
func getInvestor(productNo: String) async throws -> Bool {
|
||||
let result = try await account!.getInvestorVolume(productNo: productNo)
|
||||
if let output = result.output {
|
||||
print(output.count)
|
||||
print("Total output: \(output.count) productNo: \(productNo)")
|
||||
|
||||
guard let recentDay = output.first?.stockBusinessDate else {
|
||||
print("No investor items")
|
||||
return false
|
||||
var months = [String: [InvestorVolumeResult.OutputDetail]]()
|
||||
for item in output {
|
||||
let yyyyMM = String(item.stockBusinessDate.prefix(6))
|
||||
if let _ = months[yyyyMM] {
|
||||
months[yyyyMM]!.append(item)
|
||||
}
|
||||
else {
|
||||
months[yyyyMM] = [item]
|
||||
}
|
||||
}
|
||||
|
||||
let fileUrl = KissConsole.investorFileUrl(productNo: productNo, day: recentDay)
|
||||
try output.writeCsv(toFile: fileUrl, localized: localized)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 / PreferredCandleTPS)
|
||||
for (month, items) in months {
|
||||
let descItems = items.sorted(by: { $0.stockBusinessDate > $1.stockBusinessDate })
|
||||
guard descItems.count > 0 else {
|
||||
continue
|
||||
}
|
||||
|
||||
let fileUrl = KissConsole.investorFileUrl(productNo: productNo, day: month+"01")
|
||||
try descItems.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: localized)
|
||||
}
|
||||
}
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 / PreferredInvestorTPS)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ extension KissConsole {
|
||||
|
||||
if let outBlock = result.outBlock {
|
||||
print("Total block: \(outBlock.count) productNo: \(productNo)")
|
||||
|
||||
var monthBlock = [String: [DomesticExtra.Shorts]]()
|
||||
for block in outBlock {
|
||||
let yyyyMM = String(block.stockBusinessDate.prefix(6))
|
||||
|
||||
@@ -252,3 +252,63 @@ private func update_candle_csv_header_field() {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
private func split_investor_csv() {
|
||||
guard let enumerator = FileManager.subPathFiles("data") else {
|
||||
return
|
||||
}
|
||||
|
||||
var urls = [URL]()
|
||||
for case let fileUrl as URL in enumerator {
|
||||
guard fileUrl.pathExtension == "csv" else {
|
||||
continue
|
||||
}
|
||||
if fileUrl.lastPathComponent.prefix(9) == "investor-" {
|
||||
urls.append(fileUrl)
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
for url in urls {
|
||||
let investorDir = url.deletingLastPathComponent()
|
||||
let productNoDir = investorDir.deletingLastPathComponent()
|
||||
let productNo = productNoDir.lastPathComponent
|
||||
|
||||
let output = try [InvestorVolumeResult.OutputDetail].readCsv(fromFile: url, verifyHeader: true)
|
||||
|
||||
var months = [String: [InvestorVolumeResult.OutputDetail]]()
|
||||
for item in output {
|
||||
let yyyyMM = String(item.stockBusinessDate.prefix(6))
|
||||
if let _ = months[yyyyMM] {
|
||||
months[yyyyMM]!.append(item)
|
||||
}
|
||||
else {
|
||||
months[yyyyMM] = [item]
|
||||
}
|
||||
}
|
||||
|
||||
for (month, items) in months {
|
||||
let descItems = items.sorted(by: { $0.stockBusinessDate > $1.stockBusinessDate })
|
||||
guard descItems.count > 0 else {
|
||||
continue
|
||||
}
|
||||
|
||||
let fileUrl = KissConsole.investorFileUrl(productNo: productNo, day: month+"01")
|
||||
try descItems.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 FileManager.default.removeItem(at: url)
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ extension KissIndex {
|
||||
}
|
||||
return false
|
||||
}
|
||||
print(shorts.count)
|
||||
//print(shorts.count)
|
||||
|
||||
let prices = try await collectPrices(date: date) { price in
|
||||
if let quantity = Int(price.lastShortSellingConclusionQuantity), quantity > 0 {
|
||||
@@ -36,10 +36,10 @@ extension KissIndex {
|
||||
}
|
||||
return false
|
||||
}
|
||||
print(prices.count)
|
||||
//print(prices.count)
|
||||
|
||||
} catch {
|
||||
print(error)
|
||||
//print(error)
|
||||
writeError(error, kmi: kmi)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,27 @@ import Foundation
|
||||
extension KissIndex {
|
||||
|
||||
func indexSet_0004(date: Date, config: String?, kmi: KissIndexType) {
|
||||
if productsCount == 0 {
|
||||
loadShop(url: KissIndex.shopProductsUrl)
|
||||
}
|
||||
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
var scoreMap = [String: Double]()
|
||||
|
||||
// TODO: work
|
||||
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
private func pickNearInvestorUrl(productNo: String, date: Date) {
|
||||
let subPath = "data/\(productNo)/investor"
|
||||
let subFile = "investor-\(date.yyyyMM01).csv"
|
||||
|
||||
let fileUrl = URL.currentDirectory().appending(path: subFile)
|
||||
|
||||
// TODO: work
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ class KissIndex: KissMe.ShopContext {
|
||||
|
||||
|
||||
extension KissIndex {
|
||||
|
||||
|
||||
func collectShorts(date: Date, filter: @escaping (DomesticExtra.Shorts) -> Bool) async throws -> [DomesticExtra.Shorts] {
|
||||
let shorts = try await withThrowingTaskGroup(of: DomesticExtra.Shorts?.self, returning: [DomesticExtra.Shorts].self) { taskGroup in
|
||||
let all = getAllProducts()
|
||||
@@ -110,7 +110,7 @@ extension KissIndex {
|
||||
|
||||
let shorts = try [DomesticExtra.Shorts].readCsv(fromFile: shortsUrl)
|
||||
let targetShorts = shorts.filter { $0.stockBusinessDate == yyyyMMdd }
|
||||
|
||||
|
||||
if let aShorts = targetShorts.first, filter(aShorts) {
|
||||
return aShorts
|
||||
}
|
||||
@@ -155,6 +155,10 @@ extension KissIndex {
|
||||
}
|
||||
return prices
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension KissIndex {
|
||||
|
||||
static var shopProductsUrl: URL {
|
||||
URL.currentDirectory().appending(path: "data/shop-products.csv")
|
||||
@@ -182,6 +186,7 @@ extension KissIndex {
|
||||
extension KissIndex {
|
||||
|
||||
func normalizeAndWrite(scoreMap: [String: Double], includeName: Bool = false, kmi: KissIndexType) {
|
||||
|
||||
let totalScores = scoreMap.reduce(0, { $0 + $1.value })
|
||||
let scoreArray = scoreMap.map { ($0.key, $0.value) }.sorted(by: { $0.1 > $1.1 })
|
||||
|
||||
|
||||
2
bin/data
2
bin/data
Submodule bin/data updated: 8f92a1c7b8...753bcd9c91
@@ -6,6 +6,9 @@ PER 의 적정값에 해당되는 종목을 선별합니다.
|
||||
|
||||
**한국투자증권**에서 우선적으로 제공되는 PER 를 사용합니다.
|
||||
|
||||
* [주식현재가 시세[v1_국내주식-008]](https://apiportal.koreainvestment.com/apiservice/apiservice-domestic-stock-quotations#L_07802512-4f49-4486-91b4-1050b6f5dc9d)
|
||||
* /uapi/domestic-stock/v1/quotations/inquire-price
|
||||
|
||||
차후에는 PER 값을 예측하도록 개선될 예정입니다.
|
||||
|
||||
적합한 PER 를 선별하는 기준은 다음과 같습니다.
|
||||
|
||||
@@ -1,2 +1,20 @@
|
||||
# KMI-0004
|
||||
|
||||
## How to
|
||||
|
||||
외국인 거래량/보유량을 기준으로 종목을 선별합니다.
|
||||
|
||||
여기에 2가지 방식이 제안됩니다.
|
||||
|
||||
* 3일 연속매수하여 계속 증가했는지 검사.
|
||||
* 4일전보다 보유량이 증가했는지 검사.
|
||||
|
||||
### 3일 연속매수
|
||||
|
||||
### 4일이후 보유량 상승
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Reference in New Issue
Block a user