...
This commit is contained in:
parent
6e7f485988
commit
921e6c7caf
@ -256,14 +256,14 @@ object RagService {
|
||||
|
||||
result(finalDecision, true)
|
||||
} else {
|
||||
println("✋ [$stockName] 기술 점수 미달로 분석 중단")
|
||||
println("✋ [$stockName] 기술 점수 미달로 분석 중단 ${scores.toString()}")
|
||||
TradingLogStore.addAnalyzer(stockName, stockCode, "기술 점수 미달로 분석 중단")
|
||||
tradingDecision.confidence = 1.0
|
||||
result(tradingDecision, false)
|
||||
}
|
||||
} else {
|
||||
println("🚨 [$stockName] 재무 안전벨트 미달")
|
||||
TradingLogStore.addAnalyzer(stockName, stockCode, "재무 안전벨트 미달로 분석 중단")
|
||||
println("🚨 [$stockName] ${FinancialAnalyzer.getInvestmentStatus(financialStmt)} 재무 안전벨트 미달")
|
||||
TradingLogStore.addAnalyzer(stockName, stockCode, "재무 안전벨트 미달로 분석 중단 ${FinancialAnalyzer.getInvestmentStatus(financialStmt)}")
|
||||
tradingDecision.confidence = 1.0
|
||||
result(tradingDecision, false)
|
||||
}
|
||||
@ -290,53 +290,6 @@ object RagService {
|
||||
return result.matches().isNotEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
* 질문과 가장 유사한 정보를 H2에서 검색하여 AI 답변을 생성합니다.
|
||||
*/
|
||||
// fun askWithContext(question: String,
|
||||
// corpInfo: String,
|
||||
// financialData: String,
|
||||
// days : List<CandleData>,
|
||||
// weeks : List<CandleData>,
|
||||
// monthly : List<CandleData>): String {
|
||||
// val questionEmbedding = embeddingModel.embed(question).content()
|
||||
// val searchResult = embeddingStore.search(
|
||||
// EmbeddingSearchRequest.builder()
|
||||
// .queryEmbedding(questionEmbedding)
|
||||
// .maxResults(5)
|
||||
// .build()
|
||||
// )
|
||||
// val newsContext = searchResult.matches().joinToString("\n") { it.embedded().text() }
|
||||
//
|
||||
// // 2. 종합 분석 프롬프트 구성
|
||||
// val finalPrompt = """
|
||||
// <|begin_of_text|><|start_header_id|>system<|end_header_id|>
|
||||
// 당신은 뉴스(심리), 재무(본질), 차트(추세)를 통합 분석하는 'AI 수석 애널리스트'입니다.
|
||||
// 제공된 데이터를 바탕으로 아래 형식을 엄격히 지켜 분석 리포트를 작성하세요.
|
||||
//
|
||||
// [데이터 세트]
|
||||
// 1. 기업 기본 정보: $corpInfo
|
||||
// 2. 재무 성장성: $financialData
|
||||
// 3. 기술적 추세: ${monthly}, ${weeks}, ${days}
|
||||
// 4. 최신 이슈(뉴스): $newsContext
|
||||
//
|
||||
// [분석 요청 사항]
|
||||
// 1. **업계 상황**: 해당 종목이 속한 업종의 현재 전체적인 흐름을 먼저 정리하세요.
|
||||
// 2. **종목 이슈 분석**: 뉴스에서 포착된 핵심 키워드와 시장의 반응을 요약하세요.
|
||||
// 3. **장기/단기 전략**:
|
||||
// - 장기(재무/월봉 기반): 추천 혹은 비추천 사유
|
||||
// - 단기(뉴스/일봉 기반): 추천 혹은 비추천 사유
|
||||
// 4. **최종 결론**: '매수/관망/매도' 의견과 그에 따른 근거를 단호하게 제시하세요.
|
||||
// <|eot_id|>
|
||||
// <|start_header_id|>user<|end_header_id|>
|
||||
// 질문: $question
|
||||
// <|eot_id|><|start_header_id|>assistant<|end_header_id|>
|
||||
// """.trimIndent()
|
||||
//
|
||||
// val response = chatModel.chat(UserMessage.from(finalPrompt))
|
||||
//// println(response)
|
||||
// return response.aiMessage().text()
|
||||
// }
|
||||
private fun LLM_API_URL() = "http://127.0.0.1:$LLM_PORT/v1/chat/completions"
|
||||
|
||||
private suspend fun callLlamaWithSchema(prompt: String): String {
|
||||
@ -409,9 +362,9 @@ object RagService {
|
||||
|
||||
// 2. 뉴스 유무에 따른 동적 데이터 섹션 구성
|
||||
val newsDataSection = if (validNews != null) {
|
||||
"3. News Context: $validNews"
|
||||
"4. News Context: $validNews"
|
||||
} else {
|
||||
"3. News Context: No significant news available. Rely on financials."
|
||||
"4. News Context: No significant news available. Rely on financials."
|
||||
}
|
||||
|
||||
|
||||
@ -426,9 +379,12 @@ Your goal is to provide a final trading decision based on STRICT data analysis.
|
||||
|
||||
2. Financials: Operating Profit ${if(financialStmt.isOperatingProfitPositive) "PROFIT" else "LOSS"} (Growth: ${"%.2f".format(financialStmt.operatingProfitGrowth)}%), ROE: ${"%.2f".format(financialStmt.roe)}%, Debt: ${"%.2f".format(financialStmt.debtRatio)}%
|
||||
|
||||
3. Technical Analysis Summary: ${tempDecision.techSummary ?: "No technical summary available."}
|
||||
|
||||
$newsDataSection
|
||||
|
||||
|
||||
|
||||
# Step-by-Step Analysis Logic
|
||||
|
||||
1. Financial Review: First, evaluate the 'Financials' section for long-term stability.
|
||||
|
||||
@ -852,8 +852,8 @@ object FinancialAnalyzer {
|
||||
val isDebtSafe = fs.debtRatio < 200.0 // 부채비율 200% 미만
|
||||
val isLiquiditySafe = fs.quickRatio > 80.0 // 당좌비율 80% 이상
|
||||
val isNotDeficit = fs.isNetIncomePositive // 당기순이익은 일단 흑자여야 함
|
||||
|
||||
return isDebtSafe && isLiquiditySafe && isNotDeficit
|
||||
val isNotCrashing = fs.netIncomeGrowth > -40.0
|
||||
return isDebtSafe && isLiquiditySafe && isNotDeficit && isNotCrashing
|
||||
}
|
||||
|
||||
/**
|
||||
@ -876,10 +876,12 @@ object FinancialAnalyzer {
|
||||
val isDebtSafe = fs.debtRatio < 200.0 // 부채비율 200% 미만
|
||||
val isLiquiditySafe = fs.quickRatio > 80.0 // 당좌비율 80% 이상
|
||||
val isNotDeficit = fs.isNetIncomePositive // 당기순이익은 일단 흑자여야 함
|
||||
val isNotCrashing = fs.netIncomeGrowth > -40.0
|
||||
if ((isDebtSafe && isLiquiditySafe && isNotDeficit) == false) {
|
||||
if (isDebtSafe)buffer.appendLine( "부채비율 200% 이상")
|
||||
if (isLiquiditySafe)buffer.appendLine( "당좌비율 80% 미만")
|
||||
if (isNotDeficit)buffer.appendLine( "당기순이익 적자")
|
||||
if (!isDebtSafe)buffer.appendLine( "부채비율 200% 이상")
|
||||
if (!isLiquiditySafe)buffer.appendLine( "당좌비율 80% 미만")
|
||||
if (!isNotDeficit)buffer.appendLine( "당기순이익 적자")
|
||||
if (!isNotCrashing) { buffer.appendLine("당기순이익 급감(${String.format("%.1f", fs.netIncomeGrowth)}%)") }
|
||||
buffer.appendLine("최소 기준 미달")
|
||||
} else {
|
||||
buffer.appendLine("최소 기준 충족")
|
||||
@ -1009,23 +1011,47 @@ class TechnicalAnalyzer {
|
||||
): InvestmentScores {
|
||||
|
||||
// 1. 초단기 (분봉 + 에너지 지표 위주)
|
||||
val ultra = (calculateMFI(min30, 14) * 0.4 +
|
||||
var ultra = (calculateMFI(min30, 14) * 0.4 +
|
||||
calculateStochastic(min30) * 0.3 +
|
||||
(if(calculateChange(min30.takeLast(10)) > 0) 30 else 0)).toInt()
|
||||
|
||||
// 2. 단기 (일봉 추세 + OBV 에너지)
|
||||
val short = (calculateRSI(daily) * 0.3 +
|
||||
var short = (calculateRSI(daily) * 0.3 +
|
||||
(if(calculateOBV(daily) > 0) 40 else 10) +
|
||||
(if(calculateChange(daily.takeLast(3)) > 0) 30 else 0)).toInt()
|
||||
|
||||
// 3. 중기 (주봉 + 재무 점수 혼합)
|
||||
val mid = (if(calculateChange(weekly) > 0) 40 else 10) +
|
||||
var mid = (if(calculateChange(weekly) > 0) 40 else 10) +
|
||||
(financialScore * 0.6).toInt()
|
||||
|
||||
// 4. 장기 (월봉 + 섹터/기업 펀더멘털)
|
||||
val long = (if(calculateChange(monthly) > 0) 50 else 0) +
|
||||
var long = (if(calculateChange(monthly) > 0) 50 else 0) +
|
||||
(financialScore * 0.5).toInt()
|
||||
|
||||
// 1. 일봉 이격도 과열 체크 (20일 이평선 기준)
|
||||
if (daily.size >= 20) {
|
||||
val ma20Daily = daily.takeLast(20).map { it.stck_prpr.toDouble() }.average()
|
||||
val currentPrice = daily.last().stck_prpr.toDouble()
|
||||
val disparityDaily = (currentPrice / ma20Daily) * 100
|
||||
|
||||
if (disparityDaily > 115.0) { // 20일선보다 15% 이상 떠 있으면 감점 시작
|
||||
val penalty = ((disparityDaily - 115.0) * 1).toInt() // 초과분 1%당 2점 감점
|
||||
short -= penalty
|
||||
ultra -= (penalty / 2) // 초단기에도 영향
|
||||
println("⚠️ [과열 감점] 일봉 이격도(${String.format("%.1f", disparityDaily)}%): -${penalty}점")
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 주봉 급등 체크 (최근 3주간의 상승폭)
|
||||
if (weekly.size >= 3) {
|
||||
val weeklyChange = calculateChange(weekly.takeLast(3))
|
||||
if (weeklyChange > 30.0) { // 3주간 30% 이상 급등 시
|
||||
mid -= 15
|
||||
short -= 7
|
||||
println("⚠️ [과열 감점] 주봉 급등(${String.format("%.1f", weeklyChange)}%): -15점")
|
||||
}
|
||||
}
|
||||
|
||||
return InvestmentScores(
|
||||
ultraShort = ultra.coerceIn(0, 100),
|
||||
shortTerm = short.coerceIn(0, 100),
|
||||
@ -1296,8 +1322,15 @@ class ScalpingAnalyzer {
|
||||
val volSurge = volRatioNow > VOL_SURGE_THRESHOLD
|
||||
val bbGood = bbPos > BB_LOWER_POS && bbPos < BB_UPPER_POS
|
||||
val maBull = currentClose > sma10Now && sma10Now > sma20Now
|
||||
val buySignal = maBull && rsiBull && volSurge && bbGood && isBreakout
|
||||
// val buySignal = maBull && rsiBull && volSurge && bbGood
|
||||
// val buySignal = maBull && rsiBull && volSurge && bbGood && isBreakout
|
||||
val ma5Daily = if (candles.size >= 5) candles.takeLast(5).map { it.close.toDouble() }.average() else currentClose
|
||||
val dailyDisparity = (currentClose / ma5Daily) * 100
|
||||
|
||||
// 과열 기준 정의
|
||||
val isOverheated = dailyDisparity > 110.0 // 일봉 5일선 대비 10% 이상 이격 시 과열로 간주
|
||||
|
||||
// 매수 신호 조건에 과열 방지 추가
|
||||
val buySignal = maBull && rsiBull && volSurge && bbGood && isBreakout && !isOverheated
|
||||
|
||||
|
||||
val score = (if (maBull) 25 else 0) +
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user