feat(hub): redesign archive as inner-light-mind journal

Rename archive to 内照明心 with daily trade records by default, review quotes sidebar, on-demand chart, sick-row highlighting, and new daily-trades/quotes APIs.
This commit is contained in:
dekun
2026-06-11 17:43:45 +08:00
parent 65d2bc5e00
commit bb800b876b
5 changed files with 926 additions and 325 deletions
+74
View File
@@ -39,18 +39,25 @@ from hub_volume_rank_lib import (
)
from hub_symbol_archive_lib import (
ARCHIVE_DEFAULT_TIMEFRAME,
ARCHIVE_QUOTES_MAX,
ARCHIVE_SEED_LOOKBACK_DAYS,
ARCHIVE_SYNC_INTERVAL_SEC,
ARCHIVE_TIMEFRAMES,
ARCHIVE_TRADE_DAYS,
ARCHIVE_TRADE_LIMIT,
ARCHIVE_VISIBLE_BARS_DEFAULT,
create_review_quote,
delete_review_quote,
init_db as init_archive_db,
list_daily_trades,
list_review_quotes,
list_symbol_rows,
load_symbol_trades,
parse_wall_clock_ms,
resolve_archive_chart,
sync_exchange_symbol_archives,
today_trading_day,
update_review_quote,
upsert_trade_overlay,
)
from env_load import load_hub_dotenv
@@ -2048,6 +2055,73 @@ def api_archive_list(
return {"ok": True, "rows": rows, "count": len(rows)}
@app.get("/api/archive/daily-trades")
def api_archive_daily_trades(
trading_day: str = "",
exchange_key: str = "",
filter_profit: str = "",
filter_loss: str = "",
filter_sick: str = "",
search: str = "",
):
init_archive_db()
payload = list_daily_trades(
trading_day=trading_day or today_trading_day(),
exchange_key=exchange_key,
filter_profit=(filter_profit or "").lower() in ("1", "true", "yes", "on"),
filter_loss=(filter_loss or "").lower() in ("1", "true", "yes", "on"),
filter_sick=(filter_sick or "").lower() in ("1", "true", "yes", "on"),
search=search,
)
return {"ok": True, **payload}
@app.get("/api/archive/quotes")
def api_archive_quotes():
init_archive_db()
rows = list_review_quotes()
return {"ok": True, "quotes": rows, "count": len(rows), "max": ARCHIVE_QUOTES_MAX}
class ArchiveQuoteBody(BaseModel):
quote_date: str = ""
content: str = ""
@app.post("/api/archive/quotes")
def api_archive_quote_create(body: ArchiveQuoteBody = Body(...)):
init_archive_db()
try:
row = create_review_quote(body.quote_date, body.content)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e)) from e
return {"ok": True, "quote": row}
@app.patch("/api/archive/quotes/{quote_id}")
def api_archive_quote_update(quote_id: int, body: ArchiveQuoteBody = Body(...)):
init_archive_db()
try:
row = update_review_quote(
int(quote_id),
quote_date=body.quote_date or None,
content=body.content if body.content is not None else None,
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e)) from e
if not row:
raise HTTPException(status_code=404, detail="语录不存在")
return {"ok": True, "quote": row}
@app.delete("/api/archive/quotes/{quote_id}")
def api_archive_quote_delete(quote_id: int):
init_archive_db()
if not delete_review_quote(int(quote_id)):
raise HTTPException(status_code=404, detail="语录不存在")
return {"ok": True, "id": int(quote_id)}
@app.get("/api/archive/detail")
def api_archive_detail(exchange_key: str = "", symbol: str = ""):
ex_k = (exchange_key or "").strip().lower()