This commit is contained in:
lunaticbum 2026-01-19 17:09:08 +09:00
parent 9c3d2f8d1e
commit 86d8cfeda4
4 changed files with 78 additions and 78 deletions

View File

@ -34,7 +34,7 @@ class StockMonitorService(
// [신규] 1분마다 시장 상태 변경 체크 및 알림 발송
// (매분 0초에 실행)
@Scheduled(cron = "0 * * * * *")
// @Scheduled(cron = "0 * * * * *")
fun checkMarketStatusAndNotify() {
// 1. 현재 상태 조회
val currentStatus = MarketTimeManager.getCurrentStatus()
@ -65,11 +65,11 @@ class StockMonitorService(
@PostConstruct
fun init() {
// Reactive 리포지토리이므로 runBlocking으로 데이터를 가져옵니다.
val savedTasks = runBlocking {
autoTradeRepository.findAll().collectList().awaitSingle()
}
monitoringList.addAll(savedTasks)
println(">>> [StockMonitor] MongoDB에서 ${savedTasks.size}개의 자동매매 작업을 복구했습니다.")
// val savedTasks = runBlocking {
// autoTradeRepository.findAll().collectList().awaitSingle()
// }
// monitoringList.addAll(savedTasks)
// println(">>> [StockMonitor] MongoDB에서 ${savedTasks.size}개의 자동매매 작업을 복구했습니다.")
}
// 2. 감시 대상 등록 (MongoDB 저장 + 메모리 추가)
@ -103,8 +103,8 @@ class StockMonitorService(
// [신규] 손절 수익률 수정 기능
suspend fun updateStopLossRate(id: String, newRate: Double): Boolean {
val task = monitoringList.find { it.id == id } ?: return false
task.stopLossRate = newRate
autoTradeRepository.save(task).awaitSingle()
// task.stopLossRate = newRate
// autoTradeRepository.save(task).awaitSingle()
return true
}
@ -125,25 +125,25 @@ class StockMonitorService(
// [신규] 감시 작업 취소 (삭제)
suspend fun cancelMonitoring(id: String): Boolean {
val task = monitoringList.find { it.id == id }
if (task != null) {
monitoringList.remove(task)
autoTradeRepository.deleteById(id).awaitSingle()
return true
}
// val task = monitoringList.find { it.id == id }
// if (task != null) {
// monitoringList.remove(task)
// autoTradeRepository.deleteById(id).awaitSingle()
// return true
// }
return false
}
// [신규] 목표 수익률 수정
suspend fun updateTargetRate(id: String, newRate: Double): Boolean {
val task = monitoringList.find { it.id == id }
if (task != null) {
// 메모리 업데이트
task.targetProfitRate = newRate
// DB 업데이트
autoTradeRepository.save(task).awaitSingle()
return true
}
// val task = monitoringList.find { it.id == id }
// if (task != null) {
// // 메모리 업데이트
// task.targetProfitRate = newRate
// // DB 업데이트
// autoTradeRepository.save(task).awaitSingle()
// return true
// }
return false
}
@ -155,33 +155,33 @@ class StockMonitorService(
// 3. 주기적 실행 (Scheduler에서 호출)
fun checkAndExecuteAutoSell() {
if (!MarketTimeManager.isTradeable()) {
// (선택사항) 장 마감 중에는 로그를 남기지 않거나, 디버깅용으로만 남김
// println(">>> 장 마감 시간입니다. 자동매매 스킵")
return
}
if (monitoringList.isEmpty()) return
// 스케줄러는 동기식이므로 runBlocking 블록 내에서 비동기 작업을 수행합니다.
runBlocking {
monitoringList.forEach { task ->
try {
checkPriceAndTrade(task)
} catch (e: Exception) {
// 토큰 만료 에러 감지 시
if (isTokenExpiredError(e)) {
println(">>> [토큰 만료 감지] ${task.stockCode} 작업의 토큰을 갱신합니다.")
if (refreshToken(task)) {
// 갱신 성공 시 재시도
try { checkPriceAndTrade(task) } catch (e2: Exception) { e2.printStackTrace() }
}
} else {
println(">>> [자동매매 오류] ${task.stockCode}: ${e.message}")
}
}
}
}
// if (!MarketTimeManager.isTradeable()) {
// // (선택사항) 장 마감 중에는 로그를 남기지 않거나, 디버깅용으로만 남김
// // println(">>> 장 마감 시간입니다. 자동매매 스킵")
// return
// }
//
// if (monitoringList.isEmpty()) return
//
// // 스케줄러는 동기식이므로 runBlocking 블록 내에서 비동기 작업을 수행합니다.
// runBlocking {
// monitoringList.forEach { task ->
// try {
// checkPriceAndTrade(task)
// } catch (e: Exception) {
// // 토큰 만료 에러 감지 시
// if (isTokenExpiredError(e)) {
// println(">>> [토큰 만료 감지] ${task.stockCode} 작업의 토큰을 갱신합니다.")
// if (refreshToken(task)) {
// // 갱신 성공 시 재시도
// try { checkPriceAndTrade(task) } catch (e2: Exception) { e2.printStackTrace() }
// }
// } else {
// println(">>> [자동매매 오류] ${task.stockCode}: ${e.message}")
// }
// }
// }
// }
}
// 시세 확인 및 매도 로직
@ -207,26 +207,26 @@ class StockMonitorService(
private suspend fun executeSell(task: AutoTradeEntity, auth: KisAuthSession, price: Double, rate: Double, typeMsg: String) {
// 매도 주문
val res = kisApiService.orderStock(auth, "SELL", task.stockCode, task.quantity.toString(), "0").awaitSingle()
val output = res["output"] as? Map<String, Any> ?: emptyMap()
val orderNo = output["ODNO"] as? String ?: "Unknown"
val msg = "$typeMsg (수익률 ${String.format("%.2f", rate)}%)"
// 히스토리 저장
saveHistory(task.stockCode, task.stockName, "SELL", price, task.quantity, orderNo, true, msg)
// [알림] 텔레그램 전송
sendAlert(
"$typeMsg 실행 완료!\n" +
"종목: ${task.stockName}\n" +
"수익률: ${String.format("%.2f", rate)}%\n" +
"체결가: ${price.toInt()}"
)
// 목록 제거
monitoringList.remove(task)
autoTradeRepository.delete(task).awaitSingle()
// val res = kisApiService.orderStock(auth, "SELL", task.stockCode, task.quantity.toString(), "0").awaitSingle()
// val output = res["output"] as? Map<String, Any> ?: emptyMap()
// val orderNo = output["ODNO"] as? String ?: "Unknown"
//
// val msg = "$typeMsg (수익률 ${String.format("%.2f", rate)}%)"
//
// // 히스토리 저장
// saveHistory(task.stockCode, task.stockName, "SELL", price, task.quantity, orderNo, true, msg)
//
// // [알림] 텔레그램 전송
// sendAlert(
// "$typeMsg 실행 완료!\n" +
// "종목: ${task.stockName}\n" +
// "수익률: ${String.format("%.2f", rate)}%\n" +
// "체결가: ${price.toInt()}원"
// )
//
// // 목록 제거
// monitoringList.remove(task)
// autoTradeRepository.delete(task).awaitSingle()
}
// [도구] 알림 전송 헬퍼

View File

@ -1,8 +1,8 @@
spring.application.name=lun
server.port=443
spring.datasource.username=c
spring.datasource.password=c
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
#spring.datasource.username=c
#spring.datasource.password=c
#spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
#<<<<<<< HEAD
#spring.data.mongodb.host=nas.lunaticbum.kr
#spring.data.mongodb.host=localhost

View File

@ -1,8 +1,8 @@
spring.application.name=lun
server.port=443
spring.datasource.username=c
spring.datasource.password=c
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
#spring.datasource.username=c
#spring.datasource.password=c
#spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
#<<<<<<< HEAD
#spring.data.mongodb.host=nas.lunaticbum.kr
#spring.data.mongodb.host=localhost

View File

@ -1,8 +1,8 @@
spring.application.name=lun
server.port=443
spring.datasource.username=c
spring.datasource.password=c
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
#spring.datasource.username=c
#spring.datasource.password=c
#spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
#<<<<<<< HEAD
#spring.data.mongodb.host=nas.lunaticbum.kr
#spring.data.mongodb.host=localhost