.....
This commit is contained in:
@@ -116,7 +116,7 @@ object AutoTradingManager {
|
||||
val globalCallback = { completeTradingDecision: TradingDecision?, isSuccess: Boolean ->
|
||||
val seoulZone = ZoneId.of("Asia/Seoul")
|
||||
val now = LocalTime.now(ZoneId.of("Asia/Seoul"))
|
||||
if (KisSession.isAvailBuyTime(now) && isSuccess && completeTradingDecision != null) {
|
||||
if (KisSession.isMarketOpenTime(now) && isSuccess && completeTradingDecision != null) {
|
||||
val decision = completeTradingDecision
|
||||
|
||||
println("${decision.stockName} ${decision.decision}")
|
||||
@@ -409,7 +409,7 @@ object AutoTradingManager {
|
||||
"WATCH",
|
||||
"매수 시간 외 분석 => 재분석 대기열에 추가"
|
||||
)
|
||||
} else {
|
||||
} else if (KisSession.isMarketOpenTime(LocalTime.now()) == false){
|
||||
val unfilledResult = KisTradeService.fetchUnfilledOrders()
|
||||
unfilledResult.onSuccess { response ->
|
||||
response.filter { it.sll_buy_dvsn_cd == "02" }.forEach { order ->
|
||||
@@ -464,7 +464,7 @@ object AutoTradingManager {
|
||||
if (hasCodes) {
|
||||
actualBuyPrice = actualBuyPrice * 1.1
|
||||
}
|
||||
val absoluteMinRate = KisSession.config.getValues(ConfigIndex.TAX_INDEX) + 0.05
|
||||
val absoluteMinRate = KisSession.config.getValues(ConfigIndex.TAX_INDEX)
|
||||
val finalProfitRate = maxOf(dbItem.profitRate, absoluteMinRate)
|
||||
val finalTargetPrice =
|
||||
MarketUtil.roundToTickSize(actualBuyPrice * (1 + finalProfitRate / 100.0))
|
||||
@@ -1201,34 +1201,38 @@ object AutoTradingManager {
|
||||
candidates.addAll(reanalysisList)
|
||||
}
|
||||
reanalysisList.clear()
|
||||
val wateringCodes = mutableSetOf<String>()
|
||||
|
||||
if (KisSession.tradeConfig.lowerAveragePrice) {
|
||||
currentBalance?.getHoldings()?.map {
|
||||
// map 대신 forEach를 사용하여 메모리 낭비 방지
|
||||
currentBalance?.getHoldings()?.forEach {
|
||||
if (
|
||||
it.quantity.toInt() > KisSession.tradeConfig.lowerAverageTargetCount &&
|
||||
it.profitRate.toDouble() < 0.0 &&
|
||||
it.profitRate.toDouble() < (abs(KisSession.tradeConfig.lowerAverageMaxRate) * -1) &&
|
||||
it.profitRate.toDouble() > (abs(KisSession.tradeConfig.lowerAverageMinRate) * -1)
|
||||
) {
|
||||
// 물타기 전용 Set에 코드 등록
|
||||
wateringCodes.add(it.code)
|
||||
|
||||
candidates.add(
|
||||
RankingStock(
|
||||
mksc_shrn_iscd = it.code,
|
||||
hts_kor_isnm = it.name
|
||||
)
|
||||
)
|
||||
println("물타기 대상 추가 ${it.name}[${it.code}]")
|
||||
var oldTarget = it
|
||||
if (oldTarget != null) {
|
||||
var avgPrive = oldTarget.avgPrice.toDouble()
|
||||
println("💧 [물타기 우선순위 할당] ${it.name}[${it.code}]")
|
||||
|
||||
var qty = oldTarget.quantity.toDouble()
|
||||
var basePrice =
|
||||
((avgPrive * qty) + it.currentPrice.toDouble()).div(qty!!.toInt() + 1)
|
||||
println("물타기 ${avgPrive}, ${qty} ${basePrice}")
|
||||
}
|
||||
val avgPrive = it.avgPrice.toDouble()
|
||||
val qty = it.quantity.toDouble()
|
||||
val basePrice = ((avgPrive * qty) + it.currentPrice.toDouble()).div(qty.toInt() + 1)
|
||||
println(" -> 단가 현황: 평단가 $avgPrive, 보유 ${qty}주, 예상단가 $basePrice")
|
||||
}
|
||||
}
|
||||
}
|
||||
remainingCandidates.addAll(candidates.filter {
|
||||
|
||||
// 1. 기존 필터링 및 중복 제거 적용
|
||||
val filteredCandidates = candidates.filter {
|
||||
(if (KisSession.tradeConfig.lowerAveragePrice) {
|
||||
true
|
||||
} else {
|
||||
@@ -1238,8 +1242,16 @@ object AutoTradingManager {
|
||||
it.code !in executionCache.values.map { it.code } &&
|
||||
it.code !in failList &&
|
||||
it.code !in isSafetyBeltStockCodes
|
||||
}.distinctBy { it.code })
|
||||
remainingCandidates.shuffle()
|
||||
}.distinctBy { it.code }
|
||||
|
||||
// 🌟 2. [핵심] 리스트를 두 그룹으로 파티셔닝 (물타기 대상 vs 신규 발굴)
|
||||
val (wateringList, newList) = filteredCandidates.partition { it.code in wateringCodes }
|
||||
|
||||
remainingCandidates.clear()
|
||||
|
||||
// 🌟 3. 분석 대기열 재조립 (물타기 대상 1순위 배치)
|
||||
remainingCandidates.addAll(wateringList) // 물타기 대상을 무조건 리스트 맨 앞으로 (셔플 안 함)
|
||||
remainingCandidates.addAll(newList.shuffled())
|
||||
} else {
|
||||
println("미확인 데이터 ${remainingCandidates.size}")
|
||||
}
|
||||
@@ -1373,7 +1385,7 @@ object AutoTradingManager {
|
||||
|
||||
// 🌟 [핵심] 물타기 대상 여부 플래그 식별
|
||||
val targetHolding = currentBalance?.getHoldings()?.firstOrNull {
|
||||
it.code == stock.code && it.quantity.toInt() > 2
|
||||
it.code == stock.code && it.quantity.toInt() > 0
|
||||
}
|
||||
val isWatering = targetHolding != null && KisSession.tradeConfig.lowerAveragePrice
|
||||
|
||||
@@ -1472,7 +1484,7 @@ object AutoTradingManager {
|
||||
|
||||
val currentAtr = tempAnalyzer.calculateATR(dailyData)
|
||||
if (!isProfitable || !isValidEntryTiming) {
|
||||
print("-> [${stock.name}] 조건 미달 (물타기:$isWatering, 수익:$isProfitable, 타이밍:$isValidEntryTiming) | ")
|
||||
print("-> [${stock.name}] 목표 수익 조건 미달 (물타기:$isWatering, 수익:$isProfitable, 타이밍:$isValidEntryTiming) | 기대 수익율 : ${expectedProfitRate}")
|
||||
return@withTimeout
|
||||
}
|
||||
|
||||
@@ -1497,7 +1509,7 @@ object AutoTradingManager {
|
||||
}
|
||||
}
|
||||
|
||||
println("\n💧 [검문소 통과 -> ${if (isWatering) "물타기 탈출 분석" else "신규 매수 분석"}] ${stock.name} (물타기:$isWatering, 수익:$isProfitable, 타이밍:$isValidEntryTiming)")
|
||||
println("\n💧 [검문소 통과 -> ${if (isWatering) "물타기 탈출 분석" else "신규 매수 분석"}] ${stock.name} (물타기:$isWatering, 수익:$isProfitable, 타이밍:$isValidEntryTiming) | 기대 수익율 : ${expectedProfitRate}")
|
||||
|
||||
// 멀티 타임프레임 및 AI 분석 진입
|
||||
val analyzer = coroutineScope {
|
||||
|
||||
Reference in New Issue
Block a user