....
This commit is contained in:
parent
9c3d2f8d1e
commit
86d8cfeda4
@ -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()
|
||||
}
|
||||
|
||||
// [도구] 알림 전송 헬퍼
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user