137 lines
5.6 KiB
Python
137 lines
5.6 KiB
Python
import subprocess
|
|
import os
|
|
|
|
ROOT = "/home/hoon/kis_bot"
|
|
|
|
def run(cmd):
|
|
subprocess.run(cmd, shell=True, check=True, cwd=ROOT)
|
|
|
|
print("Restoring bloated files...")
|
|
run("git checkout templates/backtest.html static/js/backtest.js")
|
|
print("Done restoring files.")
|
|
|
|
# Re-apply HTML changes
|
|
html_path = f"{ROOT}/templates/backtest.html"
|
|
with open(html_path, "r", encoding="utf-8") as f:
|
|
html = f.read()
|
|
|
|
html = html.replace(
|
|
"<title>📈 KIS Quant 백테스트 대시보드</title>",
|
|
"<title>📈 KIS Quant 백테스트 대시보드</title>\n<link rel=\"icon\" href=\"data:,\">"
|
|
)
|
|
|
|
eod_block = """ </div>
|
|
|
|
<div class="row g-2 align-items-center mt-2 p-2 rounded" style="background:#161b22;border:1px solid #30363d">
|
|
<div class="col-12"><span class="text-muted" style="font-size:11px"><b style="color:#79c0ff">EOD 장마감 청산</b></span></div>
|
|
<div class="col-auto d-flex align-items-end">
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="bt_eod_enabled" checked>
|
|
<label class="form-check-label" for="bt_eod_enabled">적용</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<label class="form-label param-row" title="청산 시작 시간 (HH:MM)">시간</label>
|
|
<input type="time" class="form-control form-control-sm" id="bt_eod_time" value="15:25">
|
|
</div>
|
|
<div class="col-auto">
|
|
<label class="form-label param-row" title="미체결 주문(지정가 등) 전체 취소 여부">미체결취소</label>
|
|
<select class="form-select form-select-sm" id="bt_eod_cancel_open">
|
|
<option value="1" selected>Y (전체취소)</option>
|
|
<option value="0">N (유지)</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-auto">
|
|
<label class="form-label param-row" title="보유 종목 시장가 전량 매도 여부">시장가매도</label>
|
|
<select class="form-select form-select-sm" id="bt_eod_sell_holdings">
|
|
<option value="1" selected>Y (전량매도)</option>
|
|
<option value="0">N (오버나잇)</option>
|
|
</select>
|
|
</div>"""
|
|
|
|
html = html.replace(
|
|
"""<label class="form-label param-row" title="종목당 하루 최대 거래횟수">일일최대매수</label>
|
|
<input type="number" class="form-control" id="bt_max_daily" value="3" min="1" max="10">
|
|
</div>""",
|
|
f"""<label class="form-label param-row" title="종목당 하루 최대 거래횟수">일일최대매수</label>
|
|
<input type="number" class="form-control" id="bt_max_daily" value="3" min="1" max="10">
|
|
</div>{eod_block}"""
|
|
)
|
|
|
|
with open(html_path, "w", encoding="utf-8") as f:
|
|
f.write(html)
|
|
print("Applied HTML changes.")
|
|
|
|
# Re-apply JS changes
|
|
js_path = f"{ROOT}/static/js/backtest.js"
|
|
with open(js_path, "r", encoding="utf-8") as f:
|
|
js = f.read()
|
|
|
|
js = js.replace(
|
|
"cumReturnPct: Number(t.cum_return_pct ?? NaN),",
|
|
"cumReturnPct: Number(t.cum_return_pct ?? NaN),\n entryOb: t.entry_ob || null,\n exitOb: t.exit_ob || null,"
|
|
)
|
|
|
|
js = js.replace(
|
|
"const nameCell = (r.name || r.code)\n ? `${r.name || r.code}<br><span style=\"color:var(--muted);font-size:11px\">${r.code || ''}</span>`\n : (r.code || '-');",
|
|
"const nameCell = (r.name || r.code)\n ? `${r.name || r.code}<br><span style=\"color:var(--muted);font-size:11px\">${r.code || ''}</span>`\n : (r.code || '-');\n const linksHtml = _tradeExtLinksHtml(r);"
|
|
)
|
|
|
|
js = js.replace(
|
|
"<td class=\"trade-name-cell\">${causeBadge}${nameCell}</td>",
|
|
"<td class=\"trade-name-cell\">${causeBadge}${nameCell}${linksHtml}</td>"
|
|
)
|
|
|
|
js = js.replace(
|
|
"if (opts.showDebug) html += `<td style=\"font-size:10px\">${debugHtml}</td>`;",
|
|
"if (opts.showDebug) html += `<td style=\"font-size:10px\">${debugHtml}</td>`;\n html += `<td style=\"font-size:10px;line-height:1.35\">${_entryObCellHtml(r.entryOb)}</td>`;\n html += `<td style=\"font-size:10px;line-height:1.35\">${_exitObCellHtml(r.exitOb, r.isOpen)}</td>`;"
|
|
)
|
|
|
|
js = js.replace(
|
|
" html += `<td style=\"font-size:11px\">${reasonHtml}</td>`;",
|
|
" html += `<td style=\"font-size:11px\">${reasonHtml}</td>`;" # unchanged but kept for context, already handled above
|
|
)
|
|
|
|
helper_funcs = """/** KR 6자리 종목코드 */
|
|
function _normKrStockCode(code) {
|
|
const d = String(code || '').replace(/\\D/g, '');
|
|
if (!d) return '';
|
|
return d.length >= 6 ? d.slice(-6) : d.padStart(6, '0');
|
|
}
|
|
|
|
function _tradeExtLinksHtml(r) {
|
|
if (r.isUsd || !r.code) return '';
|
|
const c = _normKrStockCode(r.code);
|
|
return `
|
|
<div style="margin-top:2px; display:flex; gap:3px;">
|
|
<a href="https://finance.naver.com/item/main.naver?code=${c}" target="_blank" class="btn btn-xs btn-outline-secondary" style="font-size:9px;padding:0 3px">N</a>
|
|
<a href="https://m.stock.naver.com/domestic/stock/${c}/total" target="_blank" class="btn btn-xs btn-outline-secondary" style="font-size:9px;padding:0 3px">N(M)</a>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function _entryObCellHtml(ob) {
|
|
if (!ob) return '-';
|
|
const mr = (ob.mid_ratio || 0).toFixed(1);
|
|
const wr = (ob.whale_ratio || 0).toFixed(1);
|
|
return `M:${mr}%<br>W:${wr}%`;
|
|
}
|
|
|
|
function _exitObCellHtml(ob, isOpen) {
|
|
if (isOpen) return '<span style="color:#2ea043">보유중</span>';
|
|
if (!ob) return '-';
|
|
const mr = (ob.mid_ratio || 0).toFixed(1);
|
|
const wr = (ob.whale_ratio || 0).toFixed(1);
|
|
return `M:${mr}%<br>W:${wr}%`;
|
|
}
|
|
"""
|
|
|
|
js = js.replace(
|
|
"function renderVirtualTrades(tbodyId, trades, opts) {",
|
|
helper_funcs + "\nfunction renderVirtualTrades(tbodyId, trades, opts) {"
|
|
)
|
|
|
|
with open(js_path, "w", encoding="utf-8") as f:
|
|
f.write(js)
|
|
print("Applied JS changes.")
|