55 lines
1.7 KiB
Python
55 lines
1.7 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
ACTIONS = {"buy": "买入", "sell": "卖出", "add": "加仓", "trim": "减仓", "watch": "观察"}
|
|
EMOTIONS = {
|
|
"calm": "平静",
|
|
"confident": "笃定",
|
|
"hesitant": "犹豫",
|
|
"anxious": "焦虑",
|
|
"impulsive": "冲动",
|
|
}
|
|
|
|
|
|
def note(row) -> dict[str, Any] | None:
|
|
return dict(row) if row is not None else None
|
|
|
|
|
|
def trades(rows) -> list[dict[str, Any]]:
|
|
result = []
|
|
for row in rows:
|
|
item = dict(row)
|
|
item["tags"] = json.loads(str(item.pop("tags_json")))
|
|
item["action_label"] = ACTIONS[str(item["action"])]
|
|
item["emotion_label"] = EMOTIONS[str(item["emotion"])]
|
|
result.append(item)
|
|
return result
|
|
|
|
|
|
def trade_summary(items: list[dict[str, Any]]) -> dict[str, Any]:
|
|
realized = [
|
|
item for item in items if item["pnl_amount"] is not None or item["pnl_pct"] is not None
|
|
]
|
|
wins = sum(
|
|
float(item["pnl_pct"] if item["pnl_pct"] is not None else item["pnl_amount"]) > 0
|
|
for item in realized
|
|
)
|
|
amounts = [float(item["pnl_amount"]) for item in items if item["pnl_amount"] is not None]
|
|
positions = [float(item["position_pct"]) for item in items if item["position_pct"] is not None]
|
|
return {
|
|
"total": len(items),
|
|
"realized": len(realized),
|
|
"win_rate": round(wins / len(realized) * 100, 1) if realized else None,
|
|
"pnl_amount": round(sum(amounts), 2) if amounts else None,
|
|
"average_position": round(sum(positions) / len(positions), 1) if positions else None,
|
|
}
|
|
|
|
|
|
def alert(row, today: str) -> dict[str, Any]:
|
|
item = dict(row)
|
|
item["is_read"] = bool(item["is_read"])
|
|
item["due"] = str(item["available_date"]) <= today
|
|
return item
|