Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions backend_api_python/app/data_sources/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,10 @@ def log_result(
if klines:
latest_time = datetime.fromtimestamp(klines[-1]['time'])
time_diff = (datetime.now() - latest_time).total_seconds()
logger.info(
f"{self.name}: {symbol} 获取 {len(klines)} 条数据, "
f"最新时间: {latest_time}, 延迟: {time_diff:.0f}秒"
)
# logger.info(
# f"{self.name}: {symbol} 获取 {len(klines)} 条数据, "
# f"最新时间: {latest_time}, 延迟: {time_diff:.0f}秒"
# )

# 检查数据是否过旧
max_diff = TIMEFRAME_SECONDS.get(timeframe, 3600) * 2
Expand Down
2 changes: 1 addition & 1 deletion backend_api_python/app/routes/portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ def add_position():
INSERT INTO qd_manual_positions
(user_id, market, symbol, name, side, quantity, entry_price, entry_time, notes, tags, group_name, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
ON CONFLICT(user_id, market, symbol, side) DO UPDATE SET
ON CONFLICT(user_id, market, symbol, side, group_name) DO UPDATE SET
name = excluded.name,
quantity = excluded.quantity,
entry_price = excluded.entry_price,
Expand Down
13 changes: 9 additions & 4 deletions backend_api_python/app/services/portfolio_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def _get_positions_for_monitor(position_ids: List[int] = None, user_id: int = No
placeholders = ','.join(['?' for _ in position_ids])
cur.execute(
f"""
SELECT id, market, symbol, name, side, quantity, entry_price
SELECT id, market, symbol, name, side, quantity, entry_price, group_name
FROM qd_manual_positions
WHERE user_id = ? AND id IN ({placeholders})
""",
Expand All @@ -100,7 +100,7 @@ def _get_positions_for_monitor(position_ids: List[int] = None, user_id: int = No
else:
cur.execute(
"""
SELECT id, market, symbol, name, side, quantity, entry_price
SELECT id, market, symbol, name, side, quantity, entry_price, group_name
FROM qd_manual_positions
WHERE user_id = ?
""",
Expand All @@ -116,6 +116,7 @@ def _get_positions_for_monitor(position_ids: List[int] = None, user_id: int = No
entry_price = float(row.get('entry_price') or 0)
quantity = float(row.get('quantity') or 0)
side = row.get('side') or 'long'
group_name = row.get('group_name')

# Get current price (use realtime price API)
current_price = 0
Expand Down Expand Up @@ -143,7 +144,8 @@ def _get_positions_for_monitor(position_ids: List[int] = None, user_id: int = No
'entry_price': entry_price,
'current_price': current_price,
'pnl': round(pnl, 2),
'pnl_percent': pnl_percent
'pnl_percent': pnl_percent,
'group_name': group_name
})

return positions
Expand All @@ -168,6 +170,7 @@ def _run_ai_analysis(positions: List[Dict[str, Any]], config: Dict[str, Any]) ->
market = pos.get('market')
symbol = pos.get('symbol')
name = pos.get('name') or symbol
group_name = pos.get('group_name')

if not market or not symbol:
continue
Expand All @@ -193,6 +196,7 @@ def _run_ai_analysis(positions: List[Dict[str, Any]], config: Dict[str, Any]) ->
'market': market,
'symbol': symbol,
'name': name,
'group_name': group_name,
'entry_price': pos.get('entry_price'),
'current_price': pos.get('current_price'),
'pnl': pos.get('pnl'),
Expand Down Expand Up @@ -454,6 +458,7 @@ def _build_html_report(
symbol = pa.get('symbol', '')
name = pa.get('name', symbol)
market = pa.get('market', '')
group_name = pa.get('group_name', '')

if pa.get('error'):
html += f'''
Expand Down Expand Up @@ -538,7 +543,7 @@ def _build_html_report(
'''

# Generate unique ID for collapsible sections (use symbol hash to avoid special chars)
section_id_base = hashlib.md5(f"{symbol}_{market}".encode()).hexdigest()[:8]
section_id_base = hashlib.md5(f"{symbol}_{market}_{group_name}".encode()).hexdigest()[:8]

# Collapsible: Trader Analysis
if trader_reasoning:
Expand Down
7 changes: 7 additions & 0 deletions backend_api_python/app/services/signal_notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import os
import smtplib
import time
import traceback
from datetime import datetime, timezone
from email.message import EmailMessage
from typing import Any, Dict, List, Optional, Tuple
Expand Down Expand Up @@ -482,6 +483,7 @@ def _notify_browser(
return True, ""
except Exception as e:
logger.warning(f"browser notify persist failed: {e}")
logger.error('browser.error', traceback=traceback.format_exc())
return False, str(e)

def _notify_webhook(
Expand Down Expand Up @@ -571,6 +573,7 @@ def _post_once(timeout: float) -> requests.Response:
return False, f"http_{resp2.status_code}:{(resp2.text or '')[:300]}"
return False, f"http_{resp.status_code}:{(resp.text or '')[:300]}"
except Exception as e:
logger.error('webhook.error', traceback=traceback.format_exc())
return False, str(e)

def _notify_discord(self, *, url: str, payload: Dict[str, Any], fallback_text: str) -> Tuple[bool, str]:
Expand Down Expand Up @@ -646,6 +649,7 @@ def _post(payload_json: Dict[str, Any]) -> requests.Response:
pass
return False, f"http_{resp.status_code}:{(resp.text or '')[:300]}"
except Exception as e:
logger.error('discord.error', traceback=traceback.format_exc())
return False, str(e)

def _notify_telegram(
Expand Down Expand Up @@ -680,6 +684,7 @@ def _notify_telegram(
return True, ""
return False, f"http_{resp.status_code}:{(resp.text or '')[:300]}"
except Exception as e:
logger.error('telegram.error', traceback=traceback.format_exc())
return False, str(e)

def _notify_email(self, *, to_email: str, subject: str, body_text: str, body_html: str = "") -> Tuple[bool, str]:
Expand Down Expand Up @@ -718,6 +723,7 @@ def _notify_email(self, *, to_email: str, subject: str, body_text: str, body_htm
server.send_message(msg)
return True, ""
except Exception as e:
logger.error('email.error', traceback=traceback.format_exc())
return False, str(e)

def _notify_phone(self, *, to_phone: str, body: str) -> Tuple[bool, str]:
Expand All @@ -734,6 +740,7 @@ def _notify_phone(self, *, to_phone: str, body: str) -> Tuple[bool, str]:
return True, ""
return False, f"http_{resp.status_code}:{(resp.text or '')[:300]}"
except Exception as e:
logger.error('phone.error', traceback=traceback.format_exc())
return False, str(e)


2 changes: 1 addition & 1 deletion backend_api_python/migrations/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ CREATE TABLE IF NOT EXISTS qd_manual_positions (
group_name VARCHAR(100) DEFAULT '',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(user_id, market, symbol, side)
UNIQUE(user_id, market, symbol, side, group_name)
);

CREATE INDEX IF NOT EXISTS idx_manual_positions_user_id ON qd_manual_positions(user_id);
Expand Down
Loading