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:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user