...
This commit is contained in:
parent
1787b72499
commit
93d8b3f0aa
@ -2,8 +2,9 @@ package model
|
|||||||
|
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
const val feesAndTaxRate = 0.3
|
const val feesAndTaxRate = 0.33
|
||||||
const val minimumNetProfit = 0.8
|
const val minimumNetProfit = 0.4
|
||||||
|
const val buyWeight = 2.0
|
||||||
|
|
||||||
data class AppConfig(
|
data class AppConfig(
|
||||||
// [DB 저장 데이터]
|
// [DB 저장 데이터]
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import androidx.compose.ui.unit.TextUnit
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import model.buyWeight
|
||||||
import model.feesAndTaxRate
|
import model.feesAndTaxRate
|
||||||
import model.minimumNetProfit
|
import model.minimumNetProfit
|
||||||
import network.KisTradeService
|
import network.KisTradeService
|
||||||
@ -72,7 +73,7 @@ fun IntegratedOrderSection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var profitRate by remember(monitoringItem) {
|
var profitRate by remember(monitoringItem) {
|
||||||
mutableStateOf(monitoringItem?.profitRate?.toString() ?: "0.8")
|
mutableStateOf(monitoringItem?.profitRate?.toString() ?: minimumNetProfit.toString())
|
||||||
}
|
}
|
||||||
var stopLossRate by remember(monitoringItem) {
|
var stopLossRate by remember(monitoringItem) {
|
||||||
mutableStateOf(monitoringItem?.stopLossRate?.toString() ?: "-1.5")
|
mutableStateOf(monitoringItem?.stopLossRate?.toString() ?: "-1.5")
|
||||||
@ -83,7 +84,7 @@ fun IntegratedOrderSection(
|
|||||||
val basePrice = (if (orderPrice.isEmpty()) curPriceNum else orderPrice.toDoubleOrNull() ?: 0.0)
|
val basePrice = (if (orderPrice.isEmpty()) curPriceNum else orderPrice.toDoubleOrNull() ?: 0.0)
|
||||||
val inputQty = orderQty.replace(",", "").toIntOrNull() ?: 0
|
val inputQty = orderQty.replace(",", "").toIntOrNull() ?: 0
|
||||||
|
|
||||||
fun excuteTrade(willEnableAutoSell: Boolean,orderQty: String) {
|
fun excuteTrade(willEnableAutoSell: Boolean, orderQty: String, profitRate1: Double?) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val tickSize = MarketUtil.getTickSize(basePrice)
|
val tickSize = MarketUtil.getTickSize(basePrice)
|
||||||
val oneTickLowerPrice = basePrice - tickSize
|
val oneTickLowerPrice = basePrice - tickSize
|
||||||
@ -108,7 +109,7 @@ fun IntegratedOrderSection(
|
|||||||
|
|
||||||
// 3. 실질 목표 수익률 계산
|
// 3. 실질 목표 수익률 계산
|
||||||
// 사용자가 입력한 pRate와 (최소 순수익 + 제반 비용) 중 큰 값을 선택합니다.
|
// 사용자가 입력한 pRate와 (최소 순수익 + 제반 비용) 중 큰 값을 선택합니다.
|
||||||
val effectiveProfitRate = maxOf(pRate, minimumNetProfit + feesAndTaxRate)
|
val effectiveProfitRate = maxOf((profitRate1 ?: pRate) + feesAndTaxRate, minimumNetProfit + feesAndTaxRate)
|
||||||
|
|
||||||
// 4. 보정된 수익률을 적용하여 목표가 계산
|
// 4. 보정된 수익률을 적용하여 목표가 계산
|
||||||
val calculatedTarget = MarketUtil.roundToTickSize(basePrice * (1 + effectiveProfitRate / 100.0))
|
val calculatedTarget = MarketUtil.roundToTickSize(basePrice * (1 + effectiveProfitRate / 100.0))
|
||||||
@ -152,25 +153,50 @@ fun IntegratedOrderSection(
|
|||||||
profitPossible : ${completeTradingDecision.profitPossible()+ append}
|
profitPossible : ${completeTradingDecision.profitPossible()+ append}
|
||||||
safePossible : ${completeTradingDecision.safePossible()+ append}
|
safePossible : ${completeTradingDecision.safePossible()+ append}
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
// 2. 조건 검사: 신뢰도 80 이상 AND 중기 점수 70 이상
|
|
||||||
if (completeTradingDecision.confidence + append >= MIN_CONFIDENCE &&
|
|
||||||
completeTradingDecision.shortPossible() + append >= MIN_SHORT_SCORE &&
|
|
||||||
completeTradingDecision.profitPossible() + append >= MIN_POSSIBLE_SCORE &&
|
|
||||||
completeTradingDecision.safePossible() + append >= MIN_SAFE_SCORE
|
|
||||||
) {
|
|
||||||
|
|
||||||
println("🚀 [조건 만족] 강력 매수 시그널 포착 -> 자동 매수 진행 (1주) ${completeTradingDecision.stockCode}")
|
val weights = mapOf(
|
||||||
// 3. 매수 실행 (자동 감시 켜기: true, 수량: 1주)
|
"short" to 0.3, // 초단기 점수가 낮아도 전체에 미치는 영향 감소
|
||||||
// 수량은 필요에 따라 로직으로 계산하여 변경 가능 (예: 자산의 10% 등)
|
"profit" to 0.3,
|
||||||
excuteTrade(willEnableAutoSell = true, orderQty = "1")
|
"safe" to 0.4 // 중장기 점수 비중 강화
|
||||||
|
)
|
||||||
|
|
||||||
|
// 2. 토탈 스코어 계산
|
||||||
|
val totalScore =
|
||||||
|
(completeTradingDecision.shortPossible() * weights["short"]!!) +
|
||||||
|
(completeTradingDecision.profitPossible() * weights["profit"]!!) +
|
||||||
|
(completeTradingDecision.safePossible() * weights["safe"]!!)
|
||||||
|
|
||||||
|
// 3. 매수 결정 문턱값 (예: 70점 이상이면 매수 가능)
|
||||||
|
val MIN_PURCHASE_SCORE = 70.0
|
||||||
|
val HIGH_QUALITY_SCORE = 85.0 // 강력 추천 기준
|
||||||
|
|
||||||
|
if (totalScore >= MIN_PURCHASE_SCORE && completeTradingDecision.confidence > MIN_CONFIDENCE) {
|
||||||
|
|
||||||
|
// 4. 점수에 따른 가변 마진 적용
|
||||||
|
// 토탈 스코어가 85점 이상이면 마진을 3.0으로 고정하거나 추가 가산(append) 적용
|
||||||
|
val finalMargin = if (totalScore >= HIGH_QUALITY_SCORE) {
|
||||||
|
println("💎 [우량주 포착] 토탈 스코어($totalScore)가 매우 높아 목표 마진을 3.0%로 상향합니다.")
|
||||||
|
minimumNetProfit + (append * 1.5)
|
||||||
|
} else {
|
||||||
|
minimumNetProfit + append
|
||||||
|
}
|
||||||
|
|
||||||
|
println("🚀 [매수 진행] 토탈 스코어: ${String.format("%.1f", totalScore)} -> 종목: ${completeTradingDecision.stockCode}")
|
||||||
|
|
||||||
|
// 5. 매수 실행 (계산된 finalMargin 전달)
|
||||||
|
excuteTrade(
|
||||||
|
willEnableAutoSell = true,
|
||||||
|
orderQty = "1",
|
||||||
|
profitRate1 = finalMargin
|
||||||
|
)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
println("✋ [조건 미달] 매수 의견이나 점수 부족으로 관망")
|
println("✋ [관망] 토탈 스코어(${String.format("%.1f", totalScore)})가 기준치($MIN_PURCHASE_SCORE) 미달")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when (completeTradingDecision?.decision) {
|
when (completeTradingDecision?.decision) {
|
||||||
"BUY" -> {
|
"BUY" -> {
|
||||||
append = 3.0
|
append = buyWeight
|
||||||
println("[$stockCode] 매수 추천 resultCheck: ${completeTradingDecision?.reason}")
|
println("[$stockCode] 매수 추천 resultCheck: ${completeTradingDecision?.reason}")
|
||||||
resultCheck(completeTradingDecision)
|
resultCheck(completeTradingDecision)
|
||||||
}
|
}
|
||||||
@ -274,7 +300,7 @@ fun IntegratedOrderSection(
|
|||||||
// 매수 버튼
|
// 매수 버튼
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
excuteTrade(willEnableAutoSell,orderQty)
|
excuteTrade(willEnableAutoSell, orderQty, profitRate.toDouble())
|
||||||
},
|
},
|
||||||
modifier = Modifier.weight(1f).padding(end = 4.dp),
|
modifier = Modifier.weight(1f).padding(end = 4.dp),
|
||||||
colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFFE03E2D))
|
colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFFE03E2D))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user