Files
KissMe/KissMeIndex/Sources/KissIndex+0004.swift

202 lines
6.7 KiB
Swift

//
// KissIndex+0004.swift
// KissMeIndex
//
// Created by ened-book-m1 on 2023/06/20.
//
import Foundation
import KissMe
private struct KMI_0004_Config: Codable {
enum Strategy: String, Codable {
case foreigner3DayFocusing = "3DAY_FOCUSING"
case foreigner4DayIncreased = "4DAY_INCREASED"
}
let strategy: Strategy
static let `default`: Strategy = .foreigner4DayIncreased
}
extension KissIndex {
func indexSet_0004(date: Date, config: String?, kmi: KissIndexType) {
if productsCount == 0 {
loadShop(url: KissIndex.shopProductsUrl)
}
let strategy = loadConfig(config)
let semaphore = DispatchSemaphore(value: 0)
Task {
do {
switch strategy {
case .foreigner3DayFocusing:
try await foreigner3DayFocusing(date: date, kmi: kmi)
case .foreigner4DayIncreased:
try await foreigner4DayIncreased(date: date, kmi: kmi)
}
} catch {
writeError(error, kmi: kmi)
}
semaphore.signal()
}
semaphore.wait()
}
private func loadConfig(_ config: String?) -> KMI_0004_Config.Strategy {
var strategy = KMI_0004_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_0004_Config.self, from: data)
strategy = configData.strategy
} catch {
}
}
return strategy
}
/// 4 .
/// 4 .
///
private func foreigner4DayIncreased(date: Date, kmi: KissIndexType) async throws {
let upLock = NSLock()
var up = [String: Int]()
let prices = try await collectPrices(date: date, recentCount: 4) { prices in
if prices.count >= 4,
let endPrice = prices.first,
let startPrice = prices.last,
let holdQuantity1 = Int(endPrice.foreignHoldQuantity),
let holdQuantity2 = Int(startPrice.foreignHoldQuantity),
startPrice.stockBusinessDate < endPrice.stockBusinessDate {
let increased = holdQuantity1 - holdQuantity2
let increateRatio = Double(increased * 100) / Double(holdQuantity2)
if abs(increateRatio) >= 1.0 {
let productNo = startPrice.shortProductCode
//printError(productNo, increateRatio)
upLock.lock()
if let _ = up[productNo] {
up[productNo]! += increased
}
else {
up[productNo] = increased
}
upLock.unlock()
return true
}
}
return false
}
var scoreMap = [String: Double]()
for price in prices {
let productNo = price.shortProductCode
let score: Double
if let inc = up[productNo] {
if inc < 0 {
score = -log10(abs(Double(inc)))
}
else {
score = log10(Double(inc))
}
if score.isNaN || score.isInfinite {
printError("\(productNo) score: \(score)")
continue
}
}
else {
score = 0
}
if let _ = scoreMap[productNo] {
scoreMap[productNo]! += score
}
else {
scoreMap[productNo] = score
}
}
if let maxScore = scoreMap.max(by: { abs($0.value) < abs($1.value) }) {
let output = normalizeByScale(scoreMap: scoreMap, includeName: true, scale: abs(maxScore.value))
writeOutput(output, kmi: kmi)
}
}
/// 3 , .
/// 3 , .
///
private func foreigner3DayFocusing(date: Date, kmi: KissIndexType) async throws {
let netSumLock = NSLock()
var netSum = [String: Int]()
let investors = try await collectInvestors(date: date, recentCount: 3) { productNo, investors in
if investors.count == 3,
let netBuying1 = Int(investors[0].foreignNetBuyingQuantity),
let netBuying2 = Int(investors[1].foreignNetBuyingQuantity),
let netBuying3 = Int(investors[2].foreignNetBuyingQuantity) {
if (netBuying1 > 0 && netBuying2 > 0 && netBuying3 > 0) ||
(netBuying1 < 0 && netBuying2 < 0 && netBuying3 < 0) {
let sum = netBuying1 + netBuying2 + netBuying3
netSumLock.lock()
if let _ = netSum[productNo] {
netSum[productNo]! += sum
}
else {
netSum[productNo] = sum
}
netSumLock.unlock()
return true
}
}
return false
}
var scoreMap = [String: Double]()
for (productNo, _) in investors {
let score: Double
if let sum = netSum[productNo] {
if sum < 0 {
score = -log10(abs(Double(sum)))
}
else {
score = log10(Double(sum))
}
if score.isNaN || score.isInfinite {
printError("\(productNo) score: \(score)")
continue
}
}
else {
score = 0
}
if let _ = scoreMap[productNo] {
scoreMap[productNo]! += score
}
else {
scoreMap[productNo] = score
}
}
if let maxScore = scoreMap.max(by: { abs($0.value) < abs($1.value) }) {
let output = normalizeByScale(scoreMap: scoreMap, includeName: true, scale: abs(maxScore.value))
writeOutput(output, kmi: kmi)
}
}
}