Add deposit-based total return, session persistence flag, dual return rates

- Add fetch_total_deposits() with hourly local cache (deposit_cache.json)
- Use deposit total as capital base for accurate total return calculation
- Add --no-session-persistence to claude CLI subprocess calls
- Show both total return (deposit-based) and change rate (cost-based) in reports
- Update portfolio summary with Total Return line

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
kroutony 2026-03-15 10:21:39 +00:00
parent ddc1b9e3eb
commit 17303d5d3d
6 changed files with 118 additions and 34 deletions

View File

@ -1,15 +1,16 @@
{
"tETHUST": null,
"tSUIUST": 1773362755397,
"tSOLUST": null,
"tETHUST": 1773352562967,
"tSUIUST": null,
"tSOLUST": 1773363657396,
"tLTCUST": null,
"tADAUST": null,
"tAVAX:UST": 1773380455009,
"tUNIUST": 1773380455473,
"tXRPUST": null,
"tDOGE:UST": 1773407762194,
"tAVAX:UST": null,
"tUNIUST": null,
"tXRPUST": 1773363657836,
"tDOGE:UST": null,
"tSHIB:UST": null,
"tBTCUST": null,
"tBTCUST": 1773345335546,
"tXLMUST": null,
"tLINK:UST": null
"tLINK:UST": 1773361253747,
"tDOTUST": 1773362453371
}

View File

@ -52,6 +52,60 @@ def _auth_post(path: str, body: dict | None = None) -> dict | list:
return resp.json()
# ---------------------------------------------------------------------------
# Deposit history (cached locally, refreshed hourly)
# ---------------------------------------------------------------------------
# 2026-03-10 00:00:00 UTC in milliseconds
_DEPOSIT_START_MS = 1773100800000
_DEPOSIT_CACHE_FILE = "deposit_cache.json"
_DEPOSIT_CACHE_TTL = 3600 # 1 hour
def fetch_total_deposits() -> float:
"""Return net USDT deposits since 2026-03-10. Uses local cache, refreshes hourly."""
# Try cache first
if os.path.exists(_DEPOSIT_CACHE_FILE):
try:
with open(_DEPOSIT_CACHE_FILE) as f:
cache = json.load(f)
if time.time() - cache.get("ts", 0) < _DEPOSIT_CACHE_TTL:
return cache["total"]
except (json.JSONDecodeError, IOError, KeyError):
pass
# Cache miss or expired — fetch from API
total = _fetch_deposits_from_api()
if total > 0:
try:
with open(_DEPOSIT_CACHE_FILE, "w") as f:
json.dump({"total": total, "ts": time.time()}, f)
except IOError:
pass
return total
def _fetch_deposits_from_api() -> float:
"""Fetch net USDT deposits from Bitfinex API."""
try:
raw = _auth_post("/v2/auth/r/movements/UST/hist", {
"start": _DEPOSIT_START_MS,
"limit": 100,
})
except Exception as e:
logger.error("Failed to fetch deposit history: %s", e)
return 0.0
total = 0.0
for m in raw:
status = m[9] if len(m) > 9 else ""
if status != "COMPLETED":
continue
amount = float(m[12]) if len(m) > 12 else 0
total += amount # positive = deposit, negative = withdrawal
return total
# ---------------------------------------------------------------------------
# Public endpoints
# ---------------------------------------------------------------------------

View File

@ -68,7 +68,7 @@ def analyze_market(indicator_summary: str, account_status: str) -> list[dict]:
try:
result = subprocess.run(
["claude", "-p", prompt, "--output-format", "json"],
["claude", "-p", prompt, "--output-format", "json", "--no-session-persistence"],
capture_output=True,
text=True,
timeout=120,

16
main.py
View File

@ -99,6 +99,12 @@ def run_cycle():
port["available_usdt"] = 10000
logger.info("Paper trading: using default 10000 USDT balance")
# 2a. Fetch initial capital from deposit history
try:
port["initial_capital"] = data_fetcher.fetch_total_deposits()
except Exception as e:
logger.warning("Failed to fetch deposit history: %s", e)
# 2b. Sync stop-loss orders with exchange (real-time, not local memory)
try:
active_orders = data_fetcher.fetch_active_orders()
@ -503,13 +509,17 @@ def _build_portfolio_one_liner(port: dict, current_prices: dict) -> str:
total_cost += amount * entry
if current > 0:
total_market_value += amount * current
unrealized = total_market_value - total_cost
total_value = available + total_market_value
# 總收益率(基於入金)
initial_capital = port.get("initial_capital", 0)
total_return_pct = ((total_value / initial_capital - 1) * 100) if initial_capital > 0 else 0.0
# 變動收益率(基於持倉成本)
unrealized = total_market_value - total_cost
total_capital = available + total_cost
total_return_pct = ((total_value / total_capital - 1) * 100) if total_capital > 0 else 0.0
change_pct = ((total_value / total_capital - 1) * 100) if total_capital > 0 else 0.0
return (
f"總值 {total_value:.2f} USDT | 總收益 {total_return_pct:+.2f}% | "
f"{available:.2f} 可用 | 持倉 {pos_count}"
f"變動 {change_pct:+.2f}% | {available:.2f} 可用 | 持倉 {pos_count}"
)

View File

@ -135,7 +135,14 @@ def get_portfolio_summary(portfolio: dict, current_prices: dict) -> str:
lines.append("")
lines.append(f"*Total Portfolio Value:* {total_value:.2f} USDT")
lines.append(f"*Total Unrealized P&L:* {total_unrealized:+.2f} USDT")
lines.append(f"*Total Unrealized P&L:* {total_unrealized:+.2f} USDT ({total_unrealized / (total_value - total_unrealized) * 100:+.2f}%)" if (total_value - total_unrealized) > 0 else f"*Total Unrealized P&L:* {total_unrealized:+.2f} USDT")
initial_capital = portfolio.get("initial_capital", 0)
if initial_capital > 0:
total_return = total_value - initial_capital
total_return_pct = (total_value / initial_capital - 1) * 100
sign = "+" if total_return >= 0 else ""
lines.append(f"*Total Return:* {sign}{total_return:.2f} USDT ({sign}{total_return_pct:.2f}%)")
history = portfolio.get("trade_history", [])
if history:

View File

@ -1,26 +1,38 @@
{
"tDOGE:UST": {
"order_id": 233282701621,
"stop_price": 0.093946,
"entry_price": 0.09688198941787385,
"amount": 144.10790954
"tDOTUST": {
"order_id": 233309387815,
"stop_price": 1.3849,
"entry_price": 1.4287,
"amount": 10.03723093
},
"tSUIUST": {
"order_id": 233265250698,
"stop_price": 0.97101,
"entry_price": 1.0009320066822542,
"amount": 54.57098037
"tXRPUST": {
"order_id": 233329683966,
"stop_price": 1.372,
"entry_price": 1.4153,
"amount": 12.66451728
},
"tAVAX:UST": {
"order_id": 233268826282,
"stop_price": 9.4454,
"entry_price": 9.734478498389963,
"amount": 4.08390185
"tSOLUST": {
"order_id": 233329069994,
"stop_price": 85.644,
"entry_price": 88.367,
"amount": 0.16229378
},
"tUNIUST": {
"order_id": 233279675016,
"stop_price": 3.8778,
"entry_price": 3.9954999999999994,
"amount": 4.55798027
"tETHUST": {
"order_id": 233338658543,
"stop_price": 2048,
"entry_price": 2110.9826633288,
"amount": 0.01357354
},
"tLINK:UST": {
"order_id": 233331464285,
"stop_price": 8.9428,
"entry_price": 9.2243,
"amount": 1.16577353
},
"tBTCUST": {
"order_id": 233335923158,
"stop_price": 69413,
"entry_price": 71556.91752086183,
"amount": 0.00065071
}
}