...
This commit is contained in:
parent
57abcbf8f8
commit
7fa1c36355
@ -37,6 +37,7 @@ import model.AppConfig
|
|||||||
import model.KisSession
|
import model.KisSession
|
||||||
import network.DartCodeManager
|
import network.DartCodeManager
|
||||||
import network.KisTradeService
|
import network.KisTradeService
|
||||||
|
import network.KisWebSocketManager
|
||||||
import service.LlamaServerManager
|
import service.LlamaServerManager
|
||||||
import network.NewsService
|
import network.NewsService
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
@ -147,6 +148,7 @@ fun main() = application {
|
|||||||
AutoTradingManager.isSystemCleanedUpToday = false
|
AutoTradingManager.isSystemCleanedUpToday = false
|
||||||
CoroutineScope(Dispatchers.Default).launch {
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
AutoTradingManager.startAutoDiscoveryLoop()
|
AutoTradingManager.startAutoDiscoveryLoop()
|
||||||
|
KisWebSocketManager.onExecutionReceived = AutoTradingManager.onExecutionReceived
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.modelPath.isNotEmpty()) {
|
if (config.modelPath.isNotEmpty()) {
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import AutoTradeTable.orderNo
|
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import model.AppConfig
|
import model.AppConfig
|
||||||
@ -399,6 +398,21 @@ object TradingLogStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addSellLog(stockName: String,sellPrice : String , decision: String, log: String) {
|
||||||
|
synchronized(this) {
|
||||||
|
if (decisionLogs.size > 1000) decisionLogs.removeAt(0)
|
||||||
|
decisionLogs.add(
|
||||||
|
LogEntry(
|
||||||
|
time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")),
|
||||||
|
stockName = "${stockName}[${sellPrice}][]",
|
||||||
|
decision = decision,
|
||||||
|
confidence = 100.0,
|
||||||
|
reason = log
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun addLog(tradingDecision: TradingDecision , decision: String, log: String) {
|
fun addLog(tradingDecision: TradingDecision , decision: String, log: String) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (decisionLogs.size > 1000) decisionLogs.removeAt(0)
|
if (decisionLogs.size > 1000) decisionLogs.removeAt(0)
|
||||||
@ -414,5 +428,20 @@ object TradingLogStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addSettingLog(settingDesc : String, old : String, new : String, log: String) {
|
||||||
|
synchronized(this) {
|
||||||
|
if (decisionLogs.size > 1000) decisionLogs.removeAt(0)
|
||||||
|
decisionLogs.add(
|
||||||
|
LogEntry(
|
||||||
|
time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")),
|
||||||
|
stockName = "설정변경[${settingDesc}][$old]->[$new]",
|
||||||
|
decision = "SETTING",
|
||||||
|
confidence = 100.0,
|
||||||
|
reason = log
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import AutoTradeItem
|
import AutoTradeItem
|
||||||
import TradingDecision
|
import TradingDecision
|
||||||
|
import TradingLogStore
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import getLlamaBinPath
|
import getLlamaBinPath
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -112,7 +113,7 @@ object AutoTradingManager {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
// 5. 매수 실행 (계산된 finalMargin 전달)
|
// 5. 매수 실행 (계산된 finalMargin 전달)
|
||||||
AutoTradingManager.excuteTrade(
|
excuteTrade(
|
||||||
decision = completeTradingDecision,
|
decision = completeTradingDecision,
|
||||||
orderQty = min(calculatedQty, maxQty).toString(),
|
orderQty = min(calculatedQty, maxQty).toString(),
|
||||||
profitRate1 = finalMargin,
|
profitRate1 = finalMargin,
|
||||||
@ -120,7 +121,7 @@ object AutoTradingManager {
|
|||||||
)
|
)
|
||||||
|
|
||||||
} else if(totalScore >= (minScore * 0.85) && completeTradingDecision.confidence + append >= (MIN_CONFIDENCE * 0.85)) {
|
} else if(totalScore >= (minScore * 0.85) && completeTradingDecision.confidence + append >= (MIN_CONFIDENCE * 0.85)) {
|
||||||
AutoTradingManager.addToReanalysis(RankingStock(mksc_shrn_iscd = completeTradingDecision.stockCode,hts_kor_isnm = completeTradingDecision.stockName))
|
addToReanalysis(RankingStock(mksc_shrn_iscd = completeTradingDecision.stockCode,hts_kor_isnm = completeTradingDecision.stockName))
|
||||||
TradingLogStore.addLog(completeTradingDecision,"HOLD","✋ [관망] 토탈 스코어 또는 신뢰도 미달 이나 약간의 오차로 재분석 대기열에 추가")
|
TradingLogStore.addLog(completeTradingDecision,"HOLD","✋ [관망] 토탈 스코어 또는 신뢰도 미달 이나 약간의 오차로 재분석 대기열에 추가")
|
||||||
} else {
|
} else {
|
||||||
TradingLogStore.addLog(completeTradingDecision,"HOLD","✋ [관망] 토탈 스코어(${String.format("%.1f[${minScore}]", totalScore)}) 또는 신뢰도 (${String.format("%.1f[${MIN_CONFIDENCE}]", completeTradingDecision.confidence)}) 미달")
|
TradingLogStore.addLog(completeTradingDecision,"HOLD","✋ [관망] 토탈 스코어(${String.format("%.1f[${minScore}]", totalScore)}) 또는 신뢰도 (${String.format("%.1f[${MIN_CONFIDENCE}]", completeTradingDecision.confidence)}) 미달")
|
||||||
@ -242,7 +243,13 @@ object AutoTradingManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var onExecutionReceived : ((String, String, String, String, Boolean) -> Unit)? = {code, qty, price,orderNo, isBuy ->
|
||||||
|
scope.launch {
|
||||||
|
val exec = ExecutionData(orderNo, code, price, qty, isBuy)
|
||||||
|
executionCache[orderNo] = exec
|
||||||
|
syncAndExecute(orderNo)
|
||||||
|
}
|
||||||
|
}
|
||||||
val executionCache = mutableMapOf<String, ExecutionData>()
|
val executionCache = mutableMapOf<String, ExecutionData>()
|
||||||
val processingIds = mutableSetOf<String>() // 주문번호 기준 잠금
|
val processingIds = mutableSetOf<String>() // 주문번호 기준 잠금
|
||||||
suspend fun syncAndExecute(orderNo: String) {
|
suspend fun syncAndExecute(orderNo: String) {
|
||||||
@ -278,14 +285,15 @@ object AutoTradingManager {
|
|||||||
).onSuccess { newSellOrderNo ->
|
).onSuccess { newSellOrderNo ->
|
||||||
// 익절가 업데이트 및 상태 변경
|
// 익절가 업데이트 및 상태 변경
|
||||||
DatabaseFactory.updateStatusAndOrderNo(dbItem.id!!, TradeStatus.SELLING, newSellOrderNo)
|
DatabaseFactory.updateStatusAndOrderNo(dbItem.id!!, TradeStatus.SELLING, newSellOrderNo)
|
||||||
// (선택 사항) 실제 계산된 익절가를 DB에 기록하고 싶다면 별도 update 로직 추가 가능
|
TradingLogStore.addSellLog(dbItem.name,finalTargetPrice.toString(),"SELL","🎯 [매칭 성공] 익절 주문 실행: ${dbItem.name} | 매수가: ${actualBuyPrice.toInt()} -> 목표가: ${finalTargetPrice.toInt()} (${String.format("%.2f", finalProfitRate)}% 적용)")
|
||||||
|
|
||||||
executionCache.remove(orderNo)
|
executionCache.remove(orderNo)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
println("❌ 익절 주문 실패: ${it.message}")
|
println("❌ 익절 주문 실패: ${it.message}")
|
||||||
|
TradingLogStore.addSellLog(dbItem.name,finalTargetPrice.toString(),"SELL","❌ 익절 주문 실패: ${it.message}")
|
||||||
}
|
}
|
||||||
} else if (dbItem.status == TradeStatus.SELLING) {
|
} else if (dbItem.status == TradeStatus.SELLING) {
|
||||||
println("🎊 [매칭 성공] 매도 완료 처리: ${dbItem.name}")
|
println("🎊 [매칭 성공] 매도 완료 처리: ${dbItem.name}")
|
||||||
|
TradingLogStore.addSellLog(dbItem.name,execData.price,"SELL","🎊 [매칭 성공] 매도 완료 처리")
|
||||||
DatabaseFactory.updateStatusAndOrderNo(dbItem.id!!, TradeStatus.COMPLETED)
|
DatabaseFactory.updateStatusAndOrderNo(dbItem.id!!, TradeStatus.COMPLETED)
|
||||||
executionCache.remove(orderNo)
|
executionCache.remove(orderNo)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
|
|
||||||
import AutoTradeItem
|
|
||||||
import TradingDecision
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
@ -14,7 +12,6 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@ -25,7 +22,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import model.ConfigIndex
|
import model.ConfigIndex
|
||||||
import model.KisSession
|
import model.KisSession
|
||||||
import service.TechnicalAnalyzer
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TradingDecisionLog() {
|
fun TradingDecisionLog() {
|
||||||
@ -45,7 +41,13 @@ fun TradingDecisionLog() {
|
|||||||
Text("${log.time} - ${log.stockName}", fontWeight = FontWeight.Bold)
|
Text("${log.time} - ${log.stockName}", fontWeight = FontWeight.Bold)
|
||||||
Text(
|
Text(
|
||||||
text = log.decision,
|
text = log.decision,
|
||||||
color = if (log.decision == "BUY") Color.Red else Color.Gray,
|
color = when (log.decision) {
|
||||||
|
"BUY" -> Color.Red
|
||||||
|
"SETTING" -> Color(0xFFFFA500) // 주황색
|
||||||
|
"SELL" -> Color(0xFF800080) // 보라색
|
||||||
|
"HOLD" -> Color.Gray // HOLD는 그레이
|
||||||
|
else -> Color.Gray // 그 외 기본값 그레이
|
||||||
|
},
|
||||||
fontWeight = FontWeight.ExtraBold
|
fontWeight = FontWeight.ExtraBold
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -63,6 +65,7 @@ fun TradingDecisionLog() {
|
|||||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
modifier = Modifier.fillMaxWidth().fillMaxHeight().background(Color.White)
|
modifier = Modifier.fillMaxWidth().fillMaxHeight().background(Color.White)
|
||||||
) {
|
) {
|
||||||
|
var firstSet = mutableSetOf<ConfigIndex>()
|
||||||
item(span = { GridItemSpan(maxLineSpan) }) { // 2열을 모두 차지함
|
item(span = { GridItemSpan(maxLineSpan) }) { // 2열을 모두 차지함
|
||||||
Text(
|
Text(
|
||||||
"💰 거래 기본 설정",
|
"💰 거래 기본 설정",
|
||||||
@ -92,9 +95,16 @@ fun TradingDecisionLog() {
|
|||||||
// 저장 로직을 공통 함수로 분리
|
// 저장 로직을 공통 함수로 분리
|
||||||
val saveAction = {
|
val saveAction = {
|
||||||
var newValue = localText.toDoubleOrNull() ?: 0.0
|
var newValue = localText.toDoubleOrNull() ?: 0.0
|
||||||
|
var oldValue = KisSession.config.getValues(configKey)
|
||||||
if (configKey.label.contains("PROFIT")) {
|
if (configKey.label.contains("PROFIT")) {
|
||||||
newValue = newValue / KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)
|
newValue = newValue / KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)
|
||||||
}
|
}
|
||||||
|
if (firstSet.contains(configKey)) {
|
||||||
|
TradingLogStore.addSettingLog(configKey.label,oldValue.toString(),newValue.toString(),"💾 저장됨: ${configKey.label} = $newValue")
|
||||||
|
} else {
|
||||||
|
firstSet.add(configKey)
|
||||||
|
}
|
||||||
|
|
||||||
KisSession.config.setValues(configKey, newValue)
|
KisSession.config.setValues(configKey, newValue)
|
||||||
DatabaseFactory.saveConfig(KisSession.config)
|
DatabaseFactory.saveConfig(KisSession.config)
|
||||||
println("💾 저장됨: ${configKey.label} = $newValue")
|
println("💾 저장됨: ${configKey.label} = $newValue")
|
||||||
@ -173,11 +183,16 @@ fun TradingDecisionLog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val saveAction = {
|
val saveAction = {
|
||||||
|
var oldValue = KisSession.config.getValues(configKey)
|
||||||
var newValue = localText.toDoubleOrNull() ?: 0.0
|
var newValue = localText.toDoubleOrNull() ?: 0.0
|
||||||
//
|
//
|
||||||
KisSession.config.setValues(configKey, newValue)
|
KisSession.config.setValues(configKey, newValue)
|
||||||
DatabaseFactory.saveConfig(KisSession.config)
|
DatabaseFactory.saveConfig(KisSession.config)
|
||||||
println("💾 저장됨: ${configKey.label} = $newValue")
|
if (firstSet.contains(configKey)) {
|
||||||
|
TradingLogStore.addSettingLog(configKey.label,oldValue.toString(),newValue.toString(),"💾 저장됨: ${configKey.label} = $newValue")
|
||||||
|
} else {
|
||||||
|
firstSet.add(configKey)
|
||||||
|
}
|
||||||
labelText = if (configKey.name.contains("PROFIT")) {
|
labelText = if (configKey.name.contains("PROFIT")) {
|
||||||
getRemaining(configKey.label,common) + ": 기준율(${KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)}) * 성향별 비율(${KisSession.config.getValues(configKey)}) + 세금제비용(${KisSession.config.getValues(
|
getRemaining(configKey.label,common) + ": 기준율(${KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)}) * 성향별 비율(${KisSession.config.getValues(configKey)}) + 세금제비용(${KisSession.config.getValues(
|
||||||
ConfigIndex.TAX_INDEX)}) = ${(localText.toDouble() * KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)) + KisSession.config.getValues(
|
ConfigIndex.TAX_INDEX)}) = ${(localText.toDouble() * KisSession.config.getValues(ConfigIndex.PROFIT_INDEX)) + KisSession.config.getValues(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user