..
This commit is contained in:
parent
0413fa3e2e
commit
d7efc433bd
@ -328,6 +328,7 @@ fun main() = application {
|
|||||||
// DashboardScreen()
|
// DashboardScreen()
|
||||||
}
|
}
|
||||||
AppScreen.TradingDecision -> {
|
AppScreen.TradingDecision -> {
|
||||||
|
|
||||||
TradingDecisionLog()
|
TradingDecisionLog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,16 @@
|
|||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import model.AppConfig
|
import model.AppConfig
|
||||||
import model.TradingDecision
|
import model.TradingDecision
|
||||||
|
import network.NewsService
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
import org.jetbrains.exposed.sql.javatime.datetime
|
import org.jetbrains.exposed.sql.javatime.datetime
|
||||||
|
import org.jetbrains.exposed.sql.transactions.experimental.suspendedTransactionAsync
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import report.TradingReportManager
|
import report.TradingReportManager
|
||||||
import report.TradingReportService
|
import report.TradingReportService
|
||||||
@ -509,11 +515,21 @@ object TradingLogStore {
|
|||||||
decisionLogs.add(
|
decisionLogs.add(
|
||||||
LogEntry(
|
LogEntry(
|
||||||
time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")),
|
time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")),
|
||||||
stockName = "${tradingDecision.stockName}[${tradingDecision.currentPrice}][]",
|
stockName = "${tradingDecision.stockName}[${tradingDecision.currentPrice}]",
|
||||||
decision = decision,
|
decision = decision,
|
||||||
confidence = tradingDecision.confidence,
|
confidence = tradingDecision.confidence,
|
||||||
reason = log
|
reason = log
|
||||||
)
|
).apply {
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
println("CALLED sendTelegramMessage -1")
|
||||||
|
if (decision.contains("WATCH") || ((tradingDecision.investmentGrade?.ordinal
|
||||||
|
?: 0) < 2)
|
||||||
|
) {
|
||||||
|
println("CALLED sendTelegramMessage OK")
|
||||||
|
NewsService.sendTelegramMessage("${this@apply.decision} ${tradingDecision.stockName}[${tradingDecision.currentPrice}] ${log}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -545,8 +561,14 @@ object TradingLogStore {
|
|||||||
decision = "NOTICE",
|
decision = "NOTICE",
|
||||||
confidence = 100.0,
|
confidence = 100.0,
|
||||||
reason = log
|
reason = log
|
||||||
)
|
).apply {
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
println("CALLED sendTelegramMessage")
|
||||||
|
NewsService.sendTelegramMessage("${this@apply.decision}$name[$code] ${log}")
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -240,6 +240,7 @@ class TradeConfig {
|
|||||||
var start_buy_time : String = "08:55"
|
var start_buy_time : String = "08:55"
|
||||||
var end_buy_time : String = "15:10"
|
var end_buy_time : String = "15:10"
|
||||||
var enableOverSea : Boolean = false
|
var enableOverSea : Boolean = false
|
||||||
|
var tlg_id : String = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@ object KisTradeService {
|
|||||||
// [수정] 모든 로그(Headers + Body)를 찍도록 설정
|
// [수정] 모든 로그(Headers + Body)를 찍도록 설정
|
||||||
install(Logging) {
|
install(Logging) {
|
||||||
logger = Logger.DEFAULT
|
logger = Logger.DEFAULT
|
||||||
level = LogLevel.NONE
|
level = LogLevel.ALL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,10 +17,14 @@ import io.ktor.client.request.post
|
|||||||
import io.ktor.client.request.setBody
|
import io.ktor.client.request.setBody
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import io.ktor.client.statement.bodyAsText
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.http.ContentType.Application.Json
|
import io.ktor.http.ContentType.Application.Json
|
||||||
import io.ktor.http.HttpHeaders
|
import io.ktor.http.HttpHeaders
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.http.Parameters
|
import io.ktor.http.Parameters
|
||||||
import io.ktor.http.Url
|
import io.ktor.http.Url
|
||||||
|
import io.ktor.http.contentType
|
||||||
|
import io.ktor.network.tls.TLSConfigBuilder
|
||||||
import io.ktor.serialization.kotlinx.json.json
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@ -29,10 +33,16 @@ import model.KisSession
|
|||||||
import model.NaverNewsResponse
|
import model.NaverNewsResponse
|
||||||
import service.SafeScraper
|
import service.SafeScraper
|
||||||
import service.UrlCacheManager
|
import service.UrlCacheManager
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.net.URL
|
||||||
|
import java.net.URLEncoder
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
import javax.net.ssl.SSLContext
|
||||||
import kotlin.Double
|
import kotlin.Double
|
||||||
|
|
||||||
object NewsService {
|
object NewsService {
|
||||||
@ -122,4 +132,62 @@ object NewsService {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun sendTelegramMessage(data: String) {
|
||||||
|
Thread {
|
||||||
|
try {
|
||||||
|
|
||||||
|
var chatId = KisSession.tradeConfig.tlg_id
|
||||||
|
println("sendTelegramMessage $chatId")
|
||||||
|
sendViaSystemCurl("https://lunaticbum.kr/tlg/sendToMe.bjx",chatId,data)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendViaSystemCurl(url : String, chatId: String, message: String) {
|
||||||
|
try {
|
||||||
|
// 메시지 내 공백이나 한글이 깨지지 않도록 인코딩 (필수)
|
||||||
|
val encodedMessage = URLEncoder.encode(message, "UTF-8")
|
||||||
|
|
||||||
|
// OS 확인
|
||||||
|
val isWindows = System.getProperty("os.name").lowercase().contains("win")
|
||||||
|
|
||||||
|
val command = if (isWindows) {
|
||||||
|
// 윈도우용: 큰따옴표 이스케이프에 주의해야 합니다.
|
||||||
|
val jsonBody = "{\"id\":\"$chatId\",\"message\":\"$encodedMessage\"}"
|
||||||
|
listOf("cmd", "/c", "curl -s -X POST $url -H \"Content-Type: application/json\" -d \"$jsonBody\"")
|
||||||
|
} else {
|
||||||
|
// 맥/리눅스용: 홑따옴표를 사용하여 JSON 구조를 보호합니다.
|
||||||
|
val jsonBody = "{\"id\":\"$chatId\",\"message\":\"$message\"}"
|
||||||
|
listOf("curl", "-s", "-X", "POST", url, "-H", "Content-Type: application/json", "-d", jsonBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
val process = ProcessBuilder(command)
|
||||||
|
.redirectErrorStream(true) // 에러 출력(stderr)을 표준 출력(stdout)으로 합침
|
||||||
|
.start()
|
||||||
|
|
||||||
|
// 프로세스의 출력을 읽어오는 블록
|
||||||
|
BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
|
||||||
|
val output = StringBuilder()
|
||||||
|
var line: String?
|
||||||
|
while (reader.readLine().also { line = it } != null) {
|
||||||
|
output.append(line).append("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
val exitCode = process.waitFor() // 프로세스가 종료될 때까지 대기
|
||||||
|
|
||||||
|
println("--- Telegram Curl Log Start ---")
|
||||||
|
println("Exit Code: $exitCode") // 0이면 성공, 그 외는 curl 에러 코드
|
||||||
|
println("Response:\n$output")
|
||||||
|
println("--- Telegram Curl Log End ---")
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("시스템 명령어 실행 중 예외 발생: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,7 @@ import java.awt.Toolkit
|
|||||||
import java.awt.datatransfer.DataFlavor
|
import java.awt.datatransfer.DataFlavor
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import androidx.compose.ui.input.key.*
|
import androidx.compose.ui.input.key.*
|
||||||
|
import network.NewsService
|
||||||
|
|
||||||
fun getPastedPathFromClipboard(): String? {
|
fun getPastedPathFromClipboard(): String? {
|
||||||
val clipboard = Toolkit.getDefaultToolkit().systemClipboard
|
val clipboard = Toolkit.getDefaultToolkit().systemClipboard
|
||||||
@ -138,6 +139,7 @@ fun SettingsScreen(onAuthSuccess: () -> Unit) {
|
|||||||
SystemSleepPreventer.wakeDisplay() // 모니터 켜기
|
SystemSleepPreventer.wakeDisplay() // 모니터 켜기
|
||||||
statusMessage = "⏰ 자동 실행 시간(08:30)입니다. 시스템을 가동합니다."
|
statusMessage = "⏰ 자동 실행 시간(08:30)입니다. 시스템을 가동합니다."
|
||||||
authenticateAndStart()
|
authenticateAndStart()
|
||||||
|
|
||||||
break // 성공하면 루프 탈출
|
break // 성공하면 루프 탈출
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,10 +42,13 @@ import androidx.compose.ui.text.input.ImeAction
|
|||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
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.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import model.ConfigIndex
|
import model.ConfigIndex
|
||||||
import model.KisSession
|
import model.KisSession
|
||||||
import network.KisTradeService
|
import network.KisTradeService
|
||||||
|
import network.NewsService
|
||||||
import network.StockUniverseLoader
|
import network.StockUniverseLoader
|
||||||
import service.AutoTradingManager
|
import service.AutoTradingManager
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -62,7 +65,13 @@ fun TradingDecisionLog() {
|
|||||||
var llmAnalyser by remember { mutableStateOf(AutoTradingManager.llmAnalyser) }
|
var llmAnalyser by remember { mutableStateOf(AutoTradingManager.llmAnalyser) }
|
||||||
val tradeConfig by remember {
|
val tradeConfig by remember {
|
||||||
KisSession.tradeConfig = KisSession.loadTradeConfig()
|
KisSession.tradeConfig = KisSession.loadTradeConfig()
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
println("CALLED sendTelegramMessage -1")
|
||||||
|
val now = java.time.LocalTime.now(java.time.ZoneId.of("Asia/Seoul"))
|
||||||
|
NewsService.sendTelegramMessage("⏰ 자동 실행 시간(${now.hour}:${now.minute})입니다. 시스템을 가동합니다.")
|
||||||
|
}
|
||||||
mutableStateOf(KisSession.tradeConfig)
|
mutableStateOf(KisSession.tradeConfig)
|
||||||
|
|
||||||
}
|
}
|
||||||
LaunchedEffect(AutoTradingManager.llmAnalyser) {
|
LaunchedEffect(AutoTradingManager.llmAnalyser) {
|
||||||
llmAnalyser = AutoTradingManager.llmAnalyser
|
llmAnalyser = AutoTradingManager.llmAnalyser
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user