리포팅 테스트

This commit is contained in:
2026-04-16 15:48:23 +09:00
parent 68a7a05961
commit ed12d07bc2
9 changed files with 890 additions and 162 deletions
+49 -24
View File
@@ -1,11 +1,17 @@
import androidx.compose.runtime.mutableStateListOf
import kotlinx.serialization.Serializable
import model.AppConfig
import network.TradingDecision
import model.TradingDecision
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.datetime
import org.jetbrains.exposed.sql.transactions.transaction
import report.TradingReportManager
import report.TradingReportService
import report.database.AssetSnapshotTable
import report.database.ExecutionDetailsTable
import report.database.SnapshotHoldingsTable
import report.database.TradeHistoryTable
import java.io.File
import java.time.LocalDateTime
import java.time.LocalTime
@@ -116,21 +122,40 @@ object HolidayTable : Table("holiday_cache") {
}
object DatabaseFactory {
val reporter: TradingReportService get() = TradingReportManager
lateinit var mainDb: Database
lateinit var reportDb: Database
fun init() {
val dbPath = File("db/autotrade_db").absolutePath
Database.connect(
"jdbc:h2:$dbPath;DB_CLOSE_DELAY=-1;",
mainDb = Database.connect(
"jdbc:h2:${File("db/autotrade_db").absolutePath};DB_CLOSE_DELAY=-1;",
driver = "org.h2.Driver"
)
transaction {
reportDb = Database.connect(
"jdbc:h2:${File("db/trade_report").absolutePath};DB_CLOSE_DELAY=-1;",
driver = "org.h2.Driver"
)
transaction(reportDb) {
SchemaUtils.createMissingTablesAndColumns(
AssetSnapshotTable,
SnapshotHoldingsTable,
TradeHistoryTable,
ExecutionDetailsTable
)
}
transaction(mainDb) {
// 테이블 생성 (AutoTradeTable 포함)
SchemaUtils.createMissingTablesAndColumns(ConfigTable, TradeLogTable, AutoTradeTable,HolidayTable)
}
}
fun saveHoliday(date: String, holiday: Boolean) = transaction {
fun saveHoliday(date: String, holiday: Boolean) = transaction(mainDb) {
HolidayTable.replace {
it[bassDt] = date
it[isHoliday] = holiday
@@ -138,7 +163,7 @@ object DatabaseFactory {
}
// 특정 날짜의 휴장 여부 조회
fun getHoliday(date: String): Boolean? = transaction {
fun getHoliday(date: String): Boolean? = transaction(mainDb) {
HolidayTable.select { HolidayTable.bassDt eq date }
.map { it[HolidayTable.isHoliday] }
.singleOrNull()
@@ -147,7 +172,7 @@ object DatabaseFactory {
/**
* 새로운 자동매매 건 등록 (주로 PENDING_BUY 상태로 시작)
*/
fun saveAutoTrade(item: AutoTradeItem) = transaction {
fun saveAutoTrade(item: AutoTradeItem) = transaction(mainDb) {
AutoTradeTable.insert {
it[stockCode] = item.code
it[stockName] = item.name
@@ -165,7 +190,7 @@ object DatabaseFactory {
/**
* 상태 변경 및 가격 업데이트 (예: PENDING_BUY -> MONITORING)
*/
fun updateAutoTrade(item: AutoTradeItem) = transaction {
fun updateAutoTrade(item: AutoTradeItem) = transaction(mainDb) {
val id = item.id ?: return@transaction
AutoTradeTable.update({ AutoTradeTable.id eq id }) {
it[targetPrice] = item.targetPrice
@@ -178,7 +203,7 @@ object DatabaseFactory {
/**
* 감시 중인 모든 종목 리스트 반환 (ActiveTradeSection UI용)
*/
fun getActiveAutoTrades(): List<AutoTradeItem> = transaction {
fun getActiveAutoTrades(): List<AutoTradeItem> = transaction(mainDb) {
AutoTradeTable.select {
AutoTradeTable.status inList listOf("MONITORING", "SELLING", "PENDING_BUY")
}.map { mapToAutoTradeItem(it) }
@@ -187,18 +212,18 @@ object DatabaseFactory {
/**
* 종목코드로 현재 감시 중인 설정이 있는지 확인 (UI 체크박스 상태용)
*/
fun findConfigByCode(code: String): AutoTradeItem? = transaction {
fun findConfigByCode(code: String): AutoTradeItem? = transaction(mainDb) {
AutoTradeTable.select {
(AutoTradeTable.stockCode eq code) and (AutoTradeTable.status eq "MONITORING")
}.lastOrNull()?.let { mapToAutoTradeItem(it) }
}
fun deleteAutoTrade(id: Int) = transaction {
fun deleteAutoTrade(id: Int) = transaction(mainDb) {
AutoTradeTable.deleteWhere { AutoTradeTable.id eq id }
}
fun findAllPendingBuyCodes(): Set<String> {
return transaction {
return transaction(mainDb) {
AutoTradeTable.select {
(AutoTradeTable.status eq "PENDING_BUY") or (AutoTradeTable.status eq "ORDERED")
}.map { it[AutoTradeTable.stockCode] }.toSet()
@@ -206,7 +231,7 @@ object DatabaseFactory {
}
fun findAllMonitoringTrades(): List<AutoTradeItem> {
return transaction {
return transaction(mainDb) {
AutoTradeTable.select {
AutoTradeTable.status neq "COMPLETED"
}.map { mapToAutoTradeItem(it) }
@@ -230,7 +255,7 @@ object DatabaseFactory {
// --- 기존 설정 및 로그 관련 함수 ---
fun saveTradeLog(code: String, name: String, type: String, price: Double, qty: Int, msg: String) {
transaction {
transaction(mainDb) {
TradeLogTable.insert {
it[stockCode] = code
it[stockName] = name
@@ -243,7 +268,7 @@ object DatabaseFactory {
}
}
fun findConfigByAccount(accountNo: String): AppConfig? = transaction {
fun findConfigByAccount(accountNo: String): AppConfig? = transaction(mainDb) {
ConfigTable.select {
(ConfigTable.realAccountNo eq accountNo) or (ConfigTable.vtsAccountNo eq accountNo)
}.lastOrNull()?.let {
@@ -287,8 +312,8 @@ object DatabaseFactory {
stop_Loss = it[ConfigTable.stop_Loss],
take_profit = it[ConfigTable.take_profit],
loss_min = it[ConfigTable.loss_minrate],
loss_max = it[ConfigTable.loss_maxrate],
loss_money = it[ConfigTable.loss_max_money],
loss_max = it[ConfigTable.loss_maxrate],
loss_money = it[ConfigTable.loss_max_money],
MAX_COUNT = it[ConfigTable.max_count],
max_holding_count = it[ConfigTable.max_holding_count],
)
@@ -296,7 +321,7 @@ object DatabaseFactory {
}
fun saveConfig(config: AppConfig) {
transaction {
transaction(mainDb) {
ConfigTable.deleteAll()
ConfigTable.insert {
it[realAppKey] = config.realAppKey
@@ -346,7 +371,7 @@ object DatabaseFactory {
}
}
fun saveOrUpdate(item: AutoTradeItem) = transaction {
fun saveOrUpdate(item: AutoTradeItem) = transaction(mainDb) {
val existing = AutoTradeTable.select { AutoTradeTable.orderNo eq item.orderNo }.firstOrNull()
if (existing == null) {
AutoTradeTable.insert {
@@ -371,7 +396,7 @@ object DatabaseFactory {
/**
* 주문번호로 항목 조회 (가장 핵심적인 식별자)
*/
fun findByOrderNo(orderNo: String): AutoTradeItem? = transaction {
fun findByOrderNo(orderNo: String): AutoTradeItem? = transaction(mainDb) {
AutoTradeTable.select { AutoTradeTable.orderNo eq orderNo }
.map { mapToAutoTradeItem(it) }
.singleOrNull()
@@ -380,7 +405,7 @@ object DatabaseFactory {
/**
* 서버 동기화: DB에는 PENDING_BUY/MONITORING인데 서버 미체결 내역에 없는 경우 EXPIRED로 변경
*/
fun syncWithServer(serverOrderNos: List<String>) = transaction {
fun syncWithServer(serverOrderNos: List<String>) = transaction(mainDb) {
AutoTradeTable.update({
(AutoTradeTable.status inList listOf(TradeStatus.PENDING_BUY, TradeStatus.MONITORING)) and
(AutoTradeTable.orderNo notInList serverOrderNos)
@@ -392,7 +417,7 @@ object DatabaseFactory {
/**
* 상태 업데이트 및 주문번호 갱신 (예: 매수체결 시 신규 익절 주문번호로 교체)
*/
fun updateStatusAndOrderNo(id: Int, newStatus: String, newOrderNo: String? = null) = transaction {
fun updateStatusAndOrderNo(id: Int, newStatus: String, newOrderNo: String? = null) = transaction(mainDb) {
AutoTradeTable.update({ AutoTradeTable.id eq id }) {
it[status] = newStatus
if (newOrderNo != null) it[orderNo] = newOrderNo
@@ -402,7 +427,7 @@ object DatabaseFactory {
/**
* 감시 중인 모든 종목 리스트 (Status별 필터링 용이하게 수정)
*/
fun getAutoTradesByStatus(statusList: List<String>): List<AutoTradeItem> = transaction {
fun getAutoTradesByStatus(statusList: List<String>): List<AutoTradeItem> = transaction(mainDb) {
AutoTradeTable.select { AutoTradeTable.status inList statusList }
.map { mapToAutoTradeItem(it) }
}