π νκ΅μ΄λ‘ 보기
π Toss Auto-Trading Dev Journal Series (Phase 7-9 of 27)
- Phase 0 β Project Foundation (#0)
- Phase 0-1 β Currency Bug Fix (#0-1)
- Phase 1 β Config Split & Retry Logic (#3)
- Phase 1-2 β Config/Retry Follow-up (#4)
- Phase 1-3 β Introducing pytest (#5)
- Phase 1-4 β Log Rotation (#6)
- Phase 2-1 β Strategy Class Design (#7)
- Phase 2-2 β Multi-Symbol Monitoring (#8)
- Phase 3-1 β Data Persistence Design (#9)
- Phase 3-2 β DB Module Implementation (#10)
- Phase 3-3 β DB Integration (#11)
- Phase 2-3 β Strategy Plugin Interface (#12)
- Phase 4 β Backtest Engine (#13)
- Phase 4 β Candle API Pagination (#14)
- Phase 5 β Read-Only Dashboard (#15)
- Phase 5 β Telegram Control Logic (#16)
- Phase 5 β Telegram Control Logic Follow-up (#17)
- Phase 6 β Scheduler & Watchdog (#18)
- Phase 7 β Order Limit Guard (#19)
- Phase 7-2 β Loss Limit Auto-Pause (#20)
- Phase 7-3 β Dual-Channel Notification (#21)
- Phase 7-4 β Security Diagnostics (#22)
- Phase 7-5 β Take-Profit / Stop-Loss (#23)
- Phase 7-6 β Dashboard Notifier Security (#24)
- Phase 7-7 β Holdings Dashboard (#25)
- Phase 7-8 β One-Click Sell Condition (#26)
- Phase 7-9 β Kill Switch, Currency Formatter & Log Viewer (#27) β You are here
β¬ οΈ Previous: Phase 7-8 One-Click Sell Condition
π‘ Phase 7-9 at a Glance
- Goal: Fix the dashboard usability and currency-display issues found during dry-run testing by adding an asset summary grid, a web kill switch, one-click sell conditions, and a log viewer.
- Key decision: Separate KRW/USD formatting functions, plus collapsing two duplicate API calls (summary + holdings) into a single fetch pipeline.
- Verification: Confirmed against a live account and passed all 130 pytest unit tests.
A few days of dry-run trading and the dashboard worked fine functionally, but it was rough to actually look at. KRW and USD P&L sat in the same table with mismatched decimal formatting, and if something looked off, the only fix was opening Telegram and typing a command. So this phase was about turning a bot that “runs” into one you can actually watch and control.
Asset Summary Grid and Web Kill Switch
I added four cards to the top of dashboard.py: total KRW valuation/P&L/cost basis, total USD valuation/P&L/cost basis, blended return rate, and the status of the five-layer safety brake. Previously checking these meant scrolling through logs β now a page refresh is enough.
In the top-right corner, a live badge (π’ Monitoring / π΄ Paused) is wired to POST /api/engine/pause and POST /api/engine/resume, so one click stops or resumes monitoring. Being able to hit the brakes from any browser β no phone, no Telegram roundtrip β turned out to matter more psychologically than I expected.
One-Click Sell Conditions on Holdings
Each holding row now has an inline form: type a target price, click submit, and it’s written straight into the strategy_configs table. The bigger piece was splitting the side field into independent BUY / SELL / BOTH modes. Previously one strategy config covered both directions, so I couldn’t say “I already hold this β just watch for a sell.” Now each symbol can be configured independently.
def format_currency_price(amount: float, currency: str) -> str:
if currency == "KRW":
sign = "+" if amount > 0 else ("-" if amount < 0 else "")
return f"{sign}β©{abs(round(amount)):,}"
else: # USD
sign = "+" if amount > 0 else ("-" if amount < 0 else "")
return f"{sign}${abs(amount):,.2f}"
Precise KRW/USD Currency Formatter
Domestic holdings (e.g., Samsung Electronics, 005930) now render as β©8,969 or -β©33,166 β rounded to whole won with a sign. US holdings (e.g., SOXL) render as $144.95 or +$5.20 β two decimal places. Target-price conditions follow the same rule: 7,759μ μ΄μ for KRW, $152.00 μ΄μ for USD, split across format_currency_price, format_profit_amount, and format_target_price_condition.
Symbol names also go through db.get_display_name, so every table shows Samsung Electronics (005930) or SOXL (SOXL) consistently β no more guessing which market a row belongs to in a mixed KRW/USD list.
Single-Call API Pipeline
The summary cards and the holdings table were each calling the Toss API separately. Firing the same request twice within a second occasionally caused the second call to return empty data β the root cause of a holding intermittently vanishing from the table.
I merged both into _fetch_holdings_and_summary, a single API call whose result feeds both the summary cards and the holdings list. Load time roughly halved, and the missing-data bug hasn’t reproduced since.
Dark-Theme Log Viewer and One-Click Startup
At the bottom of the dashboard, a Live Terminal Box streams the last 40 lines of toss_trader.log in a dark-theme console β no need to open VS Code or SSH in to check what the bot is doing.
Alongside it, start_all_daemons.bat (starts all four services in the background) and stop_all_daemons.bat (cleanly kills them) let the whole stack start or stop from a single double-click on Windows.
Summary: This phase added an asset summary grid, web kill switch, one-click sell conditions, a KRW/USD currency formatter, a single-call API pipeline, and a dark-theme log viewer to the trading dashboard.
Verification: 130 Passing pytest Cases
Currency-formatting branch logic, the kill switch API, sell-condition registration, and the unified API pipeline are all covered β 130 pytest unit tests, all passing. I also ran it against a live account for several days and cross-checked the summary numbers against the actual balance.
What’s Next
The “watchable” dashboard is roughly done. Next up: how to manage priority when multiple strategies are running on the same symbol at once.
FAQ
Q1. How is the web kill switch different from the Telegram command?
A. Same function (pause/resume monitoring), but the web switch works even when Telegram is slow to respond or you don’t have your phone β any browser will do.
Q2. Why build separate KRW/USD formatters?
A. Whole-won rounding reads naturally for domestic holdings, while two-decimal cents are expected for USD β one shared function made one side or the other look off.
Q3. What does merging the API calls actually improve?
A. It avoids fetching the same data twice, which sped things up and eliminated the intermittent missing-holding bug caused by timing gaps between the two calls.
Q4. How does the log viewer work?
A. The server reads the last 40 lines of the log file and streams them into a dark-theme console box on the dashboard page β no separate terminal session needed.