package report import java.awt.Desktop import java.io.File import java.time.LocalDate import java.time.LocalDateTime import java.time.format.DateTimeFormatter object LocalReportGenerator { // ๐Ÿ’ก ๋ชจ๋“  ๊ฑฐ๋ž˜ ๋‚ด์—ญ์˜ ์ƒ์„ธ ์ง€ํ‘œ๋ฅผ ๋‹ด๋Š” DTO data class TradeDetailData( val isBuy: Boolean, // ๋งค์ˆ˜/๋งค๋„ ๊ตฌ๋ถ„ val stockName: String, val orderTime: String, // ์ฃผ๋ฌธ ์‹œ๊ฐ val timeTaken: String, // ์ฒ˜๋ฆฌ ์™„๋ฃŒ ์ด ์‹œ๊ฐ„ ๋˜๋Š” ๋ฏธ์™„๋ฃŒ ์ƒํƒœ val execQty: Int, // ์ฒ˜๋ฆฌ๋Ÿ‰ (์ฒด๊ฒฐ ์ˆ˜๋Ÿ‰) val avgPrice: Long, // ์ฒด๊ฒฐ ํ‰๊ท  ๋‹จ๊ฐ€ val profitRate: Double, // ์ˆ˜์ต๋ฅ  (๋งค๋„ ์‹œ์—๋งŒ ์œ ํšจ) val profitAmount: Long, // ์ˆ˜์ต๊ธˆ์•ก (๋งค๋„ ์‹œ์—๋งŒ ์œ ํšจ) val reason: String, val investmentGrade: String?, val aiScore: Double? ) fun generateAndOpen(startAsset: Long, endAsset: Long, tradeLogs: List) { val today = LocalDate.now().toString() val profitAmount = endAsset - startAsset val profitRate = if (startAsset > 0) (profitAmount.toDouble() / startAsset) * 100 else 0.0 val profitColor = if (profitAmount > 0) "#FF3B30" else if (profitAmount < 0) "#007AFF" else "#333333" val profitSign = if (profitAmount > 0) "+" else "" val timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss") // 1. ๋งค๋งค ์ƒ์„ธ ๋‚ด์—ญ HTML ํ–‰ ์ƒ์„ฑ val tradeRowsHtml = if (tradeLogs.isEmpty()) { "๊ธˆ์ผ ๋ฐœ์ƒํ•œ ์ฃผ๋ฌธ ๋‚ด์—ญ์ด ์—†์Šต๋‹ˆ๋‹ค." } else { tradeLogs.joinToString("\n") { trade -> // ๋งค์ˆ˜(๋นจ๊ฐ•), ๋งค๋„(ํŒŒ๋ž‘) ๋ฑƒ์ง€ val typeBadge = if (trade.isBuy) "๋งค์ˆ˜" else "๋งค๋„" // ์‹œ๊ฐ„ ํฌ๋งทํŒ… val orderTimeParsed = LocalDateTime.parse(trade.orderTime).format(timeFormatter) // ์ˆ˜์ต๋ฅ /์ˆ˜์ต๊ธˆ ์ฒ˜๋ฆฌ (๋งค์ˆ˜์ผ ๊ฒฝ์šฐ ํ‘œ์‹œ ์•ˆ ํ•จ) val rateText = if (trade.isBuy) "-" else "${String.format("%.2f", trade.profitRate)}%" val rateColor = if (trade.isBuy || trade.profitRate == 0.0) "#333" else if (trade.profitRate > 0) "#FF3B30" else "#007AFF" val amountText = if (trade.isBuy) "-" else "${String.format("%,d", trade.profitAmount)}์›" // ์ƒํƒœ/์‹œ๊ฐ„ ํ‘œ์‹œ ์ฒ˜๋ฆฌ val timeStatusHtml = if (trade.timeTaken.contains("๋ฏธ์™„๋ฃŒ")) { "${trade.timeTaken}" } else { "${trade.timeTaken}" } val gradeHtml = trade.investmentGrade?.let { "$it" } ?: "" """ $typeBadge ${trade.stockName} $gradeHtml $orderTimeParsed $timeStatusHtml ${String.format("%,d", trade.execQty)}์ฃผ ${String.format("%,d", trade.avgPrice)}์› $rateText $amountText
๐Ÿ’ก AI (${String.format("%.1f", trade.aiScore ?: 0.0)}์ ): ${trade.reason.replace("\n", " ")}
""".trimIndent() } } // 2. ์ „์ฒด HTML ํ…œํ”Œ๋ฆฟ val htmlTemplate = """ ATRADE ์ผ๊ฐ„ ๋ฆฌํฌํŠธ - $today

ATRADE ์ผ๊ฐ„ ๋งค๋งค ์ƒ์„ธ ๋ฆฌํฌํŠธ

์˜ค๋Š˜์˜ ์‹คํ˜„ ์†์ต (END - START)

$profitSign${String.format("%,d", profitAmount)}์› ($profitSign${String.format("%.2f", profitRate)}%)

๐Ÿ“Š ๊ธˆ์ผ ์ฃผ๋ฌธ ๋ฐ ์ฒด๊ฒฐ ๋‚ด์—ญ ์ „์ฒด

$tradeRowsHtml
๊ตฌ๋ถ„ ์ข…๋ชฉ๋ช… ์ฃผ๋ฌธ์‹œ๊ฐ ์†Œ์š”์‹œ๊ฐ„/์ƒํƒœ ์ฒด๊ฒฐ๋Ÿ‰ ํ‰๊ท ๋‹จ๊ฐ€ ์ˆ˜์ต๋ฅ  ์ˆ˜์ต๊ธˆ
""".trimIndent() val directory = File("reports") if (!directory.exists()) directory.mkdirs() val reportFile = File(directory, "ATRADE_Report_$today.html") try { reportFile.writeText(htmlTemplate, Charsets.UTF_8) if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { Desktop.getDesktop().browse(reportFile.toURI()) } } catch (e: Exception) { e.printStackTrace() } } }