...
This commit is contained in:
@@ -352,7 +352,7 @@ class TechnicalAnalyzer {
|
|||||||
candles: List<CandleData>,
|
candles: List<CandleData>,
|
||||||
avgReboundTerm: Double,
|
avgReboundTerm: Double,
|
||||||
dropThreshold: Double = 5.0,
|
dropThreshold: Double = 5.0,
|
||||||
timeTolerance: Double = 1.5
|
timeTolerance: Double = 2.0
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (candles.size < 20 || avgReboundTerm <= 0.0) return false
|
if (candles.size < 20 || avgReboundTerm <= 0.0) return false
|
||||||
|
|
||||||
@@ -771,7 +771,7 @@ data class VolatilityForecast(
|
|||||||
)
|
)
|
||||||
data class ReboundStats(
|
data class ReboundStats(
|
||||||
val avgReboundPeriod: Double = 0.0, // 평균 반등 소요 캔들 (일/주/월)
|
val avgReboundPeriod: Double = 0.0, // 평균 반등 소요 캔들 (일/주/월)
|
||||||
val timeTolerance: Double = 1.5, // 오차 허용 범위 (표준편차)
|
val timeTolerance: Double = 2.0, // 오차 허용 범위 (표준편차)
|
||||||
val avgDropRate: Double = 5.0, // 평균 하락폭
|
val avgDropRate: Double = 5.0, // 평균 하락폭
|
||||||
val avgReboundAmplitude: Double = 0.0, // 🌟 [신규] 바닥 찍고 평균적으로 몇 % 올랐는가?
|
val avgReboundAmplitude: Double = 0.0, // 🌟 [신규] 바닥 찍고 평균적으로 몇 % 올랐는가?
|
||||||
val isValid: Boolean = false
|
val isValid: Boolean = false
|
||||||
|
|||||||
@@ -365,4 +365,8 @@ object KisSession {
|
|||||||
return now.isBefore(LocalTime.of(15,30)) && now.isAfter(LocalTime.of(9,0))
|
return now.isBefore(LocalTime.of(15,30)) && now.isAfter(LocalTime.of(9,0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isMarketAnalyzerTime(now: LocalTime) : Boolean {
|
||||||
|
return now.isBefore(LocalTime.of(18,0)) && now.isAfter(LocalTime.of(8,0))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,7 @@ import kotlinx.serialization.json.jsonPrimitive
|
|||||||
import model.*
|
import model.*
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
|
import java.time.ZoneId
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import kotlin.coroutines.coroutineContext
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
@@ -426,12 +427,14 @@ object KisTradeService {
|
|||||||
isDomestic && !config.isSimulation -> if (isBuy) "TTTC0802U" else "TTTC0801U"
|
isDomestic && !config.isSimulation -> if (isBuy) "TTTC0802U" else "TTTC0801U"
|
||||||
else -> if (isBuy) "TTTS3002U" else "TTTS3001U"
|
else -> if (isBuy) "TTTS3002U" else "TTTS3001U"
|
||||||
}
|
}
|
||||||
val finalOrderDivision = when {
|
var finalOrderDivision = when {
|
||||||
orderDivision.isNotEmpty() -> orderDivision
|
orderDivision.isNotEmpty() -> orderDivision
|
||||||
marketCode.equals("SOR") || price == "0" || price.isEmpty() -> "01" // 시장가
|
marketCode.equals("SOR") || price == "0" || price.isEmpty() -> "01" // 시장가
|
||||||
else -> "00" // 지정가
|
else -> "00" // 지정가
|
||||||
}
|
}
|
||||||
|
if (marketCode.equals("KRX") && LocalTime.now(ZoneId.of("Asia/Seoul")).isAfter(LocalTime.of(16,0))) {
|
||||||
|
finalOrderDivision = "41"
|
||||||
|
}
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val response = client.post("$baseUrl/uapi/${if(isDomestic) "domestic" else "overseas"}-stock/v1/trading/order-cash") {
|
val response = client.post("$baseUrl/uapi/${if(isDomestic) "domestic" else "overseas"}-stock/v1/trading/order-cash") {
|
||||||
@@ -684,7 +687,7 @@ object KisTradeService {
|
|||||||
try {
|
try {
|
||||||
do {
|
do {
|
||||||
if (!coroutineContext.isActive) throw _root_ide_package_.io.ktor.utils.io.CancellationException("UI에서 작업을 취소함") // [추가]
|
if (!coroutineContext.isActive) throw _root_ide_package_.io.ktor.utils.io.CancellationException("UI에서 작업을 취소함") // [추가]
|
||||||
println("📡 [Step $pageCount] 요청 전송 중... (tr_cont: $trCont)")
|
// println("📡 [Step $pageCount] 요청 전송 중... (tr_cont: $trCont)")
|
||||||
val response = client.get("$baseUrl/uapi/domestic-stock/v1/trading/inquire-balance") {
|
val response = client.get("$baseUrl/uapi/domestic-stock/v1/trading/inquire-balance") {
|
||||||
header("authorization", "Bearer ${config.tradeToken}")
|
header("authorization", "Bearer ${config.tradeToken}")
|
||||||
header("appkey", config.realAppKey)
|
header("appkey", config.realAppKey)
|
||||||
@@ -714,8 +717,8 @@ object KisTradeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val body = response.body<StockBalanceResponse>()
|
val body = response.body<StockBalanceResponse>()
|
||||||
println("✅ [Step $pageCount] $markgetCode 수신 완료 - 종목 수: ${body.output1.size}\n${body.output2}\n\n")
|
// println("✅ [Step $pageCount] $markgetCode 수신 완료 - 종목 수: ${body.output1.size}\n${body.output2}\n\n")
|
||||||
// println("✅ [Step $pageCount] $markgetCode 수신 완료 - 종목 수: ${body.output1.size}")
|
println("✅ [Step $pageCount] $markgetCode 수신 완료 - 종목 수: ${body.output1.size}")
|
||||||
|
|
||||||
allHoldings.addAll(body.output1)
|
allHoldings.addAll(body.output1)
|
||||||
if (totalBalance == null) totalBalance = body
|
if (totalBalance == null) totalBalance = body
|
||||||
@@ -725,7 +728,7 @@ object KisTradeService {
|
|||||||
ctxAreaFk = body.ctx_area_fk100 ?: ""
|
ctxAreaFk = body.ctx_area_fk100 ?: ""
|
||||||
ctxAreaNk = body.ctx_area_nk100 ?: ""
|
ctxAreaNk = body.ctx_area_nk100 ?: ""
|
||||||
|
|
||||||
println("📝 [Header Check] tr_cont: $trCont, ctx_area_nk100: $ctxAreaNk")
|
// println("📝 [Header Check] tr_cont: $trCont, ctx_area_nk100: $ctxAreaNk")
|
||||||
|
|
||||||
if ( trCont == "M") {
|
if ( trCont == "M") {
|
||||||
pageCount++
|
pageCount++
|
||||||
|
|||||||
@@ -435,7 +435,7 @@ object RagService {
|
|||||||
put("type", "json_object")
|
put("type", "json_object")
|
||||||
}
|
}
|
||||||
}.toString()
|
}.toString()
|
||||||
println("requestBodyJson =>> $requestBodyJson")
|
// println("requestBodyJson =>> $requestBodyJson")
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(LLM_API_URL())
|
.url(LLM_API_URL())
|
||||||
.post(requestBodyJson.toRequestBody(jsonMediaType))
|
.post(requestBodyJson.toRequestBody(jsonMediaType))
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ object AutoTradingManager {
|
|||||||
val globalCallback = { completeTradingDecision: TradingDecision?, isSuccess: Boolean ->
|
val globalCallback = { completeTradingDecision: TradingDecision?, isSuccess: Boolean ->
|
||||||
val seoulZone = ZoneId.of("Asia/Seoul")
|
val seoulZone = ZoneId.of("Asia/Seoul")
|
||||||
val now = LocalTime.now(ZoneId.of("Asia/Seoul"))
|
val now = LocalTime.now(ZoneId.of("Asia/Seoul"))
|
||||||
if (KisSession.isMarketOpenTime(now) && isSuccess && completeTradingDecision != null) {
|
if (KisSession.isMarketAnalyzerTime(now) && isSuccess && completeTradingDecision != null) {
|
||||||
val decision = completeTradingDecision
|
val decision = completeTradingDecision
|
||||||
|
|
||||||
println("${decision.stockName} ${decision.decision}")
|
println("${decision.stockName} ${decision.decision}")
|
||||||
@@ -1262,7 +1262,7 @@ object AutoTradingManager {
|
|||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
totalCount--
|
totalCount--
|
||||||
val stock = iterator.next()
|
val stock = iterator.next()
|
||||||
if (KisSession.isMarketOpenTime(now)) {
|
if (KisSession.isMarketAnalyzerTime(now)) {
|
||||||
if (BLACKLISTEDSTOCKCODES.contains(stock.code)) {
|
if (BLACKLISTEDSTOCKCODES.contains(stock.code)) {
|
||||||
println("❌ 차단 처리된 주식 : ${stock.name}")
|
println("❌ 차단 처리된 주식 : ${stock.name}")
|
||||||
} else {
|
} else {
|
||||||
@@ -1385,7 +1385,7 @@ object AutoTradingManager {
|
|||||||
|
|
||||||
// 🌟 [핵심] 물타기 대상 여부 플래그 식별
|
// 🌟 [핵심] 물타기 대상 여부 플래그 식별
|
||||||
val targetHolding = currentBalance?.getHoldings()?.firstOrNull {
|
val targetHolding = currentBalance?.getHoldings()?.firstOrNull {
|
||||||
it.code == stock.code && it.quantity.toInt() > 0
|
it.code == stock.code && it.quantity.toInt() > KisSession.tradeConfig.lowerAverageTargetCount
|
||||||
}
|
}
|
||||||
val isWatering = targetHolding != null && KisSession.tradeConfig.lowerAveragePrice
|
val isWatering = targetHolding != null && KisSession.tradeConfig.lowerAveragePrice
|
||||||
|
|
||||||
@@ -1450,7 +1450,7 @@ object AutoTradingManager {
|
|||||||
val tempAnalyzer = TechnicalAnalyzer().apply { this.daily = dailyData }
|
val tempAnalyzer = TechnicalAnalyzer().apply { this.daily = dailyData }
|
||||||
val volatility = tempAnalyzer.calculateVolatilityForecast(dailyData, 20)
|
val volatility = tempAnalyzer.calculateVolatilityForecast(dailyData, 20)
|
||||||
val expectedProfitRate = ((volatility.realisticHigh - currentPrice) / currentPrice) * 100.0
|
val expectedProfitRate = ((volatility.realisticHigh - currentPrice) / currentPrice) * 100.0
|
||||||
val dailyStats = tempAnalyzer.calculateDynamicReboundStats(dailyData, 5.0)
|
val dailyStats = tempAnalyzer.calculateDynamicReboundStats(dailyData, 3.0)
|
||||||
|
|
||||||
// 🌟 [완화 3] 기대수익률 및 진입 타이밍 이원화
|
// 🌟 [완화 3] 기대수익률 및 진입 타이밍 이원화
|
||||||
val isProfitable: Boolean
|
val isProfitable: Boolean
|
||||||
|
|||||||
Reference in New Issue
Block a user