import model.AppConfig 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 java.io.File import java.time.LocalDateTime // 1. 앱 설정 테이블 object ConfigTable : Table("app_config") { val id = integer("id").autoIncrement() val realAppKey = varchar("real_app_key", 255).default("") val realSecretKey = varchar("real_secret_key", 255).default("") val realAccountNo = varchar("real_account_no", 20).default("") val vtsAppKey = varchar("vts_app_key", 255).default("") val vtsSecretKey = varchar("vts_secret_key", 255).default("") val vtsAccountNo = varchar("vts_account_no", 20).default("") val isSimulation = bool("is_simulation").default(true) val modelPath = varchar("model_path", 512).default("") val htsId = varchar("hts_id", 50).default("") // HTS ID 컬럼 추가 override val primaryKey = PrimaryKey(id) } // 2. 자동매매 감시 테이블 object AutoTradeTable : Table("auto_trades") { val id = integer("id").autoIncrement() val stockCode = varchar("stock_code", 20) val stockName = varchar("stock_name", 100) val targetPrice = double("target_price") // 익절 목표가 val stopLossPrice = double("stop_loss_price") // 손절 목표가 val status = varchar("status", 20).default("MONITORING") // MONITORING, COMPLETED val isDomestic = bool("is_domestic").default(true) override val primaryKey = PrimaryKey(id) } // 3. 거래 내역 테이블 object TradeLogTable : Table("trade_logs") { val id = long("id").autoIncrement() val stockCode = varchar("stock_code", 20) val stockName = varchar("stock_name", 50) val tradeType = varchar("trade_type", 10) val price = double("price") val quantity = integer("quantity") val timestamp = datetime("timestamp") val logMessage = text("log_message") override val primaryKey = PrimaryKey(id) } object DatabaseFactory { fun init() { val dbPath = File("db/autotrade_db").absolutePath Database.connect( "jdbc:h2:$dbPath;DB_CLOSE_DELAY=-1;", driver = "org.h2.Driver" ) transaction { // 테이블 생성 (AutoTradeTable 포함) SchemaUtils.create(ConfigTable, TradeLogTable, AutoTradeTable) } } // --- 자동매매(감시) 관련 함수 --- /** * [추가] 종목코드로 현재 감시 중인 설정 가져오기 (웹소켓 감시용) */ fun findConfigByCode(code: String): AutoTradeItem? = transaction { AutoTradeTable.select { (AutoTradeTable.stockCode eq code) and (AutoTradeTable.status eq "MONITORING") }.lastOrNull()?.let { mapToAutoTradeItem(it) } } /** * [추가] 매수 체결 시 새로운 자동매매 감시 대상 등록 */ fun saveAutoTrade(item: AutoTradeItem) { transaction { // 동일 종목이 이미 감시 중이면 삭제 후 재등록 (중복 방지) AutoTradeTable.deleteWhere { stockCode eq item.code } AutoTradeTable.insert { it[stockCode] = item.code it[stockName] = item.name it[targetPrice] = item.targetPrice it[stopLossPrice] = item.stopLossPrice it[status] = "MONITORING" it[isDomestic] = item.isDomestic } } } /** * [추가] 매도 완료 또는 취소 시 감시 대상 삭제 */ fun deleteAutoTrade(code: String) { transaction { AutoTradeTable.deleteWhere { stockCode eq code } } } /** * [수정] 감시 중인 모든 종목 리스트 반환 (ActiveTradeSection UI용) */ fun getActiveAutoTrades(): List = transaction { AutoTradeTable.select { AutoTradeTable.status eq "MONITORING" } .map { mapToAutoTradeItem(it) } } // ResultRow를 AutoTradeItem으로 매핑하는 내부 함수 private fun mapToAutoTradeItem(it: ResultRow) = AutoTradeItem( code = it[AutoTradeTable.stockCode], name = it[AutoTradeTable.stockName], targetPrice = it[AutoTradeTable.targetPrice], stopLossPrice = it[AutoTradeTable.stopLossPrice], status = it[AutoTradeTable.status], isDomestic = it[AutoTradeTable.isDomestic] ) // --- 기존 설정 및 로그 관련 함수 --- fun saveTradeLog(code: String, name: String, type: String, price: Double, qty: Int, msg: String) { transaction { TradeLogTable.insert { it[stockCode] = code it[stockName] = name it[tradeType] = type it[TradeLogTable.price] = price it[quantity] = qty it[timestamp] = LocalDateTime.now() it[logMessage] = msg } } } fun findConfigByAccount(accountNo: String): AppConfig? = transaction { ConfigTable.select { (ConfigTable.realAccountNo eq accountNo) or (ConfigTable.vtsAccountNo eq accountNo) }.lastOrNull()?.let { AppConfig( realAppKey = it[ConfigTable.realAppKey], realSecretKey = it[ConfigTable.realSecretKey], realAccountNo = it[ConfigTable.realAccountNo], vtsAppKey = it[ConfigTable.vtsAppKey], vtsSecretKey = it[ConfigTable.vtsSecretKey], vtsAccountNo = it[ConfigTable.vtsAccountNo], isSimulation = it[ConfigTable.isSimulation], htsId = it[ConfigTable.htsId], // htsId 로드 modelPath = it[ConfigTable.modelPath] ) } } fun saveConfig(config: AppConfig) { transaction { ConfigTable.deleteAll() ConfigTable.insert { it[realAppKey] = config.realAppKey it[realSecretKey] = config.realSecretKey it[vtsAppKey] = config.vtsAppKey it[vtsSecretKey] = config.vtsSecretKey it[realAccountNo] = config.realAccountNo it[vtsAccountNo] = config.vtsAccountNo it[isSimulation] = config.isSimulation it[htsId] = config.htsId it[modelPath] = config.modelPath } } } } /** * [수정] 감시 가격(익절/손절) 정보를 포함하도록 모델 확장 */ data class AutoTradeItem( val code: String, val name: String, val targetPrice: Double, val stopLossPrice: Double, // 손절가 추가 val status: String, val isDomestic: Boolean )