Implement KMI-0002 index set

This commit is contained in:
2023-06-26 18:23:45 +09:00
parent 5185f27c14
commit 2ab7c9eaac
6 changed files with 173 additions and 46 deletions

View File

@@ -49,25 +49,14 @@ open class ShopContext {
extension ShopContext {
public func loadShop(url: URL, profile: Bool = false) {
let appTime1 = Date.appTime
public func loadShop(url: URL, loggable: Bool = false) {
guard let stringCsv = try? String(contentsOfFile: url.path) else {
return
}
let appTime2 = Date.appTime
if profile {
print("\tloading file \(appTime2 - appTime1) elapsed")
}
var products = [String: [DomesticShop.Product]]()
let rows = stringCsv.split(separator: "\n")
let appTime3 = Date.appTime
if profile {
print("\trow split \(appTime3 - appTime2) elapsed")
}
for (i, row) in rows.enumerated() {
let array = row.split(separator: ",", omittingEmptySubsequences: false).map { String($0) }
if i == 0, array[0] == "baseDate" {
@@ -82,14 +71,12 @@ extension ShopContext {
products[product.itemName] = [product]
}
}
let appTime4 = Date.appTime
if profile {
print("\tparse product \(appTime4 - appTime3) elapsed")
}
setProducts(products)
let totalCount = products.reduce(0, { $0 + $1.value.count })
print("load products \(totalCount) with \(products.count) key")
if loggable {
let totalCount = products.reduce(0, { $0 + $1.value.count })
print("load products \(totalCount) with \(products.count) key")
}
}
private func setProducts(_ products: [String: [DomesticShop.Product]]) {

View File

@@ -851,7 +851,7 @@ extension KissConsole {
private func onLoadShop() async {
return await withUnsafeContinuation { continuation in
self.loadShop(url: KissConsole.shopProductsUrl)
self.loadShop(url: KissConsole.shopProductsUrl, loggable: true)
continuation.resume()
}
}

View File

@@ -9,37 +9,37 @@ import Foundation
import KissMe
private struct KMI_0002_Config: Codable {
enum Strategy: String, Codable {
case balanceRatioIncreased = "BALANCE_RATIO_INCREASED"
case shorts3DayIncreased = "3DAY_INCREASED"
}
let strategy: Strategy
static let `default`: Strategy = .balanceRatioIncreased
}
extension KissIndex {
func indexSet_0002(date: Date, config: String?, kmi: KissIndexType) {
if productsCount == 0 {
loadShop(url: KissIndex.shopProductsUrl)
}
let strategy = loadConfig(config)
let semaphore = DispatchSemaphore(value: 0)
Task {
do {
let shorts = try await collectShorts(date: date) { aShorts in
/// (1%)
if let ratio = Double(aShorts.shortSellingBalanceRatio), ratio >= 0.01 {
return true
}
return false
switch strategy {
case .balanceRatioIncreased:
try await balanceRatioIncreased(date: date, kmi: kmi)
case .shorts3DayIncreased:
try await shorts3DayIncreased(date: date, kmi: kmi)
}
//print(shorts.count)
let prices = try await collectPrices(date: date) { price in
if let quantity = Int(price.lastShortSellingConclusionQuantity), quantity > 0 {
/// ?
/// lastShortSellingConclusionQuantity
return true
}
return false
}
//print(prices.count)
} catch {
//print(error)
writeError(error, kmi: kmi)
}
@@ -47,4 +47,121 @@ extension KissIndex {
}
semaphore.wait()
}
private func loadConfig(_ config: String?) -> KMI_0002_Config.Strategy {
var strategy = KMI_0002_Config.default
if let config = config {
do {
let configUrl = URL.currentDirectory().appending(path: config)
let data = try Data(contentsOf: configUrl, options: .uncached)
let configData = try JSONDecoder().decode(KMI_0002_Config.self, from: data)
strategy = configData.strategy
} catch {
}
}
return strategy
}
/// 3 (0.01%) .
///
private func balanceRatioIncreased(date: Date, kmi: KissIndexType) async throws {
let increasedRatioLock = NSLock()
var increasedRatio = [String: Double]()
let _ = try await collectShorts(date: date, recentCount: 3) { productNo, shorts in
if shorts.count >= 3, let endShort = shorts.first, let startShort = shorts.last {
if let endRatio = Double(endShort.shortSellingBalanceRatio),
let startRatio = Double(startShort.shortSellingBalanceRatio) {
let increaseRatio = endRatio - startRatio
if abs(increaseRatio) > 0.01 {
increasedRatioLock.lock()
if let _ = increasedRatio[productNo] {
increasedRatio[productNo]! += increaseRatio
}
else {
increasedRatio[productNo] = increaseRatio
}
increasedRatioLock.unlock()
return true
}
}
}
return false
}
var scoreMap = [String: Double]()
for (productNo, ratio) in increasedRatio {
let score = -ratio
if let _ = scoreMap[productNo] {
scoreMap[productNo]! += score
}
else {
scoreMap[productNo] = score
}
}
normalizeAndWrite(scoreMap: scoreMap, includeName: true, kmi: kmi)
}
/// 3 (0.01%) .
///
private func shorts3DayIncreased(date: Date, kmi: KissIndexType) async throws {
let sumRatioLock = NSLock()
var sumRatio = [String: Double]()
let prices = try await collectPrices(date: date, recentCount: 3) { prices in
if prices.count >= 3 {
var curDay: String? = nil
var aPriceEachDay = [CapturePrice]()
for price in prices {
if curDay != price.stockBusinessDate {
aPriceEachDay.append(price)
curDay = price.stockBusinessDate
}
}
/// lastShortSellingConclusionQuantity .
let sumConclusionQuantity = aPriceEachDay.reduce(0, { $0 + (Int($1.lastShortSellingConclusionQuantity) ?? 0) })
let ratio = Double(sumConclusionQuantity * 100) / Double(prices[0].listedStockCount)!
//print("ratio: \(ratio), sumConclusionQuantity: \(sumConclusionQuantity)")
guard ratio > 0.01 else {
return false
}
let productNo = prices[0].shortProductCode
sumRatioLock.lock()
if let _ = sumRatio[productNo] {
sumRatio[productNo]! += ratio
}
else {
sumRatio[productNo] = ratio
}
sumRatioLock.unlock()
return true
}
return false
}
var scoreMap = [String: Double]()
for price in prices {
let productNo = price.shortProductCode
let score = -(sumRatio[productNo] ?? 0)
if let _ = scoreMap[productNo] {
scoreMap[productNo]! += score
}
else {
scoreMap[productNo] = score
}
}
normalizeAndWrite(scoreMap: scoreMap, includeName: true, kmi: kmi)
}
}

View File

@@ -8,7 +8,7 @@
import Foundation
struct KMI_0004_Config: Codable {
private struct KMI_0004_Config: Codable {
enum Strategy: String, Codable {
case foreigner3DayFocusing = "3DAY_FOCUSING"

View File

@@ -104,20 +104,43 @@ class KissIndex: KissMe.ShopContext {
extension KissIndex {
func collectShorts(date: Date, filter: @escaping (DomesticExtra.Shorts) -> Bool) async throws -> [DomesticExtra.Shorts] {
func collectShorts(date: Date, recentCount: Int, filter: @escaping (String, [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()
let yyyyMMdd = date.yyyyMMdd
let (yyyy, mm, dd) = date.yyyyMMdd_split!
for item in all {
taskGroup.addTask {
let shortsUrl = KissIndex.pickNearShortsUrl(productNo: item.shortCode, date: date)
let shorts = try [DomesticExtra.Shorts].readCsv(fromFile: shortsUrl)
let targetShorts = shorts.filter { $0.stockBusinessDate == yyyyMMdd }
let prevMonthDate = date.changing(year: yyyy, month: mm-1, day: dd)!
let prevMonthShortsUrl = KissIndex.pickNearShortsUrl(productNo: item.shortCode, date: prevMonthDate)
if let aShorts = targetShorts.first, filter(aShorts) {
return aShorts
var shorts = try [DomesticExtra.Shorts].readCsv(fromFile: shortsUrl)
if let prevShorts = try? [DomesticExtra.Shorts].readCsv(fromFile: prevMonthShortsUrl) {
shorts.append(contentsOf: prevShorts)
}
var targetShorts = [DomesticExtra.Shorts]()
var collectedDay = 0
var prevDays = 1
var desiredDate: Date? = date
while desiredDate != nil, prevDays < recentCount * 7 {
let selected = shorts.filter { $0.stockBusinessDate == desiredDate!.yyyyMMdd }
targetShorts.append(contentsOf: selected)
if selected.count > 0 {
collectedDay += 1
}
if collectedDay >= recentCount {
break
}
desiredDate = desiredDate!.changing(year: yyyy, month: mm, day: dd-prevDays)
prevDays += 1
}
if filter(item.shortCode, targetShorts), let first = targetShorts.first {
return first
}
return nil
}

View File

@@ -53,7 +53,7 @@
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "KMI-0004 20230622 100000"
argument = "KMI-0002 20230623 100000"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>